summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format10
-rw-r--r--.github/workflows/build_cmake.yml4
-rw-r--r--.gitignore-blame2
-rw-r--r--CMakeLists.txt19
-rw-r--r--README.md185
-rw-r--r--cmake/FindQt5.cmake103
-rw-r--r--cmake/QtCreatorAPI.cmake9
-rw-r--r--cmake/QtCreatorAPIInternal.cmake11
-rw-r--r--cmake/QtCreatorDocumentation.cmake4
-rw-r--r--cmake/QtCreatorIDEBranding.cmake6
-rw-r--r--cmake/QtCreatorTranslations.cmake4
-rw-r--r--coin/instructions/build.yaml3
-rw-r--r--coin/instructions/common_environment.yaml5
-rw-r--r--coin/product_dependencies.yaml4
-rw-r--r--doc/config/macros.qdocconf1
-rw-r--r--doc/qtcreator/config/style/qt5-sidebar.html2
-rw-r--r--doc/qtcreator/images/creator_advanceduse.svg27
-rw-r--r--doc/qtcreator/images/creator_buildingrunning.svg39
-rw-r--r--doc/qtcreator/images/creator_coding.svg37
-rw-r--r--doc/qtcreator/images/creator_designinguserinterface.svg45
-rw-r--r--doc/qtcreator/images/creator_gettinghelp.svg16
-rw-r--r--doc/qtcreator/images/creator_gettingstarted.svg22
-rw-r--r--doc/qtcreator/images/creator_managingprojects.svg26
-rw-r--r--doc/qtcreator/images/creator_publishing.svg40
-rw-r--r--doc/qtcreator/images/creator_testing.svg34
-rw-r--r--doc/qtcreator/images/front-advanced.pngbin4873 -> 355 bytes
-rw-r--r--doc/qtcreator/images/front-coding.pngbin6045 -> 125 bytes
-rw-r--r--doc/qtcreator/images/front-gs.pngbin5150 -> 2940 bytes
-rw-r--r--doc/qtcreator/images/front-help.pngbin1937 -> 1032 bytes
-rw-r--r--doc/qtcreator/images/front-preview.pngbin4789 -> 430 bytes
-rw-r--r--doc/qtcreator/images/front-projects.pngbin2712 -> 676 bytes
-rw-r--r--doc/qtcreator/images/front-publishing.pngbin5101 -> 715 bytes
-rw-r--r--doc/qtcreator/images/front-testing.pngbin2480 -> 905 bytes
-rw-r--r--doc/qtcreator/images/front-ui.pngbin3491 -> 448 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-add-online-doc.pngbin11916 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-add-online-doc.webpbin0 -> 8250 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-add-resource-wizard4.pngbin5671 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-customize.pngbin22905 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-customize.webpbin0 -> 16400 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-example.pngbin26022 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-example.webpbin0 -> 10848 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-open.pngbin11910 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator-open.webpbin0 -> 5444 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator.pngbin13532 -> 0 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-locator.webpbin0 -> 8168 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-markdown-editor.webpbin0 -> 22486 bytes
-rw-r--r--doc/qtcreator/images/qtcreator-new-file.webpbin0 -> 17272 bytes
-rw-r--r--doc/qtcreator/src/editors/creator-coding.qdoc4
-rw-r--r--doc/qtcreator/src/editors/creator-locator.qdoc211
-rw-r--r--doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc2
-rw-r--r--doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc2
-rw-r--r--doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc22
-rw-r--r--doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc6
-rw-r--r--doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc9
-rw-r--r--doc/qtcreator/src/linux-mobile/linuxdev.qdoc2
-rw-r--r--doc/qtcreator/src/overview/creator-acknowledgements.qdoc111
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc149
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc16
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc163
-rw-r--r--doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc6
-rw-r--r--doc/qtcreator/src/python/creator-python-project.qdocinc4
-rw-r--r--doc/qtcreator/src/qtcreator-toc.qdoc9
-rw-r--r--doc/qtcreator/src/qtcreator.qdoc6
-rw-r--r--doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc2
-rw-r--r--doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc64
-rw-r--r--doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc2
-rw-r--r--doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc2
-rw-r--r--doc/qtcreator/src/user-interface/creator-ui.qdoc8
-rw-r--r--doc/qtcreator/src/webassembly/creator-webassembly.qdoc2
-rw-r--r--doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc8
-rw-r--r--doc/qtdesignstudio/examples/doc/images/material-bundle-example.webpbin0 -> 60022 bytes
-rw-r--r--doc/qtdesignstudio/examples/doc/materialbundle.qdoc33
-rw-r--r--doc/qtdesignstudio/images/3d-view-context-menu.pngbin4571 -> 10442 bytes
-rw-r--r--doc/qtdesignstudio/images/assets-view-effect.pngbin4813 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/content-library-add-texture.pngbin15426 -> 15528 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/3d-background-color.pngbin335 -> 348 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/align-camera-on.pngbin309 -> 282 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/align-view-on.pngbin321 -> 277 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/apply-material.pngbin779 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/apply.pngbin0 -> 330 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/arrowleft.pngbin147 -> 176 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/arrowright.pngbin149 -> 177 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/back_one_frame.pngbin153 -> 227 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/create_component.pngbin0 -> 376 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/curve_editor.pngbin190 -> 286 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/easing-curve-linear-icon.pngbin201 -> 247 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/easing-curve-spline-icon.pngbin391 -> 360 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/easing-curve-step-icon.pngbin147 -> 255 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/edit.pngbin242 -> 263 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/edit_component.pngbin0 -> 345 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/edit_light_off.pngbin0 -> 281 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/edit_light_on.pngbin0 -> 260 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/fit_selected.pngbin0 -> 296 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/forward_one_frame.pngbin147 -> 234 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/global.pngbin0 -> 323 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/global_record_keyframes.pngbin162 -> 263 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/home.pngbin0 -> 299 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/move_off.pngbin0 -> 289 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/navigator-arrowdown.pngbin165 -> 159 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/navigator-arrowup.pngbin160 -> 162 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/orthographic_camera.pngbin0 -> 244 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particle-animation-on.pngbin316 -> 2101 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particle-pause.pngbin120 -> 157 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particle-play.pngbin159 -> 205 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particle-restart.pngbin302 -> 335 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particles-seek.pngbin311 -> 331 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/perspective_camera.pngbin0 -> 216 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/reset.pngbin0 -> 294 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/rotate_off.pngbin0 -> 272 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/scale_off.pngbin0 -> 303 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/select_group.pngbin0 -> 210 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/select_item.pngbin0 -> 263 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/settings.pngbin0 -> 315 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/start_playback.pngbin143 -> 234 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/to_first_frame.pngbin135 -> 249 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/to_last_frame.pngbin132 -> 272 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/visibilityon.pngbin271 -> 295 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/zoomAll.pngbin302 -> 236 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/zoomIn.pngbin323 -> 300 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/zoomOut.pngbin314 -> 276 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/zoomSelection.pngbin316 -> 287 bytes
-rw-r--r--doc/qtdesignstudio/images/loader3d-navigator.pngbin6100 -> 5906 bytes
-rw-r--r--doc/qtdesignstudio/images/material-copy-properties.pngbin34372 -> 24286 bytes
-rw-r--r--doc/qtdesignstudio/images/navigator-material-texture.pngbin3820 -> 4048 bytes
-rw-r--r--doc/qtdesignstudio/images/navigator-show-all-loader.pngbin9079 -> 8056 bytes
-rw-r--r--doc/qtdesignstudio/images/new-effect-file.pngbin9517 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/qml-shapes-rectangle.pngbin38147 -> 56733 bytes
-rw-r--r--doc/qtdesignstudio/images/qml-shapes.pngbin13756 -> 20563 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-alignment.pngbin13055 -> 13015 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-anchors.pngbin15914 -> 15122 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-bindings.pngbin3567 -> 3514 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-breadcrumbs.pngbin14483 -> 44652 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-button.pngbin8390 -> 3937 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-canvas-color.pngbin5691 -> 4557 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-connections.pngbin3698 -> 3294 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-dynamicprops.pngbin4094 -> 3352 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-editing-components.pngbin75716 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-editing-components.webpbin0 -> 54684 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-element-size.pngbin41513 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-element-size.webpbin0 -> 12894 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-export-item.pngbin3968 -> 4640 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.pngbin8669 -> 9983 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-form-editor.pngbin14960 -> 9604 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-mcu-support.pngbin53982 -> 50795 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-navigator-arrows.pngbin4757 -> 5657 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-navigator.pngbin14017 -> 24569 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-preview-size.pngbin33337 -> 45589 bytes
-rw-r--r--doc/qtdesignstudio/images/qmldesigner-tutorial-user-icon.pngbin41618 -> 44536 bytes
-rw-r--r--doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.pngbin66818 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.webpbin0 -> 38358 bytes
-rw-r--r--doc/qtdesignstudio/images/qtds-running-emulator.pngbin230313 -> 228819 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-annotations.pngbin24011 -> 23328 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-assets-tab.pngbin16901 -> 16953 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-components-context-menu-hide.pngbin25418 -> 20061 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-components-context-menu.pngbin26033 -> 22024 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-components-tab-add.pngbin18109 -> 24346 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-components-tab.pngbin11249 -> 14277 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-custom-properties.pngbin19316 -> 26597 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-button-types.pngbin16959 -> 16320 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-image-type.pngbin15492 -> 27166 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-indicator-types.pngbin6191 -> 5075 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-qtquickcontrols-types.pngbin14414 -> 15269 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-rotating-items.pngbin8637 -> 10790 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-scaling-items.pngbin6935 -> 8340 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-selector-types.pngbin11676 -> 11311 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-designer-stacked-view.pngbin104177 -> 103745 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-library-context-menu.pngbin16678 -> 16734 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-positioner-column-properties.pngbin14422 -> 15722 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-positioner-flow-properties.pngbin19188 -> 19764 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-positioner-grid-properties.pngbin20174 -> 24243 bytes
-rw-r--r--doc/qtdesignstudio/images/qtquick-text-editor.pngbin23333 -> 21010 bytes
-rw-r--r--doc/qtdesignstudio/images/repeater3d-listmodel-navigator.pngbin5344 -> 5996 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-2d-effects.pngbin14539 -> 11380 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-area-light.pngbin21349 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-area-light.webpbin0 -> 27570 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-directional-light.pngbin19122 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-directional-light.webpbin0 -> 14130 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-axis-helper.pngbin58091 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-axis-helper.webpbin0 -> 30310 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-move.pngbin38487 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-move.webpbin0 -> 30488 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-rotate.pngbin41596 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-rotate.webpbin0 -> 31534 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-scale.pngbin38694 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor-scale.webpbin0 -> 30344 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor.pngbin41273 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-editor.webpbin0 -> 29906 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-effects.pngbin9034 -> 17846 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-instancing-instance-list.pngbin11023 -> 7671 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-models.pngbin18552 -> 24452 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-assets.pngbin13380 -> 15298 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-components.pngbin12228 -> 13357 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.pngbin7086 -> 8182 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-sprite-template.pngbin12072 -> 10999 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-point-light.pngbin21503 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-point-light.webpbin0 -> 43008 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-spot-light.pngbin25519 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-spot-light.webpbin0 -> 37918 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-animation.pngbin5662 -> 6392 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-connection-view-properties.pngbin5878 -> 3106 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-custom-material-uniform-properties.pngbin5647 -> 3892 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-design-mode-states-timeline.pngbin12629 -> 28166 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-design-mode.pngbin77171 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-design-mode.webpbin0 -> 76408 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-dial.pngbin38697 -> 53036 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-editing-3d-scenes.pngbin46797 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-flipable.pngbin29253 -> 34716 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-and-checkbox3.pngbin24767 -> 27938 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-and.pngbin27108 -> 31579 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-bidirectional-binding.pngbin20359 -> 33054 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-input.pngbin26641 -> 29140 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-string-mapper-input.pngbin24487 -> 28290 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-not-check-box.pngbin26981 -> 33194 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-not.pngbin23491 -> 27477 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-range-mapper-inputmin.pngbin31335 -> 44681 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-logic-helper-string-mapper-text.pngbin20759 -> 36413 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-navigator-view3d.pngbin12276 -> 5890 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-qml-imports-slconnector.pngbin11185 -> 18083 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.pngbin4637 -> 7845 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-qtquick-3d-default-material.pngbin11769 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-qtquick-3d-material.pngbin64943 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-shapes.pngbin15345 -> 19638 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.pngbin13930 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.webpbin0 -> 11086 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-no-tracks.pngbin8096 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-no-tracks.webpbin0 -> 6008 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.pngbin9657 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.webpbin0 -> 6906 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-with-tracks.pngbin13697 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline-with-tracks.webpbin0 -> 10612 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline.pngbin14055 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-timeline.webpbin0 -> 10114 bytes
-rw-r--r--doc/qtdesignstudio/images/timeline-per-property-recording.pngbin6041 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/timeline-per-property-recording.webpbin0 -> 5950 bytes
-rw-r--r--doc/qtdesignstudio/images/timeline-states.pngbin9294 -> 0 bytes
-rw-r--r--doc/qtdesignstudio/images/timeline-states.webpbin0 -> 10436 bytes
-rw-r--r--doc/qtdesignstudio/images/toolbar-show-live-preview.pngbin8359 -> 4996 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-change-file.pngbin0 -> 7619 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-column-layout.pngbin0 -> 15463 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-components-2.pngbin0 -> 5217 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-components.pngbin0 -> 5196 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-new-file.pngbin0 -> 17755 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-page-components.pngbin0 -> 14101 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-page-margins.pngbin0 -> 11333 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-size-binding-2.pngbin0 -> 4485 bytes
-rw-r--r--doc/qtdesignstudio/images/web-navigation-size-binding.pngbin0 -> 4659 bytes
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-buttons.qdoc6
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc2
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc2
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc2
-rw-r--r--doc/qtdesignstudio/src/components/qtquick-positioning.qdoc6
-rw-r--r--doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc289
-rw-r--r--doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc6
-rw-r--r--doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc4
-rw-r--r--doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc3
-rw-r--r--doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc87
-rw-r--r--doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc11
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc33
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc8
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc4
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc46
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc8
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-designer.qdoc157
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc37
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-navigator.qdoc6
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-properties.qdoc22
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc14
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-timeline.qdoc11
-rw-r--r--doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc8
-rw-r--r--doc/qtdesignstudio/src/views/studio-material-editor.qdoc2
-rw-r--r--doc/qtdesignstudio/src/views/studio-texture-editor.qdoc2
-rw-r--r--doc/qtdesignstudio/src/views/studio-translations.qdoc2
-rw-r--r--doc/qtdesignstudio/src/views/studio-workspaces.qdoc2
-rw-r--r--qbs/imports/QtcTestFiles.qbs6
-rw-r--r--qbs/modules/qtc/qtc.qbs6
-rwxr-xr-xscripts/makedmg.py17
-rw-r--r--share/qtcreator/CMakeLists.txt5
-rw-r--r--share/qtcreator/debugger/creatortypes.py4
-rw-r--r--share/qtcreator/debugger/dumper.py32
-rw-r--r--share/qtcreator/debugger/lldbbridge.py11
-rw-r--r--share/qtcreator/debugger/loadorder.txt13
-rw-r--r--share/qtcreator/debugger/pdbbridge.py2
-rw-r--r--share/qtcreator/debugger/utils.py1
-rw-r--r--share/qtcreator/qml-type-descriptions/qt5QtQuick2-bundle.json23
-rw-r--r--share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-macos-bundle.json10
-rw-r--r--share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-win-bundle.json9
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml)130
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml)168
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml)78
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml)118
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFilesDialog.qml)5
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml)3
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/ErrorDialog.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/ErrorDialog.qml)0
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml)31
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml)4
-rw-r--r--share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml (renamed from share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml)12
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml88
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml124
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml4
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml12
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabBar.qml34
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabButton.qml105
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml242
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml11
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml8
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/DownloadPane.qml81
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/TextureProgressBar.qml62
-rw-r--r--share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml3
-rw-r--r--share/qtcreator/qmldesigner/designericons.json150
-rw-r--r--share/qtcreator/qmldesigner/feedback/FeedbackPopup.qml143
-rw-r--r--share/qtcreator/qmldesigner/feedback/star_empty.pngbin0 -> 468 bytes
-rw-r--r--share/qtcreator/qmldesigner/feedback/star_empty@2x.pngbin0 -> 833 bytes
-rw-r--r--share/qtcreator/qmldesigner/feedback/star_filled.pngbin0 -> 424 bytes
-rw-r--r--share/qtcreator/qmldesigner/feedback/star_filled@2x.pngbin0 -> 650 bytes
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml5
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml9
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml191
-rw-r--r--share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml599
-rw-r--r--share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml10
-rw-r--r--share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml36
-rw-r--r--share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureBrowserContextMenu.qml5
-rw-r--r--share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml20
-rw-r--r--share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml3
-rw-r--r--share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml58
-rw-r--r--share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml56
-rw-r--r--share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml14
-rw-r--r--share/qtcreator/qmldesigner/newstateseditor/Main.qml24
-rw-r--r--share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml30
-rw-r--r--share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml2
-rw-r--r--share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml3
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationTargetSection.qml1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml38
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColumnSpecifics.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ConnectionsSpecifics.qml1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml12
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/BusyIndicatorSpecifics.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ButtonSection.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/CheckSection.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ComboBoxSpecifics.qml10
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ContainerSection.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ControlSection.qml8
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DelayButtonSpecifics.qml3
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml15
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/GroupBoxSpecifics.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/IconSection.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/InsetSection.qml10
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ItemDelegateSection.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageIndicatorSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageSpecifics.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSection.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ProgressBarSpecifics.qml10
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RadioDelegateSpecifics.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RangeSliderSpecifics.qml20
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RoundButtonSpecifics.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SpinBoxSpecifics.qml13
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TabBarSpecifics.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolSeparatorSpecifics.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TumblerSpecifics.qml7
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/FlowSpecifics.qml15
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml35
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridViewSpecifics.qml35
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml24
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ListViewSpecifics.qml35
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml26
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PathViewSpecifics.qml29
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RectangleSpecifics.qml12
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RowSpecifics.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml52
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSection.qml64
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSpecifics.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSection.qml83
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSpecifics.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSection.qml66
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSpecifics.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioListenerSpecifics.qml16
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSection.qml277
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSpecifics.qml18
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/NodeSection.qml350
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml234
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSpecifics.qml18
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml41
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLogic.qml29
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml33
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml5
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml81
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml26
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableGeometrySection.qml27
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml19
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontExtrasSection.qml13
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml32
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml16
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml25
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml18
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml50
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OriginControl.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml25
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml12
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextExtrasSection.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml125
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml55
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml25
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonGroup.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonRow.qml45
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml125
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml148
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml207
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml159
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ContextMenu.qml74
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Dialog.qml59
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButton.qml98
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButtonBox.qml45
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml342
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Indicator.qml50
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/InfinityLoopIndicator.qml38
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ItemDelegate.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator2D.qml36
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3D.qml61
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3DComponent.qml34
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml21
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml41
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItemWithIcon.qml34
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml12
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ProgressBar.qml32
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RadioButton.qml93
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSliderPopup.qml38
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml267
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml135
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxInput.qml166
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollBar.qml12
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollView.qml22
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml274
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SecondColumnLayout.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml49
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLabel.qml16
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLayout.qml14
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml187
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SliderPopup.qml52
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml249
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxIndicator.qml120
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml175
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Switch.qml135
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabBar.qml18
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabButton.qml27
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextArea.qml83
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml182
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTip.qml45
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml266
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TranslationIndicator.qml111
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml30
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml4
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml190
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/DefaultStyle.qml6
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml496
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/PrimaryButtonStyle.qml30
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/SearchControlStyle.qml44
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatesControlStyle.qml33
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarButtonStyle.qml34
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarControlStyle.qml33
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ToolbarStyle.qml16
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml34
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml280
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarButtonStyle.qml35
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarControlStyle.qml15
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttfbin29604 -> 57196 bytes
-rwxr-xr-xshare/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir12
-rw-r--r--share/qtcreator/qmldesigner/statusbar/Main.qml85
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject22
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/CMakeLists.txt121
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl22
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/DirectoryFontLoader.qml.tpl34
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/constants_module.qmlproject.tpl13
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json4
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl40
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl3
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl2
-rw-r--r--share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml4
-rw-r--r--share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorToolBar.qml58
-rw-r--r--share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml5
-rw-r--r--share/qtcreator/qmldesigner/toolbar/CrumbleBar.qml93
-rw-r--r--share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml177
-rw-r--r--share/qtcreator/qmldesigner/toolbar/Main.qml403
-rw-r--r--share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml24
-rw-r--r--share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk1
-rw-r--r--share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk2
-rw-r--r--share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk2
-rwxr-xr-xshare/qtcreator/scripts/openTerminal.py112
-rw-r--r--share/qtcreator/snippets/cpp.xml2
-rw-r--r--share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.cpp2
-rw-r--r--share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.h4
-rw-r--r--share/qtcreator/templates/qt4project/customwidgetwizard/tpl_plugin.pro6
-rw-r--r--share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.cpp4
-rw-r--r--share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.h26
-rw-r--r--share/qtcreator/templates/qt4project/customwidgetwizard/tpl_widget.h2
-rw-r--r--share/qtcreator/templates/wizards/autotest/files/tst.pro29
-rw-r--r--share/qtcreator/templates/wizards/autotest/files/tst.qbs39
-rw-r--r--share/qtcreator/templates/wizards/autotest/files/tst.txt27
-rw-r--r--share/qtcreator/templates/wizards/autotest/files/tst_main.cpp11
-rw-r--r--share/qtcreator/templates/wizards/autotest/files/tst_src_boost.cpp13
-rw-r--r--share/qtcreator/templates/wizards/autotest/wizard.json25
-rw-r--r--share/qtcreator/templates/wizards/codesnippet/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/files/markdown/file.md51
-rw-r--r--share/qtcreator/templates/wizards/files/markdown/wizard.json42
-rw-r--r--share/qtcreator/templates/wizards/projects/consoleapp/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json32
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json36
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json108
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json36
-rw-r--r--share/qtcreator/templates/wizards/projects/qtquickapplication/tmpl.qbs21
-rw-r--r--share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json54
-rw-r--r--share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/qtcreatorplugin/CMakeLists.txt17
-rw-r--r--share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h2
-rw-r--r--share/qtcreator/themes/dark.creatortheme131
-rw-r--r--share/qtcreator/themes/default.creatortheme82
-rw-r--r--share/qtcreator/themes/design-light.creatortheme85
-rw-r--r--share/qtcreator/themes/design.creatortheme213
-rw-r--r--share/qtcreator/themes/flat-dark.creatortheme128
-rw-r--r--share/qtcreator/themes/flat-light.creatortheme81
-rw-r--r--share/qtcreator/themes/flat.creatortheme128
-rw-r--r--share/qtcreator/translations/qtcreator_cs.ts8
-rw-r--r--share/qtcreator/translations/qtcreator_da.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_de.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_fr.ts8
-rw-r--r--share/qtcreator/translations/qtcreator_hr.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_ja.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_pl.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_ru.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_sl.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_zh_CN.ts4
-rw-r--r--share/qtcreator/translations/qtcreator_zh_TW.ts4
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/app/main.cpp55
-rw-r--r--src/libs/3rdparty/CMakeLists.txt6
-rw-r--r--src/libs/3rdparty/cplusplus/AST.cpp60
-rw-r--r--src/libs/3rdparty/cplusplus/AST.h160
-rw-r--r--src/libs/3rdparty/cplusplus/ASTClone.cpp97
-rw-r--r--src/libs/3rdparty/cplusplus/ASTMatch0.cpp51
-rw-r--r--src/libs/3rdparty/cplusplus/ASTMatcher.cpp126
-rw-r--r--src/libs/3rdparty/cplusplus/ASTMatcher.h7
-rw-r--r--src/libs/3rdparty/cplusplus/ASTVisit.cpp61
-rw-r--r--src/libs/3rdparty/cplusplus/ASTVisitor.h14
-rw-r--r--src/libs/3rdparty/cplusplus/ASTfwd.h7
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.cpp10
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.h2
-rw-r--r--src/libs/3rdparty/cplusplus/CMakeLists.txt1
-rw-r--r--src/libs/3rdparty/cplusplus/Keywords.cpp104
-rw-r--r--src/libs/3rdparty/cplusplus/Keywords.kwgen10
-rw-r--r--src/libs/3rdparty/cplusplus/Lexer.cpp7
-rw-r--r--src/libs/3rdparty/cplusplus/Parser.cpp304
-rw-r--r--src/libs/3rdparty/cplusplus/Parser.h8
-rw-r--r--src/libs/3rdparty/cplusplus/Symbol.h4
-rw-r--r--src/libs/3rdparty/cplusplus/Token.cpp8
-rw-r--r--src/libs/3rdparty/cplusplus/Token.h8
-rw-r--r--src/libs/3rdparty/libptyqt/.clang-format1
-rw-r--r--src/libs/3rdparty/libptyqt/CMakeLists.txt28
-rw-r--r--src/libs/3rdparty/libptyqt/LICENSE21
-rw-r--r--src/libs/3rdparty/libptyqt/conptyprocess.cpp343
-rw-r--r--src/libs/3rdparty/libptyqt/conptyprocess.h165
-rw-r--r--src/libs/3rdparty/libptyqt/iptyprocess.h56
-rw-r--r--src/libs/3rdparty/libptyqt/ptyqt.cpp45
-rw-r--r--src/libs/3rdparty/libptyqt/ptyqt.h12
-rw-r--r--src/libs/3rdparty/libptyqt/ptyqt.qbs45
-rw-r--r--src/libs/3rdparty/libptyqt/unixptyprocess.cpp374
-rw-r--r--src/libs/3rdparty/libptyqt/unixptyprocess.h66
-rw-r--r--src/libs/3rdparty/libptyqt/winptyprocess.cpp278
-rw-r--r--src/libs/3rdparty/libptyqt/winptyprocess.h43
-rw-r--r--src/libs/3rdparty/libvterm/CMakeLists.txt18
-rw-r--r--src/libs/3rdparty/libvterm/CONTRIBUTING22
-rw-r--r--src/libs/3rdparty/libvterm/LICENSE23
-rw-r--r--src/libs/3rdparty/libvterm/include/vterm.h637
-rw-r--r--src/libs/3rdparty/libvterm/include/vterm_keycodes.h61
-rw-r--r--src/libs/3rdparty/libvterm/src/encoding.c230
-rw-r--r--src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc36
-rw-r--r--src/libs/3rdparty/libvterm/src/encoding/uk.inc6
-rw-r--r--src/libs/3rdparty/libvterm/src/fullwidth.inc111
-rw-r--r--src/libs/3rdparty/libvterm/src/keyboard.c226
-rw-r--r--src/libs/3rdparty/libvterm/src/mouse.c99
-rw-r--r--src/libs/3rdparty/libvterm/src/parser.c402
-rw-r--r--src/libs/3rdparty/libvterm/src/pen.c613
-rw-r--r--src/libs/3rdparty/libvterm/src/rect.h56
-rw-r--r--src/libs/3rdparty/libvterm/src/screen.c1183
-rw-r--r--src/libs/3rdparty/libvterm/src/state.c2315
-rw-r--r--src/libs/3rdparty/libvterm/src/unicode.c313
-rw-r--r--src/libs/3rdparty/libvterm/src/utf8.h39
-rw-r--r--src/libs/3rdparty/libvterm/src/vterm.c429
-rw-r--r--src/libs/3rdparty/libvterm/src/vterm_internal.h296
-rw-r--r--src/libs/3rdparty/libvterm/vterm.pc.in8
-rw-r--r--src/libs/3rdparty/libvterm/vterm.qbs34
-rw-r--r--src/libs/3rdparty/sqlite/carray.c51
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c8960
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h330
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3ext.h12
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp2
-rw-r--r--src/libs/3rdparty/winpty/.gitattributes19
-rw-r--r--src/libs/3rdparty/winpty/.gitignore16
-rw-r--r--src/libs/3rdparty/winpty/CMakeLists.txt1
-rw-r--r--src/libs/3rdparty/winpty/LICENSE21
-rw-r--r--src/libs/3rdparty/winpty/README.md151
-rw-r--r--src/libs/3rdparty/winpty/RELEASES.md280
-rw-r--r--src/libs/3rdparty/winpty/VERSION.txt1
-rw-r--r--src/libs/3rdparty/winpty/appveyor.yml16
-rw-r--r--src/libs/3rdparty/winpty/configure167
-rw-r--r--src/libs/3rdparty/winpty/misc/.gitignore2
-rw-r--r--src/libs/3rdparty/winpty/misc/BufferResizeTests.cc90
-rw-r--r--src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc53
-rw-r--r--src/libs/3rdparty/winpty/misc/ClearConsole.cc72
-rw-r--r--src/libs/3rdparty/winpty/misc/ConinMode.cc117
-rw-r--r--src/libs/3rdparty/winpty/misc/ConinMode.ps1116
-rw-r--r--src/libs/3rdparty/winpty/misc/ConoutMode.cc113
-rw-r--r--src/libs/3rdparty/winpty/misc/DebugClient.py42
-rw-r--r--src/libs/3rdparty/winpty/misc/DebugServer.py63
-rw-r--r--src/libs/3rdparty/winpty/misc/DumpLines.py5
-rw-r--r--src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt46
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt528
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt633
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt16
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt4
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt144
-rw-r--r--src/libs/3rdparty/winpty/misc/FontSurvey.cc100
-rw-r--r--src/libs/3rdparty/winpty/misc/FormatChar.h21
-rw-r--r--src/libs/3rdparty/winpty/misc/FreezePerfTest.cc62
-rw-r--r--src/libs/3rdparty/winpty/misc/GetCh.cc20
-rw-r--r--src/libs/3rdparty/winpty/misc/GetConsolePos.cc41
-rw-r--r--src/libs/3rdparty/winpty/misc/GetFont.cc261
-rw-r--r--src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps151
-rw-r--r--src/libs/3rdparty/winpty/misc/IsNewConsole.cc87
-rw-r--r--src/libs/3rdparty/winpty/misc/MouseInputNotes.txt90
-rw-r--r--src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc34
-rw-r--r--src/libs/3rdparty/winpty/misc/Notes.txt219
-rw-r--r--src/libs/3rdparty/winpty/misc/OSVersion.cc27
-rw-r--r--src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc101
-rw-r--r--src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc671
-rw-r--r--src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc151
-rw-r--r--src/libs/3rdparty/winpty/misc/SelectAllTest.cc45
-rw-r--r--src/libs/3rdparty/winpty/misc/SetBufInfo.cc90
-rw-r--r--src/libs/3rdparty/winpty/misc/SetBufferSize.cc32
-rw-r--r--src/libs/3rdparty/winpty/misc/SetCursorPos.cc10
-rw-r--r--src/libs/3rdparty/winpty/misc/SetFont.cc145
-rw-r--r--src/libs/3rdparty/winpty/misc/SetWindowRect.cc36
-rw-r--r--src/libs/3rdparty/winpty/misc/ShowArgv.cc12
-rw-r--r--src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc40
-rw-r--r--src/libs/3rdparty/winpty/misc/Spew.py5
-rw-r--r--src/libs/3rdparty/winpty/misc/TestUtil.cc172
-rw-r--r--src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc102
-rw-r--r--src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc246
-rw-r--r--src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc130
-rw-r--r--src/libs/3rdparty/winpty/misc/UnixEcho.cc89
-rw-r--r--src/libs/3rdparty/winpty/misc/Utf16Echo.cc46
-rw-r--r--src/libs/3rdparty/winpty/misc/VeryLargeRead.cc122
-rw-r--r--src/libs/3rdparty/winpty/misc/VkEscapeTest.cc56
-rw-r--r--src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc52
-rw-r--r--src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc57
-rw-r--r--src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc30
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Echo1.cc26
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Echo2.cc19
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Test1.cc46
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Test2.cc70
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Test3.cc78
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Write1.cc44
-rw-r--r--src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc27
-rw-r--r--src/libs/3rdparty/winpty/misc/WriteConsole.cc106
-rw-r--r--src/libs/3rdparty/winpty/misc/build32.sh9
-rw-r--r--src/libs/3rdparty/winpty/misc/build64.sh9
-rw-r--r--src/libs/3rdparty/winpty/misc/color-test.sh212
-rw-r--r--src/libs/3rdparty/winpty/misc/font-notes.txt300
-rw-r--r--src/libs/3rdparty/winpty/misc/winbug-15048.cc201
-rw-r--r--src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat36
-rw-r--r--src/libs/3rdparty/winpty/ship/common_ship.py89
-rw-r--r--src/libs/3rdparty/winpty/ship/make_msvc_package.py163
-rw-r--r--src/libs/3rdparty/winpty/ship/ship.py104
-rw-r--r--src/libs/3rdparty/winpty/src/CMakeLists.txt111
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Agent.cc612
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Agent.h103
-rw-r--r--src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc84
-rw-r--r--src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h28
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc698
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleFont.h28
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc852
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInput.h109
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc121
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h36
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc152
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleLine.h41
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Coord.h87
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc239
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DebugShowInput.h32
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc422
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h28
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DsrSender.h30
-rw-r--r--src/libs/3rdparty/winpty/src/agent/EventLoop.cc99
-rw-r--r--src/libs/3rdparty/winpty/src/agent/EventLoop.h47
-rw-r--r--src/libs/3rdparty/winpty/src/agent/InputMap.cc246
-rw-r--r--src/libs/3rdparty/winpty/src/agent/InputMap.h114
-rw-r--r--src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc71
-rw-r--r--src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h68
-rw-r--r--src/libs/3rdparty/winpty/src/agent/NamedPipe.cc378
-rw-r--r--src/libs/3rdparty/winpty/src/agent/NamedPipe.h125
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Scraper.cc699
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Scraper.h103
-rw-r--r--src/libs/3rdparty/winpty/src/agent/SimplePool.h75
-rw-r--r--src/libs/3rdparty/winpty/src/agent/SmallRect.h143
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Terminal.cc535
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Terminal.h69
-rw-r--r--src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h157
-rw-r--r--src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc189
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32Console.cc107
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32Console.h67
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc193
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h99
-rw-r--r--src/libs/3rdparty/winpty/src/agent/main.cc120
-rw-r--r--src/libs/3rdparty/winpty/src/agent/subdir.mk61
-rw-r--r--src/libs/3rdparty/winpty/src/configurations.gypi60
-rw-r--r--src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc117
-rw-r--r--src/libs/3rdparty/winpty/src/debugserver/subdir.mk41
-rw-r--r--src/libs/3rdparty/winpty/src/include/winpty.h242
-rw-r--r--src/libs/3rdparty/winpty/src/include/winpty_constants.h131
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc75
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h28
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h54
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h72
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/subdir.mk46
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/winpty.cc970
-rw-r--r--src/libs/3rdparty/winpty/src/shared/AgentMsg.h38
-rw-r--r--src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc122
-rw-r--r--src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h73
-rw-r--r--src/libs/3rdparty/winpty/src/shared/Buffer.cc103
-rw-r--r--src/libs/3rdparty/winpty/src/shared/Buffer.h102
-rw-r--r--src/libs/3rdparty/winpty/src/shared/DebugClient.cc187
-rw-r--r--src/libs/3rdparty/winpty/src/shared/DebugClient.h38
-rw-r--r--src/libs/3rdparty/winpty/src/shared/GenRandom.cc138
-rw-r--r--src/libs/3rdparty/winpty/src/shared/GenRandom.h55
-rw-r--r--src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat13
-rw-r--r--src/libs/3rdparty/winpty/src/shared/Mutex.h54
-rw-r--r--src/libs/3rdparty/winpty/src/shared/OsModule.h63
-rw-r--r--src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc36
-rw-r--r--src/libs/3rdparty/winpty/src/shared/OwnedHandle.h45
-rw-r--r--src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h43
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringBuilder.h227
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc114
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringUtil.cc55
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringUtil.h80
-rw-r--r--src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h63
-rw-r--r--src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h45
-rw-r--r--src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat20
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc460
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h104
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc252
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsVersion.h29
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc55
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyAssert.h64
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyException.cc57
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyException.h43
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc42
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyVersion.h27
-rw-r--r--src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h99
-rw-r--r--src/libs/3rdparty/winpty/src/subdir.mk5
-rw-r--r--src/libs/3rdparty/winpty/src/tests/subdir.mk28
-rw-r--r--src/libs/3rdparty/winpty/src/tests/trivial_test.cc158
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc114
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h56
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc80
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h53
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/Util.cc86
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/Util.h31
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc70
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h42
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/main.cc729
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk41
-rw-r--r--src/libs/3rdparty/winpty/src/winpty.gyp206
-rw-r--r--src/libs/3rdparty/winpty/vcbuild.bat83
-rw-r--r--src/libs/3rdparty/winpty/winpty.qbs207
-rw-r--r--src/libs/CMakeLists.txt16
-rw-r--r--src/libs/advanceddockingsystem/ads_globals_p.h8
-rw-r--r--src/libs/advanceddockingsystem/dockareatabbar.cpp3
-rw-r--r--src/libs/advanceddockingsystem/dockareatitlebar.cpp7
-rw-r--r--src/libs/advanceddockingsystem/dockareawidget.cpp3
-rw-r--r--src/libs/advanceddockingsystem/dockcontainerwidget.cpp3
-rw-r--r--src/libs/advanceddockingsystem/dockfocuscontroller.cpp19
-rw-r--r--src/libs/advanceddockingsystem/dockmanager.cpp12
-rw-r--r--src/libs/advanceddockingsystem/dockmanager.h2
-rw-r--r--src/libs/advanceddockingsystem/docksplitter.cpp7
-rw-r--r--src/libs/advanceddockingsystem/dockwidget.cpp3
-rw-r--r--src/libs/advanceddockingsystem/dockwidgettab.cpp3
-rw-r--r--src/libs/advanceddockingsystem/floatingdockcontainer.cpp3
-rw-r--r--src/libs/advanceddockingsystem/floatingdragpreview.cpp3
-rw-r--r--src/libs/advanceddockingsystem/workspacedialog.cpp4
-rw-r--r--src/libs/cplusplus/CppDocument.cpp18
-rw-r--r--src/libs/cplusplus/CppDocument.h15
-rw-r--r--src/libs/cplusplus/DependencyTable.cpp18
-rw-r--r--src/libs/cplusplus/DependencyTable.h5
-rw-r--r--src/libs/cplusplus/cplusplus.qbs1
-rw-r--r--src/libs/cplusplus/pp-engine.cpp63
-rw-r--r--src/libs/cplusplus/pp-engine.h1
-rw-r--r--src/libs/extensionsystem/CMakeLists.txt2
-rw-r--r--src/libs/extensionsystem/iplugin.cpp53
-rw-r--r--src/libs/extensionsystem/iplugin.h25
-rw-r--r--src/libs/extensionsystem/plugindetailsview.cpp7
-rw-r--r--src/libs/extensionsystem/pluginerroroverview.cpp2
-rw-r--r--src/libs/extensionsystem/pluginerrorview.cpp7
-rw-r--r--src/libs/extensionsystem/pluginmanager.cpp12
-rw-r--r--src/libs/extensionsystem/pluginmanager.h4
-rw-r--r--src/libs/extensionsystem/pluginmanager_p.h2
-rw-r--r--src/libs/extensionsystem/pluginspec.cpp6
-rw-r--r--src/libs/languageserverprotocol/jsonrpcmessages.h1
-rw-r--r--src/libs/languageserverprotocol/lsptypes.cpp10
-rw-r--r--src/libs/languageserverprotocol/lsptypes.h3
-rw-r--r--src/libs/languageserverprotocol/lsputils.h7
-rw-r--r--src/libs/languageserverprotocol/workspace.h2
-rw-r--r--src/libs/libs.qbs5
m---------src/libs/qlitehtml0
-rw-r--r--src/libs/qmleditorwidgets/contextpanetextwidget.cpp2
-rw-r--r--src/libs/qmleditorwidgets/contextpanewidget.cpp29
-rw-r--r--src/libs/qmleditorwidgets/contextpanewidgetimage.cpp4
-rw-r--r--src/libs/qmleditorwidgets/contextpanewidgetrectangle.cpp2
-rw-r--r--src/libs/qmleditorwidgets/easingpane/easingcontextpane.cpp2
-rw-r--r--src/libs/qmljs/qmljsbundle.cpp19
-rw-r--r--src/libs/qmljs/qmljsbundle.h5
-rw-r--r--src/libs/qmljs/qmljscheck.cpp108
-rw-r--r--src/libs/qmljs/qmljscheck.h8
-rw-r--r--src/libs/qmljs/qmljscodeformatter.cpp7
-rw-r--r--src/libs/qmljs/qmljscodeformatter.h1
-rw-r--r--src/libs/qmljs/qmljslink.cpp2
-rw-r--r--src/libs/qmljs/qmljsmodelmanagerinterface.cpp95
-rw-r--r--src/libs/qmljs/qmljsmodelmanagerinterface.h19
-rw-r--r--src/libs/qmljs/qmljsplugindumper.cpp33
-rw-r--r--src/libs/qmljs/qmljsplugindumper.h6
-rw-r--r--src/libs/qmljs/qmljsreformatter.cpp24
-rw-r--r--src/libs/qmljs/qmljsscopebuilder.cpp1
-rw-r--r--src/libs/qmljs/qmljsscopechain.cpp10
-rw-r--r--src/libs/qmljs/qmljsscopechain.h4
-rw-r--r--src/libs/qmljs/qmljsutils.cpp5
-rw-r--r--src/libs/solutions/CMakeLists.txt1
-rw-r--r--src/libs/solutions/README.md48
-rw-r--r--src/libs/solutions/solutions.qbs7
-rw-r--r--src/libs/solutions/tasking/CMakeLists.txt9
-rw-r--r--src/libs/solutions/tasking/barrier.cpp52
-rw-r--r--src/libs/solutions/tasking/barrier.h97
-rw-r--r--src/libs/solutions/tasking/tasking.qbs14
-rw-r--r--src/libs/solutions/tasking/tasking_global.h14
-rw-r--r--src/libs/solutions/tasking/tasktree.cpp (renamed from src/libs/utils/tasktree.cpp)496
-rw-r--r--src/libs/solutions/tasking/tasktree.h (renamed from src/libs/utils/tasktree.h)151
-rw-r--r--src/libs/sqlite/CMakeLists.txt4
-rw-r--r--src/libs/sqlite/constraints.h2
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp326
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h36
-rw-r--r--src/libs/sqlite/sqlitedatabase.cpp2
-rw-r--r--src/libs/sqlite/sqlitedatabase.h13
-rw-r--r--src/libs/sqlite/sqlitedatabasebackend.cpp180
-rw-r--r--src/libs/sqlite/sqlitedatabasebackend.h24
-rw-r--r--src/libs/sqlite/sqliteexception.cpp823
-rw-r--r--src/libs/sqlite/sqliteexception.h700
-rw-r--r--src/libs/sqlite/sqlitefunctionregistry.cpp50
-rw-r--r--src/libs/sqlite/sqlitefunctionregistry.h12
-rw-r--r--src/libs/sqlite/sqliteindex.h4
-rw-r--r--src/libs/sqlite/sqliteprogresshandler.h31
-rw-r--r--src/libs/sqlite/sqlitereadstatement.h3
-rw-r--r--src/libs/sqlite/sqlitesessionchangeset.cpp14
-rw-r--r--src/libs/sqlite/sqlitesessions.cpp11
-rw-r--r--src/libs/sqlite/sqlitesessions.h12
-rw-r--r--src/libs/sqlite/sqlitetable.h2
-rw-r--r--src/libs/sqlite/sqlitevalue.h4
-rw-r--r--src/libs/sqlite/sqlitewritestatement.h3
-rw-r--r--src/libs/sqlite/sqlstatementbuilder.cpp19
-rw-r--r--src/libs/sqlite/sqlstatementbuilder.h3
-rw-r--r--src/libs/sqlite/sqlstatementbuilderexception.h11
-rw-r--r--src/libs/sqlite/tableconstraints.h2
-rw-r--r--src/libs/tracing/qml/ImageToolButton.qml6
-rw-r--r--src/libs/tracing/qml/MainView.qml2
-rw-r--r--src/libs/tracing/qml/RangeDetails.qml2
-rw-r--r--src/libs/tracing/qml/TimeDisplay.qml2
-rw-r--r--src/libs/tracing/timelinetheme.cpp13
-rw-r--r--src/libs/tracing/timelinetheme.h2
-rw-r--r--src/libs/tracing/timelinetracemanager.cpp36
-rw-r--r--src/libs/utils/CMakeLists.txt31
-rw-r--r--src/libs/utils/archive.cpp8
-rw-r--r--src/libs/utils/archive.h4
-rw-r--r--src/libs/utils/aspects.cpp291
-rw-r--r--src/libs/utils/aspects.h81
-rw-r--r--src/libs/utils/async.cpp55
-rw-r--r--src/libs/utils/async.h215
-rw-r--r--src/libs/utils/asynctask.cpp8
-rw-r--r--src/libs/utils/asynctask.h93
-rw-r--r--src/libs/utils/buildablehelperlibrary.cpp6
-rw-r--r--src/libs/utils/checkablemessagebox.cpp520
-rw-r--r--src/libs/utils/checkablemessagebox.h277
-rw-r--r--src/libs/utils/clangutils.cpp9
-rw-r--r--src/libs/utils/commandline.cpp40
-rw-r--r--src/libs/utils/commandline.h4
-rw-r--r--src/libs/utils/delegates.h8
-rw-r--r--src/libs/utils/devicefileaccess.cpp187
-rw-r--r--src/libs/utils/devicefileaccess.h28
-rw-r--r--src/libs/utils/deviceshell.cpp21
-rw-r--r--src/libs/utils/deviceshell.h6
-rw-r--r--src/libs/utils/differ.cpp25
-rw-r--r--src/libs/utils/differ.h8
-rw-r--r--src/libs/utils/dropsupport.cpp4
-rw-r--r--src/libs/utils/environment.cpp482
-rw-r--r--src/libs/utils/environment.h125
-rw-r--r--src/libs/utils/expected.h15
-rw-r--r--src/libs/utils/fancylineedit.cpp1
-rw-r--r--src/libs/utils/filepath.cpp527
-rw-r--r--src/libs/utils/filepath.h33
-rw-r--r--src/libs/utils/filesearch.cpp87
-rw-r--r--src/libs/utils/filesearch.h59
-rw-r--r--src/libs/utils/filestreamer.cpp512
-rw-r--r--src/libs/utils/filestreamer.h63
-rw-r--r--src/libs/utils/filestreamermanager.cpp201
-rw-r--r--src/libs/utils/filestreamermanager.h42
-rw-r--r--src/libs/utils/filesystemwatcher.cpp61
-rw-r--r--src/libs/utils/fileutils.cpp30
-rw-r--r--src/libs/utils/fileutils.h7
-rw-r--r--src/libs/utils/fileutils_mac.h1
-rw-r--r--src/libs/utils/fileutils_mac.mm11
-rw-r--r--src/libs/utils/filewizardpage.cpp1
-rw-r--r--src/libs/utils/fsengine/diriterator.h3
-rw-r--r--src/libs/utils/fsengine/fileiconprovider.cpp41
-rw-r--r--src/libs/utils/fsengine/fileiteratordevicesappender.h5
-rw-r--r--src/libs/utils/fsengine/fixedlistfsengine.h2
-rw-r--r--src/libs/utils/fsengine/fsengine.cpp51
-rw-r--r--src/libs/utils/fsengine/fsengine.h4
-rw-r--r--src/libs/utils/fsengine/fsengine_impl.cpp2
-rw-r--r--src/libs/utils/fsengine/fsenginehandler.cpp35
-rw-r--r--src/libs/utils/futuresynchronizer.h7
-rw-r--r--src/libs/utils/icon.cpp4
-rw-r--r--src/libs/utils/icon.h2
-rw-r--r--src/libs/utils/images/iconoverlay_close_small.pngbin0 -> 173 bytes
-rw-r--r--src/libs/utils/images/iconoverlay_close_small@2x.pngbin0 -> 249 bytes
-rw-r--r--src/libs/utils/images/pinned_small.pngbin0 -> 145 bytes
-rw-r--r--src/libs/utils/images/pinned_small@2x.pngbin0 -> 233 bytes
-rw-r--r--src/libs/utils/launcherinterface.cpp24
-rw-r--r--src/libs/utils/launcherpackets.cpp6
-rw-r--r--src/libs/utils/launcherpackets.h1
-rw-r--r--src/libs/utils/launchersocket.cpp7
-rw-r--r--src/libs/utils/launchersocket.h2
-rw-r--r--src/libs/utils/layoutbuilder.cpp1050
-rw-r--r--src/libs/utils/layoutbuilder.h314
-rw-r--r--src/libs/utils/linecolumn.cpp43
-rw-r--r--src/libs/utils/linecolumn.h46
-rw-r--r--src/libs/utils/link.cpp8
-rw-r--r--src/libs/utils/link.h22
-rw-r--r--src/libs/utils/macroexpander.cpp78
-rw-r--r--src/libs/utils/multitextcursor.cpp271
-rw-r--r--src/libs/utils/multitextcursor.h70
-rw-r--r--src/libs/utils/namevalueitem.cpp12
-rw-r--r--src/libs/utils/osspecificaspects.h30
-rw-r--r--src/libs/utils/pathchooser.cpp22
-rw-r--r--src/libs/utils/pathchooser.h2
-rw-r--r--src/libs/utils/persistentsettings.cpp6
-rw-r--r--src/libs/utils/persistentsettings.h2
-rw-r--r--src/libs/utils/port.cpp24
-rw-r--r--src/libs/utils/port.h4
-rw-r--r--src/libs/utils/process.cpp (renamed from src/libs/utils/qtcprocess.cpp)429
-rw-r--r--src/libs/utils/process.h (renamed from src/libs/utils/qtcprocess.h)42
-rw-r--r--src/libs/utils/process_ctrlc_stub.cpp10
-rw-r--r--src/libs/utils/process_stub.qbs21
-rw-r--r--src/libs/utils/process_stub_unix.c340
-rw-r--r--src/libs/utils/process_stub_win.c204
-rw-r--r--src/libs/utils/processenums.h7
-rw-r--r--src/libs/utils/processinfo.cpp179
-rw-r--r--src/libs/utils/processinfo.h4
-rw-r--r--src/libs/utils/processinterface.cpp17
-rw-r--r--src/libs/utils/processinterface.h38
-rw-r--r--src/libs/utils/processreaper.cpp2
-rw-r--r--src/libs/utils/processutils.cpp62
-rw-r--r--src/libs/utils/processutils.h3
-rw-r--r--src/libs/utils/projectintropage.cpp1
-rw-r--r--src/libs/utils/qrcparser.h3
-rw-r--r--src/libs/utils/qtcolorbutton.cpp90
-rw-r--r--src/libs/utils/ranges.h95
-rw-r--r--src/libs/utils/runextensions.h109
-rw-r--r--src/libs/utils/scopedtimer.cpp64
-rw-r--r--src/libs/utils/scopedtimer.h38
-rw-r--r--src/libs/utils/searchresultitem.cpp64
-rw-r--r--src/libs/utils/searchresultitem.h102
-rw-r--r--src/libs/utils/settingsaccessor.cpp78
-rw-r--r--src/libs/utils/settingsaccessor.h28
-rw-r--r--src/libs/utils/smallstringlayout.h4
-rw-r--r--src/libs/utils/stringtable.cpp10
-rw-r--r--src/libs/utils/stringutils.cpp105
-rw-r--r--src/libs/utils/stringutils.h18
-rw-r--r--src/libs/utils/styleanimator.cpp (renamed from src/plugins/coreplugin/styleanimator.cpp)6
-rw-r--r--src/libs/utils/styleanimator.h (renamed from src/plugins/coreplugin/styleanimator.h)17
-rw-r--r--src/libs/utils/styledbar.cpp16
-rw-r--r--src/libs/utils/stylehelper.cpp193
-rw-r--r--src/libs/utils/stylehelper.h237
-rw-r--r--src/libs/utils/terminalcommand.cpp8
-rw-r--r--src/libs/utils/terminalhooks.cpp225
-rw-r--r--src/libs/utils/terminalhooks.h91
-rw-r--r--src/libs/utils/terminalinterface.cpp433
-rw-r--r--src/libs/utils/terminalinterface.h61
-rw-r--r--src/libs/utils/terminalprocess.cpp721
-rw-r--r--src/libs/utils/terminalprocess_p.h54
-rw-r--r--src/libs/utils/textutils.cpp130
-rw-r--r--src/libs/utils/textutils.h44
-rw-r--r--src/libs/utils/theme/theme.cpp3
-rw-r--r--src/libs/utils/theme/theme.h46
-rw-r--r--src/libs/utils/utils.qbs31
-rw-r--r--src/libs/utils/utils.qrc4
-rw-r--r--src/libs/utils/utilsicons.cpp4
-rw-r--r--src/libs/utils/utilsicons.h2
-rw-r--r--src/libs/utils/utiltypes.h14
-rw-r--r--src/plugins/CMakeLists.txt7
-rw-r--r--src/plugins/android/android.qbs4
-rw-r--r--src/plugins/android/androidavdmanager.cpp44
-rw-r--r--src/plugins/android/androidavdmanager.h9
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp21
-rw-r--r--src/plugins/android/androidconfigurations.cpp28
-rw-r--r--src/plugins/android/androidcreatekeystorecertificate.cpp7
-rw-r--r--src/plugins/android/androiddebugsupport.cpp2
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp123
-rw-r--r--src/plugins/android/androiddeployqtstep.h83
-rw-r--r--src/plugins/android/androiddevice.cpp24
-rw-r--r--src/plugins/android/androiddevice.h5
-rw-r--r--src/plugins/android/androidmanager.cpp31
-rw-r--r--src/plugins/android/androidmanager.h1
-rw-r--r--src/plugins/android/androidmanifesteditorwidget.cpp4
-rw-r--r--src/plugins/android/androidpackageinstallationstep.cpp10
-rw-r--r--src/plugins/android/androidplugin.cpp8
-rw-r--r--src/plugins/android/androidpotentialkit.cpp24
-rw-r--r--src/plugins/android/androidpotentialkit.h18
-rw-r--r--src/plugins/android/androidqmlpreviewworker.cpp12
-rw-r--r--src/plugins/android/androidrunconfiguration.cpp2
-rw-r--r--src/plugins/android/androidruncontrol.h6
-rw-r--r--src/plugins/android/androidrunnerworker.cpp31
-rw-r--r--src/plugins/android/androidsdkmanager.cpp86
-rw-r--r--src/plugins/android/androidsdkmanagerwidget.cpp12
-rw-r--r--src/plugins/android/androidsettingswidget.cpp12
-rw-r--r--src/plugins/android/androidsignaloperation.cpp6
-rw-r--r--src/plugins/android/androidsignaloperation.h2
-rw-r--r--src/plugins/autotest/autotest.qbs4
-rw-r--r--src/plugins/autotest/autotestplugin.cpp25
-rw-r--r--src/plugins/autotest/autotestplugin.h2
-rw-r--r--src/plugins/autotest/autotestunittests.cpp16
-rw-r--r--src/plugins/autotest/boost/boosttestconfiguration.cpp9
-rw-r--r--src/plugins/autotest/boost/boosttestconfiguration.h3
-rw-r--r--src/plugins/autotest/boost/boosttestframework.h9
-rw-r--r--src/plugins/autotest/boost/boosttestoutputreader.cpp10
-rw-r--r--src/plugins/autotest/boost/boosttestoutputreader.h3
-rw-r--r--src/plugins/autotest/boost/boosttestparser.cpp5
-rw-r--r--src/plugins/autotest/boost/boosttestparser.h2
-rw-r--r--src/plugins/autotest/boost/boosttestsettings.cpp50
-rw-r--r--src/plugins/autotest/boost/boosttestsettings.h19
-rw-r--r--src/plugins/autotest/boost/boosttesttreeitem.cpp10
-rw-r--r--src/plugins/autotest/catch/catchconfiguration.cpp11
-rw-r--r--src/plugins/autotest/catch/catchconfiguration.h3
-rw-r--r--src/plugins/autotest/catch/catchframework.h9
-rw-r--r--src/plugins/autotest/catch/catchoutputreader.cpp9
-rw-r--r--src/plugins/autotest/catch/catchoutputreader.h3
-rw-r--r--src/plugins/autotest/catch/catchtestparser.cpp5
-rw-r--r--src/plugins/autotest/catch/catchtestparser.h2
-rw-r--r--src/plugins/autotest/catch/catchtestsettings.cpp62
-rw-r--r--src/plugins/autotest/catch/catchtestsettings.h18
-rw-r--r--src/plugins/autotest/catch/catchtreeitem.cpp16
-rw-r--r--src/plugins/autotest/ctest/ctestconfiguration.cpp5
-rw-r--r--src/plugins/autotest/ctest/ctestconfiguration.h3
-rw-r--r--src/plugins/autotest/ctest/ctestoutputreader.cpp7
-rw-r--r--src/plugins/autotest/ctest/ctestoutputreader.h5
-rw-r--r--src/plugins/autotest/ctest/ctestsettings.cpp77
-rw-r--r--src/plugins/autotest/ctest/ctestsettings.h18
-rw-r--r--src/plugins/autotest/ctest/ctesttool.h9
-rw-r--r--src/plugins/autotest/ctest/ctesttreeitem.cpp8
-rw-r--r--src/plugins/autotest/gtest/gtestconfiguration.cpp10
-rw-r--r--src/plugins/autotest/gtest/gtestconfiguration.h3
-rw-r--r--src/plugins/autotest/gtest/gtestframework.h9
-rw-r--r--src/plugins/autotest/gtest/gtestoutputreader.cpp13
-rw-r--r--src/plugins/autotest/gtest/gtestoutputreader.h3
-rw-r--r--src/plugins/autotest/gtest/gtestparser.cpp5
-rw-r--r--src/plugins/autotest/gtest/gtestparser.h2
-rw-r--r--src/plugins/autotest/gtest/gtestsettings.cpp60
-rw-r--r--src/plugins/autotest/gtest/gtestsettings.h18
-rw-r--r--src/plugins/autotest/gtest/gtesttreeitem.cpp14
-rw-r--r--src/plugins/autotest/itestparser.cpp6
-rw-r--r--src/plugins/autotest/itestparser.h10
-rw-r--r--src/plugins/autotest/qtest/qttest_utils.cpp12
-rw-r--r--src/plugins/autotest/qtest/qttest_utils.h4
-rw-r--r--src/plugins/autotest/qtest/qttestconfiguration.cpp12
-rw-r--r--src/plugins/autotest/qtest/qttestconfiguration.h3
-rw-r--r--src/plugins/autotest/qtest/qttestframework.h9
-rw-r--r--src/plugins/autotest/qtest/qttestoutputreader.cpp18
-rw-r--r--src/plugins/autotest/qtest/qttestoutputreader.h6
-rw-r--r--src/plugins/autotest/qtest/qttestparser.cpp7
-rw-r--r--src/plugins/autotest/qtest/qttestparser.h4
-rw-r--r--src/plugins/autotest/qtest/qttestsettings.cpp63
-rw-r--r--src/plugins/autotest/qtest/qttestsettings.h18
-rw-r--r--src/plugins/autotest/qtest/qttesttreeitem.cpp16
-rw-r--r--src/plugins/autotest/quick/quicktest_utils.cpp3
-rw-r--r--src/plugins/autotest/quick/quicktest_utils.h2
-rw-r--r--src/plugins/autotest/quick/quicktestconfiguration.cpp15
-rw-r--r--src/plugins/autotest/quick/quicktestconfiguration.h3
-rw-r--r--src/plugins/autotest/quick/quicktestparser.cpp40
-rw-r--r--src/plugins/autotest/quick/quicktestparser.h6
-rw-r--r--src/plugins/autotest/quick/quicktesttreeitem.cpp34
-rw-r--r--src/plugins/autotest/quick/quicktesttreeitem.h2
-rw-r--r--src/plugins/autotest/testcodeparser.cpp185
-rw-r--r--src/plugins/autotest/testcodeparser.h31
-rw-r--r--src/plugins/autotest/testconfiguration.cpp16
-rw-r--r--src/plugins/autotest/testconfiguration.h6
-rw-r--r--src/plugins/autotest/testframeworkmanager.cpp3
-rw-r--r--src/plugins/autotest/testframeworkmanager.h2
-rw-r--r--src/plugins/autotest/testnavigationwidget.cpp11
-rw-r--r--src/plugins/autotest/testoutputreader.cpp19
-rw-r--r--src/plugins/autotest/testoutputreader.h8
-rw-r--r--src/plugins/autotest/testprojectsettings.cpp2
-rw-r--r--src/plugins/autotest/testresultdelegate.cpp7
-rw-r--r--src/plugins/autotest/testresultdelegate.h7
-rw-r--r--src/plugins/autotest/testresultmodel.cpp3
-rw-r--r--src/plugins/autotest/testresultspane.cpp10
-rw-r--r--src/plugins/autotest/testrunner.cpp401
-rw-r--r--src/plugins/autotest/testrunner.h28
-rw-r--r--src/plugins/autotest/testsettings.cpp159
-rw-r--r--src/plugins/autotest/testsettings.h61
-rw-r--r--src/plugins/autotest/testsettingspage.cpp240
-rw-r--r--src/plugins/autotest/testsettingspage.h22
-rw-r--r--src/plugins/autotest/testtreeitem.cpp8
-rw-r--r--src/plugins/autotest/testtreeitem.h2
-rw-r--r--src/plugins/autotest/testtreemodel.cpp46
-rw-r--r--src/plugins/autotest/testtreemodel.h3
-rw-r--r--src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp3
-rw-r--r--src/plugins/autotoolsprojectmanager/makefileparser.cpp6
-rw-r--r--src/plugins/baremetal/baremetaldebugsupport.cpp3
-rw-r--r--src/plugins/baremetal/baremetaldebugsupport.h5
-rw-r--r--src/plugins/baremetal/baremetaldevice.cpp1
-rw-r--r--src/plugins/baremetal/debugserverproviderssettingspage.cpp4
-rw-r--r--src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h5
-rw-r--r--src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp4
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvproject.cpp2
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp5
-rw-r--r--src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h4
-rw-r--r--src/plugins/baremetal/iarewtoolchain.cpp6
-rw-r--r--src/plugins/baremetal/keiltoolchain.cpp8
-rw-r--r--src/plugins/baremetal/sdcctoolchain.cpp6
-rw-r--r--src/plugins/bazaar/bazaarclient.cpp30
-rw-r--r--src/plugins/bazaar/bazaarclient.h4
-rw-r--r--src/plugins/bazaar/bazaarcommitwidget.cpp7
-rw-r--r--src/plugins/bazaar/bazaarplugin.cpp17
-rw-r--r--src/plugins/bazaar/bazaarsettings.cpp39
-rw-r--r--src/plugins/bazaar/bazaarsettings.h8
-rw-r--r--src/plugins/bazaar/pullorpushdialog.cpp2
-rw-r--r--src/plugins/beautifier/CMakeLists.txt1
-rw-r--r--src/plugins/beautifier/abstractsettings.cpp6
-rw-r--r--src/plugins/beautifier/abstractsettings.h3
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp7
-rw-r--r--src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp4
-rw-r--r--src/plugins/beautifier/beautifier.qbs2
-rw-r--r--src/plugins/beautifier/beautifierplugin.cpp20
-rw-r--r--src/plugins/beautifier/clangformat/clangformatoptionspage.cpp2
-rw-r--r--src/plugins/beautifier/configurationdialog.cpp2
-rw-r--r--src/plugins/beautifier/configurationpanel.cpp9
-rw-r--r--src/plugins/beautifier/generaloptionspage.cpp105
-rw-r--r--src/plugins/beautifier/generaloptionspage.h16
-rw-r--r--src/plugins/beautifier/generalsettings.cpp142
-rw-r--r--src/plugins/beautifier/generalsettings.h33
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp4
-rw-r--r--src/plugins/beautifier/uncrustify/uncrustifysettings.cpp4
-rw-r--r--src/plugins/bookmarks/bookmarkfilter.cpp118
-rw-r--r--src/plugins/bookmarks/bookmarkfilter.h11
-rw-r--r--src/plugins/bookmarks/bookmarkmanager.cpp15
-rw-r--r--src/plugins/bookmarks/bookmarksplugin.cpp10
-rw-r--r--src/plugins/boot2qt/device-detection/qdbwatcher.cpp4
-rw-r--r--src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp2
-rw-r--r--src/plugins/boot2qt/qdbdevice.cpp15
-rw-r--r--src/plugins/boot2qt/qdbdevicedebugsupport.cpp12
-rw-r--r--src/plugins/boot2qt/qdbmakedefaultappstep.cpp68
-rw-r--r--src/plugins/boot2qt/qdbplugin.cpp34
-rw-r--r--src/plugins/boot2qt/qdbstopapplicationstep.cpp67
-rw-r--r--src/plugins/clangcodemodel/CMakeLists.txt1
-rw-r--r--src/plugins/clangcodemodel/clangcodemodel.qbs6
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.cpp47
-rw-r--r--src/plugins/clangcodemodel/clangcodemodelplugin.h2
-rw-r--r--src/plugins/clangcodemodel/clangdast.cpp14
-rw-r--r--src/plugins/clangcodemodel/clangdast.h1
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp49
-rw-r--r--src/plugins/clangcodemodel/clangdclient.h6
-rw-r--r--src/plugins/clangcodemodel/clangdfindreferences.cpp30
-rw-r--r--src/plugins/clangcodemodel/clangdfindreferences.h4
-rw-r--r--src/plugins/clangcodemodel/clangdlocatorfilters.cpp375
-rw-r--r--src/plugins/clangcodemodel/clangdlocatorfilters.h50
-rw-r--r--src/plugins/clangcodemodel/clangdsemantichighlighting.cpp24
-rw-r--r--src/plugins/clangcodemodel/clangdsemantichighlighting.h8
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp206
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.h2
-rw-r--r--src/plugins/clangcodemodel/clangtextmark.cpp9
-rw-r--r--src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp729
-rw-r--r--src/plugins/clangcodemodel/test/clangbatchfileprocessor.h16
-rw-r--r--src/plugins/clangcodemodel/test/clangdtests.cpp89
-rw-r--r--src/plugins/clangcodemodel/test/clangdtests.h4
-rw-r--r--src/plugins/clangformat/CMakeLists.txt1
-rw-r--r--src/plugins/clangformat/clangformat.qbs4
-rw-r--r--src/plugins/clangformat/clangformatbaseindenter.cpp27
-rw-r--r--src/plugins/clangformat/clangformatchecks.cpp2
-rw-r--r--src/plugins/clangformat/clangformatconfigwidget.cpp1
-rw-r--r--src/plugins/clangformat/clangformatfile.h3
-rw-r--r--src/plugins/clangformat/clangformatglobalconfigwidget.cpp40
-rw-r--r--src/plugins/clangformat/clangformatindenter.cpp2
-rw-r--r--src/plugins/clangformat/clangformatutils.cpp8
-rw-r--r--src/plugins/clangtools/clangtool.cpp24
-rw-r--r--src/plugins/clangtools/clangtoolruncontrol.cpp11
-rw-r--r--src/plugins/clangtools/clangtoolruncontrol.h4
-rw-r--r--src/plugins/clangtools/clangtoolrunner.cpp59
-rw-r--r--src/plugins/clangtools/clangtoolrunner.h10
-rw-r--r--src/plugins/clangtools/clangtools.qbs4
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp8
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticview.cpp5
-rw-r--r--src/plugins/clangtools/clangtoolsplugin.cpp3
-rw-r--r--src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp24
-rw-r--r--src/plugins/clangtools/clangtoolsprojectsettings.cpp2
-rw-r--r--src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp7
-rw-r--r--src/plugins/clangtools/clangtoolssettings.cpp17
-rw-r--r--src/plugins/clangtools/clangtoolssettings.h6
-rw-r--r--src/plugins/clangtools/clangtoolsunittests.cpp1
-rw-r--r--src/plugins/clangtools/clangtoolsutils.cpp29
-rw-r--r--src/plugins/clangtools/clangtoolsutils.h3
-rw-r--r--src/plugins/clangtools/diagnosticconfigswidget.cpp75
-rw-r--r--src/plugins/clangtools/diagnosticconfigswidget.h1
-rw-r--r--src/plugins/clangtools/documentclangtoolrunner.cpp23
-rw-r--r--src/plugins/clangtools/documentclangtoolrunner.h4
-rw-r--r--src/plugins/clangtools/executableinfo.cpp4
-rw-r--r--src/plugins/clangtools/filterdialog.cpp2
-rw-r--r--src/plugins/clangtools/runsettingswidget.cpp17
-rw-r--r--src/plugins/clangtools/runsettingswidget.h1
-rw-r--r--src/plugins/clangtools/settingswidget.cpp3
-rw-r--r--src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt2
-rw-r--r--src/plugins/classview/classviewmanager.cpp13
-rw-r--r--src/plugins/classview/classviewparsertreeitem.cpp8
-rw-r--r--src/plugins/clearcase/checkoutdialog.cpp4
-rw-r--r--src/plugins/clearcase/clearcaseplugin.cpp32
-rw-r--r--src/plugins/clearcase/clearcasesubmiteditorwidget.cpp2
-rw-r--r--src/plugins/clearcase/clearcasesync.cpp37
-rw-r--r--src/plugins/clearcase/clearcasesync.h9
-rw-r--r--src/plugins/clearcase/settingspage.cpp6
-rw-r--r--src/plugins/clearcase/versionselector.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt136
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md4
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx241
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h115
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.c2835
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h69
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l548
-rw-r--r--src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h88
-rw-r--r--src/plugins/cmakeprojectmanager/CMakeLists.txt6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp83
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildstep.cpp123
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildstep.h7
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp538
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildsystem.h31
-rw-r--r--src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp5
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatter.cpp198
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatter.h31
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp101
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h17
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp135
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeformattersettings.h58
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp9
-rw-r--r--src/plugins/cmakeprojectmanager/cmakekitinformation.cpp77
-rw-r--r--src/plugins/cmakeprojectmanager/cmakekitinformation.h5
-rw-r--r--src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp190
-rw-r--r--src/plugins/cmakeprojectmanager/cmakelocatorfilter.h35
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.cpp12
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprocess.h4
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.cpp43
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectconstants.h1
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp247
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectimporter.h10
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp93
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.h2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs18
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp13
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectnodes.h6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp82
-rw-r--r--src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp79
-rw-r--r--src/plugins/cmakeprojectmanager/cmakespecificsettings.h18
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.cpp8
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.h4
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp7
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp61
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.h2
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.cpp7
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.h8
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.cpp26
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.h2
-rw-r--r--src/plugins/cmakeprojectmanager/presetsmacros.cpp55
-rw-r--r--src/plugins/cmakeprojectmanager/presetsparser.cpp14
-rw-r--r--src/plugins/cmakeprojectmanager/projecttreehelper.cpp33
-rw-r--r--src/plugins/cmakeprojectmanager/projecttreehelper.h2
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp25
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs4
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp4
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp4
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp29
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h8
-rw-r--r--src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp6
-rw-r--r--src/plugins/conan/conaninstallstep.cpp2
-rw-r--r--src/plugins/conan/conanplugin.cpp8
-rw-r--r--src/plugins/conan/conansettings.cpp1
-rw-r--r--src/plugins/conan/conansettings.h2
-rw-r--r--src/plugins/copilot/CMakeLists.txt17
-rw-r--r--src/plugins/copilot/Copilot.json.in19
-rw-r--r--src/plugins/copilot/authwidget.cpp155
-rw-r--r--src/plugins/copilot/authwidget.h48
-rw-r--r--src/plugins/copilot/copilot.qbs33
-rw-r--r--src/plugins/copilot/copilot.qrc6
-rw-r--r--src/plugins/copilot/copilotclient.cpp217
-rw-r--r--src/plugins/copilot/copilotclient.h60
-rw-r--r--src/plugins/copilot/copilothoverhandler.cpp154
-rw-r--r--src/plugins/copilot/copilothoverhandler.h32
-rw-r--r--src/plugins/copilot/copilotoptionspage.cpp99
-rw-r--r--src/plugins/copilot/copilotoptionspage.h18
-rw-r--r--src/plugins/copilot/copilotplugin.cpp64
-rw-r--r--src/plugins/copilot/copilotplugin.h30
-rw-r--r--src/plugins/copilot/copilotsettings.cpp69
-rw-r--r--src/plugins/copilot/copilotsettings.h22
-rw-r--r--src/plugins/copilot/copilotsuggestion.cpp79
-rw-r--r--src/plugins/copilot/copilotsuggestion.h32
-rw-r--r--src/plugins/copilot/copilottr.h15
-rw-r--r--src/plugins/copilot/images/settingscategory_copilot.pngbin0 -> 219 bytes
-rw-r--r--src/plugins/copilot/images/settingscategory_copilot@2x.pngbin0 -> 404 bytes
-rw-r--r--src/plugins/copilot/requests/checkstatus.h54
-rw-r--r--src/plugins/copilot/requests/getcompletions.h119
-rw-r--r--src/plugins/copilot/requests/signinconfirm.h36
-rw-r--r--src/plugins/copilot/requests/signininitiate.h43
-rw-r--r--src/plugins/copilot/requests/signout.h26
-rw-r--r--src/plugins/coreplugin/CMakeLists.txt5
-rw-r--r--src/plugins/coreplugin/actionmanager/actionmanager.cpp4
-rw-r--r--src/plugins/coreplugin/actionmanager/commandmappings.h2
-rw-r--r--src/plugins/coreplugin/actionsfilter.cpp139
-rw-r--r--src/plugins/coreplugin/actionsfilter.h17
-rw-r--r--src/plugins/coreplugin/coreconstants.h2
-rw-r--r--src/plugins/coreplugin/coreplugin.cpp9
-rw-r--r--src/plugins/coreplugin/coreplugin.h2
-rw-r--r--src/plugins/coreplugin/coreplugin.qbs12
-rw-r--r--src/plugins/coreplugin/dialogs/addtovcsdialog.cpp4
-rw-r--r--src/plugins/coreplugin/dialogs/externaltoolconfig.cpp8
-rw-r--r--src/plugins/coreplugin/dialogs/ioptionspage.cpp37
-rw-r--r--src/plugins/coreplugin/dialogs/ioptionspage.h43
-rw-r--r--src/plugins/coreplugin/dialogs/openwithdialog.cpp2
-rw-r--r--src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp8
-rw-r--r--src/plugins/coreplugin/dialogs/saveitemsdialog.cpp2
-rw-r--r--src/plugins/coreplugin/dialogs/settingsdialog.cpp50
-rw-r--r--src/plugins/coreplugin/dialogs/shortcutsettings.cpp57
-rw-r--r--src/plugins/coreplugin/dialogs/shortcutsettings.h13
-rw-r--r--src/plugins/coreplugin/editormanager/documentmodel.cpp108
-rw-r--r--src/plugins/coreplugin/editormanager/documentmodel_p.h12
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.cpp34
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.h22
-rw-r--r--src/plugins/coreplugin/editortoolbar.cpp13
-rw-r--r--src/plugins/coreplugin/externaltool.cpp10
-rw-r--r--src/plugins/coreplugin/externaltool.h4
-rw-r--r--src/plugins/coreplugin/fancyactionbar.cpp17
-rw-r--r--src/plugins/coreplugin/fileutils.cpp18
-rw-r--r--src/plugins/coreplugin/find/findtoolbar.cpp7
-rw-r--r--src/plugins/coreplugin/find/findtoolwindow.cpp3
-rw-r--r--src/plugins/coreplugin/find/itemviewfind.cpp11
-rw-r--r--src/plugins/coreplugin/find/searchresultcolor.h52
-rw-r--r--src/plugins/coreplugin/find/searchresultitem.h132
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitems.cpp11
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitems.h13
-rw-r--r--src/plugins/coreplugin/find/searchresulttreemodel.cpp26
-rw-r--r--src/plugins/coreplugin/find/searchresulttreemodel.h6
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeview.cpp7
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeview.h8
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.cpp8
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.h15
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.cpp62
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.h23
-rw-r--r--src/plugins/coreplugin/foldernavigationwidget.cpp3
-rw-r--r--src/plugins/coreplugin/generalsettings.cpp45
-rw-r--r--src/plugins/coreplugin/generalsettings.h2
-rw-r--r--src/plugins/coreplugin/icore.cpp2
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.cpp276
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.h65
-rw-r--r--src/plugins/coreplugin/locator/commandlocator.cpp128
-rw-r--r--src/plugins/coreplugin/locator/commandlocator.h21
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.cpp139
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.h22
-rw-r--r--src/plugins/coreplugin/locator/executefilter.cpp140
-rw-r--r--src/plugins/coreplugin/locator/executefilter.h25
-rw-r--r--src/plugins/coreplugin/locator/externaltoolsfilter.cpp99
-rw-r--r--src/plugins/coreplugin/locator/externaltoolsfilter.h15
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.cpp377
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.h32
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.cpp980
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.h173
-rw-r--r--src/plugins/coreplugin/locator/javascriptfilter.cpp486
-rw-r--r--src/plugins/coreplugin/locator/javascriptfilter.h31
-rw-r--r--src/plugins/coreplugin/locator/locator.cpp24
-rw-r--r--src/plugins/coreplugin/locator/locator.h7
-rw-r--r--src/plugins/coreplugin/locator/locator_test.cpp28
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltersfilter.cpp91
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltersfilter.h21
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltertest.cpp43
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltertest.h18
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.cpp35
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.h16
-rw-r--r--src/plugins/coreplugin/locator/locatorsettingspage.cpp124
-rw-r--r--src/plugins/coreplugin/locator/locatorsettingspage.h8
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.cpp341
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.h29
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.cpp129
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.h37
-rw-r--r--src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp249
-rw-r--r--src/plugins/coreplugin/locator/spotlightlocatorfilter.h18
-rw-r--r--src/plugins/coreplugin/locator/urllocatorfilter.cpp72
-rw-r--r--src/plugins/coreplugin/locator/urllocatorfilter.h22
-rw-r--r--src/plugins/coreplugin/mainwindow.cpp73
-rw-r--r--src/plugins/coreplugin/manhattanstyle.cpp901
-rw-r--r--src/plugins/coreplugin/manhattanstyle.h22
-rw-r--r--src/plugins/coreplugin/mimetypemagicdialog.cpp2
-rw-r--r--src/plugins/coreplugin/mimetypesettings.cpp55
-rw-r--r--src/plugins/coreplugin/mimetypesettings.h7
-rw-r--r--src/plugins/coreplugin/minisplitter.cpp4
-rw-r--r--src/plugins/coreplugin/navigationsubwidget.cpp3
-rw-r--r--src/plugins/coreplugin/outputpanemanager.cpp28
-rw-r--r--src/plugins/coreplugin/outputpanemanager.h1
-rw-r--r--src/plugins/coreplugin/patchtool.cpp4
-rw-r--r--src/plugins/coreplugin/plugindialog.cpp1
-rw-r--r--src/plugins/coreplugin/plugininstallwizard.cpp26
-rw-r--r--src/plugins/coreplugin/progressmanager/processprogress.cpp24
-rw-r--r--src/plugins/coreplugin/progressmanager/processprogress.h4
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager.cpp29
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager.h2
-rw-r--r--src/plugins/coreplugin/progressmanager/progressview.cpp42
-rw-r--r--src/plugins/coreplugin/progressmanager/progressview.h1
-rw-r--r--src/plugins/coreplugin/progressmanager/taskprogress.cpp4
-rw-r--r--src/plugins/coreplugin/progressmanager/taskprogress.h4
-rw-r--r--src/plugins/coreplugin/statusbarmanager.cpp1
-rw-r--r--src/plugins/coreplugin/systemsettings.cpp21
-rw-r--r--src/plugins/coreplugin/welcomepagehelper.cpp215
-rw-r--r--src/plugins/coreplugin/welcomepagehelper.h52
-rw-r--r--src/plugins/cpaster/cpasterplugin.cpp4
-rw-r--r--src/plugins/cpaster/fileshareprotocol.cpp20
-rw-r--r--src/plugins/cpaster/fileshareprotocol.h5
-rw-r--r--src/plugins/cpaster/fileshareprotocolsettingspage.cpp22
-rw-r--r--src/plugins/cpaster/fileshareprotocolsettingspage.h10
-rw-r--r--src/plugins/cpaster/pasteselectdialog.cpp2
-rw-r--r--src/plugins/cpaster/pasteview.cpp5
-rw-r--r--src/plugins/cpaster/protocol.cpp2
-rw-r--r--src/plugins/cpaster/protocol.h2
-rw-r--r--src/plugins/cpaster/settings.cpp4
-rw-r--r--src/plugins/cpaster/stickynotespasteprotocol.h1
-rw-r--r--src/plugins/cppcheck/cppcheckconstants.h17
-rw-r--r--src/plugins/cppcheck/cppcheckmanualrundialog.cpp19
-rw-r--r--src/plugins/cppcheck/cppcheckmanualrundialog.h8
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.cpp295
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.h95
-rw-r--r--src/plugins/cppcheck/cppcheckplugin.cpp34
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.cpp6
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.h4
-rw-r--r--src/plugins/cppcheck/cppchecktool.cpp51
-rw-r--r--src/plugins/cppcheck/cppchecktool.h8
-rw-r--r--src/plugins/cppcheck/cppchecktrigger.cpp4
-rw-r--r--src/plugins/cppeditor/CMakeLists.txt2
-rw-r--r--src/plugins/cppeditor/baseeditordocumentparser.cpp9
-rw-r--r--src/plugins/cppeditor/baseeditordocumentparser.h11
-rw-r--r--src/plugins/cppeditor/baseeditordocumentprocessor.cpp19
-rw-r--r--src/plugins/cppeditor/baseeditordocumentprocessor.h7
-rw-r--r--src/plugins/cppeditor/builtincursorinfo.cpp6
-rw-r--r--src/plugins/cppeditor/builtineditordocumentparser.cpp10
-rw-r--r--src/plugins/cppeditor/builtineditordocumentparser.h3
-rw-r--r--src/plugins/cppeditor/builtineditordocumentprocessor.cpp42
-rw-r--r--src/plugins/cppeditor/clangdiagnosticconfig.h4
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.cpp7
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder.h1
-rw-r--r--src/plugins/cppeditor/compileroptionsbuilder_test.cpp3
-rw-r--r--src/plugins/cppeditor/cppcodeformatter.cpp2
-rw-r--r--src/plugins/cppeditor/cppcodemodelinspectordialog.cpp21
-rw-r--r--src/plugins/cppeditor/cppcodemodelinspectordumper.cpp8
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettings.cpp5
-rw-r--r--src/plugins/cppeditor/cppcodemodelsettingspage.cpp5
-rw-r--r--src/plugins/cppeditor/cppcodestylesettingspage.cpp82
-rw-r--r--src/plugins/cppeditor/cppcodestylesettingspage.h8
-rw-r--r--src/plugins/cppeditor/cppcompletion_test.cpp16
-rw-r--r--src/plugins/cppeditor/cppcurrentdocumentfilter.cpp169
-rw-r--r--src/plugins/cppeditor/cppcurrentdocumentfilter.h49
-rw-r--r--src/plugins/cppeditor/cppeditor.qbs11
-rw-r--r--src/plugins/cppeditor/cppeditorconstants.h8
-rw-r--r--src/plugins/cppeditor/cppeditoroutline.cpp3
-rw-r--r--src/plugins/cppeditor/cppeditorplugin.cpp6
-rw-r--r--src/plugins/cppeditor/cppeditorwidget.cpp11
-rw-r--r--src/plugins/cppeditor/cppelementevaluator.cpp43
-rw-r--r--src/plugins/cppeditor/cppelementevaluator.h10
-rw-r--r--src/plugins/cppeditor/cppfindreferences.cpp95
-rw-r--r--src/plugins/cppeditor/cppfindreferences.h19
-rw-r--r--src/plugins/cppeditor/cppfunctiondecldeflink.cpp4
-rw-r--r--src/plugins/cppeditor/cpphighlighter.cpp88
-rw-r--r--src/plugins/cppeditor/cpphighlighter.h1
-rw-r--r--src/plugins/cppeditor/cppincludehierarchy.cpp2
-rw-r--r--src/plugins/cppeditor/cppincludesfilter.cpp149
-rw-r--r--src/plugins/cppeditor/cppincludesfilter.h14
-rw-r--r--src/plugins/cppeditor/cppindexingsupport.cpp65
-rw-r--r--src/plugins/cppeditor/cppindexingsupport.h7
-rw-r--r--src/plugins/cppeditor/cpplocatordata.cpp16
-rw-r--r--src/plugins/cppeditor/cpplocatordata.h2
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter.cpp395
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter.h53
-rw-r--r--src/plugins/cppeditor/cpplocatorfilter_test.cpp127
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.cpp317
-rw-r--r--src/plugins/cppeditor/cppmodelmanager.h2
-rw-r--r--src/plugins/cppeditor/cppmodelmanager_test.cpp12
-rw-r--r--src/plugins/cppeditor/cppoutline.cpp10
-rw-r--r--src/plugins/cppeditor/cppoutlinemodel.cpp28
-rw-r--r--src/plugins/cppeditor/cppoutlinemodel.h6
-rw-r--r--src/plugins/cppeditor/cppprojectinfogenerator.cpp16
-rw-r--r--src/plugins/cppeditor/cppprojectinfogenerator.h11
-rw-r--r--src/plugins/cppeditor/cppprojectupdater.cpp24
-rw-r--r--src/plugins/cppeditor/cppprojectupdater.h4
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.cpp263
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.h4
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp596
-rw-r--r--src/plugins/cppeditor/cppquickfixes.h53
-rw-r--r--src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp2
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingspage.cpp26
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingspage.h18
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingswidget.cpp21
-rw-r--r--src/plugins/cppeditor/cppquickfixsettingswidget.h7
-rw-r--r--src/plugins/cppeditor/cpprefactoringchanges.cpp4
-rw-r--r--src/plugins/cppeditor/cppsemanticinfoupdater.cpp17
-rw-r--r--src/plugins/cppeditor/cppsourceprocessor.cpp18
-rw-r--r--src/plugins/cppeditor/cpptoolsjsextension.cpp11
-rw-r--r--src/plugins/cppeditor/cpptoolsreuse.cpp14
-rw-r--r--src/plugins/cppeditor/cpptoolssettings.cpp48
-rw-r--r--src/plugins/cppeditor/cpptoolstestcase.cpp10
-rw-r--r--src/plugins/cppeditor/cpptypehierarchy.cpp5
-rw-r--r--src/plugins/cppeditor/cppworkingcopy.cpp14
-rw-r--r--src/plugins/cppeditor/cppworkingcopy.h11
-rw-r--r--src/plugins/cppeditor/doxygengenerator.cpp12
-rw-r--r--src/plugins/cppeditor/editordocumenthandle.cpp5
-rw-r--r--src/plugins/cppeditor/editordocumenthandle.h4
-rw-r--r--src/plugins/cppeditor/fileandtokenactions_test.cpp2
-rw-r--r--src/plugins/cppeditor/generatedcodemodelsupport.cpp2
-rw-r--r--src/plugins/cppeditor/generatedcodemodelsupport.h2
-rw-r--r--src/plugins/cppeditor/indexitem.cpp4
-rw-r--r--src/plugins/cppeditor/indexitem.h5
-rw-r--r--src/plugins/cppeditor/insertionpointlocator.h26
-rw-r--r--src/plugins/cppeditor/modelmanagertesthelper.cpp6
-rw-r--r--src/plugins/cppeditor/projectinfo_test.cpp6
-rw-r--r--src/plugins/cppeditor/searchsymbols.cpp3
-rw-r--r--src/plugins/cppeditor/senddocumenttracker.cpp236
-rw-r--r--src/plugins/cppeditor/senddocumenttracker.h67
-rw-r--r--src/plugins/cppeditor/symbolsearcher_test.cpp12
-rw-r--r--src/plugins/cppeditor/symbolsfindfilter.cpp15
-rw-r--r--src/plugins/cppeditor/symbolsfindfilter.h10
-rw-r--r--src/plugins/cppeditor/testcases/highlightingtestcase.cpp12
-rw-r--r--src/plugins/cppeditor/typehierarchybuilder.cpp28
-rw-r--r--src/plugins/cppeditor/typehierarchybuilder.h12
-rw-r--r--src/plugins/ctfvisualizer/ctfvisualizertool.cpp4
-rw-r--r--src/plugins/cvs/cvsplugin.cpp42
-rw-r--r--src/plugins/cvs/cvssettings.cpp61
-rw-r--r--src/plugins/cvs/cvssettings.h12
-rw-r--r--src/plugins/debugger/CMakeLists.txt1
-rw-r--r--src/plugins/debugger/breakhandler.cpp21
-rw-r--r--src/plugins/debugger/breakhandler.h2
-rw-r--r--src/plugins/debugger/breakpoint.cpp7
-rw-r--r--src/plugins/debugger/breakpoint.h2
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp119
-rw-r--r--src/plugins/debugger/cdb/cdbengine.h14
-rw-r--r--src/plugins/debugger/commonoptionspage.cpp51
-rw-r--r--src/plugins/debugger/dap/dapengine.cpp799
-rw-r--r--src/plugins/debugger/dap/dapengine.h98
-rw-r--r--src/plugins/debugger/debugger.qbs12
-rw-r--r--src/plugins/debugger/debugger.qrc1
-rw-r--r--src/plugins/debugger/debuggeractions.cpp10
-rw-r--r--src/plugins/debugger/debuggeractions.h3
-rw-r--r--src/plugins/debugger/debuggerconstants.h1
-rw-r--r--src/plugins/debugger/debuggerdialogs.cpp1
-rw-r--r--src/plugins/debugger/debuggerengine.cpp59
-rw-r--r--src/plugins/debugger/debuggerengine.h15
-rw-r--r--src/plugins/debugger/debuggeritem.cpp30
-rw-r--r--src/plugins/debugger/debuggeritem.h5
-rw-r--r--src/plugins/debugger/debuggeritemmanager.cpp136
-rw-r--r--src/plugins/debugger/debuggerkitinformation.cpp100
-rw-r--r--src/plugins/debugger/debuggerkitinformation.h1
-rw-r--r--src/plugins/debugger/debuggermainwindow.cpp14
-rw-r--r--src/plugins/debugger/debuggerplugin.cpp28
-rw-r--r--src/plugins/debugger/debuggerrunconfigurationaspect.cpp223
-rw-r--r--src/plugins/debugger/debuggerrunconfigurationaspect.h6
-rw-r--r--src/plugins/debugger/debuggerruncontrol.cpp70
-rw-r--r--src/plugins/debugger/debuggersourcepathmappingwidget.cpp8
-rw-r--r--src/plugins/debugger/debuggertooltipmanager.cpp6
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp263
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h24
-rw-r--r--src/plugins/debugger/images/pin.xpm19
-rw-r--r--src/plugins/debugger/lldb/lldbengine.cpp37
-rw-r--r--src/plugins/debugger/lldb/lldbengine.h8
-rw-r--r--src/plugins/debugger/loadcoredialog.cpp18
-rw-r--r--src/plugins/debugger/moduleshandler.cpp32
-rw-r--r--src/plugins/debugger/moduleshandler.h6
-rw-r--r--src/plugins/debugger/pdb/pdbengine.cpp20
-rw-r--r--src/plugins/debugger/pdb/pdbengine.h8
-rw-r--r--src/plugins/debugger/qml/qmlengine.cpp32
-rw-r--r--src/plugins/debugger/qml/qmlengine.h6
-rw-r--r--src/plugins/debugger/qml/qmlengineutils.cpp8
-rw-r--r--src/plugins/debugger/qml/qmlengineutils.h4
-rw-r--r--src/plugins/debugger/qml/qmlinspectoragent.cpp7
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp2
-rw-r--r--src/plugins/debugger/sourcefileshandler.cpp14
-rw-r--r--src/plugins/debugger/sourcefileshandler.h6
-rw-r--r--src/plugins/debugger/stackframe.cpp2
-rw-r--r--src/plugins/debugger/stackhandler.cpp2
-rw-r--r--src/plugins/debugger/terminal.cpp11
-rw-r--r--src/plugins/debugger/terminal.h4
-rw-r--r--src/plugins/debugger/uvsc/uvscengine.cpp2
-rw-r--r--src/plugins/debugger/watchdata.cpp14
-rw-r--r--src/plugins/debugger/watchdata.h2
-rw-r--r--src/plugins/debugger/watchhandler.cpp75
-rw-r--r--src/plugins/debugger/watchhandler.h1
-rw-r--r--src/plugins/designer/CMakeLists.txt2
-rw-r--r--src/plugins/designer/codemodelhelpers.cpp4
-rw-r--r--src/plugins/designer/cpp/newclasswidget.cpp5
-rw-r--r--src/plugins/designer/designer.qbs6
-rw-r--r--src/plugins/designer/designercontext.cpp4
-rw-r--r--src/plugins/designer/editorwidget.cpp4
-rw-r--r--src/plugins/designer/formeditor.cpp (renamed from src/plugins/designer/formeditorw.cpp)54
-rw-r--r--src/plugins/designer/formeditor.h64
-rw-r--r--src/plugins/designer/formeditorfactory.cpp12
-rw-r--r--src/plugins/designer/formeditorplugin.cpp8
-rw-r--r--src/plugins/designer/formeditorstack.cpp4
-rw-r--r--src/plugins/designer/formeditorw.h69
-rw-r--r--src/plugins/designer/formtemplatewizardpage.cpp4
-rw-r--r--src/plugins/designer/gotoslot_test.cpp4
-rw-r--r--src/plugins/designer/qtcreatorintegration.cpp37
-rw-r--r--src/plugins/designer/resourcehandler.cpp13
-rw-r--r--src/plugins/designer/settingspage.cpp56
-rw-r--r--src/plugins/designer/settingspage.h23
-rw-r--r--src/plugins/diffeditor/diffeditor.cpp85
-rw-r--r--src/plugins/diffeditor/diffeditor.h1
-rw-r--r--src/plugins/diffeditor/diffeditorcontroller.cpp1
-rw-r--r--src/plugins/diffeditor/diffeditorcontroller.h8
-rw-r--r--src/plugins/diffeditor/diffeditordocument.cpp6
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.cpp53
-rw-r--r--src/plugins/diffeditor/diffeditorplugin.h4
-rw-r--r--src/plugins/diffeditor/diffutils.cpp108
-rw-r--r--src/plugins/diffeditor/diffutils.h8
-rw-r--r--src/plugins/diffeditor/diffview.cpp27
-rw-r--r--src/plugins/diffeditor/diffview.h9
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.cpp70
-rw-r--r--src/plugins/diffeditor/sidebysidediffeditorwidget.h27
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.cpp80
-rw-r--r--src/plugins/diffeditor/unifieddiffeditorwidget.h28
-rw-r--r--src/plugins/docker/dockerapi.cpp10
-rw-r--r--src/plugins/docker/dockerdevice.cpp361
-rw-r--r--src/plugins/docker/dockerdevice.h6
-rw-r--r--src/plugins/docker/dockerdevicewidget.cpp5
-rw-r--r--src/plugins/docker/dockerplugin.cpp1
-rw-r--r--src/plugins/docker/dockersettings.cpp49
-rw-r--r--src/plugins/docker/dockersettings.h10
-rw-r--r--src/plugins/fakevim/fakevim.qbs4
-rw-r--r--src/plugins/fakevim/fakevimactions.h3
-rw-r--r--src/plugins/fakevim/fakevimhandler.cpp83
-rw-r--r--src/plugins/fakevim/fakevimhandler.h87
-rw-r--r--src/plugins/fakevim/fakevimplugin.cpp667
-rw-r--r--src/plugins/fossil/configuredialog.cpp2
-rw-r--r--src/plugins/fossil/fossilclient.cpp39
-rw-r--r--src/plugins/fossil/fossilclient.h3
-rw-r--r--src/plugins/fossil/fossilcommitwidget.cpp5
-rw-r--r--src/plugins/fossil/fossilplugin.cpp29
-rw-r--r--src/plugins/fossil/fossilplugin.h3
-rw-r--r--src/plugins/fossil/fossilsettings.cpp113
-rw-r--r--src/plugins/fossil/fossilsettings.h31
-rw-r--r--src/plugins/fossil/pullorpushdialog.cpp2
-rw-r--r--src/plugins/fossil/wizard/fossiljsextension.cpp34
-rw-r--r--src/plugins/fossil/wizard/fossiljsextension.h8
-rw-r--r--src/plugins/genericprojectmanager/genericproject.cpp10
-rw-r--r--src/plugins/git/branchadddialog.cpp2
-rw-r--r--src/plugins/git/branchcheckoutdialog.cpp2
-rw-r--r--src/plugins/git/branchmodel.cpp112
-rw-r--r--src/plugins/git/branchmodel.h3
-rw-r--r--src/plugins/git/branchview.cpp82
-rw-r--r--src/plugins/git/branchview.h4
-rw-r--r--src/plugins/git/changeselectiondialog.cpp10
-rw-r--r--src/plugins/git/changeselectiondialog.h4
-rw-r--r--src/plugins/git/gerrit/gerritmodel.cpp12
-rw-r--r--src/plugins/git/gerrit/gerritoptionspage.cpp167
-rw-r--r--src/plugins/git/gerrit/gerritoptionspage.h49
-rw-r--r--src/plugins/git/gerrit/gerritplugin.cpp37
-rw-r--r--src/plugins/git/gerrit/gerritplugin.h9
-rw-r--r--src/plugins/git/gerrit/gerritpushdialog.cpp4
-rw-r--r--src/plugins/git/gitclient.cpp204
-rw-r--r--src/plugins/git/gitclient.h11
-rw-r--r--src/plugins/git/giteditor.cpp2
-rw-r--r--src/plugins/git/gitgrep.cpp43
-rw-r--r--src/plugins/git/gitgrep.h13
-rw-r--r--src/plugins/git/gitplugin.cpp89
-rw-r--r--src/plugins/git/gitplugin.h3
-rw-r--r--src/plugins/git/gitsettings.cpp95
-rw-r--r--src/plugins/git/gitsettings.h7
-rw-r--r--src/plugins/git/gitsubmiteditor.cpp4
-rw-r--r--src/plugins/git/gitsubmiteditorwidget.cpp5
-rw-r--r--src/plugins/git/logchangedialog.cpp4
-rw-r--r--src/plugins/git/mergetool.cpp4
-rw-r--r--src/plugins/git/mergetool.h4
-rw-r--r--src/plugins/gitlab/gitlabclonedialog.cpp2
-rw-r--r--src/plugins/gitlab/gitlabdialog.cpp4
-rw-r--r--src/plugins/gitlab/gitlaboptionspage.cpp147
-rw-r--r--src/plugins/gitlab/gitlaboptionspage.h75
-rw-r--r--src/plugins/gitlab/gitlabparameters.cpp7
-rw-r--r--src/plugins/gitlab/gitlabparameters.h9
-rw-r--r--src/plugins/gitlab/gitlabplugin.cpp43
-rw-r--r--src/plugins/gitlab/gitlabprojectsettings.cpp2
-rw-r--r--src/plugins/gitlab/queryrunner.cpp2
-rw-r--r--src/plugins/gitlab/queryrunner.h4
-rw-r--r--src/plugins/haskell/CMakeLists.txt3
-rw-r--r--src/plugins/haskell/haskell.qbs2
-rw-r--r--src/plugins/haskell/haskell.qrc3
-rw-r--r--src/plugins/haskell/haskelleditorfactory.cpp14
-rw-r--r--src/plugins/haskell/haskelleditorfactory.h6
-rw-r--r--src/plugins/haskell/haskellmanager.cpp85
-rw-r--r--src/plugins/haskell/haskellmanager.h21
-rw-r--r--src/plugins/haskell/haskellplugin.cpp18
-rw-r--r--src/plugins/haskell/haskellproject.cpp1
-rw-r--r--src/plugins/haskell/haskellrunconfiguration.cpp4
-rw-r--r--src/plugins/haskell/haskellsettings.cpp63
-rw-r--r--src/plugins/haskell/haskellsettings.h20
-rw-r--r--src/plugins/haskell/images/category_haskell.pngbin6264 -> 0 bytes
-rw-r--r--src/plugins/haskell/images/settingscategory_haskell.pngbin0 -> 187 bytes
-rw-r--r--src/plugins/haskell/images/settingscategory_haskell@2x.pngbin0 -> 322 bytes
-rw-r--r--src/plugins/haskell/optionspage.cpp63
-rw-r--r--src/plugins/haskell/optionspage.h31
-rw-r--r--src/plugins/haskell/stackbuildstep.cpp4
-rw-r--r--src/plugins/help/CMakeLists.txt3
-rw-r--r--src/plugins/help/filtersettingspage.cpp74
-rw-r--r--src/plugins/help/filtersettingspage.h29
-rw-r--r--src/plugins/help/generalsettingspage.cpp310
-rw-r--r--src/plugins/help/generalsettingspage.h43
-rw-r--r--src/plugins/help/help.qbs1
-rw-r--r--src/plugins/help/helpindexfilter.cpp123
-rw-r--r--src/plugins/help/helpindexfilter.h34
-rw-r--r--src/plugins/help/helpmanager.cpp19
-rw-r--r--src/plugins/help/helpmanager.h14
-rw-r--r--src/plugins/help/helpmode.cpp24
-rw-r--r--src/plugins/help/helpmode.h21
-rw-r--r--src/plugins/help/helpplugin.cpp70
-rw-r--r--src/plugins/help/helpplugin.h2
-rw-r--r--src/plugins/help/helpwidget.cpp5
-rw-r--r--src/plugins/imageviewer/CMakeLists.txt2
-rw-r--r--src/plugins/imageviewer/imageview.cpp2
-rw-r--r--src/plugins/imageviewer/imageviewer.cpp5
-rw-r--r--src/plugins/incredibuild/cmakecommandbuilder.cpp2
-rw-r--r--src/plugins/incredibuild/commandbuilderaspect.cpp8
-rw-r--r--src/plugins/incredibuild/commandbuilderaspect.h2
-rw-r--r--src/plugins/ios/createsimulatordialog.cpp9
-rw-r--r--src/plugins/ios/iosbuildstep.cpp8
-rw-r--r--src/plugins/ios/iosconfigurations.cpp9
-rw-r--r--src/plugins/ios/iosdevice.cpp6
-rw-r--r--src/plugins/ios/iosdevice.h1
-rw-r--r--src/plugins/ios/iosdsymbuildstep.cpp14
-rw-r--r--src/plugins/ios/iosprobe.cpp4
-rw-r--r--src/plugins/ios/iosrunconfiguration.cpp8
-rw-r--r--src/plugins/ios/iosrunconfiguration.h2
-rw-r--r--src/plugins/ios/iosrunner.cpp2
-rw-r--r--src/plugins/ios/iossettingswidget.cpp23
-rw-r--r--src/plugins/ios/iossimulator.cpp9
-rw-r--r--src/plugins/ios/iossimulator.h1
-rw-r--r--src/plugins/ios/iostoolhandler.cpp52
-rw-r--r--src/plugins/ios/simulatorcontrol.cpp127
-rw-r--r--src/plugins/ios/simulatorcontrol.h6
-rw-r--r--src/plugins/ios/simulatorinfomodel.cpp6
-rw-r--r--src/plugins/ios/simulatoroperationdialog.cpp2
-rw-r--r--src/plugins/languageclient/CMakeLists.txt8
-rw-r--r--src/plugins/languageclient/callhierarchy.cpp48
-rw-r--r--src/plugins/languageclient/callhierarchy.h6
-rw-r--r--src/plugins/languageclient/client.cpp48
-rw-r--r--src/plugins/languageclient/client.h12
-rw-r--r--src/plugins/languageclient/clientrequesttask.cpp39
-rw-r--r--src/plugins/languageclient/clientrequesttask.h80
-rw-r--r--src/plugins/languageclient/currentdocumentsymbolsrequest.cpp83
-rw-r--r--src/plugins/languageclient/currentdocumentsymbolsrequest.h53
-rw-r--r--src/plugins/languageclient/documentsymbolcache.cpp2
-rw-r--r--src/plugins/languageclient/languageclient.qbs4
-rw-r--r--src/plugins/languageclient/languageclient_global.h13
-rw-r--r--src/plugins/languageclient/languageclientinterface.cpp10
-rw-r--r--src/plugins/languageclient/languageclientinterface.h11
-rw-r--r--src/plugins/languageclient/languageclientmanager.cpp39
-rw-r--r--src/plugins/languageclient/languageclientmanager.h13
-rw-r--r--src/plugins/languageclient/languageclientoutline.cpp110
-rw-r--r--src/plugins/languageclient/languageclientoutline.h38
-rw-r--r--src/plugins/languageclient/languageclientsettings.cpp129
-rw-r--r--src/plugins/languageclient/languageclientsymbolsupport.cpp34
-rw-r--r--src/plugins/languageclient/languageclientsymbolsupport.h13
-rw-r--r--src/plugins/languageclient/languageclientutils.cpp5
-rw-r--r--src/plugins/languageclient/locatorfilter.cpp474
-rw-r--r--src/plugins/languageclient/locatorfilter.h123
-rw-r--r--src/plugins/macros/macrolocatorfilter.cpp96
-rw-r--r--src/plugins/macros/macrolocatorfilter.h18
-rw-r--r--src/plugins/macros/macrooptionswidget.cpp2
-rw-r--r--src/plugins/macros/savedialog.cpp2
-rw-r--r--src/plugins/marketplace/productlistmodel.cpp2
-rw-r--r--src/plugins/mcusupport/mcukitinformation.cpp2
-rw-r--r--src/plugins/mcusupport/mcuqmlprojectnode.h2
-rw-r--r--src/plugins/mcusupport/mcusupport.qbs3
-rw-r--r--src/plugins/mcusupport/mcusupportdevice.h1
-rw-r--r--src/plugins/mcusupport/mcusupportplugin.cpp6
-rw-r--r--src/plugins/mcusupport/mcusupportversiondetection.cpp4
-rw-r--r--src/plugins/mcusupport/test/unittest.cpp10
-rw-r--r--src/plugins/mcusupport/wizards/qmlproject/wizard.json2
-rw-r--r--src/plugins/mercurial/authenticationdialog.cpp2
-rw-r--r--src/plugins/mercurial/mercurialclient.cpp15
-rw-r--r--src/plugins/mercurial/mercurialclient.h3
-rw-r--r--src/plugins/mercurial/mercurialcommitwidget.cpp7
-rw-r--r--src/plugins/mercurial/mercurialplugin.cpp13
-rw-r--r--src/plugins/mercurial/mercurialsettings.cpp40
-rw-r--r--src/plugins/mercurial/mercurialsettings.h8
-rw-r--r--src/plugins/mercurial/revertdialog.cpp6
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp5
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildconfiguration.h4
-rw-r--r--src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp14
-rw-r--r--src/plugins/mesonprojectmanager/mesonprocess.cpp10
-rw-r--r--src/plugins/mesonprojectmanager/mesonprocess.h4
-rw-r--r--src/plugins/mesonprojectmanager/mesonprojectparser.cpp4
-rw-r--r--src/plugins/mesonprojectmanager/mesonwrapper.h4
-rw-r--r--src/plugins/mesonprojectmanager/settings.cpp2
-rw-r--r--src/plugins/mesonprojectmanager/toolitemsettings.cpp3
-rw-r--r--src/plugins/mesonprojectmanager/toolkitaspectwidget.h6
-rw-r--r--src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp8
-rw-r--r--src/plugins/mesonprojectmanager/toolwrapper.cpp4
-rw-r--r--src/plugins/modeleditor/componentviewcontroller.cpp16
-rw-r--r--src/plugins/modeleditor/elementtasks.cpp54
-rw-r--r--src/plugins/modeleditor/modelindexer.cpp34
-rw-r--r--src/plugins/nim/editor/nimcompletionassistprovider.cpp2
-rw-r--r--src/plugins/nim/images/settingscategory_nim.pngbin245 -> 245 bytes
-rw-r--r--src/plugins/nim/images/settingscategory_nim@2x.pngbin511 -> 511 bytes
-rw-r--r--src/plugins/nim/nimplugin.cpp1
-rw-r--r--src/plugins/nim/project/nimblebuildsystem.cpp8
-rw-r--r--src/plugins/nim/project/nimbletaskstep.cpp5
-rw-r--r--src/plugins/nim/project/nimcompilerbuildstep.cpp4
-rw-r--r--src/plugins/nim/project/nimcompilercleanstep.cpp2
-rw-r--r--src/plugins/nim/project/nimtoolchain.cpp4
-rw-r--r--src/plugins/nim/project/nimtoolchainfactory.cpp5
-rw-r--r--src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp5
-rw-r--r--src/plugins/nim/settings/nimcodestylesettingspage.cpp4
-rw-r--r--src/plugins/nim/settings/nimsettings.cpp71
-rw-r--r--src/plugins/nim/settings/nimsettings.h13
-rw-r--r--src/plugins/nim/suggest/server.cpp4
-rw-r--r--src/plugins/nim/suggest/server.h4
-rw-r--r--src/plugins/perforce/changenumberdialog.cpp2
-rw-r--r--src/plugins/perforce/pendingchangesdialog.cpp2
-rw-r--r--src/plugins/perforce/perforcechecker.cpp8
-rw-r--r--src/plugins/perforce/perforcechecker.h4
-rw-r--r--src/plugins/perforce/perforceplugin.cpp18
-rw-r--r--src/plugins/perforce/perforcesettings.cpp33
-rw-r--r--src/plugins/perforce/perforcesettings.h5
-rw-r--r--src/plugins/perforce/perforcesubmiteditorwidget.cpp1
-rw-r--r--src/plugins/perfprofiler/perfconfigwidget.cpp6
-rw-r--r--src/plugins/perfprofiler/perfconfigwidget.h4
-rw-r--r--src/plugins/perfprofiler/perfdatareader.cpp2
-rw-r--r--src/plugins/perfprofiler/perfloaddialog.cpp4
-rw-r--r--src/plugins/perfprofiler/perfprofiler.qbs4
-rw-r--r--src/plugins/perfprofiler/perfprofilerflamegraphmodel.h5
-rw-r--r--src/plugins/perfprofiler/perfprofilerruncontrol.cpp18
-rw-r--r--src/plugins/perfprofiler/perfprofilertool.cpp20
-rw-r--r--src/plugins/perfprofiler/perfsettings.cpp4
-rw-r--r--src/plugins/perfprofiler/perftracepointdialog.cpp14
-rw-r--r--src/plugins/perfprofiler/perftracepointdialog.h4
-rw-r--r--src/plugins/plugins.qbs7
-rw-r--r--src/plugins/projectexplorer/CMakeLists.txt23
-rw-r--r--src/plugins/projectexplorer/abstractprocessstep.cpp21
-rw-r--r--src/plugins/projectexplorer/abstractprocessstep.h9
-rw-r--r--src/plugins/projectexplorer/allprojectsfilter.cpp56
-rw-r--r--src/plugins/projectexplorer/allprojectsfilter.h19
-rw-r--r--src/plugins/projectexplorer/allprojectsfind.cpp6
-rw-r--r--src/plugins/projectexplorer/buildaspects.cpp6
-rw-r--r--src/plugins/projectexplorer/buildaspects.h2
-rw-r--r--src/plugins/projectexplorer/buildconfiguration.cpp43
-rw-r--r--src/plugins/projectexplorer/buildmanager.cpp12
-rw-r--r--src/plugins/projectexplorer/buildpropertiessettings.cpp45
-rw-r--r--src/plugins/projectexplorer/buildpropertiessettings.h13
-rw-r--r--src/plugins/projectexplorer/buildsettingspropertiespage.cpp8
-rw-r--r--src/plugins/projectexplorer/buildstep.cpp54
-rw-r--r--src/plugins/projectexplorer/buildstep.h41
-rw-r--r--src/plugins/projectexplorer/buildstepspage.cpp8
-rw-r--r--src/plugins/projectexplorer/buildsystem.cpp10
-rw-r--r--src/plugins/projectexplorer/buildsystem.h1
-rw-r--r--src/plugins/projectexplorer/codestylesettingspropertiespage.cpp7
-rw-r--r--src/plugins/projectexplorer/copystep.cpp118
-rw-r--r--src/plugins/projectexplorer/copystep.h22
-rw-r--r--src/plugins/projectexplorer/currentprojectfilter.cpp51
-rw-r--r--src/plugins/projectexplorer/currentprojectfilter.h23
-rw-r--r--src/plugins/projectexplorer/currentprojectfind.cpp13
-rw-r--r--src/plugins/projectexplorer/customparserconfigdialog.cpp2
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp4
-rw-r--r--src/plugins/projectexplorer/dependenciespanel.cpp22
-rw-r--r--src/plugins/projectexplorer/desktoprunconfiguration.cpp3
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.cpp118
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.h10
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp88
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h14
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp28
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp63
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicesettingswidget.h1
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp2
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp8
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h7
-rw-r--r--src/plugins/projectexplorer/devicesupport/filetransfer.cpp41
-rw-r--r--src/plugins/projectexplorer/devicesupport/filetransfer.h12
-rw-r--r--src/plugins/projectexplorer/devicesupport/filetransferinterface.h8
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp39
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h19
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicefactory.cpp27
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevicefactory.h3
-rw-r--r--src/plugins/projectexplorer/devicesupport/localprocesslist.cpp59
-rw-r--r--src/plugins/projectexplorer/devicesupport/processlist.cpp61
-rw-r--r--src/plugins/projectexplorer/devicesupport/processlist.h (renamed from src/plugins/projectexplorer/devicesupport/localprocesslist.h)10
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp83
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h36
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshparameters.cpp33
-rw-r--r--src/plugins/projectexplorer/devicesupport/sshparameters.h34
-rw-r--r--src/plugins/projectexplorer/editorconfiguration.cpp8
-rw-r--r--src/plugins/projectexplorer/editorsettingspropertiespage.cpp5
-rw-r--r--src/plugins/projectexplorer/environmentaspect.cpp3
-rw-r--r--src/plugins/projectexplorer/environmentaspect.h7
-rw-r--r--src/plugins/projectexplorer/environmentaspectwidget.cpp9
-rw-r--r--src/plugins/projectexplorer/extraabi.cpp17
-rw-r--r--src/plugins/projectexplorer/extracompiler.cpp90
-rw-r--r--src/plugins/projectexplorer/extracompiler.h20
-rw-r--r--src/plugins/projectexplorer/fileinsessionfinder.cpp12
-rw-r--r--src/plugins/projectexplorer/filesinallprojectsfind.cpp4
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.cpp23
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.h4
-rw-r--r--src/plugins/projectexplorer/ipotentialkit.h10
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp37
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp4
-rw-r--r--src/plugins/projectexplorer/kitchooser.cpp4
-rw-r--r--src/plugins/projectexplorer/kitinformation.cpp98
-rw-r--r--src/plugins/projectexplorer/kitmanager.cpp9
-rw-r--r--src/plugins/projectexplorer/kitmanager.h2
-rw-r--r--src/plugins/projectexplorer/kitmanagerconfigwidget.cpp49
-rw-r--r--src/plugins/projectexplorer/kitmanagerconfigwidget.h13
-rw-r--r--src/plugins/projectexplorer/kitmodel.cpp186
-rw-r--r--src/plugins/projectexplorer/kitoptionspage.cpp55
-rw-r--r--src/plugins/projectexplorer/kitoptionspage.h14
-rw-r--r--src/plugins/projectexplorer/makestep.cpp29
-rw-r--r--src/plugins/projectexplorer/makestep.h5
-rw-r--r--src/plugins/projectexplorer/miniprojecttargetselector.cpp72
-rw-r--r--src/plugins/projectexplorer/msvctoolchain.cpp43
-rw-r--r--src/plugins/projectexplorer/msvctoolchain.h11
-rw-r--r--src/plugins/projectexplorer/processparameters.cpp2
-rw-r--r--src/plugins/projectexplorer/project.cpp158
-rw-r--r--src/plugins/projectexplorer/project.h6
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp598
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h3
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qbs12
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qrc8
-rw-r--r--src/plugins/projectexplorer/projectexplorerconstants.h6
-rw-r--r--src/plugins/projectexplorer/projectexplorersettings.h2
-rw-r--r--src/plugins/projectexplorer/projectexplorersettingspage.cpp55
-rw-r--r--src/plugins/projectexplorer/projectexplorersettingspage.h17
-rw-r--r--src/plugins/projectexplorer/projectfilewizardextension.cpp5
-rw-r--r--src/plugins/projectexplorer/projectmanager.cpp816
-rw-r--r--src/plugins/projectexplorer/projectmanager.h70
-rw-r--r--src/plugins/projectexplorer/projectmodels.cpp22
-rw-r--r--src/plugins/projectexplorer/projectnodes.cpp58
-rw-r--r--src/plugins/projectexplorer/projectnodes.h10
-rw-r--r--src/plugins/projectexplorer/projectnodeshelper.h26
-rw-r--r--src/plugins/projectexplorer/projecttree.cpp22
-rw-r--r--src/plugins/projectexplorer/projecttreewidget.cpp7
-rw-r--r--src/plugins/projectexplorer/projectwelcomepage.cpp5
-rw-r--r--src/plugins/projectexplorer/projectwindow.cpp38
-rw-r--r--src/plugins/projectexplorer/projectwizardpage.cpp14
-rw-r--r--src/plugins/projectexplorer/rawprojectpart.cpp6
-rw-r--r--src/plugins/projectexplorer/rawprojectpart.h2
-rw-r--r--src/plugins/projectexplorer/runconfiguration.cpp26
-rw-r--r--src/plugins/projectexplorer/runconfiguration.h1
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.cpp40
-rw-r--r--src/plugins/projectexplorer/runconfigurationaspects.h11
-rw-r--r--src/plugins/projectexplorer/runcontrol.cpp80
-rw-r--r--src/plugins/projectexplorer/runcontrol.h3
-rw-r--r--src/plugins/projectexplorer/runsettingspropertiespage.cpp9
-rw-r--r--src/plugins/projectexplorer/selectablefilesmodel.cpp14
-rw-r--r--src/plugins/projectexplorer/selectablefilesmodel.h7
-rw-r--r--src/plugins/projectexplorer/session.cpp1196
-rw-r--r--src/plugins/projectexplorer/session.h82
-rw-r--r--src/plugins/projectexplorer/session_p.h48
-rw-r--r--src/plugins/projectexplorer/sessiondialog.cpp10
-rw-r--r--src/plugins/projectexplorer/sessionmodel.cpp5
-rw-r--r--src/plugins/projectexplorer/target.cpp79
-rw-r--r--src/plugins/projectexplorer/target.h7
-rw-r--r--src/plugins/projectexplorer/targetsettingspanel.cpp14
-rw-r--r--src/plugins/projectexplorer/targetsetuppage.cpp9
-rw-r--r--src/plugins/projectexplorer/targetsetupwidget.cpp6
-rw-r--r--src/plugins/projectexplorer/task.cpp48
-rw-r--r--src/plugins/projectexplorer/task.h6
-rw-r--r--src/plugins/projectexplorer/taskfile.cpp2
-rw-r--r--src/plugins/projectexplorer/taskhub.cpp10
-rw-r--r--src/plugins/projectexplorer/taskmodel.cpp80
-rw-r--r--src/plugins/projectexplorer/taskmodel.h2
-rw-r--r--src/plugins/projectexplorer/taskwindow.cpp649
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt3
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro4
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp6
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro4
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp6
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h3
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro4
-rw-r--r--src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs10
-rw-r--r--src/plugins/projectexplorer/toolchain.cpp23
-rw-r--r--src/plugins/projectexplorer/toolchain.h10
-rw-r--r--src/plugins/projectexplorer/toolchainconfigwidget.cpp2
-rw-r--r--src/plugins/projectexplorer/toolchainoptionspage.cpp45
-rw-r--r--src/plugins/projectexplorer/toolchainsettingsaccessor.cpp16
-rw-r--r--src/plugins/projectexplorer/treescanner.cpp16
-rw-r--r--src/plugins/projectexplorer/treescanner.h4
-rw-r--r--src/plugins/projectexplorer/userfileaccessor.cpp18
-rw-r--r--src/plugins/python/CMakeLists.txt1
-rw-r--r--src/plugins/python/Python.json.in8
-rw-r--r--src/plugins/python/pipsupport.cpp53
-rw-r--r--src/plugins/python/pipsupport.h11
-rw-r--r--src/plugins/python/pyside.cpp21
-rw-r--r--src/plugins/python/pyside.h1
-rw-r--r--src/plugins/python/pysidebuildconfiguration.cpp4
-rw-r--r--src/plugins/python/pysideuicextracompiler.cpp4
-rw-r--r--src/plugins/python/pysideuicextracompiler.h2
-rw-r--r--src/plugins/python/python.qbs2
-rw-r--r--src/plugins/python/pythoneditor.cpp49
-rw-r--r--src/plugins/python/pythonlanguageclient.cpp14
-rw-r--r--src/plugins/python/pythonplugin.cpp14
-rw-r--r--src/plugins/python/pythonplugin.h3
-rw-r--r--src/plugins/python/pythonproject.h3
-rw-r--r--src/plugins/python/pythonrunconfiguration.cpp11
-rw-r--r--src/plugins/python/pythonsettings.cpp122
-rw-r--r--src/plugins/python/pythonsettings.h13
-rw-r--r--src/plugins/python/pythonutils.cpp67
-rw-r--r--src/plugins/python/pythonutils.h4
-rw-r--r--src/plugins/python/pythonwizardpage.cpp220
-rw-r--r--src/plugins/python/pythonwizardpage.h43
-rw-r--r--src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp2
-rw-r--r--src/plugins/qbsprojectmanager/qbsbuildstep.cpp48
-rw-r--r--src/plugins/qbsprojectmanager/qbsinstallstep.cpp25
-rw-r--r--src/plugins/qbsprojectmanager/qbsinstallstep.h2
-rw-r--r--src/plugins/qbsprojectmanager/qbskitinformation.cpp6
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilemanager.cpp4
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp30
-rw-r--r--src/plugins/qbsprojectmanager/qbsprofilessettingspage.h14
-rw-r--r--src/plugins/qbsprojectmanager/qbsproject.cpp15
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp18
-rw-r--r--src/plugins/qbsprojectmanager/qbsprojectparser.cpp8
-rw-r--r--src/plugins/qbsprojectmanager/qbssession.cpp13
-rw-r--r--src/plugins/qbsprojectmanager/qbssettings.cpp36
-rw-r--r--src/plugins/qbsprojectmanager/qbssettings.h23
-rw-r--r--src/plugins/qmakeprojectmanager/addlibrarywizard.cpp1
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp18
-rw-r--r--src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/makefileparse.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/profileeditor.cpp10
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp10
-rw-r--r--src/plugins/qmakeprojectmanager/qmakekitinformation.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakemakestep.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodes.cpp4
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodes.h1
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp26
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparser.cpp7
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.h2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp100
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.h1
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp14
-rw-r--r--src/plugins/qmakeprojectmanager/qmakesettings.cpp121
-rw-r--r--src/plugins/qmakeprojectmanager/qmakesettings.h36
-rw-r--r--src/plugins/qmakeprojectmanager/qmakestep.cpp27
-rw-r--r--src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp6
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt119
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp4
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp24
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp12
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp19
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc3
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp17
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h1
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp49
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h11
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp40
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h7
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp230
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h62
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx.pngbin0 -> 1505 bytes
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx@2x.pngbin0 -> 2594 bytes
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx_128.pngbin0 -> 3257 bytes
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractaction.cpp37
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractaction.h22
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp7
-rw-r--r--src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp83
-rw-r--r--src/plugins/qmldesigner/components/componentcore/changestyleaction.h8
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h7
-rw-r--r--src/plugins/qmldesigner/components/componentcore/crumblebar.cpp40
-rw-r--r--src/plugins/qmldesigner/components/componentcore/crumblebar.h36
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp383
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.h6
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designericons.cpp40
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designericons.h38
-rw-r--r--src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp155
-rw-r--r--src/plugins/qmldesigner/components/componentcore/groupitemaction.h23
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h16
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp51
-rw-r--r--src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp152
-rw-r--r--src/plugins/qmldesigner/components/componentcore/qmleditormenu.h65
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.cpp27
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.h144
-rw-r--r--src/plugins/qmldesigner/components/componentcore/viewmanager.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/zoomaction.cpp33
-rw-r--r--src/plugins/qmldesigner/components/componentcore/zoomaction.h3
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp7
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/backendmodel.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp6
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp5
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/delegates.cpp4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/delegates.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp262
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h23
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.h4
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp34
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h16
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp191
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h16
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp99
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h33
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h4
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp58
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h6
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp227
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h23
-rw-r--r--src/plugins/qmldesigner/components/createtexture.cpp55
-rw-r--r--src/plugins/qmldesigner/components/createtexture.h28
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp8
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditorconstants.h14
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp103
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h7
-rw-r--r--src/plugins/qmldesigner/components/debugview/debugview.cpp9
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp75
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dactions.h42
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp2
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.cpp320
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.h13
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp168
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.h16
-rw-r--r--src/plugins/qmldesigner/components/eventlist/eventlist.cpp4
-rw-r--r--src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp6
-rw-r--r--src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp4
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.cpp4
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.cpp10
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp89
-rw-r--r--src/plugins/qmldesigner/components/formeditor/seekerslider.cpp143
-rw-r--r--src/plugins/qmldesigner/components/formeditor/seekerslider.h64
-rw-r--r--src/plugins/qmldesigner/components/formeditor/toolbox.cpp41
-rw-r--r--src/plugins/qmldesigner/components/formeditor/toolbox.h4
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp16
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocumentview.cpp1
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon.pngbin312 -> 677 bytes
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon@2x.pngbin346 -> 1333 bytes
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp71
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h1
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp39
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp9
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp45
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h12
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp26
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h7
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp12
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h2
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp164
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h7
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp142
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h28
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp71
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialutils.h20
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.h4
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp1
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditortransaction.h7
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp115
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.h18
-rw-r--r--src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp8
-rw-r--r--src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp20
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp65
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp80
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.h3
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.cpp29
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp100
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorwidget.h2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp10
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.cpp50
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h (renamed from src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h)12
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp107
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h25
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp49
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp103
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h23
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp7
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp53
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp30
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h3
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.h7
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp260
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h75
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp42
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h16
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/resources/dockwidgets.css5
-rw-r--r--src/plugins/qmldesigner/components/resources/stylesheet.css41
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp17
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h4
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp63
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h2
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp82
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorview.h15
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp43
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h11
-rw-r--r--src/plugins/qmldesigner/components/stateseditornew/stateseditorwidget.cpp2
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorview.cpp8
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp14
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/images/texture_ktx.pngbin0 -> 3706 bytes
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/images/texture_ktx@2x.pngbin0 -> 6625 bytes
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditor.qrc2
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp3
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.h4
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp5
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h5
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditortransaction.h1
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp12
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorview.h9
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp6
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp44
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineutils.h6
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp3
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp7
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbar.cpp132
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbar.h17
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp614
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.h159
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp14
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp3
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp45
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp58
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp45
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h5
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp35
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h6
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h10
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h25
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h6
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp15
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h6
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h2
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp25
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h30
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h51
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h3
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h6
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp50
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h28
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp26
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h19
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp33
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp54
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h31
-rw-r--r--src/plugins/qmldesigner/designercore/include/abstractview.h96
-rw-r--r--src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h7
-rw-r--r--src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h5
-rw-r--r--src/plugins/qmldesigner/designercore/include/model.h5
-rw-r--r--src/plugins/qmldesigner/designercore/include/modelnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodeinstanceview.h5
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodemetainfo.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/propertymetainfo.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlobjectnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/rewriterview.h4
-rw-r--r--src/plugins/qmldesigner/designercore/include/synchronousimagecache.h3
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp12
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp65
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h38
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui78
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp42
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetdialog.h35
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetdialog.ui96
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp28
-rw-r--r--src/plugins/qmldesigner/designercore/model/abstractview.cpp225
-rw-r--r--src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/model/model.cpp19
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelnode.cpp26
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp1
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriterview.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp1
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp214
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp98
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h89
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h49
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp325
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h80
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h3
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp78
-rw-r--r--src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h2
-rw-r--r--src/plugins/qmldesigner/designmodecontext.cpp17
-rw-r--r--src/plugins/qmldesigner/designmodecontext.h9
-rw-r--r--src/plugins/qmldesigner/designmodewidget.cpp179
-rw-r--r--src/plugins/qmldesigner/designmodewidget.h23
-rw-r--r--src/plugins/qmldesigner/documentmanager.cpp20
-rw-r--r--src/plugins/qmldesigner/dynamiclicensecheck.h39
-rw-r--r--src/plugins/qmldesigner/generateresource.cpp26
-rw-r--r--src/plugins/qmldesigner/openuiqmlfiledialog.cpp2
-rw-r--r--src/plugins/qmldesigner/puppetenvironmentbuilder.cpp25
-rw-r--r--src/plugins/qmldesigner/puppetenvironmentbuilder.h8
-rw-r--r--src/plugins/qmldesigner/qmldesignerconstants.h31
-rw-r--r--src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp68
-rw-r--r--src/plugins/qmldesigner/qmldesignerexternaldependencies.h1
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp127
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.h13
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.cpp152
-rw-r--r--src/plugins/qmldesigner/qmldesignerprojectmanager.h1
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp16
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp23
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-16.pngbin0 -> 315 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24.pngbin0 -> 514 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24@2x.pngbin0 -> 1846 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-engine-16.pngbin0 -> 363 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24.pngbin0 -> 472 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24@2x.pngbin0 -> 803 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-listener-16.pngbin0 -> 311 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24.pngbin0 -> 924 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24@2x.pngbin0 -> 1691 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-room-16.pngbin0 -> 276 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-room-24.pngbin0 -> 419 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/audio-room-24@2x.pngbin0 -> 878 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/default3d.pngbin0 -> 375 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/default3d16.pngbin0 -> 253 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/default3d@2x.pngbin0 -> 499 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-16.pngbin0 -> 319 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24.pngbin0 -> 664 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24@2x.pngbin0 -> 1536 bytes
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc18
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo132
-rw-r--r--src/plugins/qmldesigner/settingspage.cpp2
-rw-r--r--src/plugins/qmldesigner/shortcutmanager.cpp22
-rw-r--r--src/plugins/qmldesigner/shortcutmanager.h4
-rw-r--r--src/plugins/qmldesigner/studioplugin/studioplugin.metainfo1
-rw-r--r--src/plugins/qmldesigner/utils/asset.cpp99
-rw-r--r--src/plugins/qmldesigner/utils/asset.h21
-rw-r--r--src/plugins/qmldesigner/utils/designersettings.h82
-rw-r--r--src/plugins/qmldesigner/utils/filedownloader.cpp288
-rw-r--r--src/plugins/qmldesigner/utils/filedownloader.h92
-rw-r--r--src/plugins/qmldesigner/utils/fileextractor.cpp236
-rw-r--r--src/plugins/qmldesigner/utils/fileextractor.h92
-rw-r--r--src/plugins/qmldesigner/utils/hdrimage.cpp2
-rw-r--r--src/plugins/qmldesigner/utils/imageutils.cpp26
-rw-r--r--src/plugins/qmldesigner/utils/imageutils.h2
-rw-r--r--src/plugins/qmldesigner/utils/ktximage.cpp100
-rw-r--r--src/plugins/qmldesigner/utils/ktximage.h29
-rw-r--r--src/plugins/qmldesigner/utils/multifiledownloader.cpp137
-rw-r--r--src/plugins/qmldesigner/utils/multifiledownloader.h78
-rw-r--r--src/plugins/qmldesignerbase/CMakeLists.txt17
-rw-r--r--src/plugins/qmldesignerbase/QmlDesignerBase.json.in19
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbase.qbs29
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbase_global.h14
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp272
-rw-r--r--src/plugins/qmldesignerbase/qmldesignerbaseplugin.h72
-rw-r--r--src/plugins/qmldesignerbase/utils/designersettings.cpp (renamed from src/plugins/qmldesigner/utils/designersettings.cpp)3
-rw-r--r--src/plugins/qmldesignerbase/utils/designersettings.h88
-rw-r--r--src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp70
-rw-r--r--src/plugins/qmldesignerbase/utils/qmlpuppetpaths.h20
-rw-r--r--src/plugins/qmldesignerbase/utils/studioquickwidget.cpp84
-rw-r--r--src/plugins/qmldesignerbase/utils/studioquickwidget.h52
-rw-r--r--src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp7
-rw-r--r--src/plugins/qmljseditor/qmljscomponentfromobjectdef.h7
-rw-r--r--src/plugins/qmljseditor/qmljscomponentnamedialog.cpp2
-rw-r--r--src/plugins/qmljseditor/qmljseditingsettingspage.cpp243
-rw-r--r--src/plugins/qmljseditor/qmljseditingsettingspage.h16
-rw-r--r--src/plugins/qmljseditor/qmljsfindreferences.cpp78
-rw-r--r--src/plugins/qmljseditor/qmljsfindreferences.h9
-rw-r--r--src/plugins/qmljseditor/qmljsquickfix.h2
-rw-r--r--src/plugins/qmljseditor/qmljssemantichighlighter.cpp27
-rw-r--r--src/plugins/qmljseditor/qmljssemantichighlighter.h2
-rw-r--r--src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp2
-rw-r--r--src/plugins/qmljseditor/qmllssettings.cpp2
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.cpp15
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.h2
-rw-r--r--src/plugins/qmljstools/qmljsbundleprovider.cpp30
-rw-r--r--src/plugins/qmljstools/qmljsbundleprovider.h9
-rw-r--r--src/plugins/qmljstools/qmljscodestylesettingspage.cpp83
-rw-r--r--src/plugins/qmljstools/qmljscodestylesettingspage.h16
-rw-r--r--src/plugins/qmljstools/qmljscodestylesettingswidget.cpp7
-rw-r--r--src/plugins/qmljstools/qmljsfunctionfilter.cpp83
-rw-r--r--src/plugins/qmljstools/qmljsfunctionfilter.h20
-rw-r--r--src/plugins/qmljstools/qmljslocatordata.cpp6
-rw-r--r--src/plugins/qmljstools/qmljsmodelmanager.cpp9
-rw-r--r--src/plugins/qmljstools/qmljsrefactoringchanges.h2
-rw-r--r--src/plugins/qmljstools/qmljstools.qbs4
-rw-r--r--src/plugins/qmljstools/qmljstoolsplugin.cpp2
-rw-r--r--src/plugins/qmljstools/qmljstoolssettings.cpp39
-rw-r--r--src/plugins/qmlpreview/qmlpreview.qbs4
-rw-r--r--src/plugins/qmlpreview/qmlpreviewplugin.cpp10
-rw-r--r--src/plugins/qmlpreview/qmlpreviewruncontrol.cpp19
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.qbs4
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilersettings.cpp58
-rw-r--r--src/plugins/qmlprofiler/qmlprofilersettings.h17
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp9
-rw-r--r--src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp40
-rw-r--r--src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp26
-rw-r--r--src/plugins/qmlprojectmanager/CMakeLists.txt3
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp13
-rw-r--r--src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp78
-rw-r--r--src/plugins/qmlprojectmanager/projectfilecontenttools.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/projectfilecontenttools.h2
-rw-r--r--src/plugins/qmlprojectmanager/qdslandingpage.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/qmlmainfileaspect.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp4
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp42
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.h3
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.qbs1
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.cpp12
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp21
-rw-r--r--src/plugins/qnx/CMakeLists.txt5
-rw-r--r--src/plugins/qnx/qnx.qbs10
-rw-r--r--src/plugins/qnx/qnxanalyzesupport.cpp3
-rw-r--r--src/plugins/qnx/qnxconfiguration.cpp468
-rw-r--r--src/plugins/qnx/qnxconfiguration.h114
-rw-r--r--src/plugins/qnx/qnxconfigurationmanager.cpp124
-rw-r--r--src/plugins/qnx/qnxconfigurationmanager.h41
-rw-r--r--src/plugins/qnx/qnxconstants.h1
-rw-r--r--src/plugins/qnx/qnxdebugsupport.cpp6
-rw-r--r--src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp28
-rw-r--r--src/plugins/qnx/qnxdevice.cpp212
-rw-r--r--src/plugins/qnx/qnxdevice.h34
-rw-r--r--src/plugins/qnx/qnxdeviceprocesslist.cpp59
-rw-r--r--src/plugins/qnx/qnxdeviceprocesslist.h21
-rw-r--r--src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp36
-rw-r--r--src/plugins/qnx/qnxdeviceprocesssignaloperation.h22
-rw-r--r--src/plugins/qnx/qnxdevicetester.cpp83
-rw-r--r--src/plugins/qnx/qnxdevicetester.h17
-rw-r--r--src/plugins/qnx/qnxdevicewizard.cpp48
-rw-r--r--src/plugins/qnx/qnxdevicewizard.h38
-rw-r--r--src/plugins/qnx/qnxplugin.cpp35
-rw-r--r--src/plugins/qnx/qnxqtversion.cpp2
-rw-r--r--src/plugins/qnx/qnxqtversion.h2
-rw-r--r--src/plugins/qnx/qnxsettingspage.cpp682
-rw-r--r--src/plugins/qnx/qnxsettingspage.h6
-rw-r--r--src/plugins/qnx/qnxtoolchain.cpp10
-rw-r--r--src/plugins/qnx/qnxutils.cpp13
-rw-r--r--src/plugins/qnx/qnxutils.h10
-rw-r--r--src/plugins/qnx/slog2inforunner.cpp39
-rw-r--r--src/plugins/qnx/slog2inforunner.h4
-rw-r--r--src/plugins/qtsupport/baseqtversion.cpp22
-rw-r--r--src/plugins/qtsupport/baseqtversion.h2
-rw-r--r--src/plugins/qtsupport/codegensettingspage.cpp4
-rw-r--r--src/plugins/qtsupport/exampleslistmodel.cpp27
-rw-r--r--src/plugins/qtsupport/examplesparser.cpp4
-rw-r--r--src/plugins/qtsupport/externaleditors.cpp8
-rw-r--r--src/plugins/qtsupport/gettingstartedwelcomepage.cpp4
-rw-r--r--src/plugins/qtsupport/qscxmlcgenerator.cpp2
-rw-r--r--src/plugins/qtsupport/qscxmlcgenerator.h2
-rw-r--r--src/plugins/qtsupport/qtbuildaspects.cpp12
-rw-r--r--src/plugins/qtsupport/qtbuildaspects.h4
-rw-r--r--src/plugins/qtsupport/qtkitinformation.cpp6
-rw-r--r--src/plugins/qtsupport/qtoptionspage.cpp9
-rw-r--r--src/plugins/qtsupport/qtsupportplugin.cpp10
-rw-r--r--src/plugins/qtsupport/qtversionfactory.h2
-rw-r--r--src/plugins/qtsupport/qtversionmanager.cpp10
-rw-r--r--src/plugins/qtsupport/uicgenerator.cpp11
-rw-r--r--src/plugins/qtsupport/uicgenerator.h2
-rw-r--r--src/plugins/remotelinux/CMakeLists.txt2
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp186
-rw-r--r--src/plugins/remotelinux/abstractremotelinuxdeploystep.h93
-rw-r--r--src/plugins/remotelinux/customcommanddeploystep.cpp90
-rw-r--r--src/plugins/remotelinux/filesystemaccess_test.cpp392
-rw-r--r--src/plugins/remotelinux/filesystemaccess_test.h16
-rw-r--r--src/plugins/remotelinux/genericdirectuploadservice.cpp314
-rw-r--r--src/plugins/remotelinux/genericdirectuploadservice.h38
-rw-r--r--src/plugins/remotelinux/genericdirectuploadstep.cpp326
-rw-r--r--src/plugins/remotelinux/genericdirectuploadstep.h19
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp63
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h13
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp8
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp34
-rw-r--r--src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h3
-rw-r--r--src/plugins/remotelinux/killappstep.cpp48
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp659
-rw-r--r--src/plugins/remotelinux/linuxdevice.h10
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.cpp88
-rw-r--r--src/plugins/remotelinux/linuxdevicetester.h4
-rw-r--r--src/plugins/remotelinux/linuxprocessinterface.h33
-rw-r--r--src/plugins/remotelinux/makeinstallstep.cpp45
-rw-r--r--src/plugins/remotelinux/makeinstallstep.h10
-rw-r--r--src/plugins/remotelinux/publickeydeploymentdialog.cpp6
-rw-r--r--src/plugins/remotelinux/remotelinux.qbs7
-rw-r--r--src/plugins/remotelinux/remotelinux_constants.h5
-rw-r--r--src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp43
-rw-r--r--src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp2
-rw-r--r--src/plugins/remotelinux/remotelinuxplugin.cpp18
-rw-r--r--src/plugins/remotelinux/remotelinuxrunconfiguration.cpp18
-rw-r--r--src/plugins/remotelinux/remotelinuxsignaloperation.cpp6
-rw-r--r--src/plugins/remotelinux/remotelinuxsignaloperation.h2
-rw-r--r--src/plugins/remotelinux/rsyncdeploystep.cpp131
-rw-r--r--src/plugins/remotelinux/rsyncdeploystep.h11
-rw-r--r--src/plugins/remotelinux/sshkeycreationdialog.cpp4
-rw-r--r--src/plugins/remotelinux/sshprocessinterface.h46
-rw-r--r--src/plugins/remotelinux/tarpackagecreationstep.cpp27
-rw-r--r--src/plugins/remotelinux/tarpackagedeploystep.cpp118
-rw-r--r--src/plugins/resourceeditor/qrceditor/qrceditor.cpp2
-rw-r--r--src/plugins/resourceeditor/resourcenode.cpp4
-rw-r--r--src/plugins/scxmleditor/CMakeLists.txt1
-rw-r--r--src/plugins/scxmleditor/common/colorpicker.cpp6
-rw-r--r--src/plugins/scxmleditor/common/colorsettings.cpp5
-rw-r--r--src/plugins/scxmleditor/common/colorthemedialog.cpp2
-rw-r--r--src/plugins/scxmleditor/common/navigatorslider.cpp6
-rw-r--r--src/plugins/scxmleditor/common/search.cpp6
-rw-r--r--src/plugins/scxmleditor/common/shapestoolbox.cpp6
-rw-r--r--src/plugins/scxmleditor/common/stateproperties.cpp1
-rw-r--r--src/plugins/scxmleditor/common/stateview.cpp12
-rw-r--r--src/plugins/scxmleditor/common/statistics.cpp7
-rw-r--r--src/plugins/scxmleditor/common/statisticsdialog.cpp2
-rw-r--r--src/plugins/scxmleditor/plugin_interface/baseitem.cpp3
-rw-r--r--src/plugins/scxmleditor/plugin_interface/baseitem.h1
-rw-r--r--src/plugins/scxmleditor/plugin_interface/eventitem.cpp108
-rw-r--r--src/plugins/scxmleditor/plugin_interface/eventitem.h41
-rw-r--r--src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp15
-rw-r--r--src/plugins/scxmleditor/plugin_interface/stateitem.cpp66
-rw-r--r--src/plugins/scxmleditor/plugin_interface/stateitem.h8
-rw-r--r--src/plugins/scxmleditor/scxmleditor.qbs1
-rw-r--r--src/plugins/scxmleditor/scxmleditordocument.cpp2
-rw-r--r--src/plugins/silversearcher/findinfilessilversearcher.cpp32
-rw-r--r--src/plugins/silversearcher/findinfilessilversearcher.h4
-rw-r--r--src/plugins/silversearcher/outputparser_test.cpp65
-rw-r--r--src/plugins/silversearcher/silversearcher.qbs4
-rw-r--r--src/plugins/silversearcher/silversearcheroutputparser.cpp189
-rw-r--r--src/plugins/silversearcher/silversearcheroutputparser.h29
-rw-r--r--src/plugins/squish/deletesymbolicnamedialog.cpp2
-rw-r--r--src/plugins/squish/images/picker.pngbin0 -> 194 bytes
-rw-r--r--src/plugins/squish/images/picker@2x.pngbin0 -> 362 bytes
-rw-r--r--src/plugins/squish/objectsmapdocument.cpp6
-rw-r--r--src/plugins/squish/objectsmapeditorwidget.cpp2
-rw-r--r--src/plugins/squish/opensquishsuitesdialog.cpp2
-rw-r--r--src/plugins/squish/squish.qrc2
-rw-r--r--src/plugins/squish/squishfilehandler.cpp7
-rw-r--r--src/plugins/squish/squishnavigationwidget.cpp19
-rw-r--r--src/plugins/squish/squishoutputpane.cpp6
-rw-r--r--src/plugins/squish/squishperspective.cpp224
-rw-r--r--src/plugins/squish/squishperspective.h36
-rw-r--r--src/plugins/squish/squishprocessbase.cpp4
-rw-r--r--src/plugins/squish/squishprocessbase.h4
-rw-r--r--src/plugins/squish/squishrunnerprocess.cpp93
-rw-r--r--src/plugins/squish/squishrunnerprocess.h16
-rw-r--r--src/plugins/squish/squishserverprocess.cpp4
-rw-r--r--src/plugins/squish/squishsettings.cpp15
-rw-r--r--src/plugins/squish/squishtools.cpp84
-rw-r--r--src/plugins/squish/squishtools.h10
-rw-r--r--src/plugins/squish/squishwizardpages.cpp3
-rw-r--r--src/plugins/studiowelcome/examplecheckout.cpp466
-rw-r--r--src/plugins/studiowelcome/examplecheckout.h134
-rw-r--r--src/plugins/studiowelcome/qdsnewdialog.cpp10
-rw-r--r--src/plugins/studiowelcome/qml/downloaddialog/main.qml2
-rw-r--r--src/plugins/studiowelcome/studiowelcomeplugin.cpp230
-rw-r--r--src/plugins/studiowelcome/studiowelcomeplugin.h34
-rw-r--r--src/plugins/studiowelcome/stylemodel.cpp4
-rw-r--r--src/plugins/studiowelcome/wizardhandler.cpp2
-rw-r--r--src/plugins/subversion/subversionclient.cpp46
-rw-r--r--src/plugins/subversion/subversionclient.h2
-rw-r--r--src/plugins/subversion/subversionplugin.cpp40
-rw-r--r--src/plugins/subversion/subversionsettings.cpp66
-rw-r--r--src/plugins/subversion/subversionsettings.h13
-rw-r--r--src/plugins/terminal/CMakeLists.txt23
-rw-r--r--src/plugins/terminal/Terminal.json.in18
-rw-r--r--src/plugins/terminal/celliterator.cpp94
-rw-r--r--src/plugins/terminal/celliterator.h97
-rw-r--r--src/plugins/terminal/glyphcache.cpp48
-rw-r--r--src/plugins/terminal/glyphcache.h34
-rw-r--r--src/plugins/terminal/images/settingscategory_terminal.pngbin0 -> 164 bytes
-rw-r--r--src/plugins/terminal/images/settingscategory_terminal@2x.pngbin0 -> 193 bytes
-rw-r--r--src/plugins/terminal/images/terminal.pngbin0 -> 154 bytes
-rw-r--r--src/plugins/terminal/images/terminal@2x.pngbin0 -> 180 bytes
-rw-r--r--src/plugins/terminal/keys.cpp83
-rw-r--r--src/plugins/terminal/keys.h15
-rw-r--r--src/plugins/terminal/scrollback.cpp61
-rw-r--r--src/plugins/terminal/scrollback.h57
-rw-r--r--src/plugins/terminal/shellintegration.cpp164
-rw-r--r--src/plugins/terminal/shellintegration.h34
-rwxr-xr-xsrc/plugins/terminal/shellintegrations/shellintegration-bash.sh252
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-clink.lua52
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-env.zsh15
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-login.zsh7
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-profile.zsh15
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration-rc.zsh160
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration.fish122
-rw-r--r--src/plugins/terminal/shellintegrations/shellintegration.ps1158
-rw-r--r--src/plugins/terminal/shellmodel.cpp110
-rw-r--r--src/plugins/terminal/shellmodel.h37
-rw-r--r--src/plugins/terminal/terminal.qbs46
-rw-r--r--src/plugins/terminal/terminal.qrc16
-rw-r--r--src/plugins/terminal/terminalcommands.cpp186
-rw-r--r--src/plugins/terminal/terminalcommands.h76
-rw-r--r--src/plugins/terminal/terminalpane.cpp397
-rw-r--r--src/plugins/terminal/terminalpane.h65
-rw-r--r--src/plugins/terminal/terminalplugin.cpp84
-rw-r--r--src/plugins/terminal/terminalplugin.h30
-rw-r--r--src/plugins/terminal/terminalprocessimpl.cpp72
-rw-r--r--src/plugins/terminal/terminalprocessimpl.h18
-rw-r--r--src/plugins/terminal/terminalsearch.cpp283
-rw-r--r--src/plugins/terminal/terminalsearch.h82
-rw-r--r--src/plugins/terminal/terminalsettings.cpp177
-rw-r--r--src/plugins/terminal/terminalsettings.h36
-rw-r--r--src/plugins/terminal/terminalsettingspage.cpp464
-rw-r--r--src/plugins/terminal/terminalsettingspage.h18
-rw-r--r--src/plugins/terminal/terminalsurface.cpp531
-rw-r--r--src/plugins/terminal/terminalsurface.h111
-rw-r--r--src/plugins/terminal/terminaltr.h15
-rw-r--r--src/plugins/terminal/terminalwidget.cpp1502
-rw-r--r--src/plugins/terminal/terminalwidget.h231
-rwxr-xr-xsrc/plugins/terminal/tests/colors112
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/bar3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/blinkbar3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/blinkblock3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/blinkunderline3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/block3
-rwxr-xr-xsrc/plugins/terminal/tests/cursor/underline3
-rwxr-xr-xsrc/plugins/terminal/tests/decoration9
-rwxr-xr-xsrc/plugins/terminal/tests/filenames22
-rwxr-xr-xsrc/plugins/terminal/tests/integration70
-rw-r--r--src/plugins/texteditor/CMakeLists.txt1
-rw-r--r--src/plugins/texteditor/basefilefind.cpp65
-rw-r--r--src/plugins/texteditor/basefilefind.h25
-rw-r--r--src/plugins/texteditor/basehoverhandler.h3
-rw-r--r--src/plugins/texteditor/behaviorsettingspage.cpp126
-rw-r--r--src/plugins/texteditor/behaviorsettingspage.h22
-rw-r--r--src/plugins/texteditor/behaviorsettingswidget.cpp9
-rw-r--r--src/plugins/texteditor/codeassist/asyncprocessor.cpp4
-rw-r--r--src/plugins/texteditor/codeassist/codeassistant.cpp12
-rw-r--r--src/plugins/texteditor/codestyleeditor.cpp1
-rw-r--r--src/plugins/texteditor/codestyleselectorwidget.cpp8
-rw-r--r--src/plugins/texteditor/colorschemeedit.cpp66
-rw-r--r--src/plugins/texteditor/colorschemeedit.h8
-rw-r--r--src/plugins/texteditor/completionsettingspage.cpp2
-rw-r--r--src/plugins/texteditor/displaysettingspage.cpp59
-rw-r--r--src/plugins/texteditor/fontsettingspage.cpp3
-rw-r--r--src/plugins/texteditor/formattexteditor.cpp10
-rw-r--r--src/plugins/texteditor/highlightersettingspage.cpp178
-rw-r--r--src/plugins/texteditor/highlightersettingspage.h4
-rw-r--r--src/plugins/texteditor/linenumberfilter.cpp102
-rw-r--r--src/plugins/texteditor/linenumberfilter.h24
-rw-r--r--src/plugins/texteditor/markdowneditor.cpp328
-rw-r--r--src/plugins/texteditor/markdowneditor.h21
-rw-r--r--src/plugins/texteditor/outlinefactory.cpp5
-rw-r--r--src/plugins/texteditor/quickfix.h6
-rw-r--r--src/plugins/texteditor/snippets/snippet.cpp4
-rw-r--r--src/plugins/texteditor/snippets/snippetscollection.cpp4
-rw-r--r--src/plugins/texteditor/snippets/snippetssettingspage.cpp202
-rw-r--r--src/plugins/texteditor/snippets/snippetssettingspage.h16
-rw-r--r--src/plugins/texteditor/tabsettingswidget.cpp3
-rw-r--r--src/plugins/texteditor/textdocument.cpp21
-rw-r--r--src/plugins/texteditor/textdocument.h4
-rw-r--r--src/plugins/texteditor/textdocumentlayout.cpp142
-rw-r--r--src/plugins/texteditor/textdocumentlayout.h40
-rw-r--r--src/plugins/texteditor/texteditor.cpp312
-rw-r--r--src/plugins/texteditor/texteditor.h25
-rw-r--r--src/plugins/texteditor/texteditor.qbs6
-rw-r--r--src/plugins/texteditor/texteditoractionhandler.cpp5
-rw-r--r--src/plugins/texteditor/texteditoractionhandler.h3
-rw-r--r--src/plugins/texteditor/texteditorconstants.h1
-rw-r--r--src/plugins/texteditor/texteditoroverlay.cpp12
-rw-r--r--src/plugins/texteditor/texteditorplugin.cpp2
-rw-r--r--src/plugins/texteditor/textmark.cpp17
-rw-r--r--src/plugins/texteditor/textmark.h6
-rw-r--r--src/plugins/todo/keyworddialog.cpp2
-rw-r--r--src/plugins/todo/optionsdialog.cpp2
-rw-r--r--src/plugins/todo/todoitemsprovider.cpp7
-rw-r--r--src/plugins/todo/todoprojectsettingswidget.cpp2
-rw-r--r--src/plugins/updateinfo/settingspage.cpp2
-rw-r--r--src/plugins/updateinfo/settingspage.h8
-rw-r--r--src/plugins/updateinfo/updateinfoplugin.cpp19
-rw-r--r--src/plugins/valgrind/callgrindengine.cpp18
-rw-r--r--src/plugins/valgrind/callgrindengine.h4
-rw-r--r--src/plugins/valgrind/callgrindtool.cpp8
-rw-r--r--src/plugins/valgrind/memcheckerrorview.cpp2
-rw-r--r--src/plugins/valgrind/memchecktool.cpp34
-rw-r--r--src/plugins/valgrind/suppressiondialog.cpp8
-rw-r--r--src/plugins/valgrind/valgrind.qbs4
-rw-r--r--src/plugins/valgrind/valgrindengine.cpp29
-rw-r--r--src/plugins/valgrind/valgrindrunner.cpp14
-rw-r--r--src/plugins/valgrind/valgrindsettings.cpp6
-rw-r--r--src/plugins/valgrind/valgrindsettings.h2
-rw-r--r--src/plugins/vcpkg/CMakeLists.txt17
-rw-r--r--src/plugins/vcpkg/Vcpkg.json.in30
-rw-r--r--src/plugins/vcpkg/vcpkg.qbs32
-rw-r--r--src/plugins/vcpkg/vcpkg.qrc6
-rw-r--r--src/plugins/vcpkg/vcpkg_test.cpp110
-rw-r--r--src/plugins/vcpkg/vcpkg_test.h24
-rw-r--r--src/plugins/vcpkg/vcpkgconstants.h14
-rw-r--r--src/plugins/vcpkg/vcpkgmanifesteditor.cpp71
-rw-r--r--src/plugins/vcpkg/vcpkgmanifesteditor.h16
-rw-r--r--src/plugins/vcpkg/vcpkgplugin.cpp38
-rw-r--r--src/plugins/vcpkg/vcpkgplugin.h26
-rw-r--r--src/plugins/vcpkg/vcpkgsearch.cpp214
-rw-r--r--src/plugins/vcpkg/vcpkgsearch.h29
-rw-r--r--src/plugins/vcpkg/vcpkgsettings.cpp76
-rw-r--r--src/plugins/vcpkg/vcpkgsettings.h21
-rw-r--r--src/plugins/vcpkg/vcpkgtr.h15
-rw-r--r--src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl6
-rw-r--r--src/plugins/vcpkg/wizards/manifest/wizard.json80
-rw-r--r--src/plugins/vcsbase/cleandialog.cpp29
-rw-r--r--src/plugins/vcsbase/commonvcssettings.cpp95
-rw-r--r--src/plugins/vcsbase/commonvcssettings.h14
-rw-r--r--src/plugins/vcsbase/submiteditorwidget.cpp7
-rw-r--r--src/plugins/vcsbase/vcsbaseclient.cpp18
-rw-r--r--src/plugins/vcsbase/vcsbaseclient.h8
-rw-r--r--src/plugins/vcsbase/vcsbaseclientsettings.h9
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp42
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.h8
-rw-r--r--src/plugins/vcsbase/vcsbaseeditor.cpp4
-rw-r--r--src/plugins/vcsbase/vcsbaseplugin.cpp16
-rw-r--r--src/plugins/vcsbase/vcsbasesubmiteditor.cpp12
-rw-r--r--src/plugins/vcsbase/vcscommand.cpp28
-rw-r--r--src/plugins/vcsbase/vcscommand.h4
-rw-r--r--src/plugins/vcsbase/vcsenums.h2
-rw-r--r--src/plugins/vcsbase/vcsoutputwindow.cpp2
-rw-r--r--src/plugins/vcsbase/vcsplugin.cpp78
-rw-r--r--src/plugins/vcsbase/vcsplugin.h9
-rw-r--r--src/plugins/vcsbase/wizard/vcscommandpage.cpp1
-rw-r--r--src/plugins/webassembly/webassembly.qbs4
-rw-r--r--src/plugins/webassembly/webassemblydevice.h1
-rw-r--r--src/plugins/webassembly/webassemblyemsdk.cpp6
-rw-r--r--src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp8
-rw-r--r--src/plugins/webassembly/webassemblyrunconfigurationaspects.h2
-rw-r--r--src/plugins/webassembly/webassemblytoolchain.cpp2
-rw-r--r--src/plugins/welcome/introductionwidget.cpp3
-rw-r--r--src/share/qtcreator/externaltools/lrelease.xml1
-rw-r--r--src/share/qtcreator/externaltools/lupdate.xml1
-rw-r--r--src/share/qtcreator/externaltools/qml.xml1
-rw-r--r--src/share/qtcreator/externaltools/qmlscene.xml1
-rw-r--r--src/shared/help/bookmarkmanager.cpp2
-rw-r--r--src/shared/help/topicchooser.cpp2
-rw-r--r--src/shared/qtsingleapplication/qtlocalpeer.h2
-rw-r--r--src/shared/qtsingleapplication/qtsingleapplication.cpp68
-rw-r--r--src/shared/qtsingleapplication/qtsingleapplication.h5
-rw-r--r--src/shared/registryaccess/CMakeLists.txt6
-rw-r--r--src/tools/CMakeLists.txt15
-rw-r--r--src/tools/cplusplus-shared/CPlusPlusTool.qbs1
-rw-r--r--src/tools/cplusplustools.qbs1
-rw-r--r--src/tools/icons/GenericDocumentIcon.iconset/readme.txt15
-rw-r--r--src/tools/icons/applicationicons.svg2206
-rw-r--r--src/tools/icons/documenttypeicons.svg879
-rw-r--r--src/tools/icons/exportapplicationicons.sh102
-rw-r--r--src/tools/icons/exportdocumenttypeicons.sh32
-rw-r--r--src/tools/icons/qtcreatoricons.svg119
m---------src/tools/perfparser0
-rw-r--r--src/tools/process_stub/CMakeLists.txt12
-rw-r--r--src/tools/process_stub/main.cpp572
-rw-r--r--src/tools/process_stub/process_stub.qbs10
-rw-r--r--src/tools/processlauncher/CMakeLists.txt3
-rw-r--r--src/tools/processlauncher/launchersockethandler.cpp41
-rw-r--r--src/tools/processlauncher/launchersockethandler.h18
-rw-r--r--src/tools/processlauncher/processlauncher-main.cpp3
-rw-r--r--src/tools/processlauncher/processlauncher.qbs3
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt49
-rw-r--r--src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp8
-rw-r--r--src/tools/qml2puppet/qml2puppet/appmetadata.cpp51
-rw-r--r--src/tools/qml2puppet/qml2puppet/appmetadata.h44
-rw-r--r--src/tools/qml2puppet/qml2puppet/configcrashpad.h16
-rw-r--r--src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp25
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp13
-rw-r--r--src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp9
-rw-r--r--src/tools/qml2puppet/qml2puppet/qmlbase.h19
-rw-r--r--src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp23
-rw-r--r--src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp10
-rw-r--r--src/tools/qml2puppet/windows_application_icon/qml2puppet.rc30
-rw-r--r--src/tools/qtcdebugger/main.cpp2
-rw-r--r--src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp29
-rw-r--r--src/tools/sdktool/CMakeLists.txt30
-rw-r--r--src/tools/sdktool/addcmakeoperation.cpp5
-rw-r--r--src/tools/sdktool/adddebuggeroperation.cpp3
-rw-r--r--src/tools/sdktool/addkitoperation.cpp4
-rw-r--r--src/tools/sdktool/addqtoperation.cpp9
-rw-r--r--src/tools/sdktool/addtoolchainoperation.cpp5
-rw-r--r--src/tools/sdktool/main.cpp11
-rw-r--r--src/tools/sdktool/operation.cpp25
-rw-r--r--src/tools/sdktool/operation.h2
-rw-r--r--src/tools/sdktool/sdkpersistentsettings.cpp871
-rw-r--r--src/tools/sdktool/sdkpersistentsettings.h37
-rw-r--r--src/tools/sdktool/sdktoollib.qbs31
-rw-r--r--src/tools/sdktool/settings.cpp26
-rw-r--r--src/tools/sdktool/settings.h6
-rw-r--r--src/tools/tools.qbs1
-rw-r--r--tests/auto/CMakeLists.txt1
-rw-r--r--tests/auto/auto.qbs1
-rw-r--r--tests/auto/cplusplus/cxx11/tst_cxx11.cpp257
-rw-r--r--tests/auto/cplusplus/lexer/tst_lexer.cpp33
-rw-r--r--tests/auto/debugger/CMakeLists.txt2
-rw-r--r--tests/auto/debugger/tst_dumpers.cpp10
-rw-r--r--tests/auto/environment/tst_environment.cpp91
-rw-r--r--tests/auto/examples/tst_examples.cpp10
-rw-r--r--tests/auto/extensionsystem/pluginmanager/tst_pluginmanager.cpp6
-rw-r--r--tests/auto/filesearch/tst_filesearch.cpp105
-rw-r--r--tests/auto/qml/codemodel/check/tst_check.cpp3
-rw-r--r--tests/auto/qml/codemodel/dependencies/tst_dependencies.cpp4
-rw-r--r--tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp4
-rw-r--r--tests/auto/qml/codemodel/importscheck/tst_importscheck.cpp6
-rw-r--r--tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp5
-rw-r--r--tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp2
-rw-r--r--tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp13
-rw-r--r--tests/auto/qml/reformatter/forEachType.qml9
-rw-r--r--tests/auto/qml/reformatter/jsdirectives.js15
-rw-r--r--tests/auto/qml/reformatter/tst_reformatter.cpp14
-rw-r--r--tests/auto/runextensions/tst_runextensions.cpp59
-rw-r--r--tests/auto/solutions/CMakeLists.txt1
-rw-r--r--tests/auto/solutions/solutions.qbs8
-rw-r--r--tests/auto/solutions/tasking/CMakeLists.txt4
-rw-r--r--tests/auto/solutions/tasking/tasking.qbs7
-rw-r--r--tests/auto/solutions/tasking/tst_tasking.cpp1592
-rw-r--r--tests/auto/tracing/timelineabstractrenderer/tst_timelineabstractrenderer.cpp10
-rw-r--r--tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp36
-rw-r--r--tests/auto/tracing/timelinemodelaggregator/tst_timelinemodelaggregator.cpp30
-rw-r--r--tests/auto/tracing/timelinenotesmodel/tst_timelinenotesmodel.cpp4
-rw-r--r--tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp29
-rw-r--r--tests/auto/utils/CMakeLists.txt8
-rw-r--r--tests/auto/utils/async/CMakeLists.txt4
-rw-r--r--tests/auto/utils/async/async.qbs7
-rw-r--r--tests/auto/utils/async/tst_async.cpp596
-rw-r--r--tests/auto/utils/asynctask/CMakeLists.txt4
-rw-r--r--tests/auto/utils/asynctask/asynctask.qbs7
-rw-r--r--tests/auto/utils/asynctask/tst_asynctask.cpp437
-rw-r--r--tests/auto/utils/commandline/tst_commandline.cpp81
-rw-r--r--tests/auto/utils/deviceshell/tst_deviceshell.cpp27
-rw-r--r--tests/auto/utils/filepath/CMakeLists.txt4
-rw-r--r--tests/auto/utils/filepath/filepath.qbs11
-rw-r--r--tests/auto/utils/filepath/tst_filepath.cpp1688
-rw-r--r--tests/auto/utils/fileutils/tst_fileutils.cpp1293
-rw-r--r--tests/auto/utils/fsengine/tst_fsengine.cpp53
-rw-r--r--tests/auto/utils/mathutils/tst_mathutils.cpp2
-rw-r--r--tests/auto/utils/process/CMakeLists.txt (renamed from tests/auto/utils/qtcprocess/CMakeLists.txt)4
-rw-r--r--tests/auto/utils/process/process.qbs (renamed from tests/auto/utils/qtcprocess/qtcprocess.qbs)4
-rw-r--r--tests/auto/utils/process/processtestapp/CMakeLists.txt (renamed from tests/auto/utils/qtcprocess/processtestapp/CMakeLists.txt)0
-rw-r--r--tests/auto/utils/process/processtestapp/main.cpp (renamed from tests/auto/utils/qtcprocess/processtestapp/main.cpp)2
-rw-r--r--tests/auto/utils/process/processtestapp/processtestapp.cpp (renamed from tests/auto/utils/qtcprocess/processtestapp/processtestapp.cpp)10
-rw-r--r--tests/auto/utils/process/processtestapp/processtestapp.h (renamed from tests/auto/utils/qtcprocess/processtestapp/processtestapp.h)6
-rw-r--r--tests/auto/utils/process/processtestapp/processtestapp.qbs (renamed from tests/auto/utils/qtcprocess/processtestapp/processtestapp.qbs)0
-rw-r--r--tests/auto/utils/process/tst_process.cpp (renamed from tests/auto/utils/qtcprocess/tst_qtcprocess.cpp)199
-rw-r--r--tests/auto/utils/settings/tst_settings.cpp8
-rw-r--r--tests/auto/utils/stringutils/tst_stringutils.cpp34
-rw-r--r--tests/auto/utils/tasktree/CMakeLists.txt11
-rw-r--r--tests/auto/utils/tasktree/tasktree.qbs27
-rw-r--r--tests/auto/utils/tasktree/testapp/CMakeLists.txt12
-rw-r--r--tests/auto/utils/tasktree/testapp/main.cpp63
-rw-r--r--tests/auto/utils/tasktree/testapp/testapp.qbs20
-rw-r--r--tests/auto/utils/tasktree/tst_tasktree.cpp1095
-rw-r--r--tests/auto/utils/text/CMakeLists.txt4
-rw-r--r--tests/auto/utils/text/text.qbs7
-rw-r--r--tests/auto/utils/text/tst_text.cpp211
-rw-r--r--tests/auto/utils/unixdevicefileaccess/CMakeLists.txt4
-rw-r--r--tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp78
-rw-r--r--tests/auto/utils/unixdevicefileaccess/unixdevicefileaccess.qbs11
-rw-r--r--tests/auto/utils/utils.qbs7
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/CMakeLists.txt37
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/README.md4
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/main.cpp15
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.cpp20
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.h28
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.ui22
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/my_add_executable.cmake5
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/myclass.cpp10
-rw-r--r--tests/manual/cmakeprojectmanager/hello-widgets/myclass.h16
-rw-r--r--tests/manual/deviceshell/tst_deviceshell.cpp38
-rw-r--r--tests/manual/fakevim/main.cpp15
-rw-r--r--tests/manual/layoutbuilder/comparison/CMakeLists.txt6
-rw-r--r--tests/manual/layoutbuilder/comparison/layoutbuilder/CMakeLists.txt46
-rw-r--r--tests/manual/layoutbuilder/comparison/layoutbuilder/main.cpp6
-rw-r--r--tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.cpp0
-rw-r--r--tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.h24
-rw-r--r--tests/manual/layoutbuilder/comparison/quick/CMakeLists.txt37
-rw-r--r--tests/manual/layoutbuilder/comparison/quick/Main.qml30
-rw-r--r--tests/manual/layoutbuilder/comparison/quick/main.cpp16
-rw-r--r--tests/manual/layoutbuilder/comparison/widgets/CMakeLists.txt40
-rw-r--r--tests/manual/layoutbuilder/comparison/widgets/main.cpp9
-rw-r--r--tests/manual/layoutbuilder/comparison/widgets/mainwindow.cpp24
-rw-r--r--tests/manual/layoutbuilder/comparison/widgets/mainwindow.h28
-rw-r--r--tests/manual/layoutbuilder/demo/CMakeLists.txt44
-rw-r--r--tests/manual/layoutbuilder/demo/main.cpp41
-rw-r--r--tests/manual/layoutbuilder/demo/mainwindow.cpp0
-rw-r--r--tests/manual/layoutbuilder/demo/mainwindow.h0
-rw-r--r--tests/manual/tasktree/main.cpp20
-rw-r--r--tests/manual/tasktree/taskwidget.cpp17
-rw-r--r--tests/manual/tasktree/taskwidget.h18
-rw-r--r--tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp2
-rw-r--r--tests/unit/CMakeLists.txt2
m---------tests/unit/unittest/3rdparty/googletest0
-rw-r--r--tests/unit/unittest/CMakeLists.txt25
-rw-r--r--tests/unit/unittest/asynchronousexplicitimagecache-test.cpp77
-rw-r--r--tests/unit/unittest/asynchronousimagecache-test.cpp174
-rw-r--r--tests/unit/unittest/asynchronousimagefactory-test.cpp8
-rw-r--r--tests/unit/unittest/compare-operators.h25
-rw-r--r--tests/unit/unittest/googletest.h2
-rw-r--r--tests/unit/unittest/gtest-creator-printing.cpp8
-rw-r--r--tests/unit/unittest/gtest-creator-printing.h1
-rw-r--r--tests/unit/unittest/imagecachecollectormock.h2
-rw-r--r--tests/unit/unittest/imagecachedispatchcollector-test.cpp41
-rw-r--r--tests/unit/unittest/imagecachegenerator-test.cpp192
-rw-r--r--tests/unit/unittest/imagecachestorage-test.cpp223
-rw-r--r--tests/unit/unittest/mockimagecachegenerator.h2
-rw-r--r--tests/unit/unittest/mockimagecachestorage.h6
-rw-r--r--tests/unit/unittest/projectstoragepathwatcher-test.cpp317
-rw-r--r--tests/unit/unittest/projectstoragepathwatchermock.h2
-rw-r--r--tests/unit/unittest/projectstorageupdater-test.cpp1242
-rw-r--r--tests/unit/unittest/qmldocumentparser-test.cpp53
-rw-r--r--tests/unit/unittest/qmltypesparser-test.cpp199
-rw-r--r--tests/unit/unittest/smallstring-test.cpp18
-rw-r--r--tests/unit/unittest/sqlitedatabase-test.cpp53
-rw-r--r--tests/unit/unittest/sqlitedatabasebackend-test.cpp25
-rw-r--r--tests/unit/unittest/sqlitedatabasemock.h7
-rw-r--r--tests/unit/unittest/sqlitefunctionregistry-test.cpp41
-rw-r--r--tests/unit/unittest/sqlitereadwritestatementmock.h2
-rw-r--r--tests/unit/unittest/sqlitetransaction-test.cpp34
-rw-r--r--tests/unit/unittest/sqlitewritestatementmock.h4
-rw-r--r--tests/unit/unittest/synchronousimagecache-test.cpp60
2972 files changed, 118060 insertions, 48386 deletions
diff --git a/.clang-format b/.clang-format
index 1b9871712f..0a0df0c152 100644
--- a/.clang-format
+++ b/.clang-format
@@ -23,19 +23,19 @@ AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
-AllowShortBlocksOnASingleLine: false
+AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: true
+AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
- AfterControlStatement: false
+ AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
@@ -99,7 +99,7 @@ PenaltyExcessCharacter: 50
PenaltyReturnTypeOnItsOwnLine: 300
PointerAlignment: Right
ReflowComments: false
-SortIncludes: true
+SortIncludes: CaseSensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
@@ -112,6 +112,6 @@ SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
-Standard: Cpp11
+Standard: c++11
TabWidth: 4
UseTab: Never
diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml
index 14f4a74751..661b6365b2 100644
--- a/.github/workflows/build_cmake.yml
+++ b/.github/workflows/build_cmake.yml
@@ -7,8 +7,8 @@ on:
- 'doc/**'
env:
- QT_VERSION: 6.4.3
- MACOS_DEPLOYMENT_TARGET: 10.14
+ QT_VERSION: 6.5.0
+ MACOS_DEPLOYMENT_TARGET: 10.15
CLANG_VERSION: 16.0.0
ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1
diff --git a/.gitignore-blame b/.gitignore-blame
new file mode 100644
index 0000000000..156cb8d806
--- /dev/null
+++ b/.gitignore-blame
@@ -0,0 +1,2 @@
+#bulk clang-format for src/plugins/qmldesigner/components/stateseditor/
+fef7852da53b84c7fb960b18fef8cda0b6663703
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d70dab026c..d52a1c8b65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -65,11 +65,24 @@ if(MSVC)
add_compile_options(/wd4573)
endif()
-find_package(Qt5
+find_package(Qt6
${IDE_QT_VERSION_MIN}
COMPONENTS Concurrent Core Gui Network PrintSupport Qml Sql Widgets Xml Core5Compat ${QT_TEST_COMPONENT}
REQUIRED
)
+# hack for Qbs which still supports Qt5 and Qt6
+if (TARGET Qt6::Core5CompatPrivate)
+ if (CMAKE_VERSION VERSION_LESS 3.18)
+ set_property(TARGET Qt6::Core5CompatPrivate PROPERTY IMPORTED_GLOBAL TRUE)
+ endif()
+ add_library(Qt6Core5CompatPrivate ALIAS Qt6::Core5CompatPrivate)
+endif()
+if (TARGET Qt6::Core5Compat)
+ if (CMAKE_VERSION VERSION_LESS 3.18)
+ set_property(TARGET Qt6::Core5Compat PROPERTY IMPORTED_GLOBAL TRUE)
+ endif()
+ add_library(Qt6Core5Compat ALIAS Qt6::Core5Compat)
+endif()
# Common intermediate directory for QML modules which are defined via qt_add_qml_module()
set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml_modules")
@@ -82,8 +95,8 @@ if (MSVC AND QT_FEATURE_static_runtime)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
-find_package(Qt5 COMPONENTS LinguistTools QUIET)
-find_package(Qt5 COMPONENTS Quick QuickWidgets Designer DesignerComponents Help SerialPort Svg Tools QUIET)
+find_package(Qt6 OPTIONAL_COMPONENTS Quick QuickWidgets Designer DesignerComponentsPrivate
+ Help SerialPort Svg Tools LinguistTools QUIET)
find_package(Threads)
find_package(Clang QUIET)
diff --git a/README.md b/README.md
index 590361d1ef..5a5301b3b5 100644
--- a/README.md
+++ b/README.md
@@ -747,3 +747,188 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
public domain worldwide. This software is distributed without any warranty.
http://creativecommons.org/publicdomain/zero/1.0/
+
+### WinPty
+
+ Implementation of a pseudo terminal for Windows.
+
+ https://github.com/rprichard/winpty
+
+ The MIT License (MIT)
+
+ Copyright (c) 2011-2016 Ryan Prichard
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+
+### ptyqt
+
+ Pty-Qt is small library for access to console applications by pseudo-terminal interface on Mac,
+ Linux and Windows. On Mac and Linux it uses standard PseudoTerminal API and on Windows it uses
+ WinPty(prefer) or ConPty.
+
+ https://github.com/kafeg/ptyqt
+
+ MIT License
+
+ Copyright (c) 2019 Vitaly Petrov, v31337@gmail.com
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+### libvterm
+
+ An abstract C99 library which implements a VT220 or xterm-like terminal emulator.
+ It doesn't use any particular graphics toolkit or output system, instead it invokes callback
+ function pointers that its embedding program should provide it to draw on its behalf.
+ It avoids calling malloc() during normal running state, allowing it to be used in embedded kernel
+ situations.
+
+ https://www.leonerd.org.uk/code/libvterm/
+
+ The MIT License
+
+ Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+### terminal/shellintegrations
+
+ The Terminal plugin uses scripts to integrate with the shell. The scripts are
+ located in the Qt Creator source tree in src/plugins/terminal/shellintegrations.
+
+ https://github.com/microsoft/vscode/tree/main/src/vs/workbench/contrib/terminal/browser/media
+
+ MIT License
+
+ Copyright (c) 2015 - present Microsoft Corporation
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+### terminal/shellintegrations/clink
+
+ The Terminal plugin uses a lua script to integrate with the cmd shell when using clink.
+
+ https://github.com/chrisant996/clink-gizmos
+
+ MIT License
+
+ Copyright (c) 2023 Chris Antos
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+### cmake
+
+ The CMake project manager uses the CMake lexer code for parsing CMake files
+
+ https://gitlab.kitware.com/cmake/cmake.git
+
+ CMake - Cross Platform Makefile Generator
+ Copyright 2000-2023 Kitware, Inc. and Contributors
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Kitware, Inc. nor the names of Contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cmake/FindQt5.cmake b/cmake/FindQt5.cmake
deleted file mode 100644
index fc16ec1723..0000000000
--- a/cmake/FindQt5.cmake
+++ /dev/null
@@ -1,103 +0,0 @@
-#.rst:
-# FindQt5
-# -------
-#
-# Qt5 wrapper around Qt6 CMake code.
-#
-
-unset(__arguments)
-if (Qt5_FIND_QUIETLY)
- list(APPEND __arguments QUIET)
-endif()
-if (Qt5_FIND_REQUIRED)
- list(APPEND __arguments REQUIRED)
-endif()
-
-if (Qt5_FIND_COMPONENTS)
- # for some reason QUIET doesn't really work when passed to the arguments list
- if (Qt5_FIND_QUIETLY)
- list(APPEND __arguments OPTIONAL_COMPONENTS)
- else()
- list(APPEND __arguments COMPONENTS)
- endif()
-endif()
-
-find_package(Qt6 ${Qt5_FIND_VERSION} CONFIG COMPONENTS Core QUIET)
-if (NOT Qt6_FOUND)
- # remove Core5Compat from components to find in Qt5, but add a dummy target,
- # which unfortunately cannot start with "Qt6::"
- # also remove Tools, where some tools have moved in Qt6, e.g. from Help
- list(REMOVE_ITEM Qt5_FIND_COMPONENTS Core5Compat)
- list(REMOVE_ITEM Qt5_FIND_COMPONENTS Tools)
- find_package(Qt5 ${Qt5_FIND_VERSION} CONFIG ${__arguments} ${Qt5_FIND_COMPONENTS})
- if (NOT TARGET Qt6Core5Compat)
- add_library(Qt6Core5Compat INTERFACE)
- endif()
-
- # Remove Qt6 from the not found packages in Qt5 mode
- get_property(not_found_packages GLOBAL PROPERTY "PACKAGES_NOT_FOUND")
- if(not_found_packages)
- list(REMOVE_ITEM not_found_packages Qt6)
- set_property(GLOBAL PROPERTY "PACKAGES_NOT_FOUND" "${not_found_packages}")
- endif()
- return()
-else()
- # since Qt 6.2 some components are renamed to *Private
- foreach(possible_private_libs DesignerComponents QmlDebug)
- list(FIND Qt5_FIND_COMPONENTS ${possible_private_libs} dcIndex)
- if(dcIndex GREATER_EQUAL 0)
- find_package(Qt6${possible_private_libs}Private CONFIG QUIET)
- if(TARGET Qt6::${possible_private_libs}Private)
- set_property(TARGET Qt6::${possible_private_libs}Private PROPERTY IMPORTED_GLOBAL TRUE)
- add_library(Qt5::${possible_private_libs} ALIAS Qt6::${possible_private_libs}Private)
- list(REMOVE_AT Qt5_FIND_COMPONENTS ${dcIndex})
- endif()
- endif()
- endforeach()
- find_package(Qt6 CONFIG ${__arguments} ${Qt5_FIND_COMPONENTS})
-endif()
-
-set(__additional_imported_components ATSPI2_nolink) # Work around QTBUG-97023
-foreach(comp IN LISTS Qt5_FIND_COMPONENTS __additional_imported_components)
- if(TARGET Qt6::${comp})
- if (NOT TARGET Qt5::${comp})
- if (NOT QT_FEATURE_static)
- set_property(TARGET Qt6::${comp} PROPERTY IMPORTED_GLOBAL TRUE)
- endif()
- add_library(Qt5::${comp} ALIAS Qt6::${comp})
- endif()
- if (TARGET Qt6::${comp}Private AND NOT TARGET Qt5::${comp}Private)
- if (NOT QT_FEATURE_static)
- set_property(TARGET Qt6::${comp}Private PROPERTY IMPORTED_GLOBAL TRUE)
- endif()
- add_library(Qt5::${comp}Private ALIAS Qt6::${comp}Private)
- endif()
- endif()
-endforeach()
-
-# alias Qt6::Core5Compat to Qt6Core5Compat to make consistent with Qt5 path
-if (TARGET Qt6::Core5Compat AND NOT TARGET Qt6Core5Compat)
- add_library(Qt6Core5Compat ALIAS Qt6::Core5Compat)
-endif()
-
-set(Qt5_FOUND ${Qt6_FOUND})
-set(Qt5_VERSION ${Qt6_VERSION})
-
-foreach(tool qmake lrelease lupdate moc rcc qhelpgenerator)
- if (TARGET Qt6::${tool} AND NOT TARGET Qt5::${tool})
- add_executable(Qt5::${tool} IMPORTED GLOBAL)
- get_target_property(imported_location Qt6::${tool} IMPORTED_LOCATION)
- # handle separate tools for each configuration
- if (NOT imported_location)
- get_target_property(imported_location Qt6::${tool} IMPORTED_LOCATION_RELEASE)
- endif()
- set_target_properties(Qt5::${tool} PROPERTIES IMPORTED_LOCATION "${imported_location}")
- endif()
-endforeach()
-
-if (NOT DEFINED qt5_wrap_cpp)
- function(qt5_wrap_cpp outfiles)
- qt6_wrap_cpp(${outfiles} ${ARGN})
- set(${outfiles} ${${outfiles}} PARENT_SCOPE)
- endfunction()
-endif()
diff --git a/cmake/QtCreatorAPI.cmake b/cmake/QtCreatorAPI.cmake
index 70b8114fb9..effbb45404 100644
--- a/cmake/QtCreatorAPI.cmake
+++ b/cmake/QtCreatorAPI.cmake
@@ -188,7 +188,7 @@ function(add_qtc_library name)
set(TEST_DEFINES WITH_TESTS SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
endif()
- if(_arg_STATIC AND UNIX)
+ if((_arg_STATIC OR _arg_OBJECT) AND UNIX)
# not added by Qt if reduce_relocations is turned off for it
set_target_properties(${name} PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
@@ -628,6 +628,7 @@ function(add_qtc_executable name)
update_cached_list(__QTC_EXECUTABLES "${name}")
+ condition_info(_extra_text _arg_CONDITION)
if (NOT _arg_CONDITION)
set(_arg_CONDITION ON)
endif()
@@ -648,6 +649,9 @@ function(add_qtc_executable name)
else()
set(_executable_enabled OFF)
endif()
+ if (NOT _arg_INTERNAL_ONLY)
+ add_feature_info("Executable ${name}" _executable_enabled "${_extra_text}")
+ endif()
if (NOT _executable_enabled)
return()
endif()
@@ -817,7 +821,7 @@ endfunction()
function(add_qtc_test name)
cmake_parse_arguments(_arg "GTEST;MANUALTEST;EXCLUDE_FROM_PRECHECK" "TIMEOUT"
- "DEFINES;DEPENDS;INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;SKIP_PCH;CONDITION" ${ARGN})
+ "DEFINES;DEPENDS;INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;SKIP_PCH;CONDITION;PROPERTIES" ${ARGN})
if (${_arg_UNPARSED_ARGUMENTS})
message(FATAL_ERROR "add_qtc_test had unparsed arguments!")
@@ -877,6 +881,7 @@ function(add_qtc_test name)
VISIBILITY_INLINES_HIDDEN ON
BUILD_RPATH "${_RPATH_BASE}/${_RPATH};${CMAKE_BUILD_RPATH}"
INSTALL_RPATH "${_RPATH_BASE}/${_RPATH};${CMAKE_INSTALL_RPATH}"
+ ${_arg_PROPERTIES}
)
if (NOT _arg_SKIP_PCH)
enable_pch(${name})
diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake
index efe7efd9ce..ecb6d17f36 100644
--- a/cmake/QtCreatorAPIInternal.cmake
+++ b/cmake/QtCreatorAPIInternal.cmake
@@ -182,7 +182,7 @@ endfunction()
function(qtc_add_link_flags_no_undefined target)
# needs CheckLinkerFlags
- if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18)
+ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18 AND NOT MSVC)
set(no_undefined_flag "-Wl,--no-undefined")
check_linker_flag(CXX ${no_undefined_flag} QTC_LINKER_SUPPORTS_NO_UNDEFINED)
if (NOT QTC_LINKER_SUPPORTS_NO_UNDEFINED)
@@ -224,7 +224,7 @@ function(set_explicit_moc target_name file)
set(file_dependencies DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.json")
endif()
set_property(SOURCE "${file}" PROPERTY SKIP_AUTOMOC ON)
- qt5_wrap_cpp(file_moc "${file}" ${file_dependencies})
+ qt_wrap_cpp(file_moc "${file}" ${file_dependencies})
target_sources(${target_name} PRIVATE "${file_moc}")
endfunction()
@@ -413,6 +413,7 @@ function(enable_pch target)
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
CXX_EXTENSIONS OFF
+ POSITION_INDEPENDENT_CODE ON
)
target_link_libraries(${pch_target} PRIVATE ${pch_dependency})
endif()
@@ -464,7 +465,7 @@ function(extend_qtc_target target_name)
cmake_parse_arguments(_arg
""
"SOURCES_PREFIX;SOURCES_PREFIX_FROM_TARGET;FEATURE_INFO"
- "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES"
+ "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;PUBLIC_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PROPERTIES;SOURCES_PROPERTIES"
${ARGN}
)
@@ -546,4 +547,8 @@ function(extend_qtc_target target_name)
if (_arg_PROPERTIES)
set_target_properties(${target_name} PROPERTIES ${_arg_PROPERTIES})
endif()
+
+ if (_arg_SOURCES_PROPERTIES)
+ set_source_files_properties(${_arg_SOURCES} PROPERTIES ${_arg_SOURCES_PROPERTIES})
+ endif()
endfunction()
diff --git a/cmake/QtCreatorDocumentation.cmake b/cmake/QtCreatorDocumentation.cmake
index bc18f79828..8fa0c58d93 100644
--- a/cmake/QtCreatorDocumentation.cmake
+++ b/cmake/QtCreatorDocumentation.cmake
@@ -10,7 +10,7 @@ add_feature_info("Build online documentation" WITH_ONLINE_DOCS "")
# Used for QT_INSTALL_DOCS
function(qt5_query_qmake)
if (NOT TARGET Qt::qmake)
- message(FATAL_ERROR "Qmake was not found. Add find_package(Qt5 COMPONENTS Core) to CMake to enable.")
+ message(FATAL_ERROR "Qmake was not found. Add find_package(Qt6 COMPONENTS Core) to CMake to enable.")
endif()
# dummy check for if we already queried qmake
if (QT_INSTALL_BINS)
@@ -142,7 +142,7 @@ function(_setup_qhelpgenerator_targets _qdocconf_file _html_outputdir)
endif()
if (NOT TARGET Qt::qhelpgenerator)
- message(WARNING "qhelpgenerator missing: No QCH documentation targets were generated. Add find_package(Qt5 COMPONENTS Help) to CMake to enable.")
+ message(WARNING "qhelpgenerator missing: No QCH documentation targets were generated. Add find_package(Qt6 COMPONENTS Help) to CMake to enable.")
return()
endif()
diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake
index 6f94c1fd90..0d9e4b8186 100644
--- a/cmake/QtCreatorIDEBranding.cmake
+++ b/cmake/QtCreatorIDEBranding.cmake
@@ -1,6 +1,6 @@
-set(IDE_VERSION "10.0.2") # The IDE version.
-set(IDE_VERSION_COMPAT "10.0.0") # The IDE Compatibility version.
-set(IDE_VERSION_DISPLAY "10.0.2") # The IDE display version.
+set(IDE_VERSION "10.0.82") # The IDE version.
+set(IDE_VERSION_COMPAT "10.0.82") # The IDE Compatibility version.
+set(IDE_VERSION_DISPLAY "11.0.0-beta1") # The IDE display version.
set(IDE_COPYRIGHT_YEAR "2023") # The IDE current copyright year.
set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation.
diff --git a/cmake/QtCreatorTranslations.cmake b/cmake/QtCreatorTranslations.cmake
index 7c795f65a0..4803d66be5 100644
--- a/cmake/QtCreatorTranslations.cmake
+++ b/cmake/QtCreatorTranslations.cmake
@@ -30,7 +30,7 @@ function(_extract_ts_data_from_targets outprefix)
set(_target_sources "")
if(_source_files)
- list(FILTER _source_files EXCLUDE REGEX ".*[.]json[.]in|.*[.]svg")
+ list(FILTER _source_files EXCLUDE REGEX ".*[.]json[.]in|.*[.]svg|.*[.]pro|.*[.]css")
list(APPEND _target_sources ${_source_files})
endif()
if(_extra_translations)
@@ -130,7 +130,7 @@ endfunction()
function(add_translation_targets file_prefix)
if (NOT TARGET Qt::lrelease OR NOT TARGET Qt::lupdate)
# No Qt translation tools were found: Skip this directory
- message(WARNING "No Qt translation tools found, skipping translation targets. Add find_package(Qt5 COMPONENTS LinguistTools) to CMake to enable.")
+ message(WARNING "No Qt translation tools found, skipping translation targets. Add find_package(Qt6 COMPONENTS LinguistTools) to CMake to enable.")
return()
endif()
diff --git a/coin/instructions/build.yaml b/coin/instructions/build.yaml
index 401db50562..abeb792c4d 100644
--- a/coin/instructions/build.yaml
+++ b/coin/instructions/build.yaml
@@ -66,6 +66,9 @@ instructions:
userMessageOnFailure: "Failed to run build.py, check logs."
- type: ChangeDirectory
directory: "{{.AgentWorkingDir}}/build/tqtc-qtsdk/packaging_tools"
+ - type: EnvironmentVariable
+ variableName: MACOSX_DEPLOYMENT_TARGET
+ variableValue: "{{.Env.SDKTOOL_MACOSX_DEPLOYMENT_TARGET}}"
- type: ExecuteCommand
command: "python3 -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}/build/sdktool/qt --src {{.AgentWorkingDir}}/qt-creator/qt-creator/src/tools/sdktool --build {{.AgentWorkingDir}}/build/sdktool/build --install {{.AgentWorkingDir}}/build/sdktool/install --make-command make"
maxTimeInSeconds: 36000
diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml
index d3c072f0a9..b8be5f5c4a 100644
--- a/coin/instructions/common_environment.yaml
+++ b/coin/instructions/common_environment.yaml
@@ -10,12 +10,15 @@ instructions:
variableValue: http://master.qt.io/development_releases/prebuilt/libclang/libclang-release_16.0.0-based
- type: EnvironmentVariable
variableName: QTC_QT_BASE_URL
- variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.4/6.4.3-released/Qt"
+ variableValue: "http://ci-files02-hki.intra.qt.io/packages/jenkins/archive/qt/6.5/6.5.0-released/Qt"
- type: EnvironmentVariable
variableName: QTC_QT_MODULES
variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine"
- type: EnvironmentVariable
variableName: MACOSX_DEPLOYMENT_TARGET
+ variableValue: 10.15
+ - type: EnvironmentVariable
+ variableName: SDKTOOL_MACOSX_DEPLOYMENT_TARGET
variableValue: 10.14
- type: EnvironmentVariable
variableName: QTC_SDKTOOL_QT_BASE_URL
diff --git a/coin/product_dependencies.yaml b/coin/product_dependencies.yaml
index cbcc2bbf7e..4212b9b2e2 100644
--- a/coin/product_dependencies.yaml
+++ b/coin/product_dependencies.yaml
@@ -1,4 +1,4 @@
dependencies:
- ../../qt/tqtc-qt5.git:
- ref: "tqtc/lts-6.2"
+ ../../qt/qt5.git:
+ ref: "6.5"
diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf
index 4ae4dc1617..97e6fa7593 100644
--- a/doc/config/macros.qdocconf
+++ b/doc/config/macros.qdocconf
@@ -50,6 +50,7 @@ macro.beginfloatright.HTML = "<div style=\"float: right; margin-left: 2em\">"
macro.endfloat.HTML = "</div>"
macro.clearfloat.HTML = "<br style=\"clear: both\" />"
macro.emptyspan.HTML = "<span></span>"
+macro.externallink.HTML = "<a href=\"\1\" target=\"_blank\">\2</a>"
# Embed YouTube content by video ID - Example: \youtube dQw4w9WgXcQ
# Also requires a <ID>.jpg thumbnail for offline docs. In .qdocconf, add:
diff --git a/doc/qtcreator/config/style/qt5-sidebar.html b/doc/qtcreator/config/style/qt5-sidebar.html
index e38e76483a..48111154eb 100644
--- a/doc/qtcreator/config/style/qt5-sidebar.html
+++ b/doc/qtcreator/config/style/qt5-sidebar.html
@@ -1,6 +1,6 @@
<div class="sectionlist normallist">
<ul>
- <li><a href="qtcreator-toc.html">All Topics</a></li>
+ <li><a href="qtcreator-toc.html">Click Here for a List of All Topics</a></li>
</ul>
</div>
<div class="sectionlist normallist">
diff --git a/doc/qtcreator/images/creator_advanceduse.svg b/doc/qtcreator/images/creator_advanceduse.svg
deleted file mode 100644
index 7a7b77186b..0000000000
--- a/doc/qtcreator/images/creator_advanceduse.svg
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M435.78,272H14.19C6.36,272,0,265.64,0,257.81V14.21C0,6.37,6.36,0.01,14.19,0.01h421.58
- c7.84,0,14.19,6.36,14.19,14.19v243.6C449.97,265.64,443.61,272,435.78,272z"/>
- <path fill="#878686" d="M0.44,19.66L0,14.19C0,6.36,6.35,0.01,14.18,0.01h421.61c7.83,0,14.18,6.35,14.18,14.18v6.84"/>
- <circle fill="#DD5958" cx="17.43" cy="10.66" r="5"/>
- <circle fill="#DBC558" cx="34.41" cy="10.66" r="5"/>
- <circle fill="#52B257" cx="51.4" cy="10.66" r="5"/>
-</g>
-<path fill="#FFFFFF" d="M310.9,130.29c-5.72-1.44-12.54-1.96-19.95-2.96c-1.52-5.95-3.87-11.55-6.92-16.72
- c4.57-6.01,9.08-11.24,12.13-16.37c0.86-1.44,1.03-3.56-0.1-4.69L282,75.49c-1.13-1.13-3.23-0.97-4.69-0.1
- c-5.11,3.05-10.33,7.56-16.35,12.11c-5.11-3.01-10.66-5.35-16.55-6.9c-1.03-7.53-1.54-14.47-3.01-20.28
- c-0.41-1.65-1.81-3.25-3.4-3.25h-19.89c-1.59,0-2.99,1.61-3.4,3.25c-1.46,5.76-1.96,12.66-2.99,20.13
- c-5.91,1.5-11.49,3.81-16.61,6.79c-6.05-4.59-11.3-9.14-16.45-12.21c-1.44-0.86-3.56-1.01-4.69,0.1L159.92,89.2
- c-1.13,1.13-0.97,3.25-0.1,4.69c3.03,5.11,7.51,10.29,12.04,16.26c-3.07,5.15-5.48,10.75-7.06,16.68
- c-7.43,1.01-14.31,1.52-20.05,2.99c-1.65,0.41-3.25,1.79-3.25,3.38v19.89c0,1.59,1.61,2.99,3.25,3.4
- c5.68,1.44,12.43,1.96,19.78,2.94c1.5,6.03,3.85,11.73,6.9,16.98c-4.51,5.91-8.93,11.08-11.96,16.12c-0.86,1.46-1.01,3.58,0.1,4.69
- l14.06,14.06c1.13,1.13,3.25,0.97,4.69,0.1c5-2.96,10.11-7.35,15.93-11.8c5.25,3.15,10.97,5.58,17.05,7.16
- c0.99,7.31,1.5,14.04,2.94,19.72c0.41,1.63,1.79,3.23,3.38,3.23c6.63,0,13.26,0,19.89,0c1.59,0,2.99-1.61,3.4-3.23
- c1.42-5.64,1.93-12.33,2.92-19.58c6.07-1.54,11.82-3.93,17.09-7.04c5.89,4.47,11.01,8.87,16.04,11.88c1.46,0.86,3.58,1.03,4.69-0.1
- l14.06-14.06c1.13-1.13,0.97-3.23,0.1-4.69c-2.99-5.02-7.39-10.15-11.86-16.02c3.09-5.23,5.48-10.91,7.02-16.92
- c7.39-1.01,14.21-1.52,19.93-2.96c1.63-0.41,3.23-1.81,3.23-3.4v-19.89C314.13,132.1,312.53,130.7,310.9,130.29z M227.41,175.67
- c-18.07,0-32.71-14.65-32.71-32.71s14.65-32.71,32.71-32.71s32.71,14.65,32.71,32.71S245.48,175.67,227.41,175.67z"/>
-</svg>
diff --git a/doc/qtcreator/images/creator_buildingrunning.svg b/doc/qtcreator/images/creator_buildingrunning.svg
deleted file mode 100644
index 3d4808cdb5..0000000000
--- a/doc/qtcreator/images/creator_buildingrunning.svg
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M377.11,224.13H33.89c-7.84,0-14.19-6.36-14.19-14.19V13.71c0-7.84,6.36-14.19,14.19-14.19h343.21
- c7.84,0,14.19,6.36,14.19,14.19v196.23C391.3,217.78,384.95,224.13,377.11,224.13z"/>
- <path fill="#878686" d="M20.06,15.74l-0.37-4.51c0-6.47,5.24-11.71,11.71-11.71h348.18c6.47,0,11.71,5.24,11.71,11.71v5.65"/>
- <circle fill="#DD5958" cx="34.09" cy="8.31" r="4.13"/>
- <circle fill="#DBC558" cx="48.12" cy="8.31" r="4.13"/>
- <circle fill="#52B257" cx="62.14" cy="8.31" r="4.13"/>
-</g>
-<rect x="63.92" y="25.97" fill="#949292" width="277.09" height="185.41"/>
-<g>
- <path fill="none" stroke="#FFFFFF" stroke-width="7.8665" stroke-linecap="round" stroke-miterlimit="10" d="M153.93,111.58
- c0-26.07,21.13-47.2,47.2-47.2s47.2,21.13,47.2,47.2s-21.13,47.2-47.2,47.2"/>
- <circle fill="#FFFFFF" cx="200.14" cy="111.25" r="21.12"/>
- <text transform="matrix(1 0 0 1 123.5649 108.1074)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="26.1845px">1</text>
- <text transform="matrix(1 0 0 1 248.9873 71.7993)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="26.1845px">2</text>
- <text transform="matrix(1 0 0 1 202.1196 190.623)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="26.1845px">3</text>
-</g>
-<g>
- <g>
- <path fill="#C9C7C7" d="M412.27,272h-73c-7.84,0-14.19-6.36-14.19-14.19V89.58c0-7.84,6.36-14.19,14.19-14.19h73
- c7.84,0,14.19,6.36,14.19,14.19v168.22C426.47,265.64,420.11,272,412.27,272z"/>
- <path fill="#878686" d="M325.4,89.59l-0.32-3.95c0-5.66,4.59-10.25,10.25-10.25h80.88c5.66,0,10.25,4.59,10.25,10.25v4.94"/>
- </g>
-
- <rect x="302.37" y="133.85" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 551.7495 -200.3197)" fill="#949292" width="147.32" height="83.73"/>
- <g>
- <path fill="none" stroke="#FFFFFF" stroke-width="3.7066" stroke-linecap="round" stroke-miterlimit="10" d="M355.31,172.15
- c0-12.28,9.96-22.24,22.24-22.24c12.28,0,22.24,9.96,22.24,22.24c0,12.28-9.96,22.24-22.24,22.24"/>
- <circle fill="#FFFFFF" cx="377.08" cy="171.99" r="9.95"/>
- <text transform="matrix(1 0 0 1 341 170.5078)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="12.338px">1</text>
- <text transform="matrix(1 0 0 1 400.0977 153.4004)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="12.338px">2</text>
- <text transform="matrix(1 0 0 1 378.0137 209.3896)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="12.338px">3</text>
- </g>
-</g>
-</svg>
diff --git a/doc/qtcreator/images/creator_coding.svg b/doc/qtcreator/images/creator_coding.svg
deleted file mode 100644
index 39149cdf39..0000000000
--- a/doc/qtcreator/images/creator_coding.svg
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M435.81,271.62H14.41c-7.84,0-14.19-6.36-14.19-14.19V13.94c0-7.84,6.36-14.19,14.19-14.19h421.4
- c7.84,0,14.19,6.36,14.19,14.19v243.49C450,265.27,443.64,271.62,435.81,271.62z"/>
- <path fill="#878686" d="M0.66,19.38l-0.44-5.46c0-7.83,6.35-14.18,14.18-14.18h421.43c7.83,0,14.18,6.35,14.18,14.18v6.84"/>
- <circle fill="#DD5958" cx="17.63" cy="10.39" r="4.99"/>
- <circle fill="#DBC558" cx="34.61" cy="10.39" r="4.99"/>
- <circle fill="#52B257" cx="51.59" cy="10.39" r="4.99"/>
-</g>
-<rect x="66.15" y="30.54" fill="#444345" width="343.54" height="233.69"/>
-<rect x="40.18" y="30.54" fill="#333333" width="25.97" height="233.69"/>
-<text transform="matrix(1 0 0 1 51.6992 49.5161)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">1</text>
-<text transform="matrix(1 0 0 1 51.6992 67.8916)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">2</text>
-<text transform="matrix(1 0 0 1 51.6992 86.2676)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">3</text>
-<text transform="matrix(1 0 0 1 51.6992 104.6431)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">4</text>
-<text transform="matrix(1 0 0 1 51.6992 123.0186)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">5</text>
-<text transform="matrix(1 0 0 1 51.6992 141.3945)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">6</text>
-<text transform="matrix(1 0 0 1 51.6992 159.7695)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">7</text>
-<text transform="matrix(1 0 0 1 51.6992 178.1455)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">8</text>
-<text transform="matrix(1 0 0 1 51.6992 196.5215)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">9</text>
-<text transform="matrix(1 0 0 1 43.3105 214.8965)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">10</text>
-<text transform="matrix(1 0 0 1 43.3105 233.2725)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">11</text>
-<text transform="matrix(1 0 0 1 43.3105 251.6484)" fill="#FFFFFF" font-family="'IBMPlexMono'" font-size="13.9815px">12</text>
-<text transform="matrix(1 0 0 1 78.064 49.5161)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">// main.cpp</text>
-<text transform="matrix(1 0 0 1 78.064 67.8916)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">#include &lt;QApplication&gt;</text>
-<text transform="matrix(1 0 0 1 78.064 86.2676)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">#include &lt;QTableView&gt;</text>
-<text transform="matrix(1 0 0 1 78.064 104.6431)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">#include &quot;mymodel.h&quot;</text>
-<text transform="matrix(1 0 0 1 78.064 141.3945)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">int main(int argc, char *argv[])</text>
-<text transform="matrix(1 0 0 1 78.064 178.1455)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">{</text>
-<text transform="matrix(1 0 0 1 78.064 196.5215)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px"> QApplication a(argc, argv);</text>
-<text transform="matrix(1 0 0 1 78.064 214.8965)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px"> QTableView tableView;</text>
-<text transform="matrix(1 0 0 1 78.064 233.2725)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px"> MyModel myModel;</text>
-<text transform="matrix(1 0 0 1 78.064 251.6484)" fill="#FFFFFF" font-family="'IBMPlexMono-Light'" font-size="13.9815px">}</text>
-</svg>
diff --git a/doc/qtcreator/images/creator_designinguserinterface.svg b/doc/qtcreator/images/creator_designinguserinterface.svg
deleted file mode 100644
index b3aff29240..0000000000
--- a/doc/qtcreator/images/creator_designinguserinterface.svg
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M435.81,271.62H14.83c-7.84,0-14.19-6.36-14.19-14.19V14.19C0.63,6.36,6.99,0,14.83,0h420.98
- C443.64,0,450,6.36,450,14.19v243.23C450,265.27,443.64,271.62,435.81,271.62z"/>
- <path fill="#878686" d="M1.08,19.62l-0.44-5.45C0.63,6.34,6.97,0,14.8,0h421.04C443.66,0,450,6.34,450,14.16v6.83"/>
- <circle fill="#DD5958" cx="18.04" cy="10.64" r="4.99"/>
- <circle fill="#DBC558" cx="35" cy="10.64" r="4.99"/>
- <circle fill="#52B257" cx="51.96" cy="10.64" r="4.99"/>
-</g>
-<path fill="#949292" d="M98.62,245.03H18.8c-2.75,0-5-2.25-5-5V47.72c0-2.75,2.25-5,5-5h79.81c2.75,0,5,2.25,5,5v192.3
- C103.62,242.78,101.37,245.03,98.62,245.03z"/>
-<path fill="none" stroke="#FFFFFF" stroke-width="11.8898" stroke-linecap="round" stroke-miterlimit="10" d="M177.01,125.88
- c0-39.4,31.94-71.34,71.34-71.34s71.34,31.94,71.34,71.34s-31.94,71.34-71.34,71.34"/>
-<path fill="none" stroke="#FFFFFF" stroke-width="2.5935" stroke-linecap="round" stroke-miterlimit="10" d="M44.02,68.87
- c0-8.59,6.97-15.56,15.56-15.56s15.56,6.97,15.56,15.56s-6.97,15.56-15.56,15.56"/>
-<circle fill="#FFFFFF" cx="246.85" cy="125.38" r="31.93"/>
-<polygon fill="#878686" points="252.27,115.11 247.86,173.16 266.03,151.21 293.54,155.76 "/>
-<text transform="matrix(1 0 0 1 131.1162 120.6279)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="39.5765px">1</text>
-<text transform="matrix(1 0 0 1 320.6885 65.7515)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="39.5765px">2</text>
-<text transform="matrix(1 0 0 1 249.8477 245.3447)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="39.5765px">3</text>
-<g>
- <path fill="#FFFFFF" d="M72.84,134.43H42.91c-2.75,0-5-2.25-5-5v-9.3c0-2.75,2.25-5,5-5h29.93c2.75,0,5,2.25,5,5v9.3
- C77.84,132.18,75.59,134.43,72.84,134.43z"/>
- <text transform="matrix(1 0 0 1 49.1719 128.6772)" fill="#C9C7C7" font-family="'TitilliumWeb-Bold'" font-size="11.9663px">ON</text>
-</g>
-<g>
- <path fill="#FFFFFF" d="M57.58,180.2h0.19c0.62,0,1.13-0.51,1.13-1.13v-10.95c0-0.62-0.51-1.13-1.13-1.13h-0.19
- c-0.62,0-1.13,0.51-1.13,1.13v10.95C56.45,179.69,56.96,180.2,57.58,180.2z"/>
- <path fill="#FFFFFF" d="M84.49,175.31H31.25c-0.95,0-1.72-0.77-1.72-1.72v0c0-0.95,0.77-1.72,1.72-1.72h53.23
- c0.95,0,1.72,0.77,1.72,1.72v0C86.21,174.54,85.43,175.31,84.49,175.31z"/>
- <path fill="#E0E1E0" d="M58.93,171.87h25.55c0.95,0,1.73,0.77,1.73,1.72v0c0,0.95-0.78,1.72-1.73,1.72H58.93V171.87z"/>
-</g>
-<g>
- <path fill="none" stroke="#FFFFFF" stroke-width="0.5769" stroke-miterlimit="10" d="M74.13,228.45H42.6
- c-6.13,0-11.15-5.02-11.15-11.15v0c0-6.13,5.02-11.15,11.15-11.15h31.54c6.13,0,11.15,5.02,11.15,11.15v0
- C85.29,223.43,80.27,228.45,74.13,228.45z"/>
- <circle fill="#FFFFFF" cx="43.65" cy="217.2" r="7.88"/>
-</g>
-<line fill="none" stroke="#C9C7C7" stroke-width="0.9977" stroke-miterlimit="10" x1="13.16" y1="98.25" x2="103.47" y2="98.25"/>
-<line fill="none" stroke="#C9C7C7" stroke-width="0.9977" stroke-miterlimit="10" x1="13.16" y1="150.11" x2="103.47" y2="150.11"/>
-<line fill="none" stroke="#C9C7C7" stroke-width="0.9977" stroke-miterlimit="10" x1="13.16" y1="195.79" x2="103.47" y2="195.79"/>
-</svg>
diff --git a/doc/qtcreator/images/creator_gettinghelp.svg b/doc/qtcreator/images/creator_gettinghelp.svg
deleted file mode 100644
index 6663ed53eb..0000000000
--- a/doc/qtcreator/images/creator_gettinghelp.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C8C6C6" d="M435.91,272.12H15.19C7.36,272.12,1,265.76,1,257.92V14.85C1,7.01,7.36,0.66,15.19,0.66h420.71
- c7.84,0,14.19,6.36,14.19,14.19v243.07C450.1,265.76,443.75,272.12,435.91,272.12z"/>
- <path fill="#868585" d="M1.44,20.26L1,14.81C1,6.99,7.34,0.66,15.15,0.66h420.79c7.82,0,14.15,6.34,14.15,14.15v6.83"/>
- <circle fill="#DC5958" cx="18.39" cy="11.29" r="4.99"/>
- <circle fill="#DAC457" cx="35.35" cy="11.29" r="4.99"/>
- <circle fill="#51B156" cx="52.3" cy="11.29" r="4.99"/>
-</g>
-<circle fill="#FFFFFF" cx="222.34" cy="144.43" r="64.54"/>
-<rect x="208.26" y="124.62" fill="none" width="314.12" height="137.75"/>
-<text transform="matrix(1 0 0 1 208.2568 170.8926)" fill="#C8C6C6" font-family="'TitilliumWeb-Bold'" font-size="71.7954px">?</text>
-</svg>
diff --git a/doc/qtcreator/images/creator_gettingstarted.svg b/doc/qtcreator/images/creator_gettingstarted.svg
deleted file mode 100644
index 779ae13947..0000000000
--- a/doc/qtcreator/images/creator_gettingstarted.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M435.78,272H15.41c-7.84,0-14.19-6.36-14.19-14.19V14.94c0-7.84,6.36-14.19,14.19-14.19h420.37
- c7.84,0,14.19,6.36,14.19,14.19v242.86C449.97,265.64,443.61,272,435.78,272z"/>
- <path fill="#878686" d="M1.66,20.34l-0.44-5.45c0-7.81,6.33-14.14,14.14-14.14h420.47c7.81,0,14.14,6.33,14.14,14.14v6.82"/>
- <circle fill="#DD5958" cx="18.6" cy="11.37" r="4.98"/>
- <circle fill="#DBC558" cx="35.53" cy="11.37" r="4.98"/>
- <circle fill="#52B257" cx="52.47" cy="11.37" r="4.98"/>
-</g>
-<g>
- <g>
- <path fill="#FFFFFF" d="M303.47,70.58c-2.26,40.17-11.3,68.54-51.47,101.18c0,17.07-6.78,34.15-20.59,46.2l-22.85-25.61
- c-4.77,4.52-11.05,6.78-17.07,7.03c-8.03,0.25-15.82-2.76-21.59-9.04c-10.04-11.3-9.04-28.62,2.01-38.66l-22.85-25.61
- c13.81-12.3,31.38-17.32,48.46-15.32C234.17,74.35,263.3,68.32,303.47,70.58z M261.79,132.34c7.53-6.78,8.03-18.08,1.51-25.61
- c-3.77-4.02-8.79-6.28-14.31-6.03c-4.27,0.25-8.29,1.76-11.3,4.52c-7.53,6.78-8.03,18.08-1.51,25.61
- c3.77,4.02,8.79,6.28,14.31,6.03C254.51,136.61,258.53,135.11,261.79,132.34z"/>
- </g>
-</g>
-</svg>
diff --git a/doc/qtcreator/images/creator_managingprojects.svg b/doc/qtcreator/images/creator_managingprojects.svg
deleted file mode 100644
index fb934268a0..0000000000
--- a/doc/qtcreator/images/creator_managingprojects.svg
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M434.78,272H14.41c-7.84,0-14.19-6.36-14.19-14.19V14.94c0-7.84,6.36-14.19,14.19-14.19h420.37
- c7.84,0,14.19,6.36,14.19,14.19V257.8C448.97,265.64,442.61,272,434.78,272z"/>
- <path fill="#878686" d="M0.66,20.34l-0.44-5.45c0-7.81,6.33-14.14,14.14-14.14h420.47c7.81,0,14.14,6.33,14.14,14.14v6.82"/>
- <circle fill="#DD5958" cx="17.6" cy="11.37" r="4.98"/>
- <circle fill="#DBC558" cx="34.53" cy="11.37" r="4.98"/>
- <circle fill="#52B257" cx="51.47" cy="11.37" r="4.98"/>
-</g>
-<g transform="translate(0,-280.06666)">
- <path fill="#878686" d="M154.99,354.72c-3,0-5.44,2.44-5.44,5.44l0,0v27.15v97.8c0,3,2.44,5.44,5.44,5.44h141.27
- c3,0,5.44-2.44,5.44-5.44v-97.8c0-3-2.44-5.44-5.44-5.44H252.9l-17.39-24.85c-1.02-1.45-2.68-2.31-4.45-2.31h-76.07V354.72z"/>
-</g>
-<path fill="#878686" d="M107.69,205.06h-96.9V79.34h79.11c0.34,0,17.09,18.5,17.79,17.79C107.69,97.14,107.69,205.06,107.69,205.06z
- "/>
-<path fill="#878686" d="M440.85,206.15h-96.9V80.43h79.11c0.34,0,17.09,18.5,17.79,17.79L440.85,206.15L440.85,206.15z"/>
-<text transform="matrix(1 0 0 1 14.9414 160.6211)" fill="#F2F2F3" font-family="'TitilliumWeb-Regular'" font-size="52.0199px">C++</text>
-<text transform="matrix(1 0 0 1 349.8203 164.9639)" fill="#F2F2F3" font-family="'TitilliumWeb-Regular'" font-size="52.0199px">qml</text>
-<polygon fill="#F2F2F3" points="200.89,150.65 162.96,128.75 162.96,172.55 "/>
-<rect x="288.68" y="143.6" fill="#F2F2F3" width="55.28" height="16.26"/>
-<rect x="107.69" y="143.6" fill="#F2F2F3" width="55.28" height="16.26"/>
-<polygon fill="#F2F2F3" points="250.75,150.65 288.68,172.55 288.68,128.75 "/>
-</svg>
diff --git a/doc/qtcreator/images/creator_publishing.svg b/doc/qtcreator/images/creator_publishing.svg
deleted file mode 100644
index 4954f8930c..0000000000
--- a/doc/qtcreator/images/creator_publishing.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M435.78,272H15.41c-7.84,0-14.19-6.36-14.19-14.19V14.94c0-7.84,6.36-14.19,14.19-14.19h420.37
- c7.84,0,14.19,6.36,14.19,14.19v242.86C449.97,265.64,443.61,272,435.78,272z"/>
- <path fill="#878686" d="M1.66,20.34l-0.44-5.45c0-7.81,6.33-14.14,14.14-14.14h420.47c7.81,0,14.14,6.33,14.14,14.14v6.82"/>
- <circle fill="#DD5958" cx="18.6" cy="11.37" r="4.98"/>
- <circle fill="#DBC558" cx="35.53" cy="11.37" r="4.98"/>
- <circle fill="#52B257" cx="52.47" cy="11.37" r="4.98"/>
-</g>
-<path fill="#EEEDEE" d="M409.72,257.05H52.05c-2.75,0-5-2.25-5-5V34.88c0-2.75,2.25-5,5-5h357.67c2.75,0,5,2.25,5,5v217.18
- C414.72,254.8,412.47,257.05,409.72,257.05z"/>
-<g>
- <polygon fill="#949292" points="207.47,103.17 251.2,103.17 289.47,168.77 341.4,168.77 363.27,122.31 385.13,122.31
- 352.33,187.91 275.8,187.91 237.53,122.31 210.2,122.31 "/>
- <polygon fill="#949292" points="305.87,81.31 327.73,81.31 327.73,125.04 344.13,125.04 316.8,152.37 289.47,125.04 305.87,125.04
- "/>
- <circle fill="#949292" cx="289.47" cy="201.57" r="10.93"/>
- <circle fill="#949292" cx="338.67" cy="201.57" r="10.93"/>
-</g>
-<g>
- <g>
- <path fill="#C9C7C7" d="M169.36,247.01h-76.9c-7.84,0-14.19-6.36-14.19-14.19V57.03c0-7.84,6.36-14.19,14.19-14.19h76.9
- c7.84,0,14.19,6.36,14.19,14.19v175.79C183.55,240.65,177.2,247.01,169.36,247.01z"/>
- <path fill="#878686" d="M78.6,57.58l-0.33-4.1c0-5.88,4.77-10.65,10.65-10.65h83.99c5.88,0,10.65,4.77,10.65,10.65v5.14"/>
- </g>
-
- <rect x="54.69" y="103.54" transform="matrix(-1.836970e-16 1 -1 -1.836970e-16 278.2001 15.8358)" fill="#949292" width="152.99" height="86.95"/>
- <g>
- <path fill="none" stroke="#FFFFFF" stroke-width="3.8493" stroke-linecap="round" stroke-miterlimit="10" d="M109.66,143.31
- c0-12.76,10.34-23.1,23.1-23.1s23.1,10.34,23.1,23.1s-10.34,23.1-23.1,23.1"/>
- <circle fill="#FFFFFF" cx="132.27" cy="143.15" r="10.34"/>
- <text transform="matrix(1 0 0 1 94.7988 141.6104)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="12.8129px">1</text>
- <text transform="matrix(1 0 0 1 156.1709 123.8447)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="12.8129px">2</text>
- <text transform="matrix(1 0 0 1 133.2363 181.9883)" fill="#FFFFFF" font-family="'TitilliumWeb-Bold'" font-size="12.8129px">3</text>
- </g>
-</g>
-</svg>
diff --git a/doc/qtcreator/images/creator_testing.svg b/doc/qtcreator/images/creator_testing.svg
deleted file mode 100644
index 14e75f3489..0000000000
--- a/doc/qtcreator/images/creator_testing.svg
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
- x="0px" y="0px" viewBox="0 0 450 272" xml:space="preserve">
-<g>
- <path fill="#C9C7C7" d="M435.78,272H15.41c-7.84,0-14.19-6.36-14.19-14.19V14.94c0-7.84,6.36-14.19,14.19-14.19h420.37
- c7.84,0,14.19,6.36,14.19,14.19v242.86C449.97,265.64,443.61,272,435.78,272z"/>
- <path fill="#878686" d="M1.66,20.34l-0.44-5.45c0-7.81,6.33-14.14,14.14-14.14h420.47c7.81,0,14.14,6.33,14.14,14.14v6.82"/>
- <circle fill="#DD5958" cx="18.6" cy="11.37" r="4.98"/>
- <circle fill="#DBC558" cx="35.53" cy="11.37" r="4.98"/>
- <circle fill="#52B257" cx="52.47" cy="11.37" r="4.98"/>
-</g>
-<path fill="#EEEDEE" d="M409.72,257.05H52.05c-2.75,0-5-2.25-5-5V34.88c0-2.75,2.25-5,5-5h357.67c2.75,0,5,2.25,5,5v217.18
- C414.72,254.8,412.47,257.05,409.72,257.05z"/>
-<polygon fill="#949292" points="330.22,140.56 167,46.32 167,234.8 "/>
-<g>
- <path fill="#EEEDEE" d="M225.27,119.2c0.11-0.35,0.23-0.69,0.36-1.02c0.49-1.19,1.22-2.31,2.15-3.25c2.33-2.35,5.63-3.33,9.06-2.98
- c0.07-2.23,0.54-4.36,1.35-6.15c1.14-2.5,2.95-4.45,5.2-5.36c1-0.39,2.13,0.09,2.52,1.08c0.41,0.98-0.07,2.11-1.06,2.5
- c-1.28,0.52-2.39,1.76-3.13,3.39c-0.7,1.56-1.08,3.44-1.01,5.42c2.29,0.85,4.5,2.24,6.43,4.15c1.91,1.89,3.32,4.09,4.19,6.37
- c1.98,0.05,3.86-0.34,5.41-1.06c1.63-0.76,2.85-1.87,3.36-3.16c0.4-0.98,1.51-1.48,2.49-1.09c1,0.38,1.48,1.51,1.11,2.51
- c-0.89,2.25-2.82,4.08-5.31,5.24c-1.78,0.83-3.89,1.34-6.13,1.41c0.38,3.43-0.56,6.74-2.89,9.09c-0.94,0.94-2.05,1.68-3.23,2.18
- c-0.33,0.14-0.67,0.25-1.02,0.37c0.06,0.44,0.12,0.88,0.16,1.34c0.22,2.97-0.11,5.82-0.92,8.45l10.51,3.39l7.86-5.01
- c0.9-0.6,2.09-0.34,2.67,0.58c0.58,0.88,0.32,2.07-0.58,2.67l-8.65,5.53c-0.5,0.31-1.09,0.37-1.63,0.22l-11.63-3.77
- c-1.24,2.48-2.88,4.75-4.84,6.74l-0.21,0.21l7.81,8.96c0.42,0.46,0.56,1.05,0.47,1.61l-1.07,8.56c-0.13,1.06-1.09,1.83-2.16,1.68
- c-1.06-0.13-1.79-1.09-1.68-2.16l0.96-7.8l-7.29-8.33c-1.66,1.26-3.46,2.34-5.36,3.21c-1.51,0.72-3.08,1.3-4.69,1.73l2.07,11.49
- c0.08,0.5-0.01,0.98-0.26,1.38l-3.58,5.81c-0.57,0.92-1.76,1.2-2.66,0.65c-0.92-0.57-1.2-1.76-0.65-2.66l3.22-5.22l-1.91-10.68
- c-0.48,0.06-0.96,0.12-1.44,0.14c-6.18,0.49-12.29-1.25-16.88-5.79c-4.61-4.57-6.4-10.66-5.97-16.84c0.02-0.48,0.07-0.96,0.13-1.44
- l-10.7-1.81l-5.19,3.27c-0.9,0.56-2.09,0.3-2.67-0.62c-0.56-0.9-0.3-2.09,0.62-2.67l5.78-3.63c0.4-0.25,0.88-0.35,1.38-0.28
- l11.5,1.96c0.41-1.61,0.98-3.19,1.69-4.71c0.85-1.9,1.92-3.71,3.16-5.39l-8.4-7.22l-7.79,1.03c-1.05,0.14-2.04-0.6-2.18-1.66
- c-0.14-1.05,0.6-2.04,1.66-2.18l8.55-1.15c0.56-0.1,1.15,0.03,1.61,0.45l9.06,7.75l0.21-0.21c1.97-1.99,4.2-3.67,6.67-4.92
- l-3.85-11.57c-0.18-0.56-0.12-1.15,0.18-1.65l5.45-8.7c0.59-0.9,1.78-1.18,2.66-0.61c0.92,0.57,1.2,1.76,0.61,2.66l-4.93,7.91
- l3.48,10.48c2.64-0.82,5.46-1.2,8.44-1C224.39,119.09,224.83,119.15,225.27,119.2z"/>
-</g>
-</svg>
diff --git a/doc/qtcreator/images/front-advanced.png b/doc/qtcreator/images/front-advanced.png
index 87780aa386..99422ed74f 100644
--- a/doc/qtcreator/images/front-advanced.png
+++ b/doc/qtcreator/images/front-advanced.png
Binary files differ
diff --git a/doc/qtcreator/images/front-coding.png b/doc/qtcreator/images/front-coding.png
index edfc5509c5..b39a8c33db 100644
--- a/doc/qtcreator/images/front-coding.png
+++ b/doc/qtcreator/images/front-coding.png
Binary files differ
diff --git a/doc/qtcreator/images/front-gs.png b/doc/qtcreator/images/front-gs.png
index 27706a8cc6..b529411a7d 100644
--- a/doc/qtcreator/images/front-gs.png
+++ b/doc/qtcreator/images/front-gs.png
Binary files differ
diff --git a/doc/qtcreator/images/front-help.png b/doc/qtcreator/images/front-help.png
index d2f9d42fec..f9640f4863 100644
--- a/doc/qtcreator/images/front-help.png
+++ b/doc/qtcreator/images/front-help.png
Binary files differ
diff --git a/doc/qtcreator/images/front-preview.png b/doc/qtcreator/images/front-preview.png
index fe7aab3966..9cb894c183 100644
--- a/doc/qtcreator/images/front-preview.png
+++ b/doc/qtcreator/images/front-preview.png
Binary files differ
diff --git a/doc/qtcreator/images/front-projects.png b/doc/qtcreator/images/front-projects.png
index 69414f4862..bd3412e848 100644
--- a/doc/qtcreator/images/front-projects.png
+++ b/doc/qtcreator/images/front-projects.png
Binary files differ
diff --git a/doc/qtcreator/images/front-publishing.png b/doc/qtcreator/images/front-publishing.png
index d6bf658215..9946aa36cd 100644
--- a/doc/qtcreator/images/front-publishing.png
+++ b/doc/qtcreator/images/front-publishing.png
Binary files differ
diff --git a/doc/qtcreator/images/front-testing.png b/doc/qtcreator/images/front-testing.png
index 1bdbe4dc05..69b1fe45d5 100644
--- a/doc/qtcreator/images/front-testing.png
+++ b/doc/qtcreator/images/front-testing.png
Binary files differ
diff --git a/doc/qtcreator/images/front-ui.png b/doc/qtcreator/images/front-ui.png
index d413b52dbf..ee966bffc1 100644
--- a/doc/qtcreator/images/front-ui.png
+++ b/doc/qtcreator/images/front-ui.png
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-add-online-doc.png b/doc/qtcreator/images/qtcreator-add-online-doc.png
deleted file mode 100644
index 761b3c74f8..0000000000
--- a/doc/qtcreator/images/qtcreator-add-online-doc.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-add-online-doc.webp b/doc/qtcreator/images/qtcreator-add-online-doc.webp
new file mode 100644
index 0000000000..94872a519f
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-add-online-doc.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-add-resource-wizard4.png b/doc/qtcreator/images/qtcreator-add-resource-wizard4.png
deleted file mode 100644
index ea985fc5ea..0000000000
--- a/doc/qtcreator/images/qtcreator-add-resource-wizard4.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-customize.png b/doc/qtcreator/images/qtcreator-locator-customize.png
deleted file mode 100644
index 8b28fa80fe..0000000000
--- a/doc/qtcreator/images/qtcreator-locator-customize.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-customize.webp b/doc/qtcreator/images/qtcreator-locator-customize.webp
new file mode 100644
index 0000000000..5a6d4e90d3
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-locator-customize.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-example.png b/doc/qtcreator/images/qtcreator-locator-example.png
deleted file mode 100644
index 8c23d94f72..0000000000
--- a/doc/qtcreator/images/qtcreator-locator-example.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-example.webp b/doc/qtcreator/images/qtcreator-locator-example.webp
new file mode 100644
index 0000000000..3de6d262eb
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-locator-example.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-open.png b/doc/qtcreator/images/qtcreator-locator-open.png
deleted file mode 100644
index 186cc6504b..0000000000
--- a/doc/qtcreator/images/qtcreator-locator-open.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator-open.webp b/doc/qtcreator/images/qtcreator-locator-open.webp
new file mode 100644
index 0000000000..eb8005b35e
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-locator-open.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator.png b/doc/qtcreator/images/qtcreator-locator.png
deleted file mode 100644
index 82fff209d8..0000000000
--- a/doc/qtcreator/images/qtcreator-locator.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-locator.webp b/doc/qtcreator/images/qtcreator-locator.webp
new file mode 100644
index 0000000000..c97a1a7634
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-locator.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-markdown-editor.webp b/doc/qtcreator/images/qtcreator-markdown-editor.webp
new file mode 100644
index 0000000000..0943d90fb3
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-markdown-editor.webp
Binary files differ
diff --git a/doc/qtcreator/images/qtcreator-new-file.webp b/doc/qtcreator/images/qtcreator-new-file.webp
new file mode 100644
index 0000000000..4429f37cd3
--- /dev/null
+++ b/doc/qtcreator/images/qtcreator-new-file.webp
Binary files differ
diff --git a/doc/qtcreator/src/editors/creator-coding.qdoc b/doc/qtcreator/src/editors/creator-coding.qdoc
index 9a45600f79..6823bf1c8f 100644
--- a/doc/qtcreator/src/editors/creator-coding.qdoc
+++ b/doc/qtcreator/src/editors/creator-coding.qdoc
@@ -68,6 +68,10 @@
\list
+ \li \l{Editing Markdown Files}
+
+ Edit and preview markdown (.md) files.
+
\li \l{Using Language Servers}
The language client offers code completion, highlighting of the
diff --git a/doc/qtcreator/src/editors/creator-locator.qdoc b/doc/qtcreator/src/editors/creator-locator.qdoc
index 232b558727..e5a55f8d6a 100644
--- a/doc/qtcreator/src/editors/creator-locator.qdoc
+++ b/doc/qtcreator/src/editors/creator-locator.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -15,7 +15,9 @@
To open it as a centered popup, click \inlineimage icons/magnifier.png
(\uicontrol Options) in it and select \uicontrol {Open as Centered Popup}.
- \image qtcreator-locator.png "List of locator filters"
+ \image qtcreator-locator.webp "List of locator filters"
+
+ \section1 Activating the Locator
To activate the locator:
@@ -32,43 +34,35 @@
\endlist
- To open a QML file called \e HelloWorld.qml in the currently open project
- using the locator:
-
- \list 1
-
- \li Activate the locator by pressing \key {Ctrl+K}.
-
- \li Start typing the filename.
-
- \image qtcreator-locator-open.png "List of files found in the locator"
-
- \li Move to the filename in the list and press \key Enter.
-
- The file opens in the editor.
+ \section1 Using Locator Filters
- \li To move to a line in the file, enter the line number in the locator.
+ You can locate not only files, but any items defined by \e{locator filters},
+ as well as trigger global actions and perform other tasks, such as build
+ projects or run external tools.
- \endlist
+ The filters that are available depend on the file type. For more information
+ about what a particular locator filter does, see the tooltips that appear
+ when you hover over a filter in the locator. For longer descriptions of the
+ filters, select \uicontrol Configure to open the \uicontrol Locator
+ \l{Creating Locator Filters}{preferences}.
- To move directly to a particular line and column in the document when you
- open the document, append them to the file name in the locator, separated by
- plus signs (+) or colons (:). For example, to open \e HelloWorld.qml to line
- 41 and column 2, enter: \c {HelloWorld.qml:41:2}.
+ To use a locator filter, type its prefix followed by \key Space. The prefix
+ is usually short, from one to three characters. Then type the search string
+ (for example, a filename or class name) or the command to execute.
- If the path to a file is very long, it might not fit into the locator
- window. To view the full path, press \key Alt when the filename is selected
- or use the handle next to the locator window to increase the window width.
+ You can also double-click a locator filter in the filter list to use it. Use
+ the up and down arrow keys or the \key Ctrl+P and \key Ctrl+N
+ keyboard shortcuts to move up and down the list, and then press \key Enter
+ to use the selected filter.
- It is also possible to enter only a part of a search string. As you type,
+ As you type a search string,
the locator shows the occurrences of that string regardless of where in the
name of an component it appears. Some locator filters, such as colon, \c m,
and \c t, support \e fuzzy matching, which means that you can enter the
uppercase letters to locate a symbol when using camel case or the letters
after the underscore when using snake case.
- To narrow down the search results, you can use the following wildcard
- characters:
+ To narrow down the search results, use the following wildcard characters:
\list
@@ -78,117 +72,69 @@
\endlist
- \section1 Using Locator Filters
-
- The locator enables you to browse not only files, but any items defined by
- \b{locator filters}. The filters that are available depend on the file type:
-
- \list
-
- \li Locating any open document (\c {o})
-
- \li Locating files anywhere on your file system (\c {f}). You can use
- environment variables in the \c {f} filter. For example, use
- \c {f $ENVVAR} to expand the environment variable \c ENVVAR on Unix
- systems and \c {f %ENVVAR%} to expand it on Windows systems.
-
- \li Locating files belonging to your project (\c {p}), such as source,
- header, resource, and \c {.ui} files, or to any project (\c {a})
+ \section2 Locating Files
- \if defined(qtcreator)
- \li Locating bookmarks (\c {b}).
- For more information, see \l{Using Bookmarks}.
+ For example, to open a QML file called \e HelloWorld.qml in the currently
+ open project using the locator:
- \li Locating class (\c {c}), enum, function (\c {m}), and type alias
- definitions in your project or anywhere referenced from your
- project (\c {:})
- \endif
-
- \li Locating QML methods (\c {m})
-
- \li Locating symbols in the current document (\c {.})
-
- \li Locating a specific line and column in the document displayed in
- your editor (\c {l <line_number>:<column_number>})
-
- \li Opening help topics, including Qt documentation (\c {?})
+ \list 1
- \li Performing web searches (\c {r})
+ \li Press \key {Ctrl+K} to activate the locator.
- \if defined(qtcreator)
- \li Running text editing macros that you record and save (\c {rm}). For
- more information, see \l{Using Text Editing Macros}
- \endif
+ \li Start typing the filename.
- \li Executing JavaScript (\c {=}), especially useful for calculations.
- For more information, see \l{Executing JavaScript}.
+ \image qtcreator-locator-open.webp "List of files found in the locator"
- \li Executing shell commands (\c {!})
+ \li Use the arrow keys to move to the filename in the list and press
+ \key Enter.
- \li Executing version control system commands
- \if defined(qtcreator)
- (\c {bzr}, \c {cvs}, \c {git}, \c {hg}, or \c {svn}).
- For more information, see \l{Using Version Control Systems}.
- \else
- (\c {git}). For more information, see \l{Using Git}.
- \endif
+ The file opens in the editor.
- \li Triggering actions (\c {t})
+ \li To move to a line in the file, enter the line number in the locator.
- \li Searching for issues from the \l{https://bugreports.qt.io/}
- {Qt Project Bug Tracker} (\c bug).
+ \endlist
- \li Searching for applications, documents, and other files by using
- platform-specific external tools or commands (\c md). The following
- tools are used by default, but you can configure the locator to
- use any other command:
+ If the path to a file is very long, it might not fit into the locator
+ window. To view the full path, press \key Alt when the filename is selected
+ or use the handle next to the locator window to increase the window width.
- \list
- \li On \macOS: using Spotlight
- \li On Windows: using \l{https://www.voidtools.com/downloads/}
- {Everything}
- \li On Linux: using the \c Locate command
- \endlist
+ \if defined(qtcreator)
+ If the locator does not find some files, see \l{Specifying Project Contents}
+ for how to make them known to the locator.
+ \endif
- \if defined(qtcreator)
- \li Running external tools (\c x)
- \li Using CMake to build the project for the current run configuration
- (\c {cm}). For more information, see \l {Setting up CMake}.
- \li Opening the CMakeLists.txt file for the current run configuration in
- the editor (\c {cmo}). This is the same build target as when you
- select \uicontrol Build > \uicontrol {Build for Run Configuration}.
- \li Running a particular run configuration (\c {rr} \e {<name>})
- \li Switching to a particular run configuration (\c {sr} \e {<name>})
- \endif
+ \section2 Locating Lines and Columns
- \endlist
+ To move directly to a particular line and column in the document when you
+ open the document, append the line and column number to the file name in
+ the locator, separated by plus signs (+) or colons (:).
- To use a specific locator filter, type the assigned prefix followed by
- \key Space. The prefix is usually a single character. Then type the search
- string (typically, a filename or class name) or the command to execute.
+ For example, to open \e HelloWorld.qml to line
+ 41 and column 2, enter:
- You can also double-click a locator filter in the filter list to use it. You
- can use the up and down arrow keys or the \key Ctrl+P and \key Ctrl+N
- keyboard shortcuts to move up and down the list, and then press \key Enter
- to use the selected filter.
+ \code
+ HelloWorld.qml:41:2
+ \endcode
\if defined(qtcreator)
- For example, to locate symbols matching QDataStream:
+ \section2 Locating Symbols
+
+ For example, to locate symbols matching \c {QGuiApplication}:
\list 1
\li Activate the locator.
\li Enter a colon (:) followed by a space and the upper case letters in
- the symbol name (QDataStream):
+ the symbol name (here, \c {QGuiApplication}):
\code
- : qds
+ : qga
\endcode
The locator lists the results.
- \image qtcreator-locator-example.png "List of files matching the locator filter"
+ \image qtcreator-locator-example.webp "List of files matching the locator filter"
\endlist
@@ -197,16 +143,21 @@
such as \c {Utils::*View}.
\endif
- For example, to create a new file and open it in the editor, type \c f
+ \section2 Creating Files from Locator
+
+ To create a new file and open it in the editor, type \c f
followed by \key Space, followed by path and file name, and then press
\key Enter.
- You can use the filter that triggers menu commands to open sessions. Enter
+ You can use the filter that triggers menu commands to open
+ \l{Managing Sessions}{sessions}. Enter
\c {t yoursess} or \c {t sess yoursess} to trigger \uicontrol File >
\uicontrol Sessions > \e yoursessionname.
- By default, the following filters are enabled and you do not need to use
- their prefixes explicitly:
+ \section2 Default Filters
+
+ By default, you can use the following preset locator filters without a
+ prefix:
\list
@@ -218,16 +169,11 @@
\endlist
- \if defined(qtcreator)
- If locator does not find some files, see \l{Specifying Project Contents}
- for how to make them known to the locator.
- \endif
-
- \section1 Configuring Locator Filters
+ \section1 Changing Locator Filters
- If the default filters do not match your use case, you can check whether you
- can change them. For all filters, you can change the filter prefix and
- restrict the search to items that match the filter.
+ You can change the preset locator filters to match your use case. For
+ example, you can change the filter prefix and restrict the search to
+ items that match the filter.
To configure a locator filter:
@@ -266,11 +212,11 @@
\li Select \uicontrol Edit > \uicontrol Preferences >
\uicontrol Environment > \uicontrol Locator >
- \uicontrol {Web Search (prefix: r)} > \uicontrol Edit.
+ \uicontrol {Web Search} > \uicontrol Edit.
\li Select \uicontrol Add to add a new entry to the list.
- \image qtcreator-add-online-doc.png "List of URLs in Filter Configuration dialog"
+ \image qtcreator-add-online-doc.webp "List of URLs in Filter Configuration dialog"
\li Double-click the new entry to specify a URL and a search command.
For example, \c {http://www.google.com/search?q=%1}.
@@ -281,7 +227,7 @@
\section1 Creating Locator Filters
- You can create custom locator filters for finding in a directory structure
+ You can create custom locator filters for searching in a directory structure
or on the web.
To quickly access files not directly mentioned in your project, you can
@@ -295,7 +241,7 @@
\li In the locator, select \uicontrol Options >
\uicontrol Configure to open the \uicontrol Locator preferences.
- \image qtcreator-locator-customize.png "Locator preferences"
+ \image qtcreator-locator-customize.webp "Locator preferences"
\li Select \uicontrol Add > \uicontrol {Files in Directories} to add
a directory filter or \uicontrol {URL Template} to add a URL
@@ -310,7 +256,7 @@
\li In the \uicontrol {File pattern} field, specify file patterns to
restrict the search to files that match the pattern.
- Use a comma separated list. For example, to search for all
+ Separate the patterns with commas. For example, to search for all
\c {.qml} and \c {.ui.qml} files, enter \c{*.qml,*.ui.qml}
\li In the \uicontrol {Exclusion pattern} field, specify file
@@ -329,9 +275,9 @@
\section1 Configuring Locator Cache
The locator searches the files matching your file pattern in the directories
- you have selected and caches that information. The cache for all default
- filters is updated as you write your code. By default, \QC updates the
- filters created by you once an hour.
+ you have selected and caches that information. \QC updates the cache for all
+ preset filters as you write code. By default, \QC updates your custom
+ filters once an hour.
To update the cached information manually, select \uicontrol Options >
\uicontrol Refresh in the locator.
@@ -349,8 +295,7 @@
\section1 Executing JavaScript
- The locator has a JavaScript interpreter that you can use to
- perform calculations.
+ The locator has a JavaScript interpreter for performing calculations.
Beside simple mathematical operations, like ((1 + 2) * 3), the following
built-in functions exist:
diff --git a/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc b/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc
index e6f910b22a..dba4740fd6 100644
--- a/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc
+++ b/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc
@@ -4,7 +4,7 @@
/*!
\previouspage creator-editor-options-text.html
\page creator-editor-fakevim.html
- \nextpage creator-language-servers.html
+ \nextpage creator-markdown-editor.html
\title Using FakeVim Mode
diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc
index 248569aa78..348845a7bd 100644
--- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc
+++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \previouspage creator-editor-fakevim.html
+ \previouspage creator-markdown-editor.html
\page creator-language-servers.html
\nextpage creator-mime-types.html
diff --git a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc
new file mode 100644
index 0000000000..9a3d2444e6
--- /dev/null
+++ b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \previouspage creator-editor-fakevim.html
+ \page creator-markdown-editor.html
+ \nextpage creator-language-servers.html
+
+ \title Editing Markdown Files
+
+ Open \l{https://www.markdownguide.org/basic-syntax/}{markdown} (.md) files
+ or select \uicontrol File > \uicontrol {New File} > \uicontrol {General} >
+ \uicontrol {Markdown File} to create a new file.
+
+ \image qtcreator-markdown-editor.webp {Markdown file in Preview and Editor views}
+
+ When you edit the file in the editor, you can see the changes in the preview.
+
+ To hide and show the views, select \uicontrol {Show Preview} and
+ \uicontrol {Show Editor}. To swap the places of the views, select
+ \uicontrol {Swap Views}.
+*/
diff --git a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc
index 75036e6cd5..ef5d0cca4c 100644
--- a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc
+++ b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc
@@ -438,7 +438,7 @@
Works with namespaces, classes, functions, variables, include
statements, and macros. Also, opens URLs in the default browser
- and Qt resource files (.qrc) in the \l{Creating Resource Files}
+ and Qt resource files (.qrc) in the \l{Resource Files}
{resource editor}
\li F2
\row
@@ -729,7 +729,7 @@
\li
\li Alt+S, Alt+D
\row
- \li Diff project
+ \li Diff project or repository
\li
\li
\li Alt+G, Alt+Shift+D
@@ -753,7 +753,7 @@
\li Alt+P, Alt+F
\li
\row
- \li Log project
+ \li Log repository
\li
\li
\li Alt+G, Alt+K
diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc
index da32fd4126..e380c1e6e8 100644
--- a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc
+++ b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc
@@ -171,16 +171,17 @@
Use the \uicontrol Locator to browse
through projects, files, classes, functions, documentation, and file systems.
+
+ \image qtcreator-locator-open.webp "List of found files"
+
To quickly access files not directly mentioned in your project, you can
create your own locator filters. That way you can locate files in a
directory structure you have defined.
- \image qtcreator-locator.png "List of locator filters"
-
To create locator filters, select \uicontrol Edit > \uicontrol Preferences >
\uicontrol Environment > \uicontrol Locator > \uicontrol Add.
- \image qtcreator-locator-customize.png "Locator preferences"
+ \image qtcreator-locator-customize.webp "Locator preferences"
For more information, see \l{Creating Locator Filters}.
@@ -190,6 +191,8 @@
You can now do basic calculations, with options to copy the results to the clipboard
by navigating through the entries and pressing \key {Enter}.
+ \image qtcreator-locator.webp "List of locator filters"
+
For more information, see \l{Executing JavaScript}.
\section1 Jump to a function in QML code
diff --git a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc
index b874866f60..028619c7fa 100644
--- a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc
+++ b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc
@@ -111,7 +111,7 @@
\li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits >
\uicontrol Add to add a kit for building for the device. Select the
Qt version, compiler, and device that you added above, and select
- \uicontrol {Remote Linux Device} in \uicontrol {Device type}.
+ \uicontrol {Remote Linux Device} in \uicontrol {Run device type}.
To build on the remote device, select \uicontrol {Remote Linux Device}
also in \uicontrol {Build device}.
diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc
index 2ef5125bab..7f60737475 100644
--- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc
+++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc
@@ -1002,5 +1002,116 @@
https://creativecommons.org/publicdomain/zero/1.0/
+ \li \b WinPty
+
+ Implementation of a pseudo terminal for Windows.
+
+ The sources can be found in:
+ \list
+ \li \l https://github.com/rprichard/winpty
+ \endlist
+
+ Distributed under the MIT license.
+
+ \include license-mit.qdocinc
+
+ \li \b ptyqt
+
+ Pty-Qt is small library for access to console applications by a
+ pseudo-terminal interface on \macos, Linux and Windows. On \macos and
+ Linux it uses standard PseudoTerminal API and on Windows it uses
+ WinPty or ConPty.
+
+ \list
+ \li \l https://github.com/kafeg/ptyqt
+ \endlist
+
+ Distributed under the MIT license.
+
+ \include license-mit.qdocinc
+
+ \li \b libvterm
+
+ An abstract C99 library, which implements a VT220 or xterm-like terminal
+ emulator. It doesn't use any particular graphics toolkit or output
+ system. Instead it invokes callback function pointers that its embedding
+ program should provide to draw on its behalf. It avoids calling malloc()
+ during normal running state, allowing it to be used in embedded kernels.
+
+ \list
+ \li \l https://www.leonerd.org.uk/code/libvterm/
+ \endlist
+
+ Distributed under the MIT license.
+
+ \include license-mit.qdocinc
+
+ \li \b terminal/shellintegrations
+
+ The Terminal plugin uses scripts to integrate with the shell. The scripts are
+ located in the Qt Creator source tree in src/plugins/terminal/shellintegrations.
+
+ \list
+ \li \l https://github.com/microsoft/vscode/tree/main/src/vs/workbench/contrib/terminal/browser/media
+ \endlist
+
+ Distributed under the MIT license.
+
+ \include license-mit.qdocinc
+
+ \li \b terminal/shellintegrations/clink
+
+ The Terminal plugin uses a lua script to integrate with the cmd shell when using clink.
+
+ \list
+ \li \l https://github.com/chrisant996/clink-gizmos
+ \endlist
+
+ Distributed under the MIT license.
+
+ \include license-mit.qdocinc
+
+ \li \b cmake
+
+ The CMake project manager uses the CMake lexer code for parsing CMake files.
+
+ \list
+ \li \l https://gitlab.kitware.com/cmake/cmake.git
+ \endlist
+
+ CMake - Cross Platform Makefile Generator
+ Copyright 2000-2023 Kitware, Inc. and Contributors
+ All rights reserved.
+
+ \badcode
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Kitware, Inc. nor the names of Contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ \endcode
+
+
\endlist
*/
diff --git a/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc
index 646fdce899..b0f188c083 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -8,122 +8,33 @@
\title Creating Files
- You can use wizard templates to add individual files to your
- \l{Creating Projects}{projects}.
- The following table lists the wizard templates for creating files.
-
- \table
- \header
- \li Category
- \li Wizard Template
- \li Purpose
- \row
- \li {1,3} C/C++
- \li C++ Class
- \li C++ header and source file for a new class that you can add to
- a C++ project.
- \row
- \li C/C++ Source File
- \li C++ source file that you can add to a C++ project.
- \row
- \li C/C++ Header File
- \li C++ header file that you can add to a C++ project.
- \row
- \li {1,3} Modeling
- \li State Chart
- \li State Chart XML (SCXML) file that has boilerplate
- code for state machines. You can use the classes in the
- \l {Qt SCXML} module to embed state machines created from
- the files in Qt applications.
- \row
- \li Model
- \li Universal Modeling Language (UML) style model with a structured
- diagram. However, the model editor uses a variant of UML and
- has only a subset of properties for specifying the
- appearance of model elements. For more information, see
- \l {Modeling}.
- \row
- \li Scratch Model
- \li Scratch model using a temporary file.
- \row
- \li {1,7} Qt
- \li Qt Item Model
- \li Source and header files that you can use to create classes
- derived from QAbstractItemModel, QAbstractTableModel, or
- QAbstractListModel.
- \row
- \li \QD Form Class
- \li \QD form and a matching class for implementing a UI based
- on Qt widgets.
- \row
- \li \QD Form
- \li \QD form for Qt widget based projects. This is useful
- if you already have an existing class for the UI logic.
- \row
- \li Qt Resource File
- \li Resource file for storing binary files in the application
- executable.
- \row
- \li QML File (Qt Quick 2)
- \li QML file that imports Qt Quick 2.0 for use in Qt Quick projects.
- \row
- \li Qt Quick UI File
- \li \l{UI Files}{UI file} (\e .ui.qml) and the corresponding
- implementation file (\e .qml) for use in Qt Quick projects.
- \row
- \li JS File
- \li JavaScript file that you can use to write the application logic
- in Qt Quick projects.
- \row
- \li {1,4} GLSL
- \li Fragment Shader (OpenGL/ES 2.0)
- \li Fragment shader that generates the final pixel colors for
- triangles, points, and lines rendered with OpenGL. You can use
- it in both Qt Quick projects and Qt widget based projects.
- \row
- \li Vertex Shader (OpenGL/ES 2.0)
- \li Vertex shader that transforms the positions, normals, and
- texture coordinates of triangles, points, and lines rendered
- with OpenGL. You can use it in both Qt Quick projects and Qt
- widget based projects.
- \row
- \li Fragment Shader (Desktop OpenGL)
- \li Fragment shader for use in both Qt Quick projects and Qt
- widget based projects.
- \row
- \li Vertex Shader (Desktop OpenGL)
- \li Vertex shader for use in both Qt Quick projects and Qt
- widget based projects.
- \row
- \li {1,2} General
- \li Empty File
- \li Empty file that you can save with any filename extension.
- \row
- \li Scratch Buffer
- \li Scratch buffer that uses temporary files. You can
- create this type of files for temporarily storing information
- that you do not intend to save
- \row
- \li Java
- \li Java File
- \li Java class files that you can use to create Java classes.
- \row
- \li {1,2} Python
- \li Python Class
- \li Python class file.
- \row
- \li Python File
- \li Python script file using UTF-8 encoding.
- \row
- \li {1,2} Nim (experimental)
- \li Nim Script File
- \li Empty Nim script file using UTF-8 encoding.
- \row
- \li Nim File
- \li Empty Nim source file using UTF-8 encoding.
- \endtable
-
- \section1 Creating C++ Classes
+ \image qtcreator-new-file.webp {New File wizard}
+
+ Use wizard templates to add individual files to your \l{Creating Projects}
+ {projects}:
+
+ \list
+ \li \uicontrol {C/C++}: header and source files for new classes.
+ \li \uicontrol {Modeling}: State Chart XML (SCXML) files,
+ Universal Modeling Language (UML) style \l {Modeling}{models},
+ and scratch models that use a temporary file.
+ \li \uicontrol {Qt}: source and header files for item, table,
+ or list models, \QD forms and a matching classes for Qt Widgets
+ projects, Qt resource files, as well as QML and JavaScript files
+ for Qt Quick projects.
+ \li \uicontrol {GLSL}: fragment and vertex shaders.
+ \li \uicontrol {General}: markdown files, empty files that you can save
+ with any filename extension, and scratch buffers that use temporary
+ files.
+ \li \uicontrol {Java}: class files.
+ \li \uicontrol {Python}: class and script files for Python projects.
+ \li \uicontrol {Nim} (experimental): empty Nim source and script files.
+ \endlist
+
+ The \uicontrol {New File} dialog shows detailed information about each file
+ wizard template.
+
+ \section1 C++ Classes
The \uicontrol {C++ Class Wizard} allows you to create a C++ header and source
file for a new class that you can add to a C++ project. Specify the class
@@ -149,7 +60,7 @@
You can create your own project and class wizards. For more information,
see \l{Adding New Custom Wizards}.
- \section1 Creating Resource Files
+ \section1 Resource Files
\QC supports the \l{The Qt Resource System}{Qt Resource System}, which is a
platform-independent mechanism for storing files in the application's
@@ -187,7 +98,7 @@
The above functions are also available in the context menu in the
\uicontrol Projects view.
- \section1 Creating OpenGL Fragment and Vertex Shaders
+ \section1 OpenGL Fragment and Vertex Shaders
Qt supports integration with OpenGL implementations on all
platforms, which allows you to display hardware accelerated 3D graphics
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc
index b3300865dd..05a340d0df 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// **********************************************************************
@@ -12,29 +12,33 @@
\section1 Selecting the Build System
- You can use several build systems to build your projects.
+ You can use several build systems to build your projects:
- \l{qmake Manual}{qmake} is a cross-platform system for build automation
+ \list
+
+ \li \l{qmake Manual}{qmake} is a cross-platform system for build automation
that helps simplify the build process for development projects across
different platforms. qmake automates the generation of build configurations
so that you need only a few lines of information to create each
configuration. Qt installers install and configure qmake.
To use one of the other supported build systems, you need to set it up.
- \l {Build with CMake}{CMake} is an alternative to qmake for automating the
+ \li \l {Build with CMake}{CMake} is an alternative to qmake for automating the
generation of build configurations. For more information, see
\l {Setting Up CMake}.
- \l {https://mesonbuild.com/}{Meson} Meson is a fast and user-friendly
+ \li \l {https://mesonbuild.com/}{Meson} is a fast and user-friendly
open-source build system that aims to minimize the time developers spend
writing or debugging build definitions and waiting for the build system
to start compiling code. For more information, see \l {Setting Up Meson}.
- \l{Qbs Manual}{Qbs} is an all-in-one build tool that generates a build graph
+ \li \l{Qbs Manual}{Qbs} is an all-in-one build tool that generates a build graph
from a high-level project description (like qmake or CMake do) and executes
the commands in the low-level build graph (like make does). For more
information, see \l{Setting Up Qbs}.
+ \endlist
+
To export a project to some other build system, such as Microsoft Visual
Studio, select \uicontrol Build > \uicontrol {Run Generator}, and select
a generator in the list. \QC generates the build files, such as .vcxproj,
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
index 141e6eab2a..6ea2b20b03 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// **********************************************************************
@@ -49,7 +49,7 @@
The installers create \l{glossary-buildandrun-kit}{kits} and specify build
and run settings for the installed device types. However, you might need to
install and configure some additional software on the devices to be able to
- connect to them from the development PC.
+ \l{Connecting Devices}{connect} to them from the development PC.
\include creator-projects-build-systems.qdocinc build systems
@@ -71,146 +71,49 @@
\section1 Selecting Project Type
- The following table lists the wizard templates for creating projects.
+ The following table lists the types of wizard templates that you can use
+ for creating projects. The \uicontrol {New Project} dialog shows detailed
+ information about each project wizard template.
\table
\header
\li Category
- \li Wizard Template
\li Purpose
\row
- \li Application (Qt for MCU)
- \li MCU Support Application
- \li Creates an application that uses a subset of Qt QML and
- Qt Quick Controls types (as supported by Qt for MCUs) that
- you can deploy, run, and debug on MCU boards. For more
- information, see \l {Connecting MCUs}.
+ \li Application
+ \li Use many UI technologies (Qt Widgets and Qt Quick) and
+ programming languages (C++, QML, and Python) to create
+ applications for different purposes that you can run on
+ many target platforms (desktop, mobile, and embedded).
\row
- \li {1,3} Application (Qt)
- \li Qt Widgets Application
- \li Uses \QD forms to design a Qt widget based user interface for
- the desktop and C++ to implement the application logic.
+ \li Library or plugin
+ \li Create a shared or static C++ library, a C++ plugin for Qt Quick
+ application extensions, or a \QC plugin.
\row
- \li Qt Console Application
- \li Uses a single main.cpp file.
+ \li Other project
+ \li Create custom \l{Developing Widget Based Applications}{\QD}
+ widgets or widget collections,
+ \l{Qt Quick UI Projects}{Qt Quick UI projects},
+ \l {Creating Tests}{auto-test projects},
+ \l{Adding Subprojects to Projects}{subprojects},
+ empty qmake projects, or qmake projects for testing
+ code snippets.
\row
- \li Qt Quick Application
- \li Creates a Qt Quick application project that can have both
- QML and C++ code. You can build the application and deploy it
- to desktop, embedded, and mobile target platforms.
-
- You can select an option to create a project that you can open
- in \QDS, which has a visual editor for Qt Quick UIs.
- \row
- \li {1,4} Application (Qt for Python)
- \li Empty Application
- \li Creates a \l{https://doc.qt.io/qtforpython/index.html}
- {Qt for Python} application that has only the main
- code for a QApplication.
- \row
- \li Empty Window
- \li Creates a Qt for Python application that has an empty
- window.
- \row
- \li Window UI
- \li Creates a Qt for Python application that has an empty
- window with a widget-based UI. Preferred approach that requires
- you to generate a Python file from the .ui file, to import
- it directly into your application.
- \row
- \li Qt Quick Application - Empty
- \li Creates a Python project that has an empty Qt Quick
- Application.
- \row
- \li {1,3} Library
- \li C++ Library
- \li A shared or static C++ library based on qmake.
- \row
- \li Qt Quick 2 Extension Plugin
- \li Creates a C++ plugin that makes it possible to offer extensions
- that the QQmlEngine class can load dynamically into Qt Quick
- applications.
- \row
- \li \QC Plugin
- \li Creates a \QC plugin.
- \row
- \li {1,6} Other Project
- \li Qt Custom Designer Widget
- \li Creates a custom \QD widget or widget collection.
- \row
- \li Qt Quick UI Prototype
- \li Creates a \l{Creating Qt Quick UI Projects}{Qt Quick UI project}
- with a single QML file that has the main view. You can
- preview Qt Quick UI projects in the
- \l{Validating with Target Hardware}{QML Scene preview tool}.
- You do not need to build them because they do not have any
- C++ code.
-
- Use this template only if you are prototyping. You cannot create
- a full application by using this template.
-
- You cannot deploy Qt Quick UI projects to embedded or mobile
- target platforms. For those platforms, create a Qt Quick
- application instead.
- \row
- \li Auto Test Project
- \li Creates a project with boilerplate code for a Qt or Google
- test. For more information, see \l {Creating Tests}.
- \row
- \li Subdirs Project
- \li Creates a subproject that enables you to structure your qmake
- projects as a tree hierarchy.
- \row
- \li Empty qmake Project
- \li Creates an empty qmake project that uses qmake as the build
- system but does not use any default classes.
- \row
- \li Code Snippet
- \li Creates a qmake project from a code snippet. When working on
- bug reports that have a code snippet, you can place the code
- snippet into a project to compile and check it.
- \row
- \li {1,4} Non-Qt Project
- \li Plain C Application
- \li Creates a plain C application that uses qmake, Qbs, or CMake
- but does not use the Qt library.
- \row
- \li Plain C++ Application
- \li Creates a plain C++ application that uses qmake, Qbs, or CMake
- but does not use the Qt library.
- \row
- \li Nim Application (experimental)
- \li Creates a Nim application that uses Nimble, but does not use the
- Qt library. For more information, see \l {Setting Up Nimble}.
- \row
- \li Nimble Application (experimental)
- \li Creates a Nimble application that uses Nimble, but does not use
- the Qt library. For more information, see
- \l {Setting Up Nimble}.
- \row
- \li {1,3} Import Project
- \li Project from version control
- \li Imports a project from a supported version control system, such
- as Bazaar, CVS, Git, Mercurial, or Subversion. For more
- information about how \QC integrates version control systems,
- see \l{Using Version Control Systems}.
- \row
- \li Import as qmake or CMake Project (Limited Functionality)
- \li Imports an existing project that does not use any of the
- supported build systems: qmake, Qbs, CMake, or Autotools. The
- template creates a project file, which enables you to use
- \QC as a code editor and as a launcher for debugging and
- analysis tools. However, if you want to build the project,
- you might need to edit the generated project file.
+ \li Non-Qt project
+ \li Create plain C or C++ applications or \l {Setting Up Nimble}
+ {Nim or Nimble} applications (experimental)
\row
- \li Import Existing Project
- \li Imports an existing project that does not use any of the
- supported build systems: qmake, Qbs, CMake, or Autotools.
- This enables you to use \QC as a code editor.
+ \li Imported project
+ \li Import projects from a supported \l{Using Version Control Systems}
+ {version control system}, such as Bazaar, CVS, Git, Mercurial, or
+ Subversion.
+
+ You can also import existing projects that do not use any of the
+ supported build systems to use \QC as a code editor and as a
+ launcher for debugging and analysis tools.
\row
\li Squish
- \li Squish Test Suite
- \li Creates a new \l {Using Squish}{Squish test suite}.
+ \li Create new \l {Using Squish}{Squish test suites}.
\endtable
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc
index 04b639f1dd..08a6d98820 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc
@@ -97,8 +97,8 @@
used for the \c CurrentKit:FileSystemName variable, which determines
the name of the shadow build directory, for example.
\row
- \li \uicontrol{Device type}
- \li Type of the device.
+ \li \uicontrol{Run device type}
+ \li Type of the run device.
Double-click the icon next to the field to select the image that is
displayed in the kit selector for this kit. You can use any
@@ -107,7 +107,7 @@
logo as an icon allows you to easily see, which compiler is used to
build the project for the selected kit.
\row
- \li \uicontrol Device
+ \li \uicontrol {Run device}
\li The device to run applications on.
\row
\li \uicontrol {Build device}
diff --git a/doc/qtcreator/src/python/creator-python-project.qdocinc b/doc/qtcreator/src/python/creator-python-project.qdocinc
index 436e712f0b..0ef6bb2d3f 100644
--- a/doc/qtcreator/src/python/creator-python-project.qdocinc
+++ b/doc/qtcreator/src/python/creator-python-project.qdocinc
@@ -140,7 +140,7 @@
For more information about the
\uicontrol {Qt for Python - Qt Quick Application - Empty} wizard, see
- \l {Creating Qt Quick Based Python Applications}.
+ \l {Qt Quick Based Python Applications}.
For examples of creating Qt for Python applications, see
\l {https://doc.qt.io/qtforpython/tutorials/index.html}
@@ -151,7 +151,7 @@
//! [python qml project wizards]
- \section1 Creating Qt Quick Based Python Applications
+ \section1 Qt Quick Based Python Applications
The \uicontrol {Qt for Python - Qt Quick Application - Empty} wizard enables
you to create a Python project that has a main QML file. Specify the
diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc
index e2299cd05c..e18f60632b 100644
--- a/doc/qtcreator/src/qtcreator-toc.qdoc
+++ b/doc/qtcreator/src/qtcreator-toc.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
@@ -7,6 +7,12 @@
\title All Topics
+ \note The following list has links to all the individual topics (HTML files)
+ in the \QC Manual. Use your browser's page search to find a link to a
+ particular topic in the list. For a more extensive search, use the
+ \uicontrol Search function in the \l{https://doc.qt.io/qtcreator/}
+ {Qt documentation} portal or in the \l {Using the Help Mode}{Help} mode.
+
\list
\li \l{Getting Started}
\list
@@ -122,6 +128,7 @@
\li \l{Specifying Text Editor Settings}
\li \l{Using FakeVim Mode}
\endlist
+ \li \l{Editing Markdown Files}
\li \l{Using Language Servers}
\li \l{Editing MIME Types}
\li \l{Modeling}
diff --git a/doc/qtcreator/src/qtcreator.qdoc b/doc/qtcreator/src/qtcreator.qdoc
index 50cbe7024e..280a65ab19 100644
--- a/doc/qtcreator/src/qtcreator.qdoc
+++ b/doc/qtcreator/src/qtcreator.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// **********************************************************************
@@ -29,9 +29,11 @@
appropriate \l{http://qt.io/licensing/}{Qt license}. For more information,
see \l{Commercial Features}.
+
+
\table
\row
- \li {4,1} \b {\l{All Topics}}
+ \li {4,1} \b {\l{All Topics}{Click Here for a List of All Topics}}
\row
\li \inlineimage front-gs.png
\li \inlineimage front-projects.png
diff --git a/doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc b/doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc
index 3cb1c61e1b..dd1178e5e3 100644
--- a/doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc
+++ b/doc/qtcreator/src/qtquick/creator-only/qt-design-viewer.qdoc
@@ -19,7 +19,7 @@
However, the actual performance of the application once started is
indistinguishable from the same application running on the desktop.
- You can run \l{Creating Qt Quick UI Projects}{Qt Quick UI projects}, which
+ You can run \l{Qt Quick UI Projects}{Qt Quick UI projects}, which
have a .qmlproject file that define the main QML file and the import paths.
Compress the project folder into a ZIP file that you upload to \QDV.
diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc
index 2c8a4e8e8e..5545834964 100644
--- a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc
+++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// **********************************************************************
@@ -14,58 +14,28 @@
\title Creating Qt Quick Projects
- The following table lists the wizard templates for creating a new
- Qt Quick project from scratch.
-
- \table
- \header
- \li Category
- \li Wizard Template
- \li Purpose
- \row
- \li Application (Qt)
- \li Qt Quick Application
- \li Creates a Qt Quick application project that can have both
- QML and C++ code. You can build the application and deploy it
- to desktop, embedded, and mobile target platforms.
-
- You can select an option to create a project that you can open
- in \QDS.
- \row
- \li Application (Qt for Python)
- \li Qt for Python - Qt Quick Application
- \li Creates a Python project that has an empty Qt Quick
- Application.
- \row
- \li Other Project
- \li Qt Quick UI Prototype
- \li Creates a Qt Quick UI project with a single QML file that
- has the main view. You can preview Qt Quick UI projects
- in the QML Scene preview tool. You do not need to build them
- because they do not have any C++ code.
-
- This project type is compatible with \QDS. However, use this
- template only if you are prototyping. You cannot create
- a full application by using this template.
-
- You cannot deploy Qt Quick UI projects to embedded or
- mobile target platforms. For those platforms, create a Qt Quick
- application instead.
- \row
- \li Library
- \li Qt Quick 2 Extension Plugin
- \li Creates C++ plugins that make it possible to offer extensions
- that can be loaded dynamically into Qt Quick applications.
- \endtable
+ Use the following wizard templates to create new Qt Quick projects:
+
+ \list
+ \li \uicontrol {Application (Qt)} > \uicontrol {Qt Quick Application}
+ \li \uicontrol {Application (Qt for Python)} >
+ \uicontrol {Qt for Python - Qt Quick Application}
+ \li \uicontrol {Other Project} > \uicontrol {Qt Quick UI Prototype}
+ \li \uicontrol {Library} > \uicontrol {Qt Quick 2 Extension Plugin}
+ \endlist
+
+ The \uicontrol {New Project} dialog shows detailed information about each
+ project wizard template.
\note The SDK for a particular target platform might install additional
templates for that platform. For example, the QNX templates are installed
as part of the QNX SDK.
\QC creates the necessary boilerplate files. Some of the files are
- specific to a particular target platform.
+ specific to a particular target platform. You can use wizard templates
+ also to \l{Creating Files}{add files} to the projects.
- \section1 Creating Qt Quick Applications
+ \section1 Qt Quick Applications
\list 1
@@ -142,7 +112,7 @@
\include creator-python-project.qdocinc python qml project wizards
- \section1 Creating Qt Quick UI Projects
+ \section1 Qt Quick UI Projects
Qt Quick UI Prototype projects are useful for testing or prototyping user
interfaces,
diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc b/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc
index f01fae3bd8..d0a5b94cc0 100644
--- a/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc
+++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc
@@ -69,7 +69,7 @@
\endlist
For more information about the settings that you skipped and the other
- wizard templates available, see \l{Creating Qt Quick Applications}.
+ wizard templates available, see \l{Qt Quick Applications}.
//! [qtquick empty application]
*/
diff --git a/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc b/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc
index 95cd83edea..01aaa637c4 100644
--- a/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc
+++ b/doc/qtcreator/src/qtquick/qtquick-live-preview.qdoc
@@ -22,7 +22,7 @@
In addition, you can use \QDV to run
\if defined(qtcreator)
- \l{Creating Qt Quick UI Projects}{Qt Quick UI projects}
+ \l{Qt Quick UI Projects}{Qt Quick UI projects}
\else
applications
\endif
diff --git a/doc/qtcreator/src/user-interface/creator-ui.qdoc b/doc/qtcreator/src/user-interface/creator-ui.qdoc
index 1e233283fd..315720275a 100644
--- a/doc/qtcreator/src/user-interface/creator-ui.qdoc
+++ b/doc/qtcreator/src/user-interface/creator-ui.qdoc
@@ -299,6 +299,14 @@
style.
\endif
+ \if defined(qtdesignstudio)
+ The mode selector is hidden by default.
+
+ To show the mode selector, go to \uicontrol Views >
+ \uicontrol {Mode Selector Style} and select \uicontrol {Icons and Text}
+ or \uicontrol {Icons Only}.
+ \endif
+
You can use \QC in the following modes:
diff --git a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc
index d01c038ce5..c05f84832e 100644
--- a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc
+++ b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc
@@ -95,7 +95,7 @@
\li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits >
\uicontrol Add.
\li In the \uicontrol Name field, specify a name for the kit.
- \li In the \uicontrol {Device type} field, select
+ \li In the \uicontrol {Run device type} field, select
\uicontrol {WebAssembly Runtime}.
The value of the \uicontrol Device field is automatically
set to \uicontrol {Web Browser}.
diff --git a/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc b/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc
index 1298e81e9f..ad38116dff 100644
--- a/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc
+++ b/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
// **********************************************************************
@@ -294,12 +294,6 @@
and select \uicontrol{Finish} or \uicontrol Done to open the file
in the code editor.
- \li In the \uicontrol Copy to Clipboard dialog, select \uicontrol Yes to
- copy the path to the resource file to the clipboard for adding it
- to the CMakeLists.txt file.
-
- \image qtcreator-add-resource-wizard4.png "Copy to Clipboard dialog"
-
\li Select \uicontrol Add > \uicontrol {Add Prefix}.
\li In the \uicontrol{Prefix} field, replace the default prefix with a slash
diff --git a/doc/qtdesignstudio/examples/doc/images/material-bundle-example.webp b/doc/qtdesignstudio/examples/doc/images/material-bundle-example.webp
new file mode 100644
index 0000000000..6dcab85469
--- /dev/null
+++ b/doc/qtdesignstudio/examples/doc/images/material-bundle-example.webp
Binary files differ
diff --git a/doc/qtdesignstudio/examples/doc/materialbundle.qdoc b/doc/qtdesignstudio/examples/doc/materialbundle.qdoc
new file mode 100644
index 0000000000..4f808b14f2
--- /dev/null
+++ b/doc/qtdesignstudio/examples/doc/materialbundle.qdoc
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page material-bundle-example.html
+ \ingroup studioexamples
+ \sa {Content Library}
+
+ \title Material Bundle
+ \brief Showcases the materials in \uicontrol {Content Library} and the real-time rendering
+ capabilities of \QDS.
+
+ \image material-bundle-example.webp
+
+ The \e{Material Bundle} example showcases the materials included in the \QDS
+ \uicontrol {Content Library} and the real-time 3D rendering capabilities of \QDS.
+
+ Run the project to:
+
+ \list
+ \li Select the material for two different meshes; all material bundle materials are available.
+ \li Navigate (rotate and zoom) with the mouse.
+ \li Choose between two different environment light options.
+ \endlist
+
+ \section1 The Material Bundle
+
+ The material bundle is included in \uicontrol {Content Library} which is
+ included in the \QDS Enterprise license. It contains a ready-made set of materials that you
+ can apply to your 3D models by dragging and dropping.
+
+ \image content-library.webp
+*/
diff --git a/doc/qtdesignstudio/images/3d-view-context-menu.png b/doc/qtdesignstudio/images/3d-view-context-menu.png
index c2e35e0019..5b08c568cb 100644
--- a/doc/qtdesignstudio/images/3d-view-context-menu.png
+++ b/doc/qtdesignstudio/images/3d-view-context-menu.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/assets-view-effect.png b/doc/qtdesignstudio/images/assets-view-effect.png
deleted file mode 100644
index b229f8ddb6..0000000000
--- a/doc/qtdesignstudio/images/assets-view-effect.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/content-library-add-texture.png b/doc/qtdesignstudio/images/content-library-add-texture.png
index ae820a39c8..75cc1bec0f 100644
--- a/doc/qtdesignstudio/images/content-library-add-texture.png
+++ b/doc/qtdesignstudio/images/content-library-add-texture.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/3d-background-color.png b/doc/qtdesignstudio/images/icons/3d-background-color.png
index d896907b30..37c68e632c 100644
--- a/doc/qtdesignstudio/images/icons/3d-background-color.png
+++ b/doc/qtdesignstudio/images/icons/3d-background-color.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/align-camera-on.png b/doc/qtdesignstudio/images/icons/align-camera-on.png
index 382a2ac6b3..a813dcaff7 100644
--- a/doc/qtdesignstudio/images/icons/align-camera-on.png
+++ b/doc/qtdesignstudio/images/icons/align-camera-on.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/align-view-on.png b/doc/qtdesignstudio/images/icons/align-view-on.png
index 3617416f6f..c7534664b1 100644
--- a/doc/qtdesignstudio/images/icons/align-view-on.png
+++ b/doc/qtdesignstudio/images/icons/align-view-on.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/apply-material.png b/doc/qtdesignstudio/images/icons/apply-material.png
deleted file mode 100644
index d0b347470b..0000000000
--- a/doc/qtdesignstudio/images/icons/apply-material.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/apply.png b/doc/qtdesignstudio/images/icons/apply.png
new file mode 100644
index 0000000000..baf72e3664
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/apply.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/arrowleft.png b/doc/qtdesignstudio/images/icons/arrowleft.png
index cdf5b0cc7c..d45ee098fe 100644
--- a/doc/qtdesignstudio/images/icons/arrowleft.png
+++ b/doc/qtdesignstudio/images/icons/arrowleft.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/arrowright.png b/doc/qtdesignstudio/images/icons/arrowright.png
index 627b2eccf9..b5a2c92dc4 100644
--- a/doc/qtdesignstudio/images/icons/arrowright.png
+++ b/doc/qtdesignstudio/images/icons/arrowright.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/back_one_frame.png b/doc/qtdesignstudio/images/icons/back_one_frame.png
index 69c93ebe3e..f7220acbf6 100644
--- a/doc/qtdesignstudio/images/icons/back_one_frame.png
+++ b/doc/qtdesignstudio/images/icons/back_one_frame.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/create_component.png b/doc/qtdesignstudio/images/icons/create_component.png
new file mode 100644
index 0000000000..75438ce56f
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/create_component.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/curve_editor.png b/doc/qtdesignstudio/images/icons/curve_editor.png
index bda4dc0095..35e019d931 100644
--- a/doc/qtdesignstudio/images/icons/curve_editor.png
+++ b/doc/qtdesignstudio/images/icons/curve_editor.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/easing-curve-linear-icon.png b/doc/qtdesignstudio/images/icons/easing-curve-linear-icon.png
index d6f56990d7..463a9bc038 100644
--- a/doc/qtdesignstudio/images/icons/easing-curve-linear-icon.png
+++ b/doc/qtdesignstudio/images/icons/easing-curve-linear-icon.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/easing-curve-spline-icon.png b/doc/qtdesignstudio/images/icons/easing-curve-spline-icon.png
index f6420d9174..c5328bed8a 100644
--- a/doc/qtdesignstudio/images/icons/easing-curve-spline-icon.png
+++ b/doc/qtdesignstudio/images/icons/easing-curve-spline-icon.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/easing-curve-step-icon.png b/doc/qtdesignstudio/images/icons/easing-curve-step-icon.png
index 5d6523f1c7..11be1b00de 100644
--- a/doc/qtdesignstudio/images/icons/easing-curve-step-icon.png
+++ b/doc/qtdesignstudio/images/icons/easing-curve-step-icon.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/edit.png b/doc/qtdesignstudio/images/icons/edit.png
index 28134d1887..f6ac1e0f1f 100644
--- a/doc/qtdesignstudio/images/icons/edit.png
+++ b/doc/qtdesignstudio/images/icons/edit.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/edit_component.png b/doc/qtdesignstudio/images/icons/edit_component.png
new file mode 100644
index 0000000000..071971b6bc
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/edit_component.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/edit_light_off.png b/doc/qtdesignstudio/images/icons/edit_light_off.png
new file mode 100644
index 0000000000..8b99d32d70
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/edit_light_off.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/edit_light_on.png b/doc/qtdesignstudio/images/icons/edit_light_on.png
new file mode 100644
index 0000000000..45be252860
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/edit_light_on.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/fit_selected.png b/doc/qtdesignstudio/images/icons/fit_selected.png
new file mode 100644
index 0000000000..547a2fd603
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/fit_selected.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/forward_one_frame.png b/doc/qtdesignstudio/images/icons/forward_one_frame.png
index 0846f194e0..9a2d092e88 100644
--- a/doc/qtdesignstudio/images/icons/forward_one_frame.png
+++ b/doc/qtdesignstudio/images/icons/forward_one_frame.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/global.png b/doc/qtdesignstudio/images/icons/global.png
new file mode 100644
index 0000000000..3ff69a4506
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/global.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/global_record_keyframes.png b/doc/qtdesignstudio/images/icons/global_record_keyframes.png
index 64a28ca075..fb8d09dfaf 100644
--- a/doc/qtdesignstudio/images/icons/global_record_keyframes.png
+++ b/doc/qtdesignstudio/images/icons/global_record_keyframes.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/home.png b/doc/qtdesignstudio/images/icons/home.png
new file mode 100644
index 0000000000..8af251cf2c
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/home.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/move_off.png b/doc/qtdesignstudio/images/icons/move_off.png
new file mode 100644
index 0000000000..9d63a51cb0
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/move_off.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/navigator-arrowdown.png b/doc/qtdesignstudio/images/icons/navigator-arrowdown.png
index 894a29ca6f..31cb5a6b38 100644
--- a/doc/qtdesignstudio/images/icons/navigator-arrowdown.png
+++ b/doc/qtdesignstudio/images/icons/navigator-arrowdown.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/navigator-arrowup.png b/doc/qtdesignstudio/images/icons/navigator-arrowup.png
index 85f248f894..80d8f12cfb 100644
--- a/doc/qtdesignstudio/images/icons/navigator-arrowup.png
+++ b/doc/qtdesignstudio/images/icons/navigator-arrowup.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/orthographic_camera.png b/doc/qtdesignstudio/images/icons/orthographic_camera.png
new file mode 100644
index 0000000000..f041caf3d9
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/orthographic_camera.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particle-animation-on.png b/doc/qtdesignstudio/images/icons/particle-animation-on.png
index d240b34c2a..a752cb164c 100644
--- a/doc/qtdesignstudio/images/icons/particle-animation-on.png
+++ b/doc/qtdesignstudio/images/icons/particle-animation-on.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particle-pause.png b/doc/qtdesignstudio/images/icons/particle-pause.png
index 442a77211f..eb922eeedd 100644
--- a/doc/qtdesignstudio/images/icons/particle-pause.png
+++ b/doc/qtdesignstudio/images/icons/particle-pause.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particle-play.png b/doc/qtdesignstudio/images/icons/particle-play.png
index cc04c94897..aada059579 100644
--- a/doc/qtdesignstudio/images/icons/particle-play.png
+++ b/doc/qtdesignstudio/images/icons/particle-play.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particle-restart.png b/doc/qtdesignstudio/images/icons/particle-restart.png
index dc1c06bd49..b8d89b73e1 100644
--- a/doc/qtdesignstudio/images/icons/particle-restart.png
+++ b/doc/qtdesignstudio/images/icons/particle-restart.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particles-seek.png b/doc/qtdesignstudio/images/icons/particles-seek.png
index c6c76fe4ca..2ffc0bb266 100644
--- a/doc/qtdesignstudio/images/icons/particles-seek.png
+++ b/doc/qtdesignstudio/images/icons/particles-seek.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/perspective_camera.png b/doc/qtdesignstudio/images/icons/perspective_camera.png
new file mode 100644
index 0000000000..b3c602da76
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/perspective_camera.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/reset.png b/doc/qtdesignstudio/images/icons/reset.png
new file mode 100644
index 0000000000..b7de014ceb
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/reset.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/rotate_off.png b/doc/qtdesignstudio/images/icons/rotate_off.png
new file mode 100644
index 0000000000..444262b487
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/rotate_off.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/scale_off.png b/doc/qtdesignstudio/images/icons/scale_off.png
new file mode 100644
index 0000000000..553dfa4a5e
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/scale_off.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/select_group.png b/doc/qtdesignstudio/images/icons/select_group.png
new file mode 100644
index 0000000000..3075d37344
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/select_group.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/select_item.png b/doc/qtdesignstudio/images/icons/select_item.png
new file mode 100644
index 0000000000..717c54c41c
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/select_item.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/settings.png b/doc/qtdesignstudio/images/icons/settings.png
new file mode 100644
index 0000000000..8f7f59714d
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/settings.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/start_playback.png b/doc/qtdesignstudio/images/icons/start_playback.png
index 0cf0865c48..9f7194bf16 100644
--- a/doc/qtdesignstudio/images/icons/start_playback.png
+++ b/doc/qtdesignstudio/images/icons/start_playback.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/to_first_frame.png b/doc/qtdesignstudio/images/icons/to_first_frame.png
index 910b856638..c9b9ed4ec0 100644
--- a/doc/qtdesignstudio/images/icons/to_first_frame.png
+++ b/doc/qtdesignstudio/images/icons/to_first_frame.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/to_last_frame.png b/doc/qtdesignstudio/images/icons/to_last_frame.png
index d6bc429196..9dfa631165 100644
--- a/doc/qtdesignstudio/images/icons/to_last_frame.png
+++ b/doc/qtdesignstudio/images/icons/to_last_frame.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/visibilityon.png b/doc/qtdesignstudio/images/icons/visibilityon.png
index 8fc4ca3c36..802096a62e 100644
--- a/doc/qtdesignstudio/images/icons/visibilityon.png
+++ b/doc/qtdesignstudio/images/icons/visibilityon.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/zoomAll.png b/doc/qtdesignstudio/images/icons/zoomAll.png
index 7917d5253c..b7c5b4d7eb 100644
--- a/doc/qtdesignstudio/images/icons/zoomAll.png
+++ b/doc/qtdesignstudio/images/icons/zoomAll.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/zoomIn.png b/doc/qtdesignstudio/images/icons/zoomIn.png
index dae425382d..520719bfec 100644
--- a/doc/qtdesignstudio/images/icons/zoomIn.png
+++ b/doc/qtdesignstudio/images/icons/zoomIn.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/zoomOut.png b/doc/qtdesignstudio/images/icons/zoomOut.png
index 3e889fded1..635475e771 100644
--- a/doc/qtdesignstudio/images/icons/zoomOut.png
+++ b/doc/qtdesignstudio/images/icons/zoomOut.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/zoomSelection.png b/doc/qtdesignstudio/images/icons/zoomSelection.png
index 407e21d271..313bceec9a 100644
--- a/doc/qtdesignstudio/images/icons/zoomSelection.png
+++ b/doc/qtdesignstudio/images/icons/zoomSelection.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/loader3d-navigator.png b/doc/qtdesignstudio/images/loader3d-navigator.png
index 0c049a6146..4c9345d462 100644
--- a/doc/qtdesignstudio/images/loader3d-navigator.png
+++ b/doc/qtdesignstudio/images/loader3d-navigator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/material-copy-properties.png b/doc/qtdesignstudio/images/material-copy-properties.png
index aae3020929..d454685a85 100644
--- a/doc/qtdesignstudio/images/material-copy-properties.png
+++ b/doc/qtdesignstudio/images/material-copy-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/navigator-material-texture.png b/doc/qtdesignstudio/images/navigator-material-texture.png
index 4256e959c6..849625e1eb 100644
--- a/doc/qtdesignstudio/images/navigator-material-texture.png
+++ b/doc/qtdesignstudio/images/navigator-material-texture.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/navigator-show-all-loader.png b/doc/qtdesignstudio/images/navigator-show-all-loader.png
index 2052be66b3..40f3fe0866 100644
--- a/doc/qtdesignstudio/images/navigator-show-all-loader.png
+++ b/doc/qtdesignstudio/images/navigator-show-all-loader.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/new-effect-file.png b/doc/qtdesignstudio/images/new-effect-file.png
deleted file mode 100644
index 521c36152c..0000000000
--- a/doc/qtdesignstudio/images/new-effect-file.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/qml-shapes-rectangle.png b/doc/qtdesignstudio/images/qml-shapes-rectangle.png
index a2f5eaee0f..0dde7e33f6 100644
--- a/doc/qtdesignstudio/images/qml-shapes-rectangle.png
+++ b/doc/qtdesignstudio/images/qml-shapes-rectangle.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qml-shapes.png b/doc/qtdesignstudio/images/qml-shapes.png
index cc0f2c3324..6aaa46a07e 100644
--- a/doc/qtdesignstudio/images/qml-shapes.png
+++ b/doc/qtdesignstudio/images/qml-shapes.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-alignment.png b/doc/qtdesignstudio/images/qmldesigner-alignment.png
index 70ad846850..ffc4109c57 100644
--- a/doc/qtdesignstudio/images/qmldesigner-alignment.png
+++ b/doc/qtdesignstudio/images/qmldesigner-alignment.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-anchors.png b/doc/qtdesignstudio/images/qmldesigner-anchors.png
index 3b4d7d632d..6038027d70 100644
--- a/doc/qtdesignstudio/images/qmldesigner-anchors.png
+++ b/doc/qtdesignstudio/images/qmldesigner-anchors.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-bindings.png b/doc/qtdesignstudio/images/qmldesigner-bindings.png
index ad7dde04b7..3ac0964f76 100644
--- a/doc/qtdesignstudio/images/qmldesigner-bindings.png
+++ b/doc/qtdesignstudio/images/qmldesigner-bindings.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-breadcrumbs.png b/doc/qtdesignstudio/images/qmldesigner-breadcrumbs.png
index 2745134bd3..d57d36b9d9 100644
--- a/doc/qtdesignstudio/images/qmldesigner-breadcrumbs.png
+++ b/doc/qtdesignstudio/images/qmldesigner-breadcrumbs.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-button.png b/doc/qtdesignstudio/images/qmldesigner-button.png
index 6af7d03ee3..bd8720a021 100644
--- a/doc/qtdesignstudio/images/qmldesigner-button.png
+++ b/doc/qtdesignstudio/images/qmldesigner-button.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-canvas-color.png b/doc/qtdesignstudio/images/qmldesigner-canvas-color.png
index af6ce61224..6baef3eb90 100644
--- a/doc/qtdesignstudio/images/qmldesigner-canvas-color.png
+++ b/doc/qtdesignstudio/images/qmldesigner-canvas-color.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-connections.png b/doc/qtdesignstudio/images/qmldesigner-connections.png
index 827359bf49..605dc83578 100644
--- a/doc/qtdesignstudio/images/qmldesigner-connections.png
+++ b/doc/qtdesignstudio/images/qmldesigner-connections.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-dynamicprops.png b/doc/qtdesignstudio/images/qmldesigner-dynamicprops.png
index 460be93f2b..565dc26b0e 100644
--- a/doc/qtdesignstudio/images/qmldesigner-dynamicprops.png
+++ b/doc/qtdesignstudio/images/qmldesigner-dynamicprops.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-editing-components.png b/doc/qtdesignstudio/images/qmldesigner-editing-components.png
deleted file mode 100644
index 6184d706dd..0000000000
--- a/doc/qtdesignstudio/images/qmldesigner-editing-components.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-editing-components.webp b/doc/qtdesignstudio/images/qmldesigner-editing-components.webp
new file mode 100644
index 0000000000..9b725ae546
--- /dev/null
+++ b/doc/qtdesignstudio/images/qmldesigner-editing-components.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-element-size.png b/doc/qtdesignstudio/images/qmldesigner-element-size.png
deleted file mode 100644
index 550eb01dfa..0000000000
--- a/doc/qtdesignstudio/images/qmldesigner-element-size.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-element-size.webp b/doc/qtdesignstudio/images/qmldesigner-element-size.webp
new file mode 100644
index 0000000000..90d61b5fa4
--- /dev/null
+++ b/doc/qtdesignstudio/images/qmldesigner-element-size.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-export-item.png b/doc/qtdesignstudio/images/qmldesigner-export-item.png
index 567a9c5fa4..62e7422208 100644
--- a/doc/qtdesignstudio/images/qmldesigner-export-item.png
+++ b/doc/qtdesignstudio/images/qmldesigner-export-item.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png b/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png
index 5aa3a6c3fb..018a94c88f 100644
--- a/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png
+++ b/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-form-editor.png b/doc/qtdesignstudio/images/qmldesigner-form-editor.png
index b042afe44e..f32f6965ba 100644
--- a/doc/qtdesignstudio/images/qmldesigner-form-editor.png
+++ b/doc/qtdesignstudio/images/qmldesigner-form-editor.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-mcu-support.png b/doc/qtdesignstudio/images/qmldesigner-mcu-support.png
index a9981017fb..98ee56b2a3 100644
--- a/doc/qtdesignstudio/images/qmldesigner-mcu-support.png
+++ b/doc/qtdesignstudio/images/qmldesigner-mcu-support.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-navigator-arrows.png b/doc/qtdesignstudio/images/qmldesigner-navigator-arrows.png
index e1fd5283bf..45754e4519 100644
--- a/doc/qtdesignstudio/images/qmldesigner-navigator-arrows.png
+++ b/doc/qtdesignstudio/images/qmldesigner-navigator-arrows.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-navigator.png b/doc/qtdesignstudio/images/qmldesigner-navigator.png
index ace30596e2..8ec9cd0ddd 100644
--- a/doc/qtdesignstudio/images/qmldesigner-navigator.png
+++ b/doc/qtdesignstudio/images/qmldesigner-navigator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-preview-size.png b/doc/qtdesignstudio/images/qmldesigner-preview-size.png
index d6a481ef73..1a4a5e01ee 100644
--- a/doc/qtdesignstudio/images/qmldesigner-preview-size.png
+++ b/doc/qtdesignstudio/images/qmldesigner-preview-size.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qmldesigner-tutorial-user-icon.png b/doc/qtdesignstudio/images/qmldesigner-tutorial-user-icon.png
index a115cf8fbf..7350ce4339 100644
--- a/doc/qtdesignstudio/images/qmldesigner-tutorial-user-icon.png
+++ b/doc/qtdesignstudio/images/qmldesigner-tutorial-user-icon.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.png b/doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.png
deleted file mode 100644
index c0bd0f897e..0000000000
--- a/doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.webp b/doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.webp
new file mode 100644
index 0000000000..ee6d6f54aa
--- /dev/null
+++ b/doc/qtdesignstudio/images/qtcreator-workspace-attaching-views.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtds-running-emulator.png b/doc/qtdesignstudio/images/qtds-running-emulator.png
index ecd3054db3..b8d7470166 100644
--- a/doc/qtdesignstudio/images/qtds-running-emulator.png
+++ b/doc/qtdesignstudio/images/qtds-running-emulator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-annotations.png b/doc/qtdesignstudio/images/qtquick-annotations.png
index 4fc9ddff3d..114cf80ada 100644
--- a/doc/qtdesignstudio/images/qtquick-annotations.png
+++ b/doc/qtdesignstudio/images/qtquick-annotations.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-assets-tab.png b/doc/qtdesignstudio/images/qtquick-assets-tab.png
index a7e02bab9e..11c4fe54b4 100644
--- a/doc/qtdesignstudio/images/qtquick-assets-tab.png
+++ b/doc/qtdesignstudio/images/qtquick-assets-tab.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-components-context-menu-hide.png b/doc/qtdesignstudio/images/qtquick-components-context-menu-hide.png
index 61ee2fe02d..1681bd2fa3 100644
--- a/doc/qtdesignstudio/images/qtquick-components-context-menu-hide.png
+++ b/doc/qtdesignstudio/images/qtquick-components-context-menu-hide.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-components-context-menu.png b/doc/qtdesignstudio/images/qtquick-components-context-menu.png
index 0b163fd3d2..d333456149 100644
--- a/doc/qtdesignstudio/images/qtquick-components-context-menu.png
+++ b/doc/qtdesignstudio/images/qtquick-components-context-menu.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-components-tab-add.png b/doc/qtdesignstudio/images/qtquick-components-tab-add.png
index 3e3d0ccf98..e208d97203 100644
--- a/doc/qtdesignstudio/images/qtquick-components-tab-add.png
+++ b/doc/qtdesignstudio/images/qtquick-components-tab-add.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-components-tab.png b/doc/qtdesignstudio/images/qtquick-components-tab.png
index b2011c9c77..4ee36abf04 100644
--- a/doc/qtdesignstudio/images/qtquick-components-tab.png
+++ b/doc/qtdesignstudio/images/qtquick-components-tab.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-custom-properties.png b/doc/qtdesignstudio/images/qtquick-custom-properties.png
index ce9fc5d400..ea5567fa06 100644
--- a/doc/qtdesignstudio/images/qtquick-custom-properties.png
+++ b/doc/qtdesignstudio/images/qtquick-custom-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-button-types.png b/doc/qtdesignstudio/images/qtquick-designer-button-types.png
index 5cb551dfd9..3d07baec39 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-button-types.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-button-types.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-image-type.png b/doc/qtdesignstudio/images/qtquick-designer-image-type.png
index 0d09670693..5817e48834 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-image-type.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-image-type.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-indicator-types.png b/doc/qtdesignstudio/images/qtquick-designer-indicator-types.png
index 4fd045bb0d..67a03eab94 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-indicator-types.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-indicator-types.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-qtquickcontrols-types.png b/doc/qtdesignstudio/images/qtquick-designer-qtquickcontrols-types.png
index 4cb28e50e2..bf089813e6 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-qtquickcontrols-types.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-qtquickcontrols-types.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-rotating-items.png b/doc/qtdesignstudio/images/qtquick-designer-rotating-items.png
index b9b8936240..f20669be4e 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-rotating-items.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-rotating-items.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-scaling-items.png b/doc/qtdesignstudio/images/qtquick-designer-scaling-items.png
index 291b4e9e68..891915dae8 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-scaling-items.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-scaling-items.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-selector-types.png b/doc/qtdesignstudio/images/qtquick-designer-selector-types.png
index 759eb79d81..84e9758430 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-selector-types.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-selector-types.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-designer-stacked-view.png b/doc/qtdesignstudio/images/qtquick-designer-stacked-view.png
index 5ede90aed6..ab9756a98d 100644
--- a/doc/qtdesignstudio/images/qtquick-designer-stacked-view.png
+++ b/doc/qtdesignstudio/images/qtquick-designer-stacked-view.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-library-context-menu.png b/doc/qtdesignstudio/images/qtquick-library-context-menu.png
index fa9a293e37..f611ea6f1c 100644
--- a/doc/qtdesignstudio/images/qtquick-library-context-menu.png
+++ b/doc/qtdesignstudio/images/qtquick-library-context-menu.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-positioner-column-properties.png b/doc/qtdesignstudio/images/qtquick-positioner-column-properties.png
index b025b5fe58..2bf25d425c 100644
--- a/doc/qtdesignstudio/images/qtquick-positioner-column-properties.png
+++ b/doc/qtdesignstudio/images/qtquick-positioner-column-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-positioner-flow-properties.png b/doc/qtdesignstudio/images/qtquick-positioner-flow-properties.png
index c027a81817..943e66b76a 100644
--- a/doc/qtdesignstudio/images/qtquick-positioner-flow-properties.png
+++ b/doc/qtdesignstudio/images/qtquick-positioner-flow-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-positioner-grid-properties.png b/doc/qtdesignstudio/images/qtquick-positioner-grid-properties.png
index 4588e39a23..ec6dab0163 100644
--- a/doc/qtdesignstudio/images/qtquick-positioner-grid-properties.png
+++ b/doc/qtdesignstudio/images/qtquick-positioner-grid-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/qtquick-text-editor.png b/doc/qtdesignstudio/images/qtquick-text-editor.png
index 0d1acf87f0..3597860c35 100644
--- a/doc/qtdesignstudio/images/qtquick-text-editor.png
+++ b/doc/qtdesignstudio/images/qtquick-text-editor.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/repeater3d-listmodel-navigator.png b/doc/qtdesignstudio/images/repeater3d-listmodel-navigator.png
index 0f3f768e14..f4b3093bbb 100644
--- a/doc/qtdesignstudio/images/repeater3d-listmodel-navigator.png
+++ b/doc/qtdesignstudio/images/repeater3d-listmodel-navigator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-2d-effects.png b/doc/qtdesignstudio/images/studio-2d-effects.png
index ebf2106280..7fd69ea805 100644
--- a/doc/qtdesignstudio/images/studio-2d-effects.png
+++ b/doc/qtdesignstudio/images/studio-2d-effects.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-area-light.png b/doc/qtdesignstudio/images/studio-3d-area-light.png
deleted file mode 100644
index 24fbfcbe33..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-area-light.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-area-light.webp b/doc/qtdesignstudio/images/studio-3d-area-light.webp
new file mode 100644
index 0000000000..9dee95bcaf
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-area-light.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-directional-light.png b/doc/qtdesignstudio/images/studio-3d-directional-light.png
deleted file mode 100644
index f739e5bba4..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-directional-light.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-directional-light.webp b/doc/qtdesignstudio/images/studio-3d-directional-light.webp
new file mode 100644
index 0000000000..4709c8642a
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-directional-light.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png b/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png
deleted file mode 100644
index 788b498991..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.webp b/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.webp
new file mode 100644
index 0000000000..d0a13907e5
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-move.png b/doc/qtdesignstudio/images/studio-3d-editor-move.png
deleted file mode 100644
index 1e8be3f865..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-editor-move.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-move.webp b/doc/qtdesignstudio/images/studio-3d-editor-move.webp
new file mode 100644
index 0000000000..d2ff9b9aac
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-editor-move.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-rotate.png b/doc/qtdesignstudio/images/studio-3d-editor-rotate.png
deleted file mode 100644
index b6c4ce167a..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-editor-rotate.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-rotate.webp b/doc/qtdesignstudio/images/studio-3d-editor-rotate.webp
new file mode 100644
index 0000000000..7cd8fe1bf6
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-editor-rotate.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-scale.png b/doc/qtdesignstudio/images/studio-3d-editor-scale.png
deleted file mode 100644
index 19d4258275..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-editor-scale.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-scale.webp b/doc/qtdesignstudio/images/studio-3d-editor-scale.webp
new file mode 100644
index 0000000000..be0270d34b
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-editor-scale.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor.png b/doc/qtdesignstudio/images/studio-3d-editor.png
deleted file mode 100644
index a62a6deb76..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-editor.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor.webp b/doc/qtdesignstudio/images/studio-3d-editor.webp
new file mode 100644
index 0000000000..903ad69b25
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-editor.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-effects.png b/doc/qtdesignstudio/images/studio-3d-effects.png
index e47cab97aa..485976b0f7 100644
--- a/doc/qtdesignstudio/images/studio-3d-effects.png
+++ b/doc/qtdesignstudio/images/studio-3d-effects.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-instancing-instance-list.png b/doc/qtdesignstudio/images/studio-3d-instancing-instance-list.png
index f7bb9f8f1f..ff2593162e 100644
--- a/doc/qtdesignstudio/images/studio-3d-instancing-instance-list.png
+++ b/doc/qtdesignstudio/images/studio-3d-instancing-instance-list.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-models.png b/doc/qtdesignstudio/images/studio-3d-models.png
index d7c0708672..de4f65d5e8 100644
--- a/doc/qtdesignstudio/images/studio-3d-models.png
+++ b/doc/qtdesignstudio/images/studio-3d-models.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png
index 733f61bc12..496db801e5 100644
--- a/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png
index e0f112b33b..f859f9f636 100644
--- a/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png
index 6972f27021..37a7b12ea7 100644
--- a/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-sprite-template.png b/doc/qtdesignstudio/images/studio-3d-particles-sprite-template.png
index dd0ce67de4..d27331fe73 100644
--- a/doc/qtdesignstudio/images/studio-3d-particles-sprite-template.png
+++ b/doc/qtdesignstudio/images/studio-3d-particles-sprite-template.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-point-light.png b/doc/qtdesignstudio/images/studio-3d-point-light.png
deleted file mode 100644
index a5eb5a888a..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-point-light.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-point-light.webp b/doc/qtdesignstudio/images/studio-3d-point-light.webp
new file mode 100644
index 0000000000..e4cbfe19d7
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-point-light.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-spot-light.png b/doc/qtdesignstudio/images/studio-3d-spot-light.png
deleted file mode 100644
index 7ebc673e18..0000000000
--- a/doc/qtdesignstudio/images/studio-3d-spot-light.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-spot-light.webp b/doc/qtdesignstudio/images/studio-3d-spot-light.webp
new file mode 100644
index 0000000000..347675a60e
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-spot-light.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-animation.png b/doc/qtdesignstudio/images/studio-animation.png
index c8ef86cfa8..b08a6deed8 100644
--- a/doc/qtdesignstudio/images/studio-animation.png
+++ b/doc/qtdesignstudio/images/studio-animation.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-connection-view-properties.png b/doc/qtdesignstudio/images/studio-connection-view-properties.png
index 6ce6c3479f..ddfade2227 100644
--- a/doc/qtdesignstudio/images/studio-connection-view-properties.png
+++ b/doc/qtdesignstudio/images/studio-connection-view-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-custom-material-uniform-properties.png b/doc/qtdesignstudio/images/studio-custom-material-uniform-properties.png
index b69d456182..5a20aa65c8 100644
--- a/doc/qtdesignstudio/images/studio-custom-material-uniform-properties.png
+++ b/doc/qtdesignstudio/images/studio-custom-material-uniform-properties.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png b/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png
index 3b2b8b173a..71356b5020 100644
--- a/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png
+++ b/doc/qtdesignstudio/images/studio-design-mode-states-timeline.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-design-mode.png b/doc/qtdesignstudio/images/studio-design-mode.png
deleted file mode 100644
index e4a03c583a..0000000000
--- a/doc/qtdesignstudio/images/studio-design-mode.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-design-mode.webp b/doc/qtdesignstudio/images/studio-design-mode.webp
new file mode 100644
index 0000000000..c739d88f99
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-design-mode.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-dial.png b/doc/qtdesignstudio/images/studio-dial.png
index 3d7b75fa89..f6bf5d4501 100644
--- a/doc/qtdesignstudio/images/studio-dial.png
+++ b/doc/qtdesignstudio/images/studio-dial.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-editing-3d-scenes.png b/doc/qtdesignstudio/images/studio-editing-3d-scenes.png
deleted file mode 100644
index 8566b6171c..0000000000
--- a/doc/qtdesignstudio/images/studio-editing-3d-scenes.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-flipable.png b/doc/qtdesignstudio/images/studio-flipable.png
index c9de7404b2..8122416239 100644
--- a/doc/qtdesignstudio/images/studio-flipable.png
+++ b/doc/qtdesignstudio/images/studio-flipable.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-and-checkbox3.png b/doc/qtdesignstudio/images/studio-logic-helper-and-checkbox3.png
index 414cd705a6..56cd080e30 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-and-checkbox3.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-and-checkbox3.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-and.png b/doc/qtdesignstudio/images/studio-logic-helper-and.png
index 0d32952fdd..63f2e454af 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-and.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-and.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-bidirectional-binding.png b/doc/qtdesignstudio/images/studio-logic-helper-bidirectional-binding.png
index 39ca5af0f3..596eb86068 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-bidirectional-binding.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-bidirectional-binding.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-input.png b/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-input.png
index 06e7e4c0cd..92858fb429 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-input.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-input.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-string-mapper-input.png b/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-string-mapper-input.png
index 0a21eaccd5..e6bff57d9b 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-string-mapper-input.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-minmax-mapper-string-mapper-input.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-not-check-box.png b/doc/qtdesignstudio/images/studio-logic-helper-not-check-box.png
index 9411437e9b..cfe4f0e50d 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-not-check-box.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-not-check-box.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-not.png b/doc/qtdesignstudio/images/studio-logic-helper-not.png
index 05ad631b62..5966a470bb 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-not.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-not.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-range-mapper-inputmin.png b/doc/qtdesignstudio/images/studio-logic-helper-range-mapper-inputmin.png
index 2144e58f92..d91b2eebb3 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-range-mapper-inputmin.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-range-mapper-inputmin.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-logic-helper-string-mapper-text.png b/doc/qtdesignstudio/images/studio-logic-helper-string-mapper-text.png
index 43a240fc5b..84754a44d1 100644
--- a/doc/qtdesignstudio/images/studio-logic-helper-string-mapper-text.png
+++ b/doc/qtdesignstudio/images/studio-logic-helper-string-mapper-text.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-navigator-view3d.png b/doc/qtdesignstudio/images/studio-navigator-view3d.png
index 1fa65c10dc..92ce85e965 100644
--- a/doc/qtdesignstudio/images/studio-navigator-view3d.png
+++ b/doc/qtdesignstudio/images/studio-navigator-view3d.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-qml-imports-slconnector.png b/doc/qtdesignstudio/images/studio-qml-imports-slconnector.png
index 03241cea9b..a94176e971 100644
--- a/doc/qtdesignstudio/images/studio-qml-imports-slconnector.png
+++ b/doc/qtdesignstudio/images/studio-qml-imports-slconnector.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png b/doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png
index 21a34f1e1a..9f3fd2f272 100644
--- a/doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png
+++ b/doc/qtdesignstudio/images/studio-qtquick-3d-custom-effect-navigator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-default-material.png b/doc/qtdesignstudio/images/studio-qtquick-3d-default-material.png
deleted file mode 100644
index 627561fd59..0000000000
--- a/doc/qtdesignstudio/images/studio-qtquick-3d-default-material.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-material.png b/doc/qtdesignstudio/images/studio-qtquick-3d-material.png
deleted file mode 100644
index 597f47e22b..0000000000
--- a/doc/qtdesignstudio/images/studio-qtquick-3d-material.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-shapes.png b/doc/qtdesignstudio/images/studio-shapes.png
index 9ac71d7669..b9fb24c88d 100644
--- a/doc/qtdesignstudio/images/studio-shapes.png
+++ b/doc/qtdesignstudio/images/studio-shapes.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.png b/doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.png
deleted file mode 100644
index 3bea1a7a6b..0000000000
--- a/doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.webp b/doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.webp
new file mode 100644
index 0000000000..f8734e98de
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-timeline-keyframe-track-colors.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-no-tracks.png b/doc/qtdesignstudio/images/studio-timeline-no-tracks.png
deleted file mode 100644
index 85a03a00b0..0000000000
--- a/doc/qtdesignstudio/images/studio-timeline-no-tracks.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-no-tracks.webp b/doc/qtdesignstudio/images/studio-timeline-no-tracks.webp
new file mode 100644
index 0000000000..543fa3274f
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-timeline-no-tracks.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.png b/doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.png
deleted file mode 100644
index d116d43e36..0000000000
--- a/doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.webp b/doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.webp
new file mode 100644
index 0000000000..183a344812
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-timeline-with-empty-tracks.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-with-tracks.png b/doc/qtdesignstudio/images/studio-timeline-with-tracks.png
deleted file mode 100644
index 060db8aa2b..0000000000
--- a/doc/qtdesignstudio/images/studio-timeline-with-tracks.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline-with-tracks.webp b/doc/qtdesignstudio/images/studio-timeline-with-tracks.webp
new file mode 100644
index 0000000000..55827cc9e5
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-timeline-with-tracks.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline.png b/doc/qtdesignstudio/images/studio-timeline.png
deleted file mode 100644
index 18e85ca5c1..0000000000
--- a/doc/qtdesignstudio/images/studio-timeline.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-timeline.webp b/doc/qtdesignstudio/images/studio-timeline.webp
new file mode 100644
index 0000000000..4c786bc6a8
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-timeline.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/timeline-per-property-recording.png b/doc/qtdesignstudio/images/timeline-per-property-recording.png
deleted file mode 100644
index 7daa337aa1..0000000000
--- a/doc/qtdesignstudio/images/timeline-per-property-recording.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/timeline-per-property-recording.webp b/doc/qtdesignstudio/images/timeline-per-property-recording.webp
new file mode 100644
index 0000000000..19c705e5d7
--- /dev/null
+++ b/doc/qtdesignstudio/images/timeline-per-property-recording.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/timeline-states.png b/doc/qtdesignstudio/images/timeline-states.png
deleted file mode 100644
index 0ef1f7da95..0000000000
--- a/doc/qtdesignstudio/images/timeline-states.png
+++ /dev/null
Binary files differ
diff --git a/doc/qtdesignstudio/images/timeline-states.webp b/doc/qtdesignstudio/images/timeline-states.webp
new file mode 100644
index 0000000000..ccaabfc6dc
--- /dev/null
+++ b/doc/qtdesignstudio/images/timeline-states.webp
Binary files differ
diff --git a/doc/qtdesignstudio/images/toolbar-show-live-preview.png b/doc/qtdesignstudio/images/toolbar-show-live-preview.png
index b942fcf5c1..b8741f9d41 100644
--- a/doc/qtdesignstudio/images/toolbar-show-live-preview.png
+++ b/doc/qtdesignstudio/images/toolbar-show-live-preview.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-change-file.png b/doc/qtdesignstudio/images/web-navigation-change-file.png
new file mode 100644
index 0000000000..efa9131d3e
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-change-file.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-column-layout.png b/doc/qtdesignstudio/images/web-navigation-column-layout.png
new file mode 100644
index 0000000000..dde2bc1c2b
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-column-layout.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-components-2.png b/doc/qtdesignstudio/images/web-navigation-components-2.png
new file mode 100644
index 0000000000..d8a02960bf
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-components-2.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-components.png b/doc/qtdesignstudio/images/web-navigation-components.png
new file mode 100644
index 0000000000..602e0b9e4c
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-components.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-new-file.png b/doc/qtdesignstudio/images/web-navigation-new-file.png
new file mode 100644
index 0000000000..ff08d0c50b
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-new-file.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-page-components.png b/doc/qtdesignstudio/images/web-navigation-page-components.png
new file mode 100644
index 0000000000..7cfde0fa19
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-page-components.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-page-margins.png b/doc/qtdesignstudio/images/web-navigation-page-margins.png
new file mode 100644
index 0000000000..b307f07e60
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-page-margins.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-size-binding-2.png b/doc/qtdesignstudio/images/web-navigation-size-binding-2.png
new file mode 100644
index 0000000000..12bf6b9903
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-size-binding-2.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/web-navigation-size-binding.png b/doc/qtdesignstudio/images/web-navigation-size-binding.png
new file mode 100644
index 0000000000..b7461581eb
--- /dev/null
+++ b/doc/qtdesignstudio/images/web-navigation-size-binding.png
Binary files differ
diff --git a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc
index 088e133d8b..d2a1a1760d 100644
--- a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc
+++ b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc
@@ -238,9 +238,9 @@
pressed down.
\image qmldesigner-borderimage-bindings1.png "Inactive state when condition"
\li Press \key {Ctrl+S} to save the button.
- \li Select the \inlineimage icons/live_preview.png
- (\uicontrol {Show Live Preview}) button to check how the
- button behaves when you click it. You can drag the preview
+ \li Select the
+ \uicontrol {Live Preview} button on the top toolbar to see how the
+ button behaves when you select it. Drag the preview
window borders to see what happens when you resize the
component.
\endlist
diff --git a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc
index 5c48c80972..1d6b1cd08a 100644
--- a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc
+++ b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc
@@ -63,7 +63,7 @@
\li Go to Implementation
\li \l{Using UI Files}
\row
- \li Go into Component
+ \li Edit Component
\li \l{Moving Within Components}
\endtable
//! [context-menu]
diff --git a/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc b/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc
index ada17fae01..0cdced17c7 100644
--- a/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc
+++ b/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc
@@ -11,7 +11,7 @@
\QDS comes with \e {preset components} that you can use in your UI by
creating instances of them.
- \image qmldesigner-editing-components.png "Creating Component Instances"
+ \image qmldesigner-editing-components.webp "Creating Component Instances"
To create component instances and edit their properties:
diff --git a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc
index 923428779b..7d394de29e 100644
--- a/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc
+++ b/doc/qtdesignstudio/src/components/qtquick-components-custom.qdoc
@@ -90,7 +90,7 @@
component instances into custom components by moving them into
separate component files (.ui.qml). Right-click a component instance
in \uicontrol Navigator or the \uicontrol {2D} view, and select
- \uicontrol {Move Component into Separate File} in the context menu.
+ \uicontrol {Create Component} in the context menu.
\image qtcreator-move-component-into-separate-file.png
diff --git a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc
index 5ea37b040c..a71831fdd1 100644
--- a/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc
+++ b/doc/qtdesignstudio/src/components/qtquick-positioning.qdoc
@@ -407,12 +407,6 @@
\uicontrol {Stack Layout}, select the components in the \l {2D} view,
and then select \uicontrol Layout in the context menu.
- You can also click the \inlineimage column.png
- (\uicontrol {Column Layout}), \inlineimage row.png
- (\uicontrol {Row Layout}), and \inlineimage grid.png
- (\uicontrol {Grid Layout}) toolbar buttons to apply
- layouts to the selected components.
-
To make a component within a layout as wide as possible while respecting the
given constraints, select the component in the \uicontrol {2D} view, and
then select \uicontrol Layout > \uicontrol {Fill Width} in the context menu.
diff --git a/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc b/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc
new file mode 100644
index 0000000000..567650a137
--- /dev/null
+++ b/doc/qtdesignstudio/src/overviews/qt-design-viewer-navigation.qdoc
@@ -0,0 +1,289 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+
+ \page design-viewer-single-page-navigation.html
+ \previouspage qt-design-viewer.html
+ \nextpage studio-exporting-and-importing.html
+
+
+ \title Creating a Single Page Navigation Web Application
+
+ This example explains how you can create a single page navigation web
+ application suitable to run in Qt Design Viewer. In this project,
+ you create the structure and navigation for the web application.
+
+ \section1 Setting up the Project
+
+ To set up the project:
+ \list 1
+ \li
+ In \QDS, create a new project where you set:
+ \list
+ \li \uicontrol Preset to \uicontrol Desktop > \uicontrol Launcher.
+ \li \uicontrol Resolution to 1024 x 768.
+ \li \uicontrol {Target Version} to 6.2.
+ \endlist
+ \li In \uicontrol Navigator:
+ \list
+ \li Select and delete \e Text.
+ \li Select \e Rectangle and in \uicontrol Properties, set
+ \uicontrol {Fill color} to #ffffff.
+ \endlist
+ \endlist
+
+ \section1 Adding Components
+
+ Next, add the needed components to create the structure for your web
+ application.
+
+ Add the \uicontrol {QtQuick Layouts} module:
+ \list 1
+ \li In \uicontrol Components, select
+ \inlineimage icons/plus.png
+ \li Select \uicontrol {QtQuick.Layouts}.
+ \endlist
+
+ To add the structure for the web application,
+ drag and drop the following components from \uicontrol Components
+ to \e rectangle in \uicontrol Navigator.
+ \list
+ \li \uicontrol Rectangle
+ \list
+ \li \uicontrol Rectangle
+ \list
+ \li \uicontrol Row
+ \list
+ \li \uicontrol Button
+ \li \uicontrol Button
+ \li \uicontrol Button
+ \endlist
+ \endlist
+ \li \uicontrol Flickable
+ \list
+ \li \uicontrol ColumnLayout
+ \endlist
+ \endlist
+
+ \endlist
+
+ \image web-navigation-components.png
+
+ \section1 Creating the Pages
+
+ Next, create the separate pages for your web application. In this example,
+ you create pages for \e Home, \e {About Us}, and \e {Contact Us}.
+
+ You create each page as a separate component and then add it to the main
+ application.
+
+ To create the first page:
+
+ \list 1
+ \li Go to \uicontrol File > \uicontrol {New File}.
+ \li On the \uicontrol {Qt Quick Files} tab,
+ select \uicontrol {Qt Quick File}.
+ \li Select \uicontrol {Choose} and enter a name, for example, \e Page1.
+ \li Set \uicontrol {Root Item} to \e Rectangle.
+ \endlist
+
+ \image web-navigation-new-file.png
+
+ When you have created the new page, select \e rectangle in
+ \uicontrol Navigator, and in the \uicontrol Properties view:
+ \list
+ \li Set \uicontrol Size > \uicontrol H to 1024.
+ \li Next to \uicontrol Size > \uicontrol W, select
+ \inlineimage icons/action-icon.png
+ and select \uicontrol Reset.
+ \endlist
+
+ Next, create a header for the page:
+ \list 1
+ \li From \uicontrol Components, drag a \uicontrol Text component
+ to \e Rectangle in \uicontrol Navigator.
+ \li In \uicontrol Properties, go to the \uicontrol Text tab and set:
+ \list
+ \li \uicontrol Text to \e Welcome.
+ \li \uicontrol {Style Name} to Bold.
+ \li \uicontrol Size to 32 px.
+ \endlist
+ \li On the \uicontrol Layout tab set the anchors and margins to:
+ \list
+ \li Top, 100
+ \li Left, 50
+ \endlist
+ \image web-navigation-page-margins.png
+ \endlist
+
+Now, with the first page done, create two more pages in the same way. For these
+pages, set the text to \e {About Us} and \e {Contact Us} respectively.
+
+You can change the file that you are working on from the drop-down menu in the
+toolbar. Now, select \e Screen01.ui.qml from this menu to go back to your
+main page.
+
+\image web-navigation-change-file.png
+
+You can see the pages you created under \uicontrol {My Components} in the
+\uicontrol Components view. To edit a component, right-click it in
+\uicontrol Components and select \uicontrol {Edit Component}
+
+\image web-navigation-page-components.png
+
+\section1 Organizing the Pages
+
+To organize the pages vertically:
+
+\list 1
+ \li From \uicontrol Components, drag each of the pages to
+ \e columnLayout in \uicontrol Navigator.
+ \image web-navigation-components-2.png
+ \li Select \e columnLayout in Navigator and in \uicontrol Properties:
+ \list
+ \li Next to \uicontrol Size > \uicontrol W and \uicontrol Size >
+ \uicontrol H, select \inlineimage icons/action-icon.png
+ and select \uicontrol Reset.
+ \li Set \uicontrol {Column Spacing} to 0.
+ \endlist
+ \li Select \e flickable in \uicontrol Navigator, and in \uicontrol Properties:
+ \list
+ \li Next to \uicontrol Size > \uicontrol W and \uicontrol Size >
+ \uicontrol H, select \inlineimage icons/action-icon.png
+ and select \uicontrol Reset.
+ \li Set \uicontrol {Content size} > \uicontrol H to 3072.
+ \li On the \uicontrol Layout tab, select
+ \uicontrol {Fill parent component}.
+ \endlist
+\endlist
+
+You must also create a scrollbar to scroll the web application. You create
+vertical and horizontal scrollbars that are visible only when the content
+doesn't fit in the window, similar to web browser scrollbars.
+
+To create the scrollbar, go to the \uicontrol Code view and enter the scrollbar
+code inside the \e Flickable component:
+\code
+ Flickable {
+ id: flickable
+ anchors.fill: parent
+ contentHeight: 3072
+ ScrollBar.vertical: ScrollBar {
+ policy: flickable.contentHeight > flickable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
+ width: 20
+ }
+ ScrollBar.horizontal: ScrollBar {
+ policy: flickable.contentWidth > flickable.width ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
+ height: 20
+ }
+ ...
+\endcode
+
+To align the scrollbar to the right and bottom side of the window, set the height and width of the
+main rectangle so that it adapts to the window size.
+
+\list 1
+ \li In \uicontrol Navigator, select \e Rectangle.
+ \li In \uicontrol Properties, select
+ \inlineimage icons/action-icon-binding.png
+ next to \uicontrol Width and select \uicontrol {Set Binding}.
+ \li Enter \c {Window.width}
+ \image web-navigation-size-binding.png
+ \li Repeat step 2 and 3 for \uicontrol Height and set the value to
+ \c {Window.height}.
+\endlist
+
+\section1 Creating the Navigation
+
+The final step is to create the navigation for the web page. To do this, use the buttons
+that you created earlier.
+
+First, create an animation to use when scrolling between the different pages:
+
+\list 1
+ \li From \uicontrol Components, drag a \uicontrol {Number Animation} to
+ \e Rectangle in \uicontrol Navigator.
+ \li In \uicontrol Properties, set:
+ \list
+ \li \uicontrol Target to \e flickable.
+ \li \uicontrol Property to \e contentY.
+ \li \uicontrol Duration to \e 200.
+ \endlist
+\endlist
+
+Next, connect the buttons to the number animation to scroll the content
+vertically to the correct place.
+
+\list 1
+ \li In \uicontrol Navigator, select \e rectangle and in \uicontrol Properties
+ set:
+ \list
+ \li \uicontrol Height to 40.
+ \li \uicontrol {Fill color} to #e0e0e0.
+ \li \uicontrol {Z stack} to 1.
+ \endlist
+ \li Select \inlineimage icons/action-icon-binding.png
+ next to \uicontrol Width and select \uicontrol {Set Binding}.
+ \li Enter \c {parent.width}.
+ \image web-navigation-size-binding-2.png
+ \li In \uicontrol Navigator:
+ \list 1
+ \li Select \e Button and on the \uicontrol Button tab in \uicontrol Properties,
+ set \uicontrol Text to \e {Home}.
+ \li Select \e Button1 and on the \uicontrol Button tab in \uicontrol Properties,
+ set \uicontrol Name to \e {About Us}.
+ \li Select \e Button2 and on the \uicontrol Button tab in \uicontrol Properties,
+ set \uicontrol Name to \e {Contact Us}.
+ \endlist
+ \li In \uicontrol Code, enter \e connections for each of the buttons to run
+ the number animation when pressed.
+ \code
+ Button {
+ id: button
+ text: qsTr("Home")
+ Connections {
+ target: button
+
+ onPressed: {
+ numberAnimation.to = 0
+ numberAnimation.start()
+ }
+ }
+ }
+
+ Button {
+ id: button1
+ text: qsTr("About Us")
+ Connections {
+ target: button1
+
+ onPressed: {
+ numberAnimation.to = 1024
+ numberAnimation.start()
+ }
+ }
+ }
+
+ Button {
+ id: button2
+ text: qsTr("Contact Us")
+ Connections {
+ target: button2
+
+ onPressed: {
+ numberAnimation.to = 2048
+ numberAnimation.start()
+ }
+ }
+ }
+ \endcode
+\endlist
+
+\section1 Previewing the application
+
+To preview your application in the live preview, select \key Alt + \key P. You
+can also go to \uicontrol File > \uicontrol {Share Application Online} to
+share and preview your application in a web browser.
+
+*/
diff --git a/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc b/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc
index 069ad71f27..1120bc6106 100644
--- a/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc
+++ b/doc/qtdesignstudio/src/overviews/qt-design-viewer.qdoc
@@ -4,7 +4,7 @@
/*!
\page qt-design-viewer.html
\previouspage creator-live-preview-android.html
- \nextpage studio-exporting-and-importing.html
+ \nextpage design-viewer-single-page-navigation.html
\title Sharing Applications Online
@@ -44,4 +44,8 @@
applications.
\image share-online-manage.webp
+
+ \section1 Best Practices
+
+ \l {Creating a Single Page Navigation Web Application}
*/
diff --git a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc
index fa268b530d..d5f19de7a4 100644
--- a/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc
+++ b/doc/qtdesignstudio/src/overviews/qtquick-annotations.qdoc
@@ -21,8 +21,8 @@
An annotation consist of an annotation name and one or several comments.
The comments have a title, author, and comment text.
- To add or edit global annotations, select \inlineimage icons/annotation.png
- on the top menu bar in the Design mode.
+ To add or edit global annotations, right-click in the \uicontrol 2D or
+ \uicontrol Navigator view and select \uicontrol {Edit Annotations}.
Global annotations have an additional status property, which enables you
to indicate whether you are still working on the design, you have submitted
diff --git a/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc b/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc
index ec7243d004..c4cdec1ea5 100644
--- a/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc
+++ b/doc/qtdesignstudio/src/prototyping/qtquick-live-preview-android.qdoc
@@ -150,13 +150,14 @@
Next, to run the emulator, do one of the following:
\list
- \li Select \uicontrol{Show Live Preview} in the the \uicontrol {2D} view toolbar.
+ \li Select \uicontrol{Live Preview} in the top toolbar.
\image toolbar-show-live-preview.png
\li Select \uicontrol Build > \uicontrol{QML Preview}.
\note The \uicontrol Build menu option is not visible by default. To show
it, go to \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment
> \uicontrol {Qt Design Studio Configuration}.
\image menu-build-qml-preview.png
+ \li Select \key Alt + \key P.
\endlist
Now the emulator runs, the qtdesignviewer APK delivered with the \QDS installation
diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc
new file mode 100644
index 0000000000..29b161562d
--- /dev/null
+++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-template.qdoc
@@ -0,0 +1,87 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \previouspage qtbridge-figma-using.html
+ \page qtbridge-figma-template.html
+ \nextpage exporting-3d-assets.html
+
+ \title Using Figma Quick Control Template Components in \QDS
+
+ You can design with the template components created by \QDS in Figma
+ and import them to \QDS with \QBF. These template components are structured
+ specifically for Figma. When you design in Figma using them, and import the design with
+ \QBF, they generate functional QML components for \QDS. So, you can edit components both in
+ \QDS and Figma.
+
+ \section1 Using Figma Template Components
+
+ You should have these prerequisites available:
+ \list
+ \li A Figma professional account.
+ \li \QDS Enterprise license.
+ \li \QDS 3.9 or above.
+
+ \note You can try out the template features with a Figma starter account as well. However,
+ if you want to publish template assets in Figma, you need a Figma professional account
+ for that.
+ \endlist
+
+ \section2 Creating Figma Design with Template Components
+
+ \list 1
+ \li Sign in to Figma.
+ \li Go to the Template provided by \QDS team
+ \externallink {https://www.figma.com/community/file/1185200043286168239}{here}.
+ \li Select \uicontrol {Get a copy} and then your account to have a copy
+ on your Figma workspace.
+
+ \note From the \uicontrol {Layers} tab you can find pages of contents. Here,
+ select \uicontrol {Introduction} to access all the knowledge about the
+ templates workflow and key concepts.
+ \li There are templates for individual components in separate pages.
+ Select the page you want to work on from template and copy
+ all of its contents.
+ \li Create a new Figma design file. In Figma, select \uicontrol Menu
+ > \uicontrol File > \uicontrol {New design file}.
+ \li Paste the copied content to this file and save.
+ Here, do all the modification you need using Figma tools.
+ \li Next, publish this template assets. In Figma, select \uicontrol Menu
+ > \uicontrol Libraries. Then select \uicontrol Publish. You don't need to
+ publish the template itself, just publish the components. You can clear
+ \uicontrol Template from the \uicontrol {Changes} section to have it removed from
+ publishing assets.
+ \li Create another new project, where you would use this published component.
+ Or, you can use this component in one of your existing projects in Figma.
+
+ \note In Figma, select \uicontrol Menu > \uicontrol Libraries to find the
+ published components. You can find these components under \uicontrol {Your teams}.
+ Toggle them active to use in a project.
+ \endlist
+
+ \section2 Importing the Figma Design to \QDS with \QBF
+
+ \list 1
+ \li In Figma, do one of the following:
+ \list
+ \li Select \uicontrol Menu > \uicontrol Plugins > \uicontrol {Qt Bridge for Figma}.
+ \li Select \uicontrol Resources \e(Shift + I) > \uicontrol {Qt Bridge for Figma}
+ > \uicontrol Run.
+ \endlist
+ \li Save the file to your local system.
+ \li Import the \QBF file to a project in \QDS. You can drag the file to project.
+ Then, select \uicontrol Import, and wait until the process is finished.
+ \li Drag the imported \QBF file to the \QDS open project. Select \uicontrol Import, and
+ wait until the process is finished.
+ \li You can find the imported design as QML files in \QDS. Figma
+ assets are also imported as components in \QDS. You can manipulate
+ \uicontrol states or \uicontrol properties natively.
+
+ \note You can also edit the design in Figma and bring it again to \QDS using \QBF.
+ However, you need to name this imported file exactly same as before. Then import it to
+ the project in \QDS to have it synchronized. \QDS lets you have the
+ updates you have made locally on top of the imported component updates.
+ \endlist
+
+
+*/
diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc
index 8a00c8833c..1af2351e7d 100644
--- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc
+++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc
@@ -4,7 +4,7 @@
/*!
\previouspage qtbridge-figma-setup.html
\page qtbridge-figma-using.html
- \nextpage exporting-3d-assets.html
+ \nextpage qtbridge-figma-template.html
\title Using \QBF
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc
index 5514acd2f2..05906cba86 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc
@@ -282,9 +282,9 @@
and transition line.
\endlist
- To preview the flow, select the \inlineimage icons/live_preview.png
- (\uicontrol {Show Live Preview}) button on the Design mode
- \l{Summary of Main Toolbar Actions}{toolbar} or press \key {Alt+P}.
+ To preview the flow, select the
+ \uicontrol {Live Preview} button on the top toolbar or press \key Alt +
+ \key P.
\section1 Common Properties
@@ -485,9 +485,8 @@
To create an event list:
\list 1
- \li Select the \inlineimage icons/edit.png
- (\uicontrol {Show Event List}) button on the Design mode
- \l{Summary of Main Toolbar Actions}{toolbar}, or press \key {Alt+E}.
+ \li Right-click in the \uicontrol 2D or \uicontrol Navigator view and select
+ \uicontrol {Event List} > \uicontrol {Show Event List}.
\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"
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc
index e00bb35486..06b3ea1590 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-effect-maker-files.qdoc
@@ -34,36 +34,20 @@
\section1 Creating an Effect File
- You can create empty Qt Quick Effect Maker effect (.qep) files in \QDS and
+ You can create Qt Quick Effect Maker effect (.qep) files in \QDS and
then edit them in Qt Quick Effect Maker.
To create an effect file:
\list 1
- \li In \QDS, go to \uicontrol File > \uicontrol {New File}.
- \li Go to the \uicontrol Effects tab and select
- \uicontrol {Effect file (Effect Maker)}.
- \image new-effect-file.png
- \li Select \uicontrol Choose and follow the wizard to create the file.
- \endlist
-
- After you have created the effect file, it is available in the
- \uicontrol Assets view.
-
- \image assets-view-effect.png
-
- \section2 Editing and Re-importing an Effect File
-
- To edit an effect file in Qt Quick Effect Maker, double-click it in
- the \uicontrol Assets view. This opens the effect in Qt Quick Effect
- Maker where you can make your changes.
-
- When you have edited the effect file in Qt Quick Effect Maker, you need
- to save and export it:
- \list 1
+ \li In \QDS, right-click in the \uicontrol Assets view and
+ select \uicontrol {New Effect}.
+ \QDS creates an effect file and opens it in Qt Quick Effect Maker.
+ \image qt-quick-effect-maker.webp
+ \li Edit the effect.
\li In Qt Quick Effect Maker, go to \uicontrol File > \uicontrol Save.
\li Select \uicontrol File > \uicontrol Export.
- \li With the default settings, select \uicontrol Ok.
+ \li With the default settings, select \uicontrol OK.
\image effect-maker-export.png
\endlist
@@ -73,7 +57,8 @@
\section1 Applying an Effect
You can apply effects to components in \QDS. To do so, drag the effect
- from the \uicontrol Assets view to the component in the \uicontrol 2D view.
+ from the \uicontrol Assets view to the component in the \uicontrol 2D or
+ \uicontrol Navigator view.
\image apply-effect-maker-effect.webp
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc
index 38c6cf5695..c5f9ef4276 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-exporting-and-importing.qdoc
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \previouspage qt-design-viewer.html
+ \previouspage design-viewer-single-page-navigation.html
\page studio-exporting-and-importing.html
\nextpage qtbridge-overview.html
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc
index d9be56eacd..ffb23d35f2 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc
@@ -183,7 +183,7 @@
\li Wizard Template
\li Purpose
\row
- \li {1,4} Qt Quick Files
+ \li {1,5} Qt Quick Files
\li Flow Item and Flow View
\li Generate components that you can use to design the
\l{Designing Application Flows}{application flow}.
@@ -203,6 +203,10 @@
\li Generates a Grid View or a List View. For more information, see
\l{List and Grid Views}.
\row
+ \li Qt Quick UI Form
+ \li Creates a UI file along with a matching QML file for
+ implementation purposes.
+ \row
\li {1,9} Qt Quick Controls
\li Custom Button
\li Creates a \l {Button}{push button} with a text label.
@@ -225,7 +229,7 @@
\li \l Pane
\li Provides a background that matches the UI style and theme.
\row
- \li StackView
+ \li Stacked Layout
\li Provides a stack-based navigation model.
\row
\li SwipeView
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc
index 77c63d5fe1..6bad9919d5 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-simulink.qdoc
@@ -23,7 +23,7 @@
\section1 The Qt Blockset for Simulink
- Install the Simulink \l {https://git.qt.io/qt-design-studio/simulink-plugin-dependencies}
+ Install the Simulink \l {https://git.qt.io/design-studio/simulink/simulink-plugin-dependencies}
{Qt Blockset} to your computer in order to connect a Simulink model to your
application. The Qt Blockset installer adds the Simulink blocks needed to
establish connectivity to your application. After installation, the
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc
index dd5c1609b1..4859c20488 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc
@@ -149,7 +149,7 @@
\uicontrol Help for reading documentation. The other modes are mostly
needed for application development.
- \image studio-design-mode.png "Design mode"
+ \image studio-design-mode.webp "Design mode"
Read more about modes:
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
index 68769528e8..89a93a4d43 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
@@ -134,6 +134,9 @@
\li \l{Previewing on Devices}
\li \l{Previewing Android Applications}
\li \l{Sharing Applications Online}
+ \list
+ \li \l{Creating a Single Page Navigation Web Application}
+ \endlist
\endlist
\li \l {Asset Creation with Other Tools}
\list
@@ -159,6 +162,7 @@
\list
\li \l{Setting Up Qt Bridge for Figma}
\li \l{Using Qt Bridge for Figma}
+ \li \l{Using Figma Quick Control Template in Qt Design Studio}
\endlist
\endlist
\li \l {Exporting 3D Assets}
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc
index 2910cf2a9b..e861133efd 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc
@@ -5,7 +5,7 @@
/*!
\page exporting-3d-assets.html
\if defined(qtdesignstudio)
- \previouspage figmaqtbridge.html
+ \previouspage qtbridge-figma-template.html
\else
\previouspage quick-states.html
\endif
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
index 810692e89b..1f69fe95cd 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
@@ -62,7 +62,7 @@
select the \inlineimage icons/reset.png
(\uicontrol {Reset View}) button.
- \image studio-3d-editor.png "The 3D view"
+ \image studio-3d-editor.webp "The 3D view"
The following video illustrates navigating in the \uicontrol{3D} view and
using the toolbar:
@@ -104,7 +104,7 @@
selected, the camera is pointed at the world origin. This does not affect
the camera zoom level.
- \image studio-3d-editor-axis-helper.png "Axis helper in the 3D view"
+ \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
@@ -116,8 +116,7 @@
\section1 Using Global and Local Orientation
To switch between local and global orientation, select
- \inlineimage local.png
- or \inlineimage global.png
+ \inlineimage global.png
(\uicontrol {Toggle Local/Global Orientation})
or press \key Y.
@@ -170,13 +169,13 @@
\target moving components 3d view
\section1 Moving Components
- \image studio-3d-editor-move.png "The 3D view in move mode"
+ \image studio-3d-editor-move.webp "The 3D view in move mode"
You can move components in relation to their coordinate system, along the x,
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_on.png
+ To move components, select \inlineimage move_off.png
or press \key W:
\list
@@ -190,9 +189,9 @@
\section1 Rotating Components
- \image studio-3d-editor-rotate.png "The 3D view in rotate mode"
+ \image studio-3d-editor-rotate.webp "The 3D view in rotate mode"
- To rotate components, select \inlineimage rotate_on.png
+ To rotate components, select \inlineimage rotate_off.png
or press \key E:
\list
@@ -204,13 +203,13 @@
\section1 Scaling Components
- \image studio-3d-editor-scale.png "The 3D view in scale mode"
+ \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
component. You can adjust the scale across one, two, or three axes,
depending on the handle.
- To scale components, select \inlineimage scale_on.png
+ To scale components, select \inlineimage scale_off.png
or press \key R:
\list
@@ -330,49 +329,45 @@
\li Keyboard Shortcut
\li Read More
\row
- \li \inlineimage select_group.png
- \inlineimage select_item.png
+ \li \inlineimage icons/select_group.png
+ \inlineimage icons/select_item.png
\li Toggle Group/Single Selection Mode
\li \key Q
\li \l{Selecting Components}
\row
- \li \inlineimage move_off.png
- \inlineimage move_on.png
+ \li \inlineimage icons/move_off.png
\li Activate the Move Tool
\li \key W
\li \l{moving components 3d view}{Moving Components}
\row
- \li \inlineimage rotate_off.png
- \inlineimage rotate_on.png
+ \li \inlineimage icons/rotate_off.png
\li Activate Rotate Tool
\li \key E
\li \l{Rotating Components}
\row
- \li \inlineimage scale_off.png
- \inlineimage scale_on.png
+ \li \inlineimage icons/scale_off.png
\li Activate Scale Tool
\li \key R
\li \l{Scaling Components}
\row
- \li \inlineimage fit_selected.png
+ \li \inlineimage icons/fit_selected.png
\li Fit Selected Object to View
\li \key F
\li \l{Controlling the 3D View Camera}
\row
- \li \inlineimage perspective_camera.png
- \inlineimage orthographic_camera.png
+ \li \inlineimage icons/perspective_camera.png
+ \inlineimage icons/orthographic_camera.png
\li Toggle Perspective/Orthographic Edit Camera
\li \key T
\li \l{Controlling the 3D View Camera}
\row
- \li \inlineimage global.png
- \inlineimage local.png
+ \li \inlineimage icons/global.png
\li Toggle Global/Local Orientation
\li \key Y
\li \l{Using Global and Local Orientation}
\row
- \li \inlineimage edit_light_off.png
- \inlineimage edit_light_on.png
+ \li \inlineimage icons/edit_light_off.png
+ \inlineimage icons/edit_light_on.png
\li Toggle Edit Light On/Off
\li \key U
\li \l{Using Edit Light}
@@ -403,7 +398,6 @@
\li \l{Particle Editor}
\row
\li \inlineimage icons/particle-animation-on.png
- \inlineimage icons/particle-animation-off.png
\li Toggle Particle Animation
\li \key V
\li \l{Particle Editor}
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc
index 6d1fbc5bd1..47e39f3211 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc
@@ -93,7 +93,7 @@
Use the \uicontrol Brightness handle of the light gizmo (1) to adjust the
\uicontrol Brightness property of any of the light components.
- \image studio-3d-directional-light.png "Models lit by a directional light."
+ \image studio-3d-directional-light.webp "Models lit by a directional light."
If the \uicontrol {Casts shadow} property is enabled, shadows are positioned
parallel to the light direction. A directional light has infinite range and
@@ -118,7 +118,7 @@
strength in all directions from the center of the light. This is similar
to the way a light bulb emits light.
- \image studio-3d-point-light.png "Models lit by a point light."
+ \image studio-3d-point-light.webp "Models lit by a point light."
Lighting is applied outwards from the center of a point light, becoming
increasingly dim away from the center. Moving a point light changes the
@@ -158,7 +158,7 @@
\uicontrol {Inner cone angle} (4), and \uicontrol {Quadratic fade} (5)
properties.
- \image studio-3d-spot-light.png "A model lit by a spot light."
+ \image studio-3d-spot-light.webp "A model lit by a spot light."
Inside the inner cone angle, the spot light behaves similarly to the point
light. There the light intensity diminishes according to inverse-square-law.
@@ -176,7 +176,7 @@
gizmo or the \uicontrol Properties view to set the \uicontrol Width (6)
and \uicontrol Height (7) properties to determine the size of the area light.
- \image studio-3d-area-light.png "A model lit by two area lights."
+ \image studio-3d-area-light.webp "A model lit by two area lights."
Aside from the size, an area light has the same properties as a directional
light.
diff --git a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc
index d7af982392..a324c6f562 100644
--- a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc
@@ -23,7 +23,7 @@
\uicontrol View > \uicontrol Views. The following images present the
views that you are likely to use most often while designing UIs.
- \image studio-design-mode.png "Design views"
+ \image studio-design-mode.webp "Design views"
\image studio-design-mode-states-timeline.png "The States and Timeline views"
You can move the views anywhere on the screen and save them as
@@ -150,6 +150,33 @@
\li Action
\li Keyboard Shortcut
\li Read More
+
+ \if defined(qtdesignstudio)
+ \row
+ \li \inlineimage icons/home.png
+ \li \uicontrol {Home}: opens the welcome page.
+ \li
+ \li
+ \row
+ \li \inlineimage icons/start_playback.png
+ \li \uicontrol {Play}: runs the application.
+ \li
+ \li
+ \row
+ \li \uicontrol {Live Preview}
+ \li Shows a preview of the current file or the entire UI. The changes you
+ make to the UI are instantly visible to you in the preview.
+ \li \key Alt+P (\key Opt+P on \macos)
+ \li \l{Validating with Target Hardware}
+ \endif
+
+ \row
+ \li Currently open file
+ \li Displays the location and filename of the currently open file. You
+ can select another file in the list of open files to view it in
+ the \uicontrol {2D} and \uicontrol Navigator views.
+ \li
+ \li \l{Open Documents}
\row
\li \inlineimage icons/prev.png
\li \uicontrol {Go Back}: moves a step backwards in your location history.
@@ -163,130 +190,20 @@
\li \key Alt+> (\key Opt+Cmd+> on \macos)
\li \l{Navigating Between Open Files and Symbols}
\row
- \li \inlineimage icons/unlocked.png
- \li File is writable: the currently open file can be modified and saved.
- \li
- \li \l{Open Documents}
- \row
- \li File type icon
- \li Indicates the type of the currently open file. Design views cannot
- be split, so the icon cannot be dragged, contrary to the tooltip.
- \li
- \li \l{Open Documents}
- \row
- \li Currently open file
- \li Displays the location and filename of the currently open file. You
- can select another file in the list of open files to view it in
- the \uicontrol {2D} and \uicontrol Navigator views.
- \li
- \li \l{Open Documents}
- \row
\li \inlineimage icons/close.png
\li \uicontrol {Close Document}: closes the current file.
\li \key Ctrl+W (\key Cmd+W on \macos)
\li
\row
- \li \inlineimage icons/live_preview.png
- \li \uicontrol {Show Live Preview} shows a preview of the current file
- or the entire UI. The changes you make to the UI are instantly
- visible to you in the preview.
- \li \key Alt+P (\key Opt+P on \macos)
- \li \l{Validating with Target Hardware}
- \row
- \li Preview size
- \li Displays the size of the preview dialog as a percentage. You can
- select another percentage in the list to view the UI in different
- sizes.
- \li
- \li \l{Previewing on Desktop}
- \row
- \li FPS
- \li Displays the frames-per-second (FPS) refresh rate of previewed
- animations.
- \li
- \li \l{Previewing on Desktop}
- \row
- \li Preview language
- \li Displays the language used for a localized application during
- preview. You can select another language in the list of languages
- that the application has been localized to.
- \li
- \li
- \row
- \li \inlineimage icons/qtcreator-reset-position-icon.png
- \li Returns a component to its \e {implicit position} after
- being moved.
- \li \key Ctrl+D (\key Cmd+D on \macos)
- \li \l{Resetting Component Position and Size}
- \row
- \li \inlineimage icons/qtcreator-reset-size-icon.png
- \li Returns a component to its implicit size after it was scaled.
- \li \key Shift+S
- \li \l{Resetting Component Position and Size}
- \row
- \li \inlineimage icons/anchor-fill.png
- \li Fills the selected component to its parent component.
- \li \key Shift+F
- \li \l{Setting Anchors and Margins}
- \row
- \li \inlineimage icons/qtcreator-anchors-reset-icon.png
- \li Resets anchors to their saved state for the selected component.
- \li \key Ctrl+Shift+R (\key Shift+Cmd+R on \macos)
- \li \l{Setting Anchors and Margins}
- \row
- \li \inlineimage icons/copy-formatting.png
- \li Copies property values from the selected component.
- \li
- \li \l{Copying and Pasting Formatting}
- \row
- \li \inlineimage icons/paste-formatting.png
- \li Applies copied property values to one or several selected
- components.
+ \li \inlineimage icons/create_component.png
+ \li Creates a custom component from the selected item.
\li
- \li \l{Copying and Pasting Formatting}
- \row
- \li \inlineimage row.png
- \li Uses a \uicontrol Row component to lay out the selected components.
- \li \key Ctrl+U (\key Cmd+U on \macos)
- \li \l{Using Layouts}
- \row
- \li \inlineimage column.png
- \li Uses a \uicontrol Column component to lay out the selected
- components.
- \li \key Ctrl+L (\key Cmd+L on \macos)
- \li \l{Using Layouts}
- \row
- \li \inlineimage grid.png
- \li Uses a \uicontrol Grid component to lay out the selected
- components.
- \li \key Shift+G
- \li \l{Using Layouts}
- \if defined(qtdesignstudio)
+ \li \l{Creating Custom Components}
\row
- \li \inlineimage icons/edit.png
- \li \uicontrol {Show Event List}: opens a dialog for viewing and
- creating an event list for an application flow.
- \li \key Alt+E (\key Opt+E on \macos)
- \li \l{Simulating Events}
- \row
- \li \inlineimage icons/assign.png
- \li \uicontrol {Assign Events to Actions}: assigns events to actions in
- an application flow.
- \li \key Alt+A (\key Opt+A on \macos)
- \li \l{Simulating Events}
- \endif
- \row
- \li Styling
- \li Displays the UI style used for UI controls.
+ \li \inlineimage icons/edit_component.png
+ \li Edits the selected custom component.
\li
- \li \l{Styling Controls}
- \row
- \li Subcomponents
- \li Displays the components referred to in the current file. Select a
- component in the list to open it in the \uicontrol {2D} and
- \uicontrol Navigator views.
- \li
- \li \l{Using Components}
+ \li \l{Creating Custom Components}
\row
\li Workspace
\li Displays the currently selected workspace. To switch to another
@@ -294,9 +211,9 @@
\li
\li \l{Managing Workspaces}
\row
- \li \inlineimage icons/annotation.png
- \li Enables you to add or edit global annotations.
+ \li \uicontrol Share
+ \li Shares the application online using Qt Design Viewer.
\li
- \li \l{Annotating Designs}
+ \li \l{Sharing Applications Online}
\endtable
*/
diff --git a/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc
index 64305f3e82..fc6d8c0223 100644
--- a/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc
@@ -36,24 +36,6 @@
\li Tooltip
\li Read More
\row
- \li \inlineimage icons/no_snapping.png
- \li Disables snapping.
- \li \l{Snapping to Parent and Sibling Components}
- \row
- \li \inlineimage icons/snapping_and_anchoring.png
- \li Anchors the component instance to the component instances that it
- is snapped to.
- \li \l{Snapping to Parent and Sibling Components}
- \row
- \li \inlineimage icons/snapping.png
- \li Snaps component instances to their parent or siblings when you
- align them.
- \li \l{Snapping to Parent and Sibling Components}
- \row
- \li \inlineimage icons/boundingrect.png
- \li Hides and shows component instance boundaries.
- \li \l{Hiding Component Boundaries}
- \row
\li \uicontrol {Override Width}
\li Shows a preview of the component using the specified width.
\li \l{Previewing Component Size}
@@ -149,13 +131,16 @@
\section1 Snapping to Parent and Sibling Components
You can use snapping to align component instances in
- the \uicontrol {2D} view. Select the \inlineimage icons/snapping.png
- button to have the component instances snap to their parent or siblings.
+ the \uicontrol {2D} view. With snapping turned on, all component instances
+ snap to their parent and siblings. If you use snapping with anchors, anchors are created
+ when you snap a component to another.
+
+ To turn on snapping, right-click in the \uicontrol 2D view and select
+ \uicontrol Snapping > \uicontrol {Snap with Anchors} or
+ \uicontrol {Snap without Anchors}.
+
Snapping lines automatically appear to help you position the component
- instances. Click the \inlineimage icons/snapping_and_anchoring.png
- button to anchor the selected component instance to those that you snap to.
- Only one snapping button can be selected at the time. Selecting
- one snapping button automatically deselects the others.
+ instances.
Choose \uicontrol Edit > \uicontrol Preferences > \uicontrol {Qt Quick} >
\uicontrol {Qt Quick Designer} to specify settings for snapping. In the
@@ -177,8 +162,8 @@
\section1 Hiding Component Boundaries
The \uicontrol {2D} view displays the boundaries of component instances.
- To hide them, select the \inlineimage icons/boundingrect.png
- button.
+ To hide them, right-click in the \uicontrol 2D view and select
+ \uicontrol {Show Bounds} from the context menu.
\section1 Previewing Component Size
diff --git a/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc b/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc
index 0c322c750d..f0cd1fe235 100644
--- a/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc
@@ -26,7 +26,7 @@
fit inside the parent component. For example, you might want to make a
mouse area larger than the rectangle or image beneath it.
- \image qmldesigner-element-size.png "Mouse area for a button"
+ \image qmldesigner-element-size.webp "Mouse area for a button"
When you copy a component, all its child components are also copied. When
you remove a component, the child components are also removed.
@@ -90,7 +90,7 @@
To change the visibility of a component in the application code, select the
\uicontrol Visibility check box in the \uicontrol Properties view or select
- \uicontrol Edit > \uicontrol Visibility in the context menu.
+ \uicontrol Visibility in the context menu.
You can also set the \uicontrol Opacity field to 0 in \uicontrol Properties
to hide components in the UI that you want to apply animation to.
@@ -224,7 +224,7 @@
\list
\li In the \uicontrol {2D} or \uicontrol Navigator view,
right-click an instance of a component and then select
- \uicontrol {Go into Component} in the context menu or
+ \uicontrol {Edit Component} in the context menu or
press \key F2.
\li In \uicontrol Properties, select \uicontrol {Edit Base Component}.
\endlist
diff --git a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc
index 8f8a1da66b..8ce14eba6b 100644
--- a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc
@@ -90,11 +90,11 @@
\section2 Resetting Component Position and Size
To return a component to its default position after moving it,
- select the \inlineimage icons/qtcreator-reset-position-icon.png
- (\uicontrol {Reset Position}) button on the \l{Design Views}
- {Design mode toolbar}. To return it to its default size, select
- \inlineimage icons/qtcreator-reset-size-icon.png
- (\uicontrol {Reset Size}) button.
+ right-click in the \uicontrol 2D or \uicontrol Navigator view and select
+ \uicontrol Edit > \uicontrol {Reset Position}.
+ To return it to its default size, right-click in the \uicontrol 2D or
+ \uicontrol Navigator view and select \uicontrol Edit >
+ \uicontrol {Reset Size}.
\section2 Managing 2D Transformations
@@ -448,15 +448,13 @@
several other components. The values are applied if the target components
have those particular properties.
- To copy property values from the selected component, select
- \inlineimage icons/copy-formatting.png
- on the \uicontrol Design mode \l{Summary of Main Toolbar Actions}
- {main toolbar}.
+ To copy property values from a component, right-click it in the
+ \uicontrol 2D or \uicontrol Navigator view and select \uicontrol Edit >
+ \uicontrol {Copy Formatting}.
To apply the values to one or several other components, select
- them in the \l Navigator or \l {2D} view, and then select
- \inlineimage icons/paste-formatting.png
- .
+ them in the \l Navigator or \l {2D} view, and then right-click and select
+ \uicontrol Edit > \uicontrol {Apply Formatting}.
\section1 Editing Properties Inline
diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc
index 455c680cd1..1fcf4ef908 100644
--- a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc
@@ -26,7 +26,7 @@
It now displays a \l{Timeline Toolbar}{toolbar} and a ruler but no
keyframe tracks.
- \image studio-timeline-no-tracks.png "Timeline view without keyframe tracks"
+ \image studio-timeline-no-tracks.webp "Timeline view without keyframe tracks"
To animate component properties in the \uicontrol Timeline view, you
must \l{Setting Keyframe Values}{insert keyframes} for them. In the
@@ -35,7 +35,7 @@
want to animate. A keyframe track is generated for each component that you
insert keyframes for.
- \image studio-timeline-with-empty-tracks.png "Timeline view with a property"
+ \image studio-timeline-with-empty-tracks.webp "Timeline view with a property"
You can now select \inlineimage icons/local_record_keyframes.png
to \l{Setting Keyframe Values}{record changes} in component properties
@@ -56,7 +56,7 @@
\section1 Navigating in Timeline
- \image studio-timeline.png "Timeline view"
+ \image studio-timeline.webp "Timeline view"
You can navigate the timeline in the following ways:
@@ -87,7 +87,7 @@
in the context menu, and then select a color in the \l{Picking Colors}
{color picker}. To reset the color, select \uicontrol {Reset Color}.
- \image studio-timeline-keyframe-track-colors.png "Keyframe track colors in Timeline"
+ \image studio-timeline-keyframe-track-colors.webp "Keyframe track colors in Timeline"
\section1 Timeline Toolbar
@@ -99,7 +99,7 @@
\li Action
\li Read More
\row
- \li \inlineimage icons/animation.png
+ \li \inlineimage icons/settings.png
\li Opens the \uicontrol {Timeline Settings} dialog for editing
timeline settings.
\li \l{Creating a Timeline}
@@ -150,7 +150,7 @@
determines the duration of the animation.
\li \l{Creating a Timeline}
\row
- \li \inlineimage icons/zoom_small.png
+ \li \inlineimage icons/zoomOut.png
\li \uicontrol {Zoom Out} (\key Ctrl+-) zooms out of the view.
\li \l{Zooming in Timeline}
\row
@@ -158,7 +158,7 @@
\li Sets the zooming level.
\li \l{Zooming in Timeline}
\row
- \li \inlineimage icons/zoom_big.png
+ \li \inlineimage icons/zoomIn.png
\li \uicontrol {Zoom In} (\key Ctrl++) zooms into the view.
\li \l{Zooming in Timeline}
\row
diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc
index 0891df05a1..12b1fc1463 100644
--- a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc
@@ -93,7 +93,7 @@
state in \uicontrol {States} and the timeline is available in
\uicontrol{Timelines}.
- \image timeline-states.png
+ \image timeline-states.webp
\section2 Setting Keyframe Values
@@ -118,7 +118,7 @@
\li In the \l Timeline view, select the
\uicontrol {Per Property Recording} button
to start recording property changes.
- \image timeline-per-property-recording.png
+ \image timeline-per-property-recording.webp
\li Ensure that the playhead is in frame 0 and enter the value of the
property in the field next to the property name on the timeline.
Press \key Enter to save the value.
@@ -180,7 +180,7 @@
\section1 Managing Keyframes
- \image studio-timeline-with-tracks.png "Timeline view"
+ \image studio-timeline-with-tracks.webp "Timeline view"
\section2 Editing Keyframes
@@ -240,9 +240,8 @@
\endlist
To preview the whole UI, select the
- \inlineimage icons/live_preview.png
- (\uicontrol {Show Live Preview}) button on the canvas toolbar
- or press \key {Alt+P}.
+ \uicontrol {Live Preview} button on the top toolbar
+ or press \key Alt + \key P.
\section1 Animating Rotation
diff --git a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc
index ba8375e365..3ee43335d6 100644
--- a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc
+++ b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc
@@ -39,7 +39,7 @@
\li Action
\li Read More
\row
- \li \inlineimage icons/animation.png
+ \li \inlineimage icons/settings.png
\li Opens \uicontrol {Transition Settings} dialog for editing
transition settings.
\li \l{Specifying Transition Settings}
@@ -54,7 +54,7 @@
curve to the selected transition.
\li \l{Editing Easing Curves}
\row
- \li \inlineimage icons/zoom_small.png
+ \li \inlineimage icons/zoomOut.png
\li \uicontrol {Zoom Out} (\key Ctrl+-): zooms out of the view.
\li \l{Zooming in Transitions}
\row
@@ -62,7 +62,7 @@
\li Sets the zooming level.
\li \l{Zooming in Transitions}
\row
- \li \inlineimage icons/zoom_big.png
+ \li \inlineimage icons/zoomIn.png
\li \uicontrol {Zoom In} (\key Ctrl++): zooms into the view.
\li \l{Zooming in Transitions}
\row
@@ -94,7 +94,7 @@
\section1 Specifying Transition Settings
- To modify transition settings, select the \inlineimage icons/animation.png
+ To modify transition settings, select the \inlineimage icons/settings.png
(\uicontrol {Transition Settings (S)}) button in
\uicontrol {Transition Editor}.
diff --git a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc
index 94209c7bcb..bb2923a7ab 100644
--- a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc
+++ b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc
@@ -55,7 +55,7 @@
to the object, you can select whether to replace the material or to add
another material to the object.
\li In \uicontrol {Material Editor}, select
- \inlineimage icons/apply-material.png
+ \inlineimage icons/apply.png
. This replaces any material already assigned to the object.
\endlist
diff --git a/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc b/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc
index 95a99401a9..4d644706e5 100644
--- a/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc
+++ b/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc
@@ -35,7 +35,7 @@
To apply a texture to a material, first select the material in the
\uicontrol {Material Browser} view and then:
\list 1
- \li Select \inlineimage icons/apply-material.png
+ \li Select \inlineimage icons/apply.png
.
\li Select the material and property that you want to add the texture to.
\image select-material-property.png
diff --git a/doc/qtdesignstudio/src/views/studio-translations.qdoc b/doc/qtdesignstudio/src/views/studio-translations.qdoc
index 6972dc1ffb..a2a610b313 100644
--- a/doc/qtdesignstudio/src/views/studio-translations.qdoc
+++ b/doc/qtdesignstudio/src/views/studio-translations.qdoc
@@ -115,7 +115,7 @@
You need to generate Qt compiled translation source files (\e{.qm})
and Qt translation source files (\e{.ts}) for your project to have the
- translations working in the actual application and \uicontrol{Live Preview}.
+ translations working in the actual application and live preview.
To generate these files, select
\inlineimage icons/generate-translation-files.png
diff --git a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc
index fdc359d96d..8d29b1f74f 100644
--- a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc
+++ b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc
@@ -30,7 +30,7 @@
where you want to attach the view is highlighted, and then drop them into
the dock area.
- \image qtcreator-workspace-attaching-views.png "Attaching views"
+ \image qtcreator-workspace-attaching-views.webp "Attaching views"
To close groups of views, select the \uicontrol {Close Group} button.
diff --git a/qbs/imports/QtcTestFiles.qbs b/qbs/imports/QtcTestFiles.qbs
new file mode 100644
index 0000000000..ab27a8df8a
--- /dev/null
+++ b/qbs/imports/QtcTestFiles.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Group {
+ name: "Unit tests"
+ condition: qtc.testsEnabled
+}
diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs
index 08d6eeffd7..226d432b18 100644
--- a/qbs/modules/qtc/qtc.qbs
+++ b/qbs/modules/qtc/qtc.qbs
@@ -6,16 +6,16 @@ import qbs.Utilities
Module {
Depends { name: "cpp"; required: false }
- property string qtcreator_display_version: '10.0.2'
+ property string qtcreator_display_version: '11.0.0-beta1'
property string ide_version_major: '10'
property string ide_version_minor: '0'
- property string ide_version_release: '2'
+ property string ide_version_release: '82'
property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.'
+ ide_version_release
property string ide_compat_version_major: '10'
property string ide_compat_version_minor: '0'
- property string ide_compat_version_release: '0'
+ property string ide_compat_version_release: '82'
property string qtcreator_compat_version: ide_compat_version_major + '.'
+ ide_compat_version_minor + '.' + ide_compat_version_release
diff --git a/scripts/makedmg.py b/scripts/makedmg.py
index e90846176f..9d7fd7f5f5 100755
--- a/scripts/makedmg.py
+++ b/scripts/makedmg.py
@@ -18,6 +18,11 @@ def parse_arguments():
parser.add_argument('dmg_volumename', help='volume name to use for the disk image')
parser.add_argument('source_directory', help='directory with the Qt Creator sources')
parser.add_argument('binary_directory', help='directory that contains the Qt Creator.app')
+ parser.add_argument('--dmg-size', default='1500m', required=False)
+ parser.add_argument('--license-replacement', default=None,
+ help='Absolute path to a license file which replaces the default LICENSE.GPL3-EXCEPT from Qt Creator source directory.')
+ parser.add_argument('--keep-signed-content-at', default=None,
+ help='For online installer we want to keep the signed .app without the dmg. This is used to create a 7z.')
return parser.parse_args()
def main():
@@ -30,13 +35,19 @@ def main():
app_path = [app for app in os.listdir(tempdir) if app.endswith('.app')][0]
common.codesign(os.path.join(tempdir, app_path))
os.symlink('/Applications', os.path.join(tempdir, 'Applications'))
- shutil.copy(os.path.join(arguments.source_directory, 'LICENSE.GPL3-EXCEPT'), tempdir)
+ license_file = os.path.join(arguments.source_directory, 'LICENSE.GPL3-EXCEPT')
+ if (arguments.license_replacement):
+ license_file = arguments.license_replacement
+ shutil.copy(license_file, tempdir)
dmg_cmd = ['hdiutil', 'create', '-srcfolder', tempdir, '-volname', arguments.dmg_volumename,
- '-format', 'UDBZ', arguments.target_diskimage, '-ov', '-scrub', '-size', '1500m', '-verbose']
+ '-format', 'UDBZ', arguments.target_diskimage, '-ov', '-scrub', '-size', arguments.dmg_size, '-verbose']
subprocess.check_call(dmg_cmd)
# sleep a few seconds to make sure disk image is fully unmounted etc
time.sleep(5)
finally:
- shutil.rmtree(tempdir_base)
+ if arguments.keep_signed_content_at:
+ shutil.move(tempdir, arguments.keep_signed_content_at)
+ else:
+ shutil.rmtree(tempdir_base)
if __name__ == "__main__":
main()
diff --git a/share/qtcreator/CMakeLists.txt b/share/qtcreator/CMakeLists.txt
index 0a868915a3..f8be01a7a7 100644
--- a/share/qtcreator/CMakeLists.txt
+++ b/share/qtcreator/CMakeLists.txt
@@ -35,12 +35,9 @@ set(resource_files
debugger/libcpp_stdtypes.py
debugger/stdtypes.py
debugger/utils.py
+ debugger/loadorder.txt
)
-if (APPLE)
- set(resource_directories ${resource_directories} scripts)
-endif()
-
# copy resource directories during build
qtc_copy_to_builddir(copy_share_to_builddir
DIRECTORIES ${resource_directories}
diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py
index af79054460..a11377b0db 100644
--- a/share/qtcreator/debugger/creatortypes.py
+++ b/share/qtcreator/debugger/creatortypes.py
@@ -235,7 +235,7 @@ def qdump__Utils__Port(d, value):
-def qdump__Utils__Environment(d, value):
+def x_qdump__Utils__Environment(d, value):
qdump__Utils__NameValueDictionary(d, value)
@@ -243,7 +243,7 @@ def qdump__Utils__DictKey(d, value):
d.putStringValue(value["name"])
-def qdump__Utils__NameValueDictionary(d, value):
+def x_qdump__Utils__NameValueDictionary(d, value):
dptr = d.extractPointer(value)
if d.qtVersion() >= 0x60000:
if dptr == 0:
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py
index 8400894303..50dabd217f 100644
--- a/share/qtcreator/debugger/dumper.py
+++ b/share/qtcreator/debugger/dumper.py
@@ -106,7 +106,7 @@ class Children():
self.d.putNumChild(0)
if self.d.currentMaxNumChild is not None:
if self.d.currentMaxNumChild < self.d.currentNumChild:
- self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
+ self.d.put('{name="<load more>",value="",type="",numchild="1"},')
self.d.currentChildType = self.savedChildType
self.d.currentChildNumChild = self.savedChildNumChild
self.d.currentNumChild = self.savedNumChild
@@ -195,10 +195,16 @@ class DumperBase():
self.childrenPrefix = 'children=['
self.childrenSuffix = '],'
- self.dumpermodules = [
- os.path.splitext(os.path.basename(p))[0] for p in
- glob.glob(os.path.join(os.path.dirname(__file__), '*types.py'))
- ]
+ self.dumpermodules = []
+
+ try:
+ # Fails in the piping case
+ self.dumpermodules = [
+ os.path.splitext(os.path.basename(p))[0] for p in
+ glob.glob(os.path.join(os.path.dirname(__file__), '*types.py'))
+ ]
+ except:
+ pass
# These values are never used, but the variables need to have
# some value base for the swapping logic in Children.__enter__()
@@ -215,7 +221,7 @@ class DumperBase():
def setVariableFetchingOptions(self, args):
self.resultVarName = args.get('resultvarname', '')
- self.expandedINames = set(args.get('expanded', []))
+ self.expandedINames = args.get('expanded', {})
self.stringCutOff = int(args.get('stringcutoff', 10000))
self.displayStringLimit = int(args.get('displaystringlimit', 100))
self.typeformats = args.get('typeformats', {})
@@ -298,6 +304,11 @@ class DumperBase():
return range(0, self.currentNumChild)
return range(min(self.currentMaxNumChild, self.currentNumChild))
+ def maxArrayCount(self):
+ if self.currentIName in self.expandedINames:
+ return self.expandedINames[self.currentIName]
+ return 100
+
def enterSubItem(self, item):
if self.useTimeStamps:
item.startTime = time.time()
@@ -872,7 +883,7 @@ class DumperBase():
self.output.append(stuff)
def takeOutput(self):
- res = '\n'.join(self.output)
+ res = ''.join(self.output)
self.output = []
return res
@@ -2233,7 +2244,7 @@ class DumperBase():
res = self.currentValue
return res # The 'short' display.
- def putArrayData(self, base, n, innerType, childNumChild=None, maxNumChild=10000):
+ def putArrayData(self, base, n, innerType, childNumChild=None):
self.checkIntType(base)
self.checkIntType(n)
addrBase = base
@@ -2241,6 +2252,7 @@ class DumperBase():
self.putNumChild(n)
#DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType))
enc = innerType.simpleEncoding()
+ maxNumChild = self.maxArrayCount()
if enc:
self.put('childtype="%s",' % innerType.name)
self.put('addrbase="0x%x",' % addrBase)
@@ -2248,7 +2260,7 @@ class DumperBase():
self.put('arrayencoding="%s",' % enc)
self.put('endian="%s",' % self.packCode)
if n > maxNumChild:
- self.put('childrenelided="%s",' % n) # FIXME: Act on that in frontend
+ self.put('childrenelided="%s",' % n)
n = maxNumChild
self.put('arraydata="')
self.put(self.readMemory(addrBase, n * innerSize))
@@ -2282,7 +2294,7 @@ class DumperBase():
def putPlotData(self, base, n, innerType, maxNumChild=1000 * 1000):
self.putPlotDataHelper(base, n, innerType, maxNumChild=maxNumChild)
if self.isExpanded():
- self.putArrayData(base, n, innerType, maxNumChild=maxNumChild)
+ self.putArrayData(base, n, innerType)
def putSpecialArgv(self, value):
"""
diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py
index c14bf30286..bef97482af 100644
--- a/share/qtcreator/debugger/lldbbridge.py
+++ b/share/qtcreator/debugger/lldbbridge.py
@@ -870,6 +870,7 @@ class Dumper(DumperBase):
self.startMode_ = args.get('startmode', 1)
self.breakOnMain_ = args.get('breakonmain', 0)
self.useTerminal_ = args.get('useterminal', 0)
+ self.firstStop_ = True
pargs = self.hexdecode(args.get('processargs', ''))
self.processArgs_ = pargs.split('\0') if len(pargs) else []
self.environment_ = args.get('environment', [])
@@ -930,6 +931,8 @@ class Dumper(DumperBase):
if self.startMode_ == DebuggerStartMode.AttachExternal:
attach_info = lldb.SBAttachInfo(self.attachPid_)
+ if self.breakOnMain_:
+ self.createBreakpointAtMain()
self.process = self.target.Attach(attach_info, error)
if not error.Success():
self.reportState('enginerunfailed')
@@ -1474,6 +1477,12 @@ class Dumper(DumperBase):
self.reportState("inferiorstopok")
else:
self.reportState("stopped")
+ if self.firstStop_:
+ self.firstStop_ = False
+ if self.useTerminal_:
+ # When using a terminal, the process will be interrupted on startup.
+ # We therefore need to continue it here.
+ self.process.Continue()
else:
self.reportState(self.stateName(state))
@@ -2120,7 +2129,7 @@ class SummaryDumper(Dumper, LogMixin):
# Expand variable if we need synthetic children
oldExpanded = self.expandedINames
- self.expandedINames = [value.name] if expanded else []
+ self.expandedINames = {value.name: 100} if expanded else {}
savedOutput = self.output
self.output = []
diff --git a/share/qtcreator/debugger/loadorder.txt b/share/qtcreator/debugger/loadorder.txt
new file mode 100644
index 0000000000..23d4dbf3a4
--- /dev/null
+++ b/share/qtcreator/debugger/loadorder.txt
@@ -0,0 +1,13 @@
+utils
+gdbtracepoint
+dumper
+***bridge***
+boosttypes
+creatortypes
+libcpp_stdtypes
+misctypes
+opencvtypes
+personaltypes
+qttypes
+stdtypes
+android_stdtypes
diff --git a/share/qtcreator/debugger/pdbbridge.py b/share/qtcreator/debugger/pdbbridge.py
index e91c56e716..228f4c8c1f 100644
--- a/share/qtcreator/debugger/pdbbridge.py
+++ b/share/qtcreator/debugger/pdbbridge.py
@@ -1445,7 +1445,7 @@ class QtcInternalDumper():
self.updateData(args)
def updateData(self, args):
- self.expandedINames = __builtins__.set(args.get('expanded', []))
+ self.expandedINames = args.get('expanded', {})
self.typeformats = args.get('typeformats', {})
self.formats = args.get('formats', {})
self.output = ''
diff --git a/share/qtcreator/debugger/utils.py b/share/qtcreator/debugger/utils.py
index fe2e558e71..8019d1e530 100644
--- a/share/qtcreator/debugger/utils.py
+++ b/share/qtcreator/debugger/utils.py
@@ -4,6 +4,7 @@
# Debugger start modes. Keep in sync with DebuggerStartMode in debuggerconstants.h
+# MT: Why does this not match (anymore?) to debuggerconstants.h : DebuggerStartMode ?
class DebuggerStartMode():
(
NoStartMode,
diff --git a/share/qtcreator/qml-type-descriptions/qt5QtQuick2-bundle.json b/share/qtcreator/qml-type-descriptions/qt5QtQuick2-bundle.json
index 51bdff39b6..42222fd2dd 100644
--- a/share/qtcreator/qml-type-descriptions/qt5QtQuick2-bundle.json
+++ b/share/qtcreator/qml-type-descriptions/qt5QtQuick2-bundle.json
@@ -199,6 +199,29 @@
"QtQuick.Controls.Styles 1.2",
"QtQuick.Controls.Styles 1.3",
"QtQuick.Controls.Styles 1.4",
+ "QtQuick.Controls.Basic 2.0",
+ "QtQuick.Controls.Basic 2.1",
+ "QtQuick.Controls.Basic 2.2",
+ "QtQuick.Controls.Basic 2.3",
+ "QtQuick.Controls.Basic 2.12",
+ "QtQuick.Controls.Basic 2.13",
+ "QtQuick.Controls.Basic 2.14",
+ "QtQuick.Controls.Basic 2.15",
+ "QtQuick.Controls.Fusion 2.0",
+ "QtQuick.Controls.Fusion 2.1",
+ "QtQuick.Controls.Fusion 2.2",
+ "QtQuick.Controls.Fusion 2.3",
+ "QtQuick.Controls.Fusion 2.12",
+ "QtQuick.Controls.Fusion 2.13",
+ "QtQuick.Controls.Fusion 2.14",
+ "QtQuick.Controls.Imagine 2.0",
+ "QtQuick.Controls.Imagine 2.1",
+ "QtQuick.Controls.Imagine 2.2",
+ "QtQuick.Controls.Imagine 2.3",
+ "QtQuick.Controls.Imagine 2.12",
+ "QtQuick.Controls.Imagine 2.13",
+ "QtQuick.Controls.Imagine 2.14",
+ "QtQuick.Controls.Imagine 2.15",
"QtQuick.Dialogs 1.0",
"QtQuick.Dialogs 1.1",
"QtQuick.Dialogs 1.2",
diff --git a/share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-macos-bundle.json b/share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-macos-bundle.json
new file mode 100644
index 0000000000..5d34b4f2bf
--- /dev/null
+++ b/share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-macos-bundle.json
@@ -0,0 +1,10 @@
+{
+ "name": "QtQuickControlsMacOS",
+ "searchPaths": [],
+ "installPaths": [],
+ "implicitImports": [],
+ "supportedImports": [
+ "QtQuick.Controls.macOS",
+ "QtQuick.Controls.iOS"
+ ]
+}
diff --git a/share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-win-bundle.json b/share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-win-bundle.json
new file mode 100644
index 0000000000..2d908c5841
--- /dev/null
+++ b/share/qtcreator/qml-type-descriptions/qt5QtQuick2ext-win-bundle.json
@@ -0,0 +1,9 @@
+{
+ "name": "QtQuickControlsWin",
+ "searchPaths": [],
+ "installPaths": [],
+ "implicitImports": [],
+ "supportedImports": [
+ "QtQuick.Controls.Windows"
+ ]
+}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml
index 1d5d2434c6..7c017476cd 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetDelegate.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml
@@ -1,9 +1,10 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 StudioTheme as StudioTheme
+import AssetsLibraryBackend
TreeViewDelegate {
id: root
@@ -11,6 +12,9 @@ TreeViewDelegate {
required property Item assetsView
required property Item assetsRoot
+ property var assetsModel: AssetsLibraryBackend.assetsModel
+ property var rootView: AssetsLibraryBackend.rootView
+
property bool hasChildWithDropHover: false
property bool isHighlighted: false
readonly property string suffix: model.fileName.substr(-4)
@@ -22,7 +26,7 @@ TreeViewDelegate {
property int __currentRow: model.index
property string __itemPath: model.filePath
- readonly property int __fileItemHeight: thumbnailImage.height
+ readonly property int __fileItemHeight: thumbnailImage.height + 2 * StudioTheme.Values.border
readonly property int __dirItemHeight: 21
implicitHeight: root.__isDirectory ? root.__dirItemHeight : root.__fileItemHeight
@@ -67,14 +71,15 @@ TreeViewDelegate {
background: Rectangle {
id: bg
- width: root.implicitWidth
+ x: root.indentation * root.depth
+ width: root.implicitWidth - bg.x
color: {
if (root.__isDirectory && (root.isHighlighted || root.hasChildWithDropHover))
return StudioTheme.Values.themeInteraction
if (!root.__isDirectory && root.assetsView.selectedAssets[root.__itemPath])
- return StudioTheme.Values.themeInteraction
+ return StudioTheme.Values.themeSectionHeadBackground
if (mouseArea.containsMouse)
return StudioTheme.Values.themeSectionHeadBackground
@@ -83,18 +88,20 @@ TreeViewDelegate {
? StudioTheme.Values.themeSectionHeadBackground
: "transparent"
}
+ border.width: StudioTheme.Values.border
+ border.color: {
+ if (root.__isDirectory && (root.isHighlighted || root.hasChildWithDropHover))
+ return StudioTheme.Values.themeInteraction
+
+ if (!root.__isDirectory && root.assetsView.selectedAssets[root.__itemPath])
+ return StudioTheme.Values.themeInteraction
- // this rectangle exists so as to have some visual indentation for the directories
- // We prepend a default pane-colored rectangle so that the nested directory will
- // look moved a bit to the right
- Rectangle {
- anchors.top: bg.top
- anchors.bottom: bg.bottom
- anchors.left: bg.left
-
- width: root.indentation * root.depth
- implicitWidth: root.indentation * root.depth
- color: StudioTheme.Values.themePanelBackground
+ if (mouseArea.containsMouse)
+ return StudioTheme.Values.themeSectionHeadBackground
+
+ return root.__isDirectory
+ ? StudioTheme.Values.themeSectionHeadBackground
+ : "transparent"
}
}
@@ -102,12 +109,11 @@ TreeViewDelegate {
id: assetLabel
text: assetLabel.__computeText()
color: StudioTheme.Values.themeTextColor
- font.pixelSize: 14
+ font.pixelSize: StudioTheme.Values.mediumFont
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Qt.AlignVCenter
- function __computeText()
- {
+ function __computeText() {
return root.__isDirectory
? (root.hasChildren
? model.display.toUpperCase()
@@ -125,20 +131,20 @@ TreeViewDelegate {
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
- onExited: tooltipBackend.hideTooltip()
+ onExited: AssetsLibraryBackend.tooltipBackend.hideTooltip()
onEntered: mouseArea.allowTooltip = true
onCanceled: {
- tooltipBackend.hideTooltip()
+ AssetsLibraryBackend.tooltipBackend.hideTooltip()
mouseArea.allowTooltip = true
}
onPositionChanged: tooltipBackend.reposition()
onPressed: (mouse) => {
- forceActiveFocus()
+ mouseArea.forceActiveFocus()
mouseArea.allowTooltip = false
- tooltipBackend.hideTooltip()
+ AssetsLibraryBackend.tooltipBackend.hideTooltip()
if (root.__isDirectory)
return
@@ -152,7 +158,7 @@ TreeViewDelegate {
if (root.currFileSelected) {
let selectedPaths = root.assetsView.selectedPathsAsList()
- rootView.startDragAsset(selectedPaths, mapToGlobal(mouse.x, mouse.y))
+ AssetsLibraryBackend.rootView.startDragAsset(selectedPaths, mapToGlobal(mouse.x, mouse.y))
}
} else {
if (!root.assetsView.isAssetSelected(root.__itemPath) && !ctrlDown)
@@ -165,20 +171,25 @@ TreeViewDelegate {
onReleased: (mouse) => {
mouseArea.allowTooltip = true
+ if (root.__isDirectory)
+ return
+
if (mouse.button === Qt.LeftButton) {
if (!(mouse.modifiers & Qt.ControlModifier))
root.assetsView.selectedAssets = {}
root.assetsView.selectedAssets[root.__itemPath] = root.currFileSelected
root.assetsView.selectedAssetsChanged()
+
+ root.assetsView.currentFilePath = root.__itemPath
}
}
onDoubleClicked: (mouse) => {
- forceActiveFocus()
- allowTooltip = false
- tooltipBackend.hideTooltip()
- if (mouse.button === Qt.LeftButton && isEffect)
- rootView.openEffectMaker(filePath)
+ mouseArea.forceActiveFocus()
+ mouseArea.allowTooltip = false
+ AssetsLibraryBackend.tooltipBackend.hideTooltip()
+ if (mouse.button === Qt.LeftButton && root.isEffect)
+ AssetsLibraryBackend.rootView.openEffectMaker(filePath)
}
ToolTip {
@@ -187,14 +198,16 @@ TreeViewDelegate {
text: assetTooltip.__computeText()
delay: 1000
- function __computeText()
- {
+ function __computeText() {
let filePath = model.filePath.replace(assetsModel.contentDirPath(), "")
let fileSize = rootView.assetFileSize(model.filePath)
let fileExtMatches = model.filePath.match(/\.(.*)$/)
let fileExt = fileExtMatches ? "(" + fileExtMatches[1] + ")" : ""
- if (rootView.assetIsImage(model.filePath)) {
+ if (root.__isDirectory)
+ return filePath
+
+ if (rootView.assetIsImageOrTexture(model.filePath)) {
let size = rootView.imageSize(model.filePath)
return filePath + "\n"
@@ -208,9 +221,8 @@ TreeViewDelegate {
}
}
- function refresh()
- {
- text = assetTooltip.__computeText()
+ function refresh() {
+ assetTooltip.text = assetTooltip.__computeText()
}
}
@@ -218,53 +230,42 @@ TreeViewDelegate {
interval: 1000
running: mouseArea.containsMouse && mouseArea.allowTooltip
onTriggered: {
- if (suffix === ".ttf" || suffix === ".otf") {
- tooltipBackend.name = model.fileName
- tooltipBackend.path = model.filePath
- tooltipBackend.showTooltip()
+ if (root.isFont) {
+ AssetsLibraryBackend.tooltipBackend.name = model.fileName
+ AssetsLibraryBackend.tooltipBackend.path = model.filePath
+ AssetsLibraryBackend.tooltipBackend.showTooltip()
}
}
- } // Timer
+ }
onClicked: (mouse) => {
if (mouse.button === Qt.LeftButton)
root.__toggleExpandCurrentRow()
else
root.__openContextMenuForCurrentRow()
-
-
}
- } // MouseArea
+ }
- function getDirPath()
- {
+ function getDirPath() {
if (root.__isDirectory)
return model.filePath
else
return assetsModel.parentDirPath(model.filePath)
}
- function __openContextMenuForCurrentRow()
- {
+ function __openContextMenuForCurrentRow() {
let modelIndex = assetsModel.indexForPath(model.filePath)
function onFolderCreated(path) {
- root.assetsView.addCreatedFolder(path)
+ if (path)
+ root.assetsView.addCreatedFolder(path)
}
if (root.__isDirectory) {
- var row = root.assetsView.rowAtIndex(modelIndex)
- var expanded = root.assetsView.isExpanded(row)
-
var allExpandedState = root.assetsView.computeAllExpandedState()
- function onFolderRenamed() {
- if (expanded)
- root.assetsView.rowToExpand = row
- }
-
root.assetsView.contextMenu.openContextMenuForDir(modelIndex, model.filePath,
- model.fileName, allExpandedState, onFolderCreated, onFolderRenamed)
+ model.fileName, allExpandedState, onFolderCreated)
} else {
let parentDirIndex = assetsModel.parentDirIndex(model.filePath)
let selectedPaths = root.assetsView.selectedPathsAsList()
@@ -273,8 +274,7 @@ TreeViewDelegate {
}
}
- function __toggleExpandCurrentRow()
- {
+ function __toggleExpandCurrentRow() {
if (!root.__isDirectory)
return
@@ -291,8 +291,7 @@ TreeViewDelegate {
}
}
- function reloadImage()
- {
+ function reloadImage() {
if (root.__isDirectory)
return
@@ -303,7 +302,8 @@ TreeViewDelegate {
Image {
id: thumbnailImage
visible: !root.__isDirectory
- x: root.depth * root.indentation
+ y: StudioTheme.Values.border
+ x: root.depth * root.indentation + StudioTheme.Values.border
width: 48
height: 48
cache: false
@@ -313,8 +313,7 @@ TreeViewDelegate {
fillMode: Image.PreserveAspectFit
source: thumbnailImage.__computeSource()
- function __computeSource()
- {
+ function __computeSource() {
return root.__isDirectory
? ""
: "image://qmldesigner_assets/" + model.filePath
@@ -324,6 +323,5 @@ TreeViewDelegate {
if (thumbnailImage.status === Image.Ready)
assetTooltip.refresh()
}
-
- } // Image
-} // TreeViewDelegate
+ }
+}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml
index 23210ae4b5..211d3449a8 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml
@@ -1,21 +1,25 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import AssetsLibraryBackend
Item {
id: root
+ property var assetsModel: AssetsLibraryBackend.assetsModel
+ property var rootView: AssetsLibraryBackend.rootView
+
// Array of supported externally dropped files that are imported as-is
property var dropSimpleExtFiles: []
// Array of supported externally dropped files that trigger custom import process
property var dropComplexExtFiles: []
- readonly property int qtVersionAtLeast6_4: rootView.qtVersionIsAtLeast6_4()
+ readonly property int qtVersion: rootView.qtVersion()
property bool __searchBoxEmpty: true
AssetsContextMenu {
@@ -23,19 +27,20 @@ Item {
assetsView: assetsView
}
- function clearSearchFilter()
- {
- searchBox.clear();
+ function clearSearchFilter() {
+ searchBox.clear()
}
- function updateDropExtFiles(drag)
- {
+ function updateDropExtFiles(drag) {
root.dropSimpleExtFiles = []
root.dropComplexExtFiles = []
- var simpleSuffixes = rootView.supportedAssetSuffixes(false);
- var complexSuffixes = rootView.supportedAssetSuffixes(true);
+ var simpleSuffixes = rootView.supportedAssetSuffixes(false)
+ var complexSuffixes = rootView.supportedAssetSuffixes(true)
for (const u of drag.urls) {
- var url = u.toString();
+ var url = u.toString()
+ if (assetsModel.urlPathExistsInModel(url))
+ continue;
+
var ext = '*.' + url.slice(url.lastIndexOf('.') + 1).toLowerCase()
if (simpleSuffixes.includes(ext))
root.dropSimpleExtFiles.push(url)
@@ -48,22 +53,26 @@ Item {
DropArea { // handles external drop on empty area of the view (goes to root folder)
id: dropArea
- y: assetsView.y + assetsView.contentHeight + 5
+ y: assetsView.y + assetsView.contentHeight - assetsView.rowSpacing
width: parent.width
height: parent.height - y
- onEntered: (drag)=> {
+ onEntered: (drag) => {
root.updateDropExtFiles(drag)
}
- onDropped: {
- rootView.handleExtFilesDrop(root.dropSimpleExtFiles, root.dropComplexExtFiles,
+ onDropped: (drag) => {
+ drag.accept()
+ rootView.handleExtFilesDrop(root.dropSimpleExtFiles,
+ root.dropComplexExtFiles,
assetsModel.rootPath())
}
Canvas { // marker for the drop area
id: dropCanvas
- anchors.fill: parent
+ y: 5
+ width: parent.width
+ height: parent.height - y
visible: dropArea.containsDrag && root.dropSimpleExtFiles.length > 0
onWidthChanged: dropCanvas.requestPaint()
@@ -99,99 +108,120 @@ Item {
}
// called from C++ to close context menu on focus out
- function handleViewFocusOut()
- {
+ function handleViewFocusOut() {
contextMenu.close()
assetsView.selectedAssets = {}
assetsView.selectedAssetsChanged()
}
+ Timer {
+ id: updateSearchFilterTimer
+ interval: 200
+ repeat: false
+
+ onTriggered: {
+ assetsView.resetVerticalScrollPosition()
+ rootView.handleSearchFilterChanged(searchBox.text)
+ assetsView.expandAll()
+
+ if (root.__searchBoxEmpty && searchBox.text)
+ root.__searchBoxEmpty = false
+ else if (!root.__searchBoxEmpty && !searchBox.text)
+ root.__searchBoxEmpty = true
+ }
+ }
+
Column {
+ id: column
anchors.fill: parent
- anchors.topMargin: 5
spacing: 5
- Row {
- id: searchRow
-
+ Rectangle {
+ id: toolbar
width: parent.width
+ height: StudioTheme.Values.doubleToolbarHeight
+ color: StudioTheme.Values.themeToolbarBackground
- StudioControls.SearchBox {
- id: searchBox
-
- width: parent.width - addAssetButton.width - 5
-
- onSearchChanged: (searchText) => {
- updateSearchFilterTimer.restart()
+ Column {
+ id: toolbarColumn
+ anchors.fill: parent
+ anchors.topMargin: 6
+ anchors.bottomMargin: 6
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ spacing: 12
+
+ StudioControls.SearchBox {
+ id: searchBox
+ width: parent.width
+ style: StudioTheme.Values.searchControlStyle
+ onSearchChanged: (searchText) => {
+ updateSearchFilterTimer.restart()
+ }
}
- }
- Timer {
- id: updateSearchFilterTimer
- interval: 200
- repeat: false
-
- onTriggered: {
- assetsView.resetVerticalScrollPosition()
- rootView.handleSearchFilterChanged(searchBox.text)
- assetsView.expandAll()
-
- if (root.__searchBoxEmpty && searchBox.text)
- root.__searchBoxEmpty = false
- else if (!root.__searchBoxEmpty && !searchBox.text)
- root.__searchBoxEmpty = true
+ Row {
+ id: buttonRow
+ width: parent.width
+ height: StudioTheme.Values.toolbarHeight
+ spacing: 6
+
+ HelperWidgets.AbstractButton {
+ id: addModuleButton
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.add_medium
+ tooltip: qsTr("Add a new asset to the project.")
+ onClicked: rootView.handleAddAsset()
+ }
}
}
-
- HelperWidgets.IconButton {
- id: addAssetButton
- anchors.verticalCenter: parent.verticalCenter
- tooltip: qsTr("Add a new asset to the project.")
- icon: StudioTheme.Constants.plus
- buttonSize: parent.height
-
- onClicked: rootView.handleAddAsset()
- }
}
Text {
text: qsTr("No match found.")
leftPadding: 10
color: StudioTheme.Values.themeTextColor
- font.pixelSize: 12
+ font.pixelSize: StudioTheme.Values.baseFont
visible: !assetsModel.haveFiles && !root.__searchBoxEmpty
}
Item { // placeholder when the assets library is empty
width: parent.width
- height: parent.height - searchRow.height
+ height: parent.height - toolbar.height - column.spacing
visible: !assetsModel.haveFiles && root.__searchBoxEmpty
clip: true
+ MouseArea { // right clicking the empty area of the view
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ onClicked: {
+ contextMenu.openContextMenuForEmpty(assetsModel.rootPath())
+ }
+ }
+
DropArea { // handles external drop (goes into default folder based on suffix)
anchors.fill: parent
- onEntered: (drag)=> {
+ onEntered: (drag) => {
root.updateDropExtFiles(drag)
}
- onDropped: {
+ onDropped: (drag) => {
+ drag.accept()
rootView.emitExtFilesDrop(root.dropSimpleExtFiles, root.dropComplexExtFiles)
}
Column {
- id: colNoAssets
-
+ id: noAssetsColumn
spacing: 20
- x: 20
- width: root.width - 2 * x
- anchors.verticalCenter: parent.verticalCenter
+ width: root.width - 40
+ anchors.centerIn: parent
Text {
text: qsTr("Looks like you don't have any assets yet.")
color: StudioTheme.Values.themeTextColor
- font.pixelSize: 18
- width: colNoAssets.width
+ font.pixelSize: StudioTheme.Values.bigFont
+ width: noAssetsColumn.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
@@ -200,6 +230,7 @@ Item {
source: "image://qmldesigner_assets/browse"
anchors.horizontalCenter: parent.horizontalCenter
scale: maBrowse.containsMouse ? 1.2 : 1
+
Behavior on scale {
NumberAnimation {
duration: 300
@@ -218,8 +249,8 @@ Item {
Text {
text: qsTr("Drag-and-drop your assets here or click the '+' button to browse assets from the file system.")
color: StudioTheme.Values.themeTextColor
- font.pixelSize: 18
- width: colNoAssets.width
+ font.pixelSize: StudioTheme.Values.bigFont
+ width: noAssetsColumn.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
}
@@ -231,9 +262,8 @@ Item {
id: assetsView
assetsRoot: root
contextMenu: contextMenu
-
width: parent.width
- height: parent.height - y
+ height: parent.height - assetsView.y
}
- } // Column
+ }
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml
index 6c13a3c0b1..391c622048 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml
@@ -5,26 +5,34 @@ import QtQuick
import QtQuick.Controls
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import AssetsLibraryBackend
StudioControls.Menu {
id: root
required property Item assetsView
+ property var assetsModel: AssetsLibraryBackend.assetsModel
+ property var rootView: AssetsLibraryBackend.rootView
+
property bool __isDirectory: false
property var __fileIndex: null
property string __dirPath: ""
property string __dirName: ""
property var __onFolderCreated: null
- property var __onFolderRenamed: null
property var __dirIndex: null
property string __allExpandedState: ""
property var __selectedAssetPathsList: null
+ property bool __showInGraphicalShellEnabled: false
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
function openContextMenuForRoot(rootModelIndex, dirPath, dirName, onFolderCreated)
{
+ root.__showInGraphicalShellEnabled = true
+
+ rootView.updateContextMenuActionsEnableState()
+
root.__onFolderCreated = onFolderCreated
root.__fileIndex = ""
root.__dirPath = dirPath
@@ -34,11 +42,13 @@ StudioControls.Menu {
root.popup()
}
- function openContextMenuForDir(dirModelIndex, dirPath, dirName, allExpandedState,
- onFolderCreated, onFolderRenamed)
+ function openContextMenuForDir(dirModelIndex, dirPath, dirName, allExpandedState, onFolderCreated)
{
+ root.__showInGraphicalShellEnabled = true
+
+ rootView.updateContextMenuActionsEnableState()
+
root.__onFolderCreated = onFolderCreated
- root.__onFolderRenamed = onFolderRenamed
root.__dirPath = dirPath
root.__dirName = dirName
root.__fileIndex = ""
@@ -50,6 +60,15 @@ StudioControls.Menu {
function openContextMenuForFile(fileIndex, dirModelIndex, selectedAssetPathsList, onFolderCreated)
{
+ // check that all assets are in the same folder
+ let asset0 = selectedAssetPathsList[0]
+ let asset0Folder = asset0.substring(0, asset0.lastIndexOf('/'))
+
+ root.__showInGraphicalShellEnabled = selectedAssetPathsList.every(v => {
+ let assetFolder = v.substring(0, v.lastIndexOf('/'))
+ return assetFolder === asset0Folder
+ })
+
if (selectedAssetPathsList.length > 1) {
deleteFileItem.text = qsTr("Delete Files")
addTexturesItem.text = qsTr("Add Textures")
@@ -58,11 +77,24 @@ StudioControls.Menu {
addTexturesItem.text = qsTr("Add Texture")
}
+ rootView.updateContextMenuActionsEnableState()
+
root.__onFolderCreated = onFolderCreated
root.__selectedAssetPathsList = selectedAssetPathsList
root.__fileIndex = fileIndex
root.__dirIndex = dirModelIndex
- root.__dirPath = assetsModel.filePath(dirModelIndex)
+ root.__dirPath = AssetsLibraryBackend.assetsModel.filePath(dirModelIndex)
+ root.__isDirectory = false
+ root.popup()
+ }
+
+ function openContextMenuForEmpty(dirPath)
+ {
+ __showInGraphicalShellEnabled = true
+
+ root.__dirPath = dirPath
+ root.__fileIndex = ""
+ root.__dirIndex = ""
root.__isDirectory = false
root.popup()
}
@@ -91,16 +123,18 @@ StudioControls.Menu {
StudioControls.MenuItem {
id: addTexturesItem
text: qsTr("Add Texture")
- visible: root.__fileIndex && assetsModel.allFilePathsAreImages(root.__selectedAssetPathsList)
+ enabled: rootView.hasMaterialLibrary
+ visible: root.__fileIndex && AssetsLibraryBackend.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList)
height: addTexturesItem.visible ? addTexturesItem.implicitHeight : 0
- onTriggered: rootView.addTextures(root.__selectedAssetPathsList)
+ onTriggered: AssetsLibraryBackend.rootView.addTextures(root.__selectedAssetPathsList)
}
StudioControls.MenuItem {
id: addLightProbes
text: qsTr("Add Light Probe")
+ enabled: rootView.hasMaterialLibrary && rootView.hasSceneEnv
visible: root.__fileIndex && root.__selectedAssetPathsList.length === 1
- && assetsModel.allFilePathsAreImages(root.__selectedAssetPathsList)
+ && AssetsLibraryBackend.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList)
height: addLightProbes.visible ? addLightProbes.implicitHeight : 0
onTriggered: rootView.addLightProbe(root.__selectedAssetPathsList[0])
}
@@ -111,7 +145,7 @@ StudioControls.Menu {
visible: root.__fileIndex
height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
onTriggered: {
- let deleted = assetsModel.requestDeleteFiles(root.__selectedAssetPathsList)
+ let deleted = AssetsLibraryBackend.assetsModel.requestDeleteFiles(root.__selectedAssetPathsList)
if (!deleted)
confirmDeleteFiles.open()
}
@@ -142,12 +176,14 @@ StudioControls.Menu {
dirPath: root.__dirPath
dirName: root.__dirName
- onAccepted: root.__onFolderRenamed()
+ onAccepted: root.__onFolderCreated(renameFolderDialog.renamedDirPath)
}
}
StudioControls.MenuItem {
text: qsTr("New Folder")
+ visible: AssetsLibraryBackend.assetsModel.haveFiles
+ height: visible ? implicitHeight : 0
NewFolderDialog {
id: newFolderDialog
@@ -173,11 +209,11 @@ StudioControls.Menu {
}
onTriggered: {
- if (!assetsModel.hasChildren(root.__dirIndex)) {
+ if (!AssetsLibraryBackend.assetsModel.hasChildren(root.__dirIndex)) {
// NOTE: the folder may still not be empty -- it doesn't have files visible to the
// user, but that doesn't mean that there are no other files (e.g. files of unknown
// types) on disk in this directory.
- assetsModel.deleteFolderRecursively(root.__dirIndex)
+ AssetsLibraryBackend.assetsModel.deleteFolderRecursively(root.__dirIndex)
} else {
confirmDeleteFolderDialog.open()
}
@@ -186,14 +222,28 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("New Effect")
- visible: assetsModel.canCreateEffects()
+ visible: rootView.canCreateEffects()
+ height: visible ? implicitHeight : 0
NewEffectDialog {
id: newEffectDialog
- parent: root.assetsView
+ parent: root.assetsView.parent
dirPath: root.__dirPath
}
onTriggered: newEffectDialog.open()
}
+
+ StudioControls.MenuItem {
+ text: rootView.showInGraphicalShellMsg()
+
+ enabled: root.__showInGraphicalShellEnabled
+
+ onTriggered: {
+ if (!root.__fileIndex || root.__selectedAssetPathsList.length > 1)
+ rootView.showInGraphicalShell(root.__dirPath)
+ else
+ rootView.showInGraphicalShell(root.__selectedAssetPathsList[0])
+ }
+ }
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml
index 9056a802d5..ede7f58caf 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml
@@ -5,20 +5,27 @@ import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
+import AssetsLibraryBackend
TreeView {
id: root
clip: true
- interactive: verticalScrollBar.visible && !root.contextMenu.opened
+ interactive: verticalScrollBar.visible && !root.contextMenu.opened && !rootView.isDragging
reuseItems: false
boundsBehavior: Flickable.StopAtBounds
rowSpacing: 5
+ property var assetsModel: AssetsLibraryBackend.assetsModel
+ property var rootView: AssetsLibraryBackend.rootView
+ property var tooltipBackend: AssetsLibraryBackend.tooltipBackend
+
required property Item assetsRoot
required property StudioControls.Menu contextMenu
property alias verticalScrollBar: verticalScrollBar
property var selectedAssets: ({})
+ // the latest file that was clicked, or changed to via Up or Down keys
+ property string currentFilePath: ""
// used to see if the op requested is to expand or to collapse.
property int lastRowCount: -1
@@ -29,7 +36,7 @@ TreeView {
property int rootPathRow: 0
// i.e. first child of the root path
readonly property int firstRow: root.rootPathRow + 1
- property int rowToExpand: -1
+ readonly property int lastRow: root.rows - 1
property var __createdDirectories: []
rowHeightProvider: (row) => {
@@ -52,7 +59,7 @@ TreeView {
assetsModel.syncHaveFiles()
}
- updateRows()
+ root.updateRows()
}
Timer {
@@ -74,6 +81,19 @@ TreeView {
updateRowsTimer.restart()
}
+
+ function onDeleteSelectedAssetsRequested()
+ {
+ let selectedPaths = root.selectedPathsAsList()
+ if (!selectedPaths.length)
+ return
+
+ let deleted = assetsModel.requestDeleteFiles(selectedPaths)
+ if (!deleted) {
+ confirmDeleteFiles.files = selectedPaths
+ confirmDeleteFiles.open()
+ }
+ }
}
Connections {
@@ -170,11 +190,6 @@ TreeView {
if (root.requestedExpandAll)
root.__doExpandAll()
} else {
- if (root.rowToExpand > 0) {
- root.expand(root.rowToExpand)
- root.rowToExpand = -1
- }
-
// on collapsing, set expandAll flag to false.
root.requestedExpandAll = false;
}
@@ -309,14 +324,84 @@ TreeView {
function __modelIndex(row)
{
// The modelIndex() function exists since 6.3. In Qt 6.3, this modelIndex() function was a
- // member of the TreeView, while in Qt6.4 it was moved to TableView. In Qt6.4, the order of
- // the arguments was changed.
- if (assetsRoot.qtVersionAtLeast6_4)
+ // member of the TreeView, while in Qt6.4 it was moved to TableView. In Qt 6.4, the order of
+ // the arguments was changed, and in Qt 6.5 the order was changed again. Due to this mess,
+ // the whole function was deprecated in Qt 6.4.3 and replaced with index() function.
+ if (assetsRoot.qtVersion >= 0x060403)
+ return root.index(row, 0)
+ else if (assetsRoot.qtVersion >= 0x060400)
return root.modelIndex(0, row)
else
return root.modelIndex(row, 0)
}
+ function __selectRow(row: int)
+ {
+ let index = root.__modelIndex(row)
+ if (assetsModel.isDirectory(index))
+ return
+
+ let filePath = assetsModel.filePath(index)
+
+ root.clearSelectedAssets()
+ root.setAssetSelected(filePath, true)
+ root.currentFilePath = filePath
+ }
+
+ Keys.enabled: true
+
+ Keys.onUpPressed: {
+ if (!root.currentFilePath)
+ return
+
+ let index = assetsModel.indexForPath(root.currentFilePath)
+ let row = root.rowAtIndex(index)
+ let nextRow = row
+ let nextIndex = index
+
+ do {
+ if (nextRow <= root.firstRow)
+ return // don't select hidden rows
+
+ nextRow--
+ nextIndex = root.__modelIndex(nextRow)
+ } while (assetsModel.isDirectory(nextIndex))
+
+ root.__selectRow(nextRow)
+ root.positionViewAtRow(nextRow, TableView.Contain)
+ }
+
+ Keys.onDownPressed: {
+ if (!root.currentFilePath)
+ return
+
+ let index = assetsModel.indexForPath(root.currentFilePath)
+ let row = root.rowAtIndex(index)
+
+ let nextRow = row
+ let nextIndex = index
+
+ do {
+ if (nextRow >= root.lastRow)
+ return // don't select hidden rows
+
+ nextRow++
+ nextIndex = root.__modelIndex(nextRow)
+ } while (assetsModel.isDirectory(nextIndex))
+
+ root.__selectRow(nextRow)
+ root.positionViewAtRow(nextRow, TableView.Contain)
+ }
+
+ ConfirmDeleteFilesDialog {
+ id: confirmDeleteFiles
+ parent: root
+ files: []
+
+ onAccepted: root.clearSelectedAssets()
+ onClosed: confirmDeleteFiles.files = []
+ }
+
DropArea {
id: dropArea
enabled: true
@@ -350,13 +435,14 @@ TreeView {
let [row, item] = dropArea.__rowAndItem(drag)
if (item) {
- root.endDropHover(row)
+ drag.accept()
+ root.endDropHover(row)
- let dirPath = item.getDirPath()
+ let dirPath = item.getDirPath()
- rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles,
- root.assetsRoot.dropComplexExtFiles,
- dirPath)
+ rootView.emitExtFilesDrop(root.assetsRoot.dropSimpleExtFiles,
+ root.assetsRoot.dropComplexExtFiles,
+ dirPath)
}
dropArea.__isHoveringDrop = false
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFilesDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml
index 0ab2ce533a..756176dcca 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFilesDialog.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFilesDialog.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioTheme as StudioTheme
import StudioControls as StudioControls
+import AssetsLibraryBackend
Dialog {
id: root
@@ -61,7 +62,7 @@ Dialog {
text: qsTr("Delete")
onClicked: {
- assetsModel.deleteFiles(root.files, dontAskAgain.checked)
+ AssetsLibraryBackend.assetsModel.deleteFiles(root.files, dontAskAgain.checked)
root.accept()
}
}
@@ -98,7 +99,7 @@ Dialog {
delegate: Text {
elide: Text.ElideLeft
- text: model.modelData.replace(assetsModel.currentProjectDirPath(), "")
+ text: model.modelData.replace(AssetsLibraryBackend.assetsModel.currentProjectDirPath(), "")
color: StudioTheme.Values.themeTextColor
width: parent.width - (verticalScrollBar.scrollBarVisible ? verticalScrollBar.width : 0)
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml
index f75a094a7f..6faef2434a 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ConfirmDeleteFolderDialog.qml
@@ -5,6 +5,7 @@ import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioTheme as StudioTheme
+import AssetsLibraryBackend
Dialog {
id: root
@@ -53,7 +54,7 @@ Dialog {
text: qsTr("Delete")
onClicked: {
- assetsModel.deleteFolderRecursively(root.dirIndex)
+ AssetsLibraryBackend.assetsModel.deleteFolderRecursively(root.dirIndex)
root.accept()
}
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ErrorDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ErrorDialog.qml
index 58e1910b18..58e1910b18 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ErrorDialog.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/ErrorDialog.qml
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml
index e34237a0c3..a5a1235587 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewEffectDialog.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewEffectDialog.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import AssetsLibraryBackend
Dialog {
id: root
@@ -18,10 +19,7 @@ Dialog {
required property string dirPath
readonly property int __maxPath: 32
- HelperWidgets.RegExpValidator {
- id: effectNameValidator
- regExp: /^[A-Z]\w[A-Za-z0-9_]*$/
- }
+ property var rootView: AssetsLibraryBackend.rootView
ErrorDialog {
id: creationFailedDialog
@@ -44,10 +42,16 @@ Dialog {
actionIndicator.visible: false
translationIndicator.visible: false
- validator: effectNameValidator
Keys.onEnterPressed: btnCreate.onClicked()
Keys.onReturnPressed: btnCreate.onClicked()
+ Keys.onEscapePressed: root.reject()
+
+ onTextChanged: {
+ let validator = /^[A-Z]\w{2,}[A-Za-z0-9_]*$/
+ txtNameValidatorMsg.visible = text.length > 0 && !validator.test(text)
+ btnCreate.enabled = !txtNameValidatorMsg.visible
+ }
}
}
@@ -65,6 +69,17 @@ Dialog {
visible: effectName.text.length > root.__maxPath
}
+ Text {
+ id: txtNameValidatorMsg
+ text: qsTr('The name must start with a capital letter, ' +
+ 'contain at least three characters, ' +
+ 'and cannot have any special characters.')
+ color: "#ff0000"
+ anchors.right: parent.right
+ anchors.left: parent.left
+ wrapMode: "WordWrap"
+ }
+
Item { // spacer
width: 1
height: 20
@@ -81,8 +96,8 @@ Dialog {
&& effectName.length >=3
&& effectName.text.length <= root.__maxPath
onClicked: {
- const path = assetsModel.getUniqueEffectPath(root.dirPath, effectName.text)
- if (assetsModel.createNewEffect(path))
+ const path = AssetsLibraryBackend.rootView.getUniqueEffectPath(root.dirPath, effectName.text)
+ if (AssetsLibraryBackend.rootView.createNewEffect(path))
root.accept()
else
creationFailedDialog.open()
@@ -97,7 +112,7 @@ Dialog {
}
onOpened: {
- const path = assetsModel.getUniqueEffectPath(root.dirPath, "Effect01")
+ const path = AssetsLibraryBackend.rootView.getUniqueEffectPath(root.dirPath, "Effect01")
effectName.text = path.split('/').pop().replace(".qep", '')
effectName.selectAll()
effectName.forceActiveFocus()
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml
index c1966a9748..a1afe46491 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/NewFolderDialog.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import AssetsLibraryBackend
Dialog {
id: root
@@ -49,6 +50,7 @@ Dialog {
Keys.onEnterPressed: btnCreate.onClicked()
Keys.onReturnPressed: btnCreate.onClicked()
+ Keys.onEscapePressed: root.reject()
onTextChanged: {
root.createdDirPath = root.dirPath + '/' + folderName.text
@@ -85,7 +87,7 @@ Dialog {
enabled: folderName.text !== "" && root.createdDirPath.length <= root.__maxPath
onClicked: {
root.createdDirPath = root.dirPath + '/' + folderName.text
- if (assetsModel.addNewFolder(root.createdDirPath))
+ if (AssetsLibraryBackend.assetsModel.addNewFolder(root.createdDirPath))
root.accept()
else
creationFailedDialog.open()
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml
index a19b7a6ee0..0b9ae30255 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml
+++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/RenameFolderDialog.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import AssetsLibraryBackend
Dialog {
id: root
@@ -17,6 +18,7 @@ Dialog {
modal: true
property bool renameError: false
+ property string renamedDirPath: ""
required property string dirPath
required property string dirName
@@ -83,9 +85,11 @@ Dialog {
text: qsTr("Rename")
enabled: folderRename.text !== ""
onClicked: {
- var success = assetsModel.renameFolder(root.dirPath, folderRename.text)
- if (success)
+ var success = AssetsLibraryBackend.assetsModel.renameFolder(root.dirPath, folderRename.text)
+ if (success) {
+ root.renamedDirPath = root.dirPath.replace(/(.*\/)[^/]+$/, "$1" + folderRename.text)
root.accept()
+ }
root.renameError = !success
}
@@ -104,4 +108,8 @@ Dialog {
folderRename.forceActiveFocus()
root.renameError = false
}
+
+ onRejected: {
+ root.renamedDirPath = ""
+ }
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml
index e9d18d9ded..37154ec170 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml
@@ -1,13 +1,14 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 QtQuickDesignerTheme
-import HelperWidgets as HelperWidgets
-import StudioControls as StudioControls
-import StudioTheme as StudioTheme
+import HelperWidgets 2.0 as HelperWidgets
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
+import ContentLibraryBackend
Item {
id: root
@@ -18,6 +19,7 @@ Item {
materialsView.closeContextMenu()
texturesView.closeContextMenu()
environmentsView.closeContextMenu()
+ HelperWidgets.Controller.closeContextMenu()
}
// Called from C++
@@ -28,30 +30,54 @@ Item {
Column {
id: col
- y: 5
+ anchors.fill: parent
spacing: 5
- StudioControls.SearchBox {
- id: searchBox
-
- width: root.width
- enabled: {
- if (tabBar.currIndex == 0) { // Materials tab
- materialsModel.matBundleExists
- && rootView.hasMaterialLibrary
- && materialsModel.hasRequiredQuick3DImport
- } else { // Textures / Environments tabs
- texturesModel.texBundleExists
+ Rectangle {
+ width: parent.width
+ height: StudioTheme.Values.doubleToolbarHeight
+ color: StudioTheme.Values.themeToolbarBackground
+
+ Column {
+ anchors.fill: parent
+ anchors.topMargin: 6
+ anchors.bottomMargin: 6
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ spacing: 12
+
+ StudioControls.SearchBox {
+ id: searchBox
+ width: parent.width
+ style: StudioTheme.Values.searchControlStyle
+ enabled: {
+ if (tabBar.currIndex === 0) { // Materials tab
+ ContentLibraryBackend.materialsModel.matBundleExists
+ && ContentLibraryBackend.rootView.hasMaterialLibrary
+ && ContentLibraryBackend.materialsModel.hasRequiredQuick3DImport
+ } else { // Textures / Environments tabs
+ ContentLibraryBackend.texturesModel.texBundleExists
+ }
+ }
+
+ onSearchChanged: (searchText) => {
+ ContentLibraryBackend.rootView.handleSearchFilterChanged(searchText)
+
+ // make sure categories with matches are expanded
+ materialsView.expandVisibleSections()
+ texturesView.expandVisibleSections()
+ environmentsView.expandVisibleSections()
+ }
}
- }
-
- onSearchChanged: (searchText) => {
- rootView.handleSearchFilterChanged(searchText)
- // make sure categories with matches are expanded
- materialsView.expandVisibleSections()
- texturesView.expandVisibleSections()
- environmentsView.expandVisibleSections()
+ ContentLibraryTabBar {
+ id: tabBar
+ width: parent.width
+ height: StudioTheme.Values.toolbarHeight
+ tabsModel: [{name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium},
+ {name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium},
+ {name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium}]
+ }
}
}
@@ -59,14 +85,6 @@ Item {
id: confirmUnimportDialog
}
- ContentLibraryTabBar {
- id: tabBar
- // TODO: update icons
- tabsModel: [{name: qsTr("Materials"), icon: StudioTheme.Constants.gradient},
- {name: qsTr("Textures"), icon: StudioTheme.Constants.materialPreviewEnvironment},
- {name: qsTr("Environments"), icon: StudioTheme.Constants.translationSelectLanguages}]
- }
-
StackLayout {
width: root.width
height: root.height - y
@@ -89,7 +107,8 @@ Item {
id: texturesView
width: root.width
- model: texturesModel
+ model: ContentLibraryBackend.texturesModel
+ sectionCategory: "ContentLib_Tex"
searchBox: searchBox
}
@@ -98,7 +117,8 @@ Item {
id: environmentsView
width: root.width
- model: environmentsModel
+ model: ContentLibraryBackend.environmentsModel
+ sectionCategory: "ContentLib_Env"
searchBox: searchBox
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml
index f582094666..5bafd1d052 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml
@@ -8,26 +8,36 @@ import HelperWidgets 2.0
import QtQuick.Controls
import StudioTheme 1.0 as StudioTheme
+import ContentLibraryBackend
+
+import WebFetcher 1.0
Item {
id: root
signal showContextMenu()
+ // Download states: "" (ie default, not downloaded), "unavailable", "downloading", "downloaded",
+ // "failed"
+ property string downloadState: modelData.isDownloaded() ? "downloaded" : ""
+
visible: modelData.bundleMaterialVisible
MouseArea {
id: mouseArea
+ enabled: root.downloadState !== "downloading"
hoverEnabled: true
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: (mouse) => {
- if (mouse.button === Qt.LeftButton && !materialsModel.importerRunning)
- rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y))
- else if (mouse.button === Qt.RightButton)
+ if (mouse.button === Qt.LeftButton && !materialsModel.importerRunning) {
+ if (root.downloadState === "downloaded")
+ ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y))
+ } else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") {
root.showContextMenu()
+ }
}
}
@@ -37,6 +47,15 @@ Item {
Item { width: 1; height: 5 } // spacer
+ DownloadPane {
+ id: downloadPane
+ width: root.width - 10
+ height: img.width
+ visible: root.downloadState === "downloading"
+
+ onRequestCancel: downloader.cancel()
+ }
+
Image {
id: img
@@ -45,6 +64,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
source: modelData.bundleMaterialIcon
cache: false
+ visible: root.downloadState != "downloading"
Rectangle { // circular indicator for imported bundle materials
width: 10
@@ -81,14 +101,61 @@ Item {
pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4)
anchors.right: img.right
anchors.bottom: img.bottom
- enabled: !materialsModel.importerRunning
- visible: containsMouse || mouseArea.containsMouse
+ enabled: !ContentLibraryBackend.materialsModel.importerRunning
+ visible: root.downloadState === "downloaded"
+ && (containsMouse || mouseArea.containsMouse)
onClicked: {
- materialsModel.addToProject(modelData)
+ ContentLibraryBackend.materialsModel.addToProject(modelData)
+ }
+ } // IconButton
+
+ IconButton {
+ id: downloadIcon
+ icon: root.downloadState === "unavailable"
+ ? StudioTheme.Constants.downloadUnavailable
+ : StudioTheme.Constants.download
+
+ iconColor: root.downloadState === "unavailable" || root.downloadState === "failed"
+ ? StudioTheme.Values.themeRedLight
+ : StudioTheme.Values.themeTextColor
+
+ iconSize: 22
+ iconScale: downloadIcon.containsMouse ? 1.2 : 1
+ iconStyle: Text.Outline
+ iconStyleColor: "black"
+
+ tooltip: qsTr("Click to download material")
+ buttonSize: 22
+
+ transparentBg: true
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ visible: root.downloadState !== "downloaded"
+
+ anchors.bottomMargin: 0
+ anchors.rightMargin: 4
+
+ Rectangle { // arrow fill
+ anchors.centerIn: parent
+ z: -1
+
+ width: parent.width / 2
+ height: parent.height / 2
+ color: "black"
}
- }
- }
+
+ onClicked: {
+ if (root.downloadState !== "" && root.downloadState !== "failed")
+ return
+
+ downloadPane.beginDownload(Qt.binding(function() { return downloader.progress }))
+
+ root.downloadState = ""
+ downloader.start()
+ }
+ } // IconButton
+ } // Image
TextInput {
id: matName
@@ -109,5 +176,44 @@ Item {
selectionColor: StudioTheme.Values.themeTextSelectionColor
selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
}
- }
+ } // Column
+
+ MultiFileDownloader {
+ id: downloader
+
+ baseUrl: modelData.bundleMaterialBaseWebUrl
+ files: modelData.bundleMaterialFiles
+
+ targetDirPath: modelData.bundleMaterialParentPath
+
+ onDownloadStarting: {
+ root.downloadState = "downloading"
+ }
+
+ onFinishedChanged: {
+ downloadPane.endDownload()
+
+ root.downloadState = "downloaded"
+ }
+
+ onDownloadCanceled: {
+ downloadPane.endDownload()
+
+ root.downloadState = ""
+ }
+
+ onDownloadFailed: {
+ downloadPane.endDownload()
+
+ root.downloadState = "failed"
+ }
+
+ downloader: FileDownloader {
+ id: fileDownloader
+ url: downloader.nextUrl
+ probeUrl: false
+ downloadEnabled: true
+ targetFilePath: downloader.nextTargetPath
+ } // FileDownloader
+ } // MultiFileDownloader
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml
index 4e4b72bb06..ca3a05bdd1 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml
@@ -29,13 +29,13 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("Apply to selected (replace)")
enabled: root.targetAvailable && root.hasModelSelection
- onTriggered: root.applyToSelected(root.targetMaterial, false)
+ onTriggered: materialsModel.applyToSelected(root.targetMaterial, false)
}
StudioControls.MenuItem {
text: qsTr("Apply to selected (add)")
enabled: root.targetAvailable && root.hasModelSelection
- onTriggered: root.applyToSelected(root.targetMaterial, true)
+ onTriggered: materialsModel.applyToSelected(root.targetMaterial, true)
}
StudioControls.MenuSeparator {}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml
index 5f171db464..2c6a5256f4 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml
@@ -5,18 +5,20 @@ import QtQuick
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import ContentLibraryBackend
HelperWidgets.ScrollView {
id: root
clip: true
- interactive: !ctxMenu.opened
+ interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging
readonly property int cellWidth: 100
readonly property int cellHeight: 120
property var currMaterialItem: null
property var rootItem: null
+ property var materialsModel: ContentLibraryBackend.materialsModel
required property var searchBox
@@ -60,6 +62,8 @@ HelperWidgets.ScrollView {
visible: bundleCategoryVisible && !materialsModel.isEmpty
expanded: bundleCategoryExpanded
expandOnClick: false
+ category: "ContentLib_Mat"
+
onToggleExpand: bundleCategoryExpanded = !bundleCategoryExpanded
onExpand: bundleCategoryExpanded = true
onCollapse: bundleCategoryExpanded = false
@@ -93,12 +97,12 @@ HelperWidgets.ScrollView {
id: infoText
text: {
if (!materialsModel.matBundleExists)
- qsTr("<b>Content Library</b> materials are not installed.")
- else if (!rootView.hasQuick3DImport)
+ qsTr("No materials available. Make sure you have internet connection.")
+ else if (!ContentLibraryBackend.rootView.hasQuick3DImport)
qsTr("To use <b>Content Library</b>, first add the QtQuick3D module in the <b>Components</b> view.")
else if (!materialsModel.hasRequiredQuick3DImport)
qsTr("To use <b>Content Library</b>, version 6.3 or later of the QtQuick3D module is required.")
- else if (!rootView.hasMaterialLibrary)
+ else if (!ContentLibraryBackend.rootView.hasMaterialLibrary)
qsTr("<b>Content Library</b> is disabled inside a non-visual component.")
else if (!searchBox.isEmpty())
qsTr("No match found.")
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabBar.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabBar.qml
index 0ee7ce8f08..ca8fb64ecc 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabBar.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabBar.qml
@@ -1,33 +1,29 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 StudioTheme as StudioTheme
+import HelperWidgets 2.0 as HelperWidgets
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
-Rectangle {
+Row {
id: root
- width: parent.width
- height: 50
- color: StudioTheme.Values.themeSectionHeadBackground
-
property int currIndex: 0
property alias tabsModel: repeater.model
- Row {
- spacing: 1
-
- Repeater {
- id: repeater
+ spacing: 6
- ContentLibraryTabButton {
- height: root.height
+ Repeater {
+ id: repeater
- name: modelData.name
- icon: modelData.icon
- selected: root.currIndex === index
- onClicked: root.currIndex = index
- }
+ ContentLibraryTabButton {
+ required property int index
+ required property var modelData
+ name: modelData.name
+ icon: modelData.icon
+ selected: root.currIndex === index
+ onClicked: root.currIndex = index
}
}
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabButton.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabButton.qml
index 4dda92592e..587f846a10 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabButton.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTabButton.qml
@@ -2,51 +2,51 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
-import StudioTheme as StudioTheme
+import HelperWidgets 2.0 as HelperWidgets
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
Rectangle {
id: root
signal clicked()
- property alias icon: icon.text
- property alias name: name.text
+ property alias icon: button.buttonIcon
+ property alias name: label.text
property bool selected: false
- width: 100
- height: 100
- color: root.selected ? StudioTheme.Values.themePanelBackground
- : mouseArea.containsMouse ? Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.3)
- : StudioTheme.Values.themeSectionHeadBackground
+ height: button.height
+ width: button.width + label.width + contentRow.spacing + 6
+ color: StudioTheme.Values.themeToolbarBackground
+ radius: StudioTheme.Values.smallRadius
- Text {
- id: icon
+ state: "default"
- color: root.selected ? StudioTheme.Values.themeInteraction : StudioTheme.Values.themeTextColor
+ Row {
+ id: contentRow
+ spacing: 6
- font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.mediumIconFontSize
- anchors.horizontalCenter: parent.horizontalCenter
- y: 8
- }
-
- Text {
- id: name
-
- font.weight: Font.DemiBold
- font.pixelSize: StudioTheme.Values.baseFontSize
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 6
+ HelperWidgets.AbstractButton {
+ id: button
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.material_medium
+ hover: mouseArea.containsMouse
+ checked: root.selected
+ checkable: true
+ checkedInverted: true
+ autoExclusive: true
+ }
- color: root.selected ? StudioTheme.Values.themeInteraction : StudioTheme.Values.themeTextColor
- }
-
- Rectangle { // strip
- width: root.width
- height: 4
- color: root.selected ? StudioTheme.Values.themeInteraction : "transparent"
- anchors.bottom: parent.bottom
+ Text {
+ id: label
+ height: StudioTheme.Values.statusbarButtonStyle.controlSize.height
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("Materials")
+ font.pixelSize: StudioTheme.Values.baseFontSize
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
}
MouseArea {
@@ -55,4 +55,43 @@ Rectangle {
hoverEnabled: true
onClicked: root.clicked()
}
+
+ states: [
+ State {
+ name: "default"
+ when: !mouseArea.containsMouse && !button.checked
+ },
+ State {
+ name: "hover"
+ when: mouseArea.containsMouse && !button.checked
+ PropertyChanges {
+ target: root
+ color: StudioTheme.Values.themeControlBackground_topToolbarHover
+ }
+ },
+ State {
+ name: "checked"
+ when: !mouseArea.containsMouse && button.checked
+ PropertyChanges {
+ target: root
+ color: StudioTheme.Values.themeInteraction
+ }
+ PropertyChanges {
+ target: label
+ color: StudioTheme.Values.themeTextSelectedTextColor
+ }
+ },
+ State {
+ name: "hoverChecked"
+ when: mouseArea.containsMouse && button.checked
+ PropertyChanges {
+ target: root
+ color: StudioTheme.Values.themeInteractionHover
+ }
+ PropertyChanges {
+ target: label
+ color: StudioTheme.Values.themeTextSelectedTextColor
+ }
+ }
+ ]
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml
index ae691775e4..a556aad72a 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml
@@ -9,43 +9,243 @@ import QtQuick.Controls
import StudioTheme 1.0 as StudioTheme
-Image {
+import WebFetcher 1.0
+import ContentLibraryBackend
+
+Item {
id: root
- source: modelData.textureIcon
- visible: modelData.textureVisible
- cache: false
+ // Download states: "" (ie default, not downloaded), "unavailable", "downloading", "downloaded",
+ // "failed"
+ property string downloadState: modelData.isDownloaded() ? "downloaded" : ""
+ property bool delegateVisible: modelData.textureVisible
+
+ property alias allowCancel: progressBar.closeButtonVisible
+ property alias progressValue: progressBar.value
+ property alias progressText: progressLabel.text
+
+ visible: root.delegateVisible
signal showContextMenu()
+ function statusText()
+ {
+ if (root.downloadState === "downloaded")
+ return qsTr("Texture was already downloaded.")
+ if (root.downloadState === "unavailable")
+ return qsTr("Network/Texture unavailable or broken Link.")
+ if (root.downloadState === "failed")
+ return qsTr("Could not download texture.")
+
+ return qsTr("Click to download the texture.")
+ }
+
+ Rectangle {
+ id: downloadPane
+ anchors.fill: parent
+ color: StudioTheme.Values.themeThumbnailBackground
+ border.color: "#00000000"
+
+ visible: root.downloadState === "downloading"
+
+ TextureProgressBar {
+ id: progressBar
+ anchors.rightMargin: 10
+ anchors.leftMargin: 10
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+
+ visible: false
+
+ onCancelRequested: {
+ downloader.cancel()
+ }
+
+ Text {
+ id: progressLabel
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("Progress:")
+ anchors.bottom: parent.top
+ anchors.bottomMargin: 5
+ anchors.left: parent.left
+ font.pixelSize: 12
+ }
+
+ Row {
+ anchors.top: parent.bottom
+ anchors.topMargin: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Text {
+ id: progressAmount
+ color: StudioTheme.Values.themeTextColor
+ text: progressBar.value.toFixed(1)
+
+ font.pixelSize: 12
+ }
+
+ Text {
+ id: percentSign
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("%")
+ font.pixelSize: 12
+ }
+ }
+ } // TextureProgressBar
+ } // Rectangle
+
+ Image {
+ id: image
+ anchors.fill: parent
+
+ source: modelData.textureIcon
+ visible: root.delegateVisible && root.downloadState != "downloading"
+ cache: false
+
+ property string webUrl: modelData.textureWebUrl
+
+ IconButton {
+ id: downloadIcon
+ icon: root.downloadState === "unavailable"
+ ? StudioTheme.Constants.downloadUnavailable
+ : StudioTheme.Constants.download
+
+ iconColor: root.downloadState === "unavailable" || root.downloadState === "failed"
+ ? StudioTheme.Values.themeRedLight
+ : StudioTheme.Values.themeTextColor
+
+ iconSize: 22
+ iconScale: downloadIcon.containsMouse ? 1.2 : 1
+ iconStyle: Text.Outline
+ iconStyleColor: "black"
+
+ tooltip: modelData.textureToolTip + (downloadIcon.visible
+ ? "\n\n" + root.statusText()
+ : "")
+ buttonSize: 22
+
+ transparentBg: true
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ visible: root.downloadState !== "downloaded"
+
+ anchors.bottomMargin: 0
+ anchors.rightMargin: 4
+
+ Rectangle { // Arrow Fill
+ anchors.centerIn: parent
+ z: -1
+
+ width: parent.width / 2
+ height: parent.height / 2
+ color: "black"
+ }
+
+ onClicked: {
+ if (root.downloadState !== "" && root.downloadState !== "failed")
+ return
+
+ if (!ContentLibraryBackend.rootView.markTextureDownloading())
+ return
+
+ progressBar.visible = true
+ tooltip.visible = false
+ root.progressText = qsTr("Downloading...")
+ root.allowCancel = true
+ root.progressValue = Qt.binding(function() { return downloader.progress })
+
+ root.downloadState = ""
+ downloader.start()
+ }
+ } // IconButton
+
+ ToolTip {
+ id: tooltip
+ // contentWidth is not calculated correctly by the toolTip (resulting in a wider tooltip than
+ // needed). Using a helper Text to calculate the correct width
+ contentWidth: helperText.width
+ bottomInset: -2
+ text: modelData.textureToolTip + (downloadIcon.visible
+ ? "\n\n" + root.statusText()
+ : "")
+ delay: 1000
+
+ Text {
+ id: helperText
+ text: tooltip.text
+ visible: false
+ }
+ }
+ } // Image
+
MouseArea {
id: mouseArea
anchors.fill: parent
+ hoverEnabled: !downloadIcon.visible
+ propagateComposedEvents: downloadIcon.visible
acceptedButtons: Qt.LeftButton | Qt.RightButton
- hoverEnabled: true
+
+ onEntered: tooltip.visible = image.visible
+ onExited: tooltip.visible = false
onPressed: (mouse) => {
- if (mouse.button === Qt.LeftButton)
- rootView.startDragTexture(modelData, mapToGlobal(mouse.x, mouse.y))
- else if (mouse.button === Qt.RightButton)
+ if (mouse.button === Qt.LeftButton) {
+ if (root.downloadState === "downloaded")
+ ContentLibraryBackend.rootView.startDragTexture(modelData, mapToGlobal(mouse.x, mouse.y))
+ } else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") {
root.showContextMenu()
+ }
+ }
+ }
+
+ FileDownloader {
+ id: downloader
+ url: image.webUrl
+ probeUrl: false
+ downloadEnabled: true
+ onDownloadStarting: {
+ root.downloadState = "downloading"
+ }
+
+ onFinishedChanged: {
+ root.progressText = qsTr("Extracting...")
+ root.allowCancel = false
+ root.progressValue = Qt.binding(function() { return extractor.progress })
+
+ extractor.extract()
+ }
+
+ onDownloadCanceled: {
+ root.progressText = ""
+ root.progressValue = 0
+
+ root.downloadState = ""
+
+ ContentLibraryBackend.rootView.markNoTextureDownloading()
+ }
+
+ onDownloadFailed: {
+ root.downloadState = "failed"
+
+ ContentLibraryBackend.rootView.markNoTextureDownloading()
}
}
- ToolTip {
- visible: mouseArea.containsMouse
- // contentWidth is not calculated correctly by the toolTip (resulting in a wider tooltip than
- // needed). Using a helper Text to calculate the correct width
- contentWidth: helperText.width
- bottomInset: -2
- text: modelData.textureToolTip
- delay: 1000
-
- Text {
- id: helperText
- text: modelData.textureToolTip
- visible: false
+ FileExtractor {
+ id: extractor
+ archiveName: downloader.completeBaseName
+ sourceFile: downloader.outputFile
+ targetPath: modelData.textureParentPath
+ alwaysCreateDir: false
+ clearTargetPathContents: false
+ onFinishedChanged: {
+ modelData.setDownloaded()
+ root.downloadState = modelData.isDownloaded() ? "downloaded" : "failed"
+
+ ContentLibraryBackend.rootView.markNoTextureDownloading()
}
}
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml
index 5e2c1d3ff3..f804f16d89 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml
@@ -5,6 +5,7 @@ import QtQuick 2.15
import HelperWidgets 2.0
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
+import ContentLibraryBackend
StudioControls.Menu {
id: root
@@ -12,12 +13,12 @@ StudioControls.Menu {
property var targetTexture: null
property bool hasSceneEnv: false
- property bool canUse3D: targetTexture && rootView.hasQuick3DImport && rootView.hasMaterialLibrary
+ property bool canUse3D: targetTexture && ContentLibraryBackend.rootView.hasQuick3DImport && ContentLibraryBackend.rootView.hasMaterialLibrary
function popupMenu(targetTexture = null)
{
this.targetTexture = targetTexture
- rootView.updateSceneEnvState();
+ ContentLibraryBackend.rootView.updateSceneEnvState();
popup()
}
@@ -26,18 +27,18 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("Add image")
enabled: root.targetTexture
- onTriggered: rootView.addImage(root.targetTexture)
+ onTriggered: ContentLibraryBackend.rootView.addImage(root.targetTexture)
}
StudioControls.MenuItem {
text: qsTr("Add texture")
enabled: canUse3D
- onTriggered: rootView.addTexture(root.targetTexture)
+ onTriggered: ContentLibraryBackend.rootView.addTexture(root.targetTexture)
}
StudioControls.MenuItem {
text: qsTr("Add light probe")
enabled: root.hasSceneEnv && canUse3D
- onTriggered: rootView.addLightProbe(root.targetTexture)
+ onTriggered: ContentLibraryBackend.rootView.addLightProbe(root.targetTexture)
}
}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml
index ac77570cb8..1c24f9bc43 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml
@@ -5,12 +5,13 @@ import QtQuick
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import ContentLibraryBackend
HelperWidgets.ScrollView {
id: root
clip: true
- interactive: !ctxMenu.opened
+ interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging
readonly property int cellWidth: 100
readonly property int cellHeight: 100
@@ -20,6 +21,7 @@ HelperWidgets.ScrollView {
required property var searchBox
required property var model
+ required property string sectionCategory
signal unimport(var bundleMat);
@@ -56,6 +58,8 @@ HelperWidgets.ScrollView {
visible: bundleCategoryVisible && !root.model.isEmpty
expanded: bundleCategoryExpanded
expandOnClick: false
+ category: root.sectionCategory
+
onToggleExpand: bundleCategoryExpanded = !bundleCategoryExpanded
onExpand: bundleCategoryExpanded = true
onCollapse: bundleCategoryExpanded = false
@@ -90,7 +94,7 @@ HelperWidgets.ScrollView {
id: infoText
text: {
if (!root.model.texBundleExists)
- qsTr("<b>Content Library</b> textures are not installed.")
+ qsTr("No textures available. Make sure you have internet connection.")
else if (!searchBox.isEmpty())
qsTr("No match found.")
else
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/DownloadPane.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DownloadPane.qml
new file mode 100644
index 0000000000..d7e040efa8
--- /dev/null
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DownloadPane.qml
@@ -0,0 +1,81 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+import StudioTheme 1.0 as StudioTheme
+
+Rectangle {
+ id: root
+
+ color: StudioTheme.Values.themeThumbnailBackground
+ border.color: "#00000000"
+
+ signal requestCancel
+
+ property alias allowCancel: progressBar.closeButtonVisible
+ property alias progressValue: progressBar.value
+ property alias progressLabel: progressLabel.text
+
+ function beginDownload(progressFunction)
+ {
+ progressBar.visible = true
+ root.progressLabel = qsTr("Downloading...")
+ root.allowCancel = true
+ root.progressValue = progressFunction
+ }
+
+ function endDownload()
+ {
+ root.allowCancel = false
+ root.progressLabel = ""
+ root.progressValue = 0
+ }
+
+ TextureProgressBar {
+ id: progressBar
+ anchors.rightMargin: 10
+ anchors.leftMargin: 10
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+
+ visible: false
+
+ onCancelRequested: {
+ root.requestCancel()
+ }
+
+ Text {
+ id: progressLabel
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("Progress:")
+ anchors.bottom: parent.top
+ anchors.bottomMargin: 5
+ anchors.left: parent.left
+ font.pixelSize: 12
+ }
+
+ Row {
+ anchors.top: parent.bottom
+ anchors.topMargin: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Text {
+ id: progressAmount
+ color: StudioTheme.Values.themeTextColor
+ text: progressBar.value.toFixed(1)
+
+ font.pixelSize: 12
+ }
+
+ Text {
+ id: percentSign
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("%")
+ font.pixelSize: 12
+ }
+ }
+ } // TextureProgressBar
+} // Rectangle
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/TextureProgressBar.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/TextureProgressBar.qml
new file mode 100644
index 0000000000..fcd7437961
--- /dev/null
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/TextureProgressBar.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Controls
+
+import StudioTheme as StudioTheme
+
+Item {
+ id: root
+ width: 272
+ height: 25
+ property int value: 0
+ property bool closeButtonVisible
+
+ readonly property int margin: 4
+
+ readonly property string qdsBrand: "#57B9FC"
+
+ signal cancelRequested
+
+ Rectangle {
+ id: progressBarGroove
+ color: StudioTheme.Values.themeThumbnailLabelBackground
+ anchors.fill: parent
+ }
+
+ Rectangle {
+ id: progressBarTrack
+ width: root.value * ((root.width - closeButton.width) - 2 * root.margin) / 100
+ color: root.qdsBrand
+ border.color: "#002e769e"
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.margins: root.margin
+ }
+
+ Text {
+ id: closeButton
+ visible: root.closeButtonVisible
+ width: 20
+ text: StudioTheme.Constants.closeCross
+ color: root.qdsBrand
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: StudioTheme.Values.myIconFontSize
+
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.margins: root.margin
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ root.cancelRequested()
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml
index b742f98577..a8c3758eb5 100644
--- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml
+++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml
@@ -8,6 +8,7 @@ import QtQuickDesignerTheme
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import ContentLibraryBackend
Dialog {
id: root
@@ -47,7 +48,7 @@ Dialog {
text: qsTr("Remove")
onClicked: {
- materialsModel.removeFromProject(root.targetBundleMaterial)
+ ContentLibraryBackend.materialsModel.removeFromProject(root.targetBundleMaterial)
root.accept()
}
}
diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json
index e4201291c6..f695e63b56 100644
--- a/share/qtcreator/qmldesigner/designericons.json
+++ b/share/qtcreator/qmldesigner/designericons.json
@@ -2,59 +2,145 @@
"ContextMenuArea": {
"size": "28x28",
"Off": {
- "Disabled": { "color": "DSiconColorDisabled" },
- "Hovered": { "color": "DSnavigatorIconHover" },
- "Normal": { "color": "DSnavigatorIcon" },
- "Selected": { "color": "DSnavigatorIconSelected" }
+ "Disabled": { "color": "DStextColorDisabled" },
+ "Hovered": { "color": "DSpanelBackground" },
+ "Normal": { "color": "DStextColor" },
+ "Selected": { "color": "DStextSelectedTextColor" }
},
"On": {
- "Disabled": { "color": "DSiconColorDisabled" },
- "Hovered": { "color": "DSnavigatorIconHover" },
- "Normal": { "color": "DSnavigatorIcon" },
- "Selected": { "color": "DSnavigatorIconSelected" }
+ "Disabled": { "color": "DStextColorDisabled" },
+ "Hovered": { "color": "DSsubPanelBackground" },
+ "Normal": { "color": "DStextColor" },
+ "Selected": { "color": "DStextSelectedTextColor" }
}
},
"AddMouseAreaIcon": {
- "iconName": "s_mouseArea"
+ "iconName": "mouseArea_small"
+ },
+ "AlignCameraToViewIcon": {
+ "iconName": "alignToCamera_small"
+ },
+ "AlignViewToCameraIcon": {
+ "iconName": "alignToObject_small"
},
"AnchorsIcon": {
- "iconName": "s_anchors"
+ "iconName": "anchors_small"
},
"AnnotationIcon": {
- "iconName": "s_annotations"
+ "iconName": "annotations_small"
},
"ArrangeIcon": {
- "iconName": "s_arrange"
+ "iconName": "arrange_small"
+ },
+ "CameraIcon": {
+ "iconName": "camera_small"
+ },
+ "CameraOrthographicIcon": {
+ "iconName": "orthCam_small"
+ },
+ "CameraPerspectiveIcon": {
+ "iconName": "perspectiveCam_small"
},
"ConnectionsIcon": {
- "iconName": "s_connections"
+ "iconName": "connection_small"
+ },
+ "CopyIcon": {
+ "iconName": "copy_small"
+ },
+ "CreateIcon": {
+ "iconName": "create_small"
+ },
+ "DeleteIcon": {
+ "iconName": "delete_small"
+ },
+ "DuplicateIcon": {
+ "iconName": "duplicate_small"
+ },
+ "EditComponentIcon": {
+ "iconName": "editComponent_small"
},
"EditIcon": {
- "iconName": "s_edit"
+ "iconName": "edit_small"
},
"EnterComponentIcon": {
- "iconName": "s_enterComponent"
+ "iconName": "editComponent_small"
},
"EventListIcon": {
- "iconName": "s_eventList"
+ "iconName": "events_small"
+ },
+ "FitSelectedIcon": {
+ "iconName": "fitSelected_small"
},
"GroupSelectionIcon": {
- "iconName": "s_group"
+ "iconName": "group_small"
+ },
+ "ImportedModelsIcon": {
+ "iconName": "importedModels_small"
},
"LayoutsIcon": {
- "iconName": "s_layouts"
+ "iconName": "layouts_small"
+ },
+ "LightIcon": {
+ "Off": {
+ "iconName": "editLightOff_medium"
+ },
+ "On": {
+ "iconName": "editLightOn_medium"
+ }
+ },
+ "LightDirectionalIcon": {
+ "iconName": "directionalLight_small"
+ },
+ "LightPointIcon": {
+ "iconName": "pointLight_small"
+ },
+ "LightSpotIcon": {
+ "iconName": "spotLight_small"
},
"MakeComponentIcon": {
- "iconName": "s_component"
+ "iconName": "createComponent_small"
+ },
+ "MaterialIcon": {
+ "iconName": "material_medium"
},
"MergeWithTemplateIcon": {
- "iconName": "s_merging"
+ "iconName": "merge_small"
+ },
+ "MinimalDownArrowIcon" : {
+ "iconName": "upDownSquare2"
+ },
+ "ModelConeIcon": {
+ "iconName": "cone_small"
+ },
+ "ModelCubeIcon": {
+ "iconName": "cube_small"
+ },
+ "ModelCylinderIcon": {
+ "iconName": "cylinder_small"
+ },
+ "ModelPlaneIcon": {
+ "iconName": "plane_small"
+ },
+ "ModelSphereIcon": {
+ "iconName": "sphere_small"
+ },
+ "ParentIcon": {
+ "iconName": "selectParent_small"
+ },
+ "PasteIcon": {
+ "iconName": "paste_small"
},
"PositionsersIcon": {
- "iconName": "s_positioners"
+ "iconName": "positioners_small"
+ },
+ "PrimitivesIcon": {
+ "iconName": "cube_small"
+ },
+ "ResetViewIcon": {
+ "iconName": "reload_medium"
},
"SelecionIcon": {
- "iconName": "s_selection"
+ "iconName": "selection_small"
},
"ShowBoundsIcon": {
"Off": {
@@ -65,10 +151,26 @@
}
},
"SnappingIcon": {
- "iconName": "s_snapping"
+ "iconName": "snapping_small"
+ },
+ "SimpleCheckIcon": {
+ "Off": {
+ "iconName": "transparent"
+ },
+ "On": {
+ "iconName": "tickMark_small"
+ }
},
"TimelineIcon": {
- "iconName": "s_timeline"
+ "iconName": "timeline_small"
+ },
+ "ToggleGroupIcon": {
+ "Off": {
+ "iconName": "selectOutline_medium"
+ },
+ "On": {
+ "iconName": "selectFill_medium"
+ }
},
"VisibilityIcon": {
"Off": {
diff --git a/share/qtcreator/qmldesigner/feedback/FeedbackPopup.qml b/share/qtcreator/qmldesigner/feedback/FeedbackPopup.qml
new file mode 100644
index 0000000000..8497626798
--- /dev/null
+++ b/share/qtcreator/qmldesigner/feedback/FeedbackPopup.qml
@@ -0,0 +1,143 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Window 2.15
+
+Rectangle {
+ id: root_rectangle
+
+ property int rating: 0
+
+ signal submitFeedback(string feedback, int rating)
+ signal closeClicked()
+
+ width: 740
+ height: 382
+ border { color: "#0094ce"; width: 1 }
+
+ Text {
+ id: h1
+ objectName: "title"
+ color: "#333333"
+ text: "Enjoying Qt Design Studio?"
+ font { family: "Titillium"; pixelSize: 21 }
+ anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: 50 }
+ }
+
+ Text {
+ id: h2
+ color: "#333333"
+ text: "Select the level of your satisfaction."
+ font { family: "Titillium"; pixelSize: 21 }
+ anchors { horizontalCenter: parent.horizontalCenter; top: h1.bottom; topMargin: 12 }
+ }
+
+ Row {
+ id: starRow
+ width: 246; height: 42; spacing: 6.5
+ anchors { horizontalCenter: parent.horizontalCenter; top: h2.bottom; topMargin: 32 }
+
+ Repeater { // create the stars
+ id: rep
+ model: 5
+ Image {
+ source: "star_empty.png"
+ fillMode: Image.PreserveAspectFit
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ for (var i = 0; i < 5; ++i) {
+ rep.itemAt(i).source = i <= index ? "star_filled.png"
+ : "star_empty.png"
+ }
+ rating = index + 1
+ }
+ }
+ }
+ }
+ }
+
+ ScrollView {
+ id: scroll_textarea
+ width: 436
+ height: 96
+ anchors { horizontalCenter: parent.horizontalCenter; top: starRow.bottom; topMargin: 28 }
+
+ TextEdit {
+ id: textarea
+ width: 426
+ height: 90
+ color: "#333333";
+ font { pixelSize: 14; family: "Titillium" }
+ wrapMode: Text.Wrap
+ property string placeholderText: "We highly appreciate additional feedback.\nBouquets, brickbats, or suggestions, all feedback is welcome!"
+
+ Text {
+ text: textarea.placeholderText
+ color: "gray"
+ visible: !textarea.text
+ font: parent.font
+ }
+ }
+
+ background: Rectangle {
+ border { color: "#e6e6e6"; width: 1 }
+ }
+ }
+
+ Row {
+ id: buttonRow
+ anchors { horizontalCenter: parent.horizontalCenter; top: scroll_textarea.bottom; topMargin: 28 }
+ spacing: 10
+
+ Button {
+ id: buttonSkip
+ width: 80
+ height: 28
+
+ contentItem: Text {
+ text: "Skip"
+ color: parent.hovered ? Qt.darker("#999999", 1.9) : Qt.darker("#999999", 1.2)
+ font { family: "Titillium"; pixelSize: 14 }
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ background: Rectangle {
+ anchors.fill: parent
+ color: "#ffffff"
+ border { color: "#999999"; width: 1 }
+ }
+
+ onClicked: root_rectangle.closeClicked()
+ }
+
+ Button {
+ id: buttonSubmit
+
+ width: 80
+ height: 28
+ enabled: rating > 0
+
+ contentItem: Text {
+ text: "Submit";
+ color: enabled ? "white" : Qt.lighter("#999999", 1.3)
+ font { family: "Titillium"; pixelSize: 14 }
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ background: Rectangle {
+ anchors.fill: parent
+ color: enabled ? parent.hovered ? Qt.lighter("#0094ce", 1.2) : "#0094ce" : "white"
+ border { color: enabled ? "#999999" : Qt.lighter("#999999", 1.3); width: 1 }
+ }
+
+ onClicked: {
+ root_rectangle.submitFeedback(textarea.text, rating);
+ root_rectangle.closeClicked();
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/feedback/star_empty.png b/share/qtcreator/qmldesigner/feedback/star_empty.png
new file mode 100644
index 0000000000..f9ab459a02
--- /dev/null
+++ b/share/qtcreator/qmldesigner/feedback/star_empty.png
Binary files differ
diff --git a/share/qtcreator/qmldesigner/feedback/star_empty@2x.png b/share/qtcreator/qmldesigner/feedback/star_empty@2x.png
new file mode 100644
index 0000000000..d5b772517f
--- /dev/null
+++ b/share/qtcreator/qmldesigner/feedback/star_empty@2x.png
Binary files differ
diff --git a/share/qtcreator/qmldesigner/feedback/star_filled.png b/share/qtcreator/qmldesigner/feedback/star_filled.png
new file mode 100644
index 0000000000..f213d09c84
--- /dev/null
+++ b/share/qtcreator/qmldesigner/feedback/star_filled.png
Binary files differ
diff --git a/share/qtcreator/qmldesigner/feedback/star_filled@2x.png b/share/qtcreator/qmldesigner/feedback/star_filled@2x.png
new file mode 100644
index 0000000000..19e8491868
--- /dev/null
+++ b/share/qtcreator/qmldesigner/feedback/star_filled@2x.png
Binary files differ
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml
index c77f995634..585c6a6d14 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AddModuleView.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls 2.15
import QtQuickDesignerTheme 1.0
import HelperWidgets 2.0
import StudioTheme 1.0 as StudioTheme
+import ItemLibraryBackend
Column {
id: root
@@ -43,7 +44,7 @@ Column {
spacing: 2
Repeater {
- model: addModuleModel
+ model: ItemLibraryBackend.addModuleModel
delegate: Rectangle {
id: itemBackground
@@ -67,7 +68,7 @@ Column {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
- onClicked: rootView.handleAddImport(index)
+ onClicked: ItemLibraryBackend.rootView.handleAddImport(index)
enabled: !isSeparator
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
index 524aca6841..1178de4a87 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls 2.15
import QtQuickDesignerTheme 1.0
import HelperWidgets 2.0
import StudioTheme 1.0 as StudioTheme
+import ItemLibraryBackend
Item {
id: delegateRoot
@@ -34,8 +35,8 @@ Item {
anchors.topMargin: styleConstants.cellVerticalMargin
anchors.horizontalCenter: parent.horizontalCenter
- width: itemLibraryIconWidth // to be set in Qml context
- height: itemLibraryIconHeight // to be set in Qml context
+ width: ItemLibraryBackend.itemLibraryIconWidth // to be set in Qml context
+ height: ItemLibraryBackend.itemLibraryIconHeight // to be set in Qml context
source: itemLibraryIconPath // to be set by model
// Icons generated for components can change if the component is edited,
@@ -75,12 +76,12 @@ Item {
allowTooltip = false
hide()
if (mouse.button === Qt.LeftButton)
- rootView.startDragAndDrop(itemLibraryEntry, mapToGlobal(mouse.x, mouse.y))
+ ItemLibraryBackend.rootView.startDragAndDrop(itemLibraryEntry, mapToGlobal(mouse.x, mouse.y))
}
onDoubleClicked: (mouse)=> {
if (mouse.button === Qt.LeftButton && itemComponentSource) {
hide()
- rootView.goIntoComponent(itemComponentSource)
+ ItemLibraryBackend.rootView.goIntoComponent(itemComponentSource)
}
}
}
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml
index 9d8e677b5b..05d8d6db9c 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml
@@ -1,24 +1,20 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Layouts 1.15
+import QtQuick
+import QtQuick.Layouts
import QtQuickDesignerTheme 1.0
-import HelperWidgets 2.0
+import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
-
+import ItemLibraryBackend
/* The view displaying the item grid.
-
The following Qml context properties have to be set:
- ItemLibraryModel itemLibraryModel
- int itemLibraryIconWidth
- int itemLibraryIconHeight
- ItemLibraryWidget rootView
- QColor highlightColor
-
itemLibraryModel structure:
-
itemLibraryModel [
ItemLibraryImport {
string importName
@@ -26,13 +22,11 @@ itemLibraryModel [
bool importVisible
bool importUsed
bool importExpanded
-
list categoryModel [
ItemLibraryCategory {
string categoryName
bool categoryVisible
bool categoryExpanded
-
list itemModel [
ItemLibraryItem {
string itemName
@@ -50,10 +44,8 @@ itemLibraryModel [
... more imports
]
*/
-
Item {
id: itemsView
-
property string importToRemove
property string importToAdd
property string componentSource
@@ -62,192 +54,182 @@ Item {
property bool isHorizontalView: false
property bool isAddModuleView: false
+ property var tooltipBackend: ItemLibraryBackend.tooltipBackend
+
// Called also from C++ to close context menu on focus out
function closeContextMenu()
{
moduleContextMenu.close()
itemContextMenu.close()
}
-
// Called from C++
function clearSearchFilter()
{
searchBox.clear();
}
-
// Called also from C++
function switchToComponentsView()
{
- isAddModuleView = false
+ ItemLibraryBackend.isAddModuleView = false
}
-
onWidthChanged: {
- itemsView.isHorizontalView = itemsView.width > widthLimit
+ itemsView.isHorizontalView = itemsView.width > ItemLibraryBackend.widthLimit
}
-
onIsHorizontalViewChanged: closeContextMenu()
-
Item {
id: styleConstants
property int textWidth: 58
property int textHeight: Theme.smallFontPixelSize() * 2
-
property int cellHorizontalMargin: 1
property int cellVerticalSpacing: 2
property int cellVerticalMargin: 4
-
// the following depend on the actual shape of the item delegate
property int cellWidth: styleConstants.textWidth + 2 * styleConstants.cellHorizontalMargin
- property int cellHeight: itemLibraryIconHeight + styleConstants.textHeight +
+ property int cellHeight: ItemLibraryBackend.itemLibraryIconHeight + styleConstants.textHeight +
2 * styleConstants.cellVerticalMargin + styleConstants.cellVerticalSpacing
-
StudioControls.Menu {
id: moduleContextMenu
-
StudioControls.MenuItem {
text: qsTr("Remove Module")
visible: itemsView.currentCategory === null
height: visible ? implicitHeight : 0
- enabled: itemsView.importToRemove && !rootView.subCompEditMode
- onTriggered: rootView.removeImport(itemsView.importToRemove)
+ enabled: itemsView.importToRemove && !ItemLibraryBackend.rootView.subCompEditMode
+ onTriggered: ItemLibraryBackend.rootView.removeImport(itemsView.importToRemove)
}
-
StudioControls.MenuSeparator {
- visible: itemsView.currentCategory === null && !rootView.searchActive
+ visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
height: visible ? StudioTheme.Values.border : 0
}
-
StudioControls.MenuItem {
text: qsTr("Expand All")
- visible: itemsView.currentCategory === null && !rootView.searchActive
+ visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
height: visible ? implicitHeight : 0
- onTriggered: itemLibraryModel.expandAll()
+ onTriggered: ItemLibraryBackend.itemLibraryModel.expandAll()
}
-
StudioControls.MenuItem {
text: qsTr("Collapse All")
- visible: itemsView.currentCategory === null && !rootView.searchActive
+ visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
height: visible ? implicitHeight : 0
- onTriggered: itemLibraryModel.collapseAll()
+ onTriggered: ItemLibraryBackend.itemLibraryModel.collapseAll()
}
-
StudioControls.MenuSeparator {
- visible: itemsView.currentCategory === null && !rootView.searchActive
+ visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
height: visible ? StudioTheme.Values.border : 0
}
-
StudioControls.MenuItem {
text: qsTr("Hide Category")
visible: itemsView.currentCategory
height: visible ? implicitHeight : 0
- onTriggered: itemLibraryModel.hideCategory(itemsView.currentImport.importUrl,
+ onTriggered: ItemLibraryBackend.itemLibraryModel.hideCategory(itemsView.currentImport.importUrl,
itemsView.currentCategory.categoryName)
}
-
StudioControls.MenuSeparator {
visible: itemsView.currentCategory
height: visible ? StudioTheme.Values.border : 0
}
-
StudioControls.MenuItem {
text: qsTr("Show Module Hidden Categories")
- visible: !rootView.searchActive
+ visible: !ItemLibraryBackend.rootView.searchActive
enabled: itemsView.currentImport && !itemsView.currentImport.allCategoriesVisible
height: visible ? implicitHeight : 0
- onTriggered: itemLibraryModel.showImportHiddenCategories(itemsView.currentImport.importUrl)
+ onTriggered: ItemLibraryBackend.itemLibraryModel.showImportHiddenCategories(itemsView.currentImport.importUrl)
}
-
StudioControls.MenuItem {
text: qsTr("Show All Hidden Categories")
- visible: !rootView.searchActive
- enabled: itemLibraryModel.isAnyCategoryHidden
+ visible: !ItemLibraryBackend.rootView.searchActive
+ enabled: ItemLibraryBackend.itemLibraryModel.isAnyCategoryHidden
height: visible ? implicitHeight : 0
- onTriggered: itemLibraryModel.showAllHiddenCategories()
+ onTriggered: ItemLibraryBackend.itemLibraryModel.showAllHiddenCategories()
}
}
-
StudioControls.Menu {
id: itemContextMenu
// Workaround for menu item implicit width not properly propagating to menu
width: Math.max(importMenuItem.implicitWidth, openSourceItem.implicitWidth)
-
StudioControls.MenuItem {
id: importMenuItem
text: qsTr("Add Module: ") + itemsView.importToAdd
visible: itemsView.importToAdd
height: visible ? implicitHeight : 0
- onTriggered: rootView.addImportForItem(itemsView.importToAdd)
+ onTriggered: ItemLibraryBackend.rootView.addImportForItem(itemsView.importToAdd)
}
-
StudioControls.MenuItem {
id: openSourceItem
- text: qsTr("Go into Component")
+ text: qsTr("Edit Component")
visible: itemsView.componentSource
height: visible ? implicitHeight : 0
- onTriggered: rootView.goIntoComponent(itemsView.componentSource)
+ onTriggered: ItemLibraryBackend.rootView.goIntoComponent(itemsView.componentSource)
}
}
}
-
Column {
id: col
width: parent.width
height: parent.height
- y: 5
spacing: 5
- Row {
+ Rectangle {
width: parent.width
+ height: StudioTheme.Values.doubleToolbarHeight
+ color: StudioTheme.Values.themeToolbarBackground
- StudioControls.SearchBox {
- id: searchBox
-
- width: parent.width - addModuleButton.width - 5
-
- onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText)
- }
+ Column {
+ anchors.fill: parent
+ anchors.topMargin: 6
+ anchors.bottomMargin: 6
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ spacing: 12
+
+ StudioControls.SearchBox {
+ id: searchBox
+ width: parent.width
+ style: StudioTheme.Values.searchControlStyle
- IconButton {
- id: addModuleButton
- anchors.verticalCenter: parent.verticalCenter
- tooltip: qsTr("Add a module.")
- icon: StudioTheme.Constants.plus
- buttonSize: parent.height
+ onSearchChanged: (searchText) => ItemLibraryBackend.rootView.handleSearchFilterChanged(searchText)
+ }
- onClicked: isAddModuleView = true
+ Row {
+ width: parent.width
+ height: StudioTheme.Values.toolbarHeight
+ spacing: 6
+
+ HelperWidgets.AbstractButton {
+ id: addModuleButton
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.add_medium
+ tooltip: qsTr("Add a module.")
+ onClicked: isAddModuleView = true
+ }
+ }
}
}
Loader {
id: loader
-
width: col.width
height: col.height - y - 5
sourceComponent: isAddModuleView ? addModuleView
: itemsView.isHorizontalView ? horizontalView : verticalView
}
}
-
Component {
id: verticalView
-
- ScrollView {
+ HelperWidgets.ScrollView {
id: verticalScrollView
anchors.fill: parent
clip: true
- interactive: !itemContextMenu.opened && !moduleContextMenu.opened
-
+ interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging
onContentHeightChanged: {
var maxPosition = Math.max(contentHeight - verticalScrollView.height, 0)
if (contentY > maxPosition)
contentY = maxPosition
}
-
Column {
spacing: 2
Repeater {
- model: itemLibraryModel // to be set in Qml context
- delegate: Section {
+ model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context
+ delegate: HelperWidgets.Section {
width: itemsView.width -
(verticalScrollView.verticalScrollBarVisible
? verticalScrollView.verticalThickness : 0)
@@ -263,6 +245,8 @@ Item {
expanded: importExpanded
expandOnClick: false
useDefaulContextMenu: false
+ category: "ItemsView"
+
onToggleExpand: {
if (categoryModel.rowCount() > 0)
importExpanded = !importExpanded
@@ -273,13 +257,12 @@ Item {
itemsView.currentCategory = null
moduleContextMenu.popup()
}
-
Column {
spacing: 2
property var currentImportModel: model // allows accessing the import model from inside the category section
Repeater {
model: categoryModel
- delegate: Section {
+ delegate: HelperWidgets.Section {
width: itemsView.width -
(verticalScrollView.verticalScrollBarVisible
? verticalScrollView.verticalThickness : 0)
@@ -296,24 +279,22 @@ Item {
expandOnClick: false
onToggleExpand: categoryExpanded = !categoryExpanded
useDefaulContextMenu: false
+ category: "ItemsView"
+
onShowContextMenu: {
- if (!rootView.searchActive) {
+ if (!ItemLibraryBackend.rootView.searchActive) {
itemsView.currentCategory = model
itemsView.currentImport = parent.currentImportModel
moduleContextMenu.popup()
}
}
-
Grid {
id: itemGrid
-
property real actualWidth: parent.width - itemGrid.leftPadding - itemGrid.rightPadding
-
leftPadding: 6
rightPadding: 6
columns: itemGrid.actualWidth / styleConstants.cellWidth
rowSpacing: 7
-
Repeater {
model: itemModel
delegate: ItemDelegate {
@@ -340,32 +321,28 @@ Item {
}
}
}
-
Component {
id: horizontalView
-
Row {
leftPadding: 5
-
- ScrollView {
+ HelperWidgets.ScrollView {
id: horizontalScrollView
width: 270
height: parent.height
clip: true
- interactive: !itemContextMenu.opened && !moduleContextMenu.opened
+ interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging
onContentHeightChanged: {
var maxPosition = Math.max(contentHeight - horizontalScrollView.height, 0)
if (contentY > maxPosition)
contentY = maxPosition
}
-
Column {
width: parent.width
spacing: 2
Repeater {
- model: itemLibraryModel // to be set in Qml context
- delegate: Section {
+ model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context
+ delegate: HelperWidgets.Section {
width: 265 -
(horizontalScrollView.verticalScrollBarVisible
? horizontalScrollView.verticalThickness : 0)
@@ -381,6 +358,8 @@ Item {
expanded: importExpanded
expandOnClick: false
useDefaulContextMenu: false
+ category: "ItemsView"
+
onToggleExpand: {
if (categoryModel.rowCount() > 0)
importExpanded = !importExpanded
@@ -391,7 +370,6 @@ Item {
itemsView.currentCategory = null
moduleContextMenu.popup()
}
-
Column {
spacing: 2
property var currentImportModel: model // allows accessing the import model from inside the category section
@@ -409,7 +387,6 @@ Item {
? StudioTheme.Values.themeControlBackgroundHover
: categoryMouseArea.containsMouse ? Qt.darker(StudioTheme.Values.themeControlBackgroundHover, 1.5)
: StudioTheme.Values.themeControlBackground
-
Text {
anchors.fill: parent
text: categoryName
@@ -419,19 +396,16 @@ Item {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
-
MouseArea {
id: categoryMouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
-
onClicked: (mouse) => {
- itemLibraryModel.selectImportCategory(parent.parent.currentImportModel.importUrl, model.index)
-
+ ItemLibraryBackend.itemLibraryModel.selectImportCategory(parent.parent.currentImportModel.importUrl, model.index)
if (mouse.button === Qt.RightButton
&& categoryModel.rowCount() !== 1
- && !rootView.searchActive) {
+ && !ItemLibraryBackend.rootView.searchActive) {
itemsView.currentCategory = model
itemsView.currentImport = parent.parent.currentImportModel
moduleContextMenu.popup()
@@ -445,39 +419,36 @@ Item {
}
}
}
-
Rectangle { // separator between import/category column and item grid
id: separatingLine
height: itemsView.height - 10
width: 1
color: StudioTheme.Values.themeControlOutline
}
-
- ScrollView {
+ HelperWidgets.ScrollView {
id: itemScrollView
width: itemsView.width - 275
height: itemsView.height
+ interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging
+
onContentHeightChanged: {
var maxPosition = Math.max(contentHeight - itemScrollView.height, 0)
if (contentY > maxPosition)
contentY = maxPosition
}
-
Grid {
id: hItemGrid
property real actualWidth: itemsView.width - 294
-
leftPadding: 9
rightPadding: 9
bottomPadding: 15
columns: hItemGrid.actualWidth / styleConstants.cellWidth
rowSpacing: 7
-
Repeater {
- model: itemLibraryModel.itemsModel
+ model: ItemLibraryBackend.itemLibraryModel.itemsModel
delegate: ItemDelegate {
visible: itemVisible
- textColor: itemLibraryModel.importUnimportedSelected
+ textColor: ItemLibraryBackend.itemLibraryModel.importUnimportedSelected
? StudioTheme.Values.themeUnimportedModuleColor : StudioTheme.Values.themeTextColor
width: styleConstants.cellWidth
height: styleConstants.cellHeight
@@ -494,10 +465,8 @@ Item {
}
}
}
-
Component {
id: addModuleView
-
AddModuleView {
onBack: isAddModuleView = false
}
diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml
index fbcaa06489..620feb3b11 100644
--- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml
+++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml
@@ -1,14 +1,16 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 2.15
+import QtQuick
import QtQuickDesignerTheme 1.0
-import HelperWidgets 2.0
+import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
+import MaterialBrowserBackend
Item {
id: root
+ focus: true
readonly property int cellWidth: 100
readonly property int cellHeight: 120
@@ -16,12 +18,16 @@ Item {
&& materialBrowserModel.hasQuick3DImport
property var currMaterialItem: null
+ property var rootView: MaterialBrowserBackend.rootView
+ property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel
+ property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
// Called also from C++ to close context menu on focus out
function closeContextMenu()
{
ctxMenu.close()
ctxMenuTextures.close()
+ HelperWidgets.Controller.closeContextMenu()
}
// Called from C++ to refresh a preview material after it changes
@@ -38,9 +44,192 @@ Item {
searchBox.clear();
}
+ function nextVisibleItem(idx, count, itemModel)
+ {
+ if (count === 0)
+ return idx
+
+ let pos = 0
+ let newIdx = idx
+ let direction = 1
+ if (count < 0)
+ direction = -1
+
+ while (pos !== count) {
+ newIdx += direction
+ if (newIdx < 0 || newIdx >= itemModel.rowCount())
+ return -1
+ if (itemModel.isVisible(newIdx))
+ pos += direction
+ }
+
+ return newIdx
+ }
+
+ function visibleItemCount(itemModel)
+ {
+ let curIdx = 0
+ let count = 0
+
+ for (; curIdx < itemModel.rowCount(); ++curIdx) {
+ if (itemModel.isVisible(curIdx))
+ ++count
+ }
+
+ return count
+ }
+
+ function rowIndexOfItem(idx, rowSize, itemModel)
+ {
+ if (rowSize === 1)
+ return 1
+
+ let curIdx = 0
+ let count = -1
+
+ while (curIdx <= idx) {
+ if (curIdx >= itemModel.rowCount())
+ break
+ if (itemModel.isVisible(curIdx))
+ ++count
+ ++curIdx
+ }
+
+ return count % rowSize
+ }
+
+ function selectNextVisibleItem(delta)
+ {
+ if (searchBox.activeFocus)
+ return
+
+ let targetIdx = -1
+ let newTargetIdx = -1
+ let origRowIdx = -1
+ let rowIdx = -1
+ let matSecFocused = rootView.materialSectionFocused && materialsSection.expanded
+ let texSecFocused = !rootView.materialSectionFocused && texturesSection.expanded
+
+ if (delta < 0) {
+ if (matSecFocused) {
+ targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex,
+ delta, materialBrowserModel)
+ if (targetIdx >= 0)
+ materialBrowserModel.selectMaterial(targetIdx)
+ } else if (texSecFocused) {
+ targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
+ delta, materialBrowserTexturesModel)
+ if (targetIdx >= 0) {
+ materialBrowserTexturesModel.selectTexture(targetIdx)
+ } else if (!materialBrowserModel.isEmpty && materialsSection.expanded) {
+ targetIdx = nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel)
+ if (targetIdx >= 0) {
+ if (delta !== -1) {
+ // Try to match column when switching between materials/textures
+ origRowIdx = rowIndexOfItem(materialBrowserTexturesModel.selectedIndex,
+ -delta, materialBrowserTexturesModel)
+ if (visibleItemCount(materialBrowserModel) > origRowIdx) {
+ rowIdx = rowIndexOfItem(targetIdx, -delta, materialBrowserModel)
+ if (rowIdx >= origRowIdx) {
+ newTargetIdx = nextVisibleItem(targetIdx,
+ -(rowIdx - origRowIdx),
+ materialBrowserModel)
+ } else {
+ newTargetIdx = nextVisibleItem(targetIdx,
+ -(-delta - origRowIdx + rowIdx),
+ materialBrowserModel)
+ }
+ } else {
+ newTargetIdx = nextVisibleItem(materialBrowserModel.rowCount(),
+ -1, materialBrowserModel)
+ }
+ if (newTargetIdx >= 0)
+ targetIdx = newTargetIdx
+ }
+ materialBrowserModel.selectMaterial(targetIdx)
+ rootView.focusMaterialSection(true)
+ }
+ }
+ }
+ } else if (delta > 0) {
+ if (matSecFocused) {
+ targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex,
+ delta, materialBrowserModel)
+ if (targetIdx >= 0) {
+ materialBrowserModel.selectMaterial(targetIdx)
+ } else if (!materialBrowserTexturesModel.isEmpty && texturesSection.expanded) {
+ targetIdx = nextVisibleItem(-1, 1, materialBrowserTexturesModel)
+ if (targetIdx >= 0) {
+ if (delta !== 1) {
+ // Try to match column when switching between materials/textures
+ origRowIdx = rowIndexOfItem(materialBrowserModel.selectedIndex,
+ delta, materialBrowserModel)
+ if (visibleItemCount(materialBrowserTexturesModel) > origRowIdx) {
+ if (origRowIdx > 0) {
+ newTargetIdx = nextVisibleItem(targetIdx, origRowIdx,
+ materialBrowserTexturesModel)
+ }
+ } else {
+ newTargetIdx = nextVisibleItem(materialBrowserTexturesModel.rowCount(),
+ -1, materialBrowserTexturesModel)
+ }
+ if (newTargetIdx >= 0)
+ targetIdx = newTargetIdx
+ }
+ materialBrowserTexturesModel.selectTexture(targetIdx)
+ rootView.focusMaterialSection(false)
+ }
+ }
+ } else if (texSecFocused) {
+ targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
+ delta, materialBrowserTexturesModel)
+ if (targetIdx >= 0)
+ materialBrowserTexturesModel.selectTexture(targetIdx)
+ }
+ }
+ }
+
+ Keys.enabled: true
+ Keys.onDownPressed: {
+ selectNextVisibleItem(gridMaterials.columns)
+ }
+
+ Keys.onUpPressed: {
+ selectNextVisibleItem(-gridMaterials.columns)
+ }
+
+ Keys.onLeftPressed: {
+ selectNextVisibleItem(-1)
+ }
+
+ Keys.onRightPressed: {
+ selectNextVisibleItem(1)
+ }
+
+ function handleEnterPress()
+ {
+ if (searchBox.activeFocus)
+ return
+
+ if (!materialBrowserModel.isEmpty && rootView.materialSectionFocused && materialsSection.expanded)
+ materialBrowserModel.openMaterialEditor()
+ else if (!materialBrowserTexturesModel.isEmpty && !rootView.materialSectionFocused && texturesSection.expanded)
+ materialBrowserTexturesModel.openTextureEditor()
+ }
+
+ Keys.onEnterPressed: {
+ handleEnterPress()
+ }
+
+ Keys.onReturnPressed: {
+ handleEnterPress()
+ }
+
MouseArea {
id: focusGrabber
- anchors.fill: parent
+ y: searchBox.height
+ width: parent.width
+ height: parent.height - searchBox.height
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: (mouse) => {
forceActiveFocus() // Steal focus from name edit
@@ -52,7 +241,9 @@ Item {
MouseArea {
id: rootMouseArea
- anchors.fill: parent
+ y: topContent.height
+ width: parent.width
+ height: parent.height - topContent.height
acceptedButtons: Qt.RightButton
@@ -70,42 +261,322 @@ Item {
}
}
+ function ensureVisible(yPos, itemHeight)
+ {
+ let currentY = contentYBehavior.targetValue && scrollViewAnim.running
+ ? contentYBehavior.targetValue : scrollView.contentY
+
+ if (currentY > yPos) {
+ if (yPos < itemHeight)
+ scrollView.contentY = 0
+ else
+ scrollView.contentY = yPos
+ return true
+ } else {
+ let adjustedY = yPos + itemHeight - scrollView.height + 8
+ if (currentY < adjustedY) {
+ if (scrollView.contentHeight - scrollView.height < adjustedY )
+ scrollView.contentY = scrollView.contentHeight - scrollView.height
+ else
+ scrollView.contentY = adjustedY
+ return true
+ }
+ }
+
+ return false
+ }
+
+ function ensureSelectedVisible()
+ {
+ if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem
+ && materialBrowserModel.isVisible(materialBrowserModel.selectedIndex)) {
+ return ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y,
+ root.currMaterialItem.height)
+ } else if (!rootView.materialSectionFocused && texturesSection.expanded) {
+ let currItem = texturesRepeater.itemAt(materialBrowserTexturesModel.selectedIndex)
+ if (currItem && materialBrowserTexturesModel.isVisible(materialBrowserTexturesModel.selectedIndex))
+ return ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y, currItem.height)
+ } else {
+ return ensureVisible(0, 90)
+ }
+ }
+
+ Timer {
+ id: ensureTimer
+ interval: 20
+ repeat: true
+ triggeredOnStart: true
+
+ onTriggered: {
+ // Redo until ensuring didn't change things
+ if (!root.ensureSelectedVisible()) {
+ stop()
+ interval = 20
+ triggeredOnStart = true
+ }
+ }
+ }
+
+ function startDelayedEnsureTimer(delay)
+ {
+ // Ensuring visibility immediately in some cases like before new search results are rendered
+ // causes mapToItem return incorrect values, leading to undesirable flicker,
+ // so delay ensuring visibility a bit.
+ ensureTimer.interval = delay
+ ensureTimer.triggeredOnStart = false
+ ensureTimer.restart()
+ }
+
Connections {
target: materialBrowserModel
- function onSelectedIndexChanged() {
+ function onSelectedIndexChanged()
+ {
// commit rename upon changing selection
if (root.currMaterialItem)
root.currMaterialItem.commitRename();
root.currMaterialItem = materialRepeater.itemAt(materialBrowserModel.selectedIndex);
+
+ ensureTimer.start()
+ }
+
+ function onIsEmptyChanged()
+ {
+ ensureTimer.start()
+ }
+ }
+
+ Connections {
+ target: materialBrowserTexturesModel
+
+ function onSelectedIndexChanged()
+ {
+ ensureTimer.start()
+ }
+
+ function onIsEmptyChanged()
+ {
+ ensureTimer.start()
+ }
+ }
+
+ Connections {
+ target: rootView
+
+ function onMaterialSectionFocusedChanged()
+ {
+ ensureTimer.start()
}
}
MaterialBrowserContextMenu {
id: ctxMenu
+ onClosed: {
+ if (restoreFocusOnClose)
+ scrollView.forceActiveFocus()
+ }
}
TextureBrowserContextMenu {
id: ctxMenuTextures
+ onClosed: {
+ scrollView.forceActiveFocus()
+ }
+ }
+
+ component DoubleButton: Rectangle {
+ id: doubleButton
+
+ signal clicked()
+
+ property alias icon: iconLabel.text
+ property alias tooltip: mouseArea.tooltip
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle
+
+ width: doubleButton.style.squareControlSize.width * 2
+ height: doubleButton.style.squareControlSize.height
+ radius: StudioTheme.Values.smallRadius
+
+ Row {
+ id: contentRow
+ spacing: 0
+
+ Text {
+ id: iconLabel
+ width: doubleButton.style.squareControlSize.width
+ height: doubleButton.height
+ text: StudioTheme.Constants.material_medium
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: doubleButton.style.baseIconFontSize
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Text {
+ id: plusLabel
+ width: doubleButton.style.squareControlSize.width
+ height: doubleButton.height
+ text: StudioTheme.Constants.add_medium
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: doubleButton.style.baseIconFontSize
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ HelperWidgets.ToolTipArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: doubleButton.clicked()
+ }
+
+ states: [
+ State {
+ name: "default"
+ when: doubleButton.enabled && !mouseArea.containsMouse && !mouseArea.pressed
+ PropertyChanges {
+ target: doubleButton
+ color: doubleButton.style.background.idle
+ border.color: doubleButton.style.border.idle
+ }
+ PropertyChanges {
+ target: iconLabel
+ color: doubleButton.style.icon.idle
+ }
+ PropertyChanges {
+ target: plusLabel
+ color: doubleButton.style.icon.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: doubleButton.enabled && mouseArea.containsMouse && !mouseArea.pressed
+ PropertyChanges {
+ target: doubleButton
+ color: doubleButton.style.background.hover
+ border.color: doubleButton.style.border.hover
+ }
+ PropertyChanges {
+ target: iconLabel
+ color: doubleButton.style.icon.hover
+ }
+ PropertyChanges {
+ target: plusLabel
+ color: doubleButton.style.icon.hover
+ }
+ },
+ State {
+ name: "pressed"
+ when: doubleButton.enabled && mouseArea.containsMouse && mouseArea.pressed
+ PropertyChanges {
+ target: doubleButton
+ color: doubleButton.style.interaction
+ border.color: doubleButton.style.interaction
+ }
+ PropertyChanges {
+ target: iconLabel
+ color: doubleButton.style.icon.interaction
+ }
+ PropertyChanges {
+ target: plusLabel
+ color: doubleButton.style.icon.interaction
+ }
+ },
+ State {
+ name: "pressedButNotHovered"
+ when: doubleButton.enabled && !mouseArea.containsMouse && mouseArea.pressed
+ extend: "hover"
+ },
+ State {
+ name: "disable"
+ when: !doubleButton.enabled
+ PropertyChanges {
+ target: doubleButton
+ color: doubleButton.style.background.disabled
+ border.color: doubleButton.style.border.disabled
+ }
+ PropertyChanges {
+ target: iconLabel
+ color: doubleButton.style.icon.disabled
+ }
+ PropertyChanges {
+ target: plusLabel
+ color: doubleButton.style.icon.disabled
+ }
+ }
+ ]
}
Column {
id: col
- y: 5
+ anchors.fill: parent
spacing: 5
- Row {
- width: root.width
- enabled: root.enableUiElements
+ Rectangle {
+ id: topContent
+ width: parent.width
+ height: StudioTheme.Values.doubleToolbarHeight
+ color: StudioTheme.Values.themeToolbarBackground
- StudioControls.SearchBox {
- id: searchBox
+ Column {
+ anchors.fill: parent
+ anchors.topMargin: 6
+ anchors.bottomMargin: 6
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ spacing: 12
+
+ StudioControls.SearchBox {
+ id: searchBox
+ width: parent.width
+ style: StudioTheme.Values.searchControlStyle
+
+ property string previousSearchText: ""
+ property bool materialsExpanded: true
+ property bool texturesExpanded: true
+
+ onSearchChanged: (searchText) => {
+ if (searchText !== "") {
+ if (previousSearchText === "") {
+ materialsExpanded = materialsSection.expanded
+ texturesExpanded = texturesSection.expanded
+ }
+ materialsSection.expanded = true
+ texturesSection.expanded = true
+ } else if (previousSearchText !== "") {
+ materialsSection.expanded = materialsExpanded
+ texturesSection.expanded = texturesExpanded
+ }
+ previousSearchText = searchText
+
+ root.startDelayedEnsureTimer(50)
+
+ rootView.handleSearchFilterChanged(searchText)
+ }
+ }
+
+ Row {
+ width: parent.width
+ height: StudioTheme.Values.toolbarHeight
+ spacing: 6
- width: root.width
+ DoubleButton {
+ id: addMaterial
+ icon: StudioTheme.Constants.material_medium
+ tooltip: qsTr("Add a Material.")
+ onClicked: materialBrowserModel.addNewMaterial()
+ enabled: root.enableUiElements
+ }
- onSearchChanged: (searchText) => {
- rootView.handleSearchFilterChanged(searchText)
+ DoubleButton {
+ id: addTexture
+ icon: StudioTheme.Constants.textures_medium
+ tooltip: qsTr("Add a Texture.")
+ onClicked: materialBrowserTexturesModel.addNewTexture()
+ enabled: root.enableUiElements
+ }
}
}
}
@@ -130,26 +601,35 @@ Item {
visible: text !== ""
}
- ScrollView {
+ HelperWidgets.ScrollView {
id: scrollView
width: root.width
- height: root.height - searchBox.height
+ height: root.height - topContent.height
clip: true
visible: root.enableUiElements
- interactive: !ctxMenu.opened && !ctxMenuTextures.opened
+ interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging
+
+ Behavior on contentY {
+ id: contentYBehavior
+ PropertyAnimation {
+ id: scrollViewAnim
+ easing.type: Easing.InOutQuad
+ }
+ }
Column {
Item {
width: root.width
height: materialsSection.height
- Section {
+ HelperWidgets.Section {
id: materialsSection
width: root.width
caption: qsTr("Materials")
dropEnabled: true
+ category: "MaterialBrowser"
onDropEnter: (drag) => {
drag.accepted = drag.formats[0] === "application/vnd.qtdesignstudio.bundlematerial"
@@ -160,13 +640,26 @@ Item {
materialsSection.highlight = false
}
- onDrop: {
+ onDrop: (drag) => {
+ drag.accept()
materialsSection.highlight = false
rootView.acceptBundleMaterialDrop()
}
+ onExpandedChanged: {
+ if (expanded) {
+ if (root.visibleItemCount(materialBrowserModel) > 0)
+ rootView.focusMaterialSection(true)
+ if (!searchBox.activeFocus)
+ scrollView.forceActiveFocus()
+ } else {
+ root.startDelayedEnsureTimer(300) // wait for section collapse animation
+ rootView.focusMaterialSection(false)
+ }
+ }
+
Grid {
- id: grid
+ id: gridMaterials
width: scrollView.width
leftPadding: 5
@@ -178,6 +671,12 @@ Item {
id: materialRepeater
model: materialBrowserModel
+
+ onItemRemoved: (index, item) => {
+ if (item === root.currMaterialItem)
+ root.currMaterialItem = null
+ }
+
delegate: MaterialItem {
width: root.cellWidth
height: root.cellHeight
@@ -208,36 +707,26 @@ Item {
width: root.width
}
}
-
- IconButton {
- id: addMaterialButton
-
- tooltip: qsTr("Add a material.")
-
- anchors.right: parent.right
- anchors.rightMargin: scrollView.verticalScrollBarVisible ? 10 : 0
- icon: StudioTheme.Constants.plus
- normalColor: "transparent"
- buttonSize: StudioTheme.Values.sectionHeadHeight
- onClicked: materialBrowserModel.addNewMaterial()
- enabled: root.enableUiElements
- }
}
Item {
width: root.width
height: texturesSection.height
- Section {
+ HelperWidgets.Section {
id: texturesSection
width: root.width
caption: qsTr("Textures")
+ category: "MaterialBrowser"
dropEnabled: true
onDropEnter: (drag) => {
- drag.accepted = drag.formats[0] === "application/vnd.qtdesignstudio.bundletexture"
+ let accepted = drag.formats[0] === "application/vnd.qtdesignstudio.bundletexture"
+ if (drag.formats[0] === "application/vnd.qtdesignstudio.assets")
+ accepted = rootView.hasAcceptableAssets(drag.urls)
+ drag.accepted = accepted
highlight = drag.accepted
}
@@ -245,12 +734,30 @@ Item {
highlight = false
}
- onDrop: {
+ onDrop: (drag) => {
+ drag.accept()
highlight = false
- rootView.acceptBundleTextureDrop()
+ if (drag.formats[0] === "application/vnd.qtdesignstudio.bundletexture")
+ rootView.acceptBundleTextureDrop()
+ else if (drag.formats[0] === "application/vnd.qtdesignstudio.assets")
+ rootView.acceptAssetsDrop(drag.urls)
+ }
+
+ onExpandedChanged: {
+ if (expanded) {
+ if (root.visibleItemCount(materialBrowserTexturesModel) > 0)
+ rootView.focusMaterialSection(false)
+ if (!searchBox.activeFocus)
+ scrollView.forceActiveFocus()
+ } else {
+ root.startDelayedEnsureTimer(300) // wait for section collapse animation
+ rootView.focusMaterialSection(true)
+ }
}
Grid {
+ id: gridTextures
+
width: scrollView.width
leftPadding: 5
rightPadding: 5
@@ -291,20 +798,6 @@ Item {
width: root.width
}
}
-
- IconButton {
- id: addTextureButton
-
- tooltip: qsTr("Add a texture.")
-
- anchors.right: parent.right
- anchors.rightMargin: scrollView.verticalScrollBarVisible ? 10 : 0
- icon: StudioTheme.Constants.plus
- normalColor: "transparent"
- buttonSize: StudioTheme.Values.sectionHeadHeight
- onClicked: materialBrowserTexturesModel.addNewTexture()
- enabled: root.enableUiElements
- }
}
DropArea {
diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml
index 36d3ca50ea..f72d21b35b 100644
--- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml
+++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml
@@ -5,6 +5,7 @@ import QtQuick
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import MaterialBrowserBackend
StudioControls.Menu {
id: root
@@ -13,11 +14,15 @@ StudioControls.Menu {
property var targetItem: null
property int copiedMaterialInternalId: -1
property var matSectionsModel: []
+ property bool restoreFocusOnClose: true
+
+ property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel
function popupMenu(targetItem = null, targetMaterial = null)
{
this.targetItem = targetItem
this.targetMaterial = targetMaterial
+ restoreFocusOnClose = true
popup()
}
@@ -102,7 +107,10 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("Rename")
enabled: root.targetItem
- onTriggered: root.targetItem.startRename();
+ onTriggered: {
+ restoreFocusOnClose = false
+ root.targetItem.startRename()
+ }
}
StudioControls.MenuItem {
diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml
index 6fdd3f5d61..abf3717e20 100644
--- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml
+++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialItem.qml
@@ -6,6 +6,7 @@ import QtQuick.Layouts 1.15
import QtQuickDesignerTheme 1.0
import HelperWidgets 2.0
import StudioTheme 1.0 as StudioTheme
+import MaterialBrowserBackend
Rectangle {
id: root
@@ -32,12 +33,12 @@ Rectangle {
if (matName.readOnly)
return;
- materialBrowserModel.renameMaterial(index, matName.text);
+ MaterialBrowserBackend.materialBrowserModel.renameMaterial(index, matName.text);
mouseArea.forceActiveFocus()
}
- border.width: materialBrowserModel.selectedIndex === index ? rootView.materialSectionFocused ? 3 : 1 : 0
- border.color: materialBrowserModel.selectedIndex === index
+ border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
+ border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index
? StudioTheme.Values.themeControlOutlineInteraction
: "transparent"
color: "transparent"
@@ -48,10 +49,20 @@ Rectangle {
onEntered: (drag) => {
drag.accepted = drag.formats[0] === "application/vnd.qtdesignstudio.texture"
+ || drag.formats[0] === "application/vnd.qtdesignstudio.bundletexture"
+ || (drag.formats[0] === "application/vnd.qtdesignstudio.assets"
+ && rootView.hasAcceptableAssets(drag.urls))
}
onDropped: (drag) => {
- rootView.acceptTextureDropOnMaterial(index, drag.getDataAsString(drag.keys[0]))
+ drag.accept()
+
+ if (drag.formats[0] === "application/vnd.qtdesignstudio.texture")
+ MaterialBrowserBackend.rootView.acceptTextureDropOnMaterial(index, drag.getDataAsString(drag.keys[0]))
+ else if (drag.formats[0] === "application/vnd.qtdesignstudio.bundletexture")
+ MaterialBrowserBackend.rootView.acceptBundleTextureDropOnMaterial(index, drag.urls[0])
+ else if (drag.formats[0] === "application/vnd.qtdesignstudio.assets")
+ MaterialBrowserBackend.rootView.acceptAssetsDropOnMaterial(index, drag.urls)
}
}
@@ -62,16 +73,16 @@ Rectangle {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: (mouse) => {
- rootView.focusMaterialSection(true)
- materialBrowserModel.selectMaterial(index)
+ MaterialBrowserBackend.materialBrowserModel.selectMaterial(index)
+ MaterialBrowserBackend.rootView.focusMaterialSection(true)
if (mouse.button === Qt.LeftButton)
- rootView.startDragMaterial(index, mapToGlobal(mouse.x, mouse.y))
+ MaterialBrowserBackend.rootView.startDragMaterial(index, mapToGlobal(mouse.x, mouse.y))
else if (mouse.button === Qt.RightButton)
root.showContextMenu()
}
- onDoubleClicked: materialBrowserModel.openMaterialEditor();
+ onDoubleClicked: MaterialBrowserBackend.materialBrowserModel.openMaterialEditor();
}
Column {
@@ -90,6 +101,11 @@ Rectangle {
cache: false
}
+ // Eat keys so they are not passed to parent while editing name
+ Keys.onPressed: (e) => {
+ e.accepted = true;
+ }
+
TextInput {
id: matName
@@ -130,8 +146,8 @@ Rectangle {
anchors.fill: parent
onClicked: {
- rootView.focusMaterialSection(true)
- materialBrowserModel.selectMaterial(index)
+ MaterialBrowserBackend.materialBrowserModel.selectMaterial(index)
+ MaterialBrowserBackend.rootView.focusMaterialSection(true)
}
onDoubleClicked: root.startRename()
}
diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureBrowserContextMenu.qml
index 658b7e7a51..714c004370 100644
--- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureBrowserContextMenu.qml
+++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureBrowserContextMenu.qml
@@ -5,6 +5,7 @@ import QtQuick
import HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
+import MaterialBrowserBackend
StudioControls.Menu {
id: root
@@ -12,6 +13,8 @@ StudioControls.Menu {
property var targetTexture: null
property int copiedTextureInternalId: -1
+ property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
+
function popupMenu(targetTexture = null)
{
this.targetTexture = targetTexture
@@ -30,7 +33,7 @@ StudioControls.Menu {
StudioControls.MenuItem {
text: qsTr("Apply to selected material")
- enabled: root.targetTexture && materialBrowserModel.selectedIndex >= 0
+ enabled: root.targetTexture && MaterialBrowserBackend.materialBrowserModel.selectedIndex >= 0
onTriggered: materialBrowserTexturesModel.applyToSelectedMaterial(root.targetTexture.textureInternalId)
}
diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml
index 002fcb0d7d..99970bc266 100644
--- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml
+++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/TextureItem.qml
@@ -7,6 +7,7 @@ import QtQuick.Layouts
import QtQuickDesignerTheme
import HelperWidgets
import StudioTheme as StudioTheme
+import MaterialBrowserBackend
Rectangle {
id: root
@@ -14,9 +15,9 @@ Rectangle {
visible: textureVisible
color: "transparent"
- border.width: materialBrowserTexturesModel.selectedIndex === index
- ? !rootView.materialSectionFocused ? 3 : 1 : 0
- border.color: materialBrowserTexturesModel.selectedIndex === index
+ border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
+ ? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
+ border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
? StudioTheme.Values.themeControlOutlineInteraction
: "transparent"
@@ -30,16 +31,16 @@ Rectangle {
hoverEnabled: true
onPressed: (mouse) => {
- rootView.focusMaterialSection(false)
- materialBrowserTexturesModel.selectTexture(index)
+ MaterialBrowserBackend.materialBrowserTexturesModel.selectTexture(index)
+ MaterialBrowserBackend.rootView.focusMaterialSection(false)
if (mouse.button === Qt.LeftButton)
- rootView.startDragTexture(index, mapToGlobal(mouse.x, mouse.y))
+ MaterialBrowserBackend.rootView.startDragTexture(index, mapToGlobal(mouse.x, mouse.y))
else if (mouse.button === Qt.RightButton)
root.showContextMenu()
}
- onDoubleClicked: materialBrowserTexturesModel.openTextureEditor();
+ onDoubleClicked: MaterialBrowserBackend.materialBrowserTexturesModel.openTextureEditor();
}
ToolTip {
@@ -61,9 +62,10 @@ Rectangle {
Image {
source: "image://materialBrowserTex/" + textureSource
asynchronous: true
- sourceSize.width: root.width - 10
- sourceSize.height: root.height - 10
+ width: root.width - 10
+ height: root.height - 10
anchors.centerIn: parent
smooth: true
+ fillMode: Image.PreserveAspectFit
}
}
diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml
index 3a28de2c94..52e4907448 100644
--- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml
+++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml
@@ -18,10 +18,11 @@ PropertyEditorPane {
topSection.refreshPreview()
}
- // Called also from C++ to close context menu on focus out
+ // Called from C++ to close context menu on focus out
function closeContextMenu()
{
topSection.closeContextMenu()
+ Controller.closeContextMenu()
}
// Called from C++ to initialize preview menu checkmarks
diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml
index 6ff49ad2cf..d5a9acf974 100644
--- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml
+++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorToolBar.qml
@@ -1,69 +1,57 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 2.15
+import QtQuick
import QtQuickDesignerTheme 1.0
-import HelperWidgets 2.0
+import HelperWidgets 2.0 as HelperWidgets
+import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
import MaterialToolBarAction 1.0
Rectangle {
id: root
- color: StudioTheme.Values.themeSectionHeadBackground
- width: row.width
- height: 40
+ color: StudioTheme.Values.themeToolbarBackground
+ height: StudioTheme.Values.toolbarHeight
signal toolBarAction(int action)
Row {
id: row
-
+ spacing: StudioTheme.Values.toolbarSpacing
anchors.verticalCenter: parent.verticalCenter
leftPadding: 6
- IconButton {
- icon: StudioTheme.Constants.applyMaterialToSelected
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.apply_medium
enabled: hasMaterial && hasModelSelection && hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
tooltip: qsTr("Apply material to selected model.")
+ onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
}
- IconButton {
- icon: StudioTheme.Constants.newMaterial
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.create_medium
enabled: hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.AddNewMaterial)
tooltip: qsTr("Create new material.")
+ onClicked: root.toolBarAction(ToolBarAction.AddNewMaterial)
}
- IconButton {
- icon: StudioTheme.Constants.deleteMaterial
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.delete_medium
enabled: hasMaterial && hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentMaterial)
tooltip: qsTr("Delete current material.")
+ onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentMaterial)
}
- IconButton {
- icon: StudioTheme.Constants.openMaterialBrowser
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.materialBrowser_medium
enabled: hasMaterial && hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
tooltip: qsTr("Open material browser.")
+ onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
}
}
}
diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml
index e4d37650e1..dbc023d83b 100644
--- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml
+++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml
@@ -1,12 +1,11 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
import QtQuickDesignerTheme 1.0
-import QtQuick.Templates 2.15 as T
-import HelperWidgets 2.0
+import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
@@ -17,6 +16,11 @@ Column {
property string previewEnv
property string previewModel
+ property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle {
+ //This is how you can override stuff from the control styles
+ controlSize: Qt.size(previewOptions.width, previewOptions.width)
+ baseIconFontSize: StudioTheme.Values.bigIconFontSize
+ }
function refreshPreview()
{
@@ -153,17 +157,17 @@ Column {
Column {
anchors.horizontalCenter: parent.horizontalCenter
- IconButton {
- icon: StudioTheme.Constants.materialPreviewEnvironment
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: previewOptions.width
+
+ HelperWidgets.AbstractButton {
+ style: root.buttonStyle
+ buttonIcon: StudioTheme.Constants.textures_medium
tooltip: qsTr("Select preview environment.")
onClicked: envMenu.popup()
}
- IconButton {
- icon: StudioTheme.Constants.materialPreviewModel
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: previewOptions.width
+
+ HelperWidgets.AbstractButton {
+ style: root.buttonStyle
+ buttonIcon: StudioTheme.Constants.cube_medium
tooltip: qsTr("Select preview model.")
onClicked: modelMenu.popup()
}
@@ -172,19 +176,19 @@ Column {
}
- Section {
+ HelperWidgets.Section {
// Section with hidden header is used so properties are aligned with the other sections' properties
hideHeader: true
width: parent.width
collapsible: false
- SectionLayout {
- PropertyLabel { text: qsTr("Name") }
+ HelperWidgets.SectionLayout {
+ HelperWidgets.PropertyLabel { text: qsTr("Name") }
- SecondColumnLayout {
- Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
+ HelperWidgets.SecondColumnLayout {
+ HelperWidgets.Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
- LineEdit {
+ HelperWidgets.LineEdit {
implicitWidth: StudioTheme.Values.singleControlColumnWidth
width: StudioTheme.Values.singleControlColumnWidth
backendValue: backendValues.objectName
@@ -195,18 +199,18 @@ Column {
showExtendedFunctionButton: false
// allow only alphanumeric characters, underscores, no space at start, and 1 space between words
- validator: RegExpValidator { regExp: /^(\w+\s)*\w+$/ }
+ validator: HelperWidgets.RegExpValidator { regExp: /^(\w+\s)*\w+$/ }
}
- ExpandingSpacer {}
+ HelperWidgets.ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Type") }
+ HelperWidgets.PropertyLabel { text: qsTr("Type") }
- SecondColumnLayout {
- Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
+ HelperWidgets.SecondColumnLayout {
+ HelperWidgets.Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth }
- ComboBox {
+ HelperWidgets.ComboBox {
currentIndex: possibleTypeIndex
model: possibleTypes
showExtendedFunctionButton: false
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml
index 84ccff7429..d89d2d5157 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml
@@ -140,11 +140,15 @@ Item {
height: DialogValues.styleImageHeight
+ 2 * DialogValues.styleImageBorderWidth
- border.color: delegateId.hovered
- ? DialogValues.textColor
- : (index === stylesList.currentIndex
- ? DialogValues.textColorInteraction
- : "transparent")
+ border.color: {
+ if (index === stylesList.currentIndex)
+ return DialogValues.textColorInteraction
+
+ if (delegateId.hovered)
+ return DialogValues.textColor
+ else
+ return "transparent"
+ }
border.width: index === stylesList.currentIndex || delegateId.hovered
? DialogValues.styleImageBorderWidth
diff --git a/share/qtcreator/qmldesigner/newstateseditor/Main.qml b/share/qtcreator/qmldesigner/newstateseditor/Main.qml
index 0176cc79f6..4cd85bb1ad 100644
--- a/share/qtcreator/qmldesigner/newstateseditor/Main.qml
+++ b/share/qtcreator/qmldesigner/newstateseditor/Main.qml
@@ -40,7 +40,7 @@ Rectangle {
property bool isLandscape: true
- color: StudioTheme.Values.themeStatePanelBackground
+ color: StudioTheme.Values.themePanelBackground
onWidthChanged: root.responsiveResize(root.width, root.height)
onHeightChanged: root.responsiveResize(root.width, root.height)
@@ -374,7 +374,7 @@ Rectangle {
}
}
- color: StudioTheme.Values.themeSectionHeadBackground
+ color: StudioTheme.Values.themeToolbarBackground
width: root.width
height: (toolBar.doubleRow ? 2 : 1) * StudioTheme.Values.toolbarHeight
@@ -403,6 +403,7 @@ Rectangle {
}
StudioControls.ComboBox {
+ style: StudioTheme.Values.viewBarControlStyle
id: stateGroupComboBox
actionIndicatorVisible: false
model: statesEditorModel.stateGroups
@@ -458,14 +459,16 @@ Rectangle {
leftPadding: toolBar.doubleRow ? root.padding : 0
HelperWidgets.AbstractButton {
- buttonIcon: StudioTheme.Constants.plus
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.add_medium
anchors.verticalCenter: parent.verticalCenter
tooltip: qsTr("Create State Group")
onClicked: statesEditorModel.addStateGroup("stateGroup")
}
HelperWidgets.AbstractButton {
- buttonIcon: StudioTheme.Constants.minus
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.remove_medium
anchors.verticalCenter: parent.verticalCenter
enabled: statesEditorModel.activeStateGroupIndex !== 0
tooltip: qsTr("Remove State Group")
@@ -474,7 +477,8 @@ Rectangle {
HelperWidgets.AbstractButton {
id: editButton
- buttonIcon: StudioTheme.Constants.edit
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.edit_medium
anchors.verticalCenter: parent.verticalCenter
enabled: statesEditorModel.activeStateGroupIndex !== 0
checked: editDialog.visible
@@ -502,7 +506,8 @@ Rectangle {
rightPadding: root.padding
HelperWidgets.AbstractButton {
- buttonIcon: StudioTheme.Constants.gridView
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.grid_medium
anchors.verticalCenter: parent.verticalCenter
enabled: !root.tinyMode
tooltip: qsTr("Show thumbnails")
@@ -513,7 +518,8 @@ Rectangle {
}
HelperWidgets.AbstractButton {
- buttonIcon: StudioTheme.Constants.textFullJustification
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.list_medium
anchors.verticalCenter: parent.verticalCenter
enabled: !root.tinyMode
tooltip: qsTr("Show property changes")
@@ -677,7 +683,7 @@ Rectangle {
onEntered: function (drag) {
let dragSource = (drag.source as StateThumbnail)
- if (dragSource === undefined)
+ if (!dragSource)
return
if (dragSource.extendString !== stateThumbnail.extendString
@@ -692,7 +698,7 @@ Rectangle {
onDropped: function (drop) {
let dropSource = (drop.source as StateThumbnail)
- if (dropSource === undefined)
+ if (!dropSource)
return
if (dropSource.extendString !== stateThumbnail.extendString
diff --git a/share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml b/share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml
index 6eda458212..a7a78b5e44 100644
--- a/share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml
+++ b/share/qtcreator/qmldesigner/newstateseditor/MenuButton.qml
@@ -52,25 +52,15 @@ Item {
property color iconColor: StudioTheme.Values.themeTextColor
- Rectangle {
- id: rectangle
- width: 19
- height: 3
- color: menuIcon.iconColor
- }
-
- Rectangle {
- id: rectangle1
- width: 19
- height: 3
- color: menuIcon.iconColor
- }
-
- Rectangle {
- id: rectangle2
- width: 19
- height: 3
- color: menuIcon.iconColor
+ Label {
+ id: moreMenu
+ anchors.fill: parent
+ text: StudioTheme.Constants.more_medium
+ color: StudioTheme.Values.themeTextColor
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: 16 //StudioTheme.Values.myIconFontSize
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
}
}
@@ -123,7 +113,7 @@ Item {
PropertyChanges {
target: background
- color: StudioTheme.Values.themeControlBackgroundHover
+ color: StudioTheme.Values.themeControlBackground_topToolbarHover
}
PropertyChanges {
target: menuIcon
diff --git a/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml b/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml
index 1591d3ecb7..51e884aa5a 100644
--- a/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml
+++ b/share/qtcreator/qmldesigner/newstateseditor/StateScrollBar.qml
@@ -41,7 +41,7 @@ T.ScrollBar {
radius: width / 2
opacity: 0.0
color: scrollBar.pressed ? StudioTheme.Values.themeScrollBarHandle //"#4C4C4C"//DARK
- : StudioTheme.Values.themeScrollBarTrack //"#3E3E3E"//DARK
+ : StudioTheme.Values.themeScrollBarHandle //"#3E3E3E"//DARK
states: State {
name: "active"
diff --git a/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml b/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
index 6d4a84e44c..e54732c8d0 100644
--- a/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
+++ b/share/qtcreator/qmldesigner/newstateseditor/StateThumbnail.qml
@@ -127,7 +127,8 @@ Item {
id: stateBackground
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeInteraction
- border.width: root.isChecked ? 4 : 0
+ border.width: root.isChecked ? 3 : 0
+ radius: 10
anchors.fill: parent
readonly property int controlHeight: 25
@@ -159,6 +160,7 @@ Item {
HelperWidgets.AbstractButton {
id: defaultButton
+ style: StudioTheme.Values.statesControlStyle
width: 50
height: stateBackground.controlHeight
checkedInverted: true
@@ -173,6 +175,7 @@ Item {
StudioControls.TextField {
id: stateNameField
+ style: StudioTheme.Values.statesControlStyle
property string previousText
@@ -648,6 +651,7 @@ Item {
StudioControls.TextField {
id: whenCondition
+ style: StudioTheme.Values.statesControlStyle
property string previousCondition
@@ -656,7 +660,7 @@ Item {
visible: !root.baseState
indicatorVisible: true
- indicator.icon.text: StudioTheme.Constants.edit
+ indicator.icon.text: StudioTheme.Constants.edit_medium
indicator.onClicked: {
whenCondition.previousCondition = whenCondition.text
@@ -755,7 +759,7 @@ Item {
PropertyChanges {
target: stateBackground
- color: StudioTheme.Values.themeControlBackground
+ color: StudioTheme.Values.themeToolbarBackground
}
},
State {
@@ -764,7 +768,7 @@ Item {
PropertyChanges {
target: stateBackground
- color: StudioTheme.Values.themeControlBackgroundHover
+ color: StudioTheme.Values.themeStateBackgroundColor_hover
}
},
State {
@@ -774,7 +778,7 @@ Item {
PropertyChanges {
target: stateBackground
- color: StudioTheme.Values.themeStateHighlight
+ color: StudioTheme.Values.themeThumbnailBackground_baseState
}
},
State {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml
index e67df362c8..4b66032bcf 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml
@@ -49,7 +49,6 @@ Section {
+ StudioTheme.Values.actionIndicatorWidth
width: implicitWidth
typeFilter: "QtQuick.AudioOutput"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.audioOutput
}
@@ -70,7 +69,6 @@ Section {
+ StudioTheme.Values.actionIndicatorWidth
width: implicitWidth
typeFilter: "QtQuick.VideoOutput"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.videoOutput
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml
index 6cac5c6f15..e9be2285c5 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml
@@ -15,6 +15,7 @@ Section {
PropertyLabel {
visible: majorQtQuickVersion > 1
text: qsTr("Enabled")
+ tooltip: qsTr("Toggles if the component is enabled to receive mouse and keyboard input.")
}
SecondColumnLayout {
@@ -32,6 +33,7 @@ Section {
PropertyLabel {
text: qsTr("Smooth")
+ tooltip: qsTr("Uses smooth filtering when the image is scaled or transformed.")
blockedByTemplate: !backendValues.smooth.isAvailable
}
@@ -49,6 +51,7 @@ Section {
PropertyLabel {
text: qsTr("Antialiasing")
+ tooltip: qsTr("Refines the edges of the image.")
blockedByTemplate: !backendValues.antialiasing.isAvailable
}
@@ -102,7 +105,7 @@ Section {
PropertyLabel {
text: qsTr("Baseline offset")
- tooltip: qsTr("Position of the component's baseline in local coordinates.")
+ tooltip: qsTr("Sets the position of the component's baseline in local coordinates.")
blockedByTemplate: !backendValues.baselineOffset.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml
index 4d1ef5711b..00b97f9942 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml
@@ -24,6 +24,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Speed")
+ tooltip: qsTr("Sets the speed of the animation.")
blockedByTemplate: !backendValues.speed.isAvailable
}
@@ -47,7 +48,7 @@ Column {
PropertyLabel {
text: qsTr("Playing")
- tooltip: qsTr("Whether the animation is playing or paused.")
+ tooltip: qsTr("Toggles if the animation is playing.")
blockedByTemplate: !backendValues.playing.isAvailable && !backendValues.paused.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationTargetSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationTargetSection.qml
index f835522596..aef4aa5269 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationTargetSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationTargetSection.qml
@@ -23,7 +23,6 @@ Section {
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "QtQuick.QtObject"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.target
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml
index a80dcf73e9..0862b7e6d8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml
@@ -18,7 +18,10 @@ Column {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Source") }
+ PropertyLabel {
+ text: qsTr("Source")
+ tooltip: qsTr("Sets the source image for the border.")
+ }
SecondColumnLayout {
UrlChooser {
@@ -30,6 +33,7 @@ Column {
PropertyLabel {
text: qsTr("Source size")
+ tooltip: qsTr("Sets the dimension of the border image.")
blockedByTemplate: !backendValues.sourceSize.isAvailable
}
@@ -49,6 +53,7 @@ Column {
ControlLabel {
//: The width of the object
text: qsTr("W", "width")
+ tooltip: qsTr("Width")
enabled: backendValues.sourceSize_width.isAvailable
}
@@ -69,6 +74,7 @@ Column {
ControlLabel {
//: The height of the object
text: qsTr("H", "height")
+ tooltip: qsTr("Height")
enabled: backendValues.sourceSize_height.isAvailable
}
/*
@@ -82,6 +88,7 @@ Column {
PropertyLabel {
text: qsTr("Tile mode H")
+ tooltip: qsTr("Sets the horizontal tiling mode.")
blockedByTemplate: !backendValues.horizontalTileMode.isAvailable
}
@@ -101,6 +108,7 @@ Column {
PropertyLabel {
text: qsTr("Tile mode V")
+ tooltip: qsTr("Sets the vertical tiling mode.")
blockedByTemplate: !backendValues.verticalTileMode.isAvailable
}
@@ -118,7 +126,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Border left") }
+ PropertyLabel {
+ text: qsTr("Border left")
+ tooltip: qsTr("Sets the left border.")
+ }
SecondColumnLayout {
SpinBox {
@@ -133,7 +144,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Border right") }
+ PropertyLabel {
+ text: qsTr("Border right")
+ tooltip: qsTr("Sets the right border.")
+ }
SecondColumnLayout {
SpinBox {
@@ -148,7 +162,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Border top") }
+ PropertyLabel {
+ text: qsTr("Border top")
+ tooltip: qsTr("Sets the top border.")
+ }
SecondColumnLayout {
SpinBox {
@@ -163,7 +180,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Border bottom") }
+ PropertyLabel {
+ text: qsTr("Border bottom")
+ tooltip: qsTr("Sets the bottom border.")
+ }
SecondColumnLayout {
SpinBox {
@@ -180,7 +200,7 @@ Column {
PropertyLabel {
text: qsTr("Mirror")
- tooltip: qsTr("Specifies whether the image should be horizontally inverted.")
+ tooltip: qsTr("Toggles if the image should be inverted horizontally.")
blockedByTemplate: !backendValues.mirror.isAvailable
}
@@ -198,7 +218,7 @@ Column {
PropertyLabel {
text: qsTr("Smooth")
- tooltip: qsTr("Specifies whether the image is smoothly filtered when scaled or transformed.")
+ tooltip: qsTr("Toggles if the image should be filtered smoothly when transformed.")
blockedByTemplate: !backendValues.smooth.isAvailable
}
@@ -216,7 +236,7 @@ Column {
PropertyLabel {
text: qsTr("Cache")
- tooltip: qsTr("Specifies whether the image should be cached.")
+ tooltip: qsTr("Toggles if the image is saved to the cache memory.")
blockedByTemplate: !backendValues.cache.isAvailable
}
@@ -234,7 +254,7 @@ Column {
PropertyLabel {
text: qsTr("Asynchronous")
- tooltip: qsTr("Specifies that images on the local filesystem should be loaded asynchronously in a separate thread.")
+ tooltip: qsTr("Toggles if the image is loaded after all the components in the design.")
blockedByTemplate: !backendValues.asynchronous.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColumnSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColumnSpecifics.qml
index c8a298ec41..d385a19f87 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColumnSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ColumnSpecifics.qml
@@ -18,7 +18,10 @@ Column {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Spacing") }
+ PropertyLabel {
+ text: qsTr("Spacing")
+ tooltip: qsTr("Sets the spacing between column items.")
+ }
SecondColumnLayout {
SpinBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ConnectionsSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ConnectionsSpecifics.qml
index 2c97b8e7e4..a4a3099146 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ConnectionsSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ConnectionsSpecifics.qml
@@ -52,7 +52,6 @@ Section {
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "QtQuick.Item"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.target
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml
index 57b12f6dc9..e625e5c842 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml
@@ -52,7 +52,7 @@ Section {
PropertyLabel {
text: qsTr("Checkable")
- tooltip: qsTr("Whether the button is checkable.")
+ tooltip: qsTr("Toggles if the button is checkable.")
}
SecondColumnLayout {
@@ -68,7 +68,7 @@ Section {
PropertyLabel {
text: qsTr("Checked")
- tooltip: qsTr("Whether the button is checked.")
+ tooltip: qsTr("Toggles if the button is checked.")
}
SecondColumnLayout {
@@ -84,7 +84,7 @@ Section {
PropertyLabel {
text: qsTr("Exclusive")
- tooltip: qsTr("Whether the button is exclusive.")
+ tooltip: qsTr("Toggles if the button is exclusive. Non-exclusive checkable buttons that belong to the same parent behave as if they are part of the same button group; only one button can be checked at any time.")
blockedByTemplate: !backendValues.autoExclusive.isAvailable
}
@@ -102,7 +102,7 @@ Section {
PropertyLabel {
text: qsTr("Auto-repeat")
- tooltip: qsTr("Whether the button repeats pressed(), released() and clicked() signals while the button is pressed and held down.")
+ tooltip: qsTr("Toggles if pressed, released, and clicked actions are repeated while the button is pressed and held down.")
}
SecondColumnLayout {
@@ -119,7 +119,7 @@ Section {
PropertyLabel {
text: qsTr("Repeat delay")
- tooltip: qsTr("Initial delay of auto-repetition in milliseconds.")
+ tooltip: qsTr("Sets the initial delay of auto-repetition in milliseconds.")
enabled: autoRepeat.checked
}
@@ -148,7 +148,7 @@ Section {
PropertyLabel {
text: qsTr("Repeat interval")
- tooltip: qsTr("Interval of auto-repetition in milliseconds.")
+ tooltip: qsTr("Sets the interval between auto-repetitions in milliseconds.")
enabled: autoRepeat.checked
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/BusyIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/BusyIndicatorSpecifics.qml
index b56a968789..9cffbc9b44 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/BusyIndicatorSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/BusyIndicatorSpecifics.qml
@@ -18,7 +18,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Running")
- tooltip: qsTr("Whether the busy indicator is currently indicating activity.")
+ tooltip: qsTr("Toggles if the busy indicator indicates activity.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ButtonSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ButtonSection.qml
index 1d4cc66f50..dbfbfd1fd4 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ButtonSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ButtonSection.qml
@@ -17,7 +17,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Appearance")
- tooltip: qsTr("Whether the button is flat and/or highlighted.")
+ tooltip: qsTr("Toggles if the button is flat or highlighted.")
blockedByTemplate: !backendValues.flat.isAvailable
&& !backendValues.highlighted.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/CheckSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/CheckSection.qml
index 0bde6f5568..c70c8c925d 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/CheckSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/CheckSection.qml
@@ -16,7 +16,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Check state")
- tooltip: qsTr("The current check state.")
+ tooltip: qsTr("Sets the state of the check box.")
}
SecondColumnLayout {
@@ -34,7 +34,7 @@ Section {
PropertyLabel {
text: qsTr("Tri-state")
- tooltip: qsTr("Whether the checkbox has three states.")
+ tooltip: qsTr("Toggles if the check box can have an intermediate state.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ComboBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ComboBoxSpecifics.qml
index 3518760ff7..7c97b865f9 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ComboBoxSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ComboBoxSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Text role")
- tooltip: qsTr("The model role used for displaying text.")
+ tooltip: qsTr("Sets the model role for populating the combo box.")
}
SecondColumnLayout {
@@ -33,7 +33,7 @@ Column {
PropertyLabel {
text: qsTr("Display text")
- tooltip: qsTr("Holds the text that is displayed on the combo box button.")
+ tooltip: qsTr("Sets the initial display text for the combo box.")
}
SecondColumnLayout {
@@ -49,7 +49,7 @@ Column {
PropertyLabel {
text: qsTr("Current index")
- tooltip: qsTr("The index of the current item.")
+ tooltip: qsTr("Sets the current item.")
}
SecondColumnLayout {
@@ -68,7 +68,7 @@ Column {
PropertyLabel {
text: qsTr("Flat")
- tooltip: qsTr("Whether the combo box button is flat.")
+ tooltip: qsTr("Toggles if the combo box button is flat.")
}
SecondColumnLayout {
@@ -84,7 +84,7 @@ Column {
PropertyLabel {
text: qsTr("Editable")
- tooltip: qsTr("Whether the combo box is editable.")
+ tooltip: qsTr("Toggles if the combo box is editable.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ContainerSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ContainerSection.qml
index e73e522cda..6a7dc753d5 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ContainerSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ContainerSection.qml
@@ -14,7 +14,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Current index")
- tooltip: qsTr("The index of the current item.")
+ tooltip: qsTr("Sets the index of the current item.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ControlSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ControlSection.qml
index 96233fc86e..2295eb1c1a 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ControlSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ControlSection.qml
@@ -16,7 +16,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Enable")
- tooltip: qsTr("Whether the control is enabled and hover events are received.")
+ tooltip: qsTr("Toggles if the component can receive hover events.")
}
SecondColumnLayout {
@@ -42,7 +42,7 @@ Section {
PropertyLabel {
text: qsTr("Focus policy")
- tooltip: qsTr("Focus policy of the control.")
+ tooltip: qsTr("Sets focus method.")
blockedByTemplate: !backendValues.focusPolicy.isAvailable
}
@@ -62,7 +62,7 @@ Section {
PropertyLabel {
text: qsTr("Spacing")
- tooltip: qsTr("Spacing between internal elements of the control.")
+ tooltip: qsTr("Sets the spacing between internal elements of the component.")
}
SecondColumnLayout {
@@ -81,7 +81,7 @@ Section {
PropertyLabel {
text: qsTr("Wheel")
- tooltip: qsTr("Whether control accepts wheel events.")
+ tooltip: qsTr("Toggles if the component supports mouse wheel events.")
blockedByTemplate: !backendValues.wheelEnabled.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DelayButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DelayButtonSpecifics.qml
index 2f19d26b00..36f69faee8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DelayButtonSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DelayButtonSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Delay")
- tooltip: qsTr("The delay in milliseconds.")
+ tooltip: qsTr("Sets the delay before the button activates.")
}
SecondColumnLayout {
@@ -36,6 +36,7 @@ Column {
ControlLabel {
text: "ms"
+ tooltip: qsTr("Milliseconds.")
elide: Text.ElideNone
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml
index 4c90f26aba..a8536e8ca3 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Value")
- tooltip: qsTr("The current value of the dial and whether it provides live value updates.")
+ tooltip: qsTr("Sets the value of the dial.")
}
SecondColumnLayout {
@@ -46,7 +46,7 @@ Column {
PropertyLabel {
text: qsTr("From")
- tooltip: qsTr("The starting value of the dial range.")
+ tooltip: qsTr("Sets the minimum value of the dial.")
}
SecondColumnLayout {
@@ -66,7 +66,7 @@ Column {
PropertyLabel {
text: qsTr("To")
- tooltip: qsTr("The ending value of the dial range.")
+ tooltip: qsTr("Sets the maximum value of the dial.")
}
SecondColumnLayout {
@@ -86,7 +86,7 @@ Column {
PropertyLabel {
text: qsTr("Step size")
- tooltip: qsTr("The step size of the dial.")
+ tooltip: qsTr("Sets the number by which the dial value changes.")
}
SecondColumnLayout {
@@ -106,7 +106,8 @@ Column {
PropertyLabel {
text: qsTr("Snap mode")
- tooltip: qsTr("The snap mode of the dial.")
+ tooltip: qsTr("Sets how the dial's handle snaps to the steps\n"
+ + "defined in <b>Step size</b>.")
}
SecondColumnLayout {
@@ -124,7 +125,7 @@ Column {
PropertyLabel {
text: qsTr("Input mode")
- tooltip: qsTr("How the dial tracks movement.")
+ tooltip: qsTr("Sets how the user can interact with the dial.")
}
SecondColumnLayout {
@@ -142,7 +143,7 @@ Column {
PropertyLabel {
text: qsTr("Wrap")
- tooltip: qsTr("Whether the dial wraps when dragged.")
+ tooltip: qsTr("Toggles if the dial wraps around when it reaches the start or end.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/GroupBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/GroupBoxSpecifics.qml
index 4e998f582a..645eac3312 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/GroupBoxSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/GroupBoxSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Title")
- tooltip: qsTr("The title of the group box.")
+ tooltip: qsTr("Sets the title for the group box.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/IconSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/IconSection.qml
index 83d6db4ae1..a01406fce0 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/IconSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/IconSection.qml
@@ -20,6 +20,7 @@ Section {
PropertyLabel {
text: qsTr("Source")
+ tooltip: qsTr("Sets a background image for the icon.")
blockedByTemplate: !backendValues.icon_source.isAvailable
enabled: !root.blockedByContext
}
@@ -35,6 +36,7 @@ Section {
PropertyLabel {
text: qsTr("Color")
+ tooltip: qsTr("Sets the color for the icon.")
blockedByTemplate: !backendValues.icon_color.isAvailable
enabled: !root.blockedByContext
}
@@ -47,6 +49,7 @@ Section {
PropertyLabel {
text: qsTr("Size")
+ tooltip: qsTr("Sets the height and width of the icon.")
blockedByTemplate: !backendValues.icon_width.isAvailable
enabled: !root.blockedByContext
}
@@ -102,7 +105,7 @@ Section {
PropertyLabel {
text: qsTr("Cache")
- tooltip: qsTr("Whether the icon should be cached.")
+ tooltip: qsTr("Toggles if the icon is saved to the cache memory.")
blockedByTemplate: !backendValues.icon_cache.isAvailable
enabled: !root.blockedByContext
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/InsetSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/InsetSection.qml
index 5f7f11e6f3..084b789ae2 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/InsetSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/InsetSection.qml
@@ -12,7 +12,10 @@ Section {
width: parent.width
SectionLayout {
- PropertyLabel { text: qsTr("Vertical") }
+ PropertyLabel {
+ text: qsTr("Vertical")
+ tooltip: qsTr("Sets the space from the top and bottom of the area to the background top and bottom.")
+ }
SecondColumnLayout {
SpinBox {
@@ -59,7 +62,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Horizontal") }
+ PropertyLabel {
+ text: qsTr("Horizontal")
+ tooltip: qsTr("Sets the space from the left and right of the area to the background left and right.")
+ }
SecondColumnLayout {
SpinBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ItemDelegateSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ItemDelegateSection.qml
index 3ce7e84eba..556f2d924c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ItemDelegateSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ItemDelegateSection.qml
@@ -17,7 +17,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Highlight")
- tooltip: qsTr("Whether the delegate is highlighted.")
+ tooltip: qsTr("Toggles if the delegate is highlighted.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageIndicatorSpecifics.qml
index 21f5c1559f..f8b78ec5b8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageIndicatorSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageIndicatorSpecifics.qml
@@ -16,7 +16,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Count")
- tooltip: qsTr("The number of pages.")
+ tooltip: qsTr("Sets the number of pages.")
}
SecondColumnLayout {
@@ -35,7 +35,7 @@ Column {
PropertyLabel {
text: qsTr("Current")
- tooltip: qsTr("The index of the current page.")
+ tooltip: qsTr("Sets the current page.")
}
SecondColumnLayout {
@@ -54,7 +54,7 @@ Column {
PropertyLabel {
text: qsTr("Interactive")
- tooltip: qsTr("Whether the control is interactive.")
+ tooltip: qsTr("Toggles if the user can interact with the page indicator.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageSpecifics.qml
index db7db41024..a240b0a45e 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PageSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Title")
- tooltip: qsTr("Title of the page.")
+ tooltip: qsTr("Sets the title of the page.")
}
SecondColumnLayout {
@@ -33,7 +33,8 @@ Column {
PropertyLabel {
text: qsTr("Content size")
- tooltip: qsTr("Content width and height used for calculating the total implicit size.")
+ tooltip: qsTr("Sets the size of the page. This is used to\n"
+ + "calculate the total implicit size.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSection.qml
index 02b4081ae6..1d1e29a1c8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/PaneSection.qml
@@ -14,7 +14,9 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Content size")
- tooltip: qsTr("Content width and height used for calculating the total implicit size.")
+ tooltip: qsTr("Sets the size of the %1. This is used to calculate\n"
+ + "the total implicit size.").arg(caption.charAt(0).toLowerCase()
+ + caption.slice(1))
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ProgressBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ProgressBarSpecifics.qml
index 9cc3444cab..7c1abbcb89 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ProgressBarSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ProgressBarSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Value")
- tooltip: qsTr("The current value of the progress.")
+ tooltip: qsTr("Sets the value of the progress bar.")
}
SecondColumnLayout {
@@ -37,7 +37,7 @@ Column {
PropertyLabel {
text: qsTr("From")
- tooltip: qsTr("The starting value for the progress.")
+ tooltip: qsTr("Sets the minimum value of the progress bar.")
}
SecondColumnLayout {
@@ -57,7 +57,7 @@ Column {
PropertyLabel {
text: qsTr("To")
- tooltip: qsTr("The ending value for the progress.")
+ tooltip: qsTr("Sets the maximum value of the progress bar.")
}
SecondColumnLayout {
@@ -77,7 +77,9 @@ Column {
PropertyLabel {
text: qsTr("Indeterminate")
- tooltip: qsTr("Whether the progress is indeterminate.")
+ tooltip: qsTr("Toggles if the progress bar is in indeterminate mode.\n"
+ +"A progress bar in indeterminate mode displays that an\n"
+ + "operation is in progress.")
blockedByTemplate: !backendValues.indeterminate.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RadioDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RadioDelegateSpecifics.qml
index 0a76d39bca..26e2f65954 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RadioDelegateSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RadioDelegateSpecifics.qml
@@ -9,9 +9,7 @@ import StudioTheme 1.0 as StudioTheme
Column {
width: parent.width
- ItemDelegateSection {
- caption: qsTr("Radio Delegate")
- }
+ ItemDelegateSection {}
AbstractButtonSection {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RangeSliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RangeSliderSpecifics.qml
index 5dc377fe61..f70a1220dd 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RangeSliderSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RangeSliderSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Value 1")
- tooltip: qsTr("The value of the first range slider handle.")
+ tooltip: qsTr("Sets the value of the first range slider handle.")
}
SecondColumnLayout {
@@ -38,7 +38,7 @@ Column {
implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
backendValue: backendValues.live
- tooltip: qsTr("Whether the range slider provides live value updates.")
+ tooltip: qsTr("Toggles if the range slider provides live value updates.")
}
ExpandingSpacer {}
@@ -47,7 +47,7 @@ Column {
PropertyLabel {
text: qsTr("Value 2")
- tooltip: qsTr("The value of the second range slider handle.")
+ tooltip: qsTr("Sets the value of the second range slider handle.")
}
SecondColumnLayout {
@@ -66,7 +66,7 @@ Column {
PropertyLabel {
text: qsTr("From")
- tooltip: qsTr("The starting value of the range slider range.")
+ tooltip: qsTr("Sets the minimum value of the range slider.")
}
SecondColumnLayout {
@@ -85,7 +85,7 @@ Column {
PropertyLabel {
text: qsTr("To")
- tooltip: qsTr("The ending value of the range slider range.")
+ tooltip: qsTr("Sets the maximum value of the range slider.")
}
SecondColumnLayout {
@@ -104,7 +104,8 @@ Column {
PropertyLabel {
text: qsTr("Step size")
- tooltip: qsTr("The step size of the range slider.")
+ tooltip: qsTr("Sets the interval between the steps.\n"
+ + "This functions if <b>Snap mode</b> is selected.")
}
SecondColumnLayout {
@@ -123,7 +124,7 @@ Column {
PropertyLabel {
text: qsTr("Drag threshold")
- tooltip: qsTr("The threshold (in logical pixels) at which a drag event will be initiated.")
+ tooltip: qsTr("Sets the threshold at which a drag event begins.")
}
SecondColumnLayout {
@@ -141,7 +142,8 @@ Column {
PropertyLabel {
text: qsTr("Snap mode")
- tooltip: qsTr("The snap mode of the range slider.")
+ tooltip: qsTr("Sets how the slider handles snaps to the steps\n"
+ + "defined in step size.")
}
SecondColumnLayout {
@@ -159,7 +161,7 @@ Column {
PropertyLabel {
text: qsTr("Orientation")
- tooltip: qsTr("The orientation of the range slider.")
+ tooltip: qsTr("Sets the orientation of the range slider.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RoundButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RoundButtonSpecifics.qml
index 39c1348dd7..067936b7f0 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RoundButtonSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/RoundButtonSpecifics.qml
@@ -16,7 +16,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Appearance")
- tooltip: qsTr("Whether the button is flat and/or highlighted.")
+ tooltip: qsTr("Toggles if the button is flat or highlighted.")
blockedByTemplate: !backendValues.flat.isAvailable
&& !backendValues.highlighted.isAvailable
}
@@ -45,7 +45,7 @@ Column {
PropertyLabel {
text: qsTr("Radius")
- tooltip: qsTr("Radius of the button.")
+ tooltip: qsTr("Sets the radius of the button.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml
index 6e93cef487..9dc06b7969 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ScrollViewSpecifics.qml
@@ -14,7 +14,11 @@ Column {
caption: qsTr("Scroll View")
SectionLayout {
- PropertyLabel { text: qsTr("Content size") }
+ PropertyLabel {
+ text: qsTr("Content size")
+ tooltip: qsTr("Sets the width and height of the view.\n"
+ + "This is used for calculating the total implicit size.")
+ }
SecondColumnLayout {
SpinBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SpinBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SpinBoxSpecifics.qml
index 09ccdc29dc..1402a5382c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SpinBoxSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SpinBoxSpecifics.qml
@@ -16,7 +16,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Value")
- tooltip: qsTr("The current value of the spin box.")
+ tooltip: qsTr("Sets the current value of the spin box.")
}
SecondColumnLayout {
@@ -34,7 +34,7 @@ Column {
PropertyLabel {
text: qsTr("From")
- tooltip: qsTr("The starting value of the spin box range.")
+ tooltip: qsTr("Sets the lowest value of the spin box range.")
}
SecondColumnLayout {
@@ -52,7 +52,7 @@ Column {
PropertyLabel {
text: qsTr("To")
- tooltip: qsTr("The ending value of the spin box range.")
+ tooltip: qsTr("Sets the highest value of the spin box range.")
}
SecondColumnLayout {
@@ -70,7 +70,7 @@ Column {
PropertyLabel {
text: qsTr("Step size")
- tooltip: qsTr("The step size of the spin box.")
+ tooltip: qsTr("Sets the number by which the spin box value changes.")
}
SecondColumnLayout {
@@ -88,7 +88,7 @@ Column {
PropertyLabel {
text: qsTr("Editable")
- tooltip: qsTr("Whether the spin box is editable.")
+ tooltip: qsTr("Toggles if the spin box is editable.")
}
SecondColumnLayout {
@@ -104,7 +104,8 @@ Column {
PropertyLabel {
text: qsTr("Wrap")
- tooltip: qsTr("Whether the spin box values wrap.")
+ tooltip: qsTr("Toggles if the spin box wraps around when \n"
+ + "it reaches the start or end.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml
index b12edde3ad..d5a300223d 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/SwipeViewSpecifics.qml
@@ -16,7 +16,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Interactive")
- tooltip: qsTr("Whether the view is interactive.")
+ tooltip: qsTr("Toggles if the user can interact with the view.")
}
SecondColumnLayout {
@@ -32,7 +32,7 @@ Column {
PropertyLabel {
text: qsTr("Orientation")
- tooltip: qsTr("Orientation of the view.")
+ tooltip: qsTr("Sets the orientation of the view.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TabBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TabBarSpecifics.qml
index 6281843fbe..86ee896483 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TabBarSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TabBarSpecifics.qml
@@ -17,7 +17,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Position")
- tooltip: qsTr("Position of the tab bar.")
+ tooltip: qsTr("Sets the position of the tab bar.")
}
SecondColumnLayout {
@@ -35,7 +35,8 @@ Column {
PropertyLabel {
text: qsTr("Content size")
- tooltip: qsTr("Content width and height used for calculating the total implicit size.")
+ tooltip: qsTr("Sets the width and height of the tab bar.\n"
+ + "This is used for calculating the total implicit size.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolSeparatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolSeparatorSpecifics.qml
index 5236477342..e38460aa6c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolSeparatorSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/ToolSeparatorSpecifics.qml
@@ -16,7 +16,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Orientation")
- tooltip: qsTr("The orientation of the separator.")
+ tooltip: qsTr("Sets the orientation of the separator.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TumblerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TumblerSpecifics.qml
index ad0f126d37..e461300a7d 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TumblerSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/TumblerSpecifics.qml
@@ -16,7 +16,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Visible count")
- tooltip: qsTr("The count of visible items.")
+ tooltip: qsTr("Sets the number of items in the model.")
}
SecondColumnLayout {
@@ -34,7 +34,7 @@ Column {
PropertyLabel {
text: qsTr("Current index")
- tooltip: qsTr("The index of the current item.")
+ tooltip: qsTr("Sets the index of the current item.")
}
SecondColumnLayout {
@@ -52,7 +52,8 @@ Column {
PropertyLabel {
text: qsTr("Wrap")
- tooltip: qsTr("Whether the tumbler values wrap.")
+ tooltip: qsTr("Toggles if the tumbler wraps around when it reaches the\n"
+ + "top or bottom.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/FlowSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/FlowSpecifics.qml
index 3429f03e79..32e90d9660 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/FlowSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/FlowSpecifics.qml
@@ -18,7 +18,10 @@ Column {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Spacing") }
+ PropertyLabel {
+ text: qsTr("Spacing")
+ tooltip: qsTr("Sets the spacing between flow items.")
+ }
SecondColumnLayout {
SpinBox {
@@ -33,7 +36,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Flow") }
+ PropertyLabel {
+ text: qsTr("Flow")
+ tooltip: qsTr("Sets the direction of flow items.")
+ }
SecondColumnLayout {
ComboBox {
@@ -48,7 +54,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Layout direction") }
+ PropertyLabel {
+ text: qsTr("Layout direction")
+ tooltip: qsTr("Sets in which direction items in the flow are placed.")
+ }
SecondColumnLayout {
ComboBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml
index 1b6cb3b52b..978612e19c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml
@@ -18,7 +18,10 @@ Column {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Columns") }
+ PropertyLabel {
+ text: qsTr("Columns")
+ tooltip: qsTr("Sets the number of columns in the grid.")
+ }
SecondColumnLayout {
SpinBox {
@@ -33,7 +36,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Rows") }
+ PropertyLabel {
+ text: qsTr("Rows")
+ tooltip: qsTr("Sets the number of rows in the grid.")
+ }
SecondColumnLayout {
SpinBox {
@@ -48,7 +54,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Spacing") }
+ PropertyLabel {
+ text: qsTr("Spacing")
+ tooltip: qsTr("Sets the space between grid items. The same space is applied for both rows and columns.")
+ }
SecondColumnLayout {
SpinBox {
@@ -63,7 +72,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Flow") }
+ PropertyLabel {
+ text: qsTr("Flow")
+ tooltip: qsTr("Sets in which direction items in the grid are placed.")
+ }
SecondColumnLayout {
ComboBox {
@@ -78,7 +90,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Layout direction") }
+ PropertyLabel {
+ text: qsTr("Layout direction")
+ tooltip: qsTr("Sets in which direction items in the grid are placed.")
+ }
SecondColumnLayout {
ComboBox {
@@ -93,7 +108,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Alignment H") }
+ PropertyLabel {
+ text: qsTr("Alignment H")
+ tooltip: qsTr("Sets the horizontal alignment of items in the grid.")
+ }
SecondColumnLayout {
ComboBox {
@@ -108,7 +126,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Alignment V") }
+ PropertyLabel {
+ text: qsTr("Alignment V")
+ tooltip: qsTr("Sets the vertical alignment of items in the grid.")
+ }
SecondColumnLayout {
ComboBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridViewSpecifics.qml
index ee0f49606f..0c7085bc2c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridViewSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridViewSpecifics.qml
@@ -23,7 +23,10 @@ Column {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Cell size") }
+ PropertyLabel {
+ text: qsTr("Cell size")
+ tooltip: qsTr("Sets the dimensions of cells in the grid.")
+ }
SecondColumnLayout {
SpinBox {
@@ -39,6 +42,7 @@ Column {
ControlLabel {
//: The width of the object
text: qsTr("W", "width")
+ tooltip: qsTr("Width")
}
Spacer { implicitWidth: StudioTheme.Values.controlGap }
@@ -56,6 +60,7 @@ Column {
ControlLabel {
//: The height of the object
text: qsTr("H", "height")
+ tooltip: qsTr("Height")
}
/*
TODO QDS-4836
@@ -66,7 +71,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Flow") }
+ PropertyLabel {
+ text: qsTr("Flow")
+ tooltip: qsTr("Sets the directions of the cells.")
+ }
SecondColumnLayout {
ComboBox {
@@ -81,7 +89,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Layout direction") }
+ PropertyLabel {
+ text: qsTr("Layout direction")
+ tooltip: qsTr("Sets in which direction items in the grid view are placed.")
+ }
SecondColumnLayout {
ComboBox {
@@ -98,7 +109,7 @@ Column {
PropertyLabel {
text: qsTr("Snap mode")
- tooltip: qsTr("Determines how the view scrolling will settle following a drag or flick.")
+ tooltip: qsTr("Sets how the view scrolling will settle following a drag or flick.")
}
SecondColumnLayout {
@@ -116,7 +127,7 @@ Column {
PropertyLabel {
text: qsTr("Cache")
- tooltip: qsTr("Cache buffer")
+ tooltip: qsTr("Sets in pixels how far the components are kept loaded outside the view's visible area.")
blockedByTemplate: !backendValues.cacheBuffer.isAvailable
}
@@ -163,7 +174,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Range")
- tooltip: qsTr("Highlight range")
+ tooltip: qsTr("Sets the highlight range mode.")
}
SecondColumnLayout {
@@ -181,7 +192,7 @@ Column {
PropertyLabel {
text: qsTr("Move duration")
- tooltip: qsTr("Move animation duration of the highlight delegate.")
+ tooltip: qsTr("Sets the animation duration of the highlight delegate.")
}
SectionLayout {
@@ -199,7 +210,9 @@ Column {
PropertyLabel {
text: qsTr("Preferred begin")
- tooltip: qsTr("Preferred highlight begin - must be smaller than Preferred end.")
+ tooltip: qsTr("Sets the preferred highlight beginning. It must be smaller than\n"
+ + "the <b>Preferred end</b>. Note that the user has to add a\n"
+ + "highlight component.")
}
SectionLayout {
@@ -217,7 +230,9 @@ Column {
PropertyLabel {
text: qsTr("Preferred end")
- tooltip: qsTr("Preferred highlight end - must be larger than Preferred begin.")
+ tooltip: qsTr("Sets the preferred highlight end. It must be larger than\n"
+ + "the <b>Preferred begin</b>. Note that the user has to add\n"
+ + "a highlight component.")
}
SectionLayout {
@@ -235,7 +250,7 @@ Column {
PropertyLabel {
text: qsTr("Follows current")
- tooltip: qsTr("Whether the highlight is managed by the view.")
+ tooltip: qsTr("Toggles if the view manages the highlight.")
}
SectionLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml
index 95f0560f40..943b35aeed 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml
@@ -54,7 +54,10 @@ PropertyEditorPane {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Opacity") }
+ PropertyLabel {
+ text: qsTr("Opacity")
+ tooltip: qsTr("Sets the transparency of the component.")
+ }
SecondColumnLayout {
SpinBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml
index e340ea9283..bfb2f51a3b 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LayerSection.qml
@@ -15,7 +15,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Enabled")
- tooltip: qsTr("Whether the component is layered or not.")
+ tooltip: qsTr("Toggles if the component is layered.")
}
SecondColumnLayout {
@@ -31,7 +31,7 @@ Section {
PropertyLabel {
text: qsTr("Sampler name")
- tooltip: qsTr("Name of the effect's source texture property.")
+ tooltip: qsTr("Sets the name of the effect's source texture property.")
}
SecondColumnLayout {
@@ -49,7 +49,7 @@ Section {
PropertyLabel {
text: qsTr("Samples")
- tooltip: qsTr("Allows requesting multisampled rendering in the layer.")
+ tooltip: qsTr("Sets the number of multisample renderings in the layer.")
}
SecondColumnLayout {
@@ -91,7 +91,7 @@ Section {
PropertyLabel {
text: qsTr("Effect")
- tooltip: qsTr("Applies the effect to this layer.")
+ tooltip: qsTr("Sets which effect is applied.")
}
SecondColumnLayout {
@@ -100,7 +100,6 @@ Section {
+ StudioTheme.Values.actionIndicatorWidth
width: implicitWidth
typeFilter: "QtQuick.Item"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.layer_effect
}
@@ -109,7 +108,7 @@ Section {
PropertyLabel {
text: qsTr("Format")
- tooltip: qsTr("Internal OpenGL format of the texture.")
+ tooltip: qsTr("Sets the internal OpenGL format for the texture.")
}
SecondColumnLayout {
@@ -127,7 +126,7 @@ Section {
PropertyLabel {
text: qsTr("Texture size")
- tooltip: qsTr("Requested pixel size of the layer's texture.")
+ tooltip: qsTr("Sets the requested pixel size of the layer's texture.")
}
SecondColumnLayout {
@@ -145,6 +144,7 @@ Section {
ControlLabel {
//: The width of the object
text: qsTr("W", "width")
+ tooltip: qsTr("Width.")
}
Spacer { implicitWidth: StudioTheme.Values.controlGap }
@@ -163,6 +163,7 @@ Section {
ControlLabel {
//: The height of the object
text: qsTr("H", "height")
+ tooltip: qsTr("Height.")
}
ExpandingSpacer {}
@@ -188,7 +189,7 @@ Section {
PropertyLabel {
text: qsTr("Wrap mode")
- tooltip: qsTr("OpenGL wrap modes associated with the texture.")
+ tooltip: qsTr("Sets the OpenGL wrap modes associated with the texture.")
}
SecondColumnLayout {
@@ -206,7 +207,7 @@ Section {
PropertyLabel {
text: qsTr("Mipmap")
- tooltip: qsTr("Generates mipmaps for the texture.")
+ tooltip: qsTr("Toggles if mipmaps are generated for the texture.")
}
SecondColumnLayout {
@@ -222,7 +223,7 @@ Section {
PropertyLabel {
text: qsTr("Smooth")
- tooltip: qsTr("Transforms the layer smoothly.")
+ tooltip: qsTr("Toggles if the layer transforms smoothly.")
}
SecondColumnLayout {
@@ -238,7 +239,8 @@ Section {
PropertyLabel {
text: qsTr("Source rectangle")
- tooltip: qsTr("Sets the rectangular area of the component that should be rendered into the texture.")
+ tooltip: qsTr("Sets the rectangular area of the component that should\n"
+ + "be rendered into the texture.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ListViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ListViewSpecifics.qml
index f4c6e62c3f..ec86f2f606 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ListViewSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ListViewSpecifics.qml
@@ -21,7 +21,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Orientation")
- tooltip: qsTr("Orientation of the list.")
+ tooltip: qsTr("Sets the orientation of the list.")
}
SecondColumnLayout {
@@ -39,6 +39,7 @@ Column {
PropertyLabel {
text: qsTr("Layout direction")
+ tooltip: qsTr("Sets the direction that the cells flow inside a list.")
blockedByTemplate: !backendValues.layoutDirection.isAvailable
}
@@ -58,7 +59,7 @@ Column {
PropertyLabel {
text: qsTr("Snap mode")
- tooltip: qsTr("Determines how the view scrolling will settle following a drag or flick.")
+ tooltip: qsTr("Sets how the view scrolling settles following a drag or flick.")
blockedByTemplate: !backendValues.snapMode.isAvailable
}
@@ -78,7 +79,7 @@ Column {
PropertyLabel {
text: qsTr("Spacing")
- tooltip: qsTr("Spacing between components.")
+ tooltip: qsTr("Sets the spacing between components.")
}
SecondColumnLayout {
@@ -96,7 +97,7 @@ Column {
PropertyLabel {
text: qsTr("Cache")
- tooltip: qsTr("Cache buffer.")
+ tooltip: qsTr("Sets in pixels how far the components are kept loaded outside the view's visible area.")
blockedByTemplate: !backendValues.cacheBuffer.isAvailable
}
@@ -116,7 +117,7 @@ Column {
PropertyLabel {
text: qsTr("Navigation wraps")
- tooltip: qsTr("Whether the grid wraps key navigation.")
+ tooltip: qsTr("Toggles if the grid wraps key navigation.")
blockedByTemplate: !backendValues.keyNavigationWraps.isAvailable
}
@@ -143,7 +144,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Range")
- tooltip: qsTr("Highlight range.")
+ tooltip: qsTr("Sets the highlight range mode.")
blockedByTemplate: !backendValues.highlightRangeMode.isAvailable
}
@@ -163,7 +164,8 @@ Column {
PropertyLabel {
text: qsTr("Move duration")
- tooltip: qsTr("Move animation duration of the highlight delegate.")
+ tooltip: qsTr("Sets the animation duration of the highlight delegate when\n"
+ + "it is moved.")
blockedByTemplate: !backendValues.highlightMoveDuration.isAvailable
}
@@ -183,7 +185,8 @@ Column {
PropertyLabel {
text: qsTr("Move velocity")
- tooltip: qsTr("Move animation velocity of the highlight delegate.")
+ tooltip: qsTr("Sets the animation velocity of the highlight delegate when\n"
+ + "it is moved.")
blockedByTemplate: !backendValues.highlightMoveVelocity.isAvailable
}
@@ -203,7 +206,8 @@ Column {
PropertyLabel {
text: qsTr("Resize duration")
- tooltip: qsTr("Resize animation duration of the highlight delegate.")
+ tooltip: qsTr("Sets the animation duration of the highlight delegate when\n"
+ + "it is resized.")
blockedByTemplate: !backendValues.highlightResizeDuration.isAvailable
}
@@ -223,7 +227,8 @@ Column {
PropertyLabel {
text: qsTr("Resize velocity")
- tooltip: qsTr("Resize animation velocity of the highlight delegate.")
+ tooltip: qsTr("Sets the animation velocity of the highlight delegate when\n"
+ + "it is resized.")
blockedByTemplate: !backendValues.highlightResizeVelocity.isAvailable
}
@@ -243,7 +248,9 @@ Column {
PropertyLabel {
text: qsTr("Preferred begin")
- tooltip: qsTr("Preferred highlight begin - must be smaller than Preferred end.")
+ tooltip: qsTr("Sets the preferred highlight beginning. It must be smaller than\n"
+ + "the <b>Preferred end</b>. Note that the user has to add\n"
+ + "a highlight component.")
blockedByTemplate: !backendValues.preferredHighlightBegin.isAvailable
}
@@ -263,7 +270,9 @@ Column {
PropertyLabel {
text: qsTr("Preferred end")
- tooltip: qsTr("Preferred highlight end - must be larger than Preferred begin.")
+ tooltip: qsTr("Sets the preferred highlight end. It must be larger than\n"
+ + "the <b>Preferred begin</b>. Note that the user has to add\n"
+ + "a highlight component.")
blockedByTemplate: !backendValues.preferredHighlightEnd.isAvailable
}
@@ -283,7 +292,7 @@ Column {
PropertyLabel {
text: qsTr("Follows current")
- tooltip: qsTr("Whether the highlight is managed by the view.")
+ tooltip: qsTr("Toggles if the view manages the highlight.")
blockedByTemplate: !backendValues.highlightFollowsCurrentItem.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml
index 2d847e0e50..4d61ee3f35 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml
@@ -55,7 +55,6 @@ Column {
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "Component"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.sourceComponent
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml
index 176ed009df..ecb82f807e 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml
@@ -20,7 +20,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Enable")
- tooltip: qsTr("Accepts mouse events.")
+ tooltip: qsTr("Sets how the mouse can interact with the area.")
blockedByTemplate: !backendValues.enabled.isAvailable
}
@@ -48,7 +48,7 @@ Column {
PropertyLabel {
text: qsTr("Accepted buttons")
- tooltip: qsTr("Mouse buttons that the mouse area reacts to.")
+ tooltip: qsTr("Sets which mouse buttons the area reacts to.")
blockedByTemplate: !backendValues.acceptedButtons.isAvailable
}
@@ -68,7 +68,7 @@ Column {
PropertyLabel {
text: qsTr("Cursor shape")
- tooltip: qsTr("Cursor shape for this mouse area.")
+ tooltip: qsTr("Sets which mouse cursor to display on this area.")
blockedByTemplate: !backendValues.cursorShape.isAvailable
}
@@ -93,7 +93,7 @@ Column {
PropertyLabel {
text: qsTr("Hold interval")
- tooltip: qsTr("Overrides the elapsed time in milliseconds before pressAndHold signal is emitted.")
+ tooltip: qsTr("Sets the time before the pressAndHold signal is registered when you press the area.")
}
SecondColumnLayout {
@@ -111,7 +111,7 @@ Column {
PropertyLabel {
text: qsTr("Scroll gesture")
- tooltip: qsTr("Responds to scroll gestures from non-mouse devices.")
+ tooltip: qsTr("Toggles if scroll gestures from non-mouse devices are supported.")
blockedByTemplate: !backendValues.scrollGestureEnabled.isAvailable
}
@@ -129,7 +129,7 @@ Column {
PropertyLabel {
text: qsTr("Prevent stealing")
- tooltip: qsTr("Stops mouse events from being stolen from this mouse area.")
+ tooltip: qsTr("Toggles if mouse events can be stolen from this area.")
blockedByTemplate: !backendValues.preventStealing.isAvailable
}
@@ -147,7 +147,7 @@ Column {
PropertyLabel {
text: qsTr("Propagate events")
- tooltip: qsTr("Automatically propagates composed mouse events to other mouse areas.")
+ tooltip: qsTr("Toggles if composed mouse events should be propagated to other mouse areas overlapping this area.")
blockedByTemplate: !backendValues.propagateComposedEvents.isAvailable
}
@@ -175,13 +175,12 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Target")
- tooltip: qsTr("ID of the component to drag.")
+ tooltip: qsTr("Sets the component to have drag functionalities.")
}
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "QtQuick.QtObject"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.drag_target
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
@@ -192,7 +191,7 @@ Column {
PropertyLabel {
text: qsTr("Axis")
- tooltip: qsTr("Whether dragging can be done horizontally, vertically, or both.")
+ tooltip: qsTr("Sets in which directions the dragging work.")
}
SecondColumnLayout {
@@ -210,7 +209,7 @@ Column {
PropertyLabel {
text: qsTr("Threshold")
- tooltip: qsTr("Threshold in pixels of when the drag operation should start.")
+ tooltip: qsTr("Sets a threshold after which the drag starts to work.")
}
SecondColumnLayout {
@@ -228,7 +227,7 @@ Column {
PropertyLabel {
text: qsTr("Filter children")
- tooltip: qsTr("Whether dragging overrides descendant mouse areas.")
+ tooltip: qsTr("Toggles if the dragging overrides descendant mouse areas.")
}
SecondColumnLayout {
@@ -244,8 +243,7 @@ Column {
PropertyLabel {
text: qsTr("Smoothed")
- tooltip: qsTr("Moves targets only after the drag operation has started.\n"
- + "When disabled, moves targets straight to the current mouse position.")
+ tooltip: qsTr("Toggles if the move is smoothly animated.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PathViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PathViewSpecifics.qml
index 2f80266697..d2eae62c5b 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PathViewSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/PathViewSpecifics.qml
@@ -20,7 +20,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Interactive")
- tooltip: qsTr("Allows users to drag or flick a path view.")
+ tooltip: qsTr("Toggles if the path view allows drag or flick.")
}
SecondColumnLayout {
@@ -34,7 +34,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Drag margin") }
+ PropertyLabel {
+ text: qsTr("Drag margin")
+ tooltip: qsTr("Sets a margin within which the drag function also works even without clicking the item itself.")
+ }
SecondColumnLayout {
SpinBox {
@@ -49,7 +52,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Flick deceleration") }
+ PropertyLabel {
+ text: qsTr("Flick deceleration")
+ tooltip: qsTr("Sets the rate by which a flick action slows down after performing.")
+ }
SecondColumnLayout {
SpinBox {
@@ -66,7 +72,7 @@ Column {
PropertyLabel {
text: qsTr("Offset")
- tooltip: qsTr("Specifies how far along the path the items are from their initial positions. This is a real number that ranges from 0.0 to the count of items in the model.")
+ tooltip: qsTr("Sets how far along the path the items are from their initial position.")
}
SecondColumnLayout {
@@ -84,7 +90,7 @@ Column {
PropertyLabel {
text: qsTr("Item count")
- tooltip: qsTr("Number of items visible on the path at any one time.")
+ tooltip: qsTr("Sets the number of items visible at once along the path.")
}
SecondColumnLayout {
@@ -111,7 +117,7 @@ Column {
SectionLayout {
PropertyLabel {
text: qsTr("Range")
- tooltip: qsTr("Highlight range")
+ tooltip: qsTr("Sets the highlight range mode.")
}
SecondColumnLayout {
@@ -129,7 +135,8 @@ Column {
PropertyLabel {
text: qsTr("Move duration")
- tooltip: qsTr("Move animation duration of the highlight delegate.")
+ tooltip: qsTr("Sets the animation duration of the highlight delegate when\n"
+ + "it is moved.")
}
SecondColumnLayout {
@@ -147,7 +154,9 @@ Column {
PropertyLabel {
text: qsTr("Preferred begin")
- tooltip: qsTr("Preferred highlight begin - must be smaller than Preferred end. Note that the user has to add a highlight component.")
+ tooltip: qsTr("Sets the preferred highlight beginning. It must be smaller than\n"
+ + "the <b>Preferred end</b>. Note that the user has to add\n"
+ + "a highlight component.")
}
SecondColumnLayout {
@@ -166,7 +175,9 @@ Column {
PropertyLabel {
text: qsTr("Preferred end")
- tooltip: qsTr("Preferred highlight end - must be larger than Preferred begin. Note that the user has to add a highlight component.")
+ tooltip: qsTr("Sets the preferred highlight end. It must be larger than\n"
+ + "the <b>Preferred begin</b>. Note that the user has to add\n"
+ + "a highlight component.")
}
SecondColumnLayout {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RectangleSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RectangleSpecifics.qml
index 1f5189e6a5..cdcea69e06 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RectangleSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RectangleSpecifics.qml
@@ -16,7 +16,10 @@ Column {
caption: qsTr("Rectangle")
SectionLayout {
- PropertyLabel { text: qsTr("Fill color") }
+ PropertyLabel {
+ text: qsTr("Fill color")
+ tooltip: qsTr("Sets the color for the background.")
+ }
ColorEditor {
backendValue: backendValues.color
@@ -25,6 +28,7 @@ Column {
PropertyLabel {
text: qsTr("Border color")
+ tooltip: qsTr("Sets the color for the border.")
visible: backendValues.border_color.isAvailable
}
@@ -36,6 +40,7 @@ Column {
PropertyLabel {
text: qsTr("Border width")
+ tooltip: qsTr("Sets the border width.")
blockedByTemplate: !backendValues.border_width.isAvailable
}
@@ -51,7 +56,10 @@ Column {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Radius") }
+ PropertyLabel {
+ text: qsTr("Radius")
+ tooltip: qsTr("Sets the radius by which the corners get rounded.")
+ }
SecondColumnLayout {
SpinBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml
index 363a70b470..a70beeb224 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml
@@ -42,7 +42,6 @@ Column {
SecondColumnLayout {
ItemFilterComboBox {
typeFilter: "Component"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: backendValues.delegate
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RowSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RowSpecifics.qml
index 388a3c0dc8..ae03cb0512 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RowSpecifics.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RowSpecifics.qml
@@ -18,7 +18,10 @@ Column {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Spacing") }
+ PropertyLabel {
+ text: qsTr("Spacing")
+ tooltip: qsTr("Sets the spacing between items in the row.")
+ }
SecondColumnLayout {
SpinBox {
@@ -35,6 +38,7 @@ Column {
PropertyLabel {
text: qsTr("Layout direction")
+ tooltip: qsTr("Sets in which direction items in the row are placed.")
blockedByTemplate: !backendValues.layoutDirection.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml
index cf9d67860a..b76666f7bc 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml
@@ -15,21 +15,30 @@ Section {
property bool isTextInput: false
SectionLayout {
- PropertyLabel { text: qsTr("Selection color") }
+ PropertyLabel {
+ text: qsTr("Selection color")
+ tooltip: qsTr("Sets the background color of selected text.")
+ }
ColorEditor {
backendValue: backendValues.selectionColor
supportGradient: false
}
- PropertyLabel { text: qsTr("Selected text color") }
+ PropertyLabel {
+ text: qsTr("Selected text color")
+ tooltip: qsTr("Sets the color of selected text.")
+ }
ColorEditor {
backendValue: backendValues.selectedTextColor
supportGradient: false
}
- PropertyLabel { text: qsTr("Selection mode") }
+ PropertyLabel {
+ text: qsTr("Selection mode")
+ tooltip: qsTr("Sets the way text is selected with the mouse.")
+ }
SecondColumnLayout {
ComboBox {
@@ -47,6 +56,7 @@ Section {
PropertyLabel {
visible: textInputSection.isTextInput
text: qsTr("Input mask")
+ tooltip: qsTr("Sets the allowed characters.")
}
SecondColumnLayout {
@@ -66,6 +76,7 @@ Section {
PropertyLabel {
visible: textInputSection.isTextInput
text: qsTr("Echo mode")
+ tooltip: qsTr("Sets the visibility mode.")
}
SecondColumnLayout {
@@ -86,7 +97,7 @@ Section {
PropertyLabel {
visible: textInputSection.isTextInput
text: qsTr("Password character")
- tooltip: qsTr("Character displayed when users enter passwords.")
+ tooltip: qsTr("Sets which character to display when passwords are entered.")
}
SecondColumnLayout {
@@ -154,7 +165,7 @@ Section {
PropertyLabel {
visible: textInputSection.isTextInput
text: qsTr("Maximum length")
- tooltip: qsTr("Maximum permitted length of the text in the Text Input.")
+ tooltip: qsTr("Sets the maximum length of the text.")
}
SecondColumnLayout {
@@ -183,21 +194,31 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Read only") }
+ PropertyLabel {
+ text: qsTr("Read only")
+ tooltip: qsTr("Toggles if the text allows edits.")
+ }
FlagItem { backendValue: backendValues.readOnly }
- PropertyLabel { text: qsTr("Cursor visible") }
+ PropertyLabel {
+ text: qsTr("Cursor visible")
+ tooltip: qsTr("Toggles if the cursor is visible.")
+ }
FlagItem { backendValue: backendValues.cursorVisible }
- PropertyLabel { text: qsTr("Focus on press") }
+ PropertyLabel {
+ text: qsTr("Focus on press")
+ tooltip: qsTr("Toggles if the text is focused on mouse click.")
+ }
FlagItem { backendValue: backendValues.activeFocusOnPress }
PropertyLabel {
visible: textInputSection.isTextInput
text: qsTr("Auto scroll")
+ tooltip: qsTr("Toggles if the text scrolls when it exceeds its boundary.")
}
FlagItem {
@@ -205,15 +226,24 @@ Section {
backendValue: backendValues.autoScroll
}
- PropertyLabel { text: qsTr("Overwrite mode") }
+ PropertyLabel {
+ text: qsTr("Overwrite mode")
+ tooltip: qsTr("Toggles if overwriting text is allowed.")
+ }
FlagItem { backendValue: backendValues.overwriteMode }
- PropertyLabel { text: qsTr("Persistent selection") }
+ PropertyLabel {
+ text: qsTr("Persistent selection")
+ tooltip: qsTr("Toggles if the text should remain selected after moving the focus elsewhere.")
+ }
FlagItem { backendValue: backendValues.persistentSelection }
- PropertyLabel { text: qsTr("Select by mouse") }
+ PropertyLabel {
+ text: qsTr("Select by mouse")
+ tooltip: qsTr("Toggles if the text can be selected with the mouse.")
+ }
FlagItem { backendValue: backendValues.selectByMouse }
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSection.qml
new file mode 100644
index 0000000000..ceb4179360
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSection.qml
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Section {
+ caption: qsTr("Baked Lightmap")
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Enabled")
+ tooltip: qsTr("When false, the lightmap generated for the model is not stored during lightmap baking,\neven if the key is set to a non-empty value.")
+ }
+
+ SecondColumnLayout {
+ CheckBox {
+ text: backendValues.enabled.valueToString
+ backendValue: backendValues.enabled
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Key")
+ tooltip: qsTr("Sets the filename base for baked lightmap files for the model.\nNo other Model in the scene can use the same key.")
+ }
+
+ SecondColumnLayout {
+ LineEdit {
+ backendValue: backendValues.key
+ showTranslateCheckBox: false
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ width: implicitWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Load Prefix")
+ tooltip: qsTr("Sets the folder where baked lightmap files are generated.\nIt should be a relative path.")
+ }
+
+ SecondColumnLayout {
+ LineEdit {
+ backendValue: backendValues.loadPrefix
+ showTranslateCheckBox: false
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ width: implicitWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSpecifics.qml
new file mode 100644
index 0000000000..cca06c421a
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/BakedLightmapSpecifics.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+
+Column {
+ width: parent.width
+
+ BakedLightmapSection {
+ width: parent.width
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSection.qml
new file mode 100644
index 0000000000..30a69e23b4
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSection.qml
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Section {
+ caption: qsTr("Ambient Sound")
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Source")
+ tooltip: qsTr("The source file for the sound to be played.")
+ }
+
+ SecondColumnLayout {
+ UrlChooser {
+ id: sourceUrlChooser
+ backendValue: backendValues.source
+ filter: "*.wav *.mp3"
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Volume")
+ tooltip: qsTr("Set the overall volume for this sound source.\nValues between 0 and 1 will attenuate the sound, while values above 1 provide an additional gain boost.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.volume
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Loops")
+ tooltip: qsTr("Sets how often the sound is played before the player stops.\nBind to AmbientSound.Infinite to loop the current sound forever.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 1
+ maximumValue: 999999
+ decimals: 0
+ stepSize: 1
+ backendValue: backendValues.loops
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Auto Play")
+ tooltip: qsTr("Sets whether the sound should automatically start playing when a source gets specified.")
+ }
+
+ SecondColumnLayout {
+ CheckBox {
+ text: backendValues.autoPlay.valueToString
+ backendValue: backendValues.autoPlay
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSpecifics.qml
new file mode 100644
index 0000000000..8b3946ab87
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AmbientSoundSpecifics.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+
+Column {
+ width: parent.width
+
+ AmbientSoundSection {
+ width: parent.width
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSection.qml
new file mode 100644
index 0000000000..88b50c7872
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSection.qml
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Section {
+ caption: qsTr("Audio Engine")
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Master Volume")
+ tooltip: qsTr("Sets or returns overall volume being used to render the sound field.\nValues between 0 and 1 will attenuate the sound, while values above 1 provide an additional gain boost.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.masterVolume
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Output Mode")
+ tooltip: qsTr("Sets the current output mode of the engine.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioEngine"
+ model: ["Surround", "Stereo", "Headphone"]
+ backendValue: backendValues.outputMode
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Output Device")
+ tooltip: qsTr("Sets the device that is being used for outputting the sound field.")
+ }
+
+ SecondColumnLayout {
+ ItemFilterComboBox {
+ typeFilter: "QtMultimedia.AudioDevice"
+ backendValue: backendValues.outputDevice
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSpecifics.qml
new file mode 100644
index 0000000000..b3764868d3
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioEngineSpecifics.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+
+Column {
+ width: parent.width
+
+ AudioEngineSection {
+ width: parent.width
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioListenerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioListenerSpecifics.qml
new file mode 100644
index 0000000000..9be94856de
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioListenerSpecifics.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+
+Column {
+ width: parent.width
+
+ // AudioListener doesn't have any properties itself, just ones inherited from Node
+
+ NodeSection {
+ width: parent.width
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSection.qml
new file mode 100644
index 0000000000..bfe814211f
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSection.qml
@@ -0,0 +1,277 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Section {
+ id: root
+ caption: qsTr("Audio Room")
+ width: parent.width
+
+ property var materialModel: [
+ "Transparent", "AcousticCeilingTiles", "BrickBare", "BrickPainted",
+ "ConcreteBlockCoarse", "ConcreteBlockPainted", "CurtainHeavy",
+ "FiberGlassInsulation", "GlassThin", "GlassThick", "Grass",
+ "LinoleumOnConcrete", "Marble", "Metal", "ParquetOnConcrete",
+ "PlasterRough", "PlasterSmooth", "PlywoodPanel", "PolishedConcreteOrTile",
+ "Sheetrock", "WaterOrIceSurface", "WoodCeiling", "WoodPanel", "Uniform"
+ ]
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Dimensions")
+ tooltip: qsTr("Sets the dimensions of the room in 3D space.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.dimensions_x
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "X"
+ color: StudioTheme.Values.theme3DAxisXColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.dimensions_y
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Y"
+ color: StudioTheme.Values.theme3DAxisYColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.dimensions_z
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Z"
+ color: StudioTheme.Values.theme3DAxisZColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Reflection Gain")
+ tooltip: qsTr("Sets the gain factor for reflections generated in this room.\nA value from 0 to 1 will dampen reflections, while a value larger than 1 will apply a gain to reflections, making them louder.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.reflectionGain
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Reverb Gain")
+ tooltip: qsTr("Sets the gain factor for reverb generated in this room.\nA value from 0 to 1 will dampen reverb, while a value larger than 1 will apply a gain to the reverb, making it louder.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.reverbGain
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Reverb Time")
+ tooltip: qsTr("Sets the factor to be applies to all reverb timings generated for this room.\nLarger values will lead to longer reverb timings, making the room sound larger.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.reverbTime
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Reverb Brightness")
+ tooltip: qsTr("Sets the brightness factor to be applied to the generated reverb.\nA positive value will increase reverb for higher frequencies and dampen lower frequencies, a negative value does the reverse.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -999999
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.reverbBrightness
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Left Material")
+ tooltip: qsTr("Sets the material to use for the left (negative x) side of the room.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioRoom"
+ model: root.materialModel
+ backendValue: backendValues.leftMaterial
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+
+ PropertyLabel {
+ text: qsTr("Right Material")
+ tooltip: qsTr("Sets the material to use for the right (positive x) side of the room.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioRoom"
+ model: root.materialModel
+ backendValue: backendValues.rightMaterial
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+
+ PropertyLabel {
+ text: qsTr("Floor Material")
+ tooltip: qsTr("Sets the material to use for the floor (negative y) side of the room.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioRoom"
+ model: root.materialModel
+ backendValue: backendValues.floorMaterial
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+
+ PropertyLabel {
+ text: qsTr("Ceiling Material")
+ tooltip: qsTr("Sets the material to use for the ceiling (positive y) side of the room.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioRoom"
+ model: root.materialModel
+ backendValue: backendValues.ceilingMaterial
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+
+ PropertyLabel {
+ text: qsTr("Back Material")
+ tooltip: qsTr("Sets the material to use for the back (negative z) side of the room.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioRoom"
+ model: root.materialModel
+ backendValue: backendValues.backMaterial
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+
+ PropertyLabel {
+ text: qsTr("Front Material")
+ tooltip: qsTr("Sets the material to use for the front (positive z) side of the room.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "AudioRoom"
+ model: root.materialModel
+ backendValue: backendValues.frontMaterial
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSpecifics.qml
new file mode 100644
index 0000000000..6f8afd7e5d
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/AudioRoomSpecifics.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+
+Column {
+ width: parent.width
+
+ AudioRoomSection {
+ width: parent.width
+ }
+
+ NodeSection {
+ width: parent.width
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/NodeSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/NodeSection.qml
new file mode 100644
index 0000000000..b48b8e24f4
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/NodeSection.qml
@@ -0,0 +1,350 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Column {
+ width: parent.width
+
+ Section {
+ width: parent.width
+ caption: qsTr("Visibility")
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Visibility")
+ tooltip: qsTr("Sets the local visibility of the node.")
+ }
+
+ SecondColumnLayout {
+ // ### should be a slider
+ CheckBox {
+ text: qsTr("Visible")
+ backendValue: backendValues.visible
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Opacity")
+ tooltip: qsTr("Sets the local opacity value of the node.")
+ }
+
+ SecondColumnLayout {
+ // ### should be a slider
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 1
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.opacity
+ sliderIndicatorVisible: true
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+ }
+
+ Section {
+ id: transformSection
+ width: parent.width
+ caption: qsTr("Transform")
+
+ ColumnLayout {
+ spacing: StudioTheme.Values.transform3DSectionSpacing
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Translation")
+ tooltip: qsTr("Sets the translation of the node.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.x
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "X"
+ color: StudioTheme.Values.theme3DAxisXColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.y
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Y"
+ color: StudioTheme.Values.theme3DAxisYColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.z
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Z"
+ color: StudioTheme.Values.theme3DAxisZColor
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Rotation")
+ tooltip: qsTr("Sets the rotation of the node in degrees.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.eulerRotation_x
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "X"
+ color: StudioTheme.Values.theme3DAxisXColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.eulerRotation_y
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Y"
+ color: StudioTheme.Values.theme3DAxisYColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.eulerRotation_z
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Z"
+ color: StudioTheme.Values.theme3DAxisZColor
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Scale")
+ tooltip: qsTr("Sets the scale of the node.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.scale_x
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "X"
+ color: StudioTheme.Values.theme3DAxisXColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.scale_y
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Y"
+ color: StudioTheme.Values.theme3DAxisYColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.scale_z
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Z"
+ color: StudioTheme.Values.theme3DAxisZColor
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Pivot")
+ tooltip: qsTr("Sets the pivot of the node.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.pivot_x
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "X"
+ color: StudioTheme.Values.theme3DAxisXColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.pivot_y
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Y"
+ color: StudioTheme.Values.theme3DAxisYColor
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {}
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: -9999999
+ maximumValue: 9999999
+ decimals: 2
+ backendValue: backendValues.pivot_z
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
+
+ ControlLabel {
+ text: "Z"
+ color: StudioTheme.Values.theme3DAxisZColor
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml
new file mode 100644
index 0000000000..9d753700df
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml
@@ -0,0 +1,234 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Section {
+ caption: qsTr("Spatial Sound")
+ width: parent.width
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Source")
+ tooltip: qsTr("The source file for the sound to be played.")
+ }
+
+ SecondColumnLayout {
+ UrlChooser {
+ id: sourceUrlChooser
+ backendValue: backendValues.source
+ filter: "*.wav *.mp3"
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Volume")
+ tooltip: qsTr("Set the overall volume for this sound source.\nValues between 0 and 1 will attenuate the sound, while values above 1 provide an additional gain boost.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.volume
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Loops")
+ tooltip: qsTr("Sets how often the sound is played before the player stops.\nBind to SpatialSound.Infinite to loop the current sound forever.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 1
+ maximumValue: 999999
+ decimals: 0
+ stepSize: 1
+ backendValue: backendValues.loops
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Auto Play")
+ tooltip: qsTr("Sets whether the sound should automatically start playing when a source gets specified.")
+ }
+
+ SecondColumnLayout {
+ CheckBox {
+ text: backendValues.autoPlay.valueToString
+ backendValue: backendValues.autoPlay
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Distance Model")
+ tooltip: qsTr("Sets thow the volume of the sound scales with distance to the listener.")
+ }
+
+ SecondColumnLayout {
+ ComboBox {
+ scope: "SpatialSound"
+ model: ["Logarithmic", "Linear", "ManualAttenuation"]
+ backendValue: backendValues.distanceModel
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+
+ }
+
+ PropertyLabel {
+ text: qsTr("Size")
+ tooltip: qsTr("Set the size of the sound source.\nIf the listener is closer to the sound object than the size, volume will stay constant.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 1
+ backendValue: backendValues.size
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Distance Cutoff")
+ tooltip: qsTr("Set the distance beyond which sound coming from the source will cutoff.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 1
+ backendValue: backendValues.distanceCutoff
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Manual Attenuation")
+ tooltip: qsTr("Set the manual attenuation factor if distanceModel is set to ManualAttenuation.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.manualAttenuation
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Occlusion Intensity")
+ tooltip: qsTr("Set how much the object is occluded.\n0 implies the object is not occluded at all, while a large number implies a large occlusion.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 0.1
+ backendValue: backendValues.occlusionIntensity
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Directivity")
+ tooltip: qsTr("Set the directivity of the sound source.\nA value of 0 implies that the sound is emitted equally in all directions, while a value of 1 implies that the source mainly emits sound in the forward direction.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 1
+ decimals: 2
+ stepSize: 0.01
+ backendValue: backendValues.directivity
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Directivity Order")
+ tooltip: qsTr("Set the order of the directivity of the sound source.\nA higher order implies a sharper localization of the sound cone.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 1
+ maximumValue: 999999
+ decimals: 2
+ stepSize: 1
+ backendValue: backendValues.directivityOrder
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Near Field Gain")
+ tooltip: qsTr("Set the near field gain for the sound source.\nA near field gain of 1 will raise the volume of the sound signal by approx 20 dB for distances very close to the listener.")
+ }
+
+ SecondColumnLayout {
+ SpinBox {
+ minimumValue: 0
+ maximumValue: 1
+ decimals: 2
+ stepSize: 0.01
+ backendValue: backendValues.nearFieldGain
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSpecifics.qml
new file mode 100644
index 0000000000..1699b3b66e
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSpecifics.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+
+Column {
+ width: parent.width
+
+ SpatialSoundSection {
+ width: parent.width
+ }
+
+ NodeSection {
+ width: parent.width
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml
index 5c71f5a765..85ce6f57e8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml
@@ -38,7 +38,10 @@ Section {
onPixelSizeChanged: sizeWidget.setPointPixelSize()
SectionLayout {
- PropertyLabel { text: qsTr("Text") }
+ PropertyLabel {
+ text: qsTr("Text")
+ tooltip: qsTr("Sets the text to display.")
+ }
SecondColumnLayout {
LineEdit {
@@ -103,7 +106,10 @@ Section {
}
}
- PropertyLabel { text: qsTr("Font") }
+ PropertyLabel {
+ text: qsTr("Font")
+ tooltip: qsTr("Sets the font of the text.")
+ }
SecondColumnLayout {
FontComboBox {
@@ -120,7 +126,7 @@ Section {
PropertyLabel {
text: qsTr("Style name")
- tooltip: qsTr("Font's style.")
+ tooltip: qsTr("Sets the style of the selected font. This is prioritized over <b>Weight</b> and <b>Emphasis</b>.")
enabled: styleNameComboBox.model.length
blockedByTemplate: !styleNameComboBox.backendValue.isAvailable
}
@@ -141,7 +147,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Size") }
+ PropertyLabel {
+ text: qsTr("Size")
+ tooltip: qsTr("Sets the font size in pixels or points.")
+ }
SecondColumnLayout {
id: sizeWidget
@@ -220,7 +229,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Text color") }
+ PropertyLabel {
+ text: qsTr("Text color")
+ tooltip: qsTr("Sets the text color.")
+ }
ColorEditor {
backendValue: backendValues.color
@@ -229,7 +241,7 @@ Section {
PropertyLabel {
text: qsTr("Weight")
- tooltip: qsTr("Font's weight.")
+ tooltip: qsTr("Sets the overall thickness of the font.")
enabled: !styleNameComboBox.styleSet
}
@@ -249,6 +261,7 @@ Section {
PropertyLabel {
text: qsTr("Emphasis")
+ tooltip: qsTr("Sets the text to bold, italic, underlined, or strikethrough.")
enabled: !styleNameComboBox.styleSet
}
@@ -260,7 +273,10 @@ Section {
enabled: !styleNameComboBox.styleSet
}
- PropertyLabel { text: qsTr("Alignment H") }
+ PropertyLabel {
+ text: qsTr("Alignment H")
+ tooltip: qsTr("Sets the horizontal alignment position.")
+ }
SecondColumnLayout {
AlignmentHorizontalButtons {}
@@ -268,7 +284,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Alignment V") }
+ PropertyLabel {
+ text: qsTr("Alignment V")
+ tooltip: qsTr("Sets the vertical alignment position.")
+ }
SecondColumnLayout {
AlignmentVerticalButtons { visible: root.showVerticalAlignment }
@@ -278,7 +297,7 @@ Section {
PropertyLabel {
text: qsTr("Letter spacing")
- tooltip: qsTr("Letter spacing for the font.")
+ tooltip: qsTr("Sets the letter spacing for the text.")
blockedByTemplate: !root.getBackendValue("letterSpacing").isAvailable
}
@@ -299,7 +318,7 @@ Section {
PropertyLabel {
text: qsTr("Word spacing")
- tooltip: qsTr("Word spacing for the font.")
+ tooltip: qsTr("Sets the word spacing for the text.")
blockedByTemplate: !root.getBackendValue("wordSpacing").isAvailable
}
@@ -321,7 +340,7 @@ Section {
PropertyLabel {
visible: root.showLineHeight
text: qsTr("Line height")
- tooltip: qsTr("Line height for the text.")
+ tooltip: qsTr("Sets the line height for the text.")
blockedByTemplate: !lineHeightSpinBox.enabled
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml
index d8520d5cad..96745a2502 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml
@@ -15,18 +15,20 @@ SecondColumnLayout {
property color color
property bool supportGradient: false
property variant backendValue
+
property variant value: {
- if (colorEditor.backendValue === undefined
- || colorEditor.backendValue.value === undefined)
+ if (!colorEditor.backendValue || !colorEditor.backendValue.value)
return "white" // default color for Rectangle
- if (colorEditor.isVector3D)
+ if (colorEditor.isVector3D) {
return Qt.rgba(colorEditor.backendValue.value.x,
colorEditor.backendValue.value.y,
colorEditor.backendValue.value.z, 1)
- else
- return colorEditor.backendValue.value
+ }
+
+ return colorEditor.backendValue.value
}
+
property alias gradientPropertyName: popupLoader.gradientPropertyName
property alias gradientThumbnail: gradientThumbnail
@@ -40,6 +42,8 @@ SecondColumnLayout {
property bool __block: false
+ property string caption // Legacy Qt5 specifics sheets compatibility
+
function resetShapeColor() {
colorEditor.backendValue.resetValue()
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLogic.qml
index ed3331d296..f72a69ed42 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLogic.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorLogic.qml
@@ -9,20 +9,11 @@ QtObject {
property variant backendValue
property color textColor: StudioTheme.Values.themeTextColor
- property variant valueFromBackend: root.backendValue === undefined ? 0 : root.backendValue.value
+ property variant valueFromBackend: root.backendValue?.value ?? 0
property bool baseStateFlag: isBaseState
- property bool isInModel: {
- if (root.backendValue !== undefined && root.backendValue.isInModel !== undefined)
- return root.backendValue.isInModel
+ property bool isInModel: root.backendValue?.isInModel ?? false
+ property bool isInSubState: root.backendValue?.isInSubState ?? false
- return false
- }
- property bool isInSubState: {
- if (root.backendValue !== undefined && root.backendValue.isInSubState !== undefined)
- return root.backendValue.isInSubState
-
- return false
- }
property bool highlight: root.textColor === root.__changedTextColor
property bool errorState: false
@@ -38,7 +29,7 @@ QtObject {
onErrorStateChanged: root.evaluate()
function evaluate() {
- if (root.backendValue === undefined)
+ if (!root.backendValue)
return
if (root.errorState) {
@@ -47,15 +38,11 @@ QtObject {
}
if (root.baseStateFlag) {
- if (root.backendValue.isInModel)
- root.textColor = root.__changedTextColor
- else
- root.textColor = root.__defaultTextColor
+ root.textColor = root.backendValue.isInModel ? root.__changedTextColor
+ : root.__defaultTextColor
} else {
- if (root.backendValue.isInSubState)
- root.textColor = StudioTheme.Values.themeChangedStateText
- else
- root.textColor = root.__defaultTextColor
+ root.textColor = root.backendValue.isInSubState ? StudioTheme.Values.themeChangedStateText
+ : root.__defaultTextColor
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml
index e91a8f86aa..1820af7958 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml
@@ -19,7 +19,7 @@ StudioControls.ComboBox {
onModelChanged: colorLogic.invalidate()
- hasActiveDrag: comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag
+ hasActiveDrag: comboBox.backendValue?.hasActiveDrag ?? false
// This is available in all editors.
@@ -69,7 +69,8 @@ StudioControls.ComboBox {
onExited: comboBox.hasActiveHoverDrag = false
- onDropped: {
+ onDropped: (drag) => {
+ drag.accept()
comboBox.backendValue.commitDrop(dropArea.dropData)
comboBox.hasActiveHoverDrag = false
}
@@ -119,7 +120,7 @@ StudioControls.ComboBox {
break
case ComboBox.ValueType.Enum:
default:
- if (comboBox.backendValue === undefined)
+ if (!comboBox.backendValue)
break
var enumString = comboBox.backendValue.enumeration
@@ -145,11 +146,15 @@ StudioControls.ComboBox {
if (!comboBox.__isCompleted)
return
- let inputValue = comboBox.editText
-
- let index = comboBox.find(inputValue)
- if (index !== -1)
- inputValue = comboBox.textAt(index)
+ let inputText = comboBox.editText
+ let inputValue = inputText;
+ let index = comboBox.find(inputText)
+ if (index !== -1) {
+ let modelIdx = comboBox.model.index(index)
+ inputValue = comboBox.valueRole
+ ? comboBox.model.data(modelIdx, comboBox.valueRole)
+ : comboBox.textAt(index)
+ }
comboBox.backendValue.value = inputValue
@@ -166,6 +171,18 @@ StudioControls.ComboBox {
if (comboBox.manualMapping)
return
+ if (comboBox.valueRole && comboBox.textRole !== comboBox.valueRole) {
+ let inputText = comboBox.currentText
+ let inputValue = comboBox.currentValue
+ let index = comboBox.find(inputText)
+ if (index !== -1) {
+ let modelIdx = comboBox.model.index(index)
+ inputValue = comboBox.model.data(modelIdx, comboBox.valueRole)
+ }
+ comboBox.backendValue.value = inputValue
+ return
+ }
+
switch (comboBox.valueType) {
case ComboBox.ValueType.String:
comboBox.backendValue.value = comboBox.currentText
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml
index 8310e3c40d..7b9be3890e 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Controller.qml
@@ -9,6 +9,7 @@ QtObject {
property Item mainScrollView
- signal collapseAll()
- signal expandAll()
+ signal collapseAll(string category)
+ signal expandAll(string category)
+ signal closeContextMenu()
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml
index 4e2ef4b044..b3353f2fba 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml
@@ -266,7 +266,6 @@ Section {
ItemFilterComboBox {
typeFilter: "QtQuick3D.TextureInput"
- validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
backendValue: layoutTextureInput.backendValue
implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ StudioTheme.Values.actionIndicatorWidth
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml
index ad62ff4b87..7b0ed94191 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml
@@ -7,11 +7,11 @@ import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
Item {
- id: editableListView
+ id: root
ExtendedFunctionLogic {
id: extFuncLogic
- backendValue: editableListView.backendValue
+ backendValue: root.backendValue
}
property var backendValue
@@ -23,10 +23,16 @@ Item {
property real __actionIndicatorWidth: StudioTheme.Values.squareComponentWidth
property real __actionIndicatorHeight: StudioTheme.Values.height
property string typeFilter: "QtQuick3D.Material"
+ property string textRole: "IdAndNameRole"
+ property string valueRole: "IdRole"
property int activatedReason: ComboBox.ActivatedReason.Other
property bool delegateHover: false
+ property string extraButtonIcon: "" // setting this will show an extra button
+ property string extraButtonToolTip: ""
+ signal extraButtonClicked(int idx)
+
signal add(string value)
signal remove(int idx)
signal replace(int idx, string value)
@@ -48,37 +54,50 @@ Item {
validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ }
actionIndicatorVisible: false
- typeFilter: editableListView.typeFilter
- editText: modelData
+ typeFilter: root.typeFilter
initialModelData: modelData
+ textRole: root.textRole
+ valueRole: root.valueRole
implicitWidth: StudioTheme.Values.singleControlColumnWidth
width: implicitWidth
+ textElidable: true
onFocusChanged: {
if (itemFilterComboBox.focus)
myColumn.currentIndex = index
- if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
+ var curValue = itemFilterComboBox.availableValue()
+ if (itemFilterComboBox.empty && curValue !== "") {
myRepeater.dirty = false
- editableListView.add(itemFilterComboBox.editText)
+ root.add(curValue)
}
}
- onCompressedActivated: {
- editableListView.activatedReason = reason
+ onCompressedActivated: function(index, reason) {
+ root.activatedReason = reason
- if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
+ var curValue = itemFilterComboBox.availableValue()
+ if (itemFilterComboBox.empty && curValue) {
myRepeater.dirty = false
- editableListView.add(itemFilterComboBox.editText)
+ root.add(curValue)
} else {
- editableListView.replace(itemFilterComboBox.myIndex, itemFilterComboBox.editText)
+ root.replace(itemFilterComboBox.myIndex, curValue)
}
}
- onHoverChanged: editableListView.delegateHover = itemFilterComboBox.hover
+ onHoverChanged: root.delegateHover = itemFilterComboBox.hover
}
- Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap }
+ Spacer { implicitWidth: extraButton.visible ? 5 : StudioTheme.Values.twoControlColumnGap }
+
+ IconIndicator {
+ id: extraButton
+ icon: root.extraButtonIcon
+ tooltip: root.extraButtonToolTip
+ onClicked: root.extraButtonClicked(index)
+ visible: root.extraButtonIcon !== ""
+ enabled: root.model[index]
+ }
IconIndicator {
id: closeIndicator
@@ -90,12 +109,12 @@ Item {
myRepeater.dirty = false
myRepeater.model = myRepeater.localModel // trigger on change handler
} else {
- editableListView.remove(index)
+ root.remove(index)
}
if (!lastItem)
myColumn.currentIndex = index - 1
}
- onHoveredChanged: editableListView.delegateHover = closeIndicator.hovered
+ onHoveredChanged: root.delegateHover = closeIndicator.hovered
}
}
}
@@ -103,7 +122,7 @@ Item {
Row {
ActionIndicator {
id: actionIndicator
- icon.visible: editableListView.delegateHover
+ icon.visible: root.delegateHover
icon.color: extFuncLogic.color
icon.text: extFuncLogic.glyph
onClicked: extFuncLogic.show()
@@ -141,7 +160,7 @@ Item {
myColumn.currentIndex = -1
myRepeater.localModel = []
- editableListView.model.forEach(function(item) {
+ root.model.forEach(function(item) {
myRepeater.localModel.push(item)
});
@@ -158,7 +177,7 @@ Item {
else
myColumn.currentIndex = myRepeater.localModel.length - 1
- if (editableListView.activatedReason === ComboBox.ActivatedReason.Other
+ if (root.activatedReason === ComboBox.ActivatedReason.Other
&& myColumn.currentItem !== null)
myColumn.currentItem.forceActiveFocus()
}
@@ -169,32 +188,36 @@ Item {
visible: myRepeater.count === 0
validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ }
actionIndicatorVisible: false
- typeFilter: editableListView.typeFilter
+ typeFilter: root.typeFilter
+ textRole: root.textRole
+ valueRole: root.valueRole
implicitWidth: StudioTheme.Values.singleControlColumnWidth
width: implicitWidth
onFocusChanged: {
- if (dummyComboBox.editText !== "")
- editableListView.add(dummyComboBox.editText)
+ var curValue = dummyComboBox.availableValue()
+ if (curValue !== "")
+ root.add(curValue)
}
onCompressedActivated: {
- editableListView.activatedReason = reason
+ root.activatedReason = reason
- if (dummyComboBox.editText !== "")
- editableListView.add(dummyComboBox.editText)
+ var curValue = dummyComboBox.availableValue()
+ if (curValue !== "")
+ root.add(curValue)
else
- editableListView.replace(dummyComboBox.myIndex, dummyComboBox.editText)
+ root.replace(dummyComboBox.myIndex, curValue)
}
- onHoverChanged: editableListView.delegateHover = dummyComboBox.hover
+ onHoverChanged: root.delegateHover = dummyComboBox.hover
}
StudioControls.AbstractButton {
id: plusButton
buttonIcon: StudioTheme.Constants.plus
- enabled: !myRepeater.dirty && !(editableListView.backendValue.isInModel
- && !editableListView.backendValue.isIdList)
+ enabled: !myRepeater.dirty && !(root.backendValue.isInModel
+ && !root.backendValue.isIdList)
onClicked: {
var idx = myRepeater.localModel.push("") - 1
myRepeater.model = myRepeater.localModel // trigger on change handler
@@ -203,7 +226,7 @@ Item {
myColumn.currentItem.forceActiveFocus()
}
- onHoveredChanged: editableListView.delegateHover = plusButton.hovered
+ onHoveredChanged: root.delegateHover = plusButton.hovered
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml
index de230b6825..c82aa2cea5 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml
@@ -11,18 +11,8 @@ Item {
id: extendedFunctionButton
property variant backendValue
- property bool isBoundBackend: {
- if (backendValue !== undefined && backendValue.isBound !== undefined)
- return backendValue.isBound
-
- return false
- }
- property string backendExpression: {
- if (backendValue !== undefined && backendValue.expression !== undefined)
- return backendValue.expression
-
- return ""
- }
+ property bool isBoundBackend: backendValue?.isBound ?? false
+ property string backendExpression: backendValue?.expression ?? ""
property string glyph: StudioTheme.Constants.actionIcon
property string color: StudioTheme.Values.themeTextColor
@@ -38,7 +28,7 @@ Item {
function setIcon() {
extendedFunctionButton.color = StudioTheme.Values.themeTextColor
- if (backendValue === undefined) {
+ if (!backendValue) {
extendedFunctionButton.glyph = StudioTheme.Constants.actionIcon
} else if (backendValue.isBound) {
if (backendValue.isTranslated) {
@@ -49,12 +39,8 @@ Item {
extendedFunctionButton.color = StudioTheme.Values.themeInteraction
}
} else {
- if (backendValue.complexNode !== undefined
- && backendValue.complexNode.exists) {
-
- } else {
+ if (!backendValue.complexNode || !backendValue.complexNode.exists)
extendedFunctionButton.glyph = StudioTheme.Constants.actionIcon
- }
}
}
@@ -77,8 +63,8 @@ Item {
id: menu
onAboutToShow: {
- exportMenuItem.checked = backendValue.hasPropertyAlias()
- exportMenuItem.enabled = !backendValue.isAttachedProperty()
+ exportMenuItem.checked = backendValue?.hasPropertyAlias() ?? false
+ exportMenuItem.enabled = !(backendValue?.isAttachedProperty() ?? false)
extendedFunctionButton.menuVisible = true
}
onAboutToHide: extendedFunctionButton.menuVisible = false
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableGeometrySection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableGeometrySection.qml
index 7b0a29900a..991016041a 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableGeometrySection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableGeometrySection.qml
@@ -15,7 +15,10 @@ Section {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Content size") }
+ PropertyLabel {
+ text: qsTr("Content size")
+ tooltip: qsTr("Sets the size of the content (the surface controlled by the flickable).")
+ }
SecondColumnLayout {
SpinBox {
@@ -60,7 +63,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Content") }
+ PropertyLabel {
+ text: qsTr("Content")
+ tooltip: qsTr("Sets the current position of the component.")
+ }
SecondColumnLayout {
SpinBox {
@@ -73,7 +79,10 @@ Section {
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
- ControlLabel { text: "X" }
+ ControlLabel {
+ text: "X"
+ tooltip: qsTr("Horizontal position.")
+ }
Spacer { implicitWidth: StudioTheme.Values.controlGap }
@@ -87,7 +96,10 @@ Section {
Spacer { implicitWidth: StudioTheme.Values.controlLabelGap }
- ControlLabel { text: "Y" }
+ ControlLabel {
+ text: "Y"
+ tooltip: qsTr("Vertical position.")
+ }
/*
TODO QDS-4836
Spacer { implicitWidth: StudioTheme.Values.controlGap }
@@ -99,6 +111,7 @@ Section {
PropertyLabel {
text: qsTr("Origin")
+ tooltip: qsTr("Sets the origin point of the content.")
blockedByTemplate: !backendValues.originX.isAvailable
&& !backendValues.originY.isAvailable
}
@@ -117,6 +130,7 @@ Section {
ControlLabel {
text: "X"
+ tooltip: qsTr("Horizontal position.")
enabled: backendValues.originX.isAvailable
}
@@ -135,6 +149,7 @@ Section {
ControlLabel {
text: "Y"
+ tooltip: qsTr("Vertical position.")
enabled: backendValues.originY.isAvailable
}
/*
@@ -148,6 +163,7 @@ Section {
PropertyLabel {
text: qsTr("Left margin")
+ tooltip: qsTr("Sets an additional left margin in the flickable area.")
blockedByTemplate: !backendValues.leftMargin.isAvailable
}
@@ -167,6 +183,7 @@ Section {
PropertyLabel {
text: qsTr("Right margin")
+ tooltip: qsTr("Sets an additional right margin in the flickable area.")
blockedByTemplate: !backendValues.rightMargin.isAvailable
}
@@ -186,6 +203,7 @@ Section {
PropertyLabel {
text: qsTr("Top margin")
+ tooltip: qsTr("Sets an additional top margin in the flickable area.")
blockedByTemplate: !backendValues.topMargin.isAvailable
}
@@ -205,6 +223,7 @@ Section {
PropertyLabel {
text: qsTr("Bottom margin")
+ tooltip: qsTr("Sets an additional bottom margin in the flickable area.")
blockedByTemplate: !backendValues.bottomMargin.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml
index 324658b502..4442cc6088 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml
@@ -16,7 +16,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Interactive")
- tooltip: qsTr("Allows users to drag or flick a flickable component.")
+ tooltip: qsTr("Toggles if the flickable supports drag and flick actions.")
}
SecondColumnLayout {
@@ -32,6 +32,7 @@ Section {
PropertyLabel {
text: qsTr("Flick direction")
+ tooltip: qsTr("Sets which directions the view can be flicked.")
blockedByTemplate: !backendValues.flickableDirection.isAvailable
}
@@ -51,7 +52,7 @@ Section {
PropertyLabel {
text: qsTr("Behavior")
- tooltip: qsTr("Whether the surface may be dragged beyond the Flickable's boundaries, or overshoot the Flickable's boundaries when flicked.")
+ tooltip: qsTr("Sets how the flickable behaves when it is dragged beyond its boundaries.")
blockedByTemplate: !backendValues.boundsBehavior.isAvailable
}
@@ -71,7 +72,7 @@ Section {
PropertyLabel {
text: qsTr("Movement")
- tooltip: qsTr("Whether the Flickable will give a feeling that the edges of the view are soft, rather than a hard physical boundary.")
+ tooltip: qsTr("Sets if the edges of the flickable should be soft or hard.")
blockedByTemplate: !backendValues.boundsMovement.isAvailable
}
@@ -91,7 +92,7 @@ Section {
PropertyLabel {
text: qsTr("Max. velocity")
- tooltip: qsTr("Maximum flick velocity")
+ tooltip: qsTr("Sets how fast an item can be flicked.")
}
SecondColumnLayout {
@@ -109,7 +110,7 @@ Section {
PropertyLabel {
text: qsTr("Deceleration")
- tooltip: qsTr("Flick deceleration")
+ tooltip: qsTr("Sets the rate by which a flick should slow down.")
blockedByTemplate: !backendValues.flickDeceleration.isAvailable
}
@@ -129,7 +130,7 @@ Section {
PropertyLabel {
text: qsTr("Press delay")
- tooltip: qsTr("Time to delay delivering a press to children of the Flickable in milliseconds.")
+ tooltip: qsTr("Sets the time to delay delivering a press to children of the flickable in milliseconds.")
blockedByTemplate: !backendValues.pressDelay.isAvailable
}
@@ -149,7 +150,7 @@ Section {
PropertyLabel {
text: qsTr("Pixel aligned")
- tooltip: qsTr("Sets the alignment of contentX and contentY to pixels (true) or subpixels (false).")
+ tooltip: qsTr("Toggles if the component is being moved by complete pixel length.")
blockedByTemplate: !backendValues.pixelAligned.isAvailable
}
@@ -167,9 +168,7 @@ Section {
PropertyLabel {
text: qsTr("Synchronous drag")
- tooltip: qsTr("If set to true, then when the mouse or touchpoint moves far enough to begin dragging\n"
- + "the content, the content will jump, such that the content pixel which was under the\n"
- + "cursor or touchpoint when pressed remains under that point.")
+ tooltip: qsTr("Toggles if the content should move instantly or not when the mouse or touchpoint is dragged to a new position.")
blockedByTemplate: !backendValues.synchronousDrag.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontExtrasSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontExtrasSection.qml
index 64a5a12071..b13e22d30e 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontExtrasSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontExtrasSection.qml
@@ -30,7 +30,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Capitalization")
- tooltip: qsTr("Capitalization for the text.")
+ tooltip: qsTr("Sets capitalization rules for the text.")
blockedByTemplate: !getBackendValue("capitalization").isAvailable
}
@@ -51,6 +51,7 @@ Section {
PropertyLabel {
visible: root.showStyle
text: qsTr("Style")
+ tooltip: qsTr("Sets the font style.")
blockedByTemplate: !styleComboBox.enabled
}
@@ -74,6 +75,7 @@ Section {
PropertyLabel {
text: qsTr("Style color")
+ tooltip: qsTr("Sets the color for the font style.")
visible: root.isBackendValueAvailable("styleColor")
}
@@ -85,7 +87,7 @@ Section {
PropertyLabel {
text: qsTr("Hinting")
- tooltip: qsTr("Preferred hinting on the text.")
+ tooltip: qsTr("Sets how to interpolate the text to render it more clearly when scaled.")
blockedByTemplate: !getBackendValue("hintingPreference").isAvailable
}
@@ -105,8 +107,7 @@ Section {
PropertyLabel {
text: qsTr("Auto kerning")
- tooltip: qsTr("Enables or disables the kerning OpenType feature when shaping the text. Disabling this may " +
- "improve performance when creating or changing the text, at the expense of some cosmetic features.")
+ tooltip: qsTr("Resolves the gap between texts if turned true.")
blockedByTemplate: !getBackendValue("kerning").isAvailable
}
@@ -124,9 +125,7 @@ Section {
PropertyLabel {
text: qsTr("Prefer shaping")
- tooltip: qsTr("Sometimes, a font will apply complex rules to a set of characters in order to display them correctly.\n" +
- "In some writing systems, such as Brahmic scripts, this is required in order for the text to be legible, whereas in " +
- "Latin script,\n it is merely a cosmetic feature. Setting the preferShaping property to false will disable all such features\nwhen they are not required, which will improve performance in most cases.")
+ tooltip: qsTr("Toggles the font-specific special features.")
blockedByTemplate: !getBackendValue("preferShaping").isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml
index 0862f18f8b..f1f7dfc07f 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml
@@ -34,7 +34,10 @@ Section {
onPixelSizeChanged: sizeWidget.setPointPixelSize()
SectionLayout {
- PropertyLabel { text: qsTr("Font") }
+ PropertyLabel {
+ text: qsTr("Font")
+ tooltip: qsTr("Sets the font of the text.")
+ }
SecondColumnLayout {
FontComboBox {
@@ -49,7 +52,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Size") }
+ PropertyLabel {
+ text: qsTr("Size")
+ tooltip: qsTr("Sets the font size in pixels or points.")
+ }
SecondColumnLayout {
id: sizeWidget
@@ -129,6 +135,7 @@ Section {
PropertyLabel {
text: qsTr("Emphasis")
+ tooltip: qsTr("Sets the text to bold, italic, underlined, or strikethrough.")
blockedByTemplate: !fontSection.boldStyle.isAvailable
&& !fontSection.italicStyle.isAvailable
&& !fontSection.underlineStyle.isAvailable
@@ -145,7 +152,7 @@ Section {
PropertyLabel {
text: qsTr("Capitalization")
- tooltip: qsTr("Capitalization for the text.")
+ tooltip: qsTr("Sets capitalization rules for the text.")
blockedByTemplate: !getBackendValue("capitalization").isAvailable
}
@@ -165,7 +172,7 @@ Section {
PropertyLabel {
text: qsTr("Weight")
- tooltip: qsTr("Font's weight.")
+ tooltip: qsTr("Sets the overall thickness of the font.")
blockedByTemplate: styleNameComboBox.styleSet
}
@@ -185,7 +192,7 @@ Section {
PropertyLabel {
text: qsTr("Style name")
- tooltip: qsTr("Font's style.")
+ tooltip: qsTr("Sets the style of the selected font. This is prioritized over <b>Weight</b> and <b>Emphasis</b>.")
blockedByTemplate: !styleNameComboBox.enabled
}
@@ -208,6 +215,7 @@ Section {
PropertyLabel {
visible: fontSection.showStyle
text: qsTr("Style")
+ tooltip: qsTr("Sets the font style.")
blockedByTemplate: !styleComboBox.enabled
}
@@ -230,6 +238,7 @@ Section {
PropertyLabel {
text: qsTr("Style color")
+ tooltip: qsTr("Sets the color for the font style.")
visible: fontSection.showStyle && backendValues.styleColor.isAvailable
}
@@ -241,7 +250,7 @@ Section {
PropertyLabel {
text: qsTr("Hinting")
- tooltip: qsTr("Preferred hinting on the text.")
+ tooltip: qsTr("Sets how to interpolate the text to render it more clearly when scaled.")
blockedByTemplate: !getBackendValue("hintingPreference").isAvailable
}
@@ -261,7 +270,7 @@ Section {
PropertyLabel {
text: qsTr("Letter spacing")
- tooltip: qsTr("Letter spacing for the font.")
+ tooltip: qsTr("Sets the letter spacing for the text.")
blockedByTemplate: !getBackendValue("letterSpacing").isAvailable
}
@@ -282,7 +291,7 @@ Section {
PropertyLabel {
text: qsTr("Word spacing")
- tooltip: qsTr("Word spacing for the font.")
+ tooltip: qsTr("Sets the word spacing for the text.")
blockedByTemplate: !getBackendValue("wordSpacing").isAvailable
}
@@ -303,8 +312,7 @@ Section {
PropertyLabel {
text: qsTr("Auto kerning")
- tooltip: qsTr("Enables or disables the kerning OpenType feature when shaping the text. Disabling this may " +
- "improve performance when creating or changing the text, at the expense of some cosmetic features.")
+ tooltip: qsTr("Resolves the gap between texts if turned true.")
blockedByTemplate: !getBackendValue("kerning").isAvailable
}
@@ -322,9 +330,7 @@ Section {
PropertyLabel {
text: qsTr("Prefer shaping")
- tooltip: qsTr("Sometimes, a font will apply complex rules to a set of characters in order to display them correctly.\n" +
- "In some writing systems, such as Brahmic scripts, this is required in order for the text to be legible, whereas in " +
- "Latin script,\n it is merely a cosmetic feature. Setting the preferShaping property to false will disable all such features\nwhen they are not required, which will improve performance in most cases.")
+ tooltip: qsTr("Toggles the disables font-specific special features.")
blockedByTemplate: !getBackendValue("preferShaping").isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml
index 0a0c6e5888..08a560baa2 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml
@@ -14,13 +14,19 @@ Rectangle {
property alias icon: icon.text
property alias tooltip: toolTip.text
property alias iconSize: icon.font.pixelSize
+ property alias iconScale: icon.scale
+ property alias iconColor: icon.color
+ property alias iconStyle: icon.style
+ property alias iconStyleColor: icon.styleColor
+
property alias containsMouse: mouseArea.containsMouse
property bool enabled: true
+ property bool transparentBg: false
property int buttonSize: StudioTheme.Values.height
- property color normalColor: StudioTheme.Values.themeControlBackground
- property color hoverColor: StudioTheme.Values.themeControlBackgroundHover
- property color pressColor: StudioTheme.Values.themeControlBackgroundInteraction
+ property color normalColor: root.transparentBg ? "transparent" : StudioTheme.Values.themeControlBackground
+ property color hoverColor: root.transparentBg ? "transparent" : StudioTheme.Values.themeControlBackgroundHover
+ property color pressColor: root.transparentBg ? "transparent" : StudioTheme.Values.themeControlBackgroundInteraction
width: buttonSize
height: buttonSize
@@ -32,18 +38,18 @@ Rectangle {
Text {
id: icon
+ anchors.centerIn: root
color: root.enabled ? StudioTheme.Values.themeTextColor : StudioTheme.Values.themeTextColorDisabled
font.family: StudioTheme.Constants.iconFont.family
font.pixelSize: StudioTheme.Values.baseIconFontSize
- anchors.centerIn: root
}
MouseArea {
id: mouseArea
anchors.fill: parent
- hoverEnabled: true
+ hoverEnabled: root.visible
onClicked: {
// We need to keep mouse area enabled even when button is disabled to make tooltip work
if (root.enabled)
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml
index 1fbd7a23b6..520a47f0ba 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImageSection.qml
@@ -13,7 +13,10 @@ Section {
anchors.right: parent.right
SectionLayout {
- PropertyLabel { text: qsTr("Source") }
+ PropertyLabel {
+ text: qsTr("Source")
+ tooltip: qsTr("Adds an image from the local file system.")
+ }
SecondColumnLayout {
UrlChooser {
@@ -23,7 +26,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Fill mode") }
+ PropertyLabel {
+ text: qsTr("Fill mode")
+ tooltip: qsTr("Sets how the image fits in the content box.")
+ }
SecondColumnLayout {
ComboBox {
@@ -40,6 +46,7 @@ Section {
PropertyLabel {
text: qsTr("Source size")
+ tooltip: qsTr("Sets the width and height of the image.")
blockedByTemplate: !backendValues.sourceSize.isAvailable
}
@@ -59,6 +66,7 @@ Section {
ControlLabel {
//: The width of the object
text: qsTr("W", "width")
+ tooltip: qsTr("Width.")
enabled: backendValues.sourceSize_width.isAvailable
}
@@ -79,6 +87,7 @@ Section {
ControlLabel {
//: The height of the object
text: qsTr("H", "height")
+ tooltip: qsTr("Height.")
enabled: backendValues.sourceSize_height.isAvailable
}
/*
@@ -90,7 +99,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Alignment H") }
+ PropertyLabel {
+ text: qsTr("Alignment H")
+ tooltip: qsTr("Sets the horizontal alignment of the image.")
+ }
SecondColumnLayout {
ComboBox {
@@ -105,7 +117,10 @@ Section {
ExpandingSpacer {}
}
- PropertyLabel { text: qsTr("Alignment V") }
+ PropertyLabel {
+ text: qsTr("Alignment V")
+ tooltip: qsTr("Sets the vertical alignment of the image.")
+ }
SecondColumnLayout {
ComboBox {
@@ -212,7 +227,7 @@ Section {
PropertyLabel {
text: qsTr("Smooth")
- tooltip: qsTr("Smoothly filters the image when it is scaled or transformed.")
+ tooltip: qsTr("Uses smooth filtering when the image is scaled or transformed.")
blockedByTemplate: !backendValues.smooth.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml
index 1ec942d6b9..2f749271d7 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ItemFilterComboBox.qml
@@ -13,13 +13,15 @@ HelperWidgets.ComboBox {
editable: true
model: comboBox.addDefaultItem(itemFilterModel.itemModel)
+ validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
+
HelperWidgets.ItemFilterModel {
id: itemFilterModel
modelNodeBackendProperty: modelNodeBackend
}
property string defaultItem: qsTr("[None]")
- property string textValue: comboBox.backendValue.expression
+ property string textValue: comboBox.backendValue?.expression ?? ""
property bool block: false
property bool dirty: true
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml
index 456c76e1d4..b73bde2931 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml
@@ -65,7 +65,7 @@ StudioControls.TextField {
lineEdit.text = ""
} else {
if (lineEdit.writeValueManually)
- lineEdit.text = convertColorToString(colorLogic.valueFromBackend)
+ lineEdit.text = convertColorToString?.(colorLogic.valueFromBackend) ?? ""
else
lineEdit.text = colorLogic.valueFromBackend
}
@@ -111,8 +111,7 @@ StudioControls.TextField {
lineEdit.__dirty = false
}
- property bool isTranslated: colorLogic.backendValue === undefined ? false
- : colorLogic.backendValue.isTranslated
+ property bool isTranslated: colorLogic.backendValue?.isTranslated ?? false
translationIndicator.onClicked: {
if (lineEdit.translationIndicator.checked) {
@@ -124,19 +123,12 @@ StudioControls.TextField {
colorLogic.evaluate()
}
- property variant backendValueValueInternal: lineEdit.backendValue === undefined ? 0
- : lineEdit.backendValue.value
+ property variant backendValueValueInternal: lineEdit.backendValue?.value ?? 0
onBackendValueValueInternalChanged: {
- if (lineEdit.backendValue === undefined)
- lineEdit.translationIndicator.checked = false
- else
- lineEdit.translationIndicator.checked = lineEdit.backendValue.isTranslated
+ lineEdit.translationIndicator.checked = lineEdit.backendValue?.isTranslated ?? false
}
onIsTranslatedChanged: {
- if (lineEdit.backendValue === undefined)
- lineEdit.translationIndicator.checked = false
- else
- lineEdit.translationIndicator.checked = lineEdit.backendValue.isTranslated
+ lineEdit.translationIndicator.checked = lineEdit.backendValue?.isTranslated ?? false
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml
index c90ba1c2aa..0512b0861b 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ListViewComboBox.qml
@@ -6,7 +6,7 @@ import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls
StudioControls.ComboBox {
- id: comboBox
+ id: root
property alias typeFilter: itemFilterModel.typeFilter
@@ -14,7 +14,9 @@ StudioControls.ComboBox {
property bool __isCompleted: false
editable: true
- model: itemFilterModel.itemModel
+ model: itemFilterModel
+ textRole: "IdRole"
+ valueRole: "IdRole"
HelperWidgets.ItemFilterModel {
id: itemFilterModel
@@ -22,13 +24,49 @@ StudioControls.ComboBox {
}
Component.onCompleted: {
- comboBox.__isCompleted = true
+ root.__isCompleted = true
+ root.resetInitialIndex()
+ }
+
+ onInitialModelDataChanged: root.resetInitialIndex()
+ onValueRoleChanged: root.resetInitialIndex()
+ onModelChanged: root.resetInitialIndex()
+ onTextRoleChanged: root.resetInitialIndex()
+
+ function resetInitialIndex() {
+ let currentSelectedDataIndex = -1
// Workaround for proper initialization. Use the initial modelData value and search for it
// in the model. If nothing was found, set the editText to the initial modelData.
- comboBox.currentIndex = comboBox.find(comboBox.initialModelData)
+ if (root.textRole === root.valueRole) {
+ currentSelectedDataIndex = root.find(root.initialModelData)
+ } else {
+ for (let i = 0; i < root.count; ++i) {
+ let movingModelIndex = root.model.index(i)
+ let movingModelValueData = root.model.data(movingModelIndex, root.valueRole)
+ if (movingModelValueData === root.initialModelData) {
+ currentSelectedDataIndex = i
+ break
+ }
+ }
+ }
+ root.currentIndex = currentSelectedDataIndex
+ if (root.currentIndex === -1)
+ root.editText = root.initialModelData
+ }
+
+ function currentData(role = root.valueRole) {
+ if (root.currentIndex !== -1) {
+ let currentModelIndex = root.model.index(root.currentIndex)
+ return root.model.data(currentModelIndex, role)
+ }
+ return root.editText
+ }
+
+ function availableValue() {
+ if (root.currentIndex !== -1 && root.currentValue !== "")
+ return root.currentValue
- if (comboBox.currentIndex === -1)
- comboBox.editText = comboBox.initialModelData
+ return root.editText
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OriginControl.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OriginControl.qml
index 201a0de6a6..f4d4f7af85 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OriginControl.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/OriginControl.qml
@@ -36,7 +36,7 @@ Row {
ActionIndicator {
id: actionIndicator
- myControl: myButton
+ __parentControl: myButton
x: 0
y: 0
width: actionIndicator.visible ? myButton.__actionIndicatorWidth : 0
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml
index 5dc978a811..e1def65263 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml
@@ -15,6 +15,7 @@ Section {
SectionLayout {
PropertyLabel {
text: qsTr("Vertical")
+ tooltip: qsTr("Sets the padding on top and bottom of the item.")
blockedByTemplate: !backendValues.topPadding.isAvailable
&& !backendValues.bottomPadding.isAvailable
}
@@ -66,6 +67,7 @@ Section {
PropertyLabel {
text: qsTr("Horizontal")
+ tooltip: qsTr("Sets the paddding on the left and right sides of the item.")
blockedByTemplate: !backendValues.leftPadding.isAvailable
&& !backendValues.rightPadding.isAvailable
}
@@ -118,7 +120,7 @@ Section {
PropertyLabel {
text: qsTr("Global")
- tooltip: qsTr("Padding between the content and the edges of the items.")
+ tooltip: qsTr("Sets the padding for all sides of the item.")
blockedByTemplate: !backendValues.padding.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml
index 07d628eb66..5317d909e8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml
@@ -18,6 +18,12 @@ Rectangle {
default property alias content: mainColumn.children
+ // Called from C++ to close context menu on focus out
+ function closeContextMenu()
+ {
+ Controller.closeContextMenu()
+ }
+
MouseArea {
anchors.fill: parent
onClicked: forceActiveFocus()
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml
index 7724aef49d..b965e8becd 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml
@@ -38,6 +38,8 @@ Item {
property bool useDefaulContextMenu: true
+ property string category: "properties"
+
clip: true
Connections {
@@ -49,19 +51,24 @@ Item {
Connections {
target: Controller
- function onCollapseAll() {
- if (collapsible) {
+ function onCollapseAll(cat) {
+ if (collapsible && cat === section.category) {
if (section.expandOnClick)
section.expanded = false
else
section.collapse()
}
}
- function onExpandAll() {
- if (section.expandOnClick)
- section.expanded = true
- else
- section.expand()
+ function onExpandAll(cat) {
+ if (cat === section.category) {
+ if (section.expandOnClick)
+ section.expanded = true
+ else
+ section.expand()
+ }
+ }
+ function onCloseContextMenu() {
+ contextMenu.close()
}
}
@@ -100,12 +107,12 @@ Item {
StudioControls.MenuItem {
text: qsTr("Expand All")
- onTriggered: Controller.expandAll()
+ onTriggered: Controller.expandAll(section.category)
}
StudioControls.MenuItem {
text: qsTr("Collapse All")
- onTriggered: Controller.collapseAll()
+ onTriggered: Controller.collapseAll(section.category)
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml
index 6dd21fd791..f125c459c5 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml
@@ -31,18 +31,18 @@ Item {
}
Component.onCompleted: {
- spinBox.enabled = backendValue === undefined ? false : !isBlocked(backendValue.name)
+ spinBox.enabled = backendValue ? !isBlocked(backendValue.name) : false
}
Connections {
target: modelNodeBackend
function onSelectionChanged() {
- spinBox.enabled = backendValue === undefined ? false : !isBlocked(backendValue.name)
+ spinBox.enabled = backendValue ? !isBlocked(backendValue.name) : false
}
}
onBackendValueChanged: {
- spinBox.enabled = backendValue === undefined ? false : !isBlocked(backendValue.name)
+ spinBox.enabled = backendValue ? !isBlocked(backendValue.name) : false
}
StudioControls.RealSpinBox {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml
index 09cb03f871..f1c4844e35 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml
@@ -98,6 +98,7 @@ Section {
PropertyLabel {
visible: root.showVerticalAlignment
text: qsTr("Wrap mode")
+ tooltip: qsTr("Sets how overflowing text is handled.")
blockedByTemplate: !backendValues.wrapMode.isAvailable
}
@@ -119,6 +120,7 @@ Section {
PropertyLabel {
visible: root.showElide
text: qsTr("Elide")
+ tooltip: qsTr("Sets how to indicate that more text is available.")
blockedByTemplate: !backendValues.elide.isAvailable
}
@@ -140,7 +142,7 @@ Section {
PropertyLabel {
visible: root.showElide
text: qsTr("Max line count")
- tooltip: qsTr("Limits the number of lines that the text component will show.")
+ tooltip: qsTr("Sets the max number of lines that the text component shows.")
blockedByTemplate: !backendValues.maximumLineCount.isAvailable
}
@@ -178,6 +180,7 @@ Section {
PropertyLabel {
visible: root.showFormatProperty
text: qsTr("Format")
+ tooltip: qsTr("Sets the formatting method of the text.")
blockedByTemplate: !backendValues.textFormat.isAvailable
}
@@ -198,7 +201,7 @@ Section {
PropertyLabel {
text: qsTr("Render type")
- tooltip: qsTr("Overrides the default rendering type for this component.")
+ tooltip: qsTr("Sets the rendering type for this component.")
blockedByTemplate: !backendValues.renderType.isAvailable
}
@@ -219,7 +222,7 @@ Section {
PropertyLabel {
visible: root.showFontSizeMode
text: qsTr("Size mode")
- tooltip: qsTr("Specifies how the font size of the displayed text is determined.")
+ tooltip: qsTr("Sets how the font size is determined.")
blockedByTemplate: !backendValues.fontSizeMode.isAvailable
}
@@ -242,6 +245,7 @@ Section {
PropertyLabel {
visible: root.showFontSizeMode
text: qsTr("Min size")
+ tooltip: qsTr("Sets the minimum font size to use. This has no effect when <b>Size</b> mode is set to Fixed.")
blockedByTemplate: !backendValues.minimumPixelSize.isAvailable
&& !backendValues.minimumPointSize.isAvailable
}
@@ -320,7 +324,7 @@ Section {
PropertyLabel {
visible: root.showLineHeight
text: qsTr("Line height mode")
- tooltip: qsTr("Determines how the line height is specified.")
+ tooltip: qsTr("Sets how to calculate the line height based on the <b>Line height</b> value.")
blockedByTemplate: !backendValues.lineHeightMode.isAvailable
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextExtrasSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextExtrasSection.qml
index 6ede474df0..28875547e0 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextExtrasSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/TextExtrasSection.qml
@@ -32,6 +32,7 @@ Section {
PropertyLabel {
visible: root.showWrapMode
text: qsTr("Wrap mode")
+ tooltip: qsTr("Sets how overflowing text is handled.")
blockedByTemplate: !root.isBackendValueAvailable("wrapMode")
}
@@ -54,6 +55,7 @@ Section {
PropertyLabel {
visible: root.showElide
text: qsTr("Elide")
+ tooltip: qsTr("Sets how to indicate that more text is available.")
blockedByTemplate: !root.isBackendValueAvailable("elide")
}
@@ -76,6 +78,7 @@ Section {
PropertyLabel {
visible: root.showFormatProperty
text: qsTr("Format")
+ tooltip: qsTr("Sets the formatting method of the text.")
blockedByTemplate: !root.isBackendValueAvailable("textFormat")
}
@@ -97,7 +100,7 @@ Section {
PropertyLabel {
text: qsTr("Render type")
- tooltip: qsTr("Overrides the default rendering type for this component.")
+ tooltip: qsTr("Sets the rendering type for this component.")
blockedByTemplate: !root.isBackendValueAvailable("renderType")
}
@@ -117,7 +120,7 @@ Section {
PropertyLabel {
text: qsTr("Render type quality")
- tooltip: qsTr("Overrides the default rendering type quality for this component.")
+ tooltip: qsTr("Sets the quality of the render. This only has an effect when <b>Render type</b> is set to QtRendering.")
blockedByTemplate: !root.isBackendValueAvailable("renderTypeQuality")
enabled: root.isBackendValueAvailable("renderTypeQuality")
&& (backendValues.renderType.value === "QtRendering"
@@ -144,7 +147,7 @@ Section {
PropertyLabel {
visible: root.showLineHeight
text: qsTr("Line height mode")
- tooltip: qsTr("Determines how the line height is specified.")
+ tooltip: qsTr("Sets how to calculate the line height based on the <b>Line height</b> value.")
blockedByTemplate: !root.isBackendValueAvailable("lineHeightMode")
}
@@ -167,7 +170,7 @@ Section {
PropertyLabel {
visible: root.showFontSizeMode
text: qsTr("Size mode")
- tooltip: qsTr("Specifies how the font size of the displayed text is determined.")
+ tooltip: qsTr("Sets how the font size is determined.")
blockedByTemplate: !root.isBackendValueAvailable("fontSizeMode")
}
@@ -191,6 +194,7 @@ Section {
PropertyLabel {
visible: root.showFontSizeMode
text: qsTr("Min size")
+ tooltip: qsTr("Sets the minimum font size to use. This has no effect when <b>Size</b> mode is set to Fixed.")
blockedByTemplate: !root.isBackendValueAvailable("minimumPixelSize")
&& !root.isBackendValueAvailable("minimumPointSize")
}
@@ -244,7 +248,7 @@ Section {
PropertyLabel {
visible: root.showElide
text: qsTr("Max line count")
- tooltip: qsTr("Limits the number of lines that the text component will show.")
+ tooltip: qsTr("Sets the max number of lines that the text component shows.")
blockedByTemplate: !root.isBackendValueAvailable("maximumLineCount")
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
index f41a2b0c2d..b754022780 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
@@ -88,7 +88,7 @@ Row {
ToolTip {
id: toolTip
visible: comboBox.hover && toolTip.text !== ""
- text: root.backendValue.valueToString
+ text: root.backendValue?.valueToString ?? ""
delay: StudioTheme.Values.toolTipDelay
background: Rectangle {
@@ -116,8 +116,6 @@ Row {
Image {
id: thumbnail
asynchronous: true
- sourceSize.height: 96
- sourceSize.width: 96
height: 96
width: 96
fillMode: Image.PreserveAspectFit
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml
index 7b0ca95f39..0acf8418f7 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/AbstractButton.qml
@@ -1,15 +1,17 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.AbstractButton {
- id: myButton
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property bool globalHover: false
- property bool hover: myButton.hovered
+ property bool hover: control.hovered
property alias buttonIcon: buttonIcon.text
property alias iconColor: buttonIcon.color
@@ -28,34 +30,35 @@ T.AbstractButton {
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
- height: StudioTheme.Values.height
- width: StudioTheme.Values.height
- z: myButton.checked ? 10 : 3
+ width: control.style.squareControlSize.width
+ height: control.style.squareControlSize.height
+ z: control.checked ? 10 : 3
activeFocusOnTab: false
onHoverChanged: {
- if (parent !== undefined && parent.hoverCallback !== undefined && myButton.enabled)
+ if (parent !== undefined && parent.hoverCallback !== undefined && control.enabled)
parent.hoverCallback()
}
background: Rectangle {
id: buttonBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
+ radius: control.style.radius
}
indicator: Item {
x: 0
y: 0
- width: myButton.width
- height: myButton.height
+ width: control.width
+ height: control.height
T.Label {
id: buttonIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.fill: parent
@@ -64,35 +67,43 @@ T.AbstractButton {
states: [
State {
name: "default"
- when: myButton.enabled && !myButton.pressed && !myButton.checked
+ when: control.enabled && !control.pressed && !control.checked && !control.hover
+ PropertyChanges {
+ target: buttonIcon
+ color: control.style.icon.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: control.enabled && !control.pressed && !control.checked && control.hover
PropertyChanges {
target: buttonIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.hover
}
},
State {
name: "press"
- when: myButton.enabled && myButton.pressed
+ when: control.enabled && control.pressed
PropertyChanges {
target: buttonIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.interaction
}
},
State {
name: "check"
- when: myButton.enabled && !myButton.pressed && myButton.checked
+ when: control.enabled && !control.pressed && control.checked
PropertyChanges {
target: buttonIcon
- color: myButton.checkedInverted ? StudioTheme.Values.themeTextSelectedTextColor
- : StudioTheme.Values.themeIconColorSelected
+ color: control.checkedInverted ? control.style.text.selectedText // TODO rather something in icon
+ : control.style.icon.selected
}
},
State {
name: "disable"
- when: !myButton.enabled
+ when: !control.enabled
PropertyChanges {
target: buttonIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.icon.disabled
}
}
]
@@ -102,78 +113,86 @@ T.AbstractButton {
states: [
State {
name: "default"
- when: myButton.enabled && !myButton.globalHover && !myButton.hover
- && !myButton.pressed && !myButton.checked
+ when: control.enabled && !control.globalHover && !control.hover
+ && !control.pressed && !control.checked
PropertyChanges {
target: buttonBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
- target: myButton
+ target: control
z: 3
}
},
State {
name: "globalHover"
- when: myButton.globalHover && !myButton.hover && !myButton.pressed && myButton.enabled
+ when: control.globalHover && !control.hover && !control.pressed && control.enabled
PropertyChanges {
target: buttonBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
},
State {
name: "hover"
- when: !myButton.checked && myButton.hover && !myButton.pressed && myButton.enabled
+ when: !control.checked && control.hover && !control.pressed && control.enabled
PropertyChanges {
target: buttonBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.hover
+ border.color: control.style.border.hover
+ }
+ PropertyChanges {
+ target: control
+ z: 100
}
},
State {
name: "hoverCheck"
- when: myButton.checked && myButton.hover && !myButton.pressed && myButton.enabled
+ when: control.checked && control.hover && !control.pressed && control.enabled
PropertyChanges {
target: buttonBackground
- color: myButton.checkedInverted ? StudioTheme.Values.themeInteractionHover
- : StudioTheme.Values.themeControlBackgroundHover
- border.color: myButton.checkedInverted ? StudioTheme.Values.themeInteractionHover
- : StudioTheme.Values.themeControlOutline
+ color: control.checkedInverted ? control.style.interactionHover
+ : control.style.background.hover
+ border.color: control.checkedInverted ? control.style.interactionHover
+ : control.style.border.hover
+ }
+ PropertyChanges {
+ target: control
+ z: 100
}
},
State {
name: "press"
- when: myButton.hover && myButton.pressed && myButton.enabled
+ when: control.hover && control.pressed && control.enabled
PropertyChanges {
target: buttonBackground
- color: StudioTheme.Values.themeInteraction
- border.color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
+ border.color: control.style.interaction
}
PropertyChanges {
- target: myButton
+ target: control
z: 100
}
},
State {
name: "check"
- when: myButton.enabled && !myButton.pressed && myButton.checked
+ when: control.enabled && !control.pressed && control.checked
PropertyChanges {
target: buttonBackground
- color: myButton.checkedInverted ? StudioTheme.Values.themeInteraction
- : StudioTheme.Values.themeControlBackground
- border.color: myButton.checkedInverted ? StudioTheme.Values.themeInteraction
- : StudioTheme.Values.themeControlOutline
+ color: control.checkedInverted ? control.style.interaction
+ : control.style.background.idle
+ border.color: control.checkedInverted ? control.style.interaction
+ : control.style.border.idle
}
},
State {
name: "disable"
- when: !myButton.enabled
+ when: !control.enabled
PropertyChanges {
target: buttonBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml
index 618c1e536f..1d52ac9244 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ActionIndicator.qml
@@ -1,16 +1,18 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: actionIndicator
+ id: control
- property Item myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property alias icon: actionIndicatorIcon
+ property Item __parentControl
+
+ property alias icon: icon
property bool hover: false
property bool pressed: false
@@ -18,55 +20,56 @@ Rectangle {
color: "transparent"
- implicitWidth: StudioTheme.Values.actionIndicatorWidth
- implicitHeight: StudioTheme.Values.actionIndicatorHeight
+ implicitWidth: control.style.actionIndicatorSize.width
+ implicitHeight: control.style.actionIndicatorSize.height
signal clicked
z: 10
T.Label {
- id: actionIndicatorIcon
+ id: icon
anchors.fill: parent
text: StudioTheme.Constants.actionIcon
- visible: text !== StudioTheme.Constants.actionIcon || actionIndicator.forceVisible
- || (myControl !== undefined &&
- ((myControl.edit !== undefined && myControl.edit)
- || (myControl.hover !== undefined && myControl.hover)
- || (myControl.drag !== undefined && myControl.drag)))
- color: StudioTheme.Values.themeTextColor
+ visible: text !== StudioTheme.Constants.actionIcon || control.forceVisible
+ || (control.__parentControl !== undefined &&
+ ((control.__parentControl.edit !== undefined && control.__parentControl.edit)
+ || (control.__parentControl.hover !== undefined && control.__parentControl.hover)
+ || (control.__parentControl.drag !== undefined && control.__parentControl.drag)))
+ color: control.style.icon.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
states: [
State {
name: "hover"
- when: actionIndicator.hover && !actionIndicator.pressed
- && (!myControl || (!myControl.edit && !myControl.drag))
- && actionIndicator.enabled
+ when: control.hover && !control.pressed
+ && (!control.__parentControl
+ || (!control.__parentControl.edit && !control.__parentControl.drag))
+ && control.enabled
PropertyChanges {
- target: actionIndicatorIcon
+ target: icon
scale: 1.2
visible: true
}
},
State {
name: "disable"
- when: !actionIndicator.enabled
+ when: !control.enabled
PropertyChanges {
- target: actionIndicatorIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ target: icon
+ color: control.style.icon.disabled
}
}
]
}
MouseArea {
- id: actionIndicatorMouseArea
+ id: mouseArea
anchors.fill: parent
hoverEnabled: true
- onContainsMouseChanged: actionIndicator.hover = containsMouse
- onClicked: actionIndicator.clicked()
+ onContainsMouseChanged: control.hover = mouseArea.containsMouse
+ onClicked: control.clicked()
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml
index df6d0b4e27..099ed20ee8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Button.qml
@@ -1,24 +1,25 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import StudioControls 1.0
+import QtQuick
ButtonRow {
- id: myButtonRow
+ id: control
- property alias buttonIcon: myAbstractButton.buttonIcon
- property alias iconColor: myAbstractButton.iconColor
- property alias iconRotation: myAbstractButton.iconRotation
- property alias checkable: myAbstractButton.checkable
- property alias checked: myAbstractButton.checked
+ property alias style: button.style
+
+ property alias buttonIcon: button.buttonIcon
+ property alias iconColor: button.iconColor
+ property alias iconRotation: button.iconRotation
+ property alias checkable: button.checkable
+ property alias checked: button.checked
signal onCheckedChanged()
signal clicked
AbstractButton {
- id: myAbstractButton
- onCheckedChanged: myButtonRow.onCheckedChanged()
- onClicked: myButtonRow.clicked()
+ id: button
+ onCheckedChanged: control.onCheckedChanged()
+ onClicked: control.clicked()
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonGroup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonGroup.qml
index 0dbe42b810..dc5274873c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonGroup.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonGroup.qml
@@ -1,8 +1,8 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
T.ButtonGroup {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonRow.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonRow.qml
index 15644cb3af..dffe70e9fa 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonRow.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ButtonRow.qml
@@ -1,51 +1,54 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Layouts 1.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Row {
- id: myButtonRow
+ id: control
- property bool hover: (actionIndicator.hover || myButtonRow.childHover) && myButtonRow.enabled
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property bool hover: (actionIndicator.hover || control.childHover) && control.enabled
property bool childHover: false
property alias actionIndicator: actionIndicator
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
ActionIndicator {
id: actionIndicator
- myControl: myButtonRow
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- // + StudioTheme.Values.border on width because of negative spacing on the row
- width: actionIndicator.visible ? myButtonRow.__actionIndicatorWidth + StudioTheme.Values.border : 0
- height: actionIndicator.visible ? myButtonRow.__actionIndicatorHeight : 0
+ // + borderWidth because of negative spacing on the row
+ width: actionIndicator.visible ? control.__actionIndicatorWidth + control.style.borderWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
- onHoverChanged: myButtonRow.hoverCallback()
+ onHoverChanged: control.hoverCallback()
}
- spacing: -StudioTheme.Values.border
+ spacing: -control.style.borderWidth
function hoverCallback() {
var hover = false
- for (var i = 0; i < children.length; ++i) {
- if (children[i].hover !== undefined)
- hover = hover || children[i].hover
+ for (var i = 0; i < control.children.length; ++i) {
+ if (control.children[i].hover !== undefined)
+ hover = hover || control.children[i].hover
}
- myButtonRow.childHover = hover
+ control.childHover = hover
}
onHoverChanged: {
- for (var i = 0; i < children.length; ++i)
- if (children[i].globalHover !== undefined)
- children[i].globalHover = myButtonRow.hover
+ for (var i = 0; i < control.children.length; ++i)
+ if (control.children[i].globalHover !== undefined)
+ control.children[i].globalHover = control.hover
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml
index aed2cd07d1..573b78012f 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml
@@ -1,30 +1,32 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.CheckBox {
- id: myCheckBox
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property alias actionIndicator: actionIndicator
// This property is used to indicate the global hover state
- property bool hover: myCheckBox.hovered && myCheckBox.enabled
+ property bool hover: control.hovered && control.enabled
property bool edit: false
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
- property alias labelVisible: checkBoxLabel.visible
- property alias labelColor: checkBoxLabel.color
+ property alias labelVisible: label.visible
+ property alias labelColor: label.color
- property alias fontFamily: checkBoxLabel.font.family
- property alias fontPixelSize: checkBoxLabel.font.pixelSize
+ property alias fontFamily: label.font.family
+ property alias fontPixelSize: label.font.pixelSize
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -32,15 +34,16 @@ T.CheckBox {
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
- spacing: checkBoxLabel.visible ? StudioTheme.Values.checkBoxSpacing : 0
+ spacing: label.visible ? control.style.controlSpacing : 0
hoverEnabled: true
activeFocusOnTab: false
ActionIndicator {
id: actionIndicator
- myControl: myCheckBox
- width: actionIndicator.visible ? myCheckBox.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? myCheckBox.__actionIndicatorHeight : 0
+ style: control.style
+ __parentControl: control
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
indicator: Rectangle {
@@ -48,20 +51,20 @@ T.CheckBox {
x: actionIndicator.width
y: 0
z: 5
- implicitWidth: StudioTheme.Values.height
- implicitHeight: StudioTheme.Values.height
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ implicitWidth: control.style.squareControlSize.width
+ implicitHeight: control.style.squareControlSize.height
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
T.Label {
id: checkedIcon
x: (parent.width - checkedIcon.width) / 2
y: (parent.height - checkedIcon.height) / 2
text: StudioTheme.Constants.tickIcon
- visible: myCheckBox.checkState === Qt.Checked
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.sliderControlSizeMulti
+ visible: control.checkState === Qt.Checked
+ color: control.style.icon.idle
+ font.pixelSize: control.style.baseIconFontSize
font.family: StudioTheme.Constants.iconFont.family
}
@@ -70,113 +73,113 @@ T.CheckBox {
x: (parent.width - checkedIcon.width) / 2
y: (parent.height - checkedIcon.height) / 2
text: StudioTheme.Constants.triState
- visible: myCheckBox.checkState === Qt.PartiallyChecked
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.sliderControlSizeMulti
+ visible: control.checkState === Qt.PartiallyChecked
+ color: control.style.icon.idle
+ font.pixelSize: control.style.baseIconFontSize
font.family: StudioTheme.Constants.iconFont.family
}
}
contentItem: T.Label {
- id: checkBoxLabel
- leftPadding: checkBoxBackground.x + checkBoxBackground.width + myCheckBox.spacing
+ id: label
+ leftPadding: checkBoxBackground.x + checkBoxBackground.width + control.spacing
rightPadding: 0
verticalAlignment: Text.AlignVCenter
- text: myCheckBox.text
- font: myCheckBox.font
- color: StudioTheme.Values.themeTextColor
- visible: checkBoxLabel.text !== ""
+ text: control.text
+ font: control.font
+ color: control.style.text.idle
+ visible: label.text !== ""
}
states: [
State {
name: "default"
- when: myCheckBox.enabled && !myCheckBox.hover
- && !myCheckBox.pressed && !actionIndicator.hover
+ when: control.enabled && !control.hover
+ && !control.pressed && !actionIndicator.hover
PropertyChanges {
target: checkBoxBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
target: checkedIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
PropertyChanges {
target: partiallyCheckedIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
},
State {
name: "globalHover"
- when: actionIndicator.hover && !myCheckBox.pressed && myCheckBox.enabled
+ when: actionIndicator.hover && !control.pressed && control.enabled
PropertyChanges {
target: checkBoxBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.globalHover
+ border.color: control.style.border.idle
}
PropertyChanges {
target: checkedIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
PropertyChanges {
target: partiallyCheckedIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
},
State {
name: "hover"
- when: myCheckBox.hover && !actionIndicator.hover && !myCheckBox.pressed
+ when: control.hover && !actionIndicator.hover && !control.pressed
PropertyChanges {
target: checkBoxBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.hover
+ border.color: control.style.border.hover
}
PropertyChanges {
target: checkedIcon
- color: StudioTheme.Values.themeIconColor // TODO naming
+ color: control.style.icon.hover
}
PropertyChanges {
target: partiallyCheckedIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.hover
}
},
State {
name: "press"
- when: myCheckBox.hover && myCheckBox.pressed
+ when: control.hover && control.pressed
PropertyChanges {
target: checkBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
PropertyChanges {
target: checkedIcon
- color: StudioTheme.Values.themeIconColorInteraction
+ color: control.style.icon.interaction
}
PropertyChanges {
target: partiallyCheckedIcon
- color: StudioTheme.Values.themeIconColorInteraction
+ color: control.style.icon.interaction
}
},
State {
name: "disable"
- when: !myCheckBox.enabled
+ when: !control.enabled
PropertyChanges {
target: checkBoxBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
PropertyChanges {
target: checkedIcon
- color: StudioTheme.Values.themeIconColorDisabled
+ color: control.style.icon.disabled
}
PropertyChanges {
target: partiallyCheckedIcon
- color: StudioTheme.Values.themeIconColorDisabled
+ color: control.style.icon.disabled
}
PropertyChanges {
- target: checkBoxLabel
- color: StudioTheme.Values.themeTextColorDisabled
+ target: label
+ color: control.style.text.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml
index a0e048a3d3..7cba51ce33 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml
@@ -1,156 +1,168 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: checkIndicator
+ id: control
- property T.Control myControl
- property T.Popup myPopup
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property bool hover: checkIndicatorMouseArea.containsMouse && checkIndicator.enabled
- property bool pressed: checkIndicatorMouseArea.containsPress
+ property T.Control __parentControl
+ property T.Popup __parentPopup
+
+ property bool hover: mouseArea.containsMouse && control.enabled
+ property bool pressed: mouseArea.containsPress
property bool checked: false
- property bool hasActiveDrag: myControl.hasActiveDrag ?? false
- property bool hasActiveHoverDrag: myControl.hasActiveHoverDrag ?? false
+ property bool hasActiveDrag: control.__parentControl.hasActiveDrag ?? false
+ property bool hasActiveHoverDrag: control.__parentControl.hasActiveHoverDrag ?? false
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
border.width: 0
Connections {
- target: myPopup
- function onClosed() { checkIndicator.checked = false }
- function onOpened() { checkIndicator.checked = true }
+ target: control.__parentPopup
+ function onClosed() { control.checked = false }
+ function onOpened() { control.checked = true }
}
MouseArea {
- id: checkIndicatorMouseArea
+ id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
- if (myControl.activeFocus)
- myControl.focus = false
+ if (control.__parentControl.activeFocus)
+ control.__parentControl.focus = false
- if (myPopup.opened) {
- myPopup.close()
+ if (control.__parentPopup.opened) {
+ control.__parentPopup.close()
} else {
- myPopup.open()
- myPopup.forceActiveFocus()
+ control.__parentPopup.open()
+ control.__parentPopup.forceActiveFocus()
}
}
}
T.Label {
- id: checkIndicatorIcon
+ id: icon
anchors.fill: parent
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
text: StudioTheme.Constants.upDownSquare2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
- font.pixelSize: StudioTheme.Values.sliderControlSizeMulti
+ font.pixelSize: control.style.baseIconFontSize
font.family: StudioTheme.Constants.iconFont.family
}
states: [
State {
name: "default"
- when: myControl.enabled && checkIndicator.enabled && !myControl.edit
- && !checkIndicator.hover && !myControl.hover && !myControl.drag
- && !checkIndicator.checked && !checkIndicator.hasActiveDrag
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.edit && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.drag
+ && !control.checked && !control.hasActiveDrag
+ PropertyChanges {
+ target: icon
+ color: control.style.icon.idle
+ }
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "dragHover"
- when: myControl.enabled && checkIndicator.hasActiveHoverDrag
+ when: control.__parentControl.enabled && control.hasActiveHoverDrag
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: control
+ color: control.style.background.interaction
}
},
State {
name: "globalHover"
- when: myControl.enabled && checkIndicator.enabled && !myControl.drag
- && !checkIndicator.hover && myControl.hover && !myControl.edit
- && !checkIndicator.checked
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && control.__parentControl.hover && !control.__parentControl.edit
+ && !control.checked
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ target: control
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: myControl.enabled && checkIndicator.enabled && !myControl.drag
- && checkIndicator.hover && myControl.hover && !checkIndicator.pressed
- && !checkIndicator.checked
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.hover && control.__parentControl.hover
+ && !control.pressed && !control.checked
+ PropertyChanges {
+ target: icon
+ color: control.style.icon.hover
+ }
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundHover
+ target: control
+ color: control.style.background.hover
}
},
State {
name: "check"
- when: checkIndicator.checked
+ when: control.checked
PropertyChanges {
- target: checkIndicatorIcon
- color: StudioTheme.Values.themeIconColor
+ target: icon
+ color: control.style.icon.interaction
}
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "edit"
- when: myControl.edit && !checkIndicator.checked
- && !(checkIndicator.hover && myControl.hover)
+ when: control.__parentControl.edit && !control.checked
+ && !(control.hover && control.__parentControl.hover)
PropertyChanges {
- target: checkIndicatorIcon
- color: StudioTheme.Values.themeTextColor
+ target: icon
+ color: control.style.icon.idle
}
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "press"
- when: myControl.enabled && checkIndicator.enabled && !myControl.drag
- && checkIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.pressed
PropertyChanges {
- target: checkIndicatorIcon
- color: StudioTheme.Values.themeIconColor
+ target: icon
+ color: control.style.icon.interaction
}
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "drag"
- when: (myControl.drag !== undefined && myControl.drag) && !checkIndicator.checked
- && !(checkIndicator.hover && myControl.hover)
+ when: (control.__parentControl.drag !== undefined && control.__parentControl.drag)
+ && !control.checked && !(control.hover && control.__parentControl.hover)
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
- target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ target: control
+ color: control.style.background.disabled
}
PropertyChanges {
- target: checkIndicatorIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ target: icon
+ color: control.style.icon.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml
index 7182bca704..39b1ac8a1c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml
@@ -1,21 +1,24 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Window 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Window
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.ComboBox {
- id: myComboBox
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property alias actionIndicator: actionIndicator
property alias labelColor: comboBoxInput.color
+ property alias textElidable: comboBoxInput.elidable
// This property is used to indicate the global hover state
property bool hover: (comboBoxInput.hover || actionIndicator.hover || popupIndicator.hover)
- && myComboBox.enabled
- property bool edit: myComboBox.activeFocus && myComboBox.editable
+ && control.enabled
+ property bool edit: control.activeFocus && control.editable
property bool open: comboBoxPopup.opened
property bool hasActiveDrag: false // an item that can be dropped on the combobox is being dragged
property bool hasActiveHoverDrag: false // an item that can be dropped on the combobox is being hovered on the combobox
@@ -23,40 +26,44 @@ T.ComboBox {
property bool dirty: false // user modification flag
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
property alias textInput: comboBoxInput
+ property alias suffix: comboBoxInput.suffix
+
+ property int maximumPopupHeight: control.style.maxComboBoxPopupHeight
signal compressedActivated(int index, int reason)
enum ActivatedReason { EditingFinished, Other }
- width: StudioTheme.Values.defaultControlWidth
- height: StudioTheme.Values.defaultControlHeight
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
leftPadding: actionIndicator.width
- rightPadding: popupIndicator.width + StudioTheme.Values.border
- font.pixelSize: StudioTheme.Values.myFontSize
+ rightPadding: popupIndicator.width + control.style.borderWidth
+ font.pixelSize: control.style.baseFontSize
wheelEnabled: false
onFocusChanged: {
- if (!myComboBox.focus)
+ if (!control.focus)
comboBoxPopup.close()
}
onActiveFocusChanged: {
- if (myComboBox.activeFocus)
- comboBoxInput.preFocusText = myComboBox.editText
+ if (control.activeFocus)
+ comboBoxInput.preFocusText = control.editText
}
ActionIndicator {
id: actionIndicator
- myControl: myComboBox
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- width: actionIndicator.visible ? myComboBox.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? myComboBox.__actionIndicatorHeight : 0
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
contentItem: ComboBoxInput {
@@ -64,98 +71,102 @@ T.ComboBox {
property string preFocusText: ""
- myControl: myComboBox
- text: myComboBox.editText
+ style: control.style
+ __parentControl: control
+ text: control.editText
onEditingFinished: {
comboBoxInput.deselect()
comboBoxInput.focus = false
- myComboBox.focus = false
+ control.focus = false
// Only trigger the signal, if the value was modified
- if (myComboBox.dirty) {
- myTimer.stop()
- myComboBox.dirty = false
- myComboBox.accepted()
+ if (control.dirty) {
+ timer.stop()
+ control.dirty = false
+ control.accepted()
}
}
- onTextEdited: myComboBox.dirty = true
+ onTextEdited: control.dirty = true
}
indicator: CheckIndicator {
id: popupIndicator
- myControl: myComboBox
- myPopup: myComboBox.popup
+ style: control.style
+ __parentControl: control
+ __parentPopup: control.popup
x: comboBoxInput.x + comboBoxInput.width
- y: StudioTheme.Values.border
- width: StudioTheme.Values.checkIndicatorWidth - StudioTheme.Values.border
- height: StudioTheme.Values.checkIndicatorHeight - StudioTheme.Values.border * 2
+ y: control.style.borderWidth
+ width: control.style.squareControlSize.width - control.style.borderWidth
+ height: control.style.squareControlSize.height - control.style.borderWidth * 2
}
background: Rectangle {
id: comboBoxBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
x: actionIndicator.width
- width: myComboBox.width - actionIndicator.width
- height: myComboBox.height
+ width: control.width - actionIndicator.width
+ height: control.height
}
Timer {
- id: myTimer
+ id: timer
property int activatedIndex
repeat: false
running: false
interval: 100
- onTriggered: myComboBox.compressedActivated(myTimer.activatedIndex,
- ComboBox.ActivatedReason.Other)
+ onTriggered: control.compressedActivated(timer.activatedIndex,
+ ComboBox.ActivatedReason.Other)
}
onActivated: function(index) {
- myTimer.activatedIndex = index
- myTimer.restart()
+ timer.activatedIndex = index
+ timer.restart()
}
delegate: ItemDelegate {
- id: myItemDelegate
+ id: itemDelegate
width: comboBoxPopup.width - comboBoxPopup.leftPadding - comboBoxPopup.rightPadding
- (comboBoxPopupScrollBar.visible ? comboBoxPopupScrollBar.contentItem.implicitWidth
+ 2 : 0) // TODO Magic number
- height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
+ height: control.style.controlSize.height - 2 * control.style.borderWidth
padding: 0
enabled: model.enabled === undefined ? true : model.enabled
contentItem: Text {
leftPadding: itemDelegateIconArea.width
- text: myComboBox.textRole ? (Array.isArray(myComboBox.model) ? modelData[myComboBox.textRole]
- : model[myComboBox.textRole]) : modelData
+ text: control.textRole ? (Array.isArray(control.model)
+ ? modelData[control.textRole]
+ : model[control.textRole])
+ : modelData
color: {
- if (!myItemDelegate.enabled)
- return StudioTheme.Values.themeTextColorDisabled
+ if (!itemDelegate.enabled)
+ return control.style.text.disabled
- return myItemDelegate.highlighted ? StudioTheme.Values.themeTextSelectedTextColor
- : StudioTheme.Values.themeTextColor
+ return itemDelegate.highlighted ? control.style.text.selectedText
+ : control.style.text.idle
}
- font: myComboBox.font
+ font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
id: itemDelegateIconArea
- width: myItemDelegate.height
- height: myItemDelegate.height
+ width: itemDelegate.height
+ height: itemDelegate.height
T.Label {
id: itemDelegateIcon
text: StudioTheme.Constants.tickIcon
- color: myItemDelegate.highlighted ? StudioTheme.Values.themeTextSelectedTextColor
- : StudioTheme.Values.themeTextColor
+ color: itemDelegate.highlighted ? control.style.text.selectedText
+ : control.style.text.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti
- visible: myComboBox.currentIndex === index ? true : false
+ font.pixelSize: control.style.smallIconFontSize
+ visible: control.currentIndex === index
anchors.fill: parent
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
@@ -163,31 +174,31 @@ T.ComboBox {
}
}
- highlighted: myComboBox.highlightedIndex === index
+ highlighted: control.highlightedIndex === index
background: Rectangle {
id: itemDelegateBackground
x: 0
y: 0
- width: myItemDelegate.width
- height: myItemDelegate.height
- color: myItemDelegate.highlighted ? StudioTheme.Values.themeInteraction : "transparent"
+ width: itemDelegate.width
+ height: itemDelegate.height
+ color: itemDelegate.highlighted ? control.style.interaction : "transparent"
}
}
popup: T.Popup {
id: comboBoxPopup
- x: actionIndicator.width + StudioTheme.Values.border
- y: myComboBox.height
- width: myComboBox.width - actionIndicator.width - StudioTheme.Values.border * 2
+ x: actionIndicator.width
+ y: control.height
+ width: control.width - actionIndicator.width
// TODO Setting the height on the popup solved the problem with the popup of height 0,
// but it has the problem that it sometimes extend over the border of the actual window
// and is then cut off.
height: Math.min(contentItem.implicitHeight + comboBoxPopup.topPadding
+ comboBoxPopup.bottomPadding,
- myComboBox.Window.height - topMargin - bottomMargin,
- StudioTheme.Values.maxComboBoxPopupHeight)
- padding: StudioTheme.Values.border
+ control.Window.height - topMargin - bottomMargin,
+ control.maximumPopupHeight)
+ padding: control.style.borderWidth
margins: 0 // If not defined margin will be -1
closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent
| T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside
@@ -197,8 +208,8 @@ T.ComboBox {
id: listView
clip: true
implicitHeight: listView.contentHeight
- model: myComboBox.popup.visible ? myComboBox.delegateModel : null
- currentIndex: myComboBox.highlightedIndex
+ model: control.popup.visible ? control.delegateModel : null
+ currentIndex: control.highlightedIndex
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar {
id: comboBoxPopupScrollBar
@@ -207,7 +218,7 @@ T.ComboBox {
}
background: Rectangle {
- color: StudioTheme.Values.themePopupBackground
+ color: control.style.popup.background
border.width: 0
}
@@ -218,10 +229,10 @@ T.ComboBox {
states: [
State {
name: "default"
- when: myComboBox.enabled && !myComboBox.hover && !myComboBox.edit && !myComboBox.open
- && !myComboBox.activeFocus && !myComboBox.hasActiveDrag
+ when: control.enabled && !control.hover && !control.edit && !control.open
+ && !control.activeFocus && !control.hasActiveDrag
PropertyChanges {
- target: myComboBox
+ target: control
wheelEnabled: false
}
PropertyChanges {
@@ -230,34 +241,41 @@ T.ComboBox {
}
PropertyChanges {
target: comboBoxBackground
- color: StudioTheme.Values.themeControlBackground
+ border.color: control.style.border.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: control.enabled && control.hover && !control.edit && !control.open
+ && !control.activeFocus && !control.hasActiveDrag
+ PropertyChanges {
+ target: comboBoxBackground
+ border.color: control.style.border.hover
}
},
State {
name: "acceptsDrag"
- when: myComboBox.enabled && myComboBox.hasActiveDrag && !myComboBox.hasActiveHoverDrag
+ when: control.enabled && control.hasActiveDrag && !control.hasActiveHoverDrag
PropertyChanges {
target: comboBoxBackground
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ border.color: control.style.border.interaction
}
},
State {
name: "dragHover"
- when: myComboBox.enabled && myComboBox.hasActiveHoverDrag
+ when: control.enabled && control.hasActiveHoverDrag
PropertyChanges {
target: comboBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ border.color: control.style.border.interaction
}
},
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via
// tab focus. It is therefor possible to use the mouse wheel to scroll through the items.
State {
name: "focus"
- when: myComboBox.enabled && myComboBox.activeFocus && !myComboBox.editable
- && !myComboBox.open
+ when: control.enabled && control.activeFocus && !control.editable && !control.open
PropertyChanges {
- target: myComboBox
+ target: control
wheelEnabled: true
}
PropertyChanges {
@@ -267,9 +285,9 @@ T.ComboBox {
},
State {
name: "edit"
- when: myComboBox.enabled && myComboBox.edit && !myComboBox.open
+ when: control.enabled && control.edit && !control.open
PropertyChanges {
- target: myComboBox
+ target: control
wheelEnabled: true
}
PropertyChanges {
@@ -279,8 +297,7 @@ T.ComboBox {
}
PropertyChanges {
target: comboBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ border.color: control.style.border.interaction
}
StateChangeScript {
script: comboBoxPopup.close()
@@ -288,9 +305,9 @@ T.ComboBox {
},
State {
name: "popup"
- when: myComboBox.enabled && myComboBox.open
+ when: control.enabled && control.open
PropertyChanges {
- target: myComboBox
+ target: control
wheelEnabled: true
}
PropertyChanges {
@@ -300,26 +317,24 @@ T.ComboBox {
}
PropertyChanges {
target: comboBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ border.color: control.style.border.interaction
}
},
State {
name: "disable"
- when: !myComboBox.enabled
+ when: !control.enabled
PropertyChanges {
target: comboBoxBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ border.color: control.style.border.disabled
}
}
]
Keys.onPressed: function(event) {
if (event.key === Qt.Key_Escape) {
- myComboBox.editText = comboBoxInput.preFocusText
- myComboBox.dirty = true
- myComboBox.focus = false
+ control.editText = comboBoxInput.preFocusText
+ control.dirty = true
+ control.focus = false
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml
index 4405fcd2f3..12dd3b1fbc 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml
@@ -1,44 +1,76 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
TextInput {
- id: textInput
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property bool edit: textInput.activeFocus
- property bool hover: mouseArea.containsMouse && textInput.enabled
+ property T.ComboBox __parentControl
+
+ property bool edit: control.activeFocus
+ property bool hover: mouseArea.containsMouse && control.enabled
+ property bool elidable: false
+ property string suffix: ""
z: 2
- font: myControl.font
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
+ font: control.__parentControl.font
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
- leftPadding: StudioTheme.Values.inputHorizontalPadding
- rightPadding: StudioTheme.Values.inputHorizontalPadding
+ leftPadding: control.style.inputHorizontalPadding
+ rightPadding: control.style.inputHorizontalPadding
- readOnly: !myControl.editable
- validator: myControl.validator
- inputMethodHints: myControl.inputMethodHints
+ readOnly: !control.__parentControl.editable
+ validator: control.__parentControl.validator
+ inputMethodHints: control.__parentControl.inputMethodHints
selectByMouse: false
activeFocusOnPress: false
clip: true
+ echoMode: control.elidable ? TextInput.NoEcho : TextInput.Normal
+
+ Text {
+ id: elidableText
+ anchors.fill: control
+ leftPadding: control.leftPadding
+ rightPadding: control.rightPadding
+ horizontalAlignment: control.horizontalAlignment
+ verticalAlignment: control.verticalAlignment
+ font: control.font
+ color: control.color
+ text: control.text + control.suffix
+ visible: control.elidable
+ elide: Text.ElideRight
+ }
+
+ Text {
+ id: nonElidableSuffix
+ anchors.fill: control
+ leftPadding: control.implicitWidth - control.rightPadding
+ rightPadding: control.rightPadding
+ horizontalAlignment: control.horizontalAlignment
+ verticalAlignment: control.verticalAlignment
+ font: control.font
+ color: control.color
+ text: control.suffix
+ visible: !control.elidable
+ }
Rectangle {
- id: textInputBackground
- x: StudioTheme.Values.border
- y: StudioTheme.Values.border
+ id: background
+ x: control.style.borderWidth
+ y: control.style.borderWidth
z: -1
- width: textInput.width
- height: StudioTheme.Values.height - StudioTheme.Values.border * 2
- color: StudioTheme.Values.themeControlBackground
+ width: control.width
+ height: control.style.controlSize.height - control.style.borderWidth * 2
+ color: control.style.background.idle
border.width: 0
}
@@ -51,16 +83,16 @@ TextInput {
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onPressed: function(mouse) {
- if (!textInput.myControl.editable) {
- if (myControl.popup.opened) {
- myControl.popup.close()
- myControl.focus = false
+ if (!control.__parentControl.editable) {
+ if (control.__parentControl.popup.opened) {
+ control.__parentControl.popup.close()
+ control.__parentControl.focus = false
} else {
- myControl.popup.open()
- //myControl.forceActiveFocus()
+ control.__parentControl.popup.open()
+ //textInput.__control.forceActiveFocus()
}
} else {
- textInput.forceActiveFocus()
+ control.forceActiveFocus()
}
mouse.accepted = false
@@ -70,11 +102,12 @@ TextInput {
states: [
State {
name: "default"
- when: myControl.enabled && !textInput.edit && !textInput.hover && !myControl.hover
- && !myControl.open && !myControl.hasActiveDrag
+ when: control.__parentControl.enabled && !control.edit && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.open
+ && !control.__parentControl.hasActiveDrag
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackground
+ target: background
+ color: control.style.background.idle
}
PropertyChanges {
target: mouseArea
@@ -84,44 +117,58 @@ TextInput {
},
State {
name: "dragHover"
- when: myControl.enabled && myControl.hasActiveHoverDrag
+ when: control.__parentControl.enabled
+ && (control.__parentControl.hasActiveHoverDrag ?? false)
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: background
+ color: control.style.background.interaction
}
},
State {
name: "globalHover"
- when: myControl.hover && !textInput.hover && !textInput.edit && !myControl.open
+ when: control.__parentControl.hover && !control.hover && !control.edit
+ && !control.__parentControl.open
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ target: background
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: textInput.hover && myControl.hover && !textInput.edit
+ when: control.hover && control.__parentControl.hover && !control.edit
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundHover
+ target: background
+ color: control.style.background.hover
}
},
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via
// tab focus. It is therefor possible to use the mouse wheel to scroll through the items.
State {
name: "focus"
- when: textInput.edit && !myControl.editable
+ when: control.edit && !control.__parentControl.editable
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: background
+ color: control.style.background.interaction
}
},
State {
name: "edit"
- when: textInput.edit && myControl.editable
+ when: control.edit && control.__parentControl.editable
+ PropertyChanges {
+ target: background
+ color: control.style.background.interaction
+ }
+ PropertyChanges {
+ target: control
+ echoMode: TextInput.Normal
+ }
+ PropertyChanges {
+ target: elidableText
+ visible: false
+ }
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: nonElidableSuffix
+ visible: false
}
PropertyChanges {
target: mouseArea
@@ -131,22 +178,22 @@ TextInput {
},
State {
name: "popup"
- when: myControl.open
+ when: control.__parentControl.open
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundHover
+ target: background
+ color: control.style.background.hover
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
- target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ target: background
+ color: control.style.background.disabled
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeTextColorDisabled
+ target: control
+ color: control.style.text.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ContextMenu.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ContextMenu.qml
index 37cf37d7e0..6865453d69 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ContextMenu.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ContextMenu.qml
@@ -1,69 +1,75 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
+import QtQuick
Menu {
- id: contextMenu
+ id: control
- property Item myTextEdit
+ required property Item __parentControl // TextInput or TextEdit
MenuItem {
- text: "Undo"
- enabled: myTextEdit.canUndo
- onTriggered: myTextEdit.undo()
+ style: control.style
+ text: qsTr("Undo")
+ enabled: control.__parentControl.canUndo
+ onTriggered: control.__parentControl.undo()
/* shortcut: StandardKey.Undo Shortcuts in QQC2 seem to override global shortcuts */
}
MenuItem {
- text: "Redo"
- enabled: myTextEdit.canRedo
- onTriggered: myTextEdit.redo()
+ style: control.style
+ text: qsTr("Redo")
+ enabled: control.__parentControl.canRedo
+ onTriggered: control.__parentControl.redo()
/* shortcut: StandardKey.Redo Shortcuts in QQC2 seem to override global shortcuts */
}
- MenuSeparator {
- }
+ MenuSeparator { style: control.style }
MenuItem {
- text: "Copy"
- enabled: myTextEdit.selectedText !== ""
- onTriggered: myTextEdit.copy()
+ style: control.style
+ text: qsTr("Copy")
+ enabled: control.__parentControl.selectedText !== ""
+ onTriggered: control.__parentControl.copy()
/* shortcut: StandardKey.Copy Shortcuts in QQC2 seem to override global shortcuts */
}
MenuItem {
- text: "Cut"
- enabled: myTextEdit.selectedText !== "" && !myTextEdit.readOnly
- onTriggered: myTextEdit.cut()
+ style: control.style
+ text: qsTr("Cut")
+ enabled: control.__parentControl.selectedText !== "" && !control.__parentControl.readOnly
+ onTriggered: control.__parentControl.cut()
/* shortcut: StandardKey.Cut Shortcuts in QQC2 seem to override global shortcuts */
}
MenuItem {
- text: "Paste"
- enabled: myTextEdit.canPaste
- onTriggered: myTextEdit.paste()
+ style: control.style
+ text: qsTr("Paste")
+ enabled: control.__parentControl.canPaste
+ onTriggered: control.__parentControl.paste()
/* shortcut: StandardKey.Paste Shortcuts in QQC2 seem to override global shortcuts */
}
MenuItem {
- text: "Delete"
- enabled: myTextEdit.selectedText !== ""
- onTriggered: myTextEdit.remove(myTextEdit.selectionStart,
- myTextEdit.selectionEnd)
+ style: control.style
+ text: qsTr("Delete")
+ enabled: control.__parentControl.selectedText !== ""
+ onTriggered: control.__parentControl.remove(control.__parentControl.selectionStart,
+ control.__parentControl.selectionEnd)
/* shortcut: StandardKey.Delete Shortcuts in QQC2 seem to override global shortcuts */
}
MenuItem {
- text: "Clear"
- enabled: myTextEdit.text !== ""
- onTriggered: myTextEdit.clear()
+ style: control.style
+ text: qsTr("Clear")
+ enabled: control.__parentControl.text !== ""
+ onTriggered: control.__parentControl.clear()
/* shortcut: StandardKey.DeleteCompleteLine Shortcuts in QQC2 seem to override global shortcuts */
}
- MenuSeparator {
- }
+ MenuSeparator { style: control.style }
MenuItem {
- text: "Select All"
- enabled: myTextEdit.text !== ""
- && myTextEdit.selectedText !== myTextEdit.text
- onTriggered: myTextEdit.selectAll()
+ style: control.style
+ text: qsTr("Select All")
+ enabled: control.__parentControl.text !== ""
+ && control.__parentControl.selectedText !== control.__parentControl.text
+ onTriggered: control.__parentControl.selectAll()
/* shortcut: StandardKey.SelectAll Shortcuts in QQC2 seem to override global shortcuts */
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Dialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Dialog.qml
index 4320021f9d..64e6ebfd4d 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Dialog.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Dialog.qml
@@ -1,34 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Dialog {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding,
@@ -39,36 +19,37 @@ T.Dialog {
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
- padding: StudioTheme.Values.dialogPadding
+ padding: control.style.dialogPadding
background: Rectangle {
- color: StudioTheme.Values.themeDialogBackground
- border.color: StudioTheme.Values.themeDialogOutline
- border.width: StudioTheme.Values.border
+ color: control.style.dialog.background
+ border.color: control.style.dialog.border
+ border.width: control.style.borderWidth
}
header: T.Label {
- text: root.title
- visible: root.title
+ text: control.title
+ visible: control.title
elide: T.Label.ElideRight
font.bold: true
- padding: StudioTheme.Values.dialogPadding
- color: StudioTheme.Values.themeTextColor
+ padding: control.padding
+ color: control.style.text.idle
background: Rectangle {
- x: StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: parent.width - (2 * StudioTheme.Values.border)
- height: parent.height - (2 * StudioTheme.Values.border)
- color: StudioTheme.Values.themeDialogBackground
+ x: control.style.borderWidth
+ y: control.style.borderWidth
+ width: parent.width - (2 * control.style.borderWidth)
+ height: parent.height - (2 * control.style.borderWidth)
+ color: control.style.dialog.background
}
}
footer: DialogButtonBox {
+ style: control.style
visible: count > 0
}
T.Overlay.modal: Rectangle {
- color: Qt.alpha(StudioTheme.Values.themeDialogBackground, 0.5)
+ color: Qt.alpha(control.style.dialog.overlay, 0.5)
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButton.qml
index 173b4ffdf3..cce9679772 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButton.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButton.qml
@@ -1,58 +1,36 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Button {
- id: root
+ id: control
- implicitWidth: Math.max(
- background ? background.implicitWidth : 0,
- textItem.implicitWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(
- background ? background.implicitHeight : 0,
- textItem.implicitHeight + topPadding + bottomPadding)
- leftPadding: StudioTheme.Values.dialogButtonPadding
- rightPadding: StudioTheme.Values.dialogButtonPadding
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ implicitWidth: Math.max(buttonBackground ? buttonBackground.implicitWidth : 0,
+ textItem.implicitWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(buttonBackground ? buttonBackground.implicitHeight : 0,
+ textItem.implicitHeight + topPadding + bottomPadding)
+ leftPadding: control.style.dialogPadding
+ rightPadding: control.style.dialogPadding
background: Rectangle {
- id: background
+ id: buttonBackground
implicitWidth: 70
implicitHeight: 20
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
anchors.fill: parent
}
contentItem: Text {
id: textItem
- text: root.text
- font.pixelSize: StudioTheme.Values.baseFontSize
- color: StudioTheme.Values.themeTextColor
+ text: control.text
+ font.pixelSize: control.style.baseFontSize
+ color: control.style.text.idle
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
@@ -60,58 +38,58 @@ T.Button {
states: [
State {
name: "default"
- when: root.enabled && !root.down && !root.hovered && !root.checked
+ when: control.enabled && !control.down && !control.hovered && !control.checked
PropertyChanges {
- target: background
- color: root.highlighted ? StudioTheme.Values.themeInteraction
- : StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ target: buttonBackground
+ color: control.highlighted ? control.style.interaction
+ : control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
target: textItem
- color: StudioTheme.Values.themeTextColor
+ color: control.style.text.idle
}
},
State {
name: "hover"
- when: root.enabled && root.hovered && !root.checked && !root.down
+ when: control.enabled && control.hovered && !control.checked && !control.down
PropertyChanges {
- target: background
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ target: buttonBackground
+ color: control.style.background.hover
+ border.color: control.style.border.hover
}
PropertyChanges {
target: textItem
- color: StudioTheme.Values.themeTextColor
+ color: control.style.text.hover
}
},
State {
name: "press"
- when: root.enabled && (root.checked || root.down)
+ when: control.enabled && (control.checked || control.down)
PropertyChanges {
- target: background
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ target: buttonBackground
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
PropertyChanges {
target: textItem
- color: StudioTheme.Values.themeTextColor
+ color: control.style.text.interaction
}
},
State {
name: "disable"
- when: !root.enabled
+ when: !control.enabled
PropertyChanges {
- target: background
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ target: buttonBackground
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
PropertyChanges {
target: textItem
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.text.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButtonBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButtonBox.qml
index c32eae2e98..199c8fbe6c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButtonBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/DialogButtonBox.qml
@@ -1,27 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Templates as T
@@ -30,20 +8,23 @@ import StudioTheme 1.0 as StudioTheme
T.DialogButtonBox {
id: control
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
(control.count === 1 ? implicitContentWidth * 2 : implicitContentWidth) + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
contentWidth: contentItem.contentWidth
- spacing: StudioTheme.Values.dialogButtonSpacing
- padding: StudioTheme.Values.dialogPadding
+ spacing: control.style.dialogButtonSpacing
+ padding: control.style.dialogPadding
alignment: Qt.AlignRight | Qt.AlignBottom
delegate: DialogButton {
+ style: control.style
width: control.count === 1 ? control.availableWidth / 2 : undefined
- implicitHeight: StudioTheme.Values.height
+ implicitHeight: control.style.controlSize.height
highlighted: DialogButtonBox.buttonRole === DialogButtonBox.AcceptRole
|| DialogButtonBox.buttonRole === DialogButtonBox.ApplyRole
}
@@ -59,10 +40,10 @@ T.DialogButtonBox {
background: Rectangle {
implicitHeight: 30
- x: StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: parent.width - (2 * StudioTheme.Values.border)
- height: parent.height - (2 * StudioTheme.Values.border)
- color: StudioTheme.Values.themeDialogBackground
+ x: control.style.borderWidth
+ y: control.style.borderWidth
+ width: parent.width - (2 * control.style.borderWidth)
+ height: parent.height - (2 * control.style.borderWidthr)
+ color: control.style.dialog.background
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml
index 583947e7ec..5593f46db0 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
@@ -6,7 +6,9 @@ import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Item {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
enum Interaction { None, TextEdit, Key }
@@ -23,7 +25,7 @@ Item {
// This is the actual filter that is applied on the model
property string filter: ""
- property bool filterActive: root.filter !== ""
+ property bool filterActive: control.filter !== ""
// Accept arbitrary input or only items from the model
property bool allowUserInput: false
@@ -40,13 +42,13 @@ Item {
// This property is used to indicate the global hover state
property bool hover: (actionIndicator.hover || textInput.hover || checkIndicator.hover)
- && root.enabled
+ && control.enabled
property alias edit: textInput.edit
property alias open: popup.visible
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
property bool dirty: false // user modification flag
@@ -65,43 +67,43 @@ Item {
property bool hasActiveDrag: false // an item that can be dropped here is being dragged
property bool hasActiveHoverDrag: false // an item that can be dropped her is being hovered on top
- width: StudioTheme.Values.defaultControlWidth
- height: StudioTheme.Values.defaultControlHeight
- implicitHeight: StudioTheme.Values.defaultControlHeight
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
+ implicitHeight: control.style.controlSize.width
function selectItem(itemsIndex) {
textInput.text = sortFilterModel.items.get(itemsIndex).model.name
- root.currentIndex = itemsIndex
- root.finishEditing()
- root.activated(itemsIndex)
+ control.currentIndex = itemsIndex
+ control.finishEditing()
+ control.activated(itemsIndex)
}
function submitValue() {
- if (!root.allowUserInput) {
+ if (!control.allowUserInput) {
// If input isn't according to any item in the model, don't finish editing
- if (root.highlightedIndex === -1)
+ if (control.highlightedIndex === -1)
return
- root.selectItem(root.highlightedIndex)
+ control.selectItem(control.highlightedIndex)
} else {
- root.currentIndex = -1
+ control.currentIndex = -1
// Only trigger the signal, if the value was modified
- if (root.dirty) {
+ if (control.dirty) {
myTimer.stop()
- root.dirty = false
- root.editText = root.editText.trim()
+ control.dirty = false
+ control.editText = control.editText.trim()
}
- root.finishEditing()
- root.accepted()
+ control.finishEditing()
+ control.accepted()
}
}
function finishEditing() {
- root.editing = false
- root.filter = ""
- root.autocompleteString = ""
+ control.editing = false
+ control.filter = ""
+ control.autocompleteString = ""
textInput.focus = false // Remove focus from text field
popup.close()
}
@@ -111,16 +113,16 @@ Item {
if (!numItems)
return
- if (root.highlightedIndex === -1) // Nothing is selected
- root.setHighlightedIndexVisible(0)
+ if (control.highlightedIndex === -1) // Nothing is selected
+ control.setHighlightedIndexVisible(0)
else {
- let currentVisibleIndex = sortFilterModel.items.get(root.highlightedIndex).visibleIndex
+ let currentVisibleIndex = sortFilterModel.items.get(control.highlightedIndex).visibleIndex
++currentVisibleIndex
if (currentVisibleIndex > numItems - 1)
currentVisibleIndex = 0
- root.setHighlightedIndexVisible(currentVisibleIndex)
+ control.setHighlightedIndexVisible(currentVisibleIndex)
}
}
@@ -129,32 +131,32 @@ Item {
if (!numItems)
return
- if (root.highlightedIndex === -1) // Nothing is selected
- root.setHighlightedIndexVisible(numItems - 1)
+ if (control.highlightedIndex === -1) // Nothing is selected
+ control.setHighlightedIndexVisible(numItems - 1)
else {
- let currentVisibleIndex = sortFilterModel.items.get(root.highlightedIndex).visibleIndex
+ let currentVisibleIndex = sortFilterModel.items.get(control.highlightedIndex).visibleIndex
--currentVisibleIndex
if (currentVisibleIndex < 0)
currentVisibleIndex = numItems - 1
- root.setHighlightedIndexVisible(currentVisibleIndex)
+ control.setHighlightedIndexVisible(currentVisibleIndex)
}
}
function updateHighlightedIndex() {
// Check if current index is still part of the filtered list, if not set it to 0
- if (root.highlightedIndex !== -1 && !sortFilterModel.items.get(root.highlightedIndex).inVisible) {
- root.setHighlightedIndexVisible(0)
+ if (control.highlightedIndex !== -1 && !sortFilterModel.items.get(control.highlightedIndex).inVisible) {
+ control.setHighlightedIndexVisible(0)
} else {
// Needs to be set in order for ListView to keep its currenIndex up to date, so
// scroll position gets updated according to the higlighted item
- root.setHighlightedIndexItems(root.highlightedIndex)
+ control.setHighlightedIndexItems(control.highlightedIndex)
}
}
function setHighlightedIndexItems(itemsIndex) { // items group index
- root.highlightedIndex = itemsIndex
+ control.highlightedIndex = itemsIndex
if (itemsIndex === -1)
listView.currentIndex = -1
@@ -164,19 +166,19 @@ Item {
function setHighlightedIndexVisible(visibleIndex) { // visible group index
if (visibleIndex === -1)
- root.highlightedIndex = -1
+ control.highlightedIndex = -1
else
- root.highlightedIndex = sortFilterModel.visibleGroup.get(visibleIndex).itemsIndex
+ control.highlightedIndex = sortFilterModel.visibleGroup.get(visibleIndex).itemsIndex
listView.currentIndex = visibleIndex
}
function updateAutocomplete() {
- if (root.highlightedIndex === -1)
- root.autocompleteString = ""
+ if (control.highlightedIndex === -1)
+ control.autocompleteString = ""
else {
- let suggestion = sortFilterModel.items.get(root.highlightedIndex).model.name
- root.autocompleteString = suggestion.substring(textInput.text.length)
+ let suggestion = sortFilterModel.items.get(control.highlightedIndex).model.name
+ control.autocompleteString = suggestion.substring(textInput.text.length)
}
}
@@ -195,8 +197,8 @@ Item {
repeat: false
running: false
interval: 100
- onTriggered: root.compressedActivated(myTimer.activatedIndex,
- ComboBox.ActivatedReason.Other)
+ onTriggered: control.compressedActivated(myTimer.activatedIndex,
+ ComboBox.ActivatedReason.Other)
}
onActivated: function(index) {
@@ -205,8 +207,8 @@ Item {
}
onHighlightedIndexChanged: {
- if (root.editing || (root.editText === "" && root.allowUserInput))
- root.updateAutocomplete()
+ if (control.editing || (control.editText === "" && control.allowUserInput))
+ control.updateAutocomplete()
}
DelegateModel {
@@ -221,14 +223,14 @@ Item {
width: popup.width - popup.leftPadding - popup.rightPadding
- (popupScrollBar.visible ? popupScrollBar.contentItem.implicitWidth + 2
: 0) // TODO Magic number
- height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
+ height: control.style.controlSize.height - 2 * control.style.borderWidth
padding: 0
contentItem: Text {
- leftPadding: StudioTheme.Values.inputHorizontalPadding
+ leftPadding: control.style.inputHorizontalPadding
text: name
font.italic: true
- color: StudioTheme.Values.themeTextColor
+ color: control.style.text.idle
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
@@ -247,7 +249,7 @@ Item {
id: sortFilterModel
filterAcceptsItem: function(item) {
- return item.name.toLowerCase().startsWith(root.filter.toLowerCase())
+ return item.name.toLowerCase().startsWith(control.filter.toLowerCase())
}
lessThan: function(left, right) {
@@ -263,17 +265,17 @@ Item {
width: popup.width - popup.leftPadding - popup.rightPadding
- (popupScrollBar.visible ? popupScrollBar.contentItem.implicitWidth + 2
: 0) // TODO Magic number
- height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
+ height: control.style.controlSize.height - 2 * control.style.borderWidth
padding: 0
hoverEnabled: true
- highlighted: root.highlightedIndex === delegateRoot.DelegateModel.itemsIndex
+ highlighted: control.highlightedIndex === delegateRoot.DelegateModel.itemsIndex
onHoveredChanged: {
if (delegateRoot.hovered && !popupMouseArea.active)
- root.setHighlightedIndexItems(delegateRoot.DelegateModel.itemsIndex)
+ control.setHighlightedIndexItems(delegateRoot.DelegateModel.itemsIndex)
}
- onClicked: root.selectItem(delegateRoot.DelegateModel.itemsIndex)
+ onClicked: control.selectItem(delegateRoot.DelegateModel.itemsIndex)
indicator: Item {
id: itemDelegateIconArea
@@ -283,12 +285,12 @@ Item {
T.Label {
id: itemDelegateIcon
text: StudioTheme.Constants.tickIcon
- color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor
- : StudioTheme.Values.themeTextColor
+ color: delegateRoot.highlighted ? control.style.text.selectedText
+ : control.style.text.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti
- visible: root.currentIndex === delegateRoot.DelegateModel.itemsIndex ? true
- : false
+ font.pixelSize: control.style.smallIconFontSize
+ visible: control.currentIndex === delegateRoot.DelegateModel.itemsIndex ? true
+ : false
anchors.fill: parent
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
@@ -299,8 +301,8 @@ Item {
contentItem: Text {
leftPadding: itemDelegateIconArea.width
text: name
- color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor
- : StudioTheme.Values.themeTextColor
+ color: delegateRoot.highlighted ? control.style.text.selectedText
+ : control.style.text.idle
font: textInput.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
@@ -311,20 +313,19 @@ Item {
y: 0
width: delegateRoot.width
height: delegateRoot.height
- color: delegateRoot.highlighted ? StudioTheme.Values.themeInteraction
- : "transparent"
+ color: delegateRoot.highlighted ? control.style.interaction : "transparent"
}
}
onUpdated: {
- if (!root.__isCompleted)
+ if (!control.__isCompleted)
return
if (sortFilterModel.count === 0)
- root.setHighlightedIndexVisible(-1)
+ control.setHighlightedIndexVisible(-1)
else {
- if (root.highlightedIndex === -1 && !root.allowUserInput)
- root.setHighlightedIndexVisible(0)
+ if (control.highlightedIndex === -1 && !control.allowUserInput)
+ control.setHighlightedIndexVisible(0)
}
}
}
@@ -332,11 +333,12 @@ Item {
Row {
ActionIndicator {
id: actionIndicator
- myControl: root
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- width: actionIndicator.visible ? root.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? root.__actionIndicatorHeight : 0
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
TextInput {
@@ -349,16 +351,16 @@ Item {
x: 0
y: 0
z: 2
- width: root.width - actionIndicator.width
- height: root.height
- leftPadding: StudioTheme.Values.inputHorizontalPadding
- rightPadding: StudioTheme.Values.inputHorizontalPadding + checkIndicator.width
- + StudioTheme.Values.border
+ width: control.width - actionIndicator.width
+ height: control.height
+ leftPadding: control.style.inputHorizontalPadding
+ rightPadding: control.style.inputHorizontalPadding + checkIndicator.width
+ + control.style.borderWidth
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
selectByMouse: true
clip: true
@@ -367,9 +369,9 @@ Item {
z: -1
width: textInput.width
height: textInput.height
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
}
MouseArea {
@@ -388,20 +390,20 @@ Item {
// Stop scrollable views from scrolling while ComboBox is in edit mode and the mouse
// pointer is on top of it. We might add wheel selection in the future.
onWheel: function(wheel) {
- wheel.accepted = root.edit
+ wheel.accepted = control.edit
}
}
onEditingFinished: {
- if (root.escapePressed) {
- root.escapePressed = false
- root.editText = textInput.preFocusText
+ if (control.escapePressed) {
+ control.escapePressed = false
+ control.editText = textInput.preFocusText
} else {
- if (root.currentInteraction === FilterComboBox.Interaction.TextEdit) {
- if (root.dirty)
- root.submitValue()
- } else if (root.currentInteraction === FilterComboBox.Interaction.Key) {
- root.selectItem(root.highlightedIndex)
+ if (control.currentInteraction === FilterComboBox.Interaction.TextEdit) {
+ if (control.dirty)
+ control.submitValue()
+ } else if (control.currentInteraction === FilterComboBox.Interaction.Key) {
+ control.selectItem(control.highlightedIndex)
}
}
@@ -409,16 +411,16 @@ Item {
}
onTextEdited: {
- root.currentInteraction = FilterComboBox.Interaction.TextEdit
- root.editing = true
+ control.currentInteraction = FilterComboBox.Interaction.TextEdit
+ control.editing = true
popupMouseArea.active = true
- root.dirty = true
+ control.dirty = true
if (textInput.text !== "")
- root.filter = textInput.text
+ control.filter = textInput.text
else {
- root.filter = ""
- root.autocompleteString = ""
+ control.filter = ""
+ control.autocompleteString = ""
}
if (!popup.visible)
@@ -426,12 +428,12 @@ Item {
sortFilterModel.update()
- if (!root.allowUserInput)
- root.updateHighlightedIndex()
+ if (!control.allowUserInput)
+ control.updateHighlightedIndex()
else
- root.setHighlightedIndexVisible(-1)
+ control.setHighlightedIndexVisible(-1)
- root.updateAutocomplete()
+ control.updateAutocomplete()
}
onActiveFocusChanged: {
@@ -445,12 +447,12 @@ Item {
states: [
State {
name: "default"
- when: root.enabled && !textInput.edit && !root.hover && !root.open
- && !root.hasActiveDrag
+ when: control.enabled && !textInput.edit && !control.hover && !control.open
+ && !control.hasActiveDrag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
target: textInputMouseArea
@@ -460,44 +462,44 @@ Item {
},
State {
name: "acceptsDrag"
- when: root.enabled && root.hasActiveDrag && !root.hasActiveHoverDrag
+ when: control.enabled && control.hasActiveDrag && !control.hasActiveHoverDrag
PropertyChanges {
target: textInputBackground
- border.color: StudioTheme.Values.themeInteraction
+ border.color: control.style.interaction
}
},
State {
name: "dragHover"
- when: root.enabled && root.hasActiveHoverDrag
+ when: control.enabled && control.hasActiveHoverDrag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeInteraction
+ color: control.style.background.interaction
+ border.color: control.style.interaction
}
},
State {
name: "globalHover"
- when: root.hover && !textInput.hover && !textInput.edit && !root.open
+ when: control.hover && !textInput.hover && !textInput.edit && !control.open
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: textInput.hover && root.hover && !textInput.edit
+ when: textInput.hover && control.hover && !textInput.edit
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundHover
+ color: control.style.background.hover
}
},
State {
name: "edit"
- when: root.edit
+ when: control.edit
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
PropertyChanges {
target: textInputMouseArea
@@ -507,23 +509,23 @@ Item {
},
State {
name: "disable"
- when: !root.enabled
+ when: !control.enabled
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
PropertyChanges {
target: textInput
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.text.disabled
}
}
]
Text {
id: tmpSelectionName
- visible: root.autocompleteString !== "" && root.open
- text: root.autocompleteString
+ visible: control.autocompleteString !== "" && control.open
+ text: control.autocompleteString
x: textInput.leftPadding + textMetrics.advanceWidth
y: (textInput.height - Math.ceil(tmpSelectionTextMetrics.height)) / 2
color: "gray" // TODO proper color value
@@ -542,7 +544,6 @@ Item {
}
}
-
Rectangle {
id: checkIndicator
@@ -550,11 +551,11 @@ Item {
property bool pressed: checkIndicatorMouseArea.containsPress
property bool checked: popup.visible
- x: textInput.width - checkIndicator.width - StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: StudioTheme.Values.height - StudioTheme.Values.border
- height: textInput.height - (StudioTheme.Values.border * 2)
- color: StudioTheme.Values.themeControlBackground
+ x: textInput.width - checkIndicator.width - control.style.borderWidth
+ y: control.style.borderWidth
+ width: control.style.squareControlSize.width - control.style.borderWidth
+ height: textInput.height - (control.style.borderWidth * 2)
+ color: control.style.background.idle
border.width: 0
MouseArea {
@@ -577,51 +578,51 @@ Item {
T.Label {
id: checkIndicatorIcon
anchors.fill: parent
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
text: StudioTheme.Constants.upDownSquare2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
- font.pixelSize: StudioTheme.Values.sliderControlSizeMulti
+ font.pixelSize: control.style.baseIconFontSize
font.family: StudioTheme.Constants.iconFont.family
}
states: [
State {
name: "default"
- when: root.enabled && checkIndicator.enabled && !root.edit
- && !checkIndicator.hover && !root.hover
- && !checkIndicator.checked && !root.hasActiveHoverDrag
+ when: control.enabled && checkIndicator.enabled && !control.edit
+ && !checkIndicator.hover && !control.hover
+ && !checkIndicator.checked && !control.hasActiveHoverDrag
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
}
},
State {
name: "dragHover"
- when: root.enabled && root.hasActiveHoverDrag
+ when: control.enabled && control.hasActiveHoverDrag
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ color: control.style.background.interaction
}
},
State {
name: "globalHover"
- when: root.enabled && checkIndicator.enabled
- && !checkIndicator.hover && root.hover && !root.edit
+ when: control.enabled && checkIndicator.enabled
+ && !checkIndicator.hover && control.hover && !control.edit
&& !checkIndicator.checked
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: root.enabled && checkIndicator.enabled
- && checkIndicator.hover && root.hover && !checkIndicator.pressed
+ when: control.enabled && checkIndicator.enabled
+ && checkIndicator.hover && control.hover && !checkIndicator.pressed
&& !checkIndicator.checked
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundHover
+ color: control.style.background.hover
}
},
State {
@@ -629,36 +630,36 @@ Item {
when: checkIndicator.checked
PropertyChanges {
target: checkIndicatorIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "press"
- when: root.enabled && checkIndicator.enabled
+ when: control.enabled && checkIndicator.enabled
&& checkIndicator.pressed
PropertyChanges {
target: checkIndicatorIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "disable"
- when: !root.enabled
+ when: !control.enabled
PropertyChanges {
target: checkIndicator
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ color: control.style.background.disabled
}
PropertyChanges {
target: checkIndicatorIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.icon.disabled
}
}
]
@@ -668,13 +669,13 @@ Item {
T.Popup {
id: popup
- x: textInput.x + StudioTheme.Values.border
+ x: textInput.x + control.style.borderWidth
y: textInput.height
- width: textInput.width - (StudioTheme.Values.border * 2)
+ width: textInput.width - (control.style.borderWidth * 2)
height: Math.min(popup.contentItem.implicitHeight + popup.topPadding + popup.bottomPadding,
- root.Window.height - popup.topMargin - popup.bottomMargin,
- StudioTheme.Values.maxComboBoxPopupHeight)
- padding: StudioTheme.Values.border
+ control.Window.height - popup.topMargin - popup.bottomMargin,
+ control.style.maxComboBoxPopupHeight)
+ padding: control.style.borderWidth
margins: 0 // If not defined margin will be -1
closePolicy: T.Popup.NoAutoClose
@@ -700,20 +701,20 @@ Item {
}
background: Rectangle {
- color: StudioTheme.Values.themePopupBackground
+ color: control.style.popup.background
border.width: 0
}
onOpened: {
// Reset the highlightedIndex of ListView as binding with condition didn't work
- if (root.highlightedIndex !== -1)
- root.setHighlightedIndexItems(root.highlightedIndex)
+ if (control.highlightedIndex !== -1)
+ control.setHighlightedIndexItems(control.highlightedIndex)
}
onAboutToShow: {
// Select first item in list
- if (root.highlightedIndex === -1 && sortFilterModel.count && !root.allowUserInput)
- root.setHighlightedIndexVisible(0)
+ if (control.highlightedIndex === -1 && sortFilterModel.count && !control.allowUserInput)
+ control.setHighlightedIndexVisible(0)
}
MouseArea {
@@ -734,8 +735,8 @@ Item {
if (!sortFilterModel.visibleGroup.count)
return
- root.currentInteraction = FilterComboBox.Interaction.Key
- root.increaseVisibleIndex()
+ control.currentInteraction = FilterComboBox.Interaction.Key
+ control.increaseVisibleIndex()
popupMouseArea.active = true
}
@@ -744,22 +745,21 @@ Item {
if (!sortFilterModel.visibleGroup.count)
return
- root.currentInteraction = FilterComboBox.Interaction.Key
- root.decreaseVisibleIndex()
+ control.currentInteraction = FilterComboBox.Interaction.Key
+ control.decreaseVisibleIndex()
popupMouseArea.active = true
}
Keys.onEscapePressed: {
- root.escapePressed = true
- root.finishEditing()
+ control.escapePressed = true
+ control.finishEditing()
}
Component.onCompleted: {
- let index = root.find(root.editText)
- root.currentIndex = index
- root.highlightedIndex = index // TODO might not be intended
-
- root.__isCompleted = true
+ let index = control.find(control.editText)
+ control.currentIndex = index
+ control.highlightedIndex = index // TODO might not be intended
+ control.__isCompleted = true
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Indicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Indicator.qml
index cc5cf43b86..3ffab90ac9 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Indicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Indicator.qml
@@ -1,41 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0
-import QtQuick 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Item {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property alias icon: icon
property bool hover: mouseArea.containsMouse
property bool pressed: mouseArea.pressed
- implicitWidth: StudioTheme.Values.height
- implicitHeight: StudioTheme.Values.height
+ implicitWidth: control.style.squareControlSize.width
+ implicitHeight: control.style.squareControlSize.height
signal clicked
z: 10
@@ -44,16 +24,16 @@ Item {
id: icon
anchors.fill: parent
text: StudioTheme.Constants.actionIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
states: [
State {
name: "hover"
- when: root.hover && !root.pressed && root.enabled
+ when: control.hover && !control.pressed && control.enabled
PropertyChanges {
target: icon
scale: 1.2
@@ -62,10 +42,10 @@ Item {
},
State {
name: "disable"
- when: !root.enabled
+ when: !control.enabled
PropertyChanges {
target: icon
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.icon.disabled
}
}
]
@@ -75,6 +55,6 @@ Item {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
- onClicked: root.clicked()
+ onClicked: control.clicked()
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/InfinityLoopIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/InfinityLoopIndicator.qml
index f31ac454b9..d6346788f8 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/InfinityLoopIndicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/InfinityLoopIndicator.qml
@@ -1,33 +1,35 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: infinityLoopIndicator
+ id: control
- property Item myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property Item __parentControl
property bool infinite: false
color: "transparent"
border.color: "transparent"
- implicitWidth: StudioTheme.Values.infinityControlWidth
- implicitHeight: StudioTheme.Values.infinityControlHeight
+ implicitWidth: control.style.indicatorIconSize.width
+ implicitHeight: control.style.indicatorIconSize.height
z: 10
T.Label {
- id: infinityLoopIndicatorIcon
+ id: icon
anchors.fill: parent
text: StudioTheme.Constants.infinity
visible: true
- color: StudioTheme.Values.themeTextColor
+ color: control.style.indicator.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -36,32 +38,32 @@ Rectangle {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
- onClicked: infinityLoopIndicator.infinite = !infinityLoopIndicator.infinite
+ onClicked: control.infinite = !control.infinite
}
states: [
State {
name: "active"
- when: infinityLoopIndicator.infinite && !mouseArea.containsMouse
+ when: control.infinite && !mouseArea.containsMouse
PropertyChanges {
- target: infinityLoopIndicatorIcon
- color: StudioTheme.Values.themeInfiniteLoopIndicatorColorInteraction
+ target: icon
+ color: control.style.indicator.interaction
}
},
State {
name: "default"
when: !mouseArea.containsMouse
PropertyChanges {
- target: infinityLoopIndicatorIcon
- color: StudioTheme.Values.themeInfiniteLoopIndicatorColor
+ target: icon
+ color: control.style.indicator.idle
}
},
State {
name: "hover"
when: mouseArea.containsMouse
PropertyChanges {
- target: infinityLoopIndicatorIcon
- color: StudioTheme.Values.themeInfiniteLoopIndicatorColorHover
+ target: icon
+ color: control.style.indicator.hover
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ItemDelegate.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ItemDelegate.qml
index 78211d73dd..f0d33d4b99 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ItemDelegate.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ItemDelegate.qml
@@ -1,8 +1,8 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
T.ItemDelegate {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator2D.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator2D.qml
index bfcd710b32..b0e6e52bd6 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator2D.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator2D.qml
@@ -1,34 +1,36 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: linkIndicator
+ id: control
- property Item myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property Item __parentControl
property bool linked: false
color: "transparent"
border.color: "transparent"
- implicitWidth: StudioTheme.Values.linkControlWidth
- implicitHeight: StudioTheme.Values.linkControlHeight
+ implicitWidth: control.style.indicatorIconSize.width
+ implicitHeight: control.style.indicatorIconSize.height
z: 10
T.Label {
- id: linkIndicatorIcon
+ id: icon
anchors.fill: parent
- text: linkIndicator.linked ? StudioTheme.Constants.linked
- : StudioTheme.Constants.unLinked
+ text: control.linked ? StudioTheme.Constants.linked
+ : StudioTheme.Constants.unLinked
visible: true
- color: StudioTheme.Values.themeTextColor
+ color: control.style.indicator.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -37,7 +39,7 @@ Rectangle {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
- onPressed: linkIndicator.linked = !linkIndicator.linked
+ onPressed: control.linked = !control.linked
}
states: [
@@ -45,16 +47,16 @@ Rectangle {
name: "default"
when: !mouseArea.containsMouse
PropertyChanges {
- target: linkIndicatorIcon
- color: StudioTheme.Values.themeLinkIndicatorColor
+ target: icon
+ color: control.style.indicator.idle
}
},
State {
name: "hover"
when: mouseArea.containsMouse
PropertyChanges {
- target: linkIndicatorIcon
- color: StudioTheme.Values.themeLinkIndicatorColorHover
+ target: icon
+ color: control.style.indicator.hover
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3D.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3D.qml
index 8d590a5315..8a483a04c3 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3D.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3D.qml
@@ -1,23 +1,25 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Shapes 1.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: linkIndicator
+ id: control
- property Item myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property Item __parentControl
property bool linked: linkXZ.linked || linkYZ.linked || linkXY.linked
color: "transparent"
border.color: "transparent"
- implicitWidth: StudioTheme.Values.height
- implicitHeight: StudioTheme.Values.height
+ implicitWidth: control.style.squareControlSize.width
+ implicitHeight: control.style.squareControlSize.height
z: 10
@@ -32,12 +34,12 @@ Rectangle {
T.Label {
id: linkIndicatorIcon
anchors.fill: parent
- text: linkIndicator.linked ? StudioTheme.Constants.linked
- : StudioTheme.Constants.unLinked
+ text: control.linked ? StudioTheme.Constants.linked
+ : StudioTheme.Constants.unLinked
visible: true
- color: StudioTheme.Values.themeTextColor
+ color: control.style.indicator.idle
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -56,10 +58,10 @@ Rectangle {
y: 0
// TODO proper size
- width: 20 + (3 * StudioTheme.Values.height)
- height: 20 + (3 * StudioTheme.Values.height)
+ width: 20 + (3 * control.style.squareControlSize.width)
+ height: 20 + (3 * control.style.squareControlSize.height)
- padding: StudioTheme.Values.border
+ padding: control.style.borderWidth
margins: 0 // If not defined margin will be -1
closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent
@@ -99,7 +101,7 @@ Rectangle {
property vector2d center: Qt.vector2d(triangle.radius, triangle.radius)
- strokeWidth: StudioTheme.Values.border
+ strokeWidth: control.style.borderWidth
strokeColor: triangleMouseArea.containsMouse ? "white" : "gray"
strokeStyle: ShapePath.SolidLine
fillColor: "transparent"
@@ -149,9 +151,9 @@ Rectangle {
onClicked: {
if (linkXZ.linked && linkYZ.linked && linkXY.linked)
- linkIndicator.unlinkAll()
+ control.unlinkAll()
else
- linkIndicator.linkAll()
+ control.linkAll()
}
MouseArea {
@@ -177,13 +179,14 @@ Rectangle {
visible: true
color: triangleMouseArea.containsMouse ? "white" : "gray"
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize * 3
+ font.pixelSize: control.style.baseIconFontSize * 3
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
LinkIndicator3DComponent {
id: linkXZ
+ style: control.style
pointA: path.pX
pointB: path.pZ
rotation: 105 // 60
@@ -191,6 +194,7 @@ Rectangle {
LinkIndicator3DComponent {
id: linkYZ
+ style: control.style
pointA: path.pZ
pointB: path.pY
rotation: 45 // -180
@@ -198,6 +202,7 @@ Rectangle {
LinkIndicator3DComponent {
id: linkXY
+ style: control.style
pointA: path.pY
pointB: path.pX
rotation: -15 // -60
@@ -211,7 +216,7 @@ Rectangle {
color: StudioTheme.Values.theme3DAxisXColor
font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -223,7 +228,7 @@ Rectangle {
color: StudioTheme.Values.theme3DAxisYColor
font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -235,7 +240,7 @@ Rectangle {
color: StudioTheme.Values.theme3DAxisZColor
font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -244,9 +249,9 @@ Rectangle {
}
background: Rectangle {
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeInteraction
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.interaction
+ border.width: control.style.borderWidth
}
enter: Transition {}
@@ -259,7 +264,7 @@ Rectangle {
when: !mouseArea.containsMouse && !linkPopup.opened
PropertyChanges {
target: linkIndicatorIcon
- color: StudioTheme.Values.themeLinkIndicatorColor
+ color: control.style.indicator.idle
}
},
State {
@@ -267,7 +272,7 @@ Rectangle {
when: mouseArea.containsMouse && !linkPopup.opened
PropertyChanges {
target: linkIndicatorIcon
- color: StudioTheme.Values.themeLinkIndicatorColorHover
+ color: control.style.indicator.hover
}
},
State {
@@ -275,7 +280,7 @@ Rectangle {
when: linkPopup.opened
PropertyChanges {
target: linkIndicatorIcon
- color: StudioTheme.Values.themeLinkIndicatorColorInteraction
+ color: control.style.indicator.interaction
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3DComponent.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3DComponent.qml
index 5347ad4e39..c6f3df59e1 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3DComponent.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/LinkIndicator3DComponent.qml
@@ -1,12 +1,14 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property var pointA: Qt.vector2d()
property var pointB: Qt.vector2d()
@@ -14,14 +16,14 @@ Rectangle {
property bool linked: false
property var middle: {
- var ab = root.pointB.minus(root.pointA) // B - A
- return root.pointA.plus(ab.normalized().times(ab.length() * 0.5))
+ var ab = control.pointB.minus(control.pointA) // B - A
+ return control.pointA.plus(ab.normalized().times(ab.length() * 0.5))
}
property var position: {
// Calculate the middle point between A and B
- var ab = root.pointB.minus(root.pointA) // B - A
- var midAB = root.pointA.plus(ab.normalized().times(ab.length() * 0.5))
+ var ab = control.pointB.minus(control.pointA) // B - A
+ var midAB = control.pointA.plus(ab.normalized().times(ab.length() * 0.5))
var perpendicularAB = Qt.vector2d(ab.y, -ab.x)
return midAB.plus(perpendicularAB.normalized().times(8.0 * StudioTheme.Values.scaleFactor))
}
@@ -29,23 +31,23 @@ Rectangle {
color: "transparent"
border.color: "transparent"
- x: root.position.x - (StudioTheme.Values.height * 0.5)
- y: root.position.y - (StudioTheme.Values.height * 0.5)
+ x: control.position.x - (control.style.squareControlSize.width * 0.5)
+ y: control.position.y - (control.style.squareControlSize.height * 0.5)
- implicitWidth: StudioTheme.Values.height
- implicitHeight: StudioTheme.Values.height
+ implicitWidth: control.style.squareControlSize.width
+ implicitHeight: control.style.squareControlSize.height
transformOrigin: Item.Center
T.Label {
id: icon
anchors.fill: parent
- text: root.linked ? StudioTheme.Constants.linked
- : StudioTheme.Constants.unLinked
+ text: control.linked ? StudioTheme.Constants.linked
+ : StudioTheme.Constants.unLinked
visible: true
color: "grey"
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -55,7 +57,7 @@ Rectangle {
anchors.fill: parent
anchors.margins: 4.0 * StudioTheme.Values.scaleFactor
hoverEnabled: true
- onPressed: root.linked = !root.linked
+ onPressed: control.linked = !control.linked
}
states: [
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml
index 65d3b0cc75..f5aef1ca17 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Menu.qml
@@ -1,21 +1,23 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Window 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Window
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Menu {
id: control
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
margins: 0
overlap: 1
@@ -25,7 +27,7 @@ T.Menu {
| T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside
| T.Popup.CloseOnReleaseOutsideParent
- delegate: MenuItem {}
+ delegate: MenuItem { style: control.style }
contentItem: ListView {
model: control.contentModel
@@ -37,9 +39,10 @@ T.Menu {
background: Rectangle {
implicitWidth: contentItem.childrenRect.width
implicitHeight: contentItem.childrenRect.height
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.popup.background
+ border.color: control.style.popup.background
+ border.width: control.style.borderWidth
+
MouseArea {
// This mouse area is here to eat clicks that are not handled by menu items
// to prevent them going through to the underlying view.
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml
index 92de52b44f..16a76c2e64 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml
@@ -1,15 +1,17 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.MenuItem {
id: control
- property int labelSpacing: StudioTheme.Values.contextMenuLabelSpacing
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property int labelSpacing: control.style.contextMenuLabelSpacing
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -19,7 +21,7 @@ T.MenuItem {
padding: 0
spacing: 0
- horizontalPadding: StudioTheme.Values.contextMenuHorizontalPadding
+ horizontalPadding: control.style.contextMenuHorizontalPadding
action: Action {}
contentItem: Item {
@@ -27,8 +29,9 @@ T.MenuItem {
id: textLabel
text: control.text
font: control.font
- color: control.enabled ? StudioTheme.Values.themeTextColor
- : StudioTheme.Values.themeTextColorDisabled
+ color: control.enabled ? control.highlighted ? control.style.text.selectedText
+ : control.style.text.idle
+ : control.style.text.disabled
anchors.verticalCenter: parent.verticalCenter
}
@@ -42,19 +45,19 @@ T.MenuItem {
Shortcut {
id: shortcut
- property int shortcutWorkaround: control.action.shortcut ? control.action.shortcut : 0
- sequence: shortcutWorkaround
+ property int shortcutWorkaround: control.action.shortcut ?? 0
+ sequence: shortcut.shortcutWorkaround
}
}
}
arrow: T.Label {
id: arrow
- x: parent.width - (StudioTheme.Values.height + arrow.width) / 2
+ x: parent.width - (control.style.controlSize.height + arrow.width) / 2
y: (parent.height - arrow.height) / 2
visible: control.subMenu
text: StudioTheme.Constants.startNode
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
font.pixelSize: 8
font.family: StudioTheme.Constants.iconFont.family
}
@@ -62,13 +65,11 @@ T.MenuItem {
background: Rectangle {
implicitWidth: textLabel.implicitWidth + control.labelSpacing + shortcutLabel.implicitWidth
+ control.leftPadding + control.rightPadding
- implicitHeight: StudioTheme.Values.height
- x: StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: control.menu.width - (StudioTheme.Values.border * 2)
- height: control.height - (StudioTheme.Values.border * 2)
- color: control.down ? control.palette.midlight
- : control.highlighted ? StudioTheme.Values.themeInteraction
- : "transparent"
+ implicitHeight: control.style.controlSize.height
+ x: control.style.borderWidth
+ y: control.style.borderWidth
+ width: (control.menu?.width ?? 0) - (control.style.borderWidth * 2)
+ height: control.height - (control.style.borderWidth * 2)
+ color: control.highlighted ? control.style.interaction : "transparent"
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItemWithIcon.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItemWithIcon.qml
index 14588ad556..e6bd35e8c4 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItemWithIcon.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItemWithIcon.qml
@@ -1,15 +1,17 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.MenuItem {
id: control
- property int labelSpacing: StudioTheme.Values.contextMenuLabelSpacing
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property int labelSpacing: control.style.contextMenuLabelSpacing
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -20,7 +22,7 @@ T.MenuItem {
padding: 0
spacing: 0
- horizontalPadding: StudioTheme.Values.contextMenuHorizontalPadding
+ horizontalPadding: control.style.contextMenuHorizontalPadding
action: Action {}
contentItem: Item {
@@ -28,18 +30,18 @@ T.MenuItem {
id: iconLabel
text: control.checked ? StudioTheme.Constants.tickIcon : ""
visible: true
- color: control.enabled ? StudioTheme.Values.themeTextColor : StudioTheme.Values.themeTextColorDisabled
+ color: control.enabled ? control.highlighted ? control.style.text.selectedText : control.style.text.idle : control.style.text.disabled
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
anchors.verticalCenter: parent.verticalCenter
}
Text {
id: textLabel
- x: StudioTheme.Values.height
+ x: control.style.squareControlSize.width
text: control.text
font: control.font
- color: control.enabled ? StudioTheme.Values.themeTextColor : StudioTheme.Values.themeTextColorDisabled
+ color: control.enabled ? control.highlighted ? control.style.text.selectedText : control.style.text.idle : control.style.text.disabled
anchors.verticalCenter: parent.verticalCenter
}
}
@@ -47,11 +49,11 @@ T.MenuItem {
background: Rectangle {
implicitWidth: iconLabel.implicitWidth + textLabel.implicitWidth + control.labelSpacing
+ control.leftPadding + control.rightPadding
- implicitHeight: StudioTheme.Values.height
- x: StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: control.menu.width - (StudioTheme.Values.border * 2)
- height: control.height - (StudioTheme.Values.border * 2)
- color: control.down ? control.palette.midlight : control.highlighted ? StudioTheme.Values.themeInteraction : "transparent"
+ implicitHeight: control.style.controlSize.height
+ x: control.style.borderWidth
+ y: control.style.borderWidth
+ width: control.menu.width - (control.style.borderWidth * 2)
+ height: control.height - (control.style.borderWidth * 2)
+ color: control.highlighted ? control.style.interaction : "transparent"
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml
index 47ad358b4f..b1c662a65c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuSeparator.qml
@@ -1,13 +1,15 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.MenuSeparator {
id: control
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@@ -16,7 +18,7 @@ T.MenuSeparator {
contentItem: Rectangle {
implicitWidth: control.parent.width
- implicitHeight: StudioTheme.Values.border
- color: StudioTheme.Values.themeControlOutline
+ implicitHeight: control.style.borderWidth
+ color: control.style.border.idle
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ProgressBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ProgressBar.qml
index 01e2a3a1c5..6e98d60fc9 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ProgressBar.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ProgressBar.qml
@@ -1,27 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Templates as T
@@ -30,6 +8,8 @@ import StudioTheme 1.0 as StudioTheme
T.ProgressBar {
id: control
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
@@ -42,13 +22,13 @@ T.ProgressBar {
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
}
background: Rectangle {
implicitWidth: 200
implicitHeight: 6
- color: StudioTheme.Values.themeThumbnailLabelBackground
+ color: control.style.thumbnailLabelBackground
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RadioButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RadioButton.qml
index 04914f1590..d1744ab5da 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RadioButton.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RadioButton.qml
@@ -1,22 +1,24 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.RadioButton {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property alias actionIndicator: actionIndicator
// This property is used to indicate the global hover state
- property bool hover: root.hovered && root.enabled
+ property bool hover: control.hovered && control.enabled
property bool edit: false
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
property alias labelVisible: radioButtonLabel.visible
property alias labelColor: radioButtonLabel.color
@@ -24,7 +26,7 @@ T.RadioButton {
property alias fontFamily: radioButtonLabel.font.family
property alias fontPixelSize: radioButtonLabel.font.pixelSize
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -32,119 +34,120 @@ T.RadioButton {
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
- spacing: StudioTheme.Values.radioButtonSpacing
+ spacing: control.style.controlSpacing
hoverEnabled: true
activeFocusOnTab: false
ActionIndicator {
id: actionIndicator
- myControl: root
- width: actionIndicator.visible ? root.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? root.__actionIndicatorHeight : 0
+ style: control.style
+ __parentControl: control
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
indicator: Rectangle {
id: radioButtonBackground
- implicitWidth: StudioTheme.Values.radioButtonWidth
- implicitHeight: StudioTheme.Values.radioButtonHeight
+ implicitWidth: control.style.squareControlSize.width
+ implicitHeight: control.style.squareControlSize.height
x: actionIndicator.width
y: 0
z: 5
radius: width / 2
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
Rectangle {
id: radioButtonIndicator
x: (parent.width - width) / 2
y: (parent.height - height) / 2
- width: StudioTheme.Values.radioButtonIndicatorWidth
- height: StudioTheme.Values.radioButtonIndicatorHeight
+ width: control.style.radioButtonIndicatorSize.width
+ height: control.style.radioButtonIndicatorSize.height
radius: width / 2
- color: StudioTheme.Values.themeInteraction
- visible: root.checked
+ color: control.style.interaction
+ visible: control.checked
}
}
contentItem: T.Label {
id: radioButtonLabel
- leftPadding: radioButtonBackground.x + radioButtonBackground.width + root.spacing
+ leftPadding: radioButtonBackground.x + radioButtonBackground.width + control.spacing
rightPadding: 0
verticalAlignment: Text.AlignVCenter
- text: root.text
- font: root.font
- color: StudioTheme.Values.themeTextColor
+ text: control.text
+ font: control.font
+ color: control.style.text.idle
visible: text !== ""
}
states: [
State {
name: "default"
- when: root.enabled && !root.hover && !root.pressed && !actionIndicator.hover
+ when: control.enabled && !control.hover && !control.pressed && !actionIndicator.hover
PropertyChanges {
target: radioButtonBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
target: radioButtonIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "globalHover"
- when: actionIndicator.hover && !root.pressed && root.enabled
+ when: actionIndicator.hover && !control.pressed && control.enabled
PropertyChanges {
target: radioButtonBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.globalHover
+ border.color: control.style.border.idle
}
PropertyChanges {
target: radioButtonIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "hover"
- when: root.hover && !actionIndicator.hover && !root.pressed
+ when: control.hover && !actionIndicator.hover && !control.pressed
PropertyChanges {
target: radioButtonBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.hover
+ border.color: control.style.border.hover
}
PropertyChanges {
target: radioButtonIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "press"
- when: root.hover && root.pressed
+ when: control.hover && control.pressed
PropertyChanges {
target: radioButtonBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
PropertyChanges {
target: radioButtonIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "disable"
- when: !root.enabled
+ when: !control.enabled
PropertyChanges {
target: radioButtonBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
PropertyChanges {
target: radioButtonIndicator
- color: StudioTheme.Values.themeIconColorDisabled
+ color: control.style.icon.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSliderPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSliderPopup.qml
index 60b20b4238..38ea960b88 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSliderPopup.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSliderPopup.qml
@@ -1,14 +1,16 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Popup {
- id: sliderPopup
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property T.Control __parentControl
property bool drag: slider.pressed
@@ -18,7 +20,7 @@ T.Popup {
| T.Popup.CloseOnReleaseOutsideParent
background: Rectangle {
- color: StudioTheme.Values.themePopupBackground
+ color: control.style.popup.background
border.width: 0
}
@@ -31,41 +33,41 @@ T.Popup {
rightPadding: 3
leftPadding: 3
- from: myControl.realFrom
- value: myControl.realValue
- to: myControl.realTo
+ from: control.__parentControl.realFrom
+ value: control.__parentControl.realValue
+ to: control.__parentControl.realTo
focusPolicy: Qt.NoFocus
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: slider.topPadding + (slider.availableHeight / 2) - (height / 2)
- width: StudioTheme.Values.sliderHandleWidth
- height: StudioTheme.Values.sliderHandleHeight
+ width: control.style.sliderHandleSize.width
+ height: control.style.sliderHandleSize.height
radius: 0
- color: slider.pressed ? StudioTheme.Values.themeSliderHandleInteraction
- : StudioTheme.Values.themeSliderHandle
+ color: slider.pressed ? control.style.slider.handleInteraction
+ : control.style.slider.handle
}
background: Rectangle {
x: slider.leftPadding
y: slider.topPadding + (slider.availableHeight / 2) - (height / 2)
width: slider.availableWidth
- height: StudioTheme.Values.sliderTrackHeight
+ height: control.style.sliderTrackHeight
radius: 0
- color: StudioTheme.Values.themeSliderInactiveTrack
+ color: control.style.slider.inactiveTrack
Rectangle {
width: slider.visualPosition * parent.width
height: parent.height
- color: StudioTheme.Values.themeSliderActiveTrack
+ color: control.style.slider.activeTrack
radius: 0
}
}
onMoved: {
- myControl.realValue = slider.value
- myControl.realValueModified()
+ control.__parentControl.realValue = slider.value
+ control.__parentControl.realValueModified()
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml
index dfdd08742a..aba99f75ff 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml
@@ -1,12 +1,14 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.SpinBox {
- id: mySpinBox
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property real realFrom: 0.0
property real realTo: 99.0
@@ -19,38 +21,38 @@ T.SpinBox {
property int decimals: 0
property real minStepSize: {
- var tmpMinStepSize = Number((mySpinBox.realStepSize * 0.1).toFixed(mySpinBox.decimals))
- return (tmpMinStepSize) ? tmpMinStepSize : mySpinBox.realStepSize
+ var tmpMinStepSize = Number((control.realStepSize * 0.1).toFixed(control.decimals))
+ return (tmpMinStepSize) ? tmpMinStepSize : control.realStepSize
}
property real maxStepSize: {
- var tmpMaxStepSize = Number((mySpinBox.realStepSize * 10.0).toFixed(mySpinBox.decimals))
- return (tmpMaxStepSize < mySpinBox.realTo) ? tmpMaxStepSize : mySpinBox.realStepSize
+ var tmpMaxStepSize = Number((control.realStepSize * 10.0).toFixed(control.decimals))
+ return (tmpMaxStepSize < control.realTo) ? tmpMaxStepSize : control.realStepSize
}
property bool edit: spinBoxInput.activeFocus
// This property is used to indicate the global hover state
property bool hover: (spinBoxInput.hover || actionIndicator.hover || spinBoxIndicatorUp.hover
|| spinBoxIndicatorDown.hover || sliderIndicator.hover)
- && mySpinBox.enabled
+ && control.enabled
property bool drag: false
property bool sliderDrag: sliderPopup.drag
property bool dirty: false // user modification flag
// TODO Not used anymore. Will be removed when all dependencies were removed.
- property real realDragRange: mySpinBox.realTo - mySpinBox.realFrom
+ property real realDragRange: control.realTo - control.realFrom
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
property bool spinBoxIndicatorVisible: true
- property real __spinBoxIndicatorWidth: StudioTheme.Values.spinBoxIndicatorWidth
- property real __spinBoxIndicatorHeight: StudioTheme.Values.spinBoxIndicatorHeight
+ property real __spinBoxIndicatorWidth: control.style.spinBoxIndicatorSize.width
+ property real __spinBoxIndicatorHeight: control.style.spinBoxIndicatorSize.height
property alias sliderIndicatorVisible: sliderIndicator.visible
- property real __sliderIndicatorWidth: StudioTheme.Values.sliderIndicatorWidth
- property real __sliderIndicatorHeight: StudioTheme.Values.sliderIndicatorHeight
+ property real __sliderIndicatorWidth: control.style.squareControlSize.width
+ property real __sliderIndicatorHeight: control.style.squareControlSize.height
property alias __devicePixelRatio: spinBoxInput.devicePixelRatio
property alias pixelsPerUnit: spinBoxInput.pixelsPerUnit
@@ -71,13 +73,13 @@ T.SpinBox {
wheelEnabled: false
hoverEnabled: true
- width: StudioTheme.Values.defaultControlWidth
- height: StudioTheme.Values.defaultControlHeight
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
leftPadding: spinBoxIndicatorDown.x + spinBoxIndicatorDown.width
- rightPadding: sliderIndicator.width + StudioTheme.Values.border
+ rightPadding: sliderIndicator.width + control.style.borderWidth
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
editable: true
// Leave this in for now
@@ -87,107 +89,113 @@ T.SpinBox {
validator: DoubleValidator {
id: doubleValidator
- locale: mySpinBox.locale.name
+ locale: control.locale.name
notation: DoubleValidator.StandardNotation
- decimals: mySpinBox.decimals
- bottom: Math.min(mySpinBox.realFrom, mySpinBox.realTo)
- top: Math.max(mySpinBox.realFrom, mySpinBox.realTo)
+ decimals: control.decimals
+ bottom: Math.min(control.realFrom, control.realTo)
+ top: Math.max(control.realFrom, control.realTo)
}
ActionIndicator {
id: actionIndicator
- myControl: mySpinBox
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- width: actionIndicator.visible ? mySpinBox.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? mySpinBox.__actionIndicatorHeight : 0
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
up.indicator: RealSpinBoxIndicator {
id: spinBoxIndicatorUp
- myControl: mySpinBox
+ style: control.style
+ __parentControl: control
iconFlip: -1
- visible: mySpinBox.spinBoxIndicatorVisible
- onRealPressed: mySpinBox.indicatorPressed()
- onRealReleased: mySpinBox.realIncrease()
- onRealPressAndHold: mySpinBox.realIncrease()
- x: actionIndicator.width + StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorWidth : 0
- height: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorHeight : 0
-
- realEnabled: (mySpinBox.realFrom < mySpinBox.realTo) ? (mySpinBox.realValue < mySpinBox.realTo)
- : (mySpinBox.realValue > mySpinBox.realTo)
+ visible: control.spinBoxIndicatorVisible
+ onRealPressed: control.indicatorPressed()
+ onRealReleased: control.realIncrease()
+ onRealPressAndHold: control.realIncrease()
+ x: actionIndicator.width + control.style.borderWidth
+ y: control.style.borderWidth
+ width: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorWidth : 0
+ height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0
+
+ realEnabled: (control.realFrom < control.realTo) ? (control.realValue < control.realTo)
+ : (control.realValue > control.realTo)
}
down.indicator: RealSpinBoxIndicator {
id: spinBoxIndicatorDown
- myControl: mySpinBox
- visible: mySpinBox.spinBoxIndicatorVisible
- onRealPressed: mySpinBox.indicatorPressed()
- onRealReleased: mySpinBox.realDecrease()
- onRealPressAndHold: mySpinBox.realDecrease()
- x: actionIndicator.width + StudioTheme.Values.border
+ style: control.style
+ __parentControl: control
+ visible: control.spinBoxIndicatorVisible
+ onRealPressed: control.indicatorPressed()
+ onRealReleased: control.realDecrease()
+ onRealPressAndHold: control.realDecrease()
+ x: actionIndicator.width + control.style.borderWidth
y: spinBoxIndicatorUp.y + spinBoxIndicatorUp.height
- width: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorWidth : 0
- height: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorHeight : 0
+ width: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorWidth : 0
+ height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0
- realEnabled: (mySpinBox.realFrom < mySpinBox.realTo) ? (mySpinBox.realValue > mySpinBox.realFrom)
- : (mySpinBox.realValue < mySpinBox.realFrom)
+ realEnabled: (control.realFrom < control.realTo) ? (control.realValue > control.realFrom)
+ : (control.realValue < control.realFrom)
}
contentItem: RealSpinBoxInput {
id: spinBoxInput
- myControl: mySpinBox
+ style: control.style
+ __parentControl: control
validator: doubleValidator
function handleEditingFinished() {
- mySpinBox.focus = false
+ control.focus = false
// Keep the dirty state before calling setValueFromInput(),
// it will be set to false (cleared) internally
- var valueModified = mySpinBox.dirty
+ var valueModified = control.dirty
- mySpinBox.setValueFromInput()
+ control.setValueFromInput()
myTimer.stop()
// Only trigger the signal, if the value was modified
if (valueModified)
- mySpinBox.compressedRealValueModified()
+ control.compressedRealValueModified()
}
onEditingFinished: spinBoxInput.handleEditingFinished()
- onTextEdited: mySpinBox.dirty = true
+ onTextEdited: control.dirty = true
}
background: Rectangle {
id: spinBoxBackground
- color: StudioTheme.Values.themeControlOutline
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
x: actionIndicator.width
- width: mySpinBox.width - actionIndicator.width
- height: mySpinBox.height
+ width: control.width - actionIndicator.width
+ height: control.height
}
CheckIndicator {
id: sliderIndicator
- myControl: mySpinBox
- myPopup: sliderPopup
+ style: control.style
+ __parentControl: control
+ __parentPopup: sliderPopup
x: spinBoxInput.x + spinBoxInput.width
- y: StudioTheme.Values.border
- width: sliderIndicator.visible ? mySpinBox.__sliderIndicatorWidth - StudioTheme.Values.border : 0
- height: sliderIndicator.visible ? mySpinBox.__sliderIndicatorHeight - (StudioTheme.Values.border * 2) : 0
+ y: control.style.borderWidth
+ width: sliderIndicator.visible ? control.__sliderIndicatorWidth - control.style.borderWidth : 0
+ height: sliderIndicator.visible ? control.__sliderIndicatorHeight - (control.style.borderWidth * 2) : 0
visible: false // reasonable default
}
RealSliderPopup {
id: sliderPopup
- myControl: mySpinBox
- x: actionIndicator.width + StudioTheme.Values.border
- y: StudioTheme.Values.height
- width: mySpinBox.width - actionIndicator.width - (StudioTheme.Values.border * 2)
- height: StudioTheme.Values.sliderHeight
+ style: control.style
+ __parentControl: control
+ x: actionIndicator.width + control.style.borderWidth
+ y: control.style.controlSize.height
+ width: control.width - actionIndicator.width - (control.style.borderWidth * 2)
+ height: control.style.smallControlSize.height
enter: Transition {}
exit: Transition {}
@@ -195,21 +203,21 @@ T.SpinBox {
textFromValue: function (value, locale) {
locale.numberOptions = Locale.OmitGroupSeparator
- return Number(mySpinBox.realValue).toLocaleString(locale, 'f', mySpinBox.decimals)
+ return Number(control.realValue).toLocaleString(locale, 'f', control.decimals)
}
valueFromText: function (text, locale) {
- mySpinBox.setRealValue(Number.fromLocaleString(locale, spinBoxInput.text))
+ control.setRealValue(Number.fromLocaleString(locale, spinBoxInput.text))
return 0
}
states: [
State {
name: "default"
- when: mySpinBox.enabled && !mySpinBox.hover && !mySpinBox.hovered
- && !mySpinBox.edit && !mySpinBox.drag && !mySpinBox.sliderDrag
+ when: control.enabled && !control.hover && !control.hovered
+ && !control.edit && !control.drag && !control.sliderDrag
PropertyChanges {
- target: mySpinBox
+ target: control
__wheelEnabled: false
}
PropertyChanges {
@@ -218,15 +226,23 @@ T.SpinBox {
}
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ border.color: control.style.border.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: control.enabled && control.hover && control.hovered
+ && !control.edit && !control.drag && !control.sliderDrag
+ PropertyChanges {
+ target: spinBoxBackground
+ border.color: control.style.border.hover
}
},
State {
name: "edit"
- when: mySpinBox.edit
+ when: control.edit
PropertyChanges {
- target: mySpinBox
+ target: control
__wheelEnabled: true
}
PropertyChanges {
@@ -235,26 +251,23 @@ T.SpinBox {
}
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutline
+ border.color: control.style.border.idle
}
},
State {
name: "drag"
- when: mySpinBox.drag || mySpinBox.sliderDrag
+ when: control.drag || control.sliderDrag
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ border.color: control.style.border.interaction
}
},
State {
name: "disable"
- when: !mySpinBox.enabled
+ when: !control.enabled
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlOutlineDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ border.color: control.style.border.disabled
}
}
]
@@ -264,32 +277,32 @@ T.SpinBox {
repeat: false
running: false
interval: 400
- onTriggered: mySpinBox.compressedRealValueModified()
+ onTriggered: control.compressedRealValueModified()
}
onRealValueChanged: {
- mySpinBox.setRealValue(mySpinBox.realValue) // sanitize and clamp realValue
- spinBoxInput.text = mySpinBox.textFromValue(mySpinBox.realValue, mySpinBox.locale)
- mySpinBox.value = 0 // Without setting value back to 0, it can happen that one of
+ control.setRealValue(control.realValue) // sanitize and clamp realValue
+ spinBoxInput.text = control.textFromValue(control.realValue, control.locale)
+ control.value = 0 // Without setting value back to 0, it can happen that one of
// the indicator will be disabled due to range logic.
}
onRealValueModified: myTimer.restart()
onFocusChanged: {
- if (mySpinBox.focus) {
- mySpinBox.dirty = false
+ if (control.focus) {
+ control.dirty = false
} else {
// Make sure displayed value is correct after focus loss, as onEditingFinished
// doesn't trigger when value is something validator doesn't accept.
- spinBoxInput.text = mySpinBox.textFromValue(mySpinBox.realValue, mySpinBox.locale)
+ spinBoxInput.text = control.textFromValue(control.realValue, control.locale)
- if (mySpinBox.dirty)
+ if (control.dirty)
spinBoxInput.handleEditingFinished()
}
}
- onDisplayTextChanged: spinBoxInput.text = mySpinBox.displayText
+ onDisplayTextChanged: spinBoxInput.text = control.displayText
onActiveFocusChanged: {
- if (mySpinBox.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason)
- mySpinBox.preFocusText = spinBoxInput.text
+ if (control.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason)
+ control.preFocusText = spinBoxInput.text
spinBoxInput.selectAll()
}
}
@@ -299,27 +312,27 @@ T.SpinBox {
event.accepted = true
// Store current step size
- var currStepSize = mySpinBox.realStepSize
+ var currStepSize = control.realStepSize
// Set realStepSize according to used modifier key
if (event.modifiers & Qt.ControlModifier)
- mySpinBox.realStepSize = mySpinBox.minStepSize
+ control.realStepSize = control.minStepSize
if (event.modifiers & Qt.ShiftModifier)
- mySpinBox.realStepSize = mySpinBox.maxStepSize
+ control.realStepSize = control.maxStepSize
if (event.key === Qt.Key_Up)
- mySpinBox.realIncrease()
+ control.realIncrease()
else
- mySpinBox.realDecrease()
+ control.realDecrease()
// Reset realStepSize
- mySpinBox.realStepSize = currStepSize
+ control.realStepSize = currStepSize
}
if (event.key === Qt.Key_Escape) {
- spinBoxInput.text = mySpinBox.preFocusText
- mySpinBox.dirty = true
+ spinBoxInput.text = control.preFocusText
+ control.dirty = true
spinBoxInput.handleEditingFinished()
}
}
@@ -329,60 +342,60 @@ T.SpinBox {
}
function setValueFromInput() {
- if (!mySpinBox.dirty)
+ if (!control.dirty)
return
// FIX: This is a temporary fix for QTBUG-74239
- var currValue = mySpinBox.realValue
+ var currValue = control.realValue
// Call the function but don't use return value. The realValue property
// will be implicitly set inside the function/procedure.
- mySpinBox.valueFromText(spinBoxInput.text, mySpinBox.locale)
+ control.valueFromText(spinBoxInput.text, control.locale)
- if (mySpinBox.realValue !== currValue) {
- mySpinBox.realValueModified()
+ if (control.realValue !== currValue) {
+ control.realValueModified()
} else {
// Check if input text differs in format from the current value
- var tmpInputValue = mySpinBox.textFromValue(mySpinBox.realValue, mySpinBox.locale)
+ var tmpInputValue = control.textFromValue(control.realValue, control.locale)
if (tmpInputValue !== spinBoxInput.text)
spinBoxInput.text = tmpInputValue
}
- mySpinBox.dirty = false
+ control.dirty = false
}
function setRealValue(value) {
if (Number.isNaN(value))
value = 0
- if (mySpinBox.decimals === 0)
+ if (control.decimals === 0)
value = Math.round(value)
- mySpinBox.realValue = mySpinBox.clamp(value,
- mySpinBox.validator.bottom,
- mySpinBox.validator.top)
+ control.realValue = control.clamp(value,
+ control.validator.bottom,
+ control.validator.top)
}
function realDecrease() {
// Store the current value for comparison
- var currValue = mySpinBox.realValue
- mySpinBox.valueFromText(spinBoxInput.text, mySpinBox.locale)
+ var currValue = control.realValue
+ control.valueFromText(spinBoxInput.text, control.locale)
- mySpinBox.setRealValue(mySpinBox.realValue - mySpinBox.realStepSize)
+ control.setRealValue(control.realValue - control.realStepSize)
- if (mySpinBox.realValue !== currValue)
- mySpinBox.realValueModified()
+ if (control.realValue !== currValue)
+ control.realValueModified()
}
function realIncrease() {
// Store the current value for comparison
- var currValue = mySpinBox.realValue
- mySpinBox.valueFromText(spinBoxInput.text, mySpinBox.locale)
+ var currValue = control.realValue
+ control.valueFromText(spinBoxInput.text, control.locale)
- mySpinBox.setRealValue(mySpinBox.realValue + mySpinBox.realStepSize)
+ control.setRealValue(control.realValue + control.realStepSize)
- if (mySpinBox.realValue !== currValue)
- mySpinBox.realValueModified()
+ if (control.realValue !== currValue)
+ control.realValueModified()
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml
index 8587709201..6bafb9c11e 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml
@@ -1,14 +1,16 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: spinBoxIndicator
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property T.Control __parentControl
property bool hover: spinBoxIndicatorMouseArea.containsMouse
property bool pressed: spinBoxIndicatorMouseArea.containsPress
@@ -21,13 +23,13 @@ Rectangle {
property alias iconFlip: spinBoxIndicatorIconScale.yScale
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
border.width: 0
- onEnabledChanged: syncEnabled()
+ onEnabledChanged: control.syncEnabled()
onRealEnabledChanged: {
- syncEnabled()
- if (spinBoxIndicator.realEnabled === false) {
+ control.syncEnabled()
+ if (control.realEnabled === false) {
pressAndHoldTimer.stop()
spinBoxIndicatorMouseArea.pressedAndHeld = false
}
@@ -36,7 +38,7 @@ Rectangle {
// This function is meant to synchronize enabled with realEnabled to avoid
// the internal logic messing with the actual state.
function syncEnabled() {
- spinBoxIndicator.enabled = spinBoxIndicator.realEnabled
+ control.enabled = control.realEnabled
}
Timer {
@@ -44,7 +46,7 @@ Rectangle {
repeat: true
running: false
interval: 100
- onTriggered: spinBoxIndicator.realPressAndHold()
+ onTriggered: control.realPressAndHold()
}
// This MouseArea is a workaround to avoid some hover state related bugs
@@ -58,26 +60,26 @@ Rectangle {
hoverEnabled: true
pressAndHoldInterval: 500
onPressed: function(mouse) {
- if (myControl.activeFocus)
- spinBoxIndicator.forceActiveFocus()
+ if (control.__parentControl.activeFocus)
+ control.forceActiveFocus()
- spinBoxIndicator.realPressed()
+ control.realPressed()
mouse.accepted = true
}
onPressAndHold: {
pressAndHoldTimer.restart()
- pressedAndHeld = true
+ spinBoxIndicatorMouseArea.pressedAndHeld = true
}
onReleased: function(mouse) {
// Only trigger real released when pressAndHold isn't active
if (!pressAndHoldTimer.running && containsMouse)
- spinBoxIndicator.realReleased()
+ control.realReleased()
pressAndHoldTimer.stop()
mouse.accepted = true
- pressedAndHeld = false
+ spinBoxIndicatorMouseArea.pressedAndHeld = false
}
onEntered: {
- if (pressedAndHeld)
+ if (spinBoxIndicatorMouseArea.pressedAndHeld)
pressAndHoldTimer.restart()
}
onExited: {
@@ -89,10 +91,10 @@ Rectangle {
T.Label {
id: spinBoxIndicatorIcon
text: StudioTheme.Constants.upDownSquare2
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
- font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti
+ font.pixelSize: control.style.smallIconFontSize
font.family: StudioTheme.Constants.iconFont.family
anchors.fill: parent
transform: Scale {
@@ -105,54 +107,57 @@ Rectangle {
states: [
State {
name: "default"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.edit
- && !spinBoxIndicator.hover && !myControl.hover && !myControl.drag
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.edit
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "globalHover"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && !spinBoxIndicator.hover && myControl.hover && !myControl.edit
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && control.__parentControl.hover && !control.__parentControl.edit
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "hover"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && spinBoxIndicator.hover && myControl.hover && !spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.hover
+ && control.__parentControl.hover && !control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeIconColorHover
+ color: control.style.icon.hover
}
},
State {
name: "press"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeIconColor
+ color: control.style.icon.idle
}
},
State {
name: "edit"
- when: myControl.edit && spinBoxIndicator.enabled
+ when: control.__parentControl.edit && control.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "disable"
- when: !myControl.enabled || !spinBoxIndicator.enabled
+ when: !control.__parentControl.enabled || !control.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.icon.disabled
}
}
]
@@ -161,102 +166,104 @@ Rectangle {
states: [
State {
name: "default"
- when: myControl.enabled && !myControl.edit
- && !spinBoxIndicator.hover && !myControl.hover && !myControl.drag
+ when: control.__parentControl.enabled && !control.__parentControl.edit && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.drag
PropertyChanges {
target: spinBoxIndicatorIcon
visible: false
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "globalHover"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && !spinBoxIndicator.hover && myControl.hover && !myControl.edit
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && control.__parentControl.hover && !control.__parentControl.edit
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ target: control
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: myControl.enabled && !myControl.drag && spinBoxIndicator.enabled
- && spinBoxIndicator.hover && myControl.hover && !spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && !control.__parentControl.drag
+ && control.enabled && control.hover && control.__parentControl.hover
+ && !control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundHover
+ target: control
+ color: control.style.background.hover
}
},
State {
name: "press"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "edit"
- when: myControl.edit && myControl.enabled && spinBoxIndicator.enabled
+ when: control.__parentControl.edit && control.__parentControl.enabled && control.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "drag"
- when: myControl.drag && myControl.enabled
+ when: control.__parentControl.drag && control.__parentControl.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
visible: false
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: control
+ color: control.style.background.interaction
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
visible: false
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ target: control
+ color: control.style.background.disabled
}
},
State {
name: "limit"
- when: !spinBoxIndicator.enabled && !spinBoxIndicator.realEnabled && myControl.hover
+ when: !control.enabled && !control.realEnabled && control.__parentControl.hover
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxInput.qml
index bf752d464b..c9344c4d42 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxInput.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxInput.qml
@@ -1,36 +1,38 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
TextInput {
- id: textInput
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property bool edit: textInput.activeFocus
+ property T.Control __parentControl
+
+ property bool edit: control.activeFocus
property bool drag: false
- property bool hover: mouseArea.containsMouse && textInput.enabled
+ property bool hover: mouseArea.containsMouse && control.enabled
property int devicePixelRatio: 1
property int pixelsPerUnit: 10
z: 2
- font: myControl.font
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
+ font: control.__parentControl.font
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignVCenter
- leftPadding: StudioTheme.Values.inputHorizontalPadding
- rightPadding: StudioTheme.Values.inputHorizontalPadding
+ leftPadding: control.style.inputHorizontalPadding
+ rightPadding: control.style.inputHorizontalPadding
- readOnly: !myControl.editable
- validator: myControl.validator
- inputMethodHints: myControl.inputMethodHints
+ readOnly: !control.__parentControl.editable
+ validator: control.__parentControl.validator
+ inputMethodHints: control.__parentControl.inputMethodHints
selectByMouse: false
activeFocusOnPress: false
clip: true
@@ -38,16 +40,16 @@ TextInput {
// TextInput focus needs to be set to activeFocus whenever it changes,
// otherwise TextInput will get activeFocus whenever the parent SpinBox gets
// activeFocus. This will lead to weird side effects.
- onActiveFocusChanged: textInput.focus = textInput.activeFocus
+ onActiveFocusChanged: control.focus = control.activeFocus
Rectangle {
id: textInputBackground
x: 0
- y: StudioTheme.Values.border
+ y: control.style.borderWidth
z: -1
- width: textInput.width
- height: StudioTheme.Values.height - (StudioTheme.Values.border * 2)
- color: StudioTheme.Values.themeControlBackground
+ width: control.width
+ height: control.style.controlSize.height - (control.style.borderWidth * 2)
+ color: control.style.background.idle
border.width: 0
}
@@ -57,22 +59,22 @@ TextInput {
event.accepted = true
if (event.modifiers & Qt.ControlModifier) {
- mouseArea.stepSize = myControl.minStepSize
+ mouseArea.stepSize = control.__parentControl.minStepSize
mouseArea.calcValue()
- myControl.realValueModified()
+ control.__parentControl.realValueModified()
}
if (event.modifiers & Qt.ShiftModifier) {
- mouseArea.stepSize = myControl.maxStepSize
+ mouseArea.stepSize = control.__parentControl.maxStepSize
mouseArea.calcValue()
- myControl.realValueModified()
+ control.__parentControl.realValueModified()
}
}
Keys.onReleased: function(event) {
event.accepted = true
- mouseArea.stepSize = myControl.realStepSize
+ mouseArea.stepSize = control.__parentControl.realStepSize
mouseArea.calcValue()
- myControl.realValueModified()
+ control.__parentControl.realValueModified()
}
}
@@ -84,14 +86,14 @@ TextInput {
MouseArea {
id: mouseArea
- property real stepSize: myControl.realStepSize
+ property real stepSize: control.__parentControl.realStepSize
// Properties to store the state of a drag operation
property bool dragging: false
property bool hasDragged: false
property bool potentialDragStart: false
- property real initialValue: myControl.realValue // value on drag operation starts
+ property real initialValue: control.__parentControl.realValue // value on drag operation starts
property real pressStartX: 0.0
property real dragStartX: 0.0
@@ -101,7 +103,7 @@ TextInput {
property real totalUnits: 0.0 // total number of units dragged
property real units: 0.0
- property real __pixelsPerUnit: textInput.devicePixelRatio * textInput.pixelsPerUnit
+ property real __pixelsPerUnit: control.devicePixelRatio * control.pixelsPerUnit
anchors.fill: parent
enabled: true
@@ -113,21 +115,21 @@ TextInput {
onPositionChanged: function(mouse) {
if (!mouseArea.dragging
- && !myControl.edit
+ && !control.__parentControl.edit
&& Math.abs(mouseArea.pressStartX - mouse.x) > StudioTheme.Values.dragThreshold
&& mouse.buttons === Qt.LeftButton
&& mouseArea.potentialDragStart) {
mouseArea.dragging = true
mouseArea.potentialDragStart = false
- mouseArea.initialValue = myControl.realValue
+ mouseArea.initialValue = control.__parentControl.realValue
mouseArea.cursorShape = Qt.ClosedHandCursor
mouseArea.dragStartX = mouse.x
- myControl.drag = true
- myControl.dragStarted()
+ control.__parentControl.drag = true
+ control.__parentControl.dragStarted()
// Force focus on the non visible component to receive key events
dragModifierWorkaround.forceActiveFocus()
- textInput.deselect()
+ control.deselect()
}
if (!mouseArea.dragging)
@@ -152,11 +154,11 @@ TextInput {
mouseArea.translationX += translationX
mouseArea.calcValue()
- myControl.realValueModified()
+ control.__parentControl.realValueModified()
}
onClicked: function(mouse) {
- if (textInput.edit)
+ if (control.edit)
mouse.accepted = false
if (mouseArea.hasDragged) {
@@ -164,12 +166,12 @@ TextInput {
return
}
- textInput.forceActiveFocus()
- textInput.deselect() // QTBUG-75862
+ control.forceActiveFocus()
+ control.deselect() // QTBUG-75862
}
onPressed: function(mouse) {
- if (textInput.edit)
+ if (control.edit)
mouse.accepted = false
mouseArea.potentialDragStart = true
@@ -177,7 +179,7 @@ TextInput {
}
onReleased: function(mouse) {
- if (textInput.edit)
+ if (control.edit)
mouse.accepted = false
mouseArea.endDrag()
@@ -190,18 +192,18 @@ TextInput {
mouseArea.dragging = false
mouseArea.hasDragged = true
- if (myControl.compressedValueTimer.running) {
- myControl.compressedValueTimer.stop()
+ if (control.__parentControl.compressedValueTimer.running) {
+ control.__parentControl.compressedValueTimer.stop()
mouseArea.calcValue()
- myControl.compressedRealValueModified()
+ control.__parentControl.compressedRealValueModified()
}
mouseArea.cursorShape = Qt.PointingHandCursor
- myControl.drag = false
- myControl.dragEnded()
+ control.__parentControl.drag = false
+ control.__parentControl.dragEnded()
// Avoid active focus on the component after dragging
dragModifierWorkaround.focus = false
- textInput.focus = false
- myControl.focus = false
+ control.focus = false
+ control.__parentControl.focus = false
mouseArea.translationX = 0.0
mouseArea.units = 0.0
@@ -209,47 +211,48 @@ TextInput {
}
function calcValue() {
- var minUnit = (myControl.realFrom - mouseArea.initialValue) / mouseArea.stepSize
- var maxUnit = (myControl.realTo - mouseArea.initialValue) / mouseArea.stepSize
+ var minUnit = (control.__parentControl.realFrom - mouseArea.initialValue) / mouseArea.stepSize
+ var maxUnit = (control.__parentControl.realTo - mouseArea.initialValue) / mouseArea.stepSize
var units = Math.trunc(mouseArea.translationX / mouseArea.__pixelsPerUnit)
mouseArea.units = Math.min(Math.max(mouseArea.totalUnits + units, minUnit), maxUnit)
- myControl.setRealValue(mouseArea.initialValue + (mouseArea.units * mouseArea.stepSize))
+ control.__parentControl.setRealValue(mouseArea.initialValue + (mouseArea.units * mouseArea.stepSize))
if (mouseArea.dragging)
- myControl.dragging()
+ control.__parentControl.dragging()
}
onWheel: function(wheel) {
- if (!myControl.__wheelEnabled) {
+ if (!control.__parentControl.__wheelEnabled) {
wheel.accepted = false
return
}
// Set stepSize according to used modifier key
if (wheel.modifiers & Qt.ControlModifier)
- mouseArea.stepSize = myControl.minStepSize
+ mouseArea.stepSize = control.__parentControl.minStepSize
if (wheel.modifiers & Qt.ShiftModifier)
- mouseArea.stepSize = myControl.maxStepSize
+ mouseArea.stepSize = control.__parentControl.maxStepSize
- myControl.valueFromText(textInput.text, myControl.locale)
- myControl.setRealValue(myControl.realValue + (wheel.angleDelta.y / 120.0 * mouseArea.stepSize))
- myControl.realValueModified()
+ control.__parentControl.valueFromText(control.text, control.__parentControl.locale)
+ control.__parentControl.setRealValue(__parentControl.realValue + (wheel.angleDelta.y / 120.0 * mouseArea.stepSize))
+ control.__parentControl.realValueModified()
// Reset stepSize
- mouseArea.stepSize = myControl.realStepSize
+ mouseArea.stepSize = control.__parentControl.realStepSize
}
}
states: [
State {
name: "default"
- when: myControl.enabled && !textInput.edit && !textInput.hover && !myControl.hover
- && !myControl.drag && !myControl.sliderDrag
+ when: control.__parentControl.enabled && !control.edit && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.drag
+ && !control.__parentControl.sliderDrag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
}
PropertyChanges {
target: mouseArea
@@ -258,27 +261,28 @@ TextInput {
},
State {
name: "globalHover"
- when: myControl.hover && !textInput.hover
- && !textInput.edit && !myControl.drag
+ when: control.__parentControl.hover && !control.hover
+ && !control.edit && !control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: textInput.hover && myControl.hover && !textInput.edit && !myControl.drag
+ when: control.hover && control.__parentControl.hover && !control.edit
+ && !control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundHover
+ color: control.style.background.hover
}
},
State {
name: "edit"
- when: textInput.edit && !myControl.drag
+ when: control.edit && !control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ color: control.style.background.interaction
}
PropertyChanges {
target: mouseArea
@@ -287,38 +291,38 @@ TextInput {
},
State {
name: "drag"
- when: myControl.drag
+ when: control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ color: control.style.background.interaction
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "sliderDrag"
- when: myControl.sliderDrag
+ when: control.__parentControl.sliderDrag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ color: control.style.background.disabled
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeTextColorDisabled
+ target: control
+ color: control.style.text.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollBar.qml
index e761c32541..7c1126e90d 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollBar.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollBar.qml
@@ -1,13 +1,15 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.ScrollBar {
id: control
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
// This needs to be set, when using T.ScrollBar
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -31,11 +33,11 @@ T.ScrollBar {
implicitWidth: 4
implicitHeight: 4
radius: width / 2 // TODO 0
- color: StudioTheme.Values.themeScrollBarHandle
+ color: control.style.scrollBar.handle
}
background: Rectangle {
id: controlTrack
- color: StudioTheme.Values.themeScrollBarTrack
+ color: control.style.scrollBar.track
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollView.qml
index 4ee53fa0e7..bf71dfbc28 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollView.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ScrollView.qml
@@ -1,13 +1,15 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.ScrollView {
id: control
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
property alias horizontalThickness: horizontalScrollBar.height
property alias verticalThickness: verticalScrollBar.width
property bool bothVisible: verticalScrollBar.visible
@@ -20,20 +22,22 @@ T.ScrollView {
ScrollBar.vertical: ScrollBar {
id: verticalScrollBar
+ style: control.style
parent: control
- x: control.width - verticalScrollBar.width - StudioTheme.Values.border
- y: StudioTheme.Values.border
- height: control.availableHeight - (2 * StudioTheme.Values.border)
+ x: control.width - verticalScrollBar.width - control.style.borderWidth
+ y: control.style.borderWidth
+ height: control.availableHeight - (2 * control.style.borderWidth)
- (control.bothVisible ? control.horizontalThickness : 0)
active: control.ScrollBar.horizontal.active
}
ScrollBar.horizontal: ScrollBar {
id: horizontalScrollBar
+ style: control.style
parent: control
- x: StudioTheme.Values.border
- y: control.height - horizontalScrollBar.height - StudioTheme.Values.border
- width: control.availableWidth - (2 * StudioTheme.Values.border)
+ x: control.style.borderWidth
+ y: control.height - horizontalScrollBar.height - control.style.borderWidth
+ width: control.availableWidth - (2 * control.style.borderWidth)
- (control.bothVisible ? control.verticalThickness : 0)
active: control.ScrollBar.vertical.active
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml
index e84880617c..3449fda86a 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SearchBox.qml
@@ -1,146 +1,182 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Controls 2.15
+import QtQuick
+import QtQuick.Templates as T
import QtQuickDesignerTheme 1.0
import StudioTheme 1.0 as StudioTheme
-Item {
- id: root
+T.TextField {
+ id: control
- property alias text: searchFilterText.text
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- signal searchChanged(string searchText);
+ signal searchChanged(string searchText)
- function clear()
- {
- searchFilterText.text = "";
+ function isEmpty() {
+ return control.text === ""
}
- function isEmpty()
- {
- return searchFilterText.text === "";
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
+
+ horizontalAlignment: Qt.AlignLeft
+ verticalAlignment: Qt.AlignVCenter
+
+ leftPadding: 32
+ rightPadding: 30
+
+ font.pixelSize: control.style.baseFontSize
+
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
+ placeholderTextColor: control.style.text.placeholder
+
+ placeholderText: qsTr("Search")
+
+ selectByMouse: true
+ readOnly: false
+ hoverEnabled: true
+ clip: true
+
+ Text {
+ id: placeholder
+ x: control.leftPadding
+ y: control.topPadding
+ width: control.width - (control.leftPadding + control.rightPadding)
+ height: control.height - (control.topPadding + control.bottomPadding)
+
+ text: control.placeholderText
+ font: control.font
+ color: control.placeholderTextColor
+ verticalAlignment: control.verticalAlignment
+ visible: !control.length && !control.preeditText
+ && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
+ elide: Text.ElideRight
+ renderType: control.renderType
}
- implicitWidth: searchFilterText.width
- implicitHeight: searchFilterText.height
-
- TextField {
- id: searchFilterText
-
- placeholderText: qsTr("Search")
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
- background: Rectangle {
- id: textFieldBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
-
- Behavior on color {
- ColorAnimation {
- duration: StudioTheme.Values.hoverDuration
- easing.type: StudioTheme.Values.hoverEasing
- }
+ background: Rectangle {
+ id: textFieldBackground
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
+ radius: control.style.radius
+
+ /* TODO: Lets do this when the widget controls are removed so they remain consistent
+ Behavior on color {
+ ColorAnimation {
+ duration: StudioTheme.Values.hoverDuration
+ easing.type: StudioTheme.Values.hoverEasing
}
}
+ */
+ }
- height: StudioTheme.Values.defaultControlHeight
+ onTextChanged: control.searchChanged(text)
- leftPadding: 32
- rightPadding: 30
- topPadding: 6
+ T.Label {
+ id: searchIcon
+ text: StudioTheme.Constants.search_small
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: control.style.baseIconFontSize
anchors.left: parent.left
+ anchors.leftMargin: 10
+ anchors.verticalCenter: parent.verticalCenter
+ color: control.style.icon.idle
+ }
+
+ Rectangle { // x button
+ width: 16
+ height: 15
anchors.right: parent.right
- anchors.leftMargin: 5
anchors.rightMargin: 5
- selectByMouse: true
- hoverEnabled: true
+ anchors.verticalCenter: parent.verticalCenter
+ visible: control.text !== ""
+ color: xMouseArea.containsMouse ? control.style.panel.background : "transparent"
- onTextChanged: root.searchChanged(text)
-
- Label {
- text: StudioTheme.Constants.search
+ T.Label {
+ text: StudioTheme.Constants.close_small
font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
- anchors.left: parent.left
- anchors.leftMargin: 7
- anchors.verticalCenter: parent.verticalCenter
- color: StudioTheme.Values.themeIconColor
+ font.pixelSize: control.style.baseIconFontSize
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ anchors.centerIn: parent
+ color: control.style.icon.idle
}
- Rectangle { // x button
- width: 16
- height: 15
- anchors.right: parent.right
- anchors.rightMargin: 5
- anchors.verticalCenter: parent.verticalCenter
- visible: searchFilterText.text !== ""
- color: xMouseArea.containsMouse ? StudioTheme.Values.themePanelBackground
- : "transparent"
-
- Label {
- text: StudioTheme.Constants.closeCross
- font.family: StudioTheme.Constants.iconFont.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- anchors.centerIn: parent
- color: StudioTheme.Values.themeIconColor
- }
-
- MouseArea {
- id: xMouseArea
- hoverEnabled: true
- anchors.fill: parent
- onClicked: searchFilterText.text = ""
- }
+ MouseArea {
+ id: xMouseArea
+ hoverEnabled: true
+ anchors.fill: parent
+ onClicked: control.text = ""
}
+ }
- states: [
- State {
- name: "default"
- when: !searchFilterText.hovered && !searchFilterText.activeFocus
- PropertyChanges {
- target: textFieldBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- }
- PropertyChanges {
- target: searchFilterText
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
- }
- },
- State {
- name: "hover"
- when: root.enabled && searchFilterText.hovered && !searchFilterText.activeFocus
- PropertyChanges {
- target: textFieldBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
- }
-
- PropertyChanges {
- target: searchFilterText
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
- }
- },
- State {
- name: "edit"
- when: searchFilterText.activeFocus
- PropertyChanges {
- target: textFieldBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
- }
- PropertyChanges {
- target: searchFilterText
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColorInteraction
- }
+ states: [
+ State {
+ name: "default"
+ when: control.enabled && !control.hovered && !control.activeFocus
+ PropertyChanges {
+ target: textFieldBackground
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
- ]
- }
+ PropertyChanges {
+ target: control
+ placeholderTextColor: control.style.text.placeholder
+ }
+ PropertyChanges {
+ target: searchIcon
+ color: control.style.icon.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: control.enabled && control.hovered && !control.activeFocus
+ PropertyChanges {
+ target: textFieldBackground
+ color: control.style.background.hover
+ border.color: control.style.border.hover
+ }
+ PropertyChanges {
+ target: control
+ placeholderTextColor: control.style.text.placeholderHover
+ }
+ PropertyChanges {
+ target: searchIcon
+ color: control.style.icon.idle
+ }
+ },
+ State {
+ name: "edit"
+ when: control.enabled && control.activeFocus
+ PropertyChanges {
+ target: textFieldBackground
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
+ }
+ PropertyChanges {
+ target: control
+ placeholderTextColor: control.style.text.placeholderInteraction
+ }
+ PropertyChanges {
+ target: searchIcon
+ color: control.style.icon.idle
+ }
+ },
+ State {
+ name: "disabled"
+ when: !control.enabled
+ PropertyChanges {
+ target: control
+ placeholderTextColor: control.style.text.disabled
+ }
+ PropertyChanges {
+ target: searchIcon
+ color: control.style.icon.disabled
+ }
+ }
+ ]
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SecondColumnLayout.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SecondColumnLayout.qml
index e35317d014..c90884f919 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SecondColumnLayout.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SecondColumnLayout.qml
@@ -1,8 +1,8 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Layouts 1.15
+import QtQuick
+import QtQuick.Layouts
RowLayout {
Layout.fillWidth: true
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml
index 188ca3bee6..5b2fc32845 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Section.qml
@@ -1,12 +1,15 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Layouts 1.15
+import QtQuick
+import QtQuick.Layouts
import StudioTheme 1.0 as StudioTheme
Item {
- id: section
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
property alias caption: label.text
property alias captionPixelSize: label.font.pixelSize
property alias captionColor: header.color
@@ -23,34 +26,36 @@ Item {
id: header
anchors.left: parent.left
anchors.right: parent.right
- height: StudioTheme.Values.sectionHeadHeight
- color: StudioTheme.Values.themeSectionHeadBackground
+ height: control.style.sectionHeadHeight
+ color: control.style.section.head
SectionLabel {
id: label
+ style: control.style
anchors.verticalCenter: parent.verticalCenter
- color: StudioTheme.Values.themeTextColor
+ color: control.style.text.idle
x: 22
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
font.capitalization: Font.AllUppercase
}
SectionLabel {
id: arrow
- width: StudioTheme.Values.spinControlIconSizeMulti
- height: StudioTheme.Values.spinControlIconSizeMulti
+ style: control.style
+ width: control.style.smallIconSize.width
+ height: control.style.smallIconSize.height
text: StudioTheme.Constants.sectionToggle
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
renderType: Text.NativeRendering
anchors.left: parent.left
anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter
- font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti
+ font.pixelSize: control.style.smallIconFontSize
font.family: StudioTheme.Constants.iconFont.family
Behavior on rotation {
NumberAnimation {
easing.type: Easing.OutCubic
- duration: section.animationDuration
+ duration: control.animationDuration
}
}
}
@@ -58,9 +63,9 @@ Item {
MouseArea {
anchors.fill: parent
onClicked: {
- section.expanded = !section.expanded
- if (!section.expanded) // TODO
- section.forceActiveFocus()
+ control.expanded = !control.expanded
+ if (!control.expanded) // TODO
+ control.forceActiveFocus()
}
}
}
@@ -73,7 +78,7 @@ Item {
Row {
id: topRow
- height: StudioTheme.Values.sectionHeadSpacerHeight
+ height: control.style.sectionHeadSpacerHeight
anchors.top: header.bottom
}
@@ -88,21 +93,21 @@ Item {
Row {
id: bottomRow
- height: StudioTheme.Values.sectionHeadSpacerHeight
+ height: control.style.sectionHeadSpacerHeight
anchors.top: column.bottom
}
Behavior on implicitHeight {
NumberAnimation {
easing.type: Easing.OutCubic
- duration: section.animationDuration
+ duration: control.animationDuration
}
}
states: [
State {
name: "Expanded"
- when: section.expanded
+ when: control.expanded
PropertyChanges {
target: arrow
rotation: 0
@@ -110,9 +115,9 @@ Item {
},
State {
name: "Collapsed"
- when: !section.expanded
+ when: !control.expanded
PropertyChanges {
- target: section
+ target: control
implicitHeight: header.height
}
PropertyChanges {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLabel.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLabel.qml
index 1346581bd5..b8586d9621 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLabel.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLabel.qml
@@ -1,17 +1,19 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Layouts 1.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Label {
- id: label
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
width: Math.max(Math.min(240, parent.width - 220), 90)
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.myFontSize // TODO
+ color: control.style.text.idle
+ font.pixelSize: control.style.baseFontSize
elide: Text.ElideRight
Layout.preferredWidth: width
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLayout.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLayout.qml
index 1710b76066..ddd4bf1ac9 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLayout.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SectionLayout.qml
@@ -1,13 +1,17 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Layouts 1.15
+import QtQuick
+import QtQuick.Layouts
import StudioTheme 1.0 as StudioTheme
GridLayout {
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
columns: 2
- columnSpacing: StudioTheme.Values.sectionColumnSpacing
- rowSpacing: StudioTheme.Values.sectionRowSpacing
+ columnSpacing: control.style.sectionColumnSpacing
+ rowSpacing: control.style.sectionRowSpacing
width: parent.width - 16 // TODO parameterize
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml
index 8bf54b577e..3cd5266dce 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Slider.qml
@@ -1,13 +1,15 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Shapes 1.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Slider {
- id: slider
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property int decimals: 0
property bool labels: true
@@ -15,66 +17,68 @@ T.Slider {
property real tickMarkStepSize: 0.0 // StepSize bug QTBUG-76136
property real tickMarkWidth: 1.0
property real tickMarkHeight: 4.0
- readonly property int tickMarkCount: tickMarkStepSize
- !== 0.0 ? (to - from) / tickMarkStepSize + 1 : 0
- readonly property real tickMarkSpacing: tickMarkCount
- !== 0 ? (sliderTrack.width - tickMarkWidth
- * tickMarkCount) / (tickMarkCount - 1) : 0.0
+ readonly property int tickMarkCount: control.tickMarkStepSize !== 0.0
+ ? (control.to - control.from) / control.tickMarkStepSize + 1 : 0
+ readonly property real tickMarkSpacing: control.tickMarkCount !== 0
+ ? (sliderTrack.width - control.tickMarkWidth
+ * control.tickMarkCount) / (control.tickMarkCount - 1) : 0.0
- property string __activeColor: StudioTheme.Values.themeSliderActiveTrack
- property string __inactiveColor: StudioTheme.Values.themeSliderInactiveTrack
+ property string __activeColor: control.style.slider.activeTrack
+ property string __inactiveColor: control.style.slider.inactiveTrack
property bool hover: false // This property is used to indicate the global hover state
- property bool edit: slider.activeFocus
+ property bool edit: control.activeFocus
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitHandleWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitHandleHeight + topPadding + bottomPadding,
- StudioTheme.Values.height)
+ control.style.controlSize.height)
padding: 0
leftPadding: actionIndicator.width
- - (actionIndicatorVisible ? StudioTheme.Values.border
- - StudioTheme.Values.sliderPadding : 0)
+ - (control.actionIndicatorVisible ? control.style.borderWidth
+ - control.style.sliderPadding : 0)
wheelEnabled: false
ActionIndicator {
id: actionIndicator
- myControl: slider
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- width: actionIndicator.visible ? __actionIndicatorWidth : 0
- height: actionIndicator.visible ? __actionIndicatorHeight : 0
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
handle: Rectangle {
id: sliderHandle
- x: slider.leftPadding + (slider.visualPosition * slider.availableWidth)
+ x: control.leftPadding + (control.visualPosition * control.availableWidth)
- (sliderHandle.width / 2)
- y: slider.topPadding + (slider.availableHeight / 2) - (sliderHandle.height / 2)
+ y: control.topPadding + (control.availableHeight / 2) - (sliderHandle.height / 2)
z: 20
- implicitWidth: StudioTheme.Values.sliderHandleWidth
- implicitHeight: StudioTheme.Values.sliderHandleHeight
- color: StudioTheme.Values.themeSliderHandle
+ implicitWidth: control.style.sliderHandleSize.width
+ implicitHeight: control.style.sliderHandleSize.height
+ color: control.style.slider.handle
Shape {
id: sliderHandleLabelPointer
- property real __width: StudioTheme.Values.sliderPointerWidth
- property real __height: StudioTheme.Values.sliderPointerHeight
+ property real __width: control.style.sliderPointerSize.width
+ property real __height: control.style.sliderPointerSize.height
property bool antiAlias: true
- layer.enabled: antiAlias
- layer.smooth: antiAlias
- layer.textureSize: Qt.size(width * 2, height * 2)
+ layer.enabled: sliderHandleLabelPointer.antiAlias
+ layer.smooth: sliderHandleLabelPointer.antiAlias
+ layer.textureSize: Qt.size(sliderHandleLabelPointer.width * 2,
+ sliderHandleLabelPointer.height * 2)
- implicitWidth: __width
- implicitHeight: __height
+ implicitWidth: sliderHandleLabelPointer.__width
+ implicitHeight: sliderHandleLabelPointer.__height
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: sliderHandleLabelBackground.bottom
@@ -83,7 +87,7 @@ T.Slider {
id: sliderHandleLabelPointerPath
strokeColor: "transparent"
strokeWidth: 0
- fillColor: StudioTheme.Values.themeInteraction
+ fillColor: control.style.interaction
startX: 0
startY: 0
@@ -102,20 +106,20 @@ T.Slider {
Rectangle {
id: sliderHandleLabelBackground
x: -(sliderHandleLabelBackground.width / 2) + (sliderHandle.width / 2)
- width: makeEven(
- sliderHandleLabel.width + StudioTheme.Values.inputHorizontalPadding)
+ width: control.makeEven(
+ sliderHandleLabel.width + control.style.inputHorizontalPadding)
height: sliderHandleLabel.height
anchors.bottom: parent.top
- anchors.bottomMargin: StudioTheme.Values.sliderMargin
- color: StudioTheme.Values.themeInteraction
+ anchors.bottomMargin: control.style.sliderMargin
+ color: control.style.interaction
Text {
id: sliderHandleLabel
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
- text: Number.parseFloat(slider.value).toFixed(slider.decimals)
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.sliderFontSize
+ text: Number.parseFloat(control.value).toFixed(control.decimals)
+ color: control.style.text.idle
+ font.pixelSize: control.style.smallFontSize
}
}
}
@@ -127,16 +131,16 @@ T.Slider {
background: Rectangle {
id: sliderTrack
- x: slider.leftPadding
- y: slider.topPadding + slider.availableHeight / 2 - height / 2
- width: slider.availableWidth
- height: StudioTheme.Values.sliderTrackHeight
- color: __inactiveColor
+ x: control.leftPadding
+ y: control.topPadding + control.availableHeight / 2 - sliderTrack.height / 2
+ width: control.availableWidth
+ height: control.style.sliderTrackHeight
+ color: control.__inactiveColor
Rectangle {
- width: slider.visualPosition * parent.width
+ width: control.visualPosition * parent.width
height: parent.height
- color: __activeColor
+ color: control.__activeColor
}
}
@@ -148,36 +152,37 @@ T.Slider {
Text {
id: tickmarkFromLabel
x: 0
- y: StudioTheme.Values.sliderPadding
- text: Number.parseFloat(slider.from).toFixed(slider.decimals)
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.sliderFontSize
- visible: slider.labels
+ y: control.style.sliderPadding
+ text: Number.parseFloat(control.from).toFixed(control.decimals)
+ color: control.style.text.idle
+ font.pixelSize: control.style.smallFontSize
+ visible: control.labels
}
Text {
id: tickmarkToLabel
- x: slider.availableWidth - width
- y: StudioTheme.Values.sliderPadding
- text: Number.parseFloat(slider.to).toFixed(slider.decimals)
- color: StudioTheme.Values.themeTextColor
- font.pixelSize: StudioTheme.Values.sliderFontSize
- visible: slider.labels
+ x: control.availableWidth - tickmarkToLabel.width
+ y: control.style.sliderPadding
+ text: Number.parseFloat(control.to).toFixed(control.decimals)
+ color: control.style.text.idle
+ font.pixelSize: control.style.smallFontSize
+ visible: control.labels
}
Row {
id: tickmarkRow
- spacing: tickMarkSpacing
- visible: slider.tickMarks
+ spacing: control.tickMarkSpacing
+ visible: control.tickMarks
Repeater {
id: tickmarkRepeater
- model: tickMarkCount
+ model: control.tickMarkCount
delegate: Rectangle {
- implicitWidth: tickMarkWidth
- implicitHeight: StudioTheme.Values.sliderTrackHeight
- color: x < (slider.visualPosition
- * slider.availableWidth) ? __inactiveColor : __activeColor
+ implicitWidth: control.tickMarkWidth
+ implicitHeight: control.style.sliderTrackHeight
+ color: x < (control.visualPosition
+ * control.availableWidth) ? control.__inactiveColor
+ : control.__activeColor
}
}
}
@@ -187,85 +192,85 @@ T.Slider {
id: mouseArea
x: actionIndicator.width
y: 0
- width: slider.width - actionIndicator.width
- height: slider.height
+ width: control.width - actionIndicator.width
+ height: control.height
enabled: true
hoverEnabled: true
propagateComposedEvents: true
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
// Sets the global hover
- onContainsMouseChanged: slider.hover = mouseArea.containsMouse
+ onContainsMouseChanged: control.hover = mouseArea.containsMouse
onPressed: function(mouse) { mouse.accepted = false }
}
states: [
State {
name: "default"
- when: slider.enabled && !slider.hover && !slider.edit
+ when: control.enabled && !control.hover && !control.edit
PropertyChanges {
- target: slider
+ target: control
wheelEnabled: false
}
},
State {
name: "hover"
- when: slider.enabled && slider.hover && !slider.edit
+ when: control.enabled && control.hover && !control.edit
PropertyChanges {
- target: slider
- __activeColor: StudioTheme.Values.themeSliderActiveTrackHover
- __inactiveColor: StudioTheme.Values.themeSliderInactiveTrackHover
+ target: control
+ __activeColor: control.style.slider.activeTrackHover
+ __inactiveColor: control.style.slider.inactiveTrackHover
}
PropertyChanges {
target: sliderHandle
- color: StudioTheme.Values.themeSliderHandleHover
+ color: control.style.slider.handleHover
}
},
State {
name: "focus"
- when: slider.enabled && slider.edit
+ when: control.enabled && control.edit
PropertyChanges {
- target: slider
+ target: control
wheelEnabled: true
- __activeColor: StudioTheme.Values.themeSliderActiveTrackFocus
- __inactiveColor: StudioTheme.Values.themeSliderInactiveTrackFocus
+ __activeColor: control.style.slider.activeTrackFocus
+ __inactiveColor: control.style.slider.inactiveTrackFocus
}
PropertyChanges {
target: sliderHandle
- color: StudioTheme.Values.themeSliderHandleFocus
+ color: control.style.slider.handleFocus
}
},
State {
name: "disable"
- when: !slider.enabled
+ when: !control.enabled
PropertyChanges {
target: tickmarkFromLabel
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.text.disabled
}
PropertyChanges {
target: tickmarkToLabel
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.text.disabled
}
PropertyChanges {
target: sliderHandleLabel
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.text.disabled
}
PropertyChanges {
- target: slider
- __activeColor: StudioTheme.Values.themeControlBackgroundDisabled
- __inactiveColor: StudioTheme.Values.themeControlBackgroundDisabled
+ target: control
+ __activeColor: control.style.background.disabled
+ __inactiveColor: control.style.background.disabled
}
PropertyChanges {
target: sliderHandleLabelBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ color: control.style.background.disabled
}
PropertyChanges {
target: sliderHandleLabelPointerPath
- fillColor: StudioTheme.Values.themeControlBackgroundDisabled
+ fillColor: control.style.background.disabled
}
PropertyChanges {
target: sliderHandle
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ color: control.style.background.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SliderPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SliderPopup.qml
index e550d52c17..aea63df482 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SliderPopup.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SliderPopup.qml
@@ -1,14 +1,16 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Popup {
- id: sliderPopup
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property T.Control __parentControl
property bool drag: slider.pressed
@@ -18,7 +20,7 @@ T.Popup {
| T.Popup.CloseOnReleaseOutsideParent
background: Rectangle {
- color: StudioTheme.Values.themePopupBackground
+ color: control.style.popup.background
border.width: 0
}
@@ -31,54 +33,54 @@ T.Popup {
rightPadding: 3
leftPadding: 3
- from: myControl.from
- value: myControl.value
- to: myControl.to
+ from: control.__parentControl.from
+ value: control.__parentControl.value
+ to: control.__parentControl.to
focusPolicy: Qt.NoFocus
handle: Rectangle {
x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
y: slider.topPadding + (slider.availableHeight / 2) - (height / 2)
- width: StudioTheme.Values.sliderHandleWidth
- height: StudioTheme.Values.sliderHandleHeight
+ width: control.style.sliderHandleSize.width
+ height: control.style.sliderHandleSize.height
radius: 0
- color: slider.pressed ? StudioTheme.Values.themeSliderHandleInteraction
- : StudioTheme.Values.themeSliderHandle
+ color: slider.pressed ? control.style.slider.handleInteraction
+ : control.style.slider.handle
}
background: Rectangle {
x: slider.leftPadding
y: slider.topPadding + (slider.availableHeight / 2) - (height / 2)
width: slider.availableWidth
- height: StudioTheme.Values.sliderTrackHeight
+ height: control.style.sliderTrackHeight
radius: 0
- color: StudioTheme.Values.themeSliderInactiveTrack
+ color: control.style.slider.inactiveTrack
Rectangle {
width: slider.visualPosition * parent.width
height: parent.height
- color: StudioTheme.Values.themeSliderActiveTrack
+ color: control.style.slider.activeTrack
radius: 0
}
}
onMoved: {
- var currValue = myControl.value
- myControl.value = slider.value
+ var currValue = control.__parentControl.value
+ control.__parentControl.value = slider.value
- if (currValue !== myControl.value)
- myControl.valueModified()
+ if (currValue !== control.__parentControl.value)
+ control.__parentControl.valueModified()
}
}
onOpened: {
// Check if value is in sync with text input, if not sync it!
- var val = myControl.valueFromText(myControl.contentItem.text,
- myControl.locale)
- if (myControl.value !== val) {
- myControl.value = val
- myControl.valueModified()
+ var val = control.__parentControl.valueFromText(control.__parentControl.contentItem.text,
+ control.__parentControl.locale)
+ if (control.__parentControl.value !== val) {
+ control.__parentControl.value = val
+ control.__parentControl.valueModified()
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml
index 8a5a5e8c51..fb3959f4cf 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml
index 9ba334bbf5..a0c7244c79 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml
@@ -1,18 +1,20 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.SpinBox {
- id: mySpinBox
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property alias labelColor: spinBoxInput.color
property alias actionIndicator: actionIndicator
property int decimals: 0
- property int factor: Math.pow(10, decimals)
+ property int factor: Math.pow(10, control.decimals)
property real minStepSize: 1
property real maxStepSize: 10
@@ -21,23 +23,23 @@ T.SpinBox {
// This property is used to indicate the global hover state
property bool hover: (spinBoxInput.hover || actionIndicator.hover || spinBoxIndicatorUp.hover
|| spinBoxIndicatorDown.hover || sliderIndicator.hover)
- && mySpinBox.enabled
+ && control.enabled
property bool drag: false
property bool sliderDrag: sliderPopup.drag
property bool dirty: false // user modification flag
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
property bool spinBoxIndicatorVisible: true
- property real __spinBoxIndicatorWidth: StudioTheme.Values.spinBoxIndicatorWidth
- property real __spinBoxIndicatorHeight: StudioTheme.Values.spinBoxIndicatorHeight
+ property real __spinBoxIndicatorWidth: control.style.spinBoxIndicatorSize.width
+ property real __spinBoxIndicatorHeight: control.style.spinBoxIndicatorSize.height
property alias sliderIndicatorVisible: sliderIndicator.visible
- property real __sliderIndicatorWidth: StudioTheme.Values.sliderIndicatorWidth
- property real __sliderIndicatorHeight: StudioTheme.Values.sliderIndicatorHeight
+ property real __sliderIndicatorWidth: control.style.squareControlSize.width
+ property real __sliderIndicatorHeight: control.style.squareControlSize.height
property alias __devicePixelRatio: spinBoxInput.devicePixelRatio
property alias pixelsPerUnit: spinBoxInput.pixelsPerUnit
@@ -56,87 +58,91 @@ T.SpinBox {
wheelEnabled: false
hoverEnabled: true
- width: StudioTheme.Values.defaultControlWidth
- height: StudioTheme.Values.defaultControlHeight
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
leftPadding: spinBoxIndicatorDown.x + spinBoxIndicatorDown.width
- rightPadding: sliderIndicator.width + StudioTheme.Values.border
+ rightPadding: sliderIndicator.width + control.style.borderWidth
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
editable: true
- validator: mySpinBox.decimals ? doubleValidator : intValidator
+ validator: control.decimals ? doubleValidator : intValidator
DoubleValidator {
id: doubleValidator
- locale: mySpinBox.locale.name
+ locale: control.locale.name
notation: DoubleValidator.StandardNotation
- decimals: mySpinBox.decimals
- bottom: Math.min(mySpinBox.from, mySpinBox.to) / mySpinBox.factor
- top: Math.max(mySpinBox.from, mySpinBox.to) / mySpinBox.factor
+ decimals: control.decimals
+ bottom: Math.min(control.from, control.to) / control.factor
+ top: Math.max(control.from, control.to) / control.factor
}
IntValidator {
id: intValidator
- locale: mySpinBox.locale.name
- bottom: Math.min(mySpinBox.from, mySpinBox.to)
- top: Math.max(mySpinBox.from, mySpinBox.to)
+ locale: control.locale.name
+ bottom: Math.min(control.from, control.to)
+ top: Math.max(control.from, control.to)
}
ActionIndicator {
id: actionIndicator
- myControl: mySpinBox
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- width: actionIndicator.visible ? mySpinBox.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? mySpinBox.__actionIndicatorHeight : 0
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
up.indicator: SpinBoxIndicator {
id: spinBoxIndicatorUp
- myControl: mySpinBox
+ style: control.style
+ __parentControl: control
iconFlip: -1
- visible: mySpinBox.spinBoxIndicatorVisible
- pressed: mySpinBox.up.pressed
- x: actionIndicator.width + StudioTheme.Values.border
- y: StudioTheme.Values.border
- width: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorWidth : 0
- height: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorHeight : 0
-
- enabled: (mySpinBox.from < mySpinBox.to) ? mySpinBox.value < mySpinBox.to
- : mySpinBox.value > mySpinBox.to
+ visible: control.spinBoxIndicatorVisible
+ pressed: control.up.pressed
+ x: actionIndicator.width + control.style.borderWidth
+ y: control.style.borderWidth
+ width: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorWidth : 0
+ height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0
+
+ enabled: (control.from < control.to) ? control.value < control.to
+ : control.value > control.to
}
down.indicator: SpinBoxIndicator {
id: spinBoxIndicatorDown
- myControl: mySpinBox
- visible: mySpinBox.spinBoxIndicatorVisible
- pressed: mySpinBox.down.pressed
- x: actionIndicator.width + StudioTheme.Values.border
+ style: control.style
+ __parentControl: control
+ visible: control.spinBoxIndicatorVisible
+ pressed: control.down.pressed
+ x: actionIndicator.width + control.style.borderWidth
y: spinBoxIndicatorUp.y + spinBoxIndicatorUp.height
- width: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorWidth : 0
- height: mySpinBox.spinBoxIndicatorVisible ? mySpinBox.__spinBoxIndicatorHeight : 0
+ width: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorWidth : 0
+ height: control.spinBoxIndicatorVisible ? control.__spinBoxIndicatorHeight : 0
- enabled: (mySpinBox.from < mySpinBox.to) ? mySpinBox.value > mySpinBox.from
- : mySpinBox.value < mySpinBox.from
+ enabled: (control.from < control.to) ? control.value > control.from
+ : control.value < control.from
}
contentItem: SpinBoxInput {
id: spinBoxInput
- myControl: mySpinBox
+ style: control.style
+ __parentControl: control
function handleEditingFinished() {
- mySpinBox.focus = false
+ control.focus = false
// Keep the dirty state before calling setValueFromInput(),
// it will be set to false (cleared) internally
- var valueModified = mySpinBox.dirty
+ var valueModified = control.dirty
- mySpinBox.setValueFromInput()
+ control.setValueFromInput()
myTimer.stop()
// Only trigger the signal, if the value was modified
if (valueModified)
- mySpinBox.compressedValueModified()
+ control.compressedValueModified()
}
onEditingFinished: spinBoxInput.handleEditingFinished()
@@ -144,32 +150,34 @@ T.SpinBox {
background: Rectangle {
id: spinBoxBackground
- color: StudioTheme.Values.themeControlOutline
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
x: actionIndicator.width
- width: mySpinBox.width - actionIndicator.width
- height: mySpinBox.height
+ width: control.width - actionIndicator.width
+ height: control.height
}
CheckIndicator {
id: sliderIndicator
- myControl: mySpinBox
- myPopup: sliderPopup
+ style: control.style
+ __parentControl: control
+ __parentPopup: sliderPopup
x: spinBoxInput.x + spinBoxInput.width
- y: StudioTheme.Values.border
- width: sliderIndicator.visible ? mySpinBox.__sliderIndicatorWidth - StudioTheme.Values.border : 0
- height: sliderIndicator.visible ? mySpinBox.__sliderIndicatorHeight - (StudioTheme.Values.border * 2) : 0
+ y: control.style.borderWidth
+ width: sliderIndicator.visible ? control.__sliderIndicatorWidth - control.style.borderWidth : 0
+ height: sliderIndicator.visible ? control.__sliderIndicatorHeight - (control.style.borderWidth * 2) : 0
visible: false // reasonable default
}
SliderPopup {
id: sliderPopup
- myControl: mySpinBox
- x: actionIndicator.width + StudioTheme.Values.border
- y: StudioTheme.Values.height
- width: mySpinBox.width - actionIndicator.width - (StudioTheme.Values.border * 2)
- height: StudioTheme.Values.sliderHeight
+ style: control.style
+ __parentControl: control
+ x: actionIndicator.width + control.style.borderWidth
+ y: control.style.controlSize.height
+ width: control.width - actionIndicator.width - (control.style.borderWidth * 2)
+ height: control.style.smallControlSize.height
enter: Transition {}
exit: Transition {}
@@ -177,21 +185,21 @@ T.SpinBox {
textFromValue: function (value, locale) {
locale.numberOptions = Locale.OmitGroupSeparator
- return Number(value / mySpinBox.factor).toLocaleString(locale, 'f',
- mySpinBox.decimals)
+ return Number(value / control.factor).toLocaleString(locale, 'f',
+ control.decimals)
}
valueFromText: function (text, locale) {
- return Number.fromLocaleString(locale, text) * mySpinBox.factor
+ return Number.fromLocaleString(locale, text) * control.factor
}
states: [
State {
name: "default"
- when: mySpinBox.enabled && !mySpinBox.hover && !mySpinBox.hovered
- && !mySpinBox.edit && !mySpinBox.drag && !mySpinBox.sliderDrag
+ when: control.enabled && !control.hover && !control.hovered
+ && !control.edit && !control.drag && !control.sliderDrag
PropertyChanges {
- target: mySpinBox
+ target: control
__wheelEnabled: false
}
PropertyChanges {
@@ -200,15 +208,23 @@ T.SpinBox {
}
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ border.color: control.style.border.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: control.enabled && control.hover && control.hovered
+ && !control.edit && !control.drag && !control.sliderDrag
+ PropertyChanges {
+ target: spinBoxBackground
+ border.color: control.style.border.hover
}
},
State {
name: "edit"
- when: mySpinBox.edit
+ when: control.edit
PropertyChanges {
- target: mySpinBox
+ target: control
__wheelEnabled: true
}
PropertyChanges {
@@ -217,26 +233,23 @@ T.SpinBox {
}
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutline
+ border.color: control.style.border.idle
}
},
State {
name: "drag"
- when: mySpinBox.drag || mySpinBox.sliderDrag
+ when: control.drag || control.sliderDrag
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ border.color: control.style.border.interaction
}
},
State {
name: "disable"
- when: !mySpinBox.enabled
+ when: !control.enabled
PropertyChanges {
target: spinBoxBackground
- color: StudioTheme.Values.themeControlOutlineDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ border.color: control.style.border.disabled
}
}
]
@@ -246,19 +259,19 @@ T.SpinBox {
repeat: false
running: false
interval: 400
- onTriggered: mySpinBox.compressedValueModified()
+ onTriggered: control.compressedValueModified()
}
onValueModified: myTimer.restart()
- onFocusChanged: mySpinBox.setValueFromInput()
- onDisplayTextChanged: spinBoxInput.text = mySpinBox.displayText
+ onFocusChanged: control.setValueFromInput()
+ onDisplayTextChanged: spinBoxInput.text = control.displayText
onActiveFocusChanged: {
- if (mySpinBox.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason)
- mySpinBox.preFocusText = spinBoxInput.text
+ if (control.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason)
+ control.preFocusText = spinBoxInput.text
spinBoxInput.selectAll()
}
- if (sliderPopup.opened && !mySpinBox.activeFocus)
+ if (sliderPopup.opened && !control.activeFocus)
sliderPopup.close()
}
@@ -267,43 +280,43 @@ T.SpinBox {
event.accepted = true
// Store current step size
- var currStepSize = mySpinBox.stepSize
+ var currStepSize = control.stepSize
if (event.modifiers & Qt.ControlModifier)
- mySpinBox.stepSize = mySpinBox.minStepSize
+ control.stepSize = control.minStepSize
if (event.modifiers & Qt.ShiftModifier)
- mySpinBox.stepSize = mySpinBox.maxStepSize
+ control.stepSize = control.maxStepSize
// Check if value is in sync with text input, if not sync it!
- var val = mySpinBox.valueFromText(spinBoxInput.text,
- mySpinBox.locale)
- if (mySpinBox.value !== val)
- mySpinBox.value = val
+ var val = control.valueFromText(spinBoxInput.text,
+ control.locale)
+ if (control.value !== val)
+ control.value = val
- var currValue = mySpinBox.value
+ var currValue = control.value
if (event.key === Qt.Key_Up)
- mySpinBox.increase()
+ control.increase()
else
- mySpinBox.decrease()
+ control.decrease()
- if (currValue !== mySpinBox.value)
- mySpinBox.valueModified()
+ if (currValue !== control.value)
+ control.valueModified()
// Reset step size
- mySpinBox.stepSize = currStepSize
+ control.stepSize = currStepSize
}
if (event.key === Qt.Key_Escape) {
- spinBoxInput.text = mySpinBox.preFocusText
- mySpinBox.dirty = true
+ spinBoxInput.text = control.preFocusText
+ control.dirty = true
spinBoxInput.handleEditingFinished()
}
// FIX: This is a temporary fix for QTBUG-74239
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
- mySpinBox.setValueFromInput()
+ control.setValueFromInput()
}
function clamp(v, lo, hi) {
@@ -311,27 +324,27 @@ T.SpinBox {
}
function setValueFromInput() {
- if (!mySpinBox.dirty)
+ if (!control.dirty)
return
// FIX: This is a temporary fix for QTBUG-74239
- var currValue = mySpinBox.value
+ var currValue = control.value
if (!spinBoxInput.acceptableInput)
- mySpinBox.value = clamp(valueFromText(spinBoxInput.text,
- mySpinBox.locale),
- mySpinBox.validator.bottom * mySpinBox.factor,
- mySpinBox.validator.top * mySpinBox.factor)
+ control.value = clamp(valueFromText(spinBoxInput.text,
+ control.locale),
+ control.validator.bottom * control.factor,
+ control.validator.top * control.factor)
else
- mySpinBox.value = valueFromText(spinBoxInput.text,
- mySpinBox.locale)
+ control.value = valueFromText(spinBoxInput.text,
+ control.locale)
- if (spinBoxInput.text !== mySpinBox.displayText)
- spinBoxInput.text = mySpinBox.displayText
+ if (spinBoxInput.text !== control.displayText)
+ spinBoxInput.text = control.displayText
- if (mySpinBox.value !== currValue)
- mySpinBox.valueModified()
+ if (control.value !== currValue)
+ control.valueModified()
- mySpinBox.dirty = false
+ control.dirty = false
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxIndicator.qml
index ef855b18d4..a70cc1302a 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxIndicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxIndicator.qml
@@ -1,21 +1,23 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Rectangle {
- id: spinBoxIndicator
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property bool hover: spinBoxIndicatorMouseArea.containsMouse && spinBoxIndicator.enabled
+ property T.Control __parentControl
+
+ property bool hover: spinBoxIndicatorMouseArea.containsMouse && control.enabled
property bool pressed: spinBoxIndicatorMouseArea.containsPress
property alias iconFlip: spinBoxIndicatorIconScale.yScale
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
border.width: 0
// This MouseArea is a workaround to avoid some hover state related bugs
@@ -25,8 +27,8 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
onPressed: function(mouse) {
- if (myControl.activeFocus)
- spinBoxIndicator.forceActiveFocus()
+ if (control.__parentControl.activeFocus)
+ control.forceActiveFocus()
mouse.accepted = false
}
@@ -35,11 +37,11 @@ Rectangle {
T.Label {
id: spinBoxIndicatorIcon
text: StudioTheme.Constants.upDownSquare2
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
- font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti
+ font.pixelSize: control.style.smallIconFontSize
font.family: StudioTheme.Constants.iconFont.family
anchors.fill: parent
transform: Scale {
@@ -51,46 +53,58 @@ Rectangle {
states: [
State {
+ name: "default"
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.edit
+ PropertyChanges {
+ target: spinBoxIndicatorIcon
+ color: control.style.icon.idle
+ }
+ },
+ State {
name: "globalHover"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && !spinBoxIndicator.hover && myControl.hover && !myControl.edit
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && control.__parentControl.hover && !control.__parentControl.edit
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "hover"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && spinBoxIndicator.hover && myControl.hover && !spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.hover
+ && control.__parentControl.hover && !control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeIconColorHover
+ color: control.style.icon.hover
}
},
State {
name: "press"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
- color: "#323232" // TODO
+ color: control.style.icon.idle
}
},
State {
name: "edit"
- when: myControl.edit
+ when: control.__parentControl.edit
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "disable"
- when: !myControl.enabled || !spinBoxIndicator.enabled
+ when: !control.__parentControl.enabled || !control.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.icon.disabled
}
}
]
@@ -99,102 +113,104 @@ Rectangle {
states: [
State {
name: "default"
- when: myControl.enabled && !myControl.edit
- && !spinBoxIndicator.hover && !myControl.hover && !myControl.drag
+ when: control.__parentControl.enabled && !control.__parentControl.edit && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.drag
PropertyChanges {
target: spinBoxIndicatorIcon
visible: false
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "globalHover"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && !spinBoxIndicator.hover && myControl.hover && !myControl.edit
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && !control.hover
+ && control.__parentControl.hover && !control.__parentControl.edit
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ target: control
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: myControl.enabled && !myControl.drag
- && spinBoxIndicator.hover && myControl.hover && !spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && !control.__parentControl.drag
+ && control.enabled && control.hover && control.__parentControl.hover
+ && !control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundHover
+ target: control
+ color: control.style.background.hover
}
},
State {
name: "press"
- when: myControl.enabled && spinBoxIndicator.enabled && !myControl.drag
- && spinBoxIndicator.pressed
+ when: control.__parentControl.enabled && control.enabled
+ && !control.__parentControl.drag && control.pressed
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: "#2aafd3" // TODO
+ target: control
+ color: control.style.interaction
}
},
State {
name: "edit"
- when: myControl.edit
+ when: control.__parentControl.edit && control.__parentControl.enabled && control.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
},
State {
name: "drag"
- when: myControl.drag
+ when: control.__parentControl.drag && control.__parentControl.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
visible: false
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ target: control
+ color: control.style.background.interaction
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
target: spinBoxIndicatorIcon
visible: false
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.disabled
}
},
State {
name: "limit"
- when: !spinBoxIndicator.enabled && !spinBoxIndicator.realEnabled && myControl.hover
+ when: !control.enabled && !control.realEnabled && control.__parentControl.hover
PropertyChanges {
target: spinBoxIndicatorIcon
visible: true
}
PropertyChanges {
- target: spinBoxIndicator
- color: StudioTheme.Values.themeControlBackground
+ target: control
+ color: control.style.background.idle
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml
index 446bfc1c26..8db9578991 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBoxInput.qml
@@ -1,36 +1,38 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
TextInput {
- id: textInput
+ id: control
- property T.Control myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property bool edit: textInput.activeFocus
+ property T.Control __parentControl
+
+ property bool edit: control.activeFocus
property bool drag: false
- property bool hover: mouseArea.containsMouse && textInput.enabled
+ property bool hover: mouseArea.containsMouse && control.enabled
property int devicePixelRatio: 1
property int pixelsPerUnit: 10
z: 2
- font: myControl.font
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
+ font: control.__parentControl.font
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignVCenter
- leftPadding: StudioTheme.Values.inputHorizontalPadding
- rightPadding: StudioTheme.Values.inputHorizontalPadding
+ leftPadding: control.style.inputHorizontalPadding
+ rightPadding: control.style.inputHorizontalPadding
- readOnly: !myControl.editable
- validator: myControl.validator
- inputMethodHints: myControl.inputMethodHints
+ readOnly: !control.__parentControl.editable
+ validator: control.__parentControl.validator
+ inputMethodHints: control.__parentControl.inputMethodHints
selectByMouse: false
activeFocusOnPress: false
clip: true
@@ -38,16 +40,16 @@ TextInput {
// TextInput focus needs to be set to activeFocus whenever it changes,
// otherwise TextInput will get activeFocus whenever the parent SpinBox gets
// activeFocus. This will lead to weird side effects.
- onActiveFocusChanged: textInput.focus = textInput.activeFocus
+ onActiveFocusChanged: control.focus = control.activeFocus
Rectangle {
id: textInputBackground
x: 0
- y: StudioTheme.Values.border
+ y: control.style.borderWidth
z: -1
- width: textInput.width
- height: StudioTheme.Values.height - (StudioTheme.Values.border * 2)
- color: StudioTheme.Values.themeControlBackground
+ width: control.width
+ height: control.style.controlSize.height - (control.style.borderWidth * 2)
+ color: control.style.background.idle
border.width: 0
}
@@ -57,22 +59,22 @@ TextInput {
event.accepted = true
if (event.modifiers & Qt.ControlModifier) {
- mouseArea.stepSize = myControl.minStepSize
+ mouseArea.stepSize = control.__parentControl.minStepSize
mouseArea.calcValue()
- myControl.valueModified()
+ control.__parentControl.valueModified()
}
if (event.modifiers & Qt.ShiftModifier) {
- mouseArea.stepSize = myControl.maxStepSize
+ mouseArea.stepSize = control.__parentControl.maxStepSize
mouseArea.calcValue()
- myControl.valueModified()
+ control.__parentControl.valueModified()
}
}
Keys.onReleased: function(event) {
event.accepted = true
- mouseArea.stepSize = myControl.stepSize
+ mouseArea.stepSize = control.__parentControl.stepSize
mouseArea.calcValue()
- myControl.valueModified()
+ control.__parentControl.valueModified()
}
}
@@ -84,14 +86,14 @@ TextInput {
MouseArea {
id: mouseArea
- property real stepSize: myControl.stepSize
+ property real stepSize: control.__parentControl.stepSize
// Properties to store the state of a drag operation
property bool dragging: false
property bool hasDragged: false
property bool potentialDragStart: false
- property int initialValue: myControl.value // value on drag operation starts
+ property int initialValue: control.__parentControl.value // value on drag operation starts
property real pressStartX: 0.0
property real dragStartX: 0.0
@@ -101,7 +103,7 @@ TextInput {
property real totalUnits: 0.0 // total number of units dragged
property real units: 0.0
- property real __pixelsPerUnit: textInput.devicePixelRatio * textInput.pixelsPerUnit
+ property real __pixelsPerUnit: control.devicePixelRatio * control.pixelsPerUnit
anchors.fill: parent
enabled: true
@@ -113,21 +115,21 @@ TextInput {
onPositionChanged: function(mouse) {
if (!mouseArea.dragging
- && !myControl.edit
+ && !control.__parentControl.edit
&& Math.abs(mouseArea.pressStartX - mouse.x) > StudioTheme.Values.dragThreshold
&& mouse.buttons === Qt.LeftButton
&& mouseArea.potentialDragStart) {
mouseArea.dragging = true
mouseArea.potentialDragStart = false
- mouseArea.initialValue = myControl.value
+ mouseArea.initialValue = control.__parentControl.value
mouseArea.cursorShape = Qt.ClosedHandCursor
mouseArea.dragStartX = mouse.x
- myControl.drag = true
- myControl.dragStarted()
+ control.__parentControl.drag = true
+ control.__parentControl.dragStarted()
// Force focus on the non visible component to receive key events
dragModifierWorkaround.forceActiveFocus()
- textInput.deselect()
+ control.deselect()
}
if (!mouseArea.dragging)
@@ -152,11 +154,11 @@ TextInput {
mouseArea.translationX += translationX
mouseArea.calcValue()
- myControl.valueModified()
+ control.__parentControl.valueModified()
}
onClicked: function(mouse) {
- if (textInput.edit)
+ if (control.edit)
mouse.accepted = false
if (mouseArea.hasDragged) {
@@ -164,12 +166,12 @@ TextInput {
return
}
- textInput.forceActiveFocus()
- textInput.deselect() // QTBUG-75862
+ control.forceActiveFocus()
+ control.deselect() // QTBUG-75862
}
onPressed: function(mouse) {
- if (textInput.edit)
+ if (control.edit)
mouse.accepted = false
mouseArea.potentialDragStart = true
@@ -177,7 +179,7 @@ TextInput {
}
onReleased: function(mouse) {
- if (textInput.edit)
+ if (control.edit)
mouse.accepted = false
mouseArea.endDrag()
@@ -190,18 +192,18 @@ TextInput {
mouseArea.dragging = false
mouseArea.hasDragged = true
- if (myControl.compressedValueTimer.running) {
- myControl.compressedValueTimer.stop()
+ if (control.__parentControl.compressedValueTimer.running) {
+ control.__parentControl.compressedValueTimer.stop()
mouseArea.calcValue()
- myControl.compressedValueModified()
+ control.__parentControl.compressedValueModified()
}
mouseArea.cursorShape = Qt.PointingHandCursor
- myControl.drag = false
- myControl.dragEnded()
+ control.__parentControl.drag = false
+ control.__parentControl.dragEnded()
// Avoid active focus on the component after dragging
dragModifierWorkaround.focus = false
- textInput.focus = false
- myControl.focus = false
+ control.focus = false
+ control.__parentControl.focus = false
mouseArea.translationX = 0
mouseArea.units = 0
@@ -209,53 +211,55 @@ TextInput {
}
function calcValue() {
- var minUnit = (myControl.from - mouseArea.initialValue) / mouseArea.stepSize
- var maxUnit = (myControl.to - mouseArea.initialValue) / mouseArea.stepSize
+ var minUnit = (control.__parentControl.from - mouseArea.initialValue) / mouseArea.stepSize
+ var maxUnit = (control.__parentControl.to - mouseArea.initialValue) / mouseArea.stepSize
var units = Math.trunc(mouseArea.translationX / mouseArea.__pixelsPerUnit)
mouseArea.units = Math.min(Math.max(mouseArea.totalUnits + units, minUnit), maxUnit)
- myControl.value = mouseArea.initialValue + (mouseArea.units * mouseArea.stepSize)
+ control.__parentControl.value = mouseArea.initialValue + (mouseArea.units * mouseArea.stepSize)
if (mouseArea.dragging)
- myControl.dragging()
+ control.__parentControl.dragging()
}
onWheel: function(wheel) {
- if (!myControl.__wheelEnabled) {
+ if (!control.__parentControl.__wheelEnabled) {
wheel.accepted = false
return
}
// Set stepSize according to used modifier key
if (wheel.modifiers & Qt.ControlModifier)
- mouseArea.stepSize = myControl.minStepSize
+ mouseArea.stepSize = control.__parentControl.minStepSize
if (wheel.modifiers & Qt.ShiftModifier)
- mouseArea.stepSize = myControl.maxStepSize
+ mouseArea.stepSize = control.__parentControl.maxStepSize
- var val = myControl.valueFromText(textInput.text, myControl.locale)
- if (myControl.value !== val)
- myControl.value = val
+ var val = control.__parentControl.valueFromText(control.text,
+ control.__parentControl.locale)
+ if (control.__parentControl.value !== val)
+ control.__parentControl.value = val
- var currValue = myControl.value
- myControl.value += (wheel.angleDelta.y / 120 * mouseArea.stepSize)
+ var currValue = control.__parentControl.value
+ control.__parentControl.value += (wheel.angleDelta.y / 120 * mouseArea.stepSize)
- if (currValue !== myControl.value)
- myControl.valueModified()
+ if (currValue !== control.__parentControl.value)
+ control.__parentControl.valueModified()
// Reset stepSize
- mouseArea.stepSize = myControl.stepSize
+ mouseArea.stepSize = control.__parentControl.stepSize
}
}
states: [
State {
name: "default"
- when: myControl.enabled && !textInput.edit && !textInput.hover && !myControl.hover
- && !myControl.drag && !myControl.sliderDrag
+ when: control.__parentControl.enabled && !control.edit && !control.hover
+ && !control.__parentControl.hover && !control.__parentControl.drag
+ && !control.__parentControl.sliderDrag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
}
PropertyChanges {
target: mouseArea
@@ -264,27 +268,28 @@ TextInput {
},
State {
name: "globalHover"
- when: myControl.hover && !textInput.hover && !textInput.edit && !myControl.drag
+ when: control.__parentControl.hover && !control.hover && !control.edit
+ && !control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
+ color: control.style.background.globalHover
}
},
State {
name: "hover"
- when: textInput.hover && myControl.hover
- && !textInput.edit && !myControl.drag
+ when: control.hover && control.__parentControl.hover
+ && !control.edit && !control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundHover
+ color: control.style.background.hover
}
},
State {
name: "edit"
- when: textInput.edit && !myControl.drag
+ when: control.edit && !control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ color: control.style.background.interaction
}
PropertyChanges {
target: mouseArea
@@ -293,38 +298,38 @@ TextInput {
},
State {
name: "drag"
- when: myControl.drag
+ when: control.__parentControl.drag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
+ color: control.style.background.interaction
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "sliderDrag"
- when: myControl.sliderDrag
+ when: control.__parentControl.sliderDrag
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackground
+ color: control.style.background.idle
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeInteraction
+ target: control
+ color: control.style.interaction
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
target: textInputBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
+ color: control.style.background.disabled
}
PropertyChanges {
- target: textInput
- color: StudioTheme.Values.themeTextColorDisabled
+ target: control
+ color: control.style.text.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Switch.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Switch.qml
index 092b4633da..8742bfd8c2 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Switch.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/Switch.qml
@@ -1,28 +1,30 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.Switch {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property alias actionIndicator: actionIndicator
// This property is used to indicate the global hover state
- property bool hover: root.hovered && root.enabled
+ property bool hover: control.hovered && control.enabled
property bool edit: false
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
- property alias labelVisible: switchLabel.visible
- property alias labelColor: switchLabel.color
+ property alias labelVisible: label.visible
+ property alias labelColor: label.color
- property alias fontFamily: switchLabel.font.family
- property alias fontPixelSize: switchLabel.font.pixelSize
+ property alias fontFamily: label.font.family
+ property alias fontPixelSize: label.font.pixelSize
font.pixelSize: StudioTheme.Values.myFontSize
@@ -32,15 +34,16 @@ T.Switch {
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
- spacing: StudioTheme.Values.switchSpacing
+ spacing: label.visible ? control.style.controlSpacing : 0
hoverEnabled: true
activeFocusOnTab: false
ActionIndicator {
id: actionIndicator
- myControl: root
- width: actionIndicator.visible ? root.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? root.__actionIndicatorHeight : 0
+ style: control.style
+ __parentControl: control
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
indicator: Rectangle {
@@ -48,12 +51,12 @@ T.Switch {
x: actionIndicator.width
y: 0
z: 5
- implicitWidth: StudioTheme.Values.height * 2
- implicitHeight: StudioTheme.Values.height
- radius: StudioTheme.Values.height * 0.5
- color: StudioTheme.Values.themeControlBackground
- border.width: StudioTheme.Values.border
- border.color: StudioTheme.Values.themeControlOutline
+ implicitWidth: control.style.squareControlSize.width * 2
+ implicitHeight: control.style.squareControlSize.height
+ radius: control.style.squareControlSize.height * 0.5
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
Rectangle {
id: switchIndicator
@@ -61,142 +64,142 @@ T.Switch {
readonly property real gap: 5
property real size: switchBackground.implicitHeight - switchIndicator.gap * 2
- x: root.checked ? parent.width - width - switchIndicator.gap
+ x: control.checked ? parent.width - width - switchIndicator.gap
: switchIndicator.gap
y: switchIndicator.gap
width: switchIndicator.size
height: switchIndicator.size
radius: switchIndicator.size * 0.5
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
border.width: 0
}
}
contentItem: T.Label {
- id: switchLabel
- leftPadding: switchBackground.x + switchBackground.width + root.spacing
+ id: label
+ leftPadding: switchBackground.x + switchBackground.width + control.spacing
rightPadding: 0
verticalAlignment: Text.AlignVCenter
- text: root.text
- font: root.font
- color: StudioTheme.Values.themeTextColor
- visible: text !== ""
+ text: control.text
+ font: control.font
+ color: control.style.text.idle
+ visible: control.text !== ""
}
- property bool __default: root.enabled && !root.hover && !actionIndicator.hover && !root.pressed
- property bool __globalHover: root.enabled && actionIndicator.hover && !root.pressed
- property bool __hover: root.hover && !actionIndicator.hover && !root.pressed
- property bool __press: root.hover && root.pressed
+ property bool __default: control.enabled && !control.hover && !actionIndicator.hover && !control.pressed
+ property bool __globalHover: control.enabled && actionIndicator.hover && !control.pressed
+ property bool __hover: control.hover && !actionIndicator.hover && !control.pressed
+ property bool __press: control.hover && control.pressed
states: [
State {
name: "default"
- when: root.__default && !root.checked
+ when: control.__default && !control.checked
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
target: switchIndicator
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "globalHover"
- when: root.__globalHover && !root.checked
+ when: control.__globalHover && !control.checked
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.globalHover
+ border.color: control.style.border.idle
}
PropertyChanges {
target: switchIndicator
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
}
},
State {
name: "hover"
- when: root.__hover && !root.checked
+ when: control.__hover && !control.checked
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.hover
+ border.color: control.style.border.hover
}
PropertyChanges {
target: switchIndicator
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.hover
}
},
State {
name: "press"
- when: root.__press && !root.checked
+ when: control.__press && !control.checked
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
PropertyChanges {
target: switchIndicator
- color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
}
},
State {
name: "disable"
- when: !root.enabled && !root.checked
+ when: !control.enabled && !control.checked
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
PropertyChanges {
target: switchIndicator
- color: StudioTheme.Values.themeTextColorDisabled
+ color: control.style.icon.disabled
}
PropertyChanges {
- target: switchLabel
- color: StudioTheme.Values.themeTextColorDisabled
+ target: label
+ color: control.style.text.disabled
}
},
State {
name: "defaultChecked"
- when: root.__default && root.checked
+ when: control.__default && control.checked
extend: "default"
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeInteraction
- border.color: StudioTheme.Values.themeInteraction
+ color: control.style.interaction
+ border.color: control.style.interaction
}
},
State {
name: "globalHoverChecked"
- when: root.__globalHover && root.checked
+ when: control.__globalHover && control.checked
extend: "globalHover"
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeInteractionHover
- border.color: StudioTheme.Values.themeInteractionHover
+ color: control.style.interactionHover
+ border.color: control.style.interactionHover
}
},
State {
name: "hoverChecked"
- when: root.__hover && root.checked
+ when: control.__hover && control.checked
extend: "hover"
PropertyChanges {
target: switchBackground
- color: StudioTheme.Values.themeInteractionHover
- border.color: StudioTheme.Values.themeInteractionHover
+ color: control.style.interactionHover
+ border.color: control.style.interactionHover
}
},
State {
name: "pressChecked"
- when: root.__press && root.checked
+ when: control.__press && control.checked
extend: "press"
},
State {
name: "disableChecked"
- when: !root.enabled && root.checked
+ when: !control.enabled && control.checked
extend: "disable"
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabBar.qml
index 7376e2c2e2..8404f9190a 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabBar.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabBar.qml
@@ -1,12 +1,14 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.TabBar {
- id: myButton
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -15,9 +17,9 @@ T.TabBar {
spacing: 0
contentItem: ListView {
- model: myButton.contentModel
- currentIndex: myButton.currentIndex
- spacing: myButton.spacing
+ model: control.contentModel
+ currentIndex: control.currentIndex
+ spacing: control.spacing
orientation: ListView.Horizontal
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.AutoFlickIfNeeded
@@ -25,6 +27,6 @@ T.TabBar {
}
background: Rectangle {
- color: StudioTheme.Values.themePanelBackground
+ color: control.style.panel.background
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabButton.qml
index d448ea5775..4074d54634 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabButton.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TabButton.qml
@@ -1,12 +1,14 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.TabButton {
- id: myButton
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
@@ -17,24 +19,19 @@ T.TabButton {
background: Rectangle {
id: buttonBackground
- color: myButton.checked ? StudioTheme.Values.themeInteraction
- : "transparent"
- border.width: StudioTheme.Values.border
- border.color: StudioTheme.Values.themeInteraction
+ color: control.checked ? control.style.interaction : "transparent"
+ border.width: control.style.borderWidth
+ border.color: control.style.interaction
}
contentItem: T.Label {
id: buttonIcon
- color: myButton.checked ? StudioTheme.Values.themeControlBackground
- : StudioTheme.Values.themeInteraction
- //font.weight: Font.Bold
- //font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.myFontSize
+ color: control.checked ? control.style.background.idle : control.style.interaction
+ font.pixelSize: control.style.baseFontSize
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.fill: parent
renderType: Text.QtRendering
-
- text: myButton.text
+ text: control.text
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextArea.qml
index 47dcfb7eec..abd5f73284 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextArea.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextArea.qml
@@ -1,37 +1,40 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
TextField {
- id: myTextField
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property real relativePopupX: 0 // TODO Maybe call it leftPadding
- property real popupWidth: myTextField.width
+ property real popupWidth: control.width
property string txtStorage
property int temp: 0
T.Popup {
id: popup
- x: myTextField.relativePopupX
- y: myTextField.height - StudioTheme.Values.border
- width: myTextField.popupWidth
+ x: control.relativePopupX
+ y: control.height - control.style.borderWidth
+ width: control.popupWidth
height: scrollView.height
background: Rectangle {
- color: StudioTheme.Values.themePopupBackground
- border.color: StudioTheme.Values.themeInteraction
- border.width: StudioTheme.Values.border
+ color: control.style.popup.background
+ border.color: control.style.interaction
+ border.width: control.style.borderWidth
}
contentItem: ScrollView {
id: scrollView
+ style: control.style
padding: 0
height: Math.min(textAreaPopup.contentHeight + scrollView.topPadding
+ scrollView.bottomPadding,
- StudioTheme.Values.maxTextAreaPopupHeight)
+ control.style.maxTextAreaPopupHeight)
ScrollBar.horizontal.policy: ScrollBar.AlwaysOn
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
@@ -41,10 +44,10 @@ TextField {
width: textAreaPopup.contentWidth + textAreaPopup.leftPadding
+ textAreaPopup.rightPadding
anchors.fill: parent
- font.pixelSize: StudioTheme.Values.myFontSize
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
+ font.pixelSize: control.style.baseFontSize
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
selectByMouse: true
persistentSelection: textAreaPopup.focus
@@ -61,56 +64,60 @@ TextField {
ContextMenu {
id: contextMenu
- myTextEdit: textAreaPopup
+ style: control.style
+ __parentControl: textAreaPopup
}
AbstractButton {
id: acceptButton
+ style: control.style
x: popup.width - acceptButton.width
- y: popup.height - StudioTheme.Values.border
- width: Math.round(StudioTheme.Values.smallRectWidth)
- height: Math.round(StudioTheme.Values.smallRectWidth)
+ y: popup.height - control.style.borderWidth
+ width: Math.round(control.style.smallControlSize.width)
+ height: Math.round(control.style.smallControlSize.height)
buttonIcon: StudioTheme.Constants.tickIcon
}
AbstractButton {
id: discardButton
- x: popup.width - acceptButton.width - discardButton.width + StudioTheme.Values.border
- y: popup.height - StudioTheme.Values.border
- width: Math.round(StudioTheme.Values.smallRectWidth)
- height: Math.round(StudioTheme.Values.smallRectWidth)
+ style: control.style
+ x: popup.width - acceptButton.width - discardButton.width + control.style.borderWidth
+ y: popup.height - control.style.borderWidth
+ width: Math.round(control.style.smallControlSize.width)
+ height: Math.round(control.style.smallControlSize.height)
buttonIcon: StudioTheme.Constants.closeCross
}
- Component.onCompleted: {
- storeAndFormatTextInput(myTextField.text)
- }
+ Component.onCompleted: control.storeAndFormatTextInput(control.text)
onOpened: {
- textAreaPopup.text = txtStorage
- myTextField.clear()
+ textAreaPopup.text = control.txtStorage
+ control.clear()
}
onClosed: {
- storeAndFormatTextInput(textAreaPopup.text)
- myTextField.forceActiveFocus()
+ control.storeAndFormatTextInput(textAreaPopup.text)
+ control.forceActiveFocus()
textAreaPopup.deselect()
}
}
function storeAndFormatTextInput(inputText) {
- txtStorage = inputText
- var pos = txtStorage.search(/\n/g)
+ control.txtStorage = inputText
+ var pos = control.txtStorage.search(/\n/g)
var sliceAt = Math.min(pos, 15)
- myTextField.text = txtStorage.slice(0, sliceAt).padEnd(sliceAt + 3, '.')
+ control.text = control.txtStorage.slice(0, sliceAt).padEnd(sliceAt + 3, '.')
}
Keys.onPressed: function(event) {
- if (event.key === Qt.Key_Escape)
- popup.opened ? popup.close() : myTextField.focus = false
+ if (event.key === Qt.Key_Escape) {
+ if (popup.opened)
+ popup.close()
+ else
+ control.focus = false
+ }
- if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
- && !popup.opened) {
+ if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !popup.opened) {
popup.open()
textAreaPopup.forceActiveFocus()
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml
index 517bf0f4eb..0180aa06ad 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml
@@ -1,27 +1,29 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
T.TextField {
- id: root
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
// This property is used to indicate the global hover state
property bool hover: (actionIndicator.hover || mouseArea.containsMouse || indicator.hover
- || translationIndicator.hover) && root.enabled
- property bool edit: root.activeFocus
+ || translationIndicator.hover) && control.enabled
+ property bool edit: control.activeFocus
property alias actionIndicator: actionIndicator
property alias actionIndicatorVisible: actionIndicator.visible
- property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth
- property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight
+ property real __actionIndicatorWidth: control.style.actionIndicatorSize.width
+ property real __actionIndicatorHeight: control.style.actionIndicatorSize.height
property alias translationIndicator: translationIndicator
property alias translationIndicatorVisible: translationIndicator.visible
- property real __translationIndicatorWidth: StudioTheme.Values.translationIndicatorWidth
- property real __translationIndicatorHeight: StudioTheme.Values.translationIndicatorHeight
+ property real __translationIndicatorWidth: control.style.squareControlSize.width
+ property real __translationIndicatorHeight: control.style.squareControlSize.height
property alias indicator: indicator
property alias indicatorVisible: indicator.visible
@@ -35,24 +37,24 @@ T.TextField {
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
- font.pixelSize: StudioTheme.Values.myFontSize
+ font.pixelSize: control.style.baseFontSize
- color: StudioTheme.Values.themeTextColor
- selectionColor: StudioTheme.Values.themeTextSelectionColor
- selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
+ color: control.style.text.idle
+ selectionColor: control.style.text.selection
+ selectedTextColor: control.style.text.selectedText
+ placeholderTextColor: control.style.text.placeholder
readOnly: false
selectByMouse: true
- persistentSelection: contextMenu.visible || root.focus
+ persistentSelection: contextMenu.visible || control.focus
clip: true
- width: StudioTheme.Values.defaultControlWidth
- height: StudioTheme.Values.defaultControlHeight
- implicitHeight: StudioTheme.Values.defaultControlHeight
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
+ implicitHeight: control.style.controlSize.height
- leftPadding: StudioTheme.Values.inputHorizontalPadding + actionIndicator.width
- rightPadding: StudioTheme.Values.inputHorizontalPadding + translationIndicator.width + indicator.width
+ leftPadding: control.style.inputHorizontalPadding + actionIndicator.width
+ rightPadding: control.style.inputHorizontalPadding + translationIndicator.width + indicator.width
MouseArea {
id: mouseArea
@@ -66,80 +68,84 @@ T.TextField {
onPressed: function(event) {
if (event.button === Qt.RightButton)
- contextMenu.popup(root)
+ contextMenu.popup(control)
}
ContextMenu {
id: contextMenu
- myTextEdit: root
+ style: control.style
+ __parentControl: control
- onClosed: root.forceActiveFocus()
- onAboutToShow: root.contextMenuAboutToShow = true
- onAboutToHide: root.contextMenuAboutToShow = false
+ onClosed: control.forceActiveFocus()
+ onAboutToShow: control.contextMenuAboutToShow = true
+ onAboutToHide: control.contextMenuAboutToShow = false
}
onActiveFocusChanged: {
// OtherFocusReason in this case means, if the TextField gets focus after the context menu
// was closed due to an menu item click.
- if (root.activeFocus && root.focusReason !== Qt.OtherFocusReason)
- root.preFocusText = root.text
+ if (control.activeFocus && control.focusReason !== Qt.OtherFocusReason)
+ control.preFocusText = control.text
}
onEditChanged: {
- if (root.edit)
+ if (control.edit)
contextMenu.close()
}
- onEditingFinished: root.focus = false
+ onEditingFinished: control.focus = false
ActionIndicator {
id: actionIndicator
- myControl: root
+ style: control.style
+ __parentControl: control
x: 0
y: 0
- width: actionIndicator.visible ? root.__actionIndicatorWidth : 0
- height: actionIndicator.visible ? root.__actionIndicatorHeight : 0
+ width: actionIndicator.visible ? control.__actionIndicatorWidth : 0
+ height: actionIndicator.visible ? control.__actionIndicatorHeight : 0
}
Text {
id: placeholder
- x: root.leftPadding
- y: root.topPadding
- width: root.width - (root.leftPadding + root.rightPadding)
- height: root.height - (root.topPadding + root.bottomPadding)
-
- text: root.placeholderText
- font: root.font
- color: root.placeholderTextColor
- verticalAlignment: root.verticalAlignment
- visible: !root.length && !root.preeditText
- && (!root.activeFocus || root.horizontalAlignment !== Qt.AlignHCenter)
+ x: control.leftPadding
+ y: control.topPadding
+ width: control.width - (control.leftPadding + control.rightPadding)
+ height: control.height - (control.topPadding + control.bottomPadding)
+
+ text: control.placeholderText
+ font: control.font
+ color: control.placeholderTextColor
+ verticalAlignment: control.verticalAlignment
+ visible: !control.length && !control.preeditText
+ && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
elide: Text.ElideRight
- renderType: root.renderType
+ renderType: control.renderType
}
background: Rectangle {
id: textFieldBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
x: actionIndicator.width
- width: root.width - actionIndicator.width
- height: root.height
+ width: control.width - actionIndicator.width
+ height: control.height
}
Indicator {
id: indicator
+ style: control.style
visible: false
- x: root.width - translationIndicator.width - indicator.width
- width: indicator.visible ? root.height : 0
- height: indicator.visible ? root.height : 0
+ x: control.width - translationIndicator.width - indicator.width
+ width: indicator.visible ? control.height : 0
+ height: indicator.visible ? control.height : 0
}
TranslationIndicator {
id: translationIndicator
- myControl: root
- x: root.width - translationIndicator.width
+ style: control.style
+ __parentControl: control
+ x: control.width - translationIndicator.width
width: translationIndicator.visible ? __translationIndicatorWidth : 0
height: translationIndicator.visible ? __translationIndicatorHeight : 0
}
@@ -147,16 +153,16 @@ T.TextField {
states: [
State {
name: "default"
- when: root.enabled && !root.hover && !root.edit && !contextMenu.visible
+ when: control.enabled && !control.hover && !control.edit && !contextMenu.visible
PropertyChanges {
target: textFieldBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
PropertyChanges {
- target: root
- color: StudioTheme.Values.themeTextColor
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
+ target: control
+ color: control.style.text.idle
+ placeholderTextColor: control.style.text.placeholder
}
PropertyChanges {
target: mouseArea
@@ -166,45 +172,45 @@ T.TextField {
State {
name: "globalHover"
when: (actionIndicator.hover || translationIndicator.hover || indicator.hover)
- && !root.edit && root.enabled && !contextMenu.visible
+ && !control.edit && control.enabled && !contextMenu.visible
PropertyChanges {
target: textFieldBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.globalHover
+ border.color: control.style.border.idle
}
PropertyChanges {
- target: root
- color: StudioTheme.Values.themeTextColor
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
+ target: control
+ color: control.style.text.idle
+ placeholderTextColor: control.style.text.placeholder
}
},
State {
name: "hover"
when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover
- && !indicator.hover && !root.edit && root.enabled && !contextMenu.visible
+ && !indicator.hover && !control.edit && control.enabled && !contextMenu.visible
PropertyChanges {
target: textFieldBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ color: control.style.background.hover
+ border.color: control.style.border.hover
}
PropertyChanges {
- target: root
- color: StudioTheme.Values.themeTextColor
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor
+ target: control
+ color: control.style.text.hover
+ placeholderTextColor: control.style.text.placeholder
}
},
State {
name: "edit"
- when: root.edit || contextMenu.visible
+ when: control.edit || contextMenu.visible
PropertyChanges {
target: textFieldBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
PropertyChanges {
- target: root
- color: StudioTheme.Values.themeTextColor
- placeholderTextColor: StudioTheme.Values.themePlaceholderTextColorInteraction
+ target: control
+ color: control.style.text.idle
+ placeholderTextColor: control.style.text.placeholder
}
PropertyChanges {
target: mouseArea
@@ -213,24 +219,24 @@ T.TextField {
},
State {
name: "disable"
- when: !root.enabled
+ when: !control.enabled
PropertyChanges {
target: textFieldBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
PropertyChanges {
- target: root
- color: StudioTheme.Values.themeTextColorDisabled
- placeholderTextColor: StudioTheme.Values.themeTextColorDisabled
+ target: control
+ color: control.style.text.disabled
+ placeholderTextColor: control.style.text.disabled
}
}
]
Keys.onEscapePressed: function(event) {
event.accepted = true
- root.text = root.preFocusText
- root.rejected()
- root.focus = false
+ control.text = control.preFocusText
+ control.rejected()
+ control.focus = false
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTip.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTip.qml
index 0c99890e6b..b39fb6881c 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTip.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ToolTip.qml
@@ -1,27 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Templates as T
@@ -30,8 +8,10 @@ import StudioTheme 1.0 as StudioTheme
T.ToolTip {
id: control
- x: parent ? (parent.width - implicitWidth) / 2 : 0
- y: -implicitHeight - 3
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ x: parent ? (parent.width - control.implicitWidth) / 2 : 0
+ y: -control.implicitHeight - 3
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
@@ -42,19 +22,20 @@ T.ToolTip {
padding: 4
delay: 1000
timeout: 5000
- closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent
+ closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent
+ | T.Popup.CloseOnReleaseOutsideParent
contentItem: Text {
text: control.text
wrapMode: Text.Wrap
- font.pixelSize: StudioTheme.Values.myFontSize
- color: StudioTheme.Values.themeToolTipText
+ font.pixelSize: control.style.baseFontSize
+ color: control.style.toolTip.text
}
background: Rectangle {
- color: StudioTheme.Values.themeToolTipBackground
- border.width: StudioTheme.Values.border
- border.color: StudioTheme.Values.themeToolTipOutline
+ color: control.style.toolTip.background
+ border.width: control.style.borderWidth
+ border.color: control.style.toolTip.border
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml
new file mode 100644
index 0000000000..1dddfec0b4
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml
@@ -0,0 +1,266 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Templates as T
+import StudioTheme 1.0 as StudioTheme
+import QtQuickDesignerTheme 1.0
+
+T.ComboBox {
+ id: control
+
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
+
+ property bool hover: (comboBoxInput.hover || window.visible || popupIndicator.hover)
+ && control.enabled
+ property bool edit: false
+ property bool open: window.visible
+ property bool openUpwards: false
+ property alias suffix: comboBoxInput.suffix
+
+ editable: false
+ width: control.style.controlSize.width
+ height: control.style.controlSize.height
+
+ leftPadding: 0
+ rightPadding: popupIndicator.width + control.style.borderWidth
+ font.pixelSize: control.style.baseFontSize
+
+ delegate: ItemDelegate {
+ required property int index
+ required property var modelData
+
+ width: control.width
+ highlighted: control.highlightedIndex === index
+ contentItem: Text {
+ text: modelData
+ color: "#21be2b"
+ font: control.font
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ indicator: CheckIndicator {
+ id: popupIndicator
+ style: control.style
+ __parentControl: control
+ __parentPopup: comboBoxPopup
+ x: comboBoxInput.x + comboBoxInput.width
+ y: control.style.borderWidth
+ width: control.style.squareControlSize.width - control.style.borderWidth
+ height: control.style.squareControlSize.height - control.style.borderWidth * 2
+ }
+
+ contentItem: ComboBoxInput {
+ id: comboBoxInput
+ style: control.style
+ __parentControl: control
+ text: control.editText
+ }
+
+ background: Rectangle {
+ id: comboBoxBackground
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
+ width: control.width
+ height: control.height
+ }
+
+ popup: T.Popup {
+ id: comboBoxPopup
+ width: 0
+ height: 0
+ closePolicy: T.Popup.CloseOnEscape
+ onAboutToShow: {
+ control.listView.parent = window.contentItem
+ control.listView.visible = true
+
+ var originMapped = control.mapToGlobal(0,0)
+
+ if (control.openUpwards) {
+ window.x = originMapped.x + 1 // This is a workaround for the status bar
+ window.y = originMapped.y - window.height
+ } else {
+ window.x = originMapped.x
+ window.y = originMapped.y + control.height
+ }
+
+ window.show()
+ window.requestActivate()
+
+ control.listView.focus = true
+ }
+
+ onAboutToHide: window.hide()
+ }
+
+ // Close popup when application goes to background
+ Connections {
+ target: Qt.application
+ function onStateChanged() {
+ if (Qt.application.state === Qt.ApplicationInactive)
+ comboBoxPopup.close()
+ }
+ }
+
+ Window {
+ id: window
+ width: control.listView.width
+ height: control.listView.height + 2 * control.style.borderWidth
+ visible: false
+ flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.NoDropShadowWindowHint
+ modality: Qt.NonModal
+ transientParent: null
+ color: "transparent"
+
+ onActiveFocusItemChanged: {
+ if (window.activeFocusItem === null && !comboBoxInput.hover
+ && !popupIndicator.hover && comboBoxPopup.opened)
+ comboBoxPopup.close()
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: control.style.popup.background
+ }
+ }
+
+ property ListView listView: ListView {
+ x: 0
+ y: control.style.borderWidth
+ width: control.width
+ height: control.listView.contentHeight
+ interactive: false
+ model: control.model
+ Keys.onEscapePressed: comboBoxPopup.close()
+ currentIndex: control.highlightedIndex
+
+ delegate: ItemDelegate {
+ id: itemDelegate
+
+ onClicked: {
+ control.currentIndex = index
+ control.activated(index)
+ comboBoxPopup.close()
+ }
+
+ width: control.width
+ height: control.style.controlSize.height
+ padding: 0
+
+ contentItem: Text {
+ leftPadding: itemDelegateIconArea.width
+ text: modelData
+ color: {
+ if (!itemDelegate.enabled)
+ return control.style.text.disabled
+
+ return itemDelegate.hovered ? control.style.text.selectedText
+ : control.style.text.idle
+ }
+ font: control.font
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Item {
+ id: itemDelegateIconArea
+ width: itemDelegate.height
+ height: itemDelegate.height
+
+ T.Label {
+ id: itemDelegateIcon
+ text: StudioTheme.Constants.tickIcon
+ color: itemDelegate.hovered ? control.style.text.selectedText
+ : control.style.text.idle
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: control.style.smallIconFontSize
+ visible: control.currentIndex === index
+ anchors.fill: parent
+ renderType: Text.NativeRendering
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ background: Rectangle {
+ id: itemDelegateBackground
+ x: control.style.borderWidth
+ y: 0
+ width: itemDelegate.width - 2 * control.style.borderWidth
+ height: itemDelegate.height
+ color: itemDelegate.hovered ? control.style.interaction : "transparent"
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: "default"
+ when: control.enabled && !control.hover && !control.edit && !control.open
+ && !control.activeFocus && !control.hasActiveDrag
+ PropertyChanges {
+ target: control
+ wheelEnabled: false
+ }
+ PropertyChanges {
+ target: comboBoxInput
+ selectByMouse: false
+ }
+ PropertyChanges {
+ target: comboBoxBackground
+ border.color: control.style.border.idle
+ }
+ },
+ State {
+ name: "hover"
+ when: control.enabled && control.hover && !control.edit && !control.open
+ && !control.activeFocus && !control.hasActiveDrag
+ PropertyChanges {
+ target: comboBoxBackground
+ border.color: control.style.border.hover
+ }
+ },
+ // This state is intended for ComboBoxes which aren't editable, but have focus e.g. via
+ // tab focus. It is therefor possible to use the mouse wheel to scroll through the items.
+ State {
+ name: "focus"
+ when: control.enabled && control.activeFocus && !control.editable && !control.open
+ PropertyChanges {
+ target: control
+ wheelEnabled: true
+ }
+ PropertyChanges {
+ target: comboBoxInput
+ focus: true
+ }
+ },
+ State {
+ name: "popup"
+ when: control.enabled && control.open
+ PropertyChanges {
+ target: control
+ wheelEnabled: true
+ }
+ PropertyChanges {
+ target: comboBoxInput
+ selectByMouse: false
+ readOnly: true
+ }
+ PropertyChanges {
+ target: comboBoxBackground
+ border.color: control.style.border.interaction
+ }
+ },
+ State {
+ name: "disable"
+ when: !control.enabled
+ PropertyChanges {
+ target: comboBoxBackground
+ border.color: control.style.border.disabled
+ }
+ }
+ ]
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TranslationIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TranslationIndicator.qml
index 09d00eb901..8b3962ffcd 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TranslationIndicator.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TranslationIndicator.qml
@@ -1,31 +1,33 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Templates 2.15 as T
+import QtQuick
+import QtQuick.Templates as T
import StudioTheme 1.0 as StudioTheme
Item {
- id: translationIndicator
+ id: control
- property Item myControl
+ property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
- property bool hover: translationIndicatorMouseArea.containsMouse && translationIndicator.enabled
- property bool pressed: translationIndicatorMouseArea.pressed
+ property Item __parentControl
+
+ property bool hover: mouseArea.containsMouse && control.enabled
+ property bool pressed: mouseArea.pressed
property bool checked: false
signal clicked
Rectangle {
- id: translationIndicatorBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
- border.width: StudioTheme.Values.border
+ id: background
+ color: control.style.background.idle
+ border.color: control.style.border.idle
+ border.width: control.style.borderWidth
anchors.centerIn: parent
- width: matchParity(translationIndicator.height, StudioTheme.Values.smallRectWidth)
- height: matchParity(translationIndicator.height, StudioTheme.Values.smallRectWidth)
+ width: background.matchParity(control.height, control.style.smallControlSize.width)
+ height: background.matchParity(control.height, control.style.smallControlSize.height)
function matchParity(root, value) {
var v = Math.round(value)
@@ -37,23 +39,23 @@ Item {
}
MouseArea {
- id: translationIndicatorMouseArea
+ id: mouseArea
anchors.fill: parent
hoverEnabled: true
onPressed: function(mouse) { mouse.accepted = true }
onClicked: {
- translationIndicator.checked = !translationIndicator.checked
- translationIndicator.clicked()
+ control.checked = !control.checked
+ control.clicked()
}
}
}
T.Label {
- id: translationIndicatorIcon
+ id: icon
text: "tr"
- color: StudioTheme.Values.themeTextColor
+ color: control.style.icon.idle
font.family: StudioTheme.Constants.font.family
- font.pixelSize: StudioTheme.Values.myIconFontSize
+ font.pixelSize: control.style.baseIconFontSize
font.italic: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
@@ -62,36 +64,35 @@ Item {
states: [
State {
name: "default"
- when: translationIndicator.enabled && !translationIndicator.pressed
- && !translationIndicator.checked
+ when: control.enabled && !control.pressed && !control.checked
PropertyChanges {
- target: translationIndicatorIcon
- color: StudioTheme.Values.themeIconColor
+ target: icon
+ color: control.style.icon.idle
}
},
State {
name: "press"
- when: translationIndicator.enabled && translationIndicator.pressed
+ when: control.enabled && control.pressed
PropertyChanges {
- target: translationIndicatorIcon
- color: StudioTheme.Values.themeIconColorInteraction
+ target: icon
+ color: control.style.icon.interaction
}
},
State {
name: "check"
- when: translationIndicator.enabled && !translationIndicator.pressed
- && translationIndicator.checked
+ when: control.enabled && !control.pressed
+ && control.checked
PropertyChanges {
- target: translationIndicatorIcon
- color: StudioTheme.Values.themeIconColorSelected
+ target: icon
+ color: control.style.icon.selected
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
- target: translationIndicatorIcon
- color: StudioTheme.Values.themeTextColorDisabled
+ target: icon
+ color: control.style.icon.disabled
}
}
]
@@ -100,49 +101,49 @@ Item {
states: [
State {
name: "default"
- when: myControl.enabled && !translationIndicator.hover
- && !translationIndicator.pressed && !myControl.hover
- && !myControl.edit && !translationIndicator.checked
+ when: control.__parentControl.enabled && !control.hover && !control.pressed
+ && !control.__parentControl.hover && !control.__parentControl.edit
+ && !control.checked
PropertyChanges {
- target: translationIndicatorBackground
- color: StudioTheme.Values.themeControlBackground
- border.color: StudioTheme.Values.themeControlOutline
+ target: background
+ color: control.style.background.idle
+ border.color: control.style.border.idle
}
},
State {
name: "globalHover"
- when: myControl.hover && !translationIndicator.hover
+ when: control.__parentControl.hover && !control.hover
PropertyChanges {
- target: translationIndicatorBackground
- color: StudioTheme.Values.themeControlBackgroundGlobalHover
- border.color: StudioTheme.Values.themeControlOutline
+ target: background
+ color: control.style.background.globalHover
+ border.color: control.style.border.idle
}
},
State {
name: "hover"
- when: translationIndicator.hover && !translationIndicator.pressed
+ when: control.hover && !control.pressed
PropertyChanges {
- target: translationIndicatorBackground
- color: StudioTheme.Values.themeControlBackgroundHover
- border.color: StudioTheme.Values.themeControlOutline
+ target: background
+ color: control.style.background.hover
+ border.color: control.style.border.idle
}
},
State {
name: "press"
- when: translationIndicator.hover && translationIndicator.pressed
+ when: control.hover && control.pressed
PropertyChanges {
- target: translationIndicatorBackground
- color: StudioTheme.Values.themeControlBackgroundInteraction
- border.color: StudioTheme.Values.themeControlOutlineInteraction
+ target: background
+ color: control.style.background.interaction
+ border.color: control.style.border.interaction
}
},
State {
name: "disable"
- when: !myControl.enabled
+ when: !control.__parentControl.enabled
PropertyChanges {
- target: translationIndicatorBackground
- color: StudioTheme.Values.themeControlBackgroundDisabled
- border.color: StudioTheme.Values.themeControlOutlineDisabled
+ target: background
+ color: control.style.background.disabled
+ border.color: control.style.border.disabled
}
}
]
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml
index 2c23fa33dd..752f2bc6e4 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml
@@ -1,38 +1,38 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
-import QtQuick.Controls 2.15
+import QtQuick
+//import QtQuick.Controls
import StudioTheme 1.0 as StudioTheme
ScrollBar {
- id: scrollBar
+ id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
- property bool scrollBarVisible: parent.contentHeight > scrollBar.height
+ property bool scrollBarVisible: parent.contentHeight > control.height
- minimumSize: scrollBar.width / scrollBar.height
+ minimumSize: control.width / control.height
orientation: Qt.Vertical
- policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
+ policy: control.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
height: parent.availableHeight
- (parent.bothVisible ? parent.horizontalThickness : 0)
- padding: scrollBar.active ? StudioTheme.Values.scrollBarActivePadding
- : StudioTheme.Values.scrollBarInactivePadding
+ padding: control.active ? control.style.scrollBarActivePadding
+ : control.style.scrollBarInactivePadding
background: Rectangle {
- implicitWidth: StudioTheme.Values.scrollBarThickness
- implicitHeight: StudioTheme.Values.scrollBarThickness
- color: StudioTheme.Values.themeScrollBarTrack
+ implicitWidth: control.style.scrollBarThickness
+ implicitHeight: control.style.scrollBarThickness
+ color: control.style.scrollBar.track
}
contentItem: Rectangle {
- implicitWidth: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
- implicitHeight: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
- color: StudioTheme.Values.themeScrollBarHandle
+ implicitWidth: control.style.scrollBarThickness - 2 * control.padding
+ implicitHeight: control.style.scrollBarThickness - 2 * control.padding
+ color: control.style.scrollBar.handle
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir
index fea940d878..d6071b6ff2 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir
@@ -49,3 +49,4 @@ TextField 1.0 TextField.qml
ToolTip 1.0 ToolTip.qml
TranslationIndicator 1.0 TranslationIndicator.qml
VerticalScrollBar 1.0 VerticalScrollBar.qml
+TopLevelComboBox 1.0 TopLevelComboBox.qml
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml
index 6bbaf1cb35..750fb77d35 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml
@@ -1,7 +1,7 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 Singleton
-import QtQuick 2.10
+import QtQuick
InternalConstants {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml
new file mode 100644
index 0000000000..f8e959d825
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ControlStyle.qml
@@ -0,0 +1,190 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+QtObject {
+ id: root
+
+ property real smallFontSize: Values.smallFontSize
+ property real baseFontSize: Values.baseFontSize
+ property real mediumFontSize: Values.mediumFontSize
+ property real bigFontSize: Values.bigFontSize
+
+ property real smallIconFontSize: Values.smallIconFontSize
+ property real baseIconFontSize: Values.baseIconFontSize
+ property real mediumIconFontSize: Values.mediumIconFontSize
+ property real bigIconFontSize: Values.bigIconFontSize
+
+ property real borderWidth: Values.border
+ property real radius: Values.radius
+
+ property size smallControlSize: Qt.size(Values.smallRectWidth,
+ Values.smallRectWidth)
+ property size squareControlSize: Qt.size(root.controlSize.height,
+ root.controlSize.height)
+ property size controlSize: Qt.size(Values.defaultControlWidth,
+ Values.defaultControlHeight)
+
+ property size smallIconSize: Qt.size(Values.spinControlIconSizeMulti,
+ Values.spinControlIconSizeMulti)
+
+ // TODO only used once
+ property size spinBoxIndicatorSize: Qt.size(Values.spinBoxIndicatorWidth,
+ Values.spinBoxIndicatorHeight)
+
+ property size actionIndicatorSize: Qt.size(Values.actionIndicatorWidth,
+ Values.actionIndicatorHeight)
+
+ property size indicatorIconSize: Qt.size(Values.iconAreaWidth,
+ Values.height)
+
+ property size radioButtonIndicatorSize: Qt.size(Values.radioButtonIndicatorWidth,
+ Values.radioButtonIndicatorHeight)
+
+ // Special properties
+ property real maxTextAreaPopupHeight: Values.maxTextAreaPopupHeight
+ property real maxComboBoxPopupHeight: Values.maxComboBoxPopupHeight
+ property real inputHorizontalPadding: Values.inputHorizontalPadding
+ property real controlSpacing: Values.checkBoxSpacing
+ property real dialogPadding: Values.dialogPadding
+ property real dialogButtonSpacing: Values.dialogButtonSpacing
+
+ property real contextMenuLabelSpacing: Values.contextMenuLabelSpacing
+ property real contextMenuHorizontalPadding: Values.contextMenuHorizontalPadding
+
+ property real sliderTrackHeight: Values.sliderTrackHeight
+ property size sliderHandleSize: Qt.size(Values.sliderHandleWidth,
+ Values.sliderHandleHeight)
+ property real sliderFontSize: Values.sliderFontSize
+ property real sliderPadding: Values.sliderPadding
+ property real sliderMargin: Values.sliderMargin
+ property size sliderPointerSize: Qt.size(Values.sliderPointerWidth,
+ Values.sliderPointerHeight)
+
+ property real sectionColumnSpacing: Values.sectionColumnSpacing
+ property real sectionRowSpacing: Values.sectionRowSpacing
+ property real sectionHeadHeight: Values.sectionHeadHeight
+ property real sectionHeadSpacerHeight: Values.sectionHeadSpacerHeight
+
+ property real scrollBarThickness: Values.scrollBarThickness
+ property real scrollBarActivePadding: Values.scrollBarActivePadding
+ property real scrollBarInactivePadding: Values.scrollBarInactivePadding
+
+ // Special colors
+ property color interaction: Values.themeInteraction
+ property color interactionHover: Values.themeInteractionHover
+ // TODO needs to removed in the future
+ property color thumbnailLabelBackground: Values.themeThumbnailLabelBackground
+
+ // Colors
+ component BackgroundColors: QtObject {
+ property color idle: Values.themeControlBackground
+ property color interaction: Values.themeControlBackgroundInteraction
+ property color globalHover: Values.themeControlBackground_toolbarHover
+ property color hover: Values.themeControlBackground_toolbarHover
+ property color disabled: Values.themeControlBackgroundDisabled
+ }
+
+ property BackgroundColors background: BackgroundColors {}
+
+ component BorderColors: QtObject {
+ property color idle: Values.themeControlOutline
+ property color interaction: Values.themeControlOutlineInteraction
+ property color hover: Values.controlOutline_toolbarHover
+ property color disabled: Values.themeControlBackground
+ }
+
+ property BorderColors border: BorderColors {}
+
+ component TextColors: QtObject {
+ property color idle: Values.themeTextColor
+ property color interaction: Values.themeTextSelectedTextColor
+ property color hover: Values.themeTextColor
+ property color disabled: Values.themeTextColorDisabled
+ property color selection: Values.themeTextSelectionColor
+ property color selectedText: Values.themeTextSelectedTextColor
+ property color placeholder: Values.themePlaceholderTextColor
+ property color placeholderHover: Values.themePlaceholderTextColor
+ property color placeholderInteraction: Values.themePlaceholderTextColorInteraction
+ }
+
+ property TextColors text: TextColors {}
+
+ component IconColors: QtObject {
+ property color idle: Values.themeIconColor
+ property color interaction: Values.themeIconColor
+ property color selected: Values.themeIconColorSelected
+ property color hover: Values.themeIconColorHover
+ property color disabled: Values.themeIconColorDisabled
+ }
+
+ property IconColors icon: IconColors {}
+
+ // Link- and InfinityLoopIndicator
+ component IndicatorColors: QtObject {
+ property color idle: Values.themeLinkIndicatorColor
+ property color interaction: Values.themeLinkIndicatorColorHover
+ property color hover: Values.themeLinkIndicatorColorInteraction
+ property color disabled: Values.themeLinkIndicatorColorDisabled
+ }
+
+ property IndicatorColors indicator: IndicatorColors {}
+
+ component SliderColors: QtObject {
+ property color activeTrack: Values.themeSliderActiveTrack
+ property color activeTrackHover: Values.themeSliderActiveTrackHover
+ property color activeTrackFocus: Values.themeSliderActiveTrackFocus
+ property color inactiveTrack: Values.themeSliderInactiveTrack
+ property color inactiveTrackHover: Values.themeSliderInactiveTrackHover
+ property color inactiveTrackFocus: Values.themeSliderInactiveTrackFocus
+ property color handle: Values.themeSliderHandle
+ property color handleHover: Values.themeSliderHandleHover
+ property color handleFocus: Values.themeSliderHandleFocus
+ property color handleInteraction: Values.themeSliderHandleInteraction
+ }
+
+ property SliderColors slider: SliderColors {}
+
+ component ScrollBarColors: QtObject {
+ property color track: Values.themeScrollBarTrack
+ property color handle: Values.themeScrollBarHandle
+ }
+
+ property ScrollBarColors scrollBar: ScrollBarColors {}
+
+ component SectionColors: QtObject {
+ property color head: Values.themeSectionHeadBackground
+ }
+
+ property SectionColors section: SectionColors {}
+
+ component PanelColors: QtObject {
+ property color background: Values.themePanelBackground
+ }
+
+ property PanelColors panel: PanelColors {}
+
+ component PopupColors: QtObject {
+ property color background: Values.themePopupBackground
+ property color overlay: Values.themePopupOverlayColor
+ }
+
+ property PopupColors popup: PopupColors {}
+
+ component DialogColors: QtObject {
+ property color background: Values.themeDialogBackground
+ property color border: Values.themeDialogOutline
+ property color overlay: Values.themeDialogBackground
+ }
+
+ property DialogColors dialog: DialogColors {}
+
+ component ToolTipColors: QtObject {
+ property color background: Values.themeToolTipBackground
+ property color border: Values.themeToolTipOutline
+ property color text: Values.themeToolTipText
+ }
+
+ property ToolTipColors toolTip: ToolTipColors {}
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/DefaultStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/DefaultStyle.qml
new file mode 100644
index 0000000000..46c25ad4d5
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/DefaultStyle.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml
index 3df7a9e5dd..1a07944b24 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml
@@ -1,7 +1,7 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 2.15
+import QtQuick
QtObject {
readonly property int width: 1920
@@ -21,183 +21,321 @@ QtObject {
readonly property string addRowAfter: "\u0026"
readonly property string addRowBefore: "\u0027"
readonly property string addTable: "\u0028"
- readonly property string adsClose: "\u0029"
- readonly property string adsDetach: "\u002A"
- readonly property string adsDropDown: "\u002B"
- readonly property string alias: "\u002C"
- readonly property string aliasAnimated: "\u002D"
- readonly property string alignBottom: "\u002E"
- readonly property string alignCenterHorizontal: "\u002F"
- readonly property string alignCenterVertical: "\u0030"
- readonly property string alignLeft: "\u0031"
- readonly property string alignRight: "\u0032"
- readonly property string alignTo: "\u0033"
- readonly property string alignTop: "\u0034"
- readonly property string anchorBaseline: "\u0035"
- readonly property string anchorBottom: "\u0036"
- readonly property string anchorFill: "\u0037"
- readonly property string anchorLeft: "\u0038"
- readonly property string anchorRight: "\u0039"
- readonly property string anchorTop: "\u003A"
- readonly property string animatedProperty: "\u003B"
- readonly property string annotationBubble: "\u003C"
- readonly property string annotationDecal: "\u003D"
- readonly property string applyMaterialToSelected: "\u003E"
- readonly property string assign: "\u003F"
- readonly property string bevelAll: "\u0040"
- readonly property string bevelCorner: "\u0041"
- readonly property string centerHorizontal: "\u0042"
- readonly property string centerVertical: "\u0043"
- readonly property string closeCross: "\u0044"
- readonly property string closeLink: "\u0045"
- readonly property string colorPopupClose: "\u0046"
- readonly property string columnsAndRows: "\u0047"
- readonly property string copyLink: "\u0048"
- readonly property string copyStyle: "\u0049"
- readonly property string cornerA: "\u004A"
- readonly property string cornerB: "\u004B"
- readonly property string cornersAll: "\u004C"
- readonly property string curveDesigner: "\u004D"
- readonly property string curveEditor: "\u004E"
- readonly property string customMaterialEditor: "\u004F"
- readonly property string decisionNode: "\u0050"
- readonly property string deleteColumn: "\u0051"
- readonly property string deleteMaterial: "\u0052"
- readonly property string deleteRow: "\u0053"
- readonly property string deleteTable: "\u0054"
- readonly property string detach: "\u0055"
- readonly property string distributeBottom: "\u0056"
- readonly property string distributeCenterHorizontal: "\u0057"
- readonly property string distributeCenterVertical: "\u0058"
- readonly property string distributeLeft: "\u0059"
- readonly property string distributeOriginBottomRight: "\u005A"
- readonly property string distributeOriginCenter: "\u005B"
- readonly property string distributeOriginNone: "\u005C"
- readonly property string distributeOriginTopLeft: "\u005D"
- readonly property string distributeRight: "\u005E"
- readonly property string distributeSpacingHorizontal: "\u005F"
- readonly property string distributeSpacingVertical: "\u0060"
- readonly property string distributeTop: "\u0061"
- readonly property string download: "\u0062"
- readonly property string downloadUnavailable: "\u0063"
- readonly property string downloadUpdate: "\u0064"
- readonly property string downloaded: "\u0065"
- readonly property string edit: "\u0066"
- readonly property string eyeDropper: "\u0067"
- readonly property string favorite: "\u0068"
- readonly property string flowAction: "\u0069"
- readonly property string flowTransition: "\u006A"
- readonly property string fontStyleBold: "\u006B"
- readonly property string fontStyleItalic: "\u006C"
- readonly property string fontStyleStrikethrough: "\u006D"
- readonly property string fontStyleUnderline: "\u006E"
- readonly property string gradient: "\u006F"
- readonly property string gridView: "\u0070"
- readonly property string idAliasOff: "\u0071"
- readonly property string idAliasOn: "\u0072"
- readonly property string imported: "\u0073"
- readonly property string infinity: "\u0074"
- readonly property string keyframe: "\u0075"
- readonly property string linkTriangle: "\u0076"
- readonly property string linked: "\u0077"
- readonly property string listView: "\u0078"
- readonly property string lockOff: "\u0079"
- readonly property string lockOn: "\u007A"
- readonly property string materialPreviewEnvironment: "\u007B"
- readonly property string materialPreviewModel: "\u007C"
- readonly property string mergeCells: "\u007D"
- readonly property string minus: "\u007E"
- readonly property string mirror: "\u007F"
- readonly property string newMaterial: "\u0080"
- readonly property string openLink: "\u0081"
- readonly property string openMaterialBrowser: "\u0082"
- readonly property string orientation: "\u0083"
- readonly property string paddingEdge: "\u0084"
- readonly property string paddingFrame: "\u0085"
- readonly property string pasteStyle: "\u0086"
- readonly property string pause: "\u0087"
- readonly property string pin: "\u0088"
- readonly property string play: "\u0089"
- readonly property string plus: "\u008A"
- readonly property string promote: "\u008B"
- readonly property string readOnly: "\u008C"
- readonly property string redo: "\u008D"
- readonly property string rotationFill: "\u008E"
- readonly property string rotationOutline: "\u008F"
- readonly property string s_anchors: "\u0090"
- readonly property string s_annotations: "\u0091"
- readonly property string s_arrange: "\u0092"
- readonly property string s_boundingBox: "\u0093"
- readonly property string s_component: "\u0094"
- readonly property string s_connections: "\u0095"
- readonly property string s_edit: "\u0096"
- readonly property string s_enterComponent: "\u0097"
- readonly property string s_eventList: "\u0098"
- readonly property string s_group: "\u0099"
- readonly property string s_layouts: "\u009A"
- readonly property string s_merging: "\u009B"
- readonly property string s_mouseArea: "\u009D"
- readonly property string s_positioners: "\u009E"
- readonly property string s_selection: "\u009F"
- readonly property string s_snapping: "\u00A0"
- readonly property string s_timeline: "\u00A1"
- readonly property string s_visibility: "\u00A2"
- readonly property string search: "\u00A3"
- readonly property string sectionToggle: "\u00A4"
- readonly property string splitColumns: "\u00A5"
- readonly property string splitRows: "\u00A6"
- readonly property string startNode: "\u00A7"
- readonly property string testIcon: "\u00A8"
- readonly property string textAlignBottom: "\u00A9"
- readonly property string textAlignCenter: "\u00AA"
- readonly property string textAlignJustified: "\u00AB"
- readonly property string textAlignLeft: "\u00AC"
- readonly property string textAlignMiddle: "\u00AE"
- readonly property string textAlignRight: "\u00AF"
- readonly property string textAlignTop: "\u00B0"
- readonly property string textBulletList: "\u00B1"
- readonly property string textFullJustification: "\u00B2"
- readonly property string textNumberedList: "\u00B3"
- readonly property string tickIcon: "\u00B4"
- readonly property string topToolbar_annotations: "\u00B5"
- readonly property string topToolbar_closeFile: "\u00B6"
- readonly property string topToolbar_designMode: "\u00B7"
- readonly property string topToolbar_enterComponent: "\u00B8"
- readonly property string topToolbar_home: "\u00B9"
- readonly property string topToolbar_makeComponent: "\u00BA"
- readonly property string topToolbar_navFile: "\u00BB"
- readonly property string topToolbar_runProject: "\u00BC"
- readonly property string translationCreateFiles: "\u00BD"
- readonly property string translationCreateReport: "\u00BE"
- readonly property string translationExport: "\u00BF"
- readonly property string translationImport: "\u00C0"
- readonly property string translationSelectLanguages: "\u00C1"
- readonly property string translationTest: "\u00C2"
- readonly property string transparent: "\u00C3"
- readonly property string triState: "\u00C4"
- readonly property string triangleArcA: "\u00C5"
- readonly property string triangleArcB: "\u00C6"
- readonly property string triangleCornerA: "\u00C7"
- readonly property string triangleCornerB: "\u00C8"
- readonly property string unLinked: "\u00C9"
- readonly property string undo: "\u00CA"
- readonly property string unpin: "\u00CB"
- readonly property string upDownIcon: "\u00CC"
- readonly property string upDownSquare2: "\u00CD"
- readonly property string visibilityOff: "\u00CE"
- readonly property string visibilityOn: "\u00CF"
- readonly property string wildcard: "\u00D0"
- readonly property string wizardsAutomotive: "\u00D1"
- readonly property string wizardsDesktop: "\u00D2"
- readonly property string wizardsGeneric: "\u00D3"
- readonly property string wizardsMcuEmpty: "\u00D4"
- readonly property string wizardsMcuGraph: "\u00D5"
- readonly property string wizardsMobile: "\u00D6"
- readonly property string wizardsUnknown: "\u00D7"
- readonly property string zoomAll: "\u00D8"
- readonly property string zoomIn: "\u00D9"
- readonly property string zoomOut: "\u00DA"
- readonly property string zoomSelection: "\u00DB"
+ readonly property string add_medium: "\u0029"
+ readonly property string add_small: "\u002A"
+ readonly property string adsClose: "\u002B"
+ readonly property string adsDetach: "\u002C"
+ readonly property string adsDropDown: "\u002D"
+ readonly property string alias: "\u002E"
+ readonly property string aliasAnimated: "\u002F"
+ readonly property string alignBottom: "\u0030"
+ readonly property string alignCenterHorizontal: "\u0031"
+ readonly property string alignCenterVertical: "\u0032"
+ readonly property string alignLeft: "\u0033"
+ readonly property string alignRight: "\u0034"
+ readonly property string alignTo: "\u0035"
+ readonly property string alignToCam_medium: "\u0036"
+ readonly property string alignToCamera_small: "\u0037"
+ readonly property string alignToObject_small: "\u0038"
+ readonly property string alignToView_medium: "\u0039"
+ readonly property string alignTop: "\u003A"
+ readonly property string anchorBaseline: "\u003B"
+ readonly property string anchorBottom: "\u003C"
+ readonly property string anchorFill: "\u003D"
+ readonly property string anchorLeft: "\u003E"
+ readonly property string anchorRight: "\u003F"
+ readonly property string anchorTop: "\u0040"
+ readonly property string anchors_small: "\u0041"
+ readonly property string animatedProperty: "\u0042"
+ readonly property string annotationBubble: "\u0043"
+ readonly property string annotationDecal: "\u0044"
+ readonly property string annotations_large: "\u0045"
+ readonly property string annotations_small: "\u0046"
+ readonly property string applyMaterialToSelected: "\u0047"
+ readonly property string apply_medium: "\u0048"
+ readonly property string apply_small: "\u0049"
+ readonly property string arrange_small: "\u004A"
+ readonly property string arrow_small: "\u004B"
+ readonly property string assign: "\u004C"
+ readonly property string attach_medium: "\u004D"
+ readonly property string back_medium: "\u004E"
+ readonly property string backspace_small: "\u004F"
+ readonly property string bevelAll: "\u0050"
+ readonly property string bevelCorner: "\u0051"
+ readonly property string bezier_medium: "\u0052"
+ readonly property string binding_medium: "\u0053"
+ readonly property string bounds_small: "\u0054"
+ readonly property string branch_medium: "\u0055"
+ readonly property string camera_small: "\u0056"
+ readonly property string centerHorizontal: "\u0057"
+ readonly property string centerVertical: "\u0058"
+ readonly property string cleanLogs_medium: "\u0059"
+ readonly property string closeCross: "\u005A"
+ readonly property string closeFile_large: "\u005B"
+ readonly property string closeLink: "\u005C"
+ readonly property string close_small: "\u005D"
+ readonly property string colorPopupClose: "\u005E"
+ readonly property string colorSelection_medium: "\u005F"
+ readonly property string columnsAndRows: "\u0060"
+ readonly property string cone_medium: "\u0061"
+ readonly property string cone_small: "\u0062"
+ readonly property string connection_small: "\u0063"
+ readonly property string connections_medium: "\u0064"
+ readonly property string copyLink: "\u0065"
+ readonly property string copyStyle: "\u0066"
+ readonly property string copy_small: "\u0067"
+ readonly property string cornerA: "\u0068"
+ readonly property string cornerB: "\u0069"
+ readonly property string cornersAll: "\u006A"
+ readonly property string createComponent_large: "\u006B"
+ readonly property string createComponent_small: "\u006C"
+ readonly property string create_medium: "\u006D"
+ readonly property string create_small: "\u006E"
+ readonly property string cube_medium: "\u006F"
+ readonly property string cube_small: "\u0070"
+ readonly property string curveDesigner: "\u0071"
+ readonly property string curveDesigner_medium: "\u0072"
+ readonly property string curveEditor: "\u0073"
+ readonly property string customMaterialEditor: "\u0074"
+ readonly property string cylinder_medium: "\u0075"
+ readonly property string cylinder_small: "\u0076"
+ readonly property string decisionNode: "\u0077"
+ readonly property string deleteColumn: "\u0078"
+ readonly property string deleteMaterial: "\u0079"
+ readonly property string deleteRow: "\u007A"
+ readonly property string deleteTable: "\u007B"
+ readonly property string delete_medium: "\u007C"
+ readonly property string delete_small: "\u007D"
+ readonly property string designMode_large: "\u007E"
+ readonly property string detach: "\u007F"
+ readonly property string directionalLight_small: "\u0080"
+ readonly property string distributeBottom: "\u0081"
+ readonly property string distributeCenterHorizontal: "\u0082"
+ readonly property string distributeCenterVertical: "\u0083"
+ readonly property string distributeLeft: "\u0084"
+ readonly property string distributeOriginBottomRight: "\u0085"
+ readonly property string distributeOriginCenter: "\u0086"
+ readonly property string distributeOriginNone: "\u0087"
+ readonly property string distributeOriginTopLeft: "\u0088"
+ readonly property string distributeRight: "\u0089"
+ readonly property string distributeSpacingHorizontal: "\u008A"
+ readonly property string distributeSpacingVertical: "\u008B"
+ readonly property string distributeTop: "\u008C"
+ readonly property string download: "\u008D"
+ readonly property string downloadUnavailable: "\u008E"
+ readonly property string downloadUpdate: "\u008F"
+ readonly property string downloaded: "\u0090"
+ readonly property string duplicate_small: "\u0091"
+ readonly property string edit: "\u0092"
+ readonly property string editComponent_large: "\u0093"
+ readonly property string editComponent_small: "\u0094"
+ readonly property string editLightOff_medium: "\u0095"
+ readonly property string editLightOn_medium: "\u0096"
+ readonly property string edit_medium: "\u0097"
+ readonly property string edit_small: "\u0098"
+ readonly property string events_small: "\u0099"
+ readonly property string export_medium: "\u009A"
+ readonly property string eyeDropper: "\u009B"
+ readonly property string favorite: "\u009D"
+ readonly property string fitAll_medium: "\u009E"
+ readonly property string fitSelected_small: "\u009F"
+ readonly property string fitSelection_medium: "\u00A0"
+ readonly property string fitToView_medium: "\u00A1"
+ readonly property string flowAction: "\u00A2"
+ readonly property string flowTransition: "\u00A3"
+ readonly property string fontStyleBold: "\u00A4"
+ readonly property string fontStyleItalic: "\u00A5"
+ readonly property string fontStyleStrikethrough: "\u00A6"
+ readonly property string fontStyleUnderline: "\u00A7"
+ readonly property string forward_medium: "\u00A8"
+ readonly property string globalOrient_medium: "\u00A9"
+ readonly property string gradient: "\u00AA"
+ readonly property string gridView: "\u00AB"
+ readonly property string grid_medium: "\u00AC"
+ readonly property string group_small: "\u00AE"
+ readonly property string home_large: "\u00AF"
+ readonly property string idAliasOff: "\u00B0"
+ readonly property string idAliasOn: "\u00B1"
+ readonly property string import_medium: "\u00B2"
+ readonly property string imported: "\u00B3"
+ readonly property string importedModels_small: "\u00B4"
+ readonly property string infinity: "\u00B5"
+ readonly property string invisible_medium: "\u00B6"
+ readonly property string keyframe: "\u00B7"
+ readonly property string languageList_medium: "\u00B8"
+ readonly property string layouts_small: "\u00B9"
+ readonly property string lights_small: "\u00BA"
+ readonly property string linear_medium: "\u00BB"
+ readonly property string linkTriangle: "\u00BC"
+ readonly property string linked: "\u00BD"
+ readonly property string listView: "\u00BE"
+ readonly property string list_medium: "\u00BF"
+ readonly property string localOrient_medium: "\u00C0"
+ readonly property string lockOff: "\u00C1"
+ readonly property string lockOn: "\u00C2"
+ readonly property string loopPlayback_medium: "\u00C3"
+ readonly property string materialBrowser_medium: "\u00C4"
+ readonly property string materialPreviewEnvironment: "\u00C5"
+ readonly property string materialPreviewModel: "\u00C6"
+ readonly property string material_medium: "\u00C7"
+ readonly property string mergeCells: "\u00C8"
+ readonly property string merge_small: "\u00C9"
+ readonly property string minus: "\u00CA"
+ readonly property string mirror: "\u00CB"
+ readonly property string more_medium: "\u00CC"
+ readonly property string mouseArea_small: "\u00CD"
+ readonly property string moveDown_medium: "\u00CE"
+ readonly property string moveInwards_medium: "\u00CF"
+ readonly property string moveUp_medium: "\u00D0"
+ readonly property string moveUpwards_medium: "\u00D1"
+ readonly property string move_medium: "\u00D2"
+ readonly property string newMaterial: "\u00D3"
+ readonly property string nextFile_large: "\u00D4"
+ readonly property string openLink: "\u00D5"
+ readonly property string openMaterialBrowser: "\u00D6"
+ readonly property string orientation: "\u00D7"
+ readonly property string orthCam_medium: "\u00D8"
+ readonly property string orthCam_small: "\u00D9"
+ readonly property string paddingEdge: "\u00DA"
+ readonly property string paddingFrame: "\u00DB"
+ readonly property string particleAnimation_medium: "\u00DC"
+ readonly property string pasteStyle: "\u00DD"
+ readonly property string paste_small: "\u00DE"
+ readonly property string pause: "\u00DF"
+ readonly property string perspectiveCam_medium: "\u00E0"
+ readonly property string perspectiveCam_small: "\u00E1"
+ readonly property string pin: "\u00E2"
+ readonly property string plane_medium: "\u00E3"
+ readonly property string plane_small: "\u00E4"
+ readonly property string play: "\u00E5"
+ readonly property string playFill_medium: "\u00E6"
+ readonly property string playOutline_medium: "\u00E7"
+ readonly property string plus: "\u00E8"
+ readonly property string pointLight_small: "\u00E9"
+ readonly property string positioners_small: "\u00EA"
+ readonly property string previewEnv_medium: "\u00EB"
+ readonly property string previousFile_large: "\u00EC"
+ readonly property string promote: "\u00ED"
+ readonly property string properties_medium: "\u00EE"
+ readonly property string readOnly: "\u00EF"
+ readonly property string recordFill_medium: "\u00F0"
+ readonly property string recordOutline_medium: "\u00F1"
+ readonly property string redo: "\u00F2"
+ readonly property string reload_medium: "\u00F3"
+ readonly property string remove_medium: "\u00F4"
+ readonly property string remove_small: "\u00F5"
+ readonly property string rename_small: "\u00F6"
+ readonly property string replace_small: "\u00F7"
+ readonly property string resetView_small: "\u00F8"
+ readonly property string restartParticles_medium: "\u00F9"
+ readonly property string reverseOrder_medium: "\u00FA"
+ readonly property string roatate_medium: "\u00FB"
+ readonly property string rotationFill: "\u00FC"
+ readonly property string rotationOutline: "\u00FD"
+ readonly property string runProjFill_large: "\u00FE"
+ readonly property string runProjOutline_large: "\u00FF"
+ readonly property string s_anchors: "\u0100"
+ readonly property string s_annotations: "\u0101"
+ readonly property string s_arrange: "\u0102"
+ readonly property string s_boundingBox: "\u0103"
+ readonly property string s_component: "\u0104"
+ readonly property string s_connections: "\u0105"
+ readonly property string s_edit: "\u0106"
+ readonly property string s_enterComponent: "\u0107"
+ readonly property string s_eventList: "\u0108"
+ readonly property string s_group: "\u0109"
+ readonly property string s_layouts: "\u010A"
+ readonly property string s_merging: "\u010B"
+ readonly property string s_mouseArea: "\u010C"
+ readonly property string s_positioners: "\u010D"
+ readonly property string s_selection: "\u010E"
+ readonly property string s_snapping: "\u010F"
+ readonly property string s_timeline: "\u0110"
+ readonly property string s_visibility: "\u0111"
+ readonly property string saveLogs_medium: "\u0112"
+ readonly property string scale_medium: "\u0113"
+ readonly property string search: "\u0114"
+ readonly property string search_small: "\u0115"
+ readonly property string sectionToggle: "\u0116"
+ readonly property string selectFill_medium: "\u0117"
+ readonly property string selectOutline_medium: "\u0118"
+ readonly property string selectParent_small: "\u0119"
+ readonly property string selection_small: "\u011A"
+ readonly property string settings_medium: "\u011B"
+ readonly property string signal_small: "\u011C"
+ readonly property string snapping_small: "\u011D"
+ readonly property string sphere_medium: "\u011E"
+ readonly property string sphere_small: "\u011F"
+ readonly property string splitColumns: "\u0120"
+ readonly property string splitRows: "\u0121"
+ readonly property string spotLight_small: "\u0122"
+ readonly property string stackedContainer_small: "\u0123"
+ readonly property string startNode: "\u0124"
+ readonly property string step_medium: "\u0125"
+ readonly property string stop_medium: "\u0126"
+ readonly property string testIcon: "\u0127"
+ readonly property string textAlignBottom: "\u0128"
+ readonly property string textAlignCenter: "\u0129"
+ readonly property string textAlignJustified: "\u012A"
+ readonly property string textAlignLeft: "\u012B"
+ readonly property string textAlignMiddle: "\u012C"
+ readonly property string textAlignRight: "\u012D"
+ readonly property string textAlignTop: "\u012E"
+ readonly property string textBulletList: "\u012F"
+ readonly property string textFullJustification: "\u0130"
+ readonly property string textNumberedList: "\u0131"
+ readonly property string textures_medium: "\u0132"
+ readonly property string tickIcon: "\u0133"
+ readonly property string tickMark_small: "\u0134"
+ readonly property string timeline_small: "\u0135"
+ readonly property string toEndFrame_medium: "\u0136"
+ readonly property string toNextFrame_medium: "\u0137"
+ readonly property string toPrevFrame_medium: "\u0138"
+ readonly property string toStartFrame_medium: "\u0139"
+ readonly property string topToolbar_annotations: "\u013A"
+ readonly property string topToolbar_closeFile: "\u013B"
+ readonly property string topToolbar_designMode: "\u013C"
+ readonly property string topToolbar_enterComponent: "\u013D"
+ readonly property string topToolbar_home: "\u013E"
+ readonly property string topToolbar_makeComponent: "\u013F"
+ readonly property string topToolbar_navFile: "\u0140"
+ readonly property string topToolbar_runProject: "\u0141"
+ readonly property string translationCreateFiles: "\u0142"
+ readonly property string translationCreateReport: "\u0143"
+ readonly property string translationExport: "\u0144"
+ readonly property string translationImport: "\u0145"
+ readonly property string translationSelectLanguages: "\u0146"
+ readonly property string translationTest: "\u0147"
+ readonly property string transparent: "\u0148"
+ readonly property string triState: "\u0149"
+ readonly property string triangleArcA: "\u014A"
+ readonly property string triangleArcB: "\u014B"
+ readonly property string triangleCornerA: "\u014C"
+ readonly property string triangleCornerB: "\u014D"
+ readonly property string unLinked: "\u014E"
+ readonly property string undo: "\u014F"
+ readonly property string unify_medium: "\u0150"
+ readonly property string unpin: "\u0151"
+ readonly property string upDownIcon: "\u0152"
+ readonly property string upDownSquare2: "\u0153"
+ readonly property string visibilityOff: "\u0154"
+ readonly property string visibilityOn: "\u0155"
+ readonly property string visible_medium: "\u0156"
+ readonly property string visible_small: "\u0157"
+ readonly property string wildcard: "\u0158"
+ readonly property string wizardsAutomotive: "\u0159"
+ readonly property string wizardsDesktop: "\u015A"
+ readonly property string wizardsGeneric: "\u015B"
+ readonly property string wizardsMcuEmpty: "\u015C"
+ readonly property string wizardsMcuGraph: "\u015D"
+ readonly property string wizardsMobile: "\u015E"
+ readonly property string wizardsUnknown: "\u015F"
+ readonly property string zoomAll: "\u0160"
+ readonly property string zoomIn: "\u0161"
+ readonly property string zoomIn_medium: "\u0162"
+ readonly property string zoomOut: "\u0163"
+ readonly property string zoomOut_medium: "\u0164"
+ readonly property string zoomSelection: "\u0165"
readonly property font iconFont: Qt.font({
"family": controlIcons.name,
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/PrimaryButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/PrimaryButtonStyle.qml
new file mode 100644
index 0000000000..c1cf5be9cf
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/PrimaryButtonStyle.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+ controlSize: Qt.size(Values.topLevelComboWidth, Values.topLevelComboHeight)
+ baseIconFontSize: Values.baseFontSize
+ radius: Values.smallRadius
+
+ icon: ControlStyle.IconColors {
+ idle: Values.themeTextSelectedTextColor
+ hover: Values.themeTextSelectedTextColor
+ disabled: Values.themeToolbarIcon_blocked
+ }
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themeInteraction
+ hover: Values.themePrimaryButton_hoverHighlight
+ interaction: Values.themeInteraction
+ disabled: Values.themeInteraction
+ }
+
+ border: ControlStyle.BorderColors {
+ idle: Values.themeInteraction
+ hover: Values.themePrimaryButton_hoverHighlight
+ interaction: Values.themePrimaryButton_hoverHighlight
+ disabled: Values.themeToolbarIcon_blocked
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/SearchControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/SearchControlStyle.qml
new file mode 100644
index 0000000000..f44fb0fa56
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/SearchControlStyle.qml
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+ radius: Values.smallRadius
+ baseIconFontSize: Values.miniIcon
+ controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight)
+ smallIconFontSize: Values.viewBarComboIcon
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themeToolbarBackground
+ hover: Values.themeControlBackground_toolbarHover
+ globalHover: Values.themeControlBackground_toolbarHover
+ interaction: Values.themeToolbarBackground
+ }
+
+ text: ControlStyle.TextColors {
+ idle: Values.themeTextColor
+ interaction: Values.themeTextSelectedTextColor
+ hover: Values.themeTextColor
+ disabled: Values.themeToolbarIcon_blocked
+ selection: Values.themeTextSelectionColor
+ selectedText: Values.themeTextSelectedTextColor
+ //placeholder: Values.themeTextColorDisabled
+ //placeholderHover: Values.themeTextColor
+ placeholderInteraction: Values.themeTextColor
+ }
+
+ border: ControlStyle.BorderColors {
+ idle: Values.controlOutline_toolbarIdle
+ hover: Values.controlOutline_toolbarHover
+ interaction: Values.themeInteraction
+ }
+
+ icon: ControlStyle.IconColors {
+ idle: Values.themeIconColor
+ interaction: Values.themeIconColorInteraction
+ selected: Values.themeIconColorSelected
+ hover: Values.themeIconColorHover
+ disabled: Values.themeToolbarIcon_blocked
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatesControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatesControlStyle.qml
new file mode 100644
index 0000000000..848d9f55c0
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatesControlStyle.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+ radius: Values.smallRadius
+ baseIconFontSize: Values.baseFont
+ controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight)
+ smallIconFontSize: Values.viewBarComboIcon
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themeToolbarBackground
+ hover: Values.themeStateControlBackgroundColor_globalHover
+ globalHover: Values.themeStateControlBackgroundColor_globalHover
+ interaction: Values.themeToolbarBackground
+ }
+ text: ControlStyle.TextColors {
+ idle: Values.themeTextColor
+ interaction: Values.themeTextSelectedTextColor
+ hover: Values.themeTextColor
+ disabled: Values.themeTextColorDisabled
+ selection: Values.themeTextSelectionColor
+ selectedText: Values.themeTextSelectedTextColor
+ placeholder: Values.themeTextColor
+ placeholderInteraction: Values.themeTextColor
+ }
+ border: ControlStyle.BorderColors {
+ idle: Values.controlOutline_toolbarIdle
+ hover: Values.themeStateHighlight
+ interaction: Values.themeInteraction
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarButtonStyle.qml
new file mode 100644
index 0000000000..d0ced4298d
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarButtonStyle.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+
+ baseIconFontSize: Values.bigFont
+ controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight)
+ smallIconFontSize: Values.viewBarComboIcon
+
+ radius: Values.smallRadius
+
+ icon: ControlStyle.IconColors {
+ idle: Values.themeTextColor
+ hover: Values.themeTextColor
+ interaction: Values.themeIconColor
+ disabled: "#636363"
+ }
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themecontrolBackground_statusbarIdle
+ hover: Values.themecontrolBackground_statusbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themecontrolBackground_statusbarIdle
+ }
+
+ border: ControlStyle.BorderColors {
+ idle: Values.themecontrolBackground_statusbarIdle
+ hover: Values.themeControlBackground_toolbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themecontrolBackground_statusbarIdle
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarControlStyle.qml
new file mode 100644
index 0000000000..c9c2c1abce
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/StatusBarControlStyle.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+
+ radius: Values.smallRadius
+
+ baseIconFontSize: Values.baseFont
+ controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight)
+ smallIconFontSize: Values.baseFont
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themecontrolBackground_statusbarIdle
+ hover: Values.themecontrolBackground_statusbarHover
+ globalHover: Values.themecontrolBackground_statusbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themecontrolBackground_statusbarIdle
+ }
+ icon: ControlStyle.IconColors {
+ idle: Values.themeTextColor
+ hover: Values.themeTextColor
+ interaction: Values.themeTextSelectedTextColor
+ disabled: Values.themeTextColorDisabled
+ }
+
+ border: ControlStyle.BorderColors {
+ hover: Values.themeControlBackground_toolbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themecontrolBackground_statusbarIdle
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ToolbarStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ToolbarStyle.qml
new file mode 100644
index 0000000000..af7b641cfe
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ToolbarStyle.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+ controlSize: Qt.size(Values.topLevelComboWidth, Values.topLevelComboHeight)
+ baseIconFontSize: Values.topLevelComboIcon
+ smallIconFontSize: Values.baseFont
+ background.idle: Values.themeControlBackground_toolbarIdle
+ background.hover: Values.themeControlBackground_topToolbarHover
+ background.globalHover: Values.themeControlBackground_topToolbarHover
+ border.idle: Values.controlOutline_toolbarIdle
+ border.hover: Values.themeControlBackground_topToolbarHover
+ text.hover: Values.themeTextColor
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml
new file mode 100644
index 0000000000..594dc03336
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/TopToolbarButtonStyle.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+
+ controlSize: Qt.size(Values.topLevelComboWidth, Values.topLevelComboHeight)
+ borderWidth: Values.border
+ baseIconFontSize: Values.topLevelComboIcon
+ radius: Values.smallRadius
+
+ icon: ControlStyle.IconColors {
+ idle: Values.themeTextColor
+ hover: Values.themeTextColor
+ interaction: Values.themeIconColor
+ disabled: Values.themeToolbarIcon_blocked
+ }
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themeControlBackground_toolbarIdle
+ hover: Values.themeControlBackground_topToolbarHover
+ globalHover: Values.themeControlBackground_topToolbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themeControlBackground_toolbarIdle
+ }
+
+ border: ControlStyle.BorderColors {
+ idle: Values.themeControlBackground_toolbarIdle
+ hover: Values.themeControlBackground_topToolbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themeControlBackground_toolbarIdle
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
index ae1bd543c5..ed47402de7 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
@@ -1,17 +1,30 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// 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 Singleton
-import QtQuick 2.15
+import QtQuick
import QtQuickDesignerTheme 1.0
QtObject {
id: values
property real baseHeight: 29
+
+ property real topLevelComboWidth: 210
+ property real topLevelComboHeight: 36
+ property real topLevelComboIcon: 20
+
+ property real viewBarComboWidth: 210
+ property real viewBarComboHeight: 29
+ property real viewBarComboIcon: 16
+
+ property real smallFont: 8
+ property real miniIcon: 10
property real baseFont: 12
property real mediumFont: 14
property real bigFont: 16
+
+ property real smallIconFont: 8
property real baseIconFont: 12
property real mediumIconFont: 18
property real bigIconFont: 26
@@ -19,21 +32,27 @@ QtObject {
property real scaleFactor: 1.0
property real height: Math.round(values.baseHeight * values.scaleFactor)
- property real baseFontSize: Math.round(values.baseFont * values.scaleFactor)
+
property real myFontSize: values.baseFontSize // TODO: rename all refs to myFontSize -> baseFontSize then remove myFontSize
+
+ property real smallFontSize: Math.round(values.smallFont * values.scaleFactor)
+ property real baseFontSize: Math.round(values.baseFont * values.scaleFactor)
property real mediumFontSize: Math.round(values.mediumFont * values.scaleFactor)
property real bigFontSize: Math.round(values.bigFont * values.scaleFactor)
+
+ property real myIconFontSize: values.baseIconFontSize // TODO: rename all refs to myIconFontSize -> baseIconFontSize then remove myIconFontSize
+
+ property real smallIconFontSize: Math.round(values.smallIconFont * values.scaleFactor)
property real baseIconFontSize: Math.round(values.baseIconFont * values.scaleFactor)
- property real myIconFontSize: values.baseIconFontSize; // TODO: rename all refs to myIconFontSize -> baseIconFontSize then remove myIconFontSize
property real mediumIconFontSize: Math.round(values.mediumIconFont * values.scaleFactor)
property real bigIconFontSize: Math.round(values.bigIconFont * values.scaleFactor)
property real squareComponentWidth: values.height
- property real smallRectWidth: values.height / 2 * 1.5
+ property real smallRectWidth: values.height * 0.75// / 2 * 1.5
property real inputWidth: values.height * 4
- property real sliderHeight: values.height / 2 * 1.5 // TODO:Have a look at -> sliderAreaHeight: Data.Values.height/2*1.5
+ property real sliderHeight: values.height * 0.75// / 2 * 1.5 // TODO:Have a look at -> sliderAreaHeight: Data.Values.height/2*1.5
property real sliderControlSize: 12
property real sliderControlSizeMulti: values.sliderControlSize * values.scaleFactor
@@ -43,8 +62,8 @@ QtObject {
property real spinControlIconSizeMulti: values.spinControlIconSize * values.scaleFactor
property real sliderTrackHeight: values.height / 3
- property real sliderHandleHeight: values.sliderTrackHeight * 1.8
property real sliderHandleWidth: values.sliderTrackHeight * 0.5
+ property real sliderHandleHeight: values.sliderTrackHeight * 1.8
property real sliderFontSize: Math.round(8 * values.scaleFactor)
property real sliderPadding: Math.round(6 * values.scaleFactor)
property real sliderMargin: Math.round(3 * values.scaleFactor)
@@ -55,10 +74,10 @@ QtObject {
property real checkBoxSpacing: Math.round(6 * values.scaleFactor)
property real radioButtonSpacing: values.checkBoxSpacing
- property real radioButtonWidth: values.height
- property real radioButtonHeight: values.height
- property real radioButtonIndicatorWidth: 14
- property real radioButtonIndicatorHeight: 14
+ //property real radioButtonWidth: values.height
+ //property real radioButtonHeight: values.height
+ property real radioButtonIndicatorWidth: Math.round(14 * values.scaleFactor)
+ property real radioButtonIndicatorHeight: Math.round(14 * values.scaleFactor)
property real switchSpacing: values.checkBoxSpacing
@@ -67,6 +86,9 @@ QtObject {
property real marginTopBottom: 4
property real border: 1
property real borderHover: 3
+ property real radius: 0 //adding specific radiuses
+
+ property real smallRadius: 4
property real maxComboBoxPopupHeight: Math.round(300 * values.scaleFactor)
property real maxTextAreaPopupHeight: Math.round(150 * values.scaleFactor)
@@ -139,13 +161,13 @@ QtObject {
+ values.twoControlColumnGap
+ values.actionIndicatorWidth
- property real twoControlColumnWidthMin: 3 * values.height - 2 * values.border
+ property real twoControlColumnWidthMin: 3 * values.height - 2 * values.border - 10
property real twoControlColumnWidthMax: 3 * values.twoControlColumnWidthMin
property real twoControlColumnWidth: values.twoControlColumnWidthMin
property real controlColumnWithoutControlsWidth: 2 * (values.actionIndicatorWidth
+ values.twoControlColumnGap)
- + values.linkControlWidth
+ + values.linkControlWidth // there could be an issue here with the new style
property real controlColumnWidth: values.controlColumnWithoutControlsWidth
+ 2 * values.twoControlColumnWidth
@@ -197,8 +219,10 @@ QtObject {
property real colorEditorPopupSpinBoxWidth: 54
// Toolbar
- property real toolbarHeight: 35
- property real toolbarSpacing: 8
+ property real toolbarHeight: 41
+ property real doubleToolbarHeight: values.toolbarHeight * 2
+ property real toolbarSpacing: 10
+
// Dialog
property real dialogPadding: 12
@@ -207,134 +231,184 @@ QtObject {
// Theme Colors
- property bool isLightTheme: themeControlBackground.hsvValue > themeTextColor.hsvValue
+ property bool isLightTheme: values.themeControlBackground.hsvValue > values.themeTextColor.hsvValue
+
+ //NEW QtDS 4.0
- property string themePanelBackground: Theme.color(Theme.DSpanelBackground)
+ //Top & View toolbar colors
- property string themeGreenLight: Theme.color(Theme.DSgreenLight)
- property string themeAmberLight: Theme.color(Theme.DSamberLight)
- property string themeRedLight: Theme.color(Theme.DSredLight)
+ //backgrounds
+ property color themeControlBackground_toolbarIdle: Theme.color(Theme.DScontrolBackground_toolbarIdle)
+ property color themeControlBackground_toolbarHover: Theme.color(Theme.DScontrolBackground_toolbarHover)
+ property color themeControlBackground_topToolbarHover: Theme.color(Theme.DScontrolBackground_topToolbarHover)
+ property color themeToolbarBackground: Theme.color(Theme.DStoolbarBackground)
- property string themeInteraction: Theme.color(Theme.DSinteraction)
- property string themeError: Theme.color(Theme.DSerrorColor)
- property string themeWarning: Theme.color(Theme.DSwarningColor)
- property string themeDisabled: Theme.color(Theme.DSdisabledColor)
+ //outlines
+ property color controlOutline_toolbarIdle: Theme.color(Theme.DScontrolOutline_topToolbarIdle)
+ property color controlOutline_toolbarHover: Theme.color(Theme.DScontrolOutline_topToolbarHover)
- property string themeInteractionHover: Theme.color(Theme.DSinteractionHover)
+ //icons
+ property color themeToolbarIcon_blocked: Theme.color(Theme.DStoolbarIcon_blocked)
- property string themeAliasIconChecked: Theme.color(Theme.DSnavigatorAliasIconChecked)
+ //primary buttons
+ property color themePrimaryButton_hoverHighlight: Theme.color(Theme.DSprimaryButton_hoverHighlight)
+
+ //states
+ property color themeStateControlBackgroundColor_hover: Theme.color(Theme.DSstateControlBackgroundColor_hover)
+ property color themeStateBackgroundColor_hover: Theme.color(Theme.DSstateBackgroundColor_hover)
+ property color themeStateControlBackgroundColor_globalHover: Theme.color(Theme.DSstateControlBackgroundColor_globalHover)
+ property color themeThumbnailBackground_baseState: Theme.color(Theme.DSthumbnailBackground_baseState)
+
+ //task bar
+ property color themeStatusbarBackground:Theme.color(Theme.DSstatusbarBackground)
+ property color themecontrolBackground_statusbarIdle:Theme.color(Theme.DScontrolBackground_statusbarIdle)
+ property color themecontrolBackground_statusbarHover:Theme.color(Theme.DSControlBackground_statusbarHover)
+
+ //run project button
+ property color themeIdleGreen: Theme.color(Theme.DSidleGreen)
+ property color themeRunningGreen: Theme.color(Theme.DSrunningGreen)
+
+ //END NEW COLORS QtDS 4.0
+
+ property color themePanelBackground: Theme.color(Theme.DSpanelBackground)
+
+ property color themeGreenLight: Theme.color(Theme.DSgreenLight)
+ property color themeAmberLight: Theme.color(Theme.DSamberLight)
+ property color themeRedLight: Theme.color(Theme.DSredLight)
+
+ property color themeInteraction: Theme.color(Theme.DSinteraction)
+ property color themeError: Theme.color(Theme.DSerrorColor)
+ property color themeWarning: Theme.color(Theme.DSwarningColor)
+ property color themeDisabled: Theme.color(Theme.DSdisabledColor)
+
+ property color themeInteractionHover: Theme.color(Theme.DSinteractionHover)
+
+ property color themeAliasIconChecked: Theme.color(Theme.DSnavigatorAliasIconChecked)
// Control colors
property color themeControlBackground: Theme.color(Theme.DScontrolBackground)
- property string themeControlBackgroundInteraction: Theme.color(Theme.DScontrolBackgroundInteraction)
- property string themeControlBackgroundDisabled: Theme.color(Theme.DScontrolBackgroundDisabled)
- property string themeControlBackgroundGlobalHover: Theme.color(Theme.DScontrolBackgroundGlobalHover)
- property string themeControlBackgroundHover: Theme.color(Theme.DScontrolBackgroundHover)
+ property color themeControlBackgroundInteraction: Theme.color(Theme.DScontrolBackgroundInteraction)
+ property color themeControlBackgroundDisabled: Theme.color(Theme.DScontrolBackgroundDisabled)
+ property color themeControlBackgroundGlobalHover: Theme.color(Theme.DScontrolBackgroundGlobalHover)
+ property color themeControlBackgroundHover: Theme.color(Theme.DScontrolBackgroundHover)
- property string themeControlOutline: Theme.color(Theme.DScontrolOutline)
- property string themeControlOutlineInteraction: Theme.color(Theme.DScontrolOutlineInteraction)
- property string themeControlOutlineDisabled: Theme.color(Theme.DScontrolOutlineDisabled)
+ property color themeControlOutline: Theme.color(Theme.DScontrolOutline)
+ property color themeControlOutlineInteraction: Theme.color(Theme.DScontrolOutlineInteraction)
+ property color themeControlOutlineDisabled: Theme.color(Theme.DScontrolOutlineDisabled)
// Panels & Panes
- property string themeBackgroundColorNormal: Theme.color(Theme.DSBackgroundColorNormal)
- property string themeBackgroundColorAlternate: Theme.color(Theme.DSBackgroundColorAlternate)
+ property color themeBackgroundColorNormal: Theme.color(Theme.DSBackgroundColorNormal)
+ property color themeBackgroundColorAlternate: Theme.color(Theme.DSBackgroundColorAlternate)
// Text colors
property color themeTextColor: Theme.color(Theme.DStextColor)
- property string themeTextColorDisabled: Theme.color(Theme.DStextColorDisabled)
- property string themeTextSelectionColor: Theme.color(Theme.DStextSelectionColor)
- property string themeTextSelectedTextColor: Theme.color(Theme.DStextSelectedTextColor)
- property string themeTextColorDisabledMCU: Theme.color(Theme.DStextColorDisabled)
+ property color themeTextColorDisabled: Theme.color(Theme.DStextColorDisabled)
+ property color themeTextSelectionColor: Theme.color(Theme.DStextSelectionColor)
+ property color themeTextSelectedTextColor: Theme.color(Theme.DStextSelectedTextColor)
+ property color themeTextColorDisabledMCU: Theme.color(Theme.DStextColorDisabled)
- property string themePlaceholderTextColor: Theme.color(Theme.DSplaceholderTextColor)
- property string themePlaceholderTextColorInteraction: Theme.color(Theme.DSplaceholderTextColorInteraction)
+ property color themePlaceholderTextColor: Theme.color(Theme.DSplaceholderTextColor)
+ property color themePlaceholderTextColorInteraction: Theme.color(Theme.DSplaceholderTextColorInteraction)
// Icon colors
- property string themeIconColor: Theme.color(Theme.DSiconColor)
- property string themeIconColorHover: Theme.color(Theme.DSiconColorHover)
- property string themeIconColorInteraction: Theme.color(Theme.DSiconColorInteraction)
- property string themeIconColorDisabled: Theme.color(Theme.DSiconColorDisabled)
- property string themeIconColorSelected: Theme.color(Theme.DSiconColorSelected)
+ property color themeIconColor: Theme.color(Theme.DSiconColor)
+ property color themeIconColorHover: Theme.color(Theme.DSiconColorHover)
+ property color themeIconColorInteraction: Theme.color(Theme.DSiconColorInteraction)
+ property color themeIconColorDisabled: Theme.color(Theme.DSiconColorDisabled)
+ property color themeIconColorSelected: Theme.color(Theme.DSiconColorSelected)
- property string themeLinkIndicatorColor: Theme.color(Theme.DSlinkIndicatorColor)
- property string themeLinkIndicatorColorHover: Theme.color(Theme.DSlinkIndicatorColorHover)
- property string themeLinkIndicatorColorInteraction: Theme.color(Theme.DSlinkIndicatorColorInteraction)
- property string themeLinkIndicatorColorDisabled: Theme.color(Theme.DSlinkIndicatorColorDisabled)
+ property color themeLinkIndicatorColor: Theme.color(Theme.DSlinkIndicatorColor)
+ property color themeLinkIndicatorColorHover: Theme.color(Theme.DSlinkIndicatorColorHover)
+ property color themeLinkIndicatorColorInteraction: Theme.color(Theme.DSlinkIndicatorColorInteraction)
+ property color themeLinkIndicatorColorDisabled: Theme.color(Theme.DSlinkIndicatorColorDisabled)
- property string themeInfiniteLoopIndicatorColor: Theme.color(Theme.DSlinkIndicatorColor)
- property string themeInfiniteLoopIndicatorColorHover: Theme.color(Theme.DSlinkIndicatorColorHover)
- property string themeInfiniteLoopIndicatorColorInteraction: Theme.color(Theme.DSlinkIndicatorColorInteraction)
+ property color themeInfiniteLoopIndicatorColor: Theme.color(Theme.DSlinkIndicatorColor)
+ property color themeInfiniteLoopIndicatorColorHover: Theme.color(Theme.DSlinkIndicatorColorHover)
+ property color themeInfiniteLoopIndicatorColorInteraction: Theme.color(Theme.DSlinkIndicatorColorInteraction)
// Popup background color (ComboBox, SpinBox, TextArea)
- property string themePopupBackground: Theme.color(Theme.DSpopupBackground)
- // GradientPopupDialog modal overly color
- property string themePopupOverlayColor: Theme.color(Theme.DSpopupOverlayColor)
+ property color themePopupBackground: Theme.color(Theme.DSpopupBackground)
+ // GradientPopupDialog modal overlay color
+ property color themePopupOverlayColor: Theme.color(Theme.DSpopupOverlayColor)
// ToolTip (UrlChooser)
- property string themeToolTipBackground: Theme.color(Theme.DStoolTipBackground)
- property string themeToolTipOutline: Theme.color(Theme.DStoolTipOutline)
- property string themeToolTipText: Theme.color(Theme.DStoolTipText)
+ property color themeToolTipBackground: Theme.color(Theme.DStoolTipBackground)
+ property color themeToolTipOutline: Theme.color(Theme.DStoolTipOutline)
+ property color themeToolTipText: Theme.color(Theme.DStoolTipText)
// Slider colors
- property string themeSliderActiveTrack: Theme.color(Theme.DSsliderActiveTrack)
- property string themeSliderActiveTrackHover: Theme.color(Theme.DSsliderActiveTrackHover)
- property string themeSliderActiveTrackFocus: Theme.color(Theme.DSsliderActiveTrackFocus)
- property string themeSliderInactiveTrack: Theme.color(Theme.DSsliderInactiveTrack)
- property string themeSliderInactiveTrackHover: Theme.color(Theme.DSsliderInactiveTrackHover)
- property string themeSliderInactiveTrackFocus: Theme.color(Theme.DSsliderInactiveTrackFocus)
- property string themeSliderHandle: Theme.color(Theme.DSsliderHandle)
- property string themeSliderHandleHover: Theme.color(Theme.DSsliderHandleHover)
- property string themeSliderHandleFocus: Theme.color(Theme.DSsliderHandleFocus)
- property string themeSliderHandleInteraction: Theme.color(Theme.DSsliderHandleInteraction)
-
- property string themeScrollBarTrack: Theme.color(Theme.DSscrollBarTrack)
- property string themeScrollBarHandle: Theme.color(Theme.DSscrollBarHandle)
-
- property string themeSectionHeadBackground: Theme.color(Theme.DSsectionHeadBackground)
-
- property string themeTabActiveBackground: Theme.color(Theme.DStabActiveBackground)
- property string themeTabActiveText: Theme.color(Theme.DStabActiveText)
- property string themeTabInactiveBackground: Theme.color(Theme.DStabInactiveBackground)
- property string themeTabInactiveText: Theme.color(Theme.DStabInactiveText)
+ property color themeSliderActiveTrack: Theme.color(Theme.DSsliderActiveTrack)
+ property color themeSliderActiveTrackHover: Theme.color(Theme.DSsliderActiveTrackHover)
+ property color themeSliderActiveTrackFocus: Theme.color(Theme.DSsliderActiveTrackFocus)
+ property color themeSliderInactiveTrack: Theme.color(Theme.DSsliderInactiveTrack)
+ property color themeSliderInactiveTrackHover: Theme.color(Theme.DSsliderInactiveTrackHover)
+ property color themeSliderInactiveTrackFocus: Theme.color(Theme.DSsliderInactiveTrackFocus)
+ property color themeSliderHandle: Theme.color(Theme.DSsliderHandle)
+ property color themeSliderHandleHover: Theme.color(Theme.DSsliderHandleHover)
+ property color themeSliderHandleFocus: Theme.color(Theme.DSsliderHandleFocus)
+ property color themeSliderHandleInteraction: Theme.color(Theme.DSsliderHandleInteraction)
+
+ property color themeScrollBarTrack: Theme.color(Theme.DSscrollBarTrack)
+ property color themeScrollBarHandle: Theme.color(Theme.DSscrollBarHandle)
+
+ property color themeSectionHeadBackground: Theme.color(Theme.DSsectionHeadBackground)
+
+ property color themeTabActiveBackground: Theme.color(Theme.DStabActiveBackground)
+ property color themeTabActiveText: Theme.color(Theme.DStabActiveText)
+ property color themeTabInactiveBackground: Theme.color(Theme.DStabInactiveBackground)
+ property color themeTabInactiveText: Theme.color(Theme.DStabInactiveText)
+
// State Editor
- property string themeStateSeparator: Theme.color(Theme.DSstateSeparatorColor)
- property string themeStateBackground: Theme.color(Theme.DSstateBackgroundColor)
- property string themeStatePreviewOutline: Theme.color(Theme.DSstatePreviewOutline)
+ property color themeStateSeparator: Theme.color(Theme.DSstateSeparatorColor)
+ property color themeStateBackground: Theme.color(Theme.DSstateBackgroundColor)
+ property color themeStatePreviewOutline: Theme.color(Theme.DSstatePreviewOutline)
// State Editor *new*
property color themeStatePanelBackground: Theme.color(Theme.DSstatePanelBackground)
property color themeStateHighlight: Theme.color(Theme.DSstateHighlight)
- property string themeUnimportedModuleColor: Theme.color(Theme.DSUnimportedModuleColor)
+ property color themeUnimportedModuleColor: Theme.color(Theme.DSUnimportedModuleColor)
// Taken out of Constants.js
- property string themeChangedStateText: Theme.color(Theme.DSchangedStateText)
+ property color themeChangedStateText: Theme.color(Theme.DSchangedStateText)
// 3D
- property string theme3DAxisXColor: Theme.color(Theme.DS3DAxisXColor)
- property string theme3DAxisYColor: Theme.color(Theme.DS3DAxisYColor)
- property string theme3DAxisZColor: Theme.color(Theme.DS3DAxisZColor)
-
- property string themeActionBinding: Theme.color(Theme.DSactionBinding)
- property string themeActionAlias: Theme.color(Theme.DSactionAlias)
- property string themeActionKeyframe: Theme.color(Theme.DSactionKeyframe)
- property string themeActionJIT: Theme.color(Theme.DSactionJIT)
-
- property string themeListItemBackground: Theme.color(Theme.DSnavigatorItemBackground)
- property string themeListItemBackgroundHover: Theme.color(Theme.DSnavigatorItemBackgroundHover)
- property string themeListItemBackgroundPress: Theme.color(Theme.DSnavigatorItemBackgroundSelected)
- property string themeListItemText: Theme.color(Theme.DSnavigatorText)
- property string themeListItemTextHover: Theme.color(Theme.DSnavigatorTextHover)
- property string themeListItemTextPress: Theme.color(Theme.DSnavigatorTextSelected)
+ property color theme3DAxisXColor: Theme.color(Theme.DS3DAxisXColor)
+ property color theme3DAxisYColor: Theme.color(Theme.DS3DAxisYColor)
+ property color theme3DAxisZColor: Theme.color(Theme.DS3DAxisZColor)
+
+ property color themeActionBinding: Theme.color(Theme.DSactionBinding)
+ property color themeActionAlias: Theme.color(Theme.DSactionAlias)
+ property color themeActionKeyframe: Theme.color(Theme.DSactionKeyframe)
+ property color themeActionJIT: Theme.color(Theme.DSactionJIT)
+
+ property color themeListItemBackground: Theme.color(Theme.DSnavigatorItemBackground)
+ property color themeListItemBackgroundHover: Theme.color(Theme.DSnavigatorItemBackgroundHover)
+ property color themeListItemBackgroundPress: Theme.color(Theme.DSnavigatorItemBackgroundSelected)
+ property color themeListItemText: Theme.color(Theme.DSnavigatorText)
+ property color themeListItemTextHover: Theme.color(Theme.DSnavigatorTextHover)
+ property color themeListItemTextPress: Theme.color(Theme.DSnavigatorTextSelected)
// Welcome Page
- property string welcomeScreenBackground: Theme.color(Theme.DSwelcomeScreenBackground)
- property string themeSubPanelBackground: Theme.color(Theme.DSsubPanelBackground)
- property string themeThumbnailBackground: Theme.color(Theme.DSthumbnailBackground)
- property string themeThumbnailLabelBackground: Theme.color(Theme.DSthumbnailLabelBackground)
+ property color welcomeScreenBackground: Theme.color(Theme.DSwelcomeScreenBackground)
+ property color themeSubPanelBackground: Theme.color(Theme.DSsubPanelBackground)
+ property color themeThumbnailBackground: Theme.color(Theme.DSthumbnailBackground)
+ property color themeThumbnailLabelBackground: Theme.color(Theme.DSthumbnailLabelBackground)
// Dialog
property color themeDialogBackground: values.themeThumbnailBackground
property color themeDialogOutline: values.themeInteraction
+
+ // Control Style Mapping
+ property ControlStyle controlStyle: DefaultStyle {}
+ property ControlStyle toolbarStyle: ToolbarStyle {}
+ property ControlStyle primaryToolbarStyle: PrimaryButtonStyle {}
+ property ControlStyle toolbarButtonStyle: TopToolbarButtonStyle {}
+ property ControlStyle viewBarButtonStyle: ViewBarButtonStyle {}
+ property ControlStyle viewBarControlStyle: ViewBarControlStyle {}
+ property ControlStyle statusbarButtonStyle: StatusBarButtonStyle {}
+ property ControlStyle statusbarControlStyle: StatusBarControlStyle {}
+ property ControlStyle statesControlStyle: StatesControlStyle {}
+ property ControlStyle searchControlStyle: SearchControlStyle {}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarButtonStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarButtonStyle.qml
new file mode 100644
index 0000000000..a4de68a3fd
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarButtonStyle.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+
+ baseIconFontSize: Values.bigFont
+ controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight)
+ smallIconFontSize: Values.viewBarComboIcon
+ borderWidth: Values.border
+
+ radius: Values.smallRadius
+
+ icon: ControlStyle.IconColors {
+ idle: Values.themeTextColor
+ hover: Values.themeTextColor
+ interaction: Values.themeIconColor
+ disabled: Values.themeToolbarIcon_blocked
+ }
+
+ background: ControlStyle.BackgroundColors {
+ idle: Values.themeControlBackground_toolbarIdle
+ hover: Values.themeControlBackground_topToolbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themeControlBackground_toolbarIdle
+ }
+
+ border: ControlStyle.BorderColors {
+ idle: Values.themeControlBackground_toolbarIdle
+ hover: Values.themeControlBackground_topToolbarHover
+ interaction: Values.themeInteraction
+ disabled: Values.themeControlBackground_toolbarIdle
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarControlStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarControlStyle.qml
new file mode 100644
index 0000000000..cc634b04a1
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/ViewBarControlStyle.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+ControlStyle {
+ baseIconFontSize: Values.baseFont
+ controlSize: Qt.size(Values.viewBarComboWidth, Values.viewBarComboHeight)
+ smallIconFontSize: Values.baseFont
+ background.idle: Values.themeControlBackground_toolbarIdle
+ background.hover: Values.themeControlBackground_topToolbarHover
+ background.globalHover: Values.themeControlBackground_topToolbarHover
+ border.idle: Values.controlOutline_toolbarIdle
+ text.hover: Values.themeTextColor
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf
index cacd0c3cb9..1b1821ae99 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf
Binary files differ
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir
index 4f689f9f63..2814e7809b 100755
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir
@@ -1,4 +1,14 @@
singleton Values 1.0 Values.qml
singleton Constants 1.0 Constants.qml
+ControlStyle 1.0 ControlStyle.qml
+DefaultStyle 1.0 DefaultStyle.qml
InternalConstants 1.0 InternalConstants.qml
-
+ToolbarStyle 1.0 ToolbarStyle.qml
+PrimaryButtonStyle 1.0 PrimaryButtonStyle.qml
+StatesControlStyle 1.0 StatesControlStyle.qml
+SearchControlStyle 1.0 SearchControlStyle.qml
+StatusBarButtonStyle 1.0 StatusBarButtonStyle.qml
+StatusBarControlStyle 1.0 StatusBarControlStyle.qml
+TopToolbarButtonStyle 1.0 TopToolbarButtonStyle.qml
+ViewBarButtonStyle 1.0 ViewBarButtonStyle.qml
+ViewBarControlStyle 1.0 ViewBarControlStyle.qml
diff --git a/share/qtcreator/qmldesigner/statusbar/Main.qml b/share/qtcreator/qmldesigner/statusbar/Main.qml
new file mode 100644
index 0000000000..db1a125e10
--- /dev/null
+++ b/share/qtcreator/qmldesigner/statusbar/Main.qml
@@ -0,0 +1,85 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Controls
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
+import "../toolbar"
+
+import ToolBar 1.0
+
+Item {
+ id: toolbarContainer
+
+ height: 41
+ width: 4048
+
+ ToolBarBackend {
+ id: backend
+ }
+
+ Rectangle {
+ color: StudioTheme.Values.themeStatusbarBackground
+ anchors.fill: parent
+
+ Row {
+ anchors.fill: parent
+ anchors.topMargin: 3
+ anchors.leftMargin: 4
+ spacing: 29
+
+ ToolbarButton {
+ id: settingButton
+ style: StudioTheme.Values.statusbarButtonStyle
+ buttonIcon: StudioTheme.Constants.settings_medium
+ onClicked: backend.triggerProjectSettings()
+ enabled: backend.isInDesignMode || (backend.isInEditMode && backend.projectOpened)
+ }
+
+ Text {
+ height: StudioTheme.Values.statusbarButtonStyle.controlSize.height
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("Kit")
+ font.pixelSize: StudioTheme.Values.baseFontSize
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ StudioControls.TopLevelComboBox {
+ id: kits
+ style: StudioTheme.Values.statusbarControlStyle
+ width: 160
+ model: backend.kits
+ onActivated: backend.setCurrentKit(kits.currentIndex)
+ openUpwards: true
+ enabled: (backend.isInDesignMode || (backend.isInEditMode && backend.projectOpened)) && backend.isQt6
+ property int kitIndex: backend.currentKit
+ onKitIndexChanged: kits.currentIndex = backend.currentKit
+ }
+
+ Text {
+ height: StudioTheme.Values.statusbarButtonStyle.controlSize.height
+ color: StudioTheme.Values.themeTextColor
+ text: qsTr("Style")
+ font.pixelSize: StudioTheme.Values.baseFontSize
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ StudioControls.TopLevelComboBox {
+ id: styles
+ style: StudioTheme.Values.statusbarControlStyle
+ width: 160
+ model: backend.styles
+ onActivated: backend.setCurrentStyle(styles.currentIndex)
+ openUpwards: true
+ enabled: backend.isInDesignMode
+ property int currentStyleIndex: backend.currentStyle
+ onCurrentStyleIndexChanged: currentIndex = backend.currentStyle
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject b/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject
index d339fcffb5..bbf848151f 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/app_mcu.qmlproject
@@ -1,6 +1,6 @@
-/* File generated by Qt Creator */
+/* File generated by Qt Design Studio */
-import QmlProject 1.1
+import QmlProject 1.3
Project {
mainFile: "%{MainQmlFileName}"
@@ -32,6 +32,21 @@ Project {
filter: "*.ttf;*.otf"
}
+ ModuleFiles {
+ files: [
+ "imports/Constants/constants_module.qmlproject"
+ ]
+
+ MCU.qulModules: [
+ "Controls",
+ "ControlsTemplates",
+ "Timeline",
+ "Shapes"
+ ]
+ }
+
+
+ /* Following entries are for Qt Design Studio compatibility: */
Environment {
QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf"
}
@@ -39,8 +54,7 @@ Project {
qtForMCUs: true
/* List of plugin directories passed to QML runtime */
- importPaths: [ "imports", "asset_imports" ]
+ importPaths: [ "imports" ]
- /* Required for deployment */
targetDirectory: "/opt/%{ProjectName}"
}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/CMakeLists.txt b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/CMakeLists.txt
index b979c45227..42693c255d 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/CMakeLists.txt
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.15)
+cmake_minimum_required (VERSION 3.21.1)
project(%{ProjectName} VERSION 0.0.1 LANGUAGES C CXX ASM)
@@ -6,72 +6,77 @@ if (NOT TARGET Qul::Core)
find_package(Qul)
endif()
-if (Qul_VERSION VERSION_GREATER_EQUAL "1.7")
- qul_add_target(%{ProjectName})
+if (Qul_VERSION VERSION_GREATER_EQUAL "2.4")
+ qul_add_target(%{ProjectName} QML_PROJECT %{ProjectName}.qmlproject GENERATE_ENTRYPOINT)
+ app_target_setup_os(%{ProjectName})
else()
- add_executable(%{ProjectName})
- target_link_libraries(%{ProjectName}
- Qul::QuickUltralite
- Qul::QuickUltralitePlatform)
-endif()
-
-if (Qul_VERSION VERSION_GREATER_EQUAL "2.0")
- file(GLOB_RECURSE fontSources "${CMAKE_CURRENT_SOURCE_DIR}/fonts/*.ttf")
- set_property(TARGET %{ProjectName} APPEND PROPERTY QUL_FONT_FILES ${fontSources})
-elseif (Qul_VERSION VERSION_GREATER_EQUAL "1.7")
- set_property(TARGET %{ProjectName} APPEND PROPERTY QUL_FONTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/fonts")
-else()
- set(QUL_FONTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/fonts,${QUL_FONTS_DIR}")
-endif()
+ if (Qul_VERSION VERSION_GREATER_EQUAL "1.7")
+ qul_add_target(%{ProjectName})
+ else()
+ add_executable(%{ProjectName})
+ target_link_libraries(%{ProjectName}
+ Qul::QuickUltralite
+ Qul::QuickUltralitePlatform)
+ endif()
-# Using recurse search to find image files in project directory
-# Excluding MCUDefaultStyle because it exists for compatibility purposes with QDS
-file(GLOB_RECURSE imgSources "*.png" "*.svg" "*.jpg" "*.jpeg")
-list(FILTER imgSources EXCLUDE REGEX ".*/MCUDefaultStyle/.*")
+ if (Qul_VERSION VERSION_GREATER_EQUAL "2.0")
+ file(GLOB_RECURSE fontSources "${CMAKE_CURRENT_SOURCE_DIR}/fonts/*.ttf")
+ set_property(TARGET %{ProjectName} APPEND PROPERTY QUL_FONT_FILES ${fontSources})
+ elseif (Qul_VERSION VERSION_GREATER_EQUAL "1.7")
+ set_property(TARGET %{ProjectName} APPEND PROPERTY QUL_FONTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/fonts")
+ else()
+ set(QUL_FONTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/fonts,${QUL_FONTS_DIR}")
+ endif()
-if(imgSources)
- qul_add_resource(%{ProjectName} FILES ${imgSources})
-endif()
+ # Using recurse search to find image files in project directory
+ # Excluding MCUDefaultStyle because it exists for compatibility purposes with QDS
+ file(GLOB_RECURSE imgSources "*.png" "*.svg" "*.jpg" "*.jpeg")
+ list(FILTER imgSources EXCLUDE REGEX ".*/MCUDefaultStyle/.*")
-# Registering singletons as qml module
-qul_add_qml_module(ConstantsModule
- URI Constants
- QML_FILES
- imports/Constants/Constants.qml
-)
+ if(imgSources)
+ qul_add_resource(%{ProjectName} FILES ${imgSources})
+ endif()
-message(WARNING "It is recommended to replace the recursive search with the actual list of .qml files in your project.")
-file(GLOB_RECURSE qmlSources "*.qml")
-# Excluding Constants folder because it is part of another qml module
-list(FILTER qmlSources EXCLUDE REGEX ".*/imports/Constants/.*")
-# Excluding MCUDefaultStyle because it exists for compatibility purposes with QDS
-list(FILTER qmlSources EXCLUDE REGEX ".*/MCUDefaultStyle/.*")
-# Excluding binary directory because it can break builds in source dir
-list(FILTER qmlSources EXCLUDE REGEX "${CMAKE_CURRENT_BINARY_DIR}/.*")
-qul_target_qml_sources(%{ProjectName} ${qmlSources})
+ # Registering singletons as qml module
+ qul_add_qml_module(ConstantsModule
+ URI Constants
+ QML_FILES
+ imports/Constants/Constants.qml
+ )
-if (Qul_VERSION VERSION_GREATER_EQUAL "2.0")
- target_link_libraries(%{ProjectName} PRIVATE
- Qul::Timeline
- Qul::Controls
- Qul::Shapes
- ConstantsModule)
-else()
- target_link_libraries(%{ProjectName}
- Qul::QuickUltraliteTimeline
- Qul::QuickUltraliteControlsStyleDefault
- ConstantsModule)
+ message(WARNING "It is recommended to replace the recursive search with the actual list of .qml files in your project.")
+ file(GLOB_RECURSE qmlSources "*.qml")
+ # Excluding Constants folder because it is part of another qml module
+ list(FILTER qmlSources EXCLUDE REGEX ".*/imports/Constants/.*")
+ # Excluding MCUDefaultStyle because it exists for compatibility purposes with QDS
+ list(FILTER qmlSources EXCLUDE REGEX ".*/MCUDefaultStyle/.*")
+ # Excluding binary directory because it can break builds in source dir
+ list(FILTER qmlSources EXCLUDE REGEX "${CMAKE_CURRENT_BINARY_DIR}/.*")
+ qul_target_qml_sources(%{ProjectName} ${qmlSources})
- if (Qul_VERSION VERSION_GREATER_EQUAL "1.8")
+ if (Qul_VERSION VERSION_GREATER_EQUAL "2.0")
+ target_link_libraries(%{ProjectName} PRIVATE
+ Qul::Timeline
+ Qul::Controls
+ Qul::Shapes
+ ConstantsModule)
+ else()
target_link_libraries(%{ProjectName}
- Qul::QuickUltraliteShapes)
+ Qul::QuickUltraliteTimeline
+ Qul::QuickUltraliteControlsStyleDefault
+ ConstantsModule)
+
+ if (Qul_VERSION VERSION_GREATER_EQUAL "1.8")
+ target_link_libraries(%{ProjectName}
+ Qul::QuickUltraliteShapes)
+ endif()
endif()
-endif()
-app_target_setup_os(%{ProjectName})
+ app_target_setup_os(%{ProjectName})
-if (Qul_VERSION VERSION_GREATER_EQUAL "1.7")
- app_target_default_entrypoint(%{ProjectName} %{RootItemName})
-else()
- app_target_default_main(%{ProjectName} %{RootItemName})
+ if (Qul_VERSION VERSION_GREATER_EQUAL "1.7")
+ app_target_default_entrypoint(%{ProjectName} %{RootItemName})
+ else()
+ app_target_default_main(%{ProjectName} %{RootItemName})
+ endif()
endif()
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl
index ef8bfddcef..524cbd0905 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/Constants.qml.tpl
@@ -7,26 +7,4 @@ QtObject {
readonly property color backgroundColor: "#e8e8e8"
-
- /* DirectoryFontLoader doesn't work with Qt Ultralite.
- However you may want to uncomment this block to load fonts in real qml environment */
- /*
- property alias fontDirectory: directoryFontLoader.fontDirectory
- property alias relativeFontDirectory: directoryFontLoader.relativeFontDirectory
-
- readonly property font font: Qt.font({
- family: Qt.application.font.family,
- pixelSize: Qt.application.font.pixelSize
- })
- readonly property font largeFont: Qt.font({
- family: Qt.application.font.family,
- pixelSize: Qt.application.font.pixelSize * 1.6
- })
-
-
-
- property DirectoryFontLoader directoryFontLoader: DirectoryFontLoader {
- id: directoryFontLoader
- }
- */
}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/DirectoryFontLoader.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/DirectoryFontLoader.qml.tpl
deleted file mode 100644
index 59a6365df4..0000000000
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/DirectoryFontLoader.qml.tpl
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-
-import QtQuick 2.15
-import Qt.labs.folderlistmodel 2.15
-
-QtObject {
- id: loader
-
- property url fontDirectory: Qt.resolvedUrl("../../" + relativeFontDirectory)
- property string relativeFontDirectory: "fonts"
-
- function loadFont(url) {
- var fontLoader = Qt.createQmlObject('import QtQuick 2.15; FontLoader { source: "' + url + '"; }',
- loader,
- "dynamicFontLoader");
- }
-
- property FolderListModel folderModel: FolderListModel {
- id: folderModel
- folder: loader.fontDirectory
- nameFilters: [ "*.ttf", "*.otf" ]
- showDirs: false
-
- onStatusChanged: {
- if (folderModel.status == FolderListModel.Ready) {
- var i
- for (i = 0; i < count; i++) {
- loadFont(folderModel.get(i, "fileURL"))
- }
- }
- }
- }
-}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/constants_module.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/constants_module.qmlproject.tpl
new file mode 100644
index 0000000000..01711f43ef
--- /dev/null
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/constants_module.qmlproject.tpl
@@ -0,0 +1,13 @@
+import QmlProject 1.3
+
+Project {
+ MCU.Module {
+ uri: "Constants"
+ }
+
+ QmlFiles {
+ files: [
+ "Constants.qml"
+ ]
+ }
+}
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json
index 45965fad3d..8b54e315b2 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/wizard.json
@@ -161,8 +161,8 @@
"target": "%{ProjectDirectory}/imports/Constants/Constants.qml"
},
{
- "source": "DirectoryFontLoader.qml.tpl",
- "target": "%{ProjectDirectory}/imports/Constants/DirectoryFontLoader.qml"
+ "source": "constants_module.qmlproject.tpl",
+ "target": "%{ProjectDirectory}/imports/Constants/constants_module.qmlproject"
},
{
"source": "Screen01.ui.qml.tpl",
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl
index 9daf9759b1..18aec1a93e 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl
@@ -1,42 +1,40 @@
cmake_minimum_required(VERSION 3.21.1)
-set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components")
+option(BUILD_QDS_COMPONENTS "Build design studio components" ON)
project(%{ProjectName}App LANGUAGES CXX)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
-find_package(QT NAMES Qt6 COMPONENTS Gui Qml Quick)
-find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick)
+find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick)
-# To build this application you need Qt 6.2.0 or higher
-if (Qt6_VERSION VERSION_LESS 6.2.0)
-message(FATAL_ERROR "You need Qt 6.2.0 or newer to build the application.")
+if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3)
+ qt_standard_project_setup()
endif()
-qt_add_executable(${CMAKE_PROJECT_NAME} src/main.cpp)
+qt_add_executable(%{ProjectName}App src/main.cpp)
-# qt_standard_project_setup() requires Qt 6.3 or higher. See https://doc.qt.io/qt-6/qt-standard-project-setup.html for details.
-if (${QT_VERSION_MINOR} GREATER_EQUAL 3)
-qt6_standard_project_setup()
-endif()
-
-qt_add_resources(${CMAKE_PROJECT_NAME} "configuration"
+qt_add_resources(%{ProjectName}App "configuration"
PREFIX "/"
FILES
qtquickcontrols2.conf
)
-target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
- Qt${QT_VERSION_MAJOR}::Core
- Qt${QT_VERSION_MAJOR}::Gui
- Qt${QT_VERSION_MAJOR}::Quick
- Qt${QT_VERSION_MAJOR}::Qml
+target_link_libraries(%{ProjectName}App PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Quick
)
-if (${BUILD_QDS_COMPONENTS})
+if (BUILD_QDS_COMPONENTS)
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents)
-endif ()
+endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
+
+install(TARGETS %{ProjectName}App
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl
index 722b0d72ee..d598f361c1 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl
@@ -6,6 +6,7 @@ import QmlProject 1.1
Project {
mainFile: "content/App.qml"
+ mainUiFile: "content/Screen01.ui.qml"
/* Include .qml, .js, and image files from current directory and subdirectories */
QmlFiles {
@@ -100,7 +101,7 @@ Project {
/* Required for deployment */
targetDirectory: "/opt/%{ProjectName}"
- qdsVersion: "3.9"
+ qdsVersion: "4.0"
quickVersion: "%{QtQuickVersion}"
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl
index 3619df33d3..50f3db973c 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl
@@ -8,7 +8,7 @@ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
include(FetchContent)
FetchContent_Declare(
ds
- GIT_TAG qds-3.9
+ GIT_TAG qds-4.0
GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git
)
diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml
index 14bd44571d..0aa6bd4e09 100644
--- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml
+++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorPane.qml
@@ -16,10 +16,10 @@ PropertyEditorPane {
topSection.refreshPreview()
}
- // Called also from C++ to close context menu on focus out
+ // Called from C++ to close context menu on focus out
function closeContextMenu()
{
- // Nothing
+ Controller.closeContextMenu()
}
TextureEditorTopSection {
diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorToolBar.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorToolBar.qml
index 710c6356c8..6a61a76ef6 100644
--- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorToolBar.qml
+++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorToolBar.qml
@@ -1,69 +1,57 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 2.15
+import QtQuick
import QtQuickDesignerTheme 1.0
-import HelperWidgets 2.0
+import HelperWidgets 2.0 as HelperWidgets
import StudioTheme 1.0 as StudioTheme
import TextureToolBarAction 1.0
Rectangle {
id: root
- color: StudioTheme.Values.themeSectionHeadBackground
+ color: StudioTheme.Values.themeToolbarBackground
+ height: StudioTheme.Values.toolbarHeight
width: row.width
- height: 40
signal toolBarAction(int action)
Row {
id: row
-
+ spacing: StudioTheme.Values.toolbarSpacing
anchors.verticalCenter: parent.verticalCenter
leftPadding: 6
- IconButton {
- icon: StudioTheme.Constants.applyMaterialToSelected
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.apply_medium
enabled: hasTexture && hasSingleModelSelection && hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
tooltip: qsTr("Apply texture to selected model's material.")
+ onClicked: root.toolBarAction(ToolBarAction.ApplyToSelected)
}
- IconButton {
- icon: StudioTheme.Constants.newMaterial
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.create_medium
enabled: hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.AddNewTexture)
tooltip: qsTr("Create new texture.")
+ onClicked: root.toolBarAction(ToolBarAction.AddNewTexture)
}
- IconButton {
- icon: StudioTheme.Constants.deleteMaterial
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.delete_medium
enabled: hasTexture && hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentTexture)
tooltip: qsTr("Delete current texture.")
+ onClicked: root.toolBarAction(ToolBarAction.DeleteCurrentTexture)
}
- IconButton {
- icon: StudioTheme.Constants.openMaterialBrowser
-
- normalColor: StudioTheme.Values.themeSectionHeadBackground
- iconSize: StudioTheme.Values.bigIconFontSize
- buttonSize: root.height
- enabled: hasQuick3DImport && hasMaterialLibrary
- onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
+ HelperWidgets.AbstractButton {
+ style: StudioTheme.Values.viewBarButtonStyle
+ buttonIcon: StudioTheme.Constants.materialBrowser_medium
+ enabled: hasTexture && hasQuick3DImport && hasMaterialLibrary
tooltip: qsTr("Open material browser.")
+ onClicked: root.toolBarAction(ToolBarAction.OpenMaterialBrowser)
}
}
}
diff --git a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml
index f52d126165..7c03d79571 100644
--- a/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml
+++ b/share/qtcreator/qmldesigner/textureEditorQmlSource/TextureEditorTopSection.qml
@@ -35,8 +35,9 @@ Column {
Image {
id: texturePreview
asynchronous: true
- sourceSize.width: 150
- sourceSize.height: 150
+ width: 150
+ height: 150
+ fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
source: "image://qmldesigner_thumbnails/" + resolveResourcePath(backendValues.source.valueToString)
}
diff --git a/share/qtcreator/qmldesigner/toolbar/CrumbleBar.qml b/share/qtcreator/qmldesigner/toolbar/CrumbleBar.qml
new file mode 100644
index 0000000000..930bd4fa77
--- /dev/null
+++ b/share/qtcreator/qmldesigner/toolbar/CrumbleBar.qml
@@ -0,0 +1,93 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Controls
+
+ListView {
+ id: root
+
+ property real crumbleWidth: 166
+ property real crumbleHeight: 36
+
+ property real inset: 5
+ property real strokeWidth: 1
+ property real textLeftMargin: 18
+ property real textTopMargin: 6
+ property real textRightMargin: 6
+ property real textBottomMargin: 6
+
+ property alias font: fontMetrics.font
+
+ signal clicked(index: int)
+
+ boundsBehavior: Flickable.StopAtBounds
+ flickableDirection: Flickable.HorizontalFlick
+ interactive: true
+
+ orientation: ListView.Horizontal
+ clip: true
+ focus: true
+
+ function updateContentX() {
+ root.contentX = (root.contentWidth < root.width) ? 0 : root.contentWidth - root.width
+ }
+
+ onCountChanged: root.updateContentX()
+ onWidthChanged: root.updateContentX()
+
+ visible: root.count > 1 && root.width > (root.crumbleWidth) - 24
+
+ delegate: CrumbleBread {
+ text: fileName
+ tooltip: fileAddress
+
+ width: root.crumbleWidth
+ height: root.crumbleHeight
+
+ inset: root.inset
+ strokeWidth: root.strokeWidth
+ textLeftMargin: root.textLeftMargin
+ textTopMargin: root.textTopMargin
+ textRightMargin: root.textRightMargin
+ textBottomMargin: root.textBottomMargin
+
+ font: root.font
+
+ modelSize: root.count
+
+ onClicked: {
+ if (index + 1 < root.count)
+ root.clicked(index)
+ }
+ }
+
+ add: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1.0
+ duration: 400
+ }
+ NumberAnimation {
+ property: "scale"
+ from: 0
+ to: 1.0
+ duration: 400
+ }
+ }
+
+ displaced: Transition {
+ NumberAnimation {
+ property: "x"
+ duration: 400
+ easing.type: Easing.OutBack
+ }
+ }
+
+ FontMetrics {
+ id: fontMetrics
+ font.pixelSize: 12
+ font.family: "SF Pro"
+ }
+}
diff --git a/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml b/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml
new file mode 100644
index 0000000000..538c135642
--- /dev/null
+++ b/share/qtcreator/qmldesigner/toolbar/CrumbleBread.qml
@@ -0,0 +1,177 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Shapes
+import StudioTheme 1.0 as StudioTheme
+
+Item {
+ id: root
+ antialiasing: true
+
+ property int modelSize
+
+ /* Colors might come from Theme */
+ property color idleBackgroundColor: StudioTheme.Values.themeControlBackground_toolbarIdle
+ property color idleStrokeColor: StudioTheme.Values.controlOutline_toolbarIdle
+ property color idleTextColor: StudioTheme.Values.themeTextColor
+ property color hoverBackgroundColor: StudioTheme.Values.themeControlBackground_topToolbarHover
+ property color hoverStrokeColor: StudioTheme.Values.controlOutline_toolbarHover
+ property color activeColor: StudioTheme.Values.themeInteraction
+ property color activeTextColor: StudioTheme.Values.themeTextSelectedTextColor
+
+ property string tooltip
+
+ property alias text: label.text
+ property alias font: label.font
+ property alias inset: backgroundPath.inset
+ property alias strokeWidth: backgroundPath.strokeWidth
+
+ property real textLeftMargin: 18
+ property real textTopMargin: 6
+ property real textRightMargin: 6
+ property real textBottomMargin: 6
+
+ readonly property int itemIndex: index
+ readonly property bool isFirst: itemIndex === 0
+ readonly property bool isLast: (itemIndex + 1) === modelSize
+
+ signal clicked(int callIdx)
+
+ width: 166
+ height: 36
+
+ Shape {
+ id: backgroundShape
+ anchors.fill: root
+
+ antialiasing: root.antialiasing
+ layer.enabled: antialiasing
+ layer.smooth: antialiasing
+ layer.samples: antialiasing ? 4 : 0
+
+ ShapePath {
+ id: backgroundPath
+
+ joinStyle: ShapePath.MiterJoin
+ fillColor: root.idleBackgroundColor
+ strokeColor: root.idleStrokeColor
+ strokeWidth: 1
+
+ property real inset: 5
+ property real rightOut: root.isLast ? 0 : root.inset
+ property real leftIn: root.isFirst ? 0 : root.inset
+ property real strokeOffset: backgroundPath.strokeWidth / 2
+
+ property real topY: backgroundPath.strokeOffset
+ property real bottomY: backgroundShape.height - backgroundPath.strokeOffset
+ property real halfY: (backgroundPath.topY + backgroundPath.bottomY) / 2
+
+ startX: backgroundPath.strokeOffset
+ startY: backgroundPath.topY
+
+ PathLine {
+ x: backgroundShape.width - backgroundPath.strokeOffset - backgroundPath.rightOut
+ y: backgroundPath.topY
+ }
+ PathLine {
+ x: backgroundShape.width - backgroundPath.strokeOffset
+ y: backgroundPath.halfY
+ }
+ PathLine {
+ x: backgroundShape.width - backgroundPath.strokeOffset - backgroundPath.rightOut
+ y: backgroundPath.bottomY
+ }
+ PathLine {
+ x: backgroundPath.strokeOffset
+ y: backgroundPath.bottomY
+ }
+ PathLine {
+ x: backgroundPath.leftIn + backgroundPath.strokeOffset
+ y: backgroundPath.halfY
+ }
+ PathLine {
+ x: backgroundPath.strokeOffset
+ y: backgroundPath.topY
+ }
+ }
+ }
+
+ Text {
+ id: label
+
+ anchors.fill: parent
+ anchors.leftMargin: root.textLeftMargin
+ anchors.topMargin: root.textTopMargin
+ anchors.rightMargin: root.textRightMargin
+ anchors.bottomMargin: root.textBottomMargin
+
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+
+ color: root.idleTextColor
+
+ wrapMode: Text.Wrap
+ elide: Text.ElideRight
+ maximumLineCount: 1
+
+ text: "Crumble File"
+ font.pixelSize: 12
+ font.family: "SF Pro"
+
+ ToolTip.text: root.tooltip
+ ToolTip.visible: mouseArea.containsMouse
+ ToolTip.delay: 1000
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: root.clicked(root.itemIndex)
+ }
+
+ states: [
+ State {
+ name: "idle"
+ when: !mouseArea.containsMouse && !mouseArea.pressed && !root.isLast
+
+ PropertyChanges {
+ target: backgroundPath
+ fillColor: root.idleBackgroundColor
+ strokeColor: root.idleStrokeColor
+ }
+ },
+ State {
+ name: "active"
+ when: root.isLast
+ extend: "pressed"
+ },
+ State {
+ name: "hover"
+ when: mouseArea.containsMouse && !mouseArea.pressed
+
+ PropertyChanges {
+ target: backgroundPath
+ fillColor: root.hoverBackgroundColor
+ strokeColor: root.hoverStrokeColor
+ }
+ },
+ State {
+ name: "pressed"
+ when: mouseArea.pressed
+
+ PropertyChanges {
+ target: backgroundPath
+ strokeColor: root.activeColor
+ fillColor: root.activeColor
+ }
+
+ PropertyChanges {
+ target: label
+ color: root.activeTextColor
+ }
+ }
+ ]
+}
diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml
new file mode 100644
index 0000000000..3daf50c14b
--- /dev/null
+++ b/share/qtcreator/qmldesigner/toolbar/Main.qml
@@ -0,0 +1,403 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Controls
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
+import QtQuickDesignerTheme 1.0
+
+import ToolBar 1.0
+
+Rectangle {
+ id: root
+ color: StudioTheme.Values.themeToolbarBackground
+
+ readonly property int mediumBreakpoint: 720
+ readonly property int largeBreakpoint: 1200
+ readonly property bool flyoutEnabled: root.width < root.largeBreakpoint
+
+ ToolBarBackend {
+ id: backend
+ }
+
+ Item {
+ id: topToolbarOtherMode
+ anchors.fill: parent
+ visible: !backend.isInDesignMode
+
+ ToolbarButton {
+ id: homeOther
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ tooltip: backend.isDesignModeEnabled ? qsTr("Switch to Design Mode.")
+ : qsTr("Switch to Welcome Mode.")
+ buttonIcon: backend.isDesignModeEnabled ? StudioTheme.Constants.designMode_large
+ : StudioTheme.Constants.home_large
+ onClicked: backend.triggerModeChange()
+ }
+
+ Text {
+ id: backTo
+ visible: backend.isDesignModeEnabled
+ anchors.verticalCenter: parent.verticalCenter
+ text: qsTr("Return to Design")
+ anchors.left: homeOther.right
+ anchors.leftMargin: 10
+ color: StudioTheme.Values.themeTextColor
+ }
+ }
+
+ Item {
+ id: topToolbar
+ anchors.fill: parent
+ visible: backend.isInDesignMode
+
+ ToolbarButton {
+ id: home
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ buttonIcon: StudioTheme.Constants.home_large
+ onClicked: backend.triggerModeChange()
+ tooltip: qsTr("Switch to Welcome Mode.")
+ }
+
+ ToolbarButton {
+ id: runProject
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: home.right
+ anchors.leftMargin: 10
+ buttonIcon: StudioTheme.Constants.runProjOutline_large
+ style: StudioTheme.ToolbarStyle {
+ radius: StudioTheme.Values.smallRadius
+
+ icon: StudioTheme.ControlStyle.IconColors {
+ idle: StudioTheme.Values.themeIdleGreen
+ hover: StudioTheme.Values.themeRunningGreen
+ interaction: "#ffffff"
+ disabled: "#636363"
+ }
+
+ background: StudioTheme.ControlStyle.BackgroundColors {
+ idle: StudioTheme.Values.themeControlBackground_toolbarIdle
+ hover: StudioTheme.Values.themeControlBackground_topToolbarHover
+ interaction: StudioTheme.Values.themeInteraction
+ disabled: StudioTheme.Values.themeControlBackground_toolbarIdle
+ }
+
+ border: StudioTheme.ControlStyle.BorderColors {
+ idle: StudioTheme.Values.themeControlBackground_toolbarIdle
+ hover: StudioTheme.Values.themeControlBackground_topToolbarHover
+ interaction: StudioTheme.Values.themeInteraction
+ disabled: StudioTheme.Values.themeControlBackground_toolbarIdle
+ }
+ }
+
+ onClicked: backend.runProject()
+ tooltip: qsTr("Run Project")
+ }
+
+ ToolbarButton {
+ id: livePreviewButton
+ style: StudioTheme.Values.primaryToolbarStyle
+ width: 96
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: runProject.right
+ anchors.leftMargin: 10
+ iconFont: StudioTheme.Constants.font
+ buttonIcon: qsTr("Live Preview")
+
+ onClicked: {
+ livePreview.trigger()
+ }
+
+ MouseArea {
+ acceptedButtons: Qt.RightButton
+ anchors.fill: parent
+
+ onClicked: {
+ var p = livePreviewButton.mapToGlobal(0, 0)
+ backend.showZoomMenu(p.x, p.y)
+ }
+ }
+
+ ActionSubscriber {
+ id: livePreview
+ actionId: "LivePreview"
+ }
+ }
+
+ StudioControls.TopLevelComboBox {
+ id: currentFile
+ style: StudioTheme.Values.toolbarStyle
+ width: 320 - ((root.width > root.mediumBreakpoint) ? 0 : (root.mediumBreakpoint - root.width))
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: livePreviewButton.right
+ anchors.leftMargin: 10
+ model: backend.documentModel
+
+ property int currentDocumentIndex: backend.documentIndex
+ onCurrentDocumentIndexChanged: currentFile.currentIndex = currentFile.currentDocumentIndex
+ onActivated: backend.openFileByIndex(index)
+ }
+
+ ToolbarButton {
+ id: backButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: currentFile.right
+ anchors.leftMargin: 10
+ enabled: backend.canGoBack
+ tooltip: qsTr("Go Back")
+ buttonIcon: StudioTheme.Constants.previousFile_large
+ iconRotation: 0
+
+ onClicked: backend.goBackward()
+ }
+
+ ToolbarButton {
+ id: forwardButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: backButton.right
+ anchors.leftMargin: 10
+ enabled: backend.canGoForward
+ tooltip: qsTr("Go Forward")
+ buttonIcon: StudioTheme.Constants.nextFile_large
+
+ onClicked: backend.goForward()
+ }
+
+ ToolbarButton {
+ id: closeButton
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: forwardButton.right
+ anchors.leftMargin: 10
+ tooltip: qsTr("Close")
+ buttonIcon: StudioTheme.Constants.closeFile_large
+
+ onClicked: backend.closeCurrentDocument()
+ }
+
+ CrumbleBar {
+ id: flickable
+ height: 36
+ anchors.left: closeButton.right
+ anchors.leftMargin: 10
+ anchors.right: createComponent.left
+ anchors.rightMargin: 10
+ anchors.verticalCenter: parent.verticalCenter
+ model: CrumbleBarModel {
+ id: crumbleBarModel
+ }
+
+ onClicked: crumbleBarModel.onCrumblePathElementClicked(index)
+ }
+
+ ToolbarButton {
+ id: createComponent
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: enterComponent.left
+ anchors.rightMargin: 10
+ enabled: moveToComponentBackend.available
+ tooltip: moveToComponentBackend.tooltip
+ buttonIcon: StudioTheme.Constants.createComponent_large
+ visible: !root.flyoutEnabled
+
+ onClicked: moveToComponentBackend.trigger()
+
+ ActionSubscriber {
+ id: moveToComponentBackend
+ actionId: "MakeComponent"
+ }
+ }
+
+ ToolbarButton {
+ id: enterComponent
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: workspaces.left
+ anchors.rightMargin: 10
+ enabled: goIntoComponentBackend.available
+ tooltip: goIntoComponentBackend.tooltip
+ buttonIcon: StudioTheme.Constants.editComponent_large
+ visible: !root.flyoutEnabled
+
+ onClicked: goIntoComponentBackend.trigger()
+
+ ActionSubscriber {
+ id: goIntoComponentBackend
+ actionId: "GoIntoComponent"
+ }
+ }
+
+ StudioControls.TopLevelComboBox {
+ id: workspaces
+ style: StudioTheme.Values.toolbarStyle
+ width: 210
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: annotations.left
+ anchors.rightMargin: 10
+ model: backend.workspaces
+ suffix: qsTr(" Workspace")
+ property int currentWorkspaceIndex: workspaces.find(backend.currentWorkspace)
+ onCurrentWorkspaceIndexChanged: workspaces.currentIndex = workspaces.currentWorkspaceIndex
+
+ visible: !root.flyoutEnabled
+
+ onActivated: backend.setCurrentWorkspace(workspaces.currentText)
+ }
+
+ ToolbarButton {
+ id: annotations
+ visible: false
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: shareButton.left
+ anchors.rightMargin: 10
+ tooltip: qsTr("Edit Annotations")
+ buttonIcon: StudioTheme.Constants.annotations_large
+ //visible: !root.flyoutEnabled
+
+ onClicked: backend.editGlobalAnnoation()
+ width: 0
+ }
+
+ ToolbarButton {
+ id: shareButton
+ style: StudioTheme.Values.primaryToolbarStyle
+ width: 96
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: moreItems.left
+ anchors.rightMargin: 8
+ iconFont: StudioTheme.Constants.font
+ buttonIcon: qsTr("Share")
+ visible: !root.flyoutEnabled
+
+ onClicked: backend.shareApplicationOnline()
+ }
+
+ ToolbarButton {
+ // this needs a pop-up panel where overflow toolbar content goes when toolbar is not wide enough
+ id: moreItems
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: 10
+ tooltip: qsTr("More Items")
+ buttonIcon: StudioTheme.Constants.more_medium
+ enabled: root.flyoutEnabled
+ checkable: true
+ checked: window.visible
+ checkedInverted: true
+ onClicked: {
+ if (window.visible) {
+ window.close()
+ } else {
+ var originMapped = moreItems.mapToGlobal(0,0)
+ window.x = originMapped.x + moreItems.width - window.width
+ window.y = originMapped.y + moreItems.height + 7
+ window.show()
+ window.requestActivate()
+ }
+ }
+ }
+
+ Window {
+ id: window
+
+ readonly property int padding: 6
+
+ width: row.width + window.padding * 2
+ height: row.height + workspacesFlyout.height + 3 * window.padding
+ + (workspacesFlyout.popup.opened ? workspacesFlyout.popup.height : 0)
+ visible: false
+ flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.NoDropShadowWindowHint
+ modality: Qt.NonModal
+ transientParent: null
+ color: "transparent"
+
+ onActiveFocusItemChanged: {
+ if (window.activeFocusItem === null && !moreItems.hovered)
+ window.close()
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: StudioTheme.Values.themePopupBackground
+ radius: StudioTheme.Values.smallRadius
+
+ Column {
+ id: column
+ anchors.margins: window.padding
+ anchors.fill: parent
+ spacing: window.padding
+
+ Row {
+ id: row
+ spacing: window.padding
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ ToolbarButton {
+ style: StudioTheme.Values.statusbarButtonStyle
+ anchors.verticalCenter: parent.verticalCenter
+ enabled: moveToComponentBackend.available
+ tooltip: moveToComponentBackend.tooltip
+ buttonIcon: StudioTheme.Constants.createComponent_large
+
+ onClicked: moveToComponentBackend.trigger()
+
+ ActionSubscriber {
+ actionId: "MakeComponent"
+ }
+ }
+
+ ToolbarButton {
+ style: StudioTheme.Values.statusbarButtonStyle
+ anchors.verticalCenter: parent.verticalCenter
+ enabled: goIntoComponentBackend.available
+ tooltip: goIntoComponentBackend.tooltip
+ buttonIcon: StudioTheme.Constants.editComponent_large
+
+ onClicked: goIntoComponentBackend.trigger()
+
+ ActionSubscriber {
+ actionId: "GoIntoComponent"
+ }
+ }
+
+ ToolbarButton {
+ visible: false
+ style: StudioTheme.Values.statusbarButtonStyle
+ anchors.verticalCenter: parent.verticalCenter
+ tooltip: qsTr("Edit Annotations")
+ buttonIcon: StudioTheme.Constants.annotations_large
+
+ onClicked: backend.editGlobalAnnoation()
+ }
+
+ ToolbarButton {
+ anchors.verticalCenter: parent.verticalCenter
+ style: StudioTheme.Values.primaryToolbarStyle
+ width: shareButton.width
+ iconFont: StudioTheme.Constants.font
+ buttonIcon: qsTr("Share")
+
+ onClicked: backend.shareApplicationOnline()
+ }
+ }
+
+ StudioControls.ComboBox {
+ id: workspacesFlyout
+ anchors.horizontalCenter: parent.horizontalCenter
+ actionIndicatorVisible: false
+ style: StudioTheme.Values.statusbarControlStyle
+ width: row.width
+ maximumPopupHeight: 400
+ model: backend.workspaces
+ currentIndex: workspacesFlyout.find(backend.currentWorkspace)
+
+ onCompressedActivated: backend.setCurrentWorkspace(workspacesFlyout.currentText)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml b/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml
new file mode 100644
index 0000000000..706f0f1960
--- /dev/null
+++ b/share/qtcreator/qmldesigner/toolbar/ToolbarButton.qml
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
+import HelperWidgets 2.0
+
+StudioControls.AbstractButton {
+ id: button
+
+ property alias tooltip: toolTipArea.tooltip
+
+ style: StudioTheme.Values.toolbarButtonStyle
+ hover: toolTipArea.containsMouse
+
+ ToolTipArea {
+ id: toolTipArea
+ anchors.fill: parent
+ // Without setting the acceptedButtons property the clicked event won't
+ // reach the AbstractButton, it will be consumed by the ToolTipArea
+ acceptedButtons: Qt.NoButton
+ }
+}
diff --git a/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk
new file mode 100644
index 0000000000..2336c94ffa
--- /dev/null
+++ b/share/qtcreator/qmldesigner/workspacePresets/Advanced-3D.wrk
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><QtAdvancedDockingSystem version="1" userVersion="0" containers="1"><container floating="false"><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="3"><splitter orientation="Vertical" count="3"><area tabs="2" current="Navigator"><widget name="Navigator" closed="false"/><widget name="DebugView" closed="true"/></area><area tabs="3" current="ContentLibrary"><widget name="ContentLibrary" closed="false"/><widget name="Components" closed="false"/><widget name="Assets" closed="true"/></area><area tabs="3" current="OpenDocuments"><widget name="Projects" closed="true"/><widget name="FileSystem" closed="true"/><widget name="OpenDocuments" closed="true"/></area><sizes>622 527 0</sizes></splitter><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="3"><splitter orientation="Vertical" count="2"><area tabs="1" current="Editor3D"><widget name="Editor3D" closed="false"/></area><area tabs="1" current="FormEditor"><widget name="FormEditor" closed="false"/></area><sizes>620 529</sizes></splitter><area tabs="1" current="TextEditor"><widget name="TextEditor" closed="true"/></area><splitter orientation="Vertical" count="2"><area tabs="1" current="MaterialBrowser"><widget name="MaterialBrowser" closed="false"/></area><area tabs="3" current="MaterialEditor"><widget name="MaterialEditor" closed="false"/><widget name="TextureEditor" closed="false"/><widget name="Properties" closed="false"/></area><sizes>575 574</sizes></splitter><sizes>1233 0 533</sizes></splitter><splitter orientation="Horizontal" count="2"><area tabs="3" current="Timelines"><widget name="TransitionEditor" closed="true"/><widget name="CurveEditorId" closed="true"/><widget name="Timelines" closed="true"/></area><area tabs="1" current="OutputPane"><widget name="OutputPane" closed="true"/></area><sizes>0 0</sizes></splitter><sizes>1150 0</sizes></splitter><area tabs="1" current="ConnectionView"><widget name="ConnectionView" closed="true"/></area><sizes>536 1767 0</sizes></splitter><area tabs="1" current="StatesEditor"><widget name="StatesEditor" closed="true"/></area><sizes>1150 0</sizes></splitter></container></QtAdvancedDockingSystem>
diff --git a/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk
index b74da2a048..7282eb1a63 100644
--- a/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk
+++ b/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><QtAdvancedDockingSystem version="1" userVersion="0" containers="1"><container floating="false"><splitter orientation="Horizontal" count="3"><splitter orientation="Vertical" count="3"><area tabs="2" current="Navigator"><widget name="Navigator" closed="false"/><widget name="Projects" closed="false"/></area><area tabs="2" current="FileSystem"><widget name="FileSystem" closed="true"/><widget name="OpenDocuments" closed="true"/></area><area tabs="2" current="Components"><widget name="Components" closed="false"/><widget name="Assets" closed="false"/></area><sizes>541 0 426</sizes></splitter><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="2"><splitter orientation="Vertical" count="2"><area tabs="1" current="FormEditor"><widget name="FormEditor" closed="false"/></area><area tabs="1" current="Editor3D"><widget name="Editor3D" closed="false"/></area><sizes>353 353</sizes></splitter><splitter orientation="Vertical" count="2"><area tabs="1" current="TextEditor"><widget name="TextEditor" closed="true"/></area><area tabs="1" current="OutputPane"><widget name="OutputPane" closed="true"/></area><sizes>0 0</sizes></splitter><sizes>919 0</sizes></splitter><splitter orientation="Horizontal" count="2"><area tabs="3" current="Timelines"><widget name="Timelines" closed="false"/><widget name="CurveEditorId" closed="false"/><widget name="StatesEditor" closed="true"/></area><area tabs="1" current=""><widget name="TransitionEditor" closed="true"/></area><sizes>919 0</sizes></splitter><sizes>707 260</sizes></splitter><splitter orientation="Vertical" count="3"><area tabs="1" current="Properties"><widget name="Properties" closed="false"/></area><area tabs="1" current="TranslationsView"><widget name="TranslationsView" closed="true"/></area><area tabs="1" current="ConnectionView"><widget name="ConnectionView" closed="true"/></area><sizes>968 0 0</sizes></splitter><sizes>312 919 408</sizes></splitter></container></QtAdvancedDockingSystem>
+<?xml version="1.0" encoding="UTF-8"?><QtAdvancedDockingSystem version="1" userVersion="0" containers="1"><container floating="false"><splitter orientation="Horizontal" count="2"><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="3"><area tabs="4" current="Components"><widget name="Components" closed="false"/><widget name="Assets" closed="false"/><widget name="MaterialBrowser" closed="false"/><widget name="ContentLibrary" closed="false"/></area><area tabs="2" current="Editor3D"><widget name="Editor3D" closed="false"/><widget name="FormEditor" closed="false"/></area><area tabs="1" current="TranslationsView"><widget name="TranslationsView" closed="true"/></area><sizes>688 2515 0</sizes></splitter><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="2"><area tabs="3" current="StatesEditor"><widget name="StatesEditor" closed="false"/><widget name="TransitionEditor" closed="false"/><widget name="CurveEditorId" closed="false"/></area><area tabs="1" current="Timelines"><widget name="Timelines" closed="false"/></area><sizes>1604 1599</sizes></splitter><area tabs="3" current="OutputPane"><widget name="OutputPane" closed="false"/><widget name="TextEditor" closed="false"/><widget name="ConnectionView" closed="true"/></area><sizes>544 42</sizes></splitter><sizes>1426 587</sizes></splitter><splitter orientation="Vertical" count="2"><area tabs="4" current="Navigator"><widget name="Navigator" closed="false"/><widget name="Projects" closed="false"/><widget name="FileSystem" closed="false"/><widget name="OpenDocuments" closed="true"/></area><area tabs="3" current="Properties"><widget name="Properties" closed="false"/><widget name="MaterialEditor" closed="false"/><widget name="TextureEditor" closed="false"/></area><sizes>994 1019</sizes></splitter><sizes>3204 635</sizes></splitter></container></QtAdvancedDockingSystem>
diff --git a/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk b/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk
index aec1d2e294..e7ac0763b1 100644
--- a/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk
+++ b/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><QtAdvancedDockingSystem version="1" userVersion="0" containers="1"><container floating="false"><splitter orientation="Horizontal" count="3"><splitter orientation="Vertical" count="3"><area tabs="2" current="Navigator"><widget name="Navigator" closed="false"/><widget name="Projects" closed="false"/></area><area tabs="2" current="OpenDocuments"><widget name="FileSystem" closed="true"/><widget name="OpenDocuments" closed="true"/></area><area tabs="2" current="Components"><widget name="Components" closed="false"/><widget name="Assets" closed="false"/></area><sizes>487 0 480</sizes></splitter><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="2"><splitter orientation="Vertical" count="2"><area tabs="1" current="FormEditor"><widget name="FormEditor" closed="false"/></area><area tabs="1" current="Editor3D"><widget name="Editor3D" closed="true"/></area><sizes>707 0</sizes></splitter><splitter orientation="Vertical" count="2"><area tabs="1" current="TextEditor"><widget name="TextEditor" closed="true"/></area><area tabs="1" current="OutputPane"><widget name="OutputPane" closed="true"/></area><sizes>707 0</sizes></splitter><sizes>914 0</sizes></splitter><splitter orientation="Horizontal" count="2"><area tabs="2" current="StatesEditor"><widget name="StatesEditor" closed="false"/><widget name="Timelines" closed="true"/></area><area tabs="2" current="TransitionEditor"><widget name="CurveEditorId" closed="true"/><widget name="TransitionEditor" closed="true"/></area><sizes>914 0</sizes></splitter><sizes>707 260</sizes></splitter><splitter orientation="Vertical" count="3"><area tabs="1" current="Properties"><widget name="Properties" closed="false"/></area><area tabs="1" current="TranslationsView"><widget name="TranslationsView" closed="true"/></area><area tabs="1" current="ConnectionView"><widget name="ConnectionView" closed="false"/></area><sizes>707 0 260</sizes></splitter><sizes>310 914 406</sizes></splitter></container></QtAdvancedDockingSystem>
+<?xml version="1.0" encoding="UTF-8"?><QtAdvancedDockingSystem version="1" userVersion="0" containers="1"><container floating="false"><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="3"><splitter orientation="Vertical" count="3"><area tabs="2" current="Navigator"><widget name="Navigator" closed="false"/><widget name="Projects" closed="false"/></area><area tabs="2" current="OpenDocuments"><widget name="FileSystem" closed="true"/><widget name="OpenDocuments" closed="true"/></area><area tabs="2" current="Components"><widget name="Components" closed="false"/><widget name="Assets" closed="false"/></area><sizes>439 0 433</sizes></splitter><splitter orientation="Vertical" count="2"><splitter orientation="Horizontal" count="2"><splitter orientation="Vertical" count="2"><area tabs="1" current="FormEditor"><widget name="FormEditor" closed="false"/></area><area tabs="1" current="Editor3D"><widget name="Editor3D" closed="true"/></area><sizes>873 0</sizes></splitter><splitter orientation="Vertical" count="2"><area tabs="1" current="TextEditor"><widget name="TextEditor" closed="true"/></area><area tabs="1" current="OutputPane"><widget name="OutputPane" closed="true"/></area><sizes>0 0</sizes></splitter><sizes>1291 0</sizes></splitter><splitter orientation="Horizontal" count="2"><area tabs="1" current=""><widget name="Timelines" closed="true"/></area><area tabs="2" current="TransitionEditor"><widget name="CurveEditorId" closed="true"/><widget name="TransitionEditor" closed="true"/></area><sizes>1291 0</sizes></splitter><sizes>873 0</sizes></splitter><splitter orientation="Vertical" count="3"><area tabs="1" current="Properties"><widget name="Properties" closed="false"/></area><area tabs="1" current="TranslationsView"><widget name="TranslationsView" closed="true"/></area><area tabs="1" current="ConnectionView"><widget name="ConnectionView" closed="false"/></area><sizes>637 0 235</sizes></splitter><sizes>438 1291 573</sizes></splitter><area tabs="1" current="StatesEditor"><widget name="StatesEditor" closed="false"/></area><sizes>873 276</sizes></splitter></container></QtAdvancedDockingSystem>
diff --git a/share/qtcreator/scripts/openTerminal.py b/share/qtcreator/scripts/openTerminal.py
deleted file mode 100755
index 3dd1bcb1b9..0000000000
--- a/share/qtcreator/scripts/openTerminal.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2018 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-import os
-import pipes
-import stat
-import subprocess
-import sys
-from tempfile import NamedTemporaryFile
-
-def quote_shell(arg):
- return pipes.quote(arg)
-
-def clean_environment_script():
- # keep some basic environment settings to ensure functioning terminal and config files
- env_to_keep = ' '.join(['_', 'HOME', 'LOGNAME', 'PWD', 'SHELL', 'TMPDIR', 'USER', 'TERM',
- 'TERM_PROGRAM', 'TERM_PROGRAM_VERSION', 'TERM_SESSION_CLASS_ID',
- 'TERM_SESSION_ID'])
- return r'''
-function ignore() {
- local keys=(''' + env_to_keep + ''')
- local v=$1
- for e in "${keys[@]}"; do [[ "$e" == "$v" ]] && return 0; done
-}
-while read -r line; do
- key=$(echo $line | /usr/bin/cut -d '=' -f 1)
- ignore $key || unset $key
-done < <(env)
-'''
-
-def system_login_script_bash():
- return r'''
-[ -r /etc/profile ] && source /etc/profile
-# fake behavior of /etc/profile as if BASH was set. It isn't for non-interactive shell
-export PS1='\h:\W \u\$ '
-[ -r /etc/bashrc ] && source /etc/bashrc
-'''
-
-def login_script_bash():
- return r'''
-if [ -f $HOME/.bash_profile ]; then
- source $HOME/.bash_profile
-elif [ -f $HOME/.bash_login ]; then
- source $HOME/.bash_login ]
-elif [ -f $HOME/.profile ]; then
- source $HOME/.profile
-fi
-'''
-
-def system_login_script_zsh():
- return '[ -r /etc/profile ] && source /etc/profile\n'
-
-def login_script_zsh():
- return r'''
-[ -r $HOME/.zprofile ] && source $HOME/.zprofile
-[ -r $HOME/.zshrc ] && source $HOME/.zshrc
-[ -r $HOME/.zlogin ] && source $HOME/.zlogin
-'''
-
-def environment_script():
- return ''.join(['export ' + quote_shell(key + '=' + os.environ[key]) + '\n'
- for key in os.environ])
-
-def zsh_setup(shell):
- return (shell,
- system_login_script_zsh,
- login_script_zsh,
- shell + ' -c',
- shell + ' -d -f')
-
-def bash_setup(shell):
- bash = shell if shell is not None and shell.endswith('/bash') else '/bin/bash'
- return (bash,
- system_login_script_bash,
- login_script_bash,
- bash + ' -c',
- bash + ' --noprofile -l')
-
-def main():
- # create temporary file to be sourced into bash that deletes itself
- with NamedTemporaryFile(mode='wt', delete=False) as shell_script:
- shell = os.environ.get('SHELL')
- shell, system_login_script, login_script, non_interactive_shell, interactive_shell = (
- zsh_setup(shell) if shell is not None and shell.endswith('/zsh')
- else bash_setup(shell))
-
- commands = ('#!' + shell + '\n' +
- 'rm ' + quote_shell(shell_script.name) + '\n' +
- clean_environment_script() +
- system_login_script() + # /etc/(z)profile by default resets the path, so do first
- environment_script() +
- login_script() +
- 'cd ' + quote_shell(os.getcwd()) + '\n' +
- ('exec ' + non_interactive_shell + ' ' +
- quote_shell(' '.join([quote_shell(arg) for arg in sys.argv[1:]])) + '\n'
- if len(sys.argv) > 1 else 'exec ' + interactive_shell + '\n')
- )
- shell_script.write(commands)
- shell_script.flush()
- os.chmod(shell_script.name, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
- # TODO /usr/bin/open doesn't work with notarized app in macOS 13,
- # use osascript instead (QTCREATORBUG-28683).
- # This has the disadvantage that the Terminal windows doesn't close
- # automatically anymore.
- # subprocess.call(['/usr/bin/open', '-a', 'Terminal', shell_script.name])
- subprocess.call(['/usr/bin/osascript', '-e', 'tell app "Terminal" to activate'])
- subprocess.call(['/usr/bin/osascript', '-e', 'tell app "Terminal" to do script "' + shell_script.name + '"'])
-
-if __name__ == "__main__":
- main()
diff --git a/share/qtcreator/snippets/cpp.xml b/share/qtcreator/snippets/cpp.xml
index 82a62b36e6..ca62a37998 100644
--- a/share/qtcreator/snippets/cpp.xml
+++ b/share/qtcreator/snippets/cpp.xml
@@ -204,5 +204,5 @@ case $value$:
default:
break;
}</snippet>
-<snippet group="C++" trigger="Q_PROPERTY" id="cpp_q_property" complement="(type name READ name WRITE setName NOTIFY nameChanged)">Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed)</snippet>
+<snippet group="C++" trigger="Q_PROPERTY" id="cpp_q_property" complement="(type name READ name WRITE setName NOTIFY nameChanged FINAL)">Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed FINAL)</snippet>
</snippets>
diff --git a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.cpp b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.cpp
index 7e64374bab..c5438f45ca 100644
--- a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.cpp
+++ b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.cpp
@@ -10,5 +10,3 @@ QList<QDesignerCustomWidgetInterface*> @COLLECTION_PLUGIN_CLASS@::customWidgets(
{
return m_widgets;
}
-
-@COLLECTION_PLUGIN_EXPORT@
diff --git a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.h b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.h
index f9a898e6a8..e8e792915b 100644
--- a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.h
+++ b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_collection.h
@@ -15,9 +15,9 @@ class @COLLECTION_PLUGIN_CLASS@ : public QObject, public QDesignerCustomWidgetCo
@COLLECTION_PLUGIN_METADATA@
public:
- explicit @COLLECTION_PLUGIN_CLASS@(QObject *parent = 0);
+ explicit @COLLECTION_PLUGIN_CLASS@(QObject *parent = nullptr);
- virtual QList<QDesignerCustomWidgetInterface*> customWidgets() const;
+ QList<QDesignerCustomWidgetInterface*> customWidgets() const override;
private:
QList<QDesignerCustomWidgetInterface*> m_widgets;
diff --git a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_plugin.pro b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_plugin.pro
index 53d12b7397..ab125bfb49 100644
--- a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_plugin.pro
+++ b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_plugin.pro
@@ -7,11 +7,7 @@ SOURCES =@PLUGIN_SOURCES@
RESOURCES = @PLUGIN_RESOURCES@
LIBS += -L. @WIDGET_LIBS@
-greaterThan(QT_MAJOR_VERSION, 4) {
- QT += designer
-} else {
- CONFIG += designer
-}
+QT += designer
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
diff --git a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.cpp b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.cpp
index 6ffc8481da..06790ac547 100644
--- a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.cpp
+++ b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.cpp
@@ -6,7 +6,6 @@
@PLUGIN_CLASS@::@PLUGIN_CLASS@(QObject *parent)
: QObject(parent)
{
- m_initialized = false;
}
void @PLUGIN_CLASS@::initialize(QDesignerFormEditorInterface * /* core */)
@@ -61,11 +60,10 @@ bool @PLUGIN_CLASS@::isContainer() const
QString @PLUGIN_CLASS@::domXml() const
{
- return QLatin1String("@WIDGET_DOMXML@");
+ return QLatin1String(@WIDGET_DOMXML@);
}
QString @PLUGIN_CLASS@::includeFile() const
{
return QLatin1String("@WIDGET_HEADER@");
}
-@SINGLE_PLUGIN_EXPORT@
diff --git a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.h b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.h
index 0767c0581d..2402458092 100644
--- a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.h
+++ b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_single.h
@@ -14,22 +14,22 @@ class @PLUGIN_CLASS@ : public QObject, public QDesignerCustomWidgetInterface
@SINGLE_PLUGIN_METADATA@
public:
- @PLUGIN_CLASS@(QObject *parent = 0);
+ explicit @PLUGIN_CLASS@(QObject *parent = nullptr);
- bool isContainer() const;
- bool isInitialized() const;
- QIcon icon() const;
- QString domXml() const;
- QString group() const;
- QString includeFile() const;
- QString name() const;
- QString toolTip() const;
- QString whatsThis() const;
- QWidget *createWidget(QWidget *parent);
- void initialize(QDesignerFormEditorInterface *core);
+ bool isContainer() const override;
+ bool isInitialized() const override;
+ QIcon icon() const override;
+ QString domXml() const override;
+ QString group() const override;
+ QString includeFile() const override;
+ QString name() const override;
+ QString toolTip() const override;
+ QString whatsThis() const override;
+ QWidget *createWidget(QWidget *parent) override;
+ void initialize(QDesignerFormEditorInterface *core) override;
private:
- bool m_initialized;
+ bool m_initialized = false;
};
@if ! '%{Cpp:PragmaOnce}'
diff --git a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_widget.h b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_widget.h
index 50256c5ff5..3ec192c6d3 100644
--- a/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_widget.h
+++ b/share/qtcreator/templates/qt4project/customwidgetwizard/tpl_widget.h
@@ -12,7 +12,7 @@ class @WIDGET_CLASS@ : public @WIDGET_BASE_CLASS@
Q_OBJECT
public:
- @WIDGET_CLASS@(QWidget *parent = 0);
+ explicit @WIDGET_CLASS@(QWidget *parent = nullptr);
};
@if ! '%{Cpp:PragmaOnce}'
diff --git a/share/qtcreator/templates/wizards/autotest/files/tst.pro b/share/qtcreator/templates/wizards/autotest/files/tst.pro
index 670c72e798..1a4b00a5e8 100644
--- a/share/qtcreator/templates/wizards/autotest/files/tst.pro
+++ b/share/qtcreator/templates/wizards/autotest/files/tst.pro
@@ -70,6 +70,35 @@ isEmpty(BOOST_INCLUDE_DIR): {
SOURCES += \\
%{MainCppName}
@endif
+@if "%{TestFrameWork}" == "BoostTest_dyn"
+TEMPLATE = app
+CONFIG -= qt
+CONFIG -= app_bundle
+CONFIG += console
+
+isEmpty(BOOST_INSTALL_DIR): BOOST_INSTALL_DIR=$$(BOOST_INSTALL_DIR)
+@if "%{BoostInstallDir}" != ""
+# set by Qt Creator wizard
+isEmpty(BOOST_INSTALL_DIR): BOOST_INSTALL_DIR="%{BoostInstallDir}"
+@endif
+!isEmpty(BOOST_INSTALL_DIR) {
+ win32: INCLUDEPATH *= $${BOOST_INSTALL_DIR}
+ else: INCLUDEPATH *= $${BOOST_INSTALL_DIR}/include
+}
+# Windows: adapt to name scheme, e.g. lib64-msvc-14.2
+!isEmpty(BOOST_INSTALL_DIR): LIBS *= -L$${BOOST_INSTALL_DIR}/lib
+# Windows: adapt to name scheme, e.g. boost_unit_test_framework-vc142-mt-gd-x64-1_80
+LIBS *= -lboost_unit_test_framework
+DEFINES *= BOOST_UNIT_TEST_FRAMEWORK_DYN_LINK
+
+isEmpty(BOOST_INSTALL_DIR): {
+ message("BOOST_INSTALL_DIR is not set, assuming Boost can be found automatically in your system")
+}
+
+SOURCES += \\
+ %{MainCppName} \\
+ %{TestCaseFileWithCppSuffix}
+@endif
@if "%{TestFrameWork}" == "Catch2"
TEMPLATE = app
@if "%{Catch2NeedsQt}" == "true"
diff --git a/share/qtcreator/templates/wizards/autotest/files/tst.qbs b/share/qtcreator/templates/wizards/autotest/files/tst.qbs
index 99a218090e..dda4df02cc 100644
--- a/share/qtcreator/templates/wizards/autotest/files/tst.qbs
+++ b/share/qtcreator/templates/wizards/autotest/files/tst.qbs
@@ -7,6 +7,11 @@ import "googlecommon.js" as googleCommon
import qbs.Environment
import qbs.File
@endif
+@if "%{TestFrameWork}" == "BoostTest_dyn"
+import qbs.Environment
+import qbs.File
+import qbs.FileInfo
+@endif
@if "%{TestFrameWork}" == "Catch2"
import qbs.Environment
import qbs.File
@@ -117,6 +122,40 @@ CppApplication {
files: [ "%{MainCppName}" ]
@endif
+@if "%{TestFrameWork}" == "BoostTest_dyn"
+ type: "application"
+
+ property string boostInstallDir: {
+ if (typeof Environment.getEnv("BOOST_INSTALL_DIR") !== 'undefined')
+ return Environment.getEnv("BOOST_INSTALL_DIR");
+ return "%{BoostInstallDir}"; // set by Qt Creator wizard
+ }
+
+ Properties {
+ condition: boostInstallDir && File.exists(boostInstallDir)
+ cpp.includePaths: base.concat([qbs.hostOS.contains("windows")
+ ? boostInstallDir
+ : FileInfo.joinPaths(boostInstallDir, "include")])
+ // Windows: adapt to different directory layout, e.g. "lib64-msvc-14.2"
+ cpp.libraryPaths: base.concat([FileInfo.joinPaths(boostInstallDir, "lib")])
+ }
+ cpp.defines: base.concat("BOOST_UNIT_TEST_FRAMEWORK_DYN_LINK")
+ // Windows: adapt to name scheme, e.g. "boost_unit_test_framework-vc142-mt-gd-x64-1_80"
+ cpp.dynamicLibraries: ["boost_unit_test_framework"]
+
+ condition: {
+ if (!boostInstallDir)
+ console.log("BOOST_INSTALL_DIR is not set, assuming Boost can be "
+ + "found automatically in your system");
+ return true;
+ }
+
+ files: [
+ "%{MainCppName}",
+ "%{TestCaseFileWithCppSuffix}",
+ ]
+
+@endif
@if "%{TestFrameWork}" == "Catch2"
type: "application"
diff --git a/share/qtcreator/templates/wizards/autotest/files/tst.txt b/share/qtcreator/templates/wizards/autotest/files/tst.txt
index 7e1bd4c46e..739ec2c26a 100644
--- a/share/qtcreator/templates/wizards/autotest/files/tst.txt
+++ b/share/qtcreator/templates/wizards/autotest/files/tst.txt
@@ -133,6 +133,33 @@ elseif (EXISTS ${BOOST_INCLUDE_DIR})
include_directories(${BOOST_INCLUDE_DIR})
endif ()
@endif
+@if "%{TestFrameWork}" == "BoostTest_dyn"
+set(Boost_USE_STATIC_LIBS OFF)
+set(Boost_USE_MULTITHREADED ON)
+set(Boost_USE_STATIC_RUNTIME OFF)
+
+if (DEFINED ENV{BOOST_INSTALL_DIR})
+ set(BOOST_INSTALL_DIR $ENV{BOOST_INSTALL_DIR})
+else ()
+ set(BOOST_INSTALL_DIR "%{BoostInstallDir}") # set by Qt Creator wizard
+endif ()
+if (BOOST_INSTALL_DIR STREQUAL "")
+ message("BOOST_INSTALL_DIR not set, assuming Boost can be found automatically in your system")
+elseif (EXISTS ${BOOST_INSTALL_DIR})
+ set(BOOST_ROOT ${BOOST_INSTALL_DIR})
+endif ()
+find_package(Boost COMPONENTS unit_test_framework REQUIRED)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix})
+add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
+if (Boost_FOUND)
+ include_directories(${Boost_INCLUDE_DIRS})
+ target_link_libraries(%{TestCaseName} PUBLIC ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+endif ()
+@endif
@if "%{TestFrameWork}" == "Catch2"
SET(CMAKE_CXX_STANDARD 11)
diff --git a/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp b/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp
index ac88e1b0f1..1c915089d6 100644
--- a/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp
+++ b/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp
@@ -21,13 +21,22 @@ int main(int argc, char *argv[])
}
@endif
@if "%{TestFrameWork}" == "BoostTest"
-#define BOOST_TEST_MODULE %{TestSuiteName}
+#define BOOST_TEST_MODULE My test module
#include <boost/test/included/unit_test.hpp>
+BOOST_AUTO_TEST_SUITE( %{TestSuiteName} )
+
BOOST_AUTO_TEST_CASE( %{TestCaseName} )
{
BOOST_TEST( true /* test assertion */ );
}
+
+BOOST_AUTO_TEST_SUITE_END()
+@endif
+@if "%{TestFrameWork}" == "BoostTest_dyn"
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE My test module
+#include <boost/test/unit_test.hpp>
@endif
@if "%{TestFrameWork}" == "Catch2"
@if "%{Catch2NeedsQt}" == "true"
diff --git a/share/qtcreator/templates/wizards/autotest/files/tst_src_boost.cpp b/share/qtcreator/templates/wizards/autotest/files/tst_src_boost.cpp
new file mode 100644
index 0000000000..d523db48b9
--- /dev/null
+++ b/share/qtcreator/templates/wizards/autotest/files/tst_src_boost.cpp
@@ -0,0 +1,13 @@
+%{Cpp:LicenseTemplate}\
+@if "%{TestFrameWork}" == "BoostTest_dyn"
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE( %{TestSuiteName} )
+
+BOOST_AUTO_TEST_CASE( %{TestCaseName} )
+{
+ BOOST_TEST( true /* test assertion */ );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/share/qtcreator/templates/wizards/autotest/wizard.json b/share/qtcreator/templates/wizards/autotest/wizard.json
index ae8fdb9d12..6f19d679ea 100644
--- a/share/qtcreator/templates/wizards/autotest/wizard.json
+++ b/share/qtcreator/templates/wizards/autotest/wizard.json
@@ -90,10 +90,14 @@
"value": "QtQuickTest"
},
{
- "trKey": "Boost Test",
+ "trKey": "Boost Test (header only)",
"value": "BoostTest"
},
{
+ "trKey": "Boost Test (shared libraries)",
+ "value": "BoostTest_dyn"
+ },
+ {
"trKey": "Catch2",
"value": "Catch2"
}
@@ -113,7 +117,7 @@
{
"name": "TestSuiteName",
"trDisplayName": "Test suite name:",
- "visible": "%{JS: ['BoostTest', 'GTest'].indexOf(value('TestFrameWork')) >= 0}",
+ "visible": "%{JS: ['BoostTest', 'BoostTest_dyn', 'GTest'].indexOf(value('TestFrameWork')) >= 0}",
"mandatory": true,
"type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" }
@@ -182,6 +186,16 @@
}
},
{
+ "name": "BoostInstallDir",
+ "trDisplayName": "Boost install directory (optional):",
+ "visible": "%{JS: value('TestFrameWork') == 'BoostTest_dyn'}",
+ "mandatory": false,
+ "type": "PathChooser",
+ "data": {
+ "kind": "existingDirectory"
+ }
+ },
+ {
"name": "CatchIncDir",
"trDisplayName": "Catch2 include directory (optional):",
"visible": "%{JS: value('TestFrameWork') === 'Catch2'}",
@@ -300,10 +314,15 @@
{
"source": "files/tst_main.cpp",
"target": "%{MainCppName}",
- "condition": "%{JS: ['GTest', 'QtQuickTest', 'BoostTest', 'Catch2'].indexOf(value('TestFrameWork')) >= 0}",
+ "condition": "%{JS: ['GTest', 'QtQuickTest', 'BoostTest', 'BoostTest_dyn', 'Catch2'].indexOf(value('TestFrameWork')) >= 0}",
"openInEditor": true
},
{
+ "source": "files/tst_src_boost.cpp",
+ "target": "%{TestCaseFileWithCppSuffix}",
+ "condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}"
+ },
+ {
"source": "files/tst_qml.tmpl",
"target": "%{TestCaseFileWithQmlSuffix}",
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
diff --git a/share/qtcreator/templates/wizards/codesnippet/wizard.json b/share/qtcreator/templates/wizards/codesnippet/wizard.json
index 40757cf571..c64ad36822 100644
--- a/share/qtcreator/templates/wizards/codesnippet/wizard.json
+++ b/share/qtcreator/templates/wizards/codesnippet/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject" ],
"id": "Z.Snippet",
"category": "H.Project",
- "trDescription": "Creates a CMake-based test project for which a code snippet can be entered.",
+ "trDescription": "Creates a CMake-based test project where you can enter a code snippet to compile and check it.",
"trDisplayName": "Code Snippet",
"trDisplayCategory": "Other Project",
"featuresRequired": [ "QtSupport.Wizards.FeatureQt" ],
diff --git a/share/qtcreator/templates/wizards/files/markdown/file.md b/share/qtcreator/templates/wizards/files/markdown/file.md
new file mode 100644
index 0000000000..1c00de1fca
--- /dev/null
+++ b/share/qtcreator/templates/wizards/files/markdown/file.md
@@ -0,0 +1,51 @@
+# First Level Heading
+
+Paragraph.
+
+## Second Level Heading
+
+Paragraph.
+
+- bullet
++ other bullet
+* another bullet
+ * child bullet
+
+1. ordered
+2. next ordered
+
+### Third Level Heading
+
+Some *italic* and **bold** text and `inline code`.
+
+An empty line starts a new paragraph.
+
+Use two spaces at the end
+to force a line break.
+
+A horizontal ruler follows:
+
+---
+
+Add links inline like [this link to the Qt homepage](https://www.qt.io),
+or with a reference like [this other link to the Qt homepage][1].
+
+ Add code blocks with
+ four spaces at the front.
+
+> A blockquote
+> starts with >
+>
+> and has the same paragraph rules as normal text.
+
+First Level Heading in Alternate Style
+======================================
+
+Paragraph.
+
+Second Level Heading in Alternate Style
+---------------------------------------
+
+Paragraph.
+
+[1]: https://www.qt.io
diff --git a/share/qtcreator/templates/wizards/files/markdown/wizard.json b/share/qtcreator/templates/wizards/files/markdown/wizard.json
new file mode 100644
index 0000000000..a5e49c755e
--- /dev/null
+++ b/share/qtcreator/templates/wizards/files/markdown/wizard.json
@@ -0,0 +1,42 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ ],
+ "id": "E.Markdown",
+ "category": "U.General",
+ "trDescription": "Creates a markdown file.",
+ "trDisplayName": "Markdown File",
+ "trDisplayCategory": "General",
+ "iconText": "md",
+ "platformIndependent": true,
+ "enabled": "%{JS: value('Plugins').indexOf('TextEditor') >= 0}",
+
+ "options": [
+ { "key": "FileName", "value": "%{JS: Util.fileName(value('TargetPath'), 'md')}" }
+ ],
+
+ "pages" :
+ [
+ {
+ "trDisplayName": "Location",
+ "trShortTitle": "Location",
+ "typeId": "File"
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators" :
+ [
+ {
+ "typeId": "File",
+ "data":
+ {
+ "source": "file.md",
+ "target": "%{FileName}",
+ "openInEditor": true
+ }
+ }
+ ]
+}
diff --git a/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json b/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json
index fa801e67f4..1817b79d93 100644
--- a/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "MesonProjectManager.MesonProject", "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "E.QtCore",
"category": "D.ApplicationQt",
- "trDescription": "Creates a project containing a single main.cpp file with a stub implementation.\n\nPreselects a desktop Qt for building the application if available.",
+ "trDescription": "Creates a project containing a single main.cpp file with a stub implementation and no graphical UI.\n\nPreselects a desktop Qt for building the application if available.",
"trDisplayName": "Qt Console Application",
"trDisplayCategory": "Application (Qt)",
"icon": "../../global/consoleapplication.png",
diff --git a/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json b/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json
index 0a9bb685b7..28e2242f7d 100644
--- a/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "MesonProjectManager.MesonProject", "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "H.CppLibrary",
"category": "G.Library",
- "trDescription": "Creates a C++ library. This can be used to create:<ul><li>a shared C++ library for use with <tt>QPluginLoader</tt> and runtime (Plugins)</li><li>a shared or static C++ library for use with another project at linktime</li></ul>",
+ "trDescription": "Creates a C++ library. You can create:<ul><li>a shared C++ library for use with <tt>QPluginLoader</tt> and runtime (Plugins)</li><li>a shared or static C++ library for use with another project at linktime</li></ul>",
"trDisplayName": "C++ Library",
"trDisplayCategory": "Library",
"icon": "../../global/lib.png",
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json
index 2d566dc1f6..8ca7c0cdcc 100644
--- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json
@@ -26,20 +26,28 @@
{
"trDisplayName": "Define Project Details",
"trShortTitle": "Details",
- "typeId": "Fields",
- "data" :
- [
- {
- "name": "PySideVersion",
- "trDisplayName": "PySide version:",
- "type": "ComboBox",
- "data":
+ "typeId": "PythonConfiguration",
+ "data":
+ {
+ "index": 0,
+ "items":
+ [
{
- "index": 1,
- "items": [ "PySide2", "PySide6" ]
+ "trKey": "PySide 6",
+ "value":
+ {
+ "PySideVersion": "PySide6"
+ }
+ },
+ {
+ "trKey": "PySide 2",
+ "value":
+ {
+ "PySideVersion": "PySide2"
+ }
}
- }
- ]
+ ]
+ }
},
{
"trDisplayName": "Project Management",
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json
index 58e9f8cd52..9156caaffd 100644
--- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json
@@ -31,16 +31,6 @@
"data" :
[
{
- "name": "PySideVersion",
- "trDisplayName": "PySide version:",
- "type": "ComboBox",
- "data":
- {
- "index": 1,
- "items": [ "PySide2", "PySide6" ]
- }
- },
- {
"name": "Class",
"trDisplayName": "Class name:",
"mandatory": true,
@@ -78,6 +68,32 @@
]
},
{
+ "trDisplayName": "Define Project Details",
+ "trShortTitle": "Details",
+ "typeId": "PythonConfiguration",
+ "data":
+ {
+ "index": 0,
+ "items":
+ [
+ {
+ "trKey": "PySide 6",
+ "value":
+ {
+ "PySideVersion": "PySide6"
+ }
+ },
+ {
+ "trKey": "PySide 2",
+ "value":
+ {
+ "PySideVersion": "PySide2"
+ }
+ }
+ ]
+ }
+ },
+ {
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json
index c04407347c..b1b75fc088 100644
--- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json
@@ -30,67 +30,59 @@
{
"trDisplayName": "Define Project Details",
"trShortTitle": "Details",
- "typeId": "Fields",
+ "typeId": "PythonConfiguration",
"data":
- [
- {
- "name": "QtVersion",
- "trDisplayName": "PySide version:",
- "type": "ComboBox",
- "data":
+ {
+ "index": 0,
+ "items":
+ [
+ {
+ "trKey": "PySide 6",
+ "value":
+ {
+ "QtQuickVersion": "",
+ "QtQuickWindowVersion": "",
+ "PySideVersion": "PySide6"
+ }
+ },
+ {
+ "trKey": "PySide 5.15",
+ "value":
+ {
+ "QtQuickVersion": "2.15",
+ "QtQuickWindowVersion": "2.15",
+ "PySideVersion": "PySide2"
+ }
+ },
{
- "index": 0,
- "items":
- [
- {
- "trKey": "PySide 6",
- "value":
- {
- "QtQuickVersion": "",
- "QtQuickWindowVersion": "",
- "PySideVersion": "PySide6"
- }
- },
- {
- "trKey": "PySide 5.15",
- "value":
- {
- "QtQuickVersion": "2.15",
- "QtQuickWindowVersion": "2.15",
- "PySideVersion": "PySide2"
- }
- },
- {
- "trKey": "PySide 5.14",
- "value":
- {
- "QtQuickVersion": "2.14",
- "QtQuickWindowVersion": "2.14",
- "PySideVersion": "PySide2"
- }
- },
- {
- "trKey": "PySide 5.13",
- "value":
- {
- "QtQuickVersion": "2.13",
- "QtQuickWindowVersion": "2.13",
- "PySideVersion": "PySide2"
- }
- },
- {
- "trKey": "PySide 5.12",
- "value":
- {
- "QtQuickVersion": "2.12",
- "QtQuickWindowVersion": "2.12",
- "PySideVersion": "PySide2"
- }
- }
- ]
+ "trKey": "PySide 5.14",
+ "value":
+ {
+ "QtQuickVersion": "2.14",
+ "QtQuickWindowVersion": "2.14",
+ "PySideVersion": "PySide2"
+ }
+ },
+ {
+ "trKey": "PySide 5.13",
+ "value":
+ {
+ "QtQuickVersion": "2.13",
+ "QtQuickWindowVersion": "2.13",
+ "PySideVersion": "PySide2"
+ }
+ },
+ {
+ "trKey": "PySide 5.12",
+ "value":
+ {
+ "QtQuickVersion": "2.12",
+ "QtQuickWindowVersion": "2.12",
+ "PySideVersion": "PySide2"
+ }
}
- }
- ]
+ ]
+ }
},
{
"trDisplayName": "Project Management",
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json
index 951be73475..8362cd55ec 100644
--- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json
@@ -31,16 +31,6 @@
"data" :
[
{
- "name": "PySideVersion",
- "trDisplayName": "PySide version:",
- "type": "ComboBox",
- "data":
- {
- "index": 1,
- "items": [ "PySide2", "PySide6" ]
- }
- },
- {
"name": "Class",
"trDisplayName": "Class name:",
"mandatory": true,
@@ -78,6 +68,32 @@
]
},
{
+ "trDisplayName": "Define Python Interpreter",
+ "trShortTitle": "Interpreter",
+ "typeId": "PythonConfiguration",
+ "data":
+ {
+ "index": 0,
+ "items":
+ [
+ {
+ "trKey": "PySide 6",
+ "value":
+ {
+ "PySideVersion": "PySide6"
+ }
+ },
+ {
+ "trKey": "PySide 2",
+ "value":
+ {
+ "PySideVersion": "PySide2"
+ }
+ }
+ ]
+ }
+ },
+ {
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/tmpl.qbs b/share/qtcreator/templates/wizards/projects/qtquickapplication/tmpl.qbs
deleted file mode 100644
index 050ace8415..0000000000
--- a/share/qtcreator/templates/wizards/projects/qtquickapplication/tmpl.qbs
+++ /dev/null
@@ -1,21 +0,0 @@
-import qbs
-CppApplication {
-@if "%{UseVirtualKeyboard}" == "true"
- Depends { name: "Qt"; submodules: ["quick", "virtualkeyboard"] }
-@else
- Depends { name: "Qt.quick" }
-@endif
- install: true
- // Additional import path used to resolve QML modules in Qt Creator's code model
- property pathList qmlImportPaths: []
-
- files: [
- "%{MainCppFileName}",
- ]
-
- Group {
- Qt.core.resourcePrefix: "%{ProjectName}/"
- fileTags: ["qt.qml.qml", "qt.core.resource_data"]
- files: ["Main.qml"]
- }
-}
diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json
index 88d63e19f9..8b4165fa35 100644
--- a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json
@@ -1,28 +1,27 @@
{
"version": 1,
- "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "QbsProjectManager.QbsProject" ],
+ "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject" ],
"id": "U.QtQuickApplicationEmpty",
"category": "D.ApplicationQt",
- "trDescription": "Creates a Qt Quick application that contains an empty window. Optionally, you can create a Qt Design Studio project.",
+ "trDescription": "Creates a Qt Quick application that can have both QML and C++ code. You can build the application and deploy it to desktop, embedded, and mobile target platforms.\n\nYou can select an option to create a project that you can open in Qt Design Studio, which has a visual editor for Qt Quick UIs.",
"trDisplayName": "Qt Quick Application",
"trDisplayCategory": "Application (Qt)",
"icon": "icon.png",
"iconKind": "Themed",
"featuresRequired": [ "QtSupport.Wizards.FeatureQtQmlCMakeApi" ],
- "enabled": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0 || value('Plugins').indexOf('QbsProjectManager') >= 0 }",
+ "enabled": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0 }",
"options":
[
- { "key": "ProjectFile", "value": "%{JS: value('BuildSystem') === 'cmake' ? '%{ProjectDirectory}/CMakeLists.txt' : '%{ProjectDirectory}/' + '%{ProjectName}'.toLowerCase() + '.qbs' }" },
+ { "key": "ProjectFile", "value": "%{ProjectDirectory}/CMakeLists.txt" },
{ "key": "MainCppFileName", "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" },
{ "key": "UseVirtualKeyboardByDefault", "value": "%{JS: value('Plugins').indexOf('Boot2Qt') >= 0 || value('Plugins').indexOf('Boot2QtQdb') >= 0 }" },
{ "key": "TargetName", "value": "%{JS: 'app' + value('ProjectName') }" },
{ "key": "HasQSPSetup", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.2' }"},
{ "key": "HasFailureSignal", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.3' }"},
- { "key": "UsesAutoResourcePrefix", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.4' && value('BuildSystem') === 'cmake' }"},
+ { "key": "UsesAutoResourcePrefix", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.4' }"},
{ "key": "HasLoadFromModule", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.4' && value('UsesAutoResourcePrefix') }"},
{ "key": "QdsWizardPath", "value": "%{IDE:ResourcePath}/qmldesigner/studio_templates/projects" },
- { "key": "QdsProjectStyle", "value": "%{JS: value('BuildSystem') === 'cmake' ? %{QdsProjectStyleInput} : false }" },
{ "key": "NoQdsProjectStyle", "value": "%{JS: !%{QdsProjectStyle} }" },
{ "key": "ImportModuleName", "value": "%{ProjectName}" },
@@ -48,50 +47,17 @@
"typeId": "Project"
},
{
- "trDisplayName": "Define Build System",
- "trShortTitle": "Build System",
- "typeId": "Fields",
- "enabled": "%{JS: ! %{IsSubproject}}",
- "data":
- [
- {
- "name": "BuildSystem",
- "trDisplayName": "Build system:",
- "type": "ComboBox",
- "persistenceKey": "BuildSystemType",
- "data":
- {
- "index": 0,
- "items":
- [
- {
- "trKey": "CMake",
- "value": "cmake",
- "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}"
- },
- {
- "trKey": "Qbs",
- "value": "qbs",
- "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}"
- }
- ]
- }
- }
- ]
- },
- {
"trDisplayName": "Define Project Details",
"trShortTitle": "Details",
"typeId": "Fields",
"data":
[
{
- "name": "QdsProjectStyleInput",
+ "name": "QdsProjectStyle",
"trDisplayName": "Create a project that you can open in Qt Design Studio",
"trToolTip": "Create a project with a structure that is compatible both with Qt Design Studio (via .qmlproject) and with Qt Creator (via CMakeLists.txt). It contains a .ui.qml form that you can visually edit in Qt Design Studio.",
"type": "CheckBox",
"span": true,
- "visible": "%{JS: value('BuildSystem') === 'cmake'}",
"persistenceKey": "QtQuick.QdsProjectStyle",
"data":
{
@@ -146,13 +112,7 @@
{
"source": "CMakeLists.txt",
"openAsProject": true,
- "condition": "%{JS: %{NoQdsProjectStyle} && value('BuildSystem') === 'cmake' }"
- },
- {
- "source": "tmpl.qbs",
- "target": "%{ProjectFile}",
- "openAsProject": true,
- "condition": "%{JS: value('BuildSystem') === 'qbs' }"
+ "condition": "%{NoQdsProjectStyle}"
},
{
"source": "main.cpp",
diff --git a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json
index aba8309365..251500b494 100644
--- a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "QmlProjectManager.QmlProject" ],
"id": "QA.QtQuickUi",
"category": "H.Project",
- "trDescription": "Creates a Qt Quick 2 UI project with a QML entry point. To use it, you need to have a QML runtime environment.\n\nUse this only if you are prototyping. You cannot create a full application with this. Consider using a Qt Quick Application project instead.",
+ "trDescription": "Creates a Qt Quick UI project for previewing and prototyping designs.\n\nTo develop a full application, create a Qt Quick Application project instead.",
"trDisplayName": "Qt Quick UI Prototype",
"trDisplayCategory": "Other Project",
"icon": "qtquickuiprototype.png",
diff --git a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json
index e9d005507d..58abd94344 100644
--- a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "MesonProjectManager.MesonProject","CMakeProjectManager.CMakeProject", "Qt4ProjectManager.Qt4Project", "Qbs.QbsProject" ],
"id": "C.QtWidgets",
"category": "D.ApplicationQt",
- "trDescription": "Creates a widget-based Qt application that contains a Qt Designer-based main window.\n\nPreselects a desktop Qt for building the application if available.",
+ "trDescription": "Creates a widget-based Qt application that contains a Qt Designer-based main window and C++ source and header files to implement the application logic.\n\nPreselects a desktop Qt for building the application if available.",
"trDisplayName": "Qt Widgets Application",
"trDisplayCategory": "Application (Qt)",
"icon": "../../global/guiapplication.png",
diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/CMakeLists.txt b/share/qtcreator/templates/wizards/qtcreatorplugin/CMakeLists.txt
index 48884eae64..a60e46cb7c 100644
--- a/share/qtcreator/templates/wizards/qtcreatorplugin/CMakeLists.txt
+++ b/share/qtcreator/templates/wizards/qtcreatorplugin/CMakeLists.txt
@@ -22,6 +22,23 @@ find_package(QtCreator REQUIRED COMPONENTS Core)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
set(QtX Qt${QT_VERSION_MAJOR})
+# Add a CMake option that enables building your plugin with tests.
+# You don't want your released plugin binaries to contain tests,
+# so make that default to 'NO'.
+# Enable tests by passing -DWITH_TESTS=ON to CMake.
+option(WITH_TESTS "Builds with tests" NO)
+
+if(WITH_TESTS)
+ # Look for QtTest
+ find_package(${QtX} REQUIRED COMPONENTS Test)
+
+ # Tell CMake functions like add_qtc_plugin about the QtTest component.
+ set(IMPLICIT_DEPENDS Qt::Test)
+
+ # Enable ctest for auto tests.
+ enable_testing()
+endif()
+
add_qtc_plugin(%{PluginName}
PLUGIN_DEPENDS
QtCreator::Core
diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h
index b8b387bc01..ad863f1794 100644
--- a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h
+++ b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h
@@ -6,6 +6,8 @@
#define %{GLOBAL_GUARD}
@endif
+#include <qglobal.h>
+
#if defined(%{LibraryDefine})
# define %{LibraryExport} Q_DECL_EXPORT
#else
diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme
index 82fa00f614..a214688bf9 100644
--- a/share/qtcreator/themes/dark.creatortheme
+++ b/share/qtcreator/themes/dark.creatortheme
@@ -21,9 +21,63 @@ textColorLinkVisited=ffc58af9
backgroundColorDisabled=ff444444
qmlDesignerButtonColor=ff3c3e40
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
;DS controls theme START
-DSpanelBackground=ff2E2F30
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=midnightGrey
+DScontrolBackground_toolbarHover=midnightGrey
+DScontrolBackground_topToolbarHover=ashGrey
+DScontrolBackground_statusbarIdle=offBlack
+DSControlBackground_statusbarHover=dawnGrey
+DScontrolOutline_topToolbarIdle=dawnGrey
+DScontrolOutline_topToolbarHover=raincloudGrey
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=midnightGrey
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=nearBlack
+DStextColor=lightWhite
+DSstatusbarBackground=offBlack
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=ashGrey
+DSstateControlBackgroundColor_globalHover=ashGrey
+DSstateControlBackgroundColor_hover=raincloudGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
+
+DSpanelBackground=dawnGrey
DSwelcomeScreenBackground=ff242424
DSsubPanelBackground=ff1c1c1c
@@ -34,21 +88,21 @@ DSgreenLight=ff5cdc68
DSamberLight=ffffbf00
DSredLight=ffff0401
-DSinteraction=ff2aafd3
+DSinteraction=highlightBlue
DSerrorColor=ffdf3a3a
DSwarningColor=warning
DSdisabledColor=ff707070
DSinteractionHover=ff74cbfc
-DScontrolBackground=ff2e2f30
+DScontrolBackground=dawnGrey
DScontrolBackgroundInteraction=ff3d3d3d
DScontrolBackgroundDisabled=ff2e2f30
DScontrolBackgroundGlobalHover=ff333333
DScontrolBackgroundHover=ff333333
-DScontrolOutline=ff1f1f1f
-DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutline=nearBlack
+DScontrolOutlineInteraction=highlightBlue
DScontrolOutlineDisabled=ff707070
DStextColor=ffffffff
@@ -61,16 +115,16 @@ DSplaceholderTextColorInteraction=ffababab
DSiconColor=ffffffff
DSiconColorHover=ffffffff
-DSiconColorInteraction=ff707070
+DSiconColorInteraction=nearBlack
DSiconColorDisabled=ffC7C7C7
-DSiconColorSelected=ff2aafd3
+DSiconColorSelected=nearBlack
DSlinkIndicatorColor=ff808080
DSlinkIndicatorColorHover=ffffffff
DSlinkIndicatorColorInteraction=ff2aafd3
DSlinkIndicatorColorDisabled=ff707070
-DSpopupBackground=ff474747
+DSpopupBackground=offBlack
DSpopupOverlayColor=99191919
DSsliderActiveTrack=ff7c7b7b
@@ -84,13 +138,13 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
-DSscrollBarTrack=ff3E3E3E
-DSscrollBarHandle=ff4C4C4C
+DSscrollBarTrack=dawnGrey
+DSscrollBarHandle=offBlack
-DSsectionHeadBackground=ff1f1f1f
+DSsectionHeadBackground=midnightGrey
DSstateDefaultHighlight=ffffe400
-DSstateSeparatorColor=ff7c7b7b
+DSstateSeparatorColor=graniteGrey
DSstateBackgroundColor=ff383838
DSstatePreviewOutline=ffaaaaaa
@@ -116,8 +170,8 @@ DSdockContainerSplitter=ff323232
DSdockAreaBackground=ff262728
DSdockWidgetBackground=ff00ff00
-DSdockWidgetSplitter=ff595959
-DSdockWidgetTitleBar=ff1f1f1f
+DSdockWidgetSplitter=fullBlack
+DSdockWidgetTitleBar=dawnGrey
DStitleBarText=ffdadada
DStitleBarIcon=ffffffff
@@ -133,24 +187,24 @@ DStabInactiveIcon=ffffffff
DStabInactiveButtonHover=ff1f1f1f
DStabInactiveButtonPress=ff1f1f1f
-DStabActiveBackground=ffdadada
-DStabActiveText=ff111111
-DStabActiveIcon=ff000000
+DStabActiveBackground=raincloudGrey
+DStabActiveText=offWhite
+DStabActiveIcon=offWhite
DStabActiveButtonHover=ffdadada
DStabActiveButtonPress=ffdadada
-DStabFocusBackground=ff2aafd3
+DStabFocusBackground=highlightBlue
DStabFocusText=ff111111
DStabFocusIcon=ff000000
-DStabFocusButtonHover=ff2aafd3
-DStabFocusButtonPress=ff2aafd3
+DStabFocusButtonHover=highlightBlue
+DStabFocusButtonPress=highlightBlue
DSnavigatorBranch=ff7c7b7b
DSnavigatorBranchIndicator=ff7c7b7b
-DSnavigatorItemBackground=ff2E2F30
-DSnavigatorItemBackgroundHover=ff333333
-DSnavigatorItemBackgroundSelected=ff3D3D3D
-DSnavigatorText=ffffffff
+DSnavigatorItemBackground=dawnGrey
+DSnavigatorItemBackgroundHover=graniteGrey
+DSnavigatorItemBackgroundSelected=midnightGrey
+DSnavigatorText=lightWhite
DSnavigatorTextHover=ffffffff
DSnavigatorTextSelected=ff2aafd3
DSnavigatorIcon=ffffffff
@@ -172,6 +226,8 @@ DSUnimportedModuleColor=ffe33c2e
DSBackgroundColorAlternate=alternateBackground
DSBackgroundColorNormal=normalBackground
+DStoolbarBackground=midnightGrey
+
;DS controls theme END
BackgroundColorAlternate=alternateBackground
@@ -184,7 +240,6 @@ BadgeLabelBackgroundColorChecked=normalBackground
BadgeLabelBackgroundColorUnchecked=selectedBackground
BadgeLabelTextColorChecked=text
BadgeLabelTextColorUnchecked=text
-CanceledSearchTextColor=ff0000
ComboBoxArrowColor=text
ComboBoxArrowColorDisabled=text
ComboBoxTextColor=text
@@ -369,13 +424,13 @@ PaletteWindowTextDisabled=textDisabled
PaletteBaseDisabled=backgroundColorDisabled
PaletteTextDisabled=textDisabled
PaletteMid=ffa0a0a0
-PalettePlaceholderText=ff8d8d8d
+PalettePlaceholderText=slateGrey
QmlDesigner_BackgroundColor=qmlDesignerButtonColor
QmlDesigner_HighlightColor=ff46a2da
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
QmlDesigner_FormEditorForegroundColor=ffffffff
-QmlDesigner_BackgroundColorDarkAlternate=ff323232
+QmlDesigner_BackgroundColorDarkAlternate=dawnGrey
QmlDesigner_BackgroundColorDarker=ff151515
QmlDesigner_BorderColor=splitterColor
QmlDesigner_ButtonColor=ff505050
@@ -385,6 +440,27 @@ QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
QmlDesigner_ScrollBarHandleColor=ff505050
+TerminalForeground=ffffffff
+TerminalBackground=normalBackground
+TerminalSelection=7fffffff
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0058D1
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=e5e54b
+TerminalAnsi12=003EFF
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=false
DerivePaletteFromTheme=true
@@ -401,6 +477,7 @@ FlatMenuBar=true
ToolBarIconShadow=true
WindowColorAsBase=false
DarkUserInterface=true
+QDSTheme=false
[ImageFiles]
IconOverlayCSource=:/cppeditor/images/dark_qt_c.png
diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme
index 06d36f669e..a7095266a8 100644
--- a/share/qtcreator/themes/default.creatortheme
+++ b/share/qtcreator/themes/default.creatortheme
@@ -12,8 +12,64 @@ shadowBackground=ff232323
splitterColor=ff151515
qmlDesignerButtonColor=ff4c4e50
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+concreteGrey=ffbbbbbb
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
;DS controls theme START
+
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=offWhite
+DScontrolBackground_toolbarHover=offWhite
+DScontrolBackground_topToolbarHover=concreteGrey
+DScontrolBackground_statusbarIdle=concreteGrey
+DSControlBackground_statusbarHover=lightWhite
+DScontrolOutline_topToolbarIdle=concreteGrey
+DScontrolOutline_topToolbarHover=lightWhite
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=offWhite
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=smokeGrey
+DStextColor=raincloudGrey
+DSstatusbarBackground=concreteGrey
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=concreteGrey
+DSstateControlBackgroundColor_globalHover=concreteGrey
+DSstateControlBackgroundColor_hover=smokeGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
+
DSpanelBackground=ffeaeaea
DSwelcomeScreenBackground=ffEAEAEA
@@ -86,7 +142,7 @@ DSstateBackgroundColor=ffe0e0e0
DSstatePreviewOutline=ff363636
DSstatePanelBackground=ffdadada
-DSstateHighlight=ff8d8d8d
+DSstateHighlight=offWhite
DSchangedStateText=ff99ccff
@@ -163,6 +219,7 @@ DSUnimportedModuleColor=ffe33c2e
DSBackgroundColorAlternate=ffeaeaea
DSBackgroundColorNormal=ffd8d8d8
+
;DS controls theme END
BackgroundColorAlternate=ff3d3d3d
@@ -175,7 +232,6 @@ BadgeLabelBackgroundColorChecked=ffe0e0e0
BadgeLabelBackgroundColorUnchecked=ff808080
BadgeLabelTextColorChecked=ff606060
BadgeLabelTextColorUnchecked=ffffffff
-CanceledSearchTextColor=ffff0000
ComboBoxArrowColor=ffb8b5b2
ComboBoxArrowColorDisabled=ffdcdcdc
ComboBoxTextColor=ffffffff
@@ -353,6 +409,27 @@ QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
QmlDesigner_ScrollBarHandleColor=ff7a7a7a
+TerminalForeground=ff000000
+TerminalBackground=ffffffff
+TerminalSelection=3f000000
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0000ab
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=dac911
+TerminalAnsi12=0000fe
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=true
DerivePaletteFromTheme=false
@@ -369,3 +446,4 @@ FlatMenuBar=false
ToolBarIconShadow=true
WindowColorAsBase=false
DarkUserInterface=false
+QDSTheme=false
diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme
index 1269156278..eeac285a5f 100644
--- a/share/qtcreator/themes/design-light.creatortheme
+++ b/share/qtcreator/themes/design-light.creatortheme
@@ -2,7 +2,6 @@
ThemeName=Design Light
PreferredStyles=
-
[Palette]
shadowBackground=ffd1cfcf
text=ff000000
@@ -26,8 +25,64 @@ textColorLink=ff007af4
textColorLinkVisited=ffa57aff
backgroundColorDisabled=ff8e8e8e
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+concreteGrey=ffbbbbbb
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
;DS controls theme START
+
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=offWhite
+DScontrolBackground_toolbarHover=offWhite
+DScontrolBackground_topToolbarHover=concreteGrey
+DScontrolBackground_statusbarIdle=concreteGrey
+DSControlBackground_statusbarHover=lightWhite
+DScontrolOutline_topToolbarIdle=concreteGrey
+DScontrolOutline_topToolbarHover=lightWhite
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=offWhite
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=smokeGrey
+DStextColor=raincloudGrey
+DSstatusbarBackground=concreteGrey
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=concreteGrey
+DSstateControlBackgroundColor_globalHover=concreteGrey
+DSstateControlBackgroundColor_hover=smokeGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
+
DSpanelBackground=ffeaeaea
DSwelcomeScreenBackground=ffEAEAEA
@@ -39,12 +94,12 @@ DSgreenLight=ff5cdc68
DSamberLight=ffffbf00
DSredLight=ffff0401
-DSinteraction=ff2aafd3
+DSinteraction=highlightBlue
DSerrorColor=ffdf3a3a
DSwarningColor=warning
DSdisabledColor=ff8e8e8e
-DSinteractionHover=ff74cbfc
+DSinteractionHover=highlightHover
DScontrolBackground=ffeaeaea
DScontrolBackgroundInteraction=ffc9c9c9
@@ -56,7 +111,6 @@ DScontrolOutline=ffcecccc
DScontrolOutlineInteraction=ff2aafd3
DScontrolOutlineDisabled=ff707070
-DStextColor=ff262626
DStextColorDisabled=ff707070
DStextSelectionColor=ff2aafd3
DStextSelectedTextColor=ff000000
@@ -189,7 +243,6 @@ BadgeLabelBackgroundColorChecked=ffe0e0e0
BadgeLabelBackgroundColorUnchecked=ff808080
BadgeLabelTextColorChecked=ff606060
BadgeLabelTextColorUnchecked=ffffffff
-CanceledSearchTextColor=ff0000
ComboBoxArrowColor=toolBarItem
ComboBoxArrowColorDisabled=toolBarItemDisabled
ComboBoxTextColor=fancyBarsNormalTextColor
@@ -397,6 +450,27 @@ PaletteWindowTextDisabled=textDisabled
PaletteBaseDisabled=backgroundColorDisabled
PaletteTextDisabled=textDisabled
+TerminalForeground=ff000000
+TerminalBackground=normalBackground
+TerminalSelection=3f000000
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0000ab
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=dac911
+TerminalAnsi12=0000fe
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=false
DerivePaletteFromTheme=true
@@ -413,3 +487,4 @@ FlatMenuBar=true
ToolBarIconShadow=false
WindowColorAsBase=false
DarkUserInterface=false
+QDSTheme=true
diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme
index 9335513217..5e8294efa5 100644
--- a/share/qtcreator/themes/design.creatortheme
+++ b/share/qtcreator/themes/design.creatortheme
@@ -23,37 +23,119 @@ textColorLink=ff007af4
textColorLinkVisited=ffa57aff
backgroundColorDisabled=ff444444
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+concreteGrey=ffbbbbbb
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
-;DS controls theme START
-DSpanelBackground=ff2E2F30
+;DS controls theme
+
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=midnightGrey
+DScontrolBackground_toolbarHover=midnightGrey
+DScontrolBackground_topToolbarHover=ashGrey
+DScontrolBackground_statusbarIdle=offBlack
+DSControlBackground_statusbarHover=dawnGrey
+DScontrolOutline_topToolbarIdle=dawnGrey
+DScontrolOutline_topToolbarHover=raincloudGrey
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=midnightGrey
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=nearBlack
+DStextColor=lightWhite
+DSstatusbarBackground=offBlack
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=ashGrey
+DSstateControlBackgroundColor_globalHover=ashGrey
+DSstateControlBackgroundColor_hover=raincloudGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
+
+;REMAPPED
+DSinteraction=highlightBlue
+DScontrolBackground=dawnGrey
+DScontrolOutlineInteraction=highlightBlue
+DScontrolOutline=nearBlack
+DSiconColorInteraction=nearBlack
+DSiconColorSelected=nearBlack
+DSsectionHeadBackground=midnightGrey
+DSstateSeparatorColor=graniteGrey
+DSnavigatorItemBackground=dawnGrey
+DSnavigatorItemBackgroundHover=graniteGrey
+DSnavigatorItemBackgroundSelected=midnightGrey
+DStabActiveBackground=raincloudGrey
+DStabActiveText=offWhite
+DStabActiveIcon=offWhite
+DStabFocusButtonHover=highlightBlue
+DStabFocusButtonPress=highlightBlue
+DStabFocusBackground=highlightBlue
+DSnavigatorText=lightWhite
+DSpopupBackground=offBlack
+DSinteractionHover=highlightHover
+DSnavigatorTextSelected=highlightBlue
+
+;contentious remap
+;background color for main form view, library, navigator, properties, connections
+QmlDesigner_BackgroundColorDarkAlternate=dawnGrey
+
+;TODO
+DSscrollBarTrack=dawnGrey
+DSscrollBarHandle=offBlack
+
+DSdockWidgetTitleBar=dawnGrey
+DSdockWidgetSplitter=fullBlack
+;LEGACY
DSwelcomeScreenBackground=ff242424
DSsubPanelBackground=ff1c1c1c
DSthumbnailBackground=ff232323
DSthumbnailLabelBackground=ff2b2a2a
-
DSgreenLight=ff5cdc68
DSamberLight=ffffbf00
DSredLight=ffff0401
-DSinteraction=ff2aafd3
DSerrorColor=ffdf3a3a
DSwarningColor=warning
DSdisabledColor=ff707070
-DSinteractionHover=ff74cbfc
-DScontrolBackground=ff2e2f30
DScontrolBackgroundInteraction=ff3d3d3d
DScontrolBackgroundDisabled=ff2e2f30
DScontrolBackgroundGlobalHover=ff333333
DScontrolBackgroundHover=ff333333
-DScontrolOutline=ff1f1f1f
-DScontrolOutlineInteraction=ff2aafd3
DScontrolOutlineDisabled=ff707070
-DStextColor=ffffffff
DStextColorDisabled=ff707070
DStextSelectionColor=ff2aafd3
DStextSelectedTextColor=ff000000
@@ -61,18 +143,16 @@ DStextSelectedTextColor=ff000000
DSplaceholderTextColor=ffffffff
DSplaceholderTextColorInteraction=ffababab
-DSiconColor=ffffffff
+DSiconColor=fullWhite
DSiconColorHover=ffffffff
-DSiconColorInteraction=ff707070
DSiconColorDisabled=ffC7C7C7
DSiconColorSelected=ff2aafd3
-
DSlinkIndicatorColor=ff808080
DSlinkIndicatorColorHover=ffffffff
DSlinkIndicatorColorInteraction=ff2aafd3
DSlinkIndicatorColorDisabled=ff707070
-DSpopupBackground=ff474747
+
DSpopupOverlayColor=99191919
DSsliderActiveTrack=ff7c7b7b
@@ -86,14 +166,8 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
-DSscrollBarTrack=ff3E3E3E
-DSscrollBarHandle=ff4C4C4C
-
-DSsectionHeadBackground=ff1f1f1f
-
DSstateDefaultHighlight=ffffe400
-DSstateSeparatorColor=ff7c7b7b
-DSstateBackgroundColor=ff383838
+DSstateBackgroundColor=fffff000
DSstatePreviewOutline=ffaaaaaa
DSstatePanelBackground=ff252525
@@ -116,11 +190,7 @@ DStableHeaderText=ff00ff00
DSdockContainerBackground=ff242424
DSdockContainerSplitter=ff323232
DSdockAreaBackground=ff262728
-
DSdockWidgetBackground=ff00ff00
-DSdockWidgetSplitter=ff595959
-DSdockWidgetTitleBar=ff1f1f1f
-
DStitleBarText=ffdadada
DStitleBarIcon=ffffffff
DStitleBarButtonHover=40ffffff
@@ -135,26 +205,16 @@ DStabInactiveIcon=ffffffff
DStabInactiveButtonHover=ff1f1f1f
DStabInactiveButtonPress=ff1f1f1f
-DStabActiveBackground=ffdadada
-DStabActiveText=ff111111
-DStabActiveIcon=ff000000
DStabActiveButtonHover=ffdadada
DStabActiveButtonPress=ffdadada
-DStabFocusBackground=ff2aafd3
DStabFocusText=ff111111
DStabFocusIcon=ff000000
-DStabFocusButtonHover=ff2aafd3
-DStabFocusButtonPress=ff2aafd3
DSnavigatorBranch=ff7c7b7b
DSnavigatorBranchIndicator=ff7c7b7b
-DSnavigatorItemBackground=ff2E2F30
-DSnavigatorItemBackgroundHover=ff333333
-DSnavigatorItemBackgroundSelected=ff3D3D3D
-DSnavigatorText=ffffffff
DSnavigatorTextHover=ffffffff
-DSnavigatorTextSelected=ff2aafd3
+
DSnavigatorIcon=ffffffff
DSnavigatorIconHover=ffa1a1a1
DSnavigatorIconSelected=ffffffff
@@ -186,7 +246,6 @@ BadgeLabelBackgroundColorChecked=ffe0e0e0
BadgeLabelBackgroundColorUnchecked=ff808080
BadgeLabelTextColorChecked=ff606060
BadgeLabelTextColorUnchecked=ffffffff
-CanceledSearchTextColor=ff0000
ComboBoxArrowColor=toolBarItem
ComboBoxArrowColorDisabled=toolBarItemDisabled
ComboBoxTextColor=fancyBarsNormalTextColor
@@ -350,143 +409,85 @@ ProjectExplorer_TaskWarn_TextMarkColor=warning
CodeModel_Error_TextMarkColor=error
CodeModel_Warning_TextMarkColor=warning
-
;Designer Main colors
; Design View panel backgrounds - Library, Form Viewer, Properties, States, Timeline & Connections. - lOOKS LIKE IS NO LONGER USED.
QmlDesigner_BackgroundColor=ff4c4e50
-;QmlDesigner_BackgroundColor=ffd3299a
-
; Design View selected items - Navigator Selection, Timeline Property Selection, TImeline bar, property text highlighted
QmlDesigner_HighlightColor=ff0492c9
-
-
; live selection color for the form editors smart guides.
-;QmlDesigner_FormEditorSelectionColor=ff4ba2ff
QmlDesigner_FormEditorSelectionColor=ffd3299a
-
;color for lable text in form editor
QmlDesigner_FormEditorForegroundColor=ffdadada
-;QmlDesigner_FormEditorForegroundColor=ffd3299a
-
-
-
-
-;new colors
-
-;background color for main form view, library, navigator, properties, connections
-;QmlDesigner_BackgroundColorDarkAlternate=ff4c4e50
-QmlDesigner_BackgroundColorDarkAlternate=ff2e2f30
;filter outlines, override W/H outlines, properties spinbox background, timeline separators.
-;QmlDesigner_BackgroundColorDarker=ff262728
QmlDesigner_BackgroundColorDarker=ff191919
;properties outlines, states thumbnail outlines, add state button outlines.
-;QmlDesigner_BorderColor=splitter
QmlDesigner_BorderColor=ff353535
;background sqaures for components, effects, etc. Handles for scrollbars, type background in properties.
-;QmlDesigner_ButtonColor=ff595b5c
QmlDesigner_ButtonColor=ff353535
;non - selected tabs, text color for selected tabs.
-;QmlDesigner_TabDark=shadowBackground
QmlDesigner_TabDark=ff111111
;active tab backgrounds and non selected tab text.
QmlDesigner_TabLight=text
-;QmlDesigner_TabLight=ff262626
-
-;extra_new_colors
+;extra_new_colors
QmlDesigner_FormeditorBackgroundColor=ff000000
-
QmlDesigner_AlternateBackgroundColor=ffc14fc1
-
QmlDesigner_ScrollBarHandleColor=ff595b5c
-
-
;palette colors
;outline colors on the combo box, zoom slider, dialog outlines, on loading project the whole screen flashes this color
-;PaletteWindow=normalBackground
PaletteWindow=ff262626
-
-
; item text for search results panel and application output panel, twirl down triangles in edit mode, text editor.
PaletteWindowText=text
-;PaletteWindowText=ffd3299a
-
;Search bar background, edit view project list background, style combo box background
PaletteBase=normalBackground
-;PaletteBase=ff191919
-
-
;can't see where this is used. ;Used in plugin dialog
PaletteAlternateBase=normalBackground
-;PaletteAlternateBase=ffd3299a
-
-
;flow tag backgrounds, import combobox background, edit mode scrollbars
-;PaletteButton=shadowBackground
PaletteButton=ff262626
-
-
;alternate text in the search and application output panel - NO LONGER SEEMS TO DO ANYTHING
PaletteBrightText=ffff3333
-;PaletteBrightText=ffd3299a
-
; text inside dropdown combo boxes, styles, connections.
PaletteText=text
-;PaletteText=ffd3299a
-
-
; text for ticks for tick boxes.
PaletteButtonText=text
-;PaletteButtonText=ffd3299a
-
PaletteButtonTextDisabled=textDisabled
-
;background color for the tool tip hover background
PaletteToolTipBase=selectedBackground
-;PaletteToolTipBase=ffd3299a
-
;the selection highlight on the dropdown combo box in the file selection top menu and connections panel and tab mode selector dropdowns
PaletteHighlight=selectedBackgroundText
-
; outline of warning in editor mode, underline of "open a document" page in the edit mode
PaletteDark=shadowBackground
-;PaletteDark=ffd3299a
-
;selected text highlight in edit and design studio modes
PaletteHighlightedText=ffffffff
-;PaletteHighlightedText=ffd3299a
-
; text for for floating tool tips
PaletteToolTipText=text
-;PaletteToolTipText=ffd3299a
-
PaletteLink=textColorLink
PaletteLinkVisited=textColorLinkVisited
@@ -497,6 +498,27 @@ PaletteTextDisabled=textDisabled
PaletteMid=ffafafaf
PalettePlaceholderText=ff808081
+TerminalForeground=ffffffff
+TerminalBackground=normalBackground
+TerminalSelection=7fffffff
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0058D1
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=e5e54b
+TerminalAnsi12=003EFF
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=false
DerivePaletteFromTheme=true
@@ -513,3 +535,4 @@ FlatMenuBar=true
ToolBarIconShadow=true
WindowColorAsBase=false
DarkUserInterface=true
+QDSTheme=true
diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme
index 1e28230331..bb6b905288 100644
--- a/share/qtcreator/themes/flat-dark.creatortheme
+++ b/share/qtcreator/themes/flat-dark.creatortheme
@@ -25,9 +25,62 @@ textColorLinkVisited=ffa57aff
backgroundColorDisabled=ff444444
qmlDesignerButtonColor=ff4c4e50
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
;DS controls theme START
-DSpanelBackground=ff2E2F30
+
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=midnightGrey
+DScontrolBackground_toolbarHover=midnightGrey
+DScontrolBackground_topToolbarHover=ashGrey
+DScontrolBackground_statusbarIdle=offBlack
+DSControlBackground_statusbarHover=dawnGrey
+DScontrolOutline_topToolbarIdle=dawnGrey
+DScontrolOutline_topToolbarHover=raincloudGrey
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=midnightGrey
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=nearBlack
+DStextColor=lightWhite
+DSstatusbarBackground=offBlack
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=ashGrey
+DSstateControlBackgroundColor_globalHover=ashGrey
+DSstateControlBackgroundColor_hover=raincloudGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
DSwelcomeScreenBackground=ff242424
DSsubPanelBackground=ff1c1c1c
@@ -38,21 +91,21 @@ DSgreenLight=ff5cdc68
DSamberLight=ffffbf00
DSredLight=ffff0401
-DSinteraction=ff2aafd3
+DSinteraction=highlightBlue
DSerrorColor=ffdf3a3a
DSwarningColor=warning
DSdisabledColor=ff707070
DSinteractionHover=ff74cbfc
-DScontrolBackground=ff2e2f30
+DScontrolBackground=dawnGrey
DScontrolBackgroundInteraction=ff3d3d3d
DScontrolBackgroundDisabled=ff2e2f30
DScontrolBackgroundGlobalHover=ff333333
DScontrolBackgroundHover=ff333333
-DScontrolOutline=ff1f1f1f
-DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutline=nearBlack
+DScontrolOutlineInteraction=highlightBlue
DScontrolOutlineDisabled=ff707070
DStextColor=ffffffff
@@ -65,16 +118,16 @@ DSplaceholderTextColorInteraction=ffababab
DSiconColor=ffffffff
DSiconColorHover=ffffffff
-DSiconColorInteraction=ff707070
+DSiconColorInteraction=nearBlack
DSiconColorDisabled=ffC7C7C7
-DSiconColorSelected=ff2aafd3
+DSiconColorSelected=nearBlack
DSlinkIndicatorColor=ff808080
DSlinkIndicatorColorHover=ffffffff
DSlinkIndicatorColorInteraction=ff2aafd3
DSlinkIndicatorColorDisabled=ff707070
-DSpopupBackground=ff474747
+DSpopupBackground=offBlack
DSpopupOverlayColor=99191919
DSsliderActiveTrack=ff7c7b7b
@@ -88,13 +141,13 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
-DSscrollBarTrack=ff3E3E3E
-DSscrollBarHandle=ff4C4C4C
+DSscrollBarTrack=dawnGrey
+DSscrollBarHandle=offBlack
-DSsectionHeadBackground=ff1f1f1f
+DSsectionHeadBackground=midnightGrey
DSstateDefaultHighlight=ffffe400
-DSstateSeparatorColor=ff7c7b7b
+DSstateSeparatorColor=graniteGrey
DSstateBackgroundColor=ff383838
DSstatePreviewOutline=ffaaaaaa
@@ -120,8 +173,8 @@ DSdockContainerSplitter=ff323232
DSdockAreaBackground=ff262728
DSdockWidgetBackground=ff00ff00
-DSdockWidgetSplitter=ff595959
-DSdockWidgetTitleBar=ff1f1f1f
+DSdockWidgetSplitter=fullBlack
+DSdockWidgetTitleBar=dawnGrey
DStitleBarText=ffdadada
DStitleBarIcon=ffffffff
@@ -137,24 +190,24 @@ DStabInactiveIcon=ffffffff
DStabInactiveButtonHover=ff1f1f1f
DStabInactiveButtonPress=ff1f1f1f
-DStabActiveBackground=ffdadada
-DStabActiveText=ff111111
-DStabActiveIcon=ff000000
+DStabActiveBackground=raincloudGrey
+DStabActiveText=offWhite
+DStabActiveIcon=offWhite
DStabActiveButtonHover=ffdadada
DStabActiveButtonPress=ffdadada
-DStabFocusBackground=ff2aafd3
+DStabFocusBackground=highlightBlue
DStabFocusText=ff111111
DStabFocusIcon=ff000000
-DStabFocusButtonHover=ff2aafd3
-DStabFocusButtonPress=ff2aafd3
+DStabFocusButtonHover=highlightBlue
+DStabFocusButtonPress=highlightBlue
DSnavigatorBranch=ff7c7b7b
DSnavigatorBranchIndicator=ff7c7b7b
-DSnavigatorItemBackground=ff2E2F30
-DSnavigatorItemBackgroundHover=ff333333
-DSnavigatorItemBackgroundSelected=ff3D3D3D
-DSnavigatorText=ffffffff
+DSnavigatorItemBackground=dawnGrey
+DSnavigatorItemBackgroundHover=graniteGrey
+DSnavigatorItemBackgroundSelected=midnightGrey
+DSnavigatorText=lightWhite
DSnavigatorTextHover=ffffffff
DSnavigatorTextSelected=ff2aafd3
DSnavigatorIcon=ffffffff
@@ -176,6 +229,8 @@ DSUnimportedModuleColor=ffe33c2e
DSBackgroundColorAlternate=alternateBackground
DSBackgroundColorNormal=normalBackground
+DStoolbarBackground=midnightGrey
+
;DS controls theme END
BackgroundColorAlternate=alternateBackground
@@ -188,7 +243,6 @@ BadgeLabelBackgroundColorChecked=ffe0e0e0
BadgeLabelBackgroundColorUnchecked=ff808080
BadgeLabelTextColorChecked=ff606060
BadgeLabelTextColorUnchecked=ffffffff
-CanceledSearchTextColor=ff0000
ComboBoxArrowColor=toolBarItem
ComboBoxArrowColorDisabled=toolBarItemDisabled
ComboBoxTextColor=fancyBarsNormalTextColor
@@ -356,7 +410,7 @@ QmlDesigner_BackgroundColor=qmlDesignerButtonColor
QmlDesigner_HighlightColor=ff1d545c
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
QmlDesigner_FormEditorForegroundColor=ffffffff
-QmlDesigner_BackgroundColorDarkAlternate=ff323232
+QmlDesigner_BackgroundColorDarkAlternate=dawnGrey
QmlDesigner_BackgroundColorDarker=ff262728
QmlDesigner_BorderColor=splitter
QmlDesigner_ButtonColor=ff595b5c
@@ -389,6 +443,27 @@ PaletteTextDisabled=textDisabled
PaletteMid=ffa0a0a0
PalettePlaceholderText=ff7f7f80
+TerminalForeground=ffffffff
+TerminalBackground=normalBackground
+TerminalSelection=7fffffff
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0058D1
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=e5e54b
+TerminalAnsi12=003EFF
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=false
DerivePaletteFromTheme=true
@@ -405,3 +480,4 @@ FlatMenuBar=true
ToolBarIconShadow=true
WindowColorAsBase=false
DarkUserInterface=true
+QDSTheme=false
diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme
index d3ff03a798..cbf4b09ce1 100644
--- a/share/qtcreator/themes/flat-light.creatortheme
+++ b/share/qtcreator/themes/flat-light.creatortheme
@@ -21,8 +21,64 @@ error=ffdf4f4f
warning=ffecbc1c
qmlDesignerButtonColor=fff8f8f8
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+concreteGrey=ffbbbbbb
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
;DS controls theme START
+
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=offWhite
+DScontrolBackground_toolbarHover=offWhite
+DScontrolBackground_topToolbarHover=concreteGrey
+DScontrolBackground_statusbarIdle=concreteGrey
+DSControlBackground_statusbarHover=lightWhite
+DScontrolOutline_topToolbarIdle=concreteGrey
+DScontrolOutline_topToolbarHover=lightWhite
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=offWhite
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=smokeGrey
+DStextColor=raincloudGrey
+DSstatusbarBackground=concreteGrey
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=concreteGrey
+DSstateControlBackgroundColor_globalHover=concreteGrey
+DSstateControlBackgroundColor_hover=smokeGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
+
DSpanelBackground=ffeaeaea
DSwelcomeScreenBackground=ffEAEAEA
@@ -172,6 +228,8 @@ DSUnimportedModuleColor=ffe33c2e
DSBackgroundColorAlternate=ffeaeaea
DSBackgroundColorNormal=ffd8d8d8
+DStoolbarBackground=offWhite
+
;DS controls theme END
BackgroundColorAlternate=alternateBackground
@@ -184,7 +242,6 @@ BadgeLabelBackgroundColorChecked=ffe0e0e0
BadgeLabelBackgroundColorUnchecked=ff808080
BadgeLabelTextColorChecked=ff606060
BadgeLabelTextColorUnchecked=ffffffff
-CanceledSearchTextColor=ff0000
ComboBoxArrowColor=toolBarItem
ComboBoxArrowColorDisabled=toolBarItemDisabled
ComboBoxTextColor=fancyBarsNormalTextColor
@@ -362,6 +419,27 @@ QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
QmlDesigner_ScrollBarHandleColor=ffcccccc
+TerminalForeground=ff000000
+TerminalBackground=normalBackground
+TerminalSelection=3f000000
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0000ab
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=dac911
+TerminalAnsi12=0000fe
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=false
DerivePaletteFromTheme=false
@@ -378,3 +456,4 @@ FlatMenuBar=false
ToolBarIconShadow=false
WindowColorAsBase=false
DarkUserInterface=false
+QDSTheme=false
diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme
index 45ffe8537d..a252562a56 100644
--- a/share/qtcreator/themes/flat.creatortheme
+++ b/share/qtcreator/themes/flat.creatortheme
@@ -19,9 +19,62 @@ warning=ffecbc1c
splitter=ff313131
qmlDesignerButtonColor=ff4c4e50
+;DS Theme Palette START
+;greyscale
+fullWhite=ffffffff
+lightWhite=ffdfdfdf
+offWhite=ffdcdada
+slateGrey=ff8d8d8d
+smokeGrey=ff8b8b8b
+shadowGrey=ff636363
+duskGrey=ff606060
+raincloudGrey=ff4d4d4d
+graniteGrey=ff343434
+ashGrey=ff434343
+midnightGrey=ff333333
+dawnGrey=ff2a2a2a
+offBlack=ff202020
+nearBlack=ff1b1b1b
+fullBlack=ff000000
+
+;special colors
+idleGreen=ff649a5d
+runningGreen=ff2eff68
+successGreen=ff2bcf32
+idleRed=ff6a4242
+recordingRed=ffcc3c34
+errorRed=ffaf2b2b
+warningOrange=ffca8113
+splitterBlue=ff64daff
+rgbBlue=ff64daff
+highlightBlue=ff57b9fc
+highlightHover=ff74CBFC
+;DS Theme Palette END
+
[Colors]
;DS controls theme START
-DSpanelBackground=ff2E2F30
+
+;NEW FOR QtDS 4.0
+DScontrolBackground_toolbarIdle=midnightGrey
+DScontrolBackground_toolbarHover=midnightGrey
+DScontrolBackground_topToolbarHover=ashGrey
+DScontrolBackground_statusbarIdle=offBlack
+DSControlBackground_statusbarHover=dawnGrey
+DScontrolOutline_topToolbarIdle=dawnGrey
+DScontrolOutline_topToolbarHover=raincloudGrey
+DSidleGreen=idleGreen
+DSrunningGreen=runningGreen
+DStoolbarBackground=midnightGrey
+DStoolbarIcon_blocked=shadowGrey
+DSthumbnailBackground_baseState=nearBlack
+DStextColor=lightWhite
+DSstatusbarBackground=offBlack
+DSprimaryButton_hoverHighlight=highlightHover
+DSstateBackgroundColor_hover=ashGrey
+DSstateControlBackgroundColor_globalHover=ashGrey
+DSstateControlBackgroundColor_hover=raincloudGrey
+DSpanelBackground=dawnGrey
+;END NEW FOR QtDS 4.0
DSwelcomeScreenBackground=ff242424
DSsubPanelBackground=ff1c1c1c
@@ -32,21 +85,21 @@ DSgreenLight=ff5cdc68
DSamberLight=ffffbf00
DSredLight=ffff0401
-DSinteraction=ff2aafd3
+DSinteraction=highlightBlue
DSerrorColor=ffdf3a3a
DSwarningColor=warning
DSdisabledColor=ff707070
DSinteractionHover=ff74cbfc
-DScontrolBackground=ff2e2f30
+DScontrolBackground=dawnGrey
DScontrolBackgroundInteraction=ff3d3d3d
DScontrolBackgroundDisabled=ff2e2f30
DScontrolBackgroundGlobalHover=ff333333
DScontrolBackgroundHover=ff333333
-DScontrolOutline=ff1f1f1f
-DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutline=nearBlack
+DScontrolOutlineInteraction=highlightBlue
DScontrolOutlineDisabled=ff707070
DStextColor=ffffffff
@@ -59,16 +112,16 @@ DSplaceholderTextColorInteraction=ffababab
DSiconColor=ffffffff
DSiconColorHover=ffffffff
-DSiconColorInteraction=ff707070
+DSiconColorInteraction=nearBlack
DSiconColorDisabled=ffC7C7C7
-DSiconColorSelected=ff2aafd3
+DSiconColorSelected=nearBlack
DSlinkIndicatorColor=ff808080
DSlinkIndicatorColorHover=ffffffff
DSlinkIndicatorColorInteraction=ff2aafd3
DSlinkIndicatorColorDisabled=ff707070
-DSpopupBackground=ff474747
+DSpopupBackground=offBlack
DSpopupOverlayColor=99191919
DSsliderActiveTrack=ff7c7b7b
@@ -82,13 +135,13 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
-DSscrollBarTrack=ff3E3E3E
-DSscrollBarHandle=ff4C4C4C
+DSscrollBarTrack=dawnGrey
+DSscrollBarHandle=offBlack
-DSsectionHeadBackground=ff1f1f1f
+DSsectionHeadBackground=midnightGrey
DSstateDefaultHighlight=ffffe400
-DSstateSeparatorColor=ff7c7b7b
+DSstateSeparatorColor=graniteGrey
DSstateBackgroundColor=ff383838
DSstatePreviewOutline=ffaaaaaa
@@ -114,8 +167,8 @@ DSdockContainerSplitter=ff323232
DSdockAreaBackground=ff262728
DSdockWidgetBackground=ff00ff00
-DSdockWidgetSplitter=ff595959
-DSdockWidgetTitleBar=ff1f1f1f
+DSdockWidgetSplitter=fullBlack
+DSdockWidgetTitleBar=dawnGrey
DStitleBarText=ffdadada
DStitleBarIcon=ffffffff
@@ -131,24 +184,24 @@ DStabInactiveIcon=ffffffff
DStabInactiveButtonHover=ff1f1f1f
DStabInactiveButtonPress=ff1f1f1f
-DStabActiveBackground=ffdadada
-DStabActiveText=ff111111
-DStabActiveIcon=ff000000
+DStabActiveBackground=raincloudGrey
+DStabActiveText=offWhite
+DStabActiveIcon=offWhite
DStabActiveButtonHover=ffdadada
DStabActiveButtonPress=ffdadada
-DStabFocusBackground=ff2aafd3
+DStabFocusBackground=highlightBlue
DStabFocusText=ff111111
DStabFocusIcon=ff000000
-DStabFocusButtonHover=ff2aafd3
-DStabFocusButtonPress=ff2aafd3
+DStabFocusButtonHover=highlightBlue
+DStabFocusButtonPress=highlightBlue
DSnavigatorBranch=ff7c7b7b
DSnavigatorBranchIndicator=ff7c7b7b
-DSnavigatorItemBackground=ff2E2F30
-DSnavigatorItemBackgroundHover=ff333333
-DSnavigatorItemBackgroundSelected=ff3D3D3D
-DSnavigatorText=ffffffff
+DSnavigatorItemBackground=dawnGrey
+DSnavigatorItemBackgroundHover=graniteGrey
+DSnavigatorItemBackgroundSelected=midnightGrey
+DSnavigatorText=lightWhite
DSnavigatorTextHover=ffffffff
DSnavigatorTextSelected=ff2aafd3
DSnavigatorIcon=ffffffff
@@ -170,6 +223,8 @@ DSUnimportedModuleColor=ffe33c2e
DSBackgroundColorAlternate=alternateBackground
DSBackgroundColorNormal=normalBackground
+DStoolbarBackground=midnightGrey
+
;DS controls theme END
BackgroundColorAlternate=alternateBackground
@@ -182,7 +237,6 @@ BadgeLabelBackgroundColorChecked=ffe0e0e0
BadgeLabelBackgroundColorUnchecked=ff808080
BadgeLabelTextColorChecked=ff606060
BadgeLabelTextColorUnchecked=ffffffff
-CanceledSearchTextColor=ff0000
ComboBoxArrowColor=toolBarItem
ComboBoxArrowColorDisabled=toolBarItemDisabled
ComboBoxTextColor=fancyBarsNormalTextColor
@@ -350,7 +404,7 @@ QmlDesigner_BackgroundColor=qmlDesignerButtonColor
QmlDesigner_HighlightColor=ff46a2da
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
QmlDesigner_FormEditorForegroundColor=ffffffff
-QmlDesigner_BackgroundColorDarkAlternate=ff323232
+QmlDesigner_BackgroundColorDarkAlternate=dawnGrey
QmlDesigner_BackgroundColorDarker=ff262728
QmlDesigner_BorderColor=splitter
QmlDesigner_ButtonColor=ff595b5c
@@ -360,6 +414,27 @@ QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
QmlDesigner_ScrollBarHandleColor=ff595b5c
+TerminalForeground=ff000000
+TerminalBackground=normalBackground
+TerminalSelection=3f000000
+TerminalFindMatch=7fffff00
+TerminalAnsi0=000000
+TerminalAnsi1=8b1b10
+TerminalAnsi2=4aa32e
+TerminalAnsi3=9a9a2f
+TerminalAnsi4=0000ab
+TerminalAnsi5=a320ac
+TerminalAnsi6=49a3b0
+TerminalAnsi7=bfbfbf
+TerminalAnsi8=666666
+TerminalAnsi9=d22d1f
+TerminalAnsi10=62d63f
+TerminalAnsi11=dac911
+TerminalAnsi12=0000fe
+TerminalAnsi13=d22dde
+TerminalAnsi14=69e2e4
+TerminalAnsi15=e5e5e6
+
[Flags]
ComboBoxDrawTextShadow=false
DerivePaletteFromTheme=false
@@ -376,3 +451,4 @@ FlatMenuBar=false
ToolBarIconShadow=true
WindowColorAsBase=false
DarkUserInterface=false
+QDSTheme=false
diff --git a/share/qtcreator/translations/qtcreator_cs.ts b/share/qtcreator/translations/qtcreator_cs.ts
index 7f62f6a363..c96e36d85b 100644
--- a/share/qtcreator/translations/qtcreator_cs.ts
+++ b/share/qtcreator/translations/qtcreator_cs.ts
@@ -9720,10 +9720,6 @@ se projektu &apos;%2&apos; nepodařilo přidat.</translation>
<translation type="obsolete">Zdvojit sezení</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-quick-tour.html#session-management-in-qt-creator&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation type="obsolete">&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-quick-tour.html#session-management-in-qt-creator&quot;&gt;Co je to sezení?&lt;/a&gt;</translation>
- </message>
- <message>
<source>Switch to session</source>
<translation type="obsolete">Přepnout na sezení</translation>
</message>
@@ -9748,8 +9744,8 @@ se projektu &apos;%2&apos; nepodařilo přidat.</translation>
<translation>&amp;Otevřít</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Co je sezení?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Co je sezení?</translation>
</message>
<message>
<source>New session name</source>
diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts
index 67e43c395b..210c99ebbb 100644
--- a/share/qtcreator/translations/qtcreator_da.ts
+++ b/share/qtcreator/translations/qtcreator_da.ts
@@ -22723,8 +22723,8 @@ til projektet &quot;%2&quot;.</translation>
<translation>Genskab sidste session ved opstart</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Hvad er en session?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Hvad er en session?</translation>
</message>
<message>
<source>Session</source>
diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts
index 65fec424e4..033acd385d 100644
--- a/share/qtcreator/translations/qtcreator_de.ts
+++ b/share/qtcreator/translations/qtcreator_de.ts
@@ -35115,8 +35115,8 @@ Title of a the cloned RunConfiguration window, text of the window</extracomment>
<translation>Bei Start letzte Sitzung laden</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Was ist eine Sitzung?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Was ist eine Sitzung?</translation>
</message>
<message>
<source>&amp;Open</source>
diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts
index 5ee57efdde..48fa7edbe0 100644
--- a/share/qtcreator/translations/qtcreator_fr.ts
+++ b/share/qtcreator/translations/qtcreator_fr.ts
@@ -9880,10 +9880,6 @@ francis : voila une nouvelle suggestion :)</translatorcomment>
<translation>Renommer la session</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://com.nokia.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation type="obsolete">&lt;a href=&quot;qthelp://com.nokia.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Qu&apos;est-ce qu&apos;une session?&lt;/a&gt;</translation>
- </message>
- <message>
<source>Automatically restore the last session when Qt Creator is started.</source>
<translation>Restaurer automatiquement la dernière session quand Qt Creator est démarré. </translation>
</message>
@@ -9892,8 +9888,8 @@ francis : voila une nouvelle suggestion :)</translatorcomment>
<translation>Restaurer la dernière session au démarrage</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Qu&apos;est-ce qu&apos;une session ?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Qu&apos;est-ce qu&apos;une session ?</translation>
</message>
<message>
<source>Custom Process Step</source>
diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts
index 86fd5ff638..179850cef1 100644
--- a/share/qtcreator/translations/qtcreator_hr.ts
+++ b/share/qtcreator/translations/qtcreator_hr.ts
@@ -5588,8 +5588,8 @@ Greška: %5</translation>
<translation>Vrati izvorno stanje posljednje sesije prilikom pokretanja programa</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Što je sesija?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Što je sesija?</translation>
</message>
</context>
<context>
diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts
index efdd53f058..6014b49996 100644
--- a/share/qtcreator/translations/qtcreator_ja.ts
+++ b/share/qtcreator/translations/qtcreator_ja.ts
@@ -4403,8 +4403,8 @@ Add, modify, and remove document filters, which determine the documentation set
<translation>起動時に最後のセッションを復元する</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;セッションとは?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>セッションとは?</translation>
</message>
<message>
<source>Automatically restores the last session when Qt Creator is started.</source>
diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts
index 56cdaa31b6..be632f066d 100644
--- a/share/qtcreator/translations/qtcreator_pl.ts
+++ b/share/qtcreator/translations/qtcreator_pl.ts
@@ -903,8 +903,8 @@
<translation>&amp;Otwórz</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Co to jest sesja?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Co to jest sesja?</translation>
</message>
<message>
<source>Restore last session on startup</source>
diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts
index e9fd7a8193..f6c71e9dcf 100644
--- a/share/qtcreator/translations/qtcreator_ru.ts
+++ b/share/qtcreator/translations/qtcreator_ru.ts
@@ -29153,8 +29153,8 @@ to project &quot;%2&quot;.</source>
<translation>&amp;Удалить</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Что такое сессия?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Что такое сессия?</translation>
</message>
<message>
<source>Restore last session on startup</source>
diff --git a/share/qtcreator/translations/qtcreator_sl.ts b/share/qtcreator/translations/qtcreator_sl.ts
index 6d74be5bd7..462ed14e8a 100644
--- a/share/qtcreator/translations/qtcreator_sl.ts
+++ b/share/qtcreator/translations/qtcreator_sl.ts
@@ -6772,8 +6772,8 @@ enojen »Vstopi« za oddajo signala pa vas bo privedel neposredno do ustrezne pr
<translation>&amp;Odpri</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;Kaj je seja?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>Kaj je seja?</translation>
</message>
<message>
<source>New session name</source>
diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts
index ba78031390..b1e219464c 100644
--- a/share/qtcreator/translations/qtcreator_zh_CN.ts
+++ b/share/qtcreator/translations/qtcreator_zh_CN.ts
@@ -28222,8 +28222,8 @@ to project &quot;%2&quot;.</source>
<translation>打开(&amp;S)</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-quick-tour.html#session-management-in-qt-creator&quot;&gt;什么是会话?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>什么是会话?</translation>
</message>
<message>
<source>Restore last session on startup</source>
diff --git a/share/qtcreator/translations/qtcreator_zh_TW.ts b/share/qtcreator/translations/qtcreator_zh_TW.ts
index c1f35c8180..7f39bb619d 100644
--- a/share/qtcreator/translations/qtcreator_zh_TW.ts
+++ b/share/qtcreator/translations/qtcreator_zh_TW.ts
@@ -6500,8 +6500,8 @@ Add, modify, and remove document filters, which determine the documentation set
<translation>重新命名工作階段</translation>
</message>
<message>
- <source>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;What is a Session?&lt;/a&gt;</source>
- <translation>&lt;a href=&quot;qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html&quot;&gt;什麼是工作階段?&lt;/a&gt;</translation>
+ <source>What is a Session?</source>
+ <translation>什麼是工作階段?</translation>
</message>
<message>
<source>Automatically restore the last session when Qt Creator is started.</source>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b567bbe120..792b3012f2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -36,9 +36,6 @@ install(EXPORT QtCreator
)
file(WRITE ${CMAKE_BINARY_DIR}/cmake/QtCreatorConfig.cmake "
-\# add module path for special FindQt5.cmake that considers Qt6 too
-list(APPEND CMAKE_MODULE_PATH \${CMAKE_CURRENT_LIST_DIR})
-
\# force plugins to same path naming conventions as Qt Creator
\# otherwise plugins will not be found
if(UNIX AND NOT APPLE)
@@ -50,10 +47,10 @@ if(UNIX AND NOT APPLE)
endif()
include(CMakeFindDependencyMacro)
-find_dependency(Qt5 ${IDE_QT_VERSION_MIN}
+find_dependency(Qt6 ${IDE_QT_VERSION_MIN}
COMPONENTS Concurrent Core Gui Widgets Core5Compat Network PrintSupport Qml Sql REQUIRED
)
-find_dependency(Qt5 COMPONENTS Quick QuickWidgets QUIET)
+find_dependency(Qt6 COMPONENTS Quick QuickWidgets QUIET)
if (NOT IDE_VERSION)
include(\${CMAKE_CURRENT_LIST_DIR}/QtCreatorIDEBranding.cmake)
@@ -87,7 +84,6 @@ file(COPY
${PROJECT_SOURCE_DIR}/cmake/QtCreatorDocumentation.cmake
${PROJECT_SOURCE_DIR}/cmake/QtCreatorAPI.cmake
${PROJECT_SOURCE_DIR}/cmake/QtCreatorAPIInternal.cmake
- ${PROJECT_SOURCE_DIR}/cmake/FindQt5.cmake
${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in
${PROJECT_SOURCE_DIR}/cmake/QtcSeparateDebugInfo.cmake
${PROJECT_SOURCE_DIR}/cmake/QtcSeparateDebugInfo.Info.plist.in
@@ -102,7 +98,6 @@ install(
${PROJECT_SOURCE_DIR}/cmake/QtCreatorDocumentation.cmake
${PROJECT_SOURCE_DIR}/cmake/QtCreatorAPI.cmake
${PROJECT_SOURCE_DIR}/cmake/QtCreatorAPIInternal.cmake
- ${PROJECT_SOURCE_DIR}/cmake/FindQt5.cmake
${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in
${PROJECT_SOURCE_DIR}/cmake/QtcSeparateDebugInfo.cmake
${PROJECT_SOURCE_DIR}/cmake/QtcSeparateDebugInfo.Info.plist.in
diff --git a/src/app/main.cpp b/src/app/main.cpp
index bda27b5e82..3a2cb5bfaa 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -22,29 +22,20 @@
#include <QDebug>
#include <QDir>
-#include <QFontDatabase>
#include <QFileInfo>
+#include <QFontDatabase>
#include <QLibraryInfo>
-#include <QScopeGuard>
-#include <QStyle>
-#include <QTextStream>
-#include <QThreadPool>
-#include <QTimer>
-#include <QTranslator>
-#include <QUrl>
-#include <QVariant>
-
-#include <QSysInfo>
-
-#include <QNetworkProxyFactory>
-
-#include <QApplication>
#include <QMessageBox>
+#include <QNetworkProxyFactory>
#include <QPixmapCache>
#include <QProcess>
+#include <QScopeGuard>
#include <QStandardPaths>
-#include <QTemporaryDir>
+#include <QStyle>
#include <QTextCodec>
+#include <QTextStream>
+#include <QThreadPool>
+#include <QTranslator>
#include <iterator>
#include <optional>
@@ -425,13 +416,26 @@ QStringList lastSessionArgument()
return hasProjectExplorer ? QStringList({"-lastsession"}) : QStringList();
}
+// should be in sync with src/plugins/coreplugin/icore.cpp -> FilePath ICore::crashReportsPath()
+// and src\tools\qml2puppet\qml2puppet\qmlpuppet.cpp -> QString crashReportsPath()
+QString crashReportsPath()
+{
+ std::unique_ptr<QSettings> settings(createUserSettings());
+ QFileInfo(settings->fileName()).path() + "/crashpad_reports";
+ if (Utils::HostOsInfo::isMacHost())
+ return QFileInfo(createUserSettings()->fileName()).path() + "/crashpad_reports";
+ else
+ return QCoreApplication::applicationDirPath()
+ + '/' + RELATIVE_LIBEXEC_PATH + "crashpad_reports";
+}
+
#ifdef ENABLE_CRASHPAD
bool startCrashpad(const QString &libexecPath, bool crashReportingEnabled)
{
using namespace crashpad;
// Cache directory that will store crashpad information and minidumps
- QString databasePath = QDir::cleanPath(libexecPath + "/crashpad_reports");
+ QString databasePath = QDir::cleanPath(crashReportsPath());
QString handlerPath = QDir::cleanPath(libexecPath + "/crashpad_handler");
#ifdef Q_OS_WIN
handlerPath += ".exe";
@@ -453,6 +457,9 @@ bool startCrashpad(const QString &libexecPath, bool crashReportingEnabled)
std::map<std::string, std::string> annotations;
annotations["app-version"] = Core::Constants::IDE_VERSION_DISPLAY;
annotations["qt-version"] = QT_VERSION_STR;
+#ifdef IDE_REVISION
+ annotations["sha1"] = Core::Constants::IDE_REVISION_STR;
+#endif
// Optional arguments to pass to the handler
std::vector<std::string> arguments;
@@ -511,11 +518,6 @@ int main(int argc, char **argv)
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
}
- if (Utils::HostOsInfo::isRunningUnderRosetta()) {
- // work around QTBUG-97085: QRegularExpression jitting is not reentrant under Rosetta
- qputenv("QT_ENABLE_REGEXP_JIT", "0");
- }
-
if (Utils::HostOsInfo::isLinuxHost() && !qEnvironmentVariableIsSet("GTK_THEME"))
// Work around QTCREATORBUG-28497:
// Prevent Qt's GTK3 platform theme plugin from enforcing a dark palette
@@ -573,11 +575,12 @@ int main(int argc, char **argv)
SharedTools::QtSingleApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
- int numberofArguments = static_cast<int>(options.appArguments.size());
+ int numberOfArguments = static_cast<int>(options.appArguments.size());
- SharedTools::QtSingleApplication app((QLatin1String(Core::Constants::IDE_DISPLAY_NAME)),
- numberofArguments,
- options.appArguments.data());
+ std::unique_ptr<SharedTools::QtSingleApplication>
+ appPtr(SharedTools::createApplication(QLatin1String(Core::Constants::IDE_DISPLAY_NAME),
+ numberOfArguments, options.appArguments.data()));
+ SharedTools::QtSingleApplication &app = *appPtr;
QCoreApplication::setApplicationName(Core::Constants::IDE_CASED_ID);
QCoreApplication::setApplicationVersion(QLatin1String(Core::Constants::IDE_VERSION_LONG));
QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR));
diff --git a/src/libs/3rdparty/CMakeLists.txt b/src/libs/3rdparty/CMakeLists.txt
index 7cf97ab87f..0cf9818ed5 100644
--- a/src/libs/3rdparty/CMakeLists.txt
+++ b/src/libs/3rdparty/CMakeLists.txt
@@ -1,2 +1,8 @@
add_subdirectory(cplusplus)
add_subdirectory(syntax-highlighting)
+add_subdirectory(libvterm)
+add_subdirectory(libptyqt)
+
+if(WIN32)
+ add_subdirectory(winpty)
+endif()
diff --git a/src/libs/3rdparty/cplusplus/AST.cpp b/src/libs/3rdparty/cplusplus/AST.cpp
index 5d7c9e2a90..5c59bb683c 100644
--- a/src/libs/3rdparty/cplusplus/AST.cpp
+++ b/src/libs/3rdparty/cplusplus/AST.cpp
@@ -911,6 +911,8 @@ int DeclaratorAST::lastToken() const
return candidate;
if (equal_token)
return equal_token + 1;
+ if (requiresClause)
+ return requiresClause->lastToken();
if (post_attribute_list)
if (int candidate = post_attribute_list->lastToken())
return candidate;
@@ -1657,12 +1659,18 @@ int LambdaDeclaratorAST::firstToken() const
if (trailing_return_type)
if (int candidate = trailing_return_type->firstToken())
return candidate;
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
return 0;
}
/** \generated */
int LambdaDeclaratorAST::lastToken() const
{
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
if (trailing_return_type)
if (int candidate = trailing_return_type->lastToken())
return candidate;
@@ -1690,6 +1698,15 @@ int LambdaExpressionAST::firstToken() const
if (lambda_introducer)
if (int candidate = lambda_introducer->firstToken())
return candidate;
+ if (templateParameters)
+ if (int candidate = templateParameters->firstToken())
+ return candidate;
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
+ if (attributes)
+ if (int candidate = attributes->firstToken())
+ return candidate;
if (lambda_declarator)
if (int candidate = lambda_declarator->firstToken())
return candidate;
@@ -1708,6 +1725,15 @@ int LambdaExpressionAST::lastToken() const
if (lambda_declarator)
if (int candidate = lambda_declarator->lastToken())
return candidate;
+ if (attributes)
+ if (int candidate = attributes->firstToken())
+ return candidate;
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
+ if (templateParameters)
+ if (int candidate = templateParameters->firstToken())
+ return candidate;
if (lambda_introducer)
if (int candidate = lambda_introducer->lastToken())
return candidate;
@@ -3761,6 +3787,8 @@ int TemplateTypeParameterAST::firstToken() const
{
if (template_token)
return template_token;
+ if (typeConstraint)
+ return typeConstraint->firstToken();
if (less_token)
return less_token;
if (template_parameter_list)
@@ -3805,6 +3833,8 @@ int TemplateTypeParameterAST::lastToken() const
return candidate;
if (less_token)
return less_token + 1;
+ if (typeConstraint)
+ return typeConstraint->lastToken();
if (template_token)
return template_token + 1;
return 1;
@@ -4634,3 +4664,33 @@ int NoExceptOperatorExpressionAST::lastToken() const
return noexcept_token + 1;
return 1;
}
+
+int TypeConstraintAST::firstToken() const
+{
+ if (nestedName)
+ return nestedName->firstToken();
+ return conceptName->firstToken();
+}
+
+int TypeConstraintAST::lastToken() const
+{
+ if (greaterToken)
+ return greaterToken + 1;
+ return conceptName->lastToken();
+}
+
+int PlaceholderTypeSpecifierAST::firstToken() const
+{
+ if (typeConstraint)
+ return typeConstraint->firstToken();
+ if (declTypetoken)
+ return declTypetoken;
+ return autoToken;
+}
+
+int PlaceholderTypeSpecifierAST::lastToken() const
+{
+ if (rparenToken)
+ return rparenToken + 1;
+ return autoToken + 1;
+}
diff --git a/src/libs/3rdparty/cplusplus/AST.h b/src/libs/3rdparty/cplusplus/AST.h
index 9aa81ccdf6..0f40424464 100644
--- a/src/libs/3rdparty/cplusplus/AST.h
+++ b/src/libs/3rdparty/cplusplus/AST.h
@@ -184,6 +184,7 @@ public:
virtual ArrayInitializerAST *asArrayInitializer() { return nullptr; }
virtual AsmDefinitionAST *asAsmDefinition() { return nullptr; }
virtual AttributeSpecifierAST *asAttributeSpecifier() { return nullptr; }
+ virtual AwaitExpressionAST *asAwaitExpression() { return nullptr; }
virtual BaseSpecifierAST *asBaseSpecifier() { return nullptr; }
virtual BinaryExpressionAST *asBinaryExpression() { return nullptr; }
virtual BoolLiteralAST *asBoolLiteral() { return nullptr; }
@@ -290,6 +291,7 @@ public:
virtual OperatorFunctionIdAST *asOperatorFunctionId() { return nullptr; }
virtual ParameterDeclarationAST *asParameterDeclaration() { return nullptr; }
virtual ParameterDeclarationClauseAST *asParameterDeclarationClause() { return nullptr; }
+ virtual PlaceholderTypeSpecifierAST *asPlaceholderTypeSpecifier() { return nullptr; }
virtual PointerAST *asPointer() { return nullptr; }
virtual PointerLiteralAST *asPointerLiteral() { return nullptr; }
virtual PointerToMemberAST *asPointerToMember() { return nullptr; }
@@ -322,6 +324,7 @@ public:
virtual StringLiteralAST *asStringLiteral() { return nullptr; }
virtual SwitchStatementAST *asSwitchStatement() { return nullptr; }
virtual TemplateDeclarationAST *asTemplateDeclaration() { return nullptr; }
+ virtual ConceptDeclarationAST *asConceptDeclaration() { return nullptr; }
virtual TemplateIdAST *asTemplateId() { return nullptr; }
virtual TemplateTypeParameterAST *asTemplateTypeParameter() { return nullptr; }
virtual ThisExpressionAST *asThisExpression() { return nullptr; }
@@ -329,6 +332,7 @@ public:
virtual TrailingReturnTypeAST *asTrailingReturnType() { return nullptr; }
virtual TranslationUnitAST *asTranslationUnit() { return nullptr; }
virtual TryBlockStatementAST *asTryBlockStatement() { return nullptr; }
+ virtual TypeConstraintAST *asTypeConstraint() { return nullptr; }
virtual TypeConstructorCallAST *asTypeConstructorCall() { return nullptr; }
virtual TypeIdAST *asTypeId() { return nullptr; }
virtual TypeidExpressionAST *asTypeidExpression() { return nullptr; }
@@ -336,9 +340,12 @@ public:
virtual TypenameTypeParameterAST *asTypenameTypeParameter() { return nullptr; }
virtual TypeofSpecifierAST *asTypeofSpecifier() { return nullptr; }
virtual UnaryExpressionAST *asUnaryExpression() { return nullptr; }
+ virtual RequiresExpressionAST *asRequiresExpression() { return nullptr; }
+ virtual RequiresClauseAST *asRequiresClause() { return nullptr; }
virtual UsingAST *asUsing() { return nullptr; }
virtual UsingDirectiveAST *asUsingDirective() { return nullptr; }
virtual WhileStatementAST *asWhileStatement() { return nullptr; }
+ virtual YieldExpressionAST *asYieldExpression() { return nullptr; }
protected:
virtual void accept0(ASTVisitor *visitor) = 0;
@@ -636,6 +643,49 @@ protected:
bool match0(AST *, ASTMatcher *) override;
};
+class CPLUSPLUS_EXPORT TypeConstraintAST: public AST
+{
+public:
+ NestedNameSpecifierListAST *nestedName = nullptr;
+ NameAST *conceptName = nullptr;
+ int lessToken = 0;
+ ExpressionListAST *templateArgs = nullptr;
+ int greaterToken = 0;
+
+ TypeConstraintAST *asTypeConstraint() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
+
+ TypeConstraintAST *clone(MemoryPool *pool) const override;
+
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT PlaceholderTypeSpecifierAST: public SpecifierAST
+{
+public:
+ TypeConstraintAST *typeConstraint = nullptr;
+ int declTypetoken = 0;
+ int lparenToken = 0;
+ int decltypeToken = 0;
+ int autoToken = 0;
+ int rparenToken = 0;
+
+public:
+ PlaceholderTypeSpecifierAST *asPlaceholderTypeSpecifier() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
+
+ PlaceholderTypeSpecifierAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
class CPLUSPLUS_EXPORT DeclaratorAST: public AST
{
public:
@@ -646,6 +696,7 @@ public:
SpecifierListAST *post_attribute_list = nullptr;
int equal_token = 0;
ExpressionAST *initializer = nullptr;
+ RequiresClauseAST *requiresClause = nullptr;
public:
DeclaratorAST *asDeclarator() override { return this; }
@@ -2716,6 +2767,7 @@ public:
int less_token = 0;
DeclarationListAST *template_parameter_list = nullptr;
int greater_token = 0;
+ RequiresClauseAST *requiresClause = nullptr;
DeclarationAST *declaration = nullptr;
public: // annotations
@@ -2734,6 +2786,29 @@ protected:
bool match0(AST *, ASTMatcher *) override;
};
+class CPLUSPLUS_EXPORT ConceptDeclarationAST: public DeclarationAST
+{
+public:
+ int concept_token = 0;
+ NameAST *name = nullptr;
+ SpecifierListAST *attributes = nullptr;
+ int equals_token = 0;
+ ExpressionAST *constraint = nullptr;
+ int semicolon_token = 0;
+
+public:
+ ConceptDeclarationAST *asConceptDeclaration() override { return this; }
+
+ int firstToken() const override { return concept_token; }
+ int lastToken() const override { return semicolon_token + 1; }
+
+ ConceptDeclarationAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
class CPLUSPLUS_EXPORT ThrowExpressionAST: public ExpressionAST
{
public:
@@ -2753,6 +2828,44 @@ protected:
bool match0(AST *, ASTMatcher *) override;
};
+class CPLUSPLUS_EXPORT YieldExpressionAST: public ExpressionAST
+{
+public:
+ int yield_token = 0;
+ ExpressionAST *expression = nullptr;
+
+public:
+ YieldExpressionAST *asYieldExpression() override { return this; }
+
+ int firstToken() const override { return yield_token; }
+ int lastToken() const override { return expression->lastToken(); }
+
+ YieldExpressionAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT AwaitExpressionAST: public ExpressionAST
+{
+public:
+ int await_token = 0;
+ ExpressionAST *castExpression = nullptr;
+
+public:
+ AwaitExpressionAST *asAwaitExpression() override { return this; }
+
+ int firstToken() const override { return await_token; }
+ int lastToken() const override { return castExpression->lastToken(); }
+
+ AwaitExpressionAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
class CPLUSPLUS_EXPORT NoExceptOperatorExpressionAST: public ExpressionAST
{
public:
@@ -2883,6 +2996,7 @@ class CPLUSPLUS_EXPORT TemplateTypeParameterAST: public DeclarationAST
{
public:
int template_token = 0;
+ TypeConstraintAST *typeConstraint = nullptr;
int less_token = 0;
DeclarationListAST *template_parameter_list = nullptr;
int greater_token = 0;
@@ -2927,6 +3041,48 @@ protected:
bool match0(AST *, ASTMatcher *) override;
};
+class CPLUSPLUS_EXPORT RequiresExpressionAST: public ExpressionAST
+{
+public:
+ int requires_token = 0;
+ int lparen_token = 0;
+ ParameterDeclarationClauseAST *parameters = nullptr;
+ int rparen_token = 0;
+ int lbrace_token = 0;
+ int rbrace_token = 0;
+
+public:
+ RequiresExpressionAST *asRequiresExpression() override { return this; }
+
+ int firstToken() const override { return requires_token; }
+ int lastToken() const override { return rbrace_token + 1; }
+
+ RequiresExpressionAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT RequiresClauseAST: public AST
+{
+public:
+ int requires_token = 0;
+ ExpressionAST *constraint = nullptr;
+
+public:
+ RequiresClauseAST *asRequiresClause() override { return this; }
+
+ int firstToken() const override { return requires_token; }
+ int lastToken() const override { return constraint->lastToken(); }
+
+ RequiresClauseAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
class CPLUSPLUS_EXPORT UsingAST: public DeclarationAST
{
public:
@@ -3522,6 +3678,9 @@ class LambdaExpressionAST: public ExpressionAST
{
public:
LambdaIntroducerAST *lambda_introducer = nullptr;
+ DeclarationListAST *templateParameters = nullptr;
+ RequiresClauseAST *requiresClause = nullptr;
+ SpecifierListAST *attributes = nullptr;
LambdaDeclaratorAST *lambda_declarator = nullptr;
StatementAST *statement = nullptr;
@@ -3602,6 +3761,7 @@ public:
int mutable_token = 0;
ExceptionSpecificationAST *exception_specification = nullptr;
TrailingReturnTypeAST *trailing_return_type = nullptr;
+ RequiresClauseAST *requiresClause = nullptr;
public: // annotations
Function *symbol = nullptr;
diff --git a/src/libs/3rdparty/cplusplus/ASTClone.cpp b/src/libs/3rdparty/cplusplus/ASTClone.cpp
index 9e5a773bdb..e494ad71ac 100644
--- a/src/libs/3rdparty/cplusplus/ASTClone.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTClone.cpp
@@ -141,6 +141,34 @@ DecltypeSpecifierAST *DecltypeSpecifierAST::clone(MemoryPool *pool) const
return ast;
}
+TypeConstraintAST *TypeConstraintAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) TypeConstraintAST;
+ for (NestedNameSpecifierListAST *iter = nestedName, **ast_iter = &ast->nestedName; iter;
+ iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) NestedNameSpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ if (conceptName)
+ ast->conceptName = conceptName->clone(pool);
+ ast->lessToken = lessToken;
+ for (ExpressionListAST *iter = templateArgs, **ast_iter = &ast->templateArgs;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ ast->greaterToken = greaterToken;
+ return ast;
+}
+
+PlaceholderTypeSpecifierAST *PlaceholderTypeSpecifierAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) PlaceholderTypeSpecifierAST;
+ if (typeConstraint)
+ ast->typeConstraint = typeConstraint->clone(pool);
+ ast->lparenToken = lparenToken;
+ ast->declTypetoken = declTypetoken;
+ ast->autoToken = autoToken;
+ ast->rparenToken = rparenToken;
+ return ast;
+}
+
DeclaratorAST *DeclaratorAST::clone(MemoryPool *pool) const
{
DeclaratorAST *ast = new (pool) DeclaratorAST;
@@ -1279,11 +1307,50 @@ TemplateDeclarationAST *TemplateDeclarationAST::clone(MemoryPool *pool) const
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
*ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->greater_token = greater_token;
+ if (requiresClause)
+ ast->requiresClause = requiresClause->clone(pool);
if (declaration)
ast->declaration = declaration->clone(pool);
return ast;
}
+ConceptDeclarationAST *ConceptDeclarationAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) ConceptDeclarationAST;
+ ast->concept_token = concept_token;
+ ast->name = name->clone(pool);
+ ast->equals_token = equals_token;
+ ast->semicolon_token = semicolon_token;
+ for (SpecifierListAST *iter = attributes, **ast_iter = &ast->attributes;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next) {
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ }
+ ast->constraint = constraint->clone(pool);
+ return ast;
+}
+
+RequiresExpressionAST *RequiresExpressionAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) RequiresExpressionAST;
+ ast->requires_token = requires_token;
+ ast->lparen_token = lparen_token;
+ if (parameters)
+ ast->parameters = parameters->clone(pool);
+ ast->rparen_token = rparen_token;
+ ast->lbrace_token = lbrace_token;
+ ast->rbrace_token = rbrace_token;
+ return ast;
+}
+
+RequiresClauseAST *RequiresClauseAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) RequiresClauseAST;
+ ast->requires_token = requires_token;
+ if (constraint)
+ ast->constraint = constraint->clone(pool);
+ return ast;
+}
+
ThrowExpressionAST *ThrowExpressionAST::clone(MemoryPool *pool) const
{
ThrowExpressionAST *ast = new (pool) ThrowExpressionAST;
@@ -1293,6 +1360,24 @@ ThrowExpressionAST *ThrowExpressionAST::clone(MemoryPool *pool) const
return ast;
}
+YieldExpressionAST *YieldExpressionAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) YieldExpressionAST;
+ ast->yield_token = yield_token;
+ if (expression)
+ ast->expression = expression->clone(pool);
+ return ast;
+}
+
+AwaitExpressionAST *AwaitExpressionAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) AwaitExpressionAST;
+ ast->await_token = await_token;
+ if (castExpression)
+ ast->castExpression = castExpression->clone(pool);
+ return ast;
+}
+
NoExceptOperatorExpressionAST *NoExceptOperatorExpressionAST::clone(MemoryPool *pool) const
{
NoExceptOperatorExpressionAST *ast = new (pool) NoExceptOperatorExpressionAST;
@@ -1364,6 +1449,8 @@ TemplateTypeParameterAST *TemplateTypeParameterAST::clone(MemoryPool *pool) cons
{
TemplateTypeParameterAST *ast = new (pool) TemplateTypeParameterAST;
ast->template_token = template_token;
+ if (typeConstraint)
+ ast->typeConstraint = typeConstraint->clone(pool);
ast->less_token = less_token;
for (DeclarationListAST *iter = template_parameter_list, **ast_iter = &ast->template_parameter_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
@@ -1729,6 +1816,14 @@ LambdaExpressionAST *LambdaExpressionAST::clone(MemoryPool *pool) const
LambdaExpressionAST *ast = new (pool) LambdaExpressionAST;
if (lambda_introducer)
ast->lambda_introducer = lambda_introducer->clone(pool);
+ for (DeclarationListAST *iter = templateParameters, **ast_iter = &ast->templateParameters;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ if (requiresClause)
+ ast->requiresClause = requiresClause->clone(pool);
+ for (SpecifierListAST *iter = attributes, **ast_iter = &ast->attributes;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (lambda_declarator)
ast->lambda_declarator = lambda_declarator->clone(pool);
if (statement)
@@ -1780,6 +1875,8 @@ LambdaDeclaratorAST *LambdaDeclaratorAST::clone(MemoryPool *pool) const
ast->exception_specification = exception_specification->clone(pool);
if (trailing_return_type)
ast->trailing_return_type = trailing_return_type->clone(pool);
+ if (requiresClause)
+ ast->requiresClause = requiresClause->clone(pool);
return ast;
}
diff --git a/src/libs/3rdparty/cplusplus/ASTMatch0.cpp b/src/libs/3rdparty/cplusplus/ASTMatch0.cpp
index 4105ec3c52..b58bd59efe 100644
--- a/src/libs/3rdparty/cplusplus/ASTMatch0.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTMatch0.cpp
@@ -112,6 +112,21 @@ bool DecltypeSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool TypeConstraintAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto _other = pattern->asTypeConstraint())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
+bool PlaceholderTypeSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto _other = pattern->asPlaceholderTypeSpecifier())
+ return matcher->match(this, _other);
+ return false;
+}
+
bool DeclaratorAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (DeclaratorAST *_other = pattern->asDeclarator())
@@ -896,6 +911,28 @@ bool TemplateDeclarationAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool ConceptDeclarationAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (ConceptDeclarationAST *_other = pattern->asConceptDeclaration())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
+bool RequiresExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asRequiresExpression())
+ return matcher->match(this, other);
+ return false;
+}
+
+bool RequiresClauseAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asRequiresClause())
+ return matcher->match(this, other);
+ return false;
+}
+
bool ThrowExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (ThrowExpressionAST *_other = pattern->asThrowExpression())
@@ -904,6 +941,20 @@ bool ThrowExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool YieldExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asYieldExpression())
+ return matcher->match(this, other);
+ return false;
+}
+
+bool AwaitExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asAwaitExpression())
+ return matcher->match(this, other);
+ return false;
+}
+
bool NoExceptOperatorExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (NoExceptOperatorExpressionAST *_other = pattern->asNoExceptOperatorExpression())
diff --git a/src/libs/3rdparty/cplusplus/ASTMatcher.cpp b/src/libs/3rdparty/cplusplus/ASTMatcher.cpp
index fcfe86ab57..de6f6fc87c 100644
--- a/src/libs/3rdparty/cplusplus/ASTMatcher.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTMatcher.cpp
@@ -216,6 +216,25 @@ bool ASTMatcher::match(DecltypeSpecifierAST *node, DecltypeSpecifierAST *pattern
return true;
}
+bool ASTMatcher::match(TypeConstraintAST *node, TypeConstraintAST *pattern)
+{
+ if (!pattern->nestedName)
+ pattern->nestedName = node->nestedName;
+ else if (!AST::match(node->nestedName, pattern->nestedName, this))
+ return false;
+ if (!pattern->conceptName)
+ pattern->conceptName = node->conceptName;
+ else if (!AST::match(node->conceptName, pattern->conceptName, this))
+ return false;
+ pattern->lessToken = node->lessToken;
+ if (!pattern->templateArgs)
+ pattern->templateArgs = node->templateArgs;
+ else if (!AST::match(node->templateArgs, pattern->templateArgs, this))
+ return false;
+ pattern->greaterToken = node->greaterToken;
+ return true;
+}
+
bool ASTMatcher::match(DeclaratorAST *node, DeclaratorAST *pattern)
{
(void) node;
@@ -1749,6 +1768,19 @@ bool ASTMatcher::match(ParameterDeclarationClauseAST *node, ParameterDeclaration
return true;
}
+bool ASTMatcher::match(PlaceholderTypeSpecifierAST *node, PlaceholderTypeSpecifierAST *pattern)
+{
+ if (!pattern->typeConstraint)
+ pattern->typeConstraint = node->typeConstraint;
+ else if (!AST::match(node->typeConstraint, pattern->typeConstraint, this))
+ return false;
+ pattern->declTypetoken = node->declTypetoken;
+ pattern->lparenToken = node->lparenToken;
+ pattern->autoToken = node->autoToken;
+ pattern->rparenToken = node->rparenToken;
+ return true;
+}
+
bool ASTMatcher::match(CallAST *node, CallAST *pattern)
{
(void) node;
@@ -2173,6 +2205,11 @@ bool ASTMatcher::match(TemplateDeclarationAST *node, TemplateDeclarationAST *pat
pattern->greater_token = node->greater_token;
+ if (! pattern->requiresClause)
+ pattern->requiresClause = node->requiresClause;
+ else if (! AST::match(node->requiresClause, pattern->requiresClause, this))
+ return false;
+
if (! pattern->declaration)
pattern->declaration = node->declaration;
else if (! AST::match(node->declaration, pattern->declaration, this))
@@ -2181,6 +2218,50 @@ bool ASTMatcher::match(TemplateDeclarationAST *node, TemplateDeclarationAST *pat
return true;
}
+bool ASTMatcher::match(ConceptDeclarationAST *node, ConceptDeclarationAST *pattern)
+{
+ pattern->concept_token = node->concept_token;
+ pattern->equals_token = node->equals_token;
+ pattern->semicolon_token = node->semicolon_token;
+
+ if (!pattern->attributes)
+ pattern->attributes = node->attributes;
+ else if (!AST::match(node->attributes, pattern->attributes, this))
+ return false;
+
+ if (!pattern->constraint)
+ pattern->constraint = node->constraint;
+ else if (! AST::match(node->constraint, pattern->constraint, this))
+ return false;
+
+ return true;
+}
+
+bool ASTMatcher::match(RequiresExpressionAST *node, RequiresExpressionAST *pattern)
+{
+ pattern->requires_token = node->requires_token;
+ pattern->lparen_token = node->lparen_token;
+ pattern->lbrace_token = node->lbrace_token;
+ pattern->rbrace_token = node->rbrace_token;
+
+ if (!pattern->parameters)
+ pattern->parameters = node->parameters;
+ else if (!AST::match(node->parameters, pattern->parameters, this))
+ return false;
+
+ return true;
+}
+
+bool ASTMatcher::match(RequiresClauseAST *node, RequiresClauseAST *pattern)
+{
+ pattern->requires_token = node->requires_token;
+ if (!pattern->constraint)
+ pattern->constraint = node->constraint;
+ else if (!AST::match(node->constraint, pattern->constraint, this))
+ return false;
+ return true;
+}
+
bool ASTMatcher::match(ThrowExpressionAST *node, ThrowExpressionAST *pattern)
{
(void) node;
@@ -2196,6 +2277,26 @@ bool ASTMatcher::match(ThrowExpressionAST *node, ThrowExpressionAST *pattern)
return true;
}
+bool ASTMatcher::match(YieldExpressionAST *node, YieldExpressionAST *pattern)
+{
+ pattern->yield_token = node->yield_token;
+ if (!pattern->expression)
+ pattern->expression = node->expression;
+ else if (!AST::match(node->expression, pattern->expression, this))
+ return false;
+ return true;
+}
+
+bool ASTMatcher::match(AwaitExpressionAST *node, AwaitExpressionAST *pattern)
+{
+ pattern->await_token = node->await_token;
+ if (!pattern->castExpression)
+ pattern->castExpression = node->castExpression;
+ else if (!AST::match(node->castExpression, pattern->castExpression, this))
+ return false;
+ return true;
+}
+
bool ASTMatcher::match(NoExceptOperatorExpressionAST *node, NoExceptOperatorExpressionAST *pattern)
{
(void) node;
@@ -2317,6 +2418,11 @@ bool ASTMatcher::match(TemplateTypeParameterAST *node, TemplateTypeParameterAST
pattern->template_token = node->template_token;
+ if (!pattern->typeConstraint)
+ pattern->typeConstraint = node->typeConstraint;
+ else if (!AST::match(node->typeConstraint, pattern->typeConstraint, this))
+ return false;
+
pattern->less_token = node->less_token;
if (! pattern->template_parameter_list)
@@ -2950,6 +3056,21 @@ bool ASTMatcher::match(LambdaExpressionAST *node, LambdaExpressionAST *pattern)
else if (! AST::match(node->lambda_introducer, pattern->lambda_introducer, this))
return false;
+ if (! pattern->templateParameters)
+ pattern->templateParameters = node->templateParameters;
+ else if (! AST::match(node->templateParameters, pattern->templateParameters, this))
+ return false;
+
+ if (! pattern->requiresClause)
+ pattern->requiresClause = node->requiresClause;
+ else if (! AST::match(node->requiresClause, pattern->requiresClause, this))
+ return false;
+
+ if (! pattern->attributes)
+ pattern->attributes = node->attributes;
+ else if (! AST::match(node->attributes, pattern->attributes, this))
+ return false;
+
if (! pattern->lambda_declarator)
pattern->lambda_declarator = node->lambda_declarator;
else if (! AST::match(node->lambda_declarator, pattern->lambda_declarator, this))
@@ -3041,6 +3162,11 @@ bool ASTMatcher::match(LambdaDeclaratorAST *node, LambdaDeclaratorAST *pattern)
else if (! AST::match(node->trailing_return_type, pattern->trailing_return_type, this))
return false;
+ if (! pattern->requiresClause)
+ pattern->requiresClause = node->requiresClause;
+ else if (! AST::match(node->requiresClause, pattern->requiresClause, this))
+ return false;
+
return true;
}
diff --git a/src/libs/3rdparty/cplusplus/ASTMatcher.h b/src/libs/3rdparty/cplusplus/ASTMatcher.h
index c243e18b7b..fb6109a07d 100644
--- a/src/libs/3rdparty/cplusplus/ASTMatcher.h
+++ b/src/libs/3rdparty/cplusplus/ASTMatcher.h
@@ -38,6 +38,7 @@ public:
virtual bool match(ArrayAccessAST *node, ArrayAccessAST *pattern);
virtual bool match(ArrayDeclaratorAST *node, ArrayDeclaratorAST *pattern);
virtual bool match(ArrayInitializerAST *node, ArrayInitializerAST *pattern);
+ virtual bool match(AwaitExpressionAST *node, AwaitExpressionAST *pattern);
virtual bool match(AsmDefinitionAST *node, AsmDefinitionAST *pattern);
virtual bool match(BaseSpecifierAST *node, BaseSpecifierAST *pattern);
virtual bool match(BinaryExpressionAST *node, BinaryExpressionAST *pattern);
@@ -54,6 +55,7 @@ public:
virtual bool match(CompoundExpressionAST *node, CompoundExpressionAST *pattern);
virtual bool match(CompoundLiteralAST *node, CompoundLiteralAST *pattern);
virtual bool match(CompoundStatementAST *node, CompoundStatementAST *pattern);
+ virtual bool match(ConceptDeclarationAST *node, ConceptDeclarationAST *pattern);
virtual bool match(ConditionAST *node, ConditionAST *pattern);
virtual bool match(ConditionalExpressionAST *node, ConditionalExpressionAST *pattern);
virtual bool match(ContinueStatementAST *node, ContinueStatementAST *pattern);
@@ -139,6 +141,7 @@ public:
virtual bool match(OperatorFunctionIdAST *node, OperatorFunctionIdAST *pattern);
virtual bool match(ParameterDeclarationAST *node, ParameterDeclarationAST *pattern);
virtual bool match(ParameterDeclarationClauseAST *node, ParameterDeclarationClauseAST *pattern);
+ virtual bool match(PlaceholderTypeSpecifierAST *node, PlaceholderTypeSpecifierAST *pattern);
virtual bool match(PointerAST *node, PointerAST *pattern);
virtual bool match(PointerLiteralAST *node, PointerLiteralAST *pattern);
virtual bool match(PointerToMemberAST *node, PointerToMemberAST *pattern);
@@ -156,6 +159,8 @@ public:
virtual bool match(QualifiedNameAST *node, QualifiedNameAST *pattern);
virtual bool match(RangeBasedForStatementAST *node, RangeBasedForStatementAST *pattern);
virtual bool match(ReferenceAST *node, ReferenceAST *pattern);
+ virtual bool match(RequiresClauseAST *node, RequiresClauseAST *pattern);
+ virtual bool match(RequiresExpressionAST *node, RequiresExpressionAST *pattern);
virtual bool match(ReturnStatementAST *node, ReturnStatementAST *pattern);
virtual bool match(SimpleDeclarationAST *node, SimpleDeclarationAST *pattern);
virtual bool match(SimpleNameAST *node, SimpleNameAST *pattern);
@@ -173,6 +178,7 @@ public:
virtual bool match(TrailingReturnTypeAST *node, TrailingReturnTypeAST *pattern);
virtual bool match(TranslationUnitAST *node, TranslationUnitAST *pattern);
virtual bool match(TryBlockStatementAST *node, TryBlockStatementAST *pattern);
+ virtual bool match(TypeConstraintAST *node, TypeConstraintAST *pattern);
virtual bool match(TypeConstructorCallAST *node, TypeConstructorCallAST *pattern);
virtual bool match(TypeIdAST *node, TypeIdAST *pattern);
virtual bool match(TypeidExpressionAST *node, TypeidExpressionAST *pattern);
@@ -183,6 +189,7 @@ public:
virtual bool match(UsingAST *node, UsingAST *pattern);
virtual bool match(UsingDirectiveAST *node, UsingDirectiveAST *pattern);
virtual bool match(WhileStatementAST *node, WhileStatementAST *pattern);
+ virtual bool match(YieldExpressionAST *node, YieldExpressionAST *pattern);
};
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/ASTVisit.cpp b/src/libs/3rdparty/cplusplus/ASTVisit.cpp
index 2248f51a50..17941d8122 100644
--- a/src/libs/3rdparty/cplusplus/ASTVisit.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTVisit.cpp
@@ -110,6 +110,23 @@ void DecltypeSpecifierAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
+void TypeConstraintAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(nestedName, visitor);
+ accept(conceptName, visitor);
+ accept(templateArgs, visitor);
+ }
+ visitor->endVisit(this);
+}
+
+void PlaceholderTypeSpecifierAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(typeConstraint, visitor);
+ visitor->endVisit(this);
+}
+
void DeclaratorAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -941,11 +958,36 @@ void TemplateDeclarationAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
accept(template_parameter_list, visitor);
+ accept(requiresClause, visitor);
accept(declaration, visitor);
}
visitor->endVisit(this);
}
+void ConceptDeclarationAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(name, visitor);
+ accept(attributes, visitor);
+ accept(constraint, visitor);
+ }
+ visitor->endVisit(this);
+}
+
+void RequiresExpressionAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(parameters, visitor);
+ visitor->endVisit(this);
+}
+
+void RequiresClauseAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(constraint, visitor);
+ visitor->endVisit(this);
+}
+
void ThrowExpressionAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -954,6 +996,20 @@ void ThrowExpressionAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
+void YieldExpressionAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(expression, visitor);
+ visitor->endVisit(this);
+}
+
+void AwaitExpressionAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(castExpression, visitor);
+ visitor->endVisit(this);
+}
+
void NoExceptOperatorExpressionAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -1009,6 +1065,7 @@ void TypenameTypeParameterAST::accept0(ASTVisitor *visitor)
void TemplateTypeParameterAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
+ accept(typeConstraint, visitor);
accept(template_parameter_list, visitor);
accept(name, visitor);
accept(type_id, visitor);
@@ -1260,6 +1317,9 @@ void LambdaExpressionAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
accept(lambda_introducer, visitor);
+ accept(templateParameters, visitor);
+ accept(requiresClause, visitor);
+ accept(attributes, visitor);
accept(lambda_declarator, visitor);
accept(statement, visitor);
}
@@ -1297,6 +1357,7 @@ void LambdaDeclaratorAST::accept0(ASTVisitor *visitor)
accept(attributes, visitor);
accept(exception_specification, visitor);
accept(trailing_return_type, visitor);
+ accept(requiresClause, visitor);
}
visitor->endVisit(this);
}
diff --git a/src/libs/3rdparty/cplusplus/ASTVisitor.h b/src/libs/3rdparty/cplusplus/ASTVisitor.h
index 691b57e9ac..9d9fec75b1 100644
--- a/src/libs/3rdparty/cplusplus/ASTVisitor.h
+++ b/src/libs/3rdparty/cplusplus/ASTVisitor.h
@@ -81,6 +81,7 @@ public:
virtual bool visit(ArrayDeclaratorAST *) { return true; }
virtual bool visit(ArrayInitializerAST *) { return true; }
virtual bool visit(AsmDefinitionAST *) { return true; }
+ virtual bool visit(AwaitExpressionAST *) { return true; }
virtual bool visit(BaseSpecifierAST *) { return true; }
virtual bool visit(BinaryExpressionAST *) { return true; }
virtual bool visit(BoolLiteralAST *) { return true; }
@@ -96,6 +97,7 @@ public:
virtual bool visit(CompoundExpressionAST *) { return true; }
virtual bool visit(CompoundLiteralAST *) { return true; }
virtual bool visit(CompoundStatementAST *) { return true; }
+ virtual bool visit(ConceptDeclarationAST *) { return true; }
virtual bool visit(ConditionAST *) { return true; }
virtual bool visit(ConditionalExpressionAST *) { return true; }
virtual bool visit(ContinueStatementAST *) { return true; }
@@ -181,6 +183,7 @@ public:
virtual bool visit(OperatorFunctionIdAST *) { return true; }
virtual bool visit(ParameterDeclarationAST *) { return true; }
virtual bool visit(ParameterDeclarationClauseAST *) { return true; }
+ virtual bool visit(PlaceholderTypeSpecifierAST *) { return true; }
virtual bool visit(PointerAST *) { return true; }
virtual bool visit(PointerLiteralAST *) { return true; }
virtual bool visit(PointerToMemberAST *) { return true; }
@@ -198,6 +201,8 @@ public:
virtual bool visit(QualifiedNameAST *) { return true; }
virtual bool visit(RangeBasedForStatementAST *) { return true; }
virtual bool visit(ReferenceAST *) { return true; }
+ virtual bool visit(RequiresExpressionAST *) { return true; }
+ virtual bool visit(RequiresClauseAST *) { return true; }
virtual bool visit(ReturnStatementAST *) { return true; }
virtual bool visit(SimpleDeclarationAST *) { return true; }
virtual bool visit(SimpleNameAST *) { return true; }
@@ -215,6 +220,7 @@ public:
virtual bool visit(TrailingReturnTypeAST *) { return true; }
virtual bool visit(TranslationUnitAST *) { return true; }
virtual bool visit(TryBlockStatementAST *) { return true; }
+ virtual bool visit(TypeConstraintAST *) { return true; }
virtual bool visit(TypeConstructorCallAST *) { return true; }
virtual bool visit(TypeIdAST *) { return true; }
virtual bool visit(TypeidExpressionAST *) { return true; }
@@ -225,6 +231,7 @@ public:
virtual bool visit(UsingAST *) { return true; }
virtual bool visit(UsingDirectiveAST *) { return true; }
virtual bool visit(WhileStatementAST *) { return true; }
+ virtual bool visit(YieldExpressionAST *) { return true; }
virtual void endVisit(AccessDeclarationAST *) {}
virtual void endVisit(AliasDeclarationAST *) {}
@@ -235,6 +242,7 @@ public:
virtual void endVisit(ArrayDeclaratorAST *) {}
virtual void endVisit(ArrayInitializerAST *) {}
virtual void endVisit(AsmDefinitionAST *) {}
+ virtual void endVisit(AwaitExpressionAST *) {}
virtual void endVisit(BaseSpecifierAST *) {}
virtual void endVisit(BinaryExpressionAST *) {}
virtual void endVisit(BoolLiteralAST *) {}
@@ -250,6 +258,7 @@ public:
virtual void endVisit(CompoundExpressionAST *) {}
virtual void endVisit(CompoundLiteralAST *) {}
virtual void endVisit(CompoundStatementAST *) {}
+ virtual void endVisit(ConceptDeclarationAST *) {}
virtual void endVisit(ConditionAST *) {}
virtual void endVisit(ConditionalExpressionAST *) {}
virtual void endVisit(ContinueStatementAST *) {}
@@ -335,6 +344,7 @@ public:
virtual void endVisit(OperatorFunctionIdAST *) {}
virtual void endVisit(ParameterDeclarationAST *) {}
virtual void endVisit(ParameterDeclarationClauseAST *) {}
+ virtual void endVisit(PlaceholderTypeSpecifierAST *) {}
virtual void endVisit(PointerAST *) {}
virtual void endVisit(PointerLiteralAST *) {}
virtual void endVisit(PointerToMemberAST *) {}
@@ -352,6 +362,8 @@ public:
virtual void endVisit(QualifiedNameAST *) {}
virtual void endVisit(RangeBasedForStatementAST *) {}
virtual void endVisit(ReferenceAST *) {}
+ virtual void endVisit(RequiresExpressionAST *) {}
+ virtual void endVisit(RequiresClauseAST *) {}
virtual void endVisit(ReturnStatementAST *) {}
virtual void endVisit(SimpleDeclarationAST *) {}
virtual void endVisit(SimpleNameAST *) {}
@@ -369,6 +381,7 @@ public:
virtual void endVisit(TrailingReturnTypeAST *) {}
virtual void endVisit(TranslationUnitAST *) {}
virtual void endVisit(TryBlockStatementAST *) {}
+ virtual void endVisit(TypeConstraintAST *) {}
virtual void endVisit(TypeConstructorCallAST *) {}
virtual void endVisit(TypeIdAST *) {}
virtual void endVisit(TypeidExpressionAST *) {}
@@ -379,6 +392,7 @@ public:
virtual void endVisit(UsingAST *) {}
virtual void endVisit(UsingDirectiveAST *) {}
virtual void endVisit(WhileStatementAST *) {}
+ virtual void endVisit(YieldExpressionAST *) {}
private:
TranslationUnit *_translationUnit;
diff --git a/src/libs/3rdparty/cplusplus/ASTfwd.h b/src/libs/3rdparty/cplusplus/ASTfwd.h
index 4b31aaf590..102fda75fb 100644
--- a/src/libs/3rdparty/cplusplus/ASTfwd.h
+++ b/src/libs/3rdparty/cplusplus/ASTfwd.h
@@ -40,6 +40,7 @@ class ArrayDeclaratorAST;
class ArrayInitializerAST;
class AsmDefinitionAST;
class AttributeSpecifierAST;
+class AwaitExpressionAST;
class BaseSpecifierAST;
class BinaryExpressionAST;
class BoolLiteralAST;
@@ -146,6 +147,7 @@ class OperatorAST;
class OperatorFunctionIdAST;
class ParameterDeclarationAST;
class ParameterDeclarationClauseAST;
+class PlaceholderTypeSpecifierAST;
class PointerAST;
class PointerLiteralAST;
class PointerToMemberAST;
@@ -166,6 +168,8 @@ class QtPropertyDeclarationItemAST;
class QualifiedNameAST;
class RangeBasedForStatementAST;
class ReferenceAST;
+class RequiresClauseAST;
+class RequiresExpressionAST;
class ReturnStatementAST;
class SimpleDeclarationAST;
class SimpleNameAST;
@@ -178,6 +182,7 @@ class StdAttributeSpecifierAST;
class StringLiteralAST;
class SwitchStatementAST;
class TemplateDeclarationAST;
+class ConceptDeclarationAST;
class TemplateIdAST;
class TemplateTypeParameterAST;
class ThisExpressionAST;
@@ -185,6 +190,7 @@ class ThrowExpressionAST;
class TrailingReturnTypeAST;
class TranslationUnitAST;
class TryBlockStatementAST;
+class TypeConstraintAST;
class TypeConstructorCallAST;
class TypeIdAST;
class TypeidExpressionAST;
@@ -195,6 +201,7 @@ class UnaryExpressionAST;
class UsingAST;
class UsingDirectiveAST;
class WhileStatementAST;
+class YieldExpressionAST;
typedef List<ExpressionAST *> ExpressionListAST;
typedef List<DeclarationAST *> DeclarationListAST;
diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp
index 7f10c79428..202c36a51f 100644
--- a/src/libs/3rdparty/cplusplus/Bind.cpp
+++ b/src/libs/3rdparty/cplusplus/Bind.cpp
@@ -933,6 +933,11 @@ bool Bind::visit(ParameterDeclarationClauseAST *ast)
return false;
}
+bool Bind::visit(RequiresExpressionAST *)
+{
+ return false;
+}
+
void Bind::parameterDeclarationClause(ParameterDeclarationClauseAST *ast, int lparen_token, Function *fun)
{
if (! ast)
@@ -2527,6 +2532,11 @@ bool Bind::visit(TemplateTypeParameterAST *ast)
return false;
}
+bool Bind::visit(TypeConstraintAST *)
+{
+ return false;
+}
+
bool Bind::visit(UsingAST *ast)
{
int sourceLocation = location(ast->name, ast->firstToken());
diff --git a/src/libs/3rdparty/cplusplus/Bind.h b/src/libs/3rdparty/cplusplus/Bind.h
index 3947e57026..867ed7baaa 100644
--- a/src/libs/3rdparty/cplusplus/Bind.h
+++ b/src/libs/3rdparty/cplusplus/Bind.h
@@ -128,6 +128,7 @@ protected:
bool visit(NewTypeIdAST *ast) override;
bool visit(OperatorAST *ast) override;
bool visit(ParameterDeclarationClauseAST *ast) override;
+ bool visit(RequiresExpressionAST *ast) override;
bool visit(TranslationUnitAST *ast) override;
bool visit(ObjCProtocolRefsAST *ast) override;
bool visit(ObjCMessageArgumentAST *ast) override;
@@ -229,6 +230,7 @@ protected:
bool visit(TemplateDeclarationAST *ast) override;
bool visit(TypenameTypeParameterAST *ast) override;
bool visit(TemplateTypeParameterAST *ast) override;
+ bool visit(TypeConstraintAST *ast) override;
bool visit(UsingAST *ast) override;
bool visit(UsingDirectiveAST *ast) override;
bool visit(ObjCClassForwardDeclarationAST *ast) override;
diff --git a/src/libs/3rdparty/cplusplus/CMakeLists.txt b/src/libs/3rdparty/cplusplus/CMakeLists.txt
index 2758ffe6ee..d9f130b470 100644
--- a/src/libs/3rdparty/cplusplus/CMakeLists.txt
+++ b/src/libs/3rdparty/cplusplus/CMakeLists.txt
@@ -41,7 +41,6 @@ add_qtc_library(3rd_cplusplus OBJECT
TypeVisitor.cpp TypeVisitor.h
cppassert.h
SKIP_PCH
- PROPERTIES POSITION_INDEPENDENT_CODE ON
)
set(export_symbol_declaration DEFINES CPLUSPLUS_BUILD_LIB)
diff --git a/src/libs/3rdparty/cplusplus/Keywords.cpp b/src/libs/3rdparty/cplusplus/Keywords.cpp
index 1637333b42..a60ec24aec 100644
--- a/src/libs/3rdparty/cplusplus/Keywords.cpp
+++ b/src/libs/3rdparty/cplusplus/Keywords.cpp
@@ -603,6 +603,34 @@ static inline int classify7(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.cxx20Enabled && s[0] == 'c') {
+ if (s[1] == 'h') {
+ if (s[2] == 'a') {
+ if (s[3] == 'r') {
+ if (s[4] == '8') {
+ if (s[5] == '_') {
+ if (s[6] == 't') {
+ return T_CHAR8_T;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[1] == 'o') {
+ if (s[2] == 'n') {
+ if (s[3] == 'c') {
+ if (s[4] == 'e') {
+ if (s[5] == 'p') {
+ if (s[6] == 't') {
+ return T_CONCEPT;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
else if (s[0] == 'd') {
if (s[1] == 'e') {
if (s[2] == 'f') {
@@ -847,7 +875,31 @@ static inline int classify8(const char *s, LanguageFeatures features)
}
}
else if (s[1] == 'o') {
- if (s[2] == 'n') {
+ if (features.cxx20Enabled && s[2] == '_') {
+ if (s[3] == 'a') {
+ if (s[4] == 'w') {
+ if (s[5] == 'a') {
+ if (s[6] == 'i') {
+ if (s[7] == 't') {
+ return T_CO_AWAIT;
+ }
+ }
+ }
+ }
+ }
+ else if (s[3] == 'y') {
+ if (s[4] == 'i') {
+ if (s[5] == 'e') {
+ if (s[6] == 'l') {
+ if (s[7] == 'd') {
+ return T_CO_YIELD;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[2] == 'n') {
if (s[3] == 't') {
if (s[4] == 'i') {
if (s[5] == 'n') {
@@ -945,6 +997,19 @@ static inline int classify8(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.cxx20Enabled && s[2] == 'q') {
+ if (s[3] == 'u') {
+ if (s[4] == 'i') {
+ if (s[5] == 'r') {
+ if (s[6] == 'e') {
+ if (s[7] == 's') {
+ return T_REQUIRES;
+ }
+ }
+ }
+ }
+ }
+ }
}
}
else if (features.cxxEnabled && s[0] == 't') {
@@ -1097,13 +1162,35 @@ static inline int classify9(const char *s, LanguageFeatures features)
}
}
}
- else if (features.cxx11Enabled && s[0] == 'c') {
+ else if (s[0] == 'c') {
if (s[1] == 'o') {
- if (s[2] == 'n') {
+ if (features.cxx20Enabled && s[2] == '_') {
+ if (s[3] == 'r') {
+ if (s[4] == 'e') {
+ if (s[5] == 't') {
+ if (s[6] == 'u') {
+ if (s[7] == 'r') {
+ if (s[8] == 'n') {
+ return T_CO_RETURN;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[2] == 'n') {
if (s[3] == 's') {
if (s[4] == 't') {
if (s[5] == 'e') {
- if (s[6] == 'x') {
+ if (features.cxx20Enabled && s[6] == 'v') {
+ if (s[7] == 'a') {
+ if (s[8] == 'l') {
+ return T_CONSTEVAL;
+ }
+ }
+ }
+ else if (features.cxx11Enabled && s[6] == 'x') {
if (s[7] == 'p') {
if (s[8] == 'r') {
return T_CONSTEXPR;
@@ -1111,6 +1198,15 @@ static inline int classify9(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.cxx20Enabled && s[5] == 'i') {
+ if (s[6] == 'n') {
+ if (s[7] == 'i') {
+ if (s[8] == 't') {
+ return T_CONSTINIT;
+ }
+ }
+ }
+ }
}
}
}
diff --git a/src/libs/3rdparty/cplusplus/Keywords.kwgen b/src/libs/3rdparty/cplusplus/Keywords.kwgen
index 36300a8776..70deeb648d 100644
--- a/src/libs/3rdparty/cplusplus/Keywords.kwgen
+++ b/src/libs/3rdparty/cplusplus/Keywords.kwgen
@@ -128,6 +128,16 @@ nullptr
static_assert
thread_local
+%pre-check=features.cxx20Enabled
+char8_t
+concept
+consteval
+constinit
+co_await
+co_return
+co_yield
+requires
+
%pre-check=features.qtKeywordsEnabled
emit
foreach
diff --git a/src/libs/3rdparty/cplusplus/Lexer.cpp b/src/libs/3rdparty/cplusplus/Lexer.cpp
index e2f1b4e0e6..fc1b72cb7d 100644
--- a/src/libs/3rdparty/cplusplus/Lexer.cpp
+++ b/src/libs/3rdparty/cplusplus/Lexer.cpp
@@ -217,14 +217,17 @@ void Lexer::scan_helper(Token *tok)
tok->f.kind = s._tokenKind;
const bool found = _expectedRawStringSuffix.isEmpty()
? scanUntilRawStringLiteralEndSimple() : scanUntilRawStringLiteralEndPrecise();
- if (found)
+ if (found) {
+ scanOptionalUserDefinedLiteral(tok);
_state = 0;
+ }
return;
} else { // non-raw strings
tok->f.joined = true;
tok->f.kind = s._tokenKind;
_state = 0;
scanUntilQuote(tok, '"');
+ scanOptionalUserDefinedLiteral(tok);
return;
}
@@ -829,6 +832,8 @@ void Lexer::scanRawStringLiteral(Token *tok, unsigned char hint)
_expectedRawStringSuffix.prepend(')');
_expectedRawStringSuffix.append('"');
}
+ if (closed)
+ scanOptionalUserDefinedLiteral(tok);
}
bool Lexer::scanUntilRawStringLiteralEndPrecise()
diff --git a/src/libs/3rdparty/cplusplus/Parser.cpp b/src/libs/3rdparty/cplusplus/Parser.cpp
index cb7bfa3a1b..f8c737c097 100644
--- a/src/libs/3rdparty/cplusplus/Parser.cpp
+++ b/src/libs/3rdparty/cplusplus/Parser.cpp
@@ -413,6 +413,7 @@ bool Parser::skipUntilStatement()
case T_BREAK:
case T_CONTINUE:
case T_RETURN:
+ case T_CO_RETURN:
case T_GOTO:
case T_TRY:
case T_CATCH:
@@ -1247,6 +1248,8 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
ast->less_token = consumeToken();
if (maybeSplitGreaterGreaterToken() || LA() == T_GREATER || parseTemplateParameterList(ast->template_parameter_list))
match(T_GREATER, &ast->greater_token);
+ if (!parseRequiresClauseOpt(ast->requiresClause))
+ return false;
}
while (LA()) {
@@ -1255,6 +1258,8 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
ast->declaration = nullptr;
if (parseDeclaration(ast->declaration))
break;
+ if (parseConceptDeclaration(ast->declaration))
+ break;
error(start_declaration, "expected a declaration");
rewind(start_declaration + 1);
@@ -1265,6 +1270,205 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
return true;
}
+bool Parser::parseConceptDeclaration(DeclarationAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return false;
+ if (LA() != T_CONCEPT)
+ return false;
+
+ const auto ast = new (_pool) ConceptDeclarationAST;
+ ast->concept_token = consumeToken();
+ if (!parseName(ast->name))
+ return false;
+ parseAttributeSpecifier(ast->attributes);
+ if (LA() != T_EQUAL)
+ return false;
+ ast->equals_token = consumeToken();
+ if (!parseLogicalOrExpression(ast->constraint))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ ast->semicolon_token = consumeToken();
+ node = ast;
+ return true;
+}
+
+bool Parser::parsePlaceholderTypeSpecifier(PlaceholderTypeSpecifierAST *&node)
+{
+ if ((lookAtBuiltinTypeSpecifier() || _translationUnit->tokenAt(_tokenIndex).isKeyword())
+ && (LA() != T_AUTO && LA() != T_DECLTYPE)) {
+ return false;
+ }
+
+ TypeConstraintAST *typeConstraint = nullptr;
+ const int savedCursor = cursor();
+ parseTypeConstraint(typeConstraint);
+ if (LA() != T_AUTO && (LA() != T_DECLTYPE || LA(1) != T_LPAREN || LA(2) != T_AUTO)) {
+ rewind(savedCursor);
+ return false;
+ }
+ const auto spec = new (_pool) PlaceholderTypeSpecifierAST;
+ spec->typeConstraint = typeConstraint;
+ if (LA() == T_DECLTYPE) {
+ spec->declTypetoken = consumeToken();
+ if (LA() != T_LPAREN)
+ return false;
+ spec->lparenToken = consumeToken();
+ if (LA() != T_AUTO)
+ return false;
+ spec->autoToken = consumeToken();
+ if (LA() != T_RPAREN)
+ return false;
+ spec->rparenToken = consumeToken();
+ } else {
+ spec->autoToken = consumeToken();
+ }
+ node = spec;
+ return true;
+}
+
+bool Parser::parseTypeConstraint(TypeConstraintAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return false;
+ NestedNameSpecifierListAST *nestedName = nullptr;
+ parseNestedNameSpecifierOpt(nestedName, true);
+ NameAST *conceptName = nullptr;
+ if (!parseUnqualifiedName(conceptName, false))
+ return false;
+ const auto typeConstraint = new (_pool) TypeConstraintAST;
+ typeConstraint->nestedName = nestedName;
+ typeConstraint->conceptName = conceptName;
+ if (LA() != T_LESS) {
+ node = typeConstraint;
+ return true;
+ }
+ typeConstraint->lessToken = consumeToken();
+ if (LA() != T_GREATER) {
+ if (!parseTemplateArgumentList(typeConstraint->templateArgs))
+ return false;
+ }
+ if (LA() != T_GREATER)
+ return false;
+ typeConstraint->greaterToken = consumeToken();
+ node = typeConstraint;
+ return true;
+}
+
+bool Parser::parseRequirement()
+{
+ if (LA() == T_TYPENAME) { // type-requirement
+ consumeToken();
+ NameAST *name = nullptr;
+ if (!parseName(name, true))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+ }
+ if (LA() == T_LBRACE) { // compound-requirement
+ consumeToken();
+ ExpressionAST *expr = nullptr;
+ if (!parseExpression(expr))
+ return false;
+ if (LA() != T_RBRACE)
+ return false;
+ consumeToken();
+ if (LA() == T_NOEXCEPT)
+ consumeToken();
+ if (LA() == T_SEMICOLON) {
+ consumeToken();
+ return true;
+ }
+ TypeConstraintAST *typeConstraint = nullptr;
+ if (!parseTypeConstraint(typeConstraint))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+ }
+ if (LA() == T_REQUIRES) { // nested-requirement
+ consumeToken();
+ ExpressionAST *constraintExpr = nullptr;
+ if (!parseLogicalOrExpression(constraintExpr))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+ }
+ ExpressionAST *simpleExpr;
+ if (!parseExpression(simpleExpr)) // simple-requirement
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+}
+
+bool Parser::parseRequiresClauseOpt(RequiresClauseAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return true;
+ if (LA() != T_REQUIRES)
+ return true;
+ const auto ast = new (_pool) RequiresClauseAST;
+ ast->requires_token = consumeToken();
+ if (!parsePrimaryExpression(ast->constraint))
+ return false;
+ while (true) {
+ if (LA() != T_PIPE_PIPE && LA() != T_AMPER_AMPER)
+ break;
+ ExpressionAST *next = nullptr;
+ if (!parsePrimaryExpression(next))
+ return false;
+
+ // This won't yield the right precedence, but I don't care.
+ BinaryExpressionAST *expr = new (_pool) BinaryExpressionAST;
+ expr->left_expression = ast->constraint;
+ expr->binary_op_token = consumeToken();
+ expr->right_expression = next;
+ ast->constraint = expr;
+ }
+ node = ast;
+ return true;
+}
+
+bool Parser::parseRequiresExpression(ExpressionAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return false;
+ if (LA() != T_REQUIRES)
+ return false;
+
+ const auto ast = new (_pool) RequiresExpressionAST;
+ ast->requires_token = consumeToken();
+ if (LA() == T_LPAREN) {
+ ast->lparen_token = consumeToken();
+ if (!parseParameterDeclarationClause(ast->parameters))
+ return false;
+ if (LA() != T_RPAREN)
+ return false;
+ ast->rparen_token = consumeToken();
+ }
+ if (LA() != T_LBRACE)
+ return false;
+ ast->lbrace_token = consumeToken();
+ if (!parseRequirement())
+ return false;
+ while (LA() != T_RBRACE) {
+ if (!parseRequirement())
+ return false;
+ }
+ ast->rbrace_token = consumeToken();
+
+ node = ast;
+ return true;
+}
+
bool Parser::parseOperator(OperatorAST *&node) // ### FIXME
{
DEBUG_THIS_RULE();
@@ -1500,6 +1704,14 @@ bool Parser::parseDeclSpecifierSeq(SpecifierListAST *&decl_specifier_seq,
NameAST *named_type_specifier = nullptr;
SpecifierListAST **decl_specifier_seq_ptr = &decl_specifier_seq;
for (;;) {
+ PlaceholderTypeSpecifierAST *placeholderSpec = nullptr;
+ // A simple auto is also technically a placeholder-type-specifier, but for historical
+ // reasons, it is handled further below.
+ if (LA() != T_AUTO && parsePlaceholderTypeSpecifier(placeholderSpec)) {
+ *decl_specifier_seq_ptr = new (_pool) SpecifierListAST(placeholderSpec);
+ decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
+ continue;
+ }
if (! noStorageSpecifiers && ! onlySimpleTypeSpecifiers && lookAtStorageClassSpecifier()) {
// storage-class-specifier
SimpleSpecifierAST *spec = new (_pool) SimpleSpecifierAST;
@@ -1550,8 +1762,9 @@ bool Parser::parseDeclSpecifierSeq(SpecifierListAST *&decl_specifier_seq,
}
decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
has_type_specifier = true;
- } else
+ } else {
break;
+ }
}
return decl_specifier_seq != nullptr;
@@ -1694,6 +1907,8 @@ bool Parser::hasAuto(SpecifierListAST *decl_specifier_list) const
if (_translationUnit->tokenKind(simpleSpec->specifier_token) == T_AUTO)
return true;
}
+ if (spec->asPlaceholderTypeSpecifier())
+ return true;
}
return false;
}
@@ -2012,8 +2227,8 @@ bool Parser::parseTypenameTypeParameter(DeclarationAST *&node)
bool Parser::parseTemplateTypeParameter(DeclarationAST *&node)
{
DEBUG_THIS_RULE();
+ TemplateTypeParameterAST *ast = new (_pool) TemplateTypeParameterAST;
if (LA() == T_TEMPLATE) {
- TemplateTypeParameterAST *ast = new (_pool) TemplateTypeParameterAST;
ast->template_token = consumeToken();
if (LA() == T_LESS)
ast->less_token = consumeToken();
@@ -2022,20 +2237,21 @@ bool Parser::parseTemplateTypeParameter(DeclarationAST *&node)
ast->greater_token = consumeToken();
if (LA() == T_CLASS)
ast->class_token = consumeToken();
- if (_languageFeatures.cxx11Enabled && LA() == T_DOT_DOT_DOT)
- ast->dot_dot_dot_token = consumeToken();
+ } else if (!parseTypeConstraint(ast->typeConstraint)) {
+ return false;
+ }
+ if (_languageFeatures.cxx11Enabled && LA() == T_DOT_DOT_DOT)
+ ast->dot_dot_dot_token = consumeToken();
- // parse optional name
- parseName(ast->name);
+ // parse optional name
+ parseName(ast->name);
- if (LA() == T_EQUAL) {
- ast->equal_token = consumeToken();
- parseTypeId(ast->type_id);
- }
- node = ast;
- return true;
+ if (LA() == T_EQUAL) {
+ ast->equal_token = consumeToken();
+ parseTypeId(ast->type_id);
}
- return false;
+ node = ast;
+ return true;
}
bool Parser::lookAtTypeParameter()
@@ -2071,10 +2287,9 @@ bool Parser::parseTypeParameter(DeclarationAST *&node)
if (lookAtTypeParameter())
return parseTypenameTypeParameter(node);
- else if (LA() == T_TEMPLATE)
+ if (LA() == T_TEMPLATE)
return parseTemplateTypeParameter(node);
- else
- return false;
+ return parseTemplateTypeParameter(node);
}
bool Parser::parseTypeId(ExpressionAST *&node)
@@ -2819,6 +3034,8 @@ bool Parser::parseInitDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
} else if (node->core_declarator && node->core_declarator->asDecompositionDeclarator()) {
error(cursor(), "structured binding needs initializer");
return false;
+ } else if (!parseRequiresClauseOpt(node->requiresClause)) {
+ return false;
}
return true;
}
@@ -3358,6 +3575,7 @@ bool Parser::parseStatement(StatementAST *&node, bool blockLabeledStatement)
return parseGotoStatement(node);
case T_RETURN:
+ case T_CO_RETURN:
return parseReturnStatement(node);
case T_LBRACE:
@@ -3468,7 +3686,7 @@ bool Parser::parseGotoStatement(StatementAST *&node)
bool Parser::parseReturnStatement(StatementAST *&node)
{
DEBUG_THIS_RULE();
- if (LA() == T_RETURN) {
+ if (LA() == T_RETURN || LA() == T_CO_RETURN) {
ReturnStatementAST *ast = new (_pool) ReturnStatementAST;
ast->return_token = consumeToken();
if (_languageFeatures.cxx11Enabled && LA() == T_LBRACE)
@@ -4731,6 +4949,9 @@ bool Parser::parsePrimaryExpression(ExpressionAST *&node)
case T_AT_SELECTOR:
return parseObjCExpression(node);
+ case T_REQUIRES:
+ return parseRequiresExpression(node);
+
default: {
NameAST *name = nullptr;
if (parseNameId(name)) {
@@ -5436,6 +5657,11 @@ bool Parser::parseUnaryExpression(ExpressionAST *&node)
return parseNoExceptOperatorExpression(node);
}
+ case T_CO_AWAIT:
+ if (!_languageFeatures.cxx20Enabled)
+ break;
+ return parseAwaitExpression(node);
+
default:
break;
} // switch
@@ -5694,6 +5920,8 @@ bool Parser::parseAssignmentExpression(ExpressionAST *&node)
DEBUG_THIS_RULE();
if (LA() == T_THROW)
return parseThrowExpression(node);
+ else if (LA() == T_CO_YIELD)
+ return parseYieldExpression(node);
else
PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Assignment)
}
@@ -5822,6 +6050,34 @@ bool Parser::parseThrowExpression(ExpressionAST *&node)
return false;
}
+bool Parser::parseYieldExpression(ExpressionAST *&node)
+{
+ DEBUG_THIS_RULE();
+ if (LA() != T_CO_YIELD)
+ return false;
+ const auto ast = new (_pool) YieldExpressionAST;
+ ast->yield_token = consumeToken();
+ if (parseBracedInitList0x(ast->expression) || parseAssignmentExpression(ast->expression)) {
+ node = ast;
+ return true;
+ }
+ return false;
+}
+
+bool Parser::parseAwaitExpression(ExpressionAST *&node)
+{
+ DEBUG_THIS_RULE();
+ if (LA() != T_CO_AWAIT)
+ return false;
+ const auto ast = new (_pool) AwaitExpressionAST;
+ ast->await_token = consumeToken();
+ if (parseCastExpression(ast->castExpression)) {
+ node = ast;
+ return true;
+ }
+ return false;
+}
+
bool Parser::parseNoExceptOperatorExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
@@ -6757,6 +7013,15 @@ bool Parser::parseLambdaExpression(ExpressionAST *&node)
if (parseLambdaIntroducer(lambda_introducer)) {
LambdaExpressionAST *ast = new (_pool) LambdaExpressionAST;
ast->lambda_introducer = lambda_introducer;
+ if (_languageFeatures.cxx20Enabled && LA() == T_LESS) {
+ consumeToken();
+ parseTemplateParameterList(ast->templateParameters);
+ if (LA() != T_GREATER)
+ return false;
+ consumeToken();
+ parseRequiresClauseOpt(ast->requiresClause);
+ }
+ parseOptionalAttributeSpecifierSequence(ast->attributes);
parseLambdaDeclarator(ast->lambda_declarator);
parseCompoundStatement(ast->statement);
node = ast;
@@ -6781,7 +7046,9 @@ bool Parser::parseLambdaIntroducer(LambdaIntroducerAST *&node)
if (LA() == T_RBRACKET) {
ast->rbracket_token = consumeToken();
- if (LA() == T_LPAREN || LA() == T_LBRACE) {
+ // FIXME: Attributes are also allowed ...
+ if (LA() == T_LPAREN || LA() == T_LBRACE
+ || (_languageFeatures.cxx20Enabled && LA() == T_LESS)) {
node = ast;
return true;
}
@@ -6897,6 +7164,7 @@ bool Parser::parseLambdaDeclarator(LambdaDeclaratorAST *&node)
parseExceptionSpecification(ast->exception_specification);
parseTrailingReturnType(ast->trailing_return_type);
+ parseRequiresClauseOpt(ast->requiresClause);
node = ast;
return true;
diff --git a/src/libs/3rdparty/cplusplus/Parser.h b/src/libs/3rdparty/cplusplus/Parser.h
index ba101ccd46..e34a4ff522 100644
--- a/src/libs/3rdparty/cplusplus/Parser.h
+++ b/src/libs/3rdparty/cplusplus/Parser.h
@@ -143,9 +143,17 @@ public:
bool parseTemplateArgument(ExpressionAST *&node);
bool parseTemplateArgumentList(ExpressionListAST *&node);
bool parseTemplateDeclaration(DeclarationAST *&node);
+ bool parseConceptDeclaration(DeclarationAST *&node);
+ bool parsePlaceholderTypeSpecifier(PlaceholderTypeSpecifierAST *&node);
+ bool parseTypeConstraint(TypeConstraintAST *&node);
+ bool parseRequirement();
+ bool parseRequiresClauseOpt(RequiresClauseAST *&node);
+ bool parseRequiresExpression(ExpressionAST *&node);
bool parseTemplateParameter(DeclarationAST *&node);
bool parseTemplateParameterList(DeclarationListAST *&node);
bool parseThrowExpression(ExpressionAST *&node);
+ bool parseYieldExpression(ExpressionAST *&node);
+ bool parseAwaitExpression(ExpressionAST *&node);
bool parseNoExceptOperatorExpression(ExpressionAST *&node);
bool parseTryBlockStatement(StatementAST *&node, CtorInitializerAST **placeholder);
bool parseCatchClause(CatchClauseListAST *&node);
diff --git a/src/libs/3rdparty/cplusplus/Symbol.h b/src/libs/3rdparty/cplusplus/Symbol.h
index 497226dcc1..dc07ced907 100644
--- a/src/libs/3rdparty/cplusplus/Symbol.h
+++ b/src/libs/3rdparty/cplusplus/Symbol.h
@@ -66,10 +66,10 @@ public:
/// Returns this Symbol's source location.
int sourceLocation() const { return _sourceLocation; }
- /// \returns this Symbol's line number. The line number is 1-based.
+ /// Returns this Symbol's line number. The line number is 1-based.
int line() const { return _line; }
- /// \returns this Symbol's column number. The column number is 1-based.
+ /// Returns this Symbol's column number. The column number is 1-based.
int column() const { return _column; }
/// Returns this Symbol's file name.
diff --git a/src/libs/3rdparty/cplusplus/Token.cpp b/src/libs/3rdparty/cplusplus/Token.cpp
index 31fdb2036c..2e8a0ce7f2 100644
--- a/src/libs/3rdparty/cplusplus/Token.cpp
+++ b/src/libs/3rdparty/cplusplus/Token.cpp
@@ -120,9 +120,15 @@ const char *token_names[] = {
("case"),
("catch"),
("class"),
+ ("co_await"),
+ ("co_return"),
+ ("co_yield"),
+ ("concept"),
("const"),
("const_cast"),
+ ("consteval"),
("constexpr"),
+ ("constinit"),
("continue"),
("decltype"),
("default"),
@@ -151,6 +157,7 @@ const char *token_names[] = {
("public"),
("register"),
("reinterpret_cast"),
+ ("requires"),
("return"),
("sizeof"),
("static"),
@@ -210,6 +217,7 @@ const char *token_names[] = {
// Primitive types
("bool"),
("char"),
+ ("char8_t"),
("char16_t"),
("char32_t"),
("double"),
diff --git a/src/libs/3rdparty/cplusplus/Token.h b/src/libs/3rdparty/cplusplus/Token.h
index 3a04a44a86..204096893a 100644
--- a/src/libs/3rdparty/cplusplus/Token.h
+++ b/src/libs/3rdparty/cplusplus/Token.h
@@ -130,9 +130,15 @@ enum Kind {
T_CASE,
T_CATCH,
T_CLASS,
+ T_CO_AWAIT,
+ T_CO_RETURN,
+ T_CO_YIELD,
+ T_CONCEPT,
T_CONST,
T_CONST_CAST,
+ T_CONSTEVAL,
T_CONSTEXPR,
+ T_CONSTINIT,
T_CONTINUE,
T_DECLTYPE,
T_DEFAULT,
@@ -161,6 +167,7 @@ enum Kind {
T_PUBLIC,
T_REGISTER,
T_REINTERPRET_CAST,
+ T_REQUIRES,
T_RETURN,
T_SIZEOF,
T_STATIC,
@@ -223,6 +230,7 @@ enum Kind {
T_FIRST_PRIMITIVE,
T_BOOL = T_FIRST_PRIMITIVE,
T_CHAR,
+ T_CHAR8_T,
T_CHAR16_T,
T_CHAR32_T,
T_DOUBLE,
diff --git a/src/libs/3rdparty/libptyqt/.clang-format b/src/libs/3rdparty/libptyqt/.clang-format
new file mode 100644
index 0000000000..b861ff7a95
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/.clang-format
@@ -0,0 +1 @@
+{ "DisableFormat" : true } \ No newline at end of file
diff --git a/src/libs/3rdparty/libptyqt/CMakeLists.txt b/src/libs/3rdparty/libptyqt/CMakeLists.txt
new file mode 100644
index 0000000000..c6e8b74573
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/CMakeLists.txt
@@ -0,0 +1,28 @@
+set(SOURCES
+ iptyprocess.h
+ ptyqt.cpp ptyqt.h
+)
+
+if (WIN32)
+ list(APPEND SOURCES
+ winptyprocess.cpp winptyprocess.h
+ conptyprocess.cpp conptyprocess.h
+ )
+else()
+ list(APPEND SOURCES unixptyprocess.cpp unixptyprocess.h)
+endif()
+
+add_library(ptyqt STATIC ${SOURCES})
+target_link_libraries(ptyqt PUBLIC Qt::Core)
+
+if (WIN32)
+ target_link_libraries(ptyqt PRIVATE winpty Qt::Network)
+ #target_compile_definitions(ptyqt PRIVATE PTYQT_DEBUG)
+endif()
+
+set_target_properties(ptyqt
+ PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}
+ QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ POSITION_INDEPENDENT_CODE ON
+)
diff --git a/src/libs/3rdparty/libptyqt/LICENSE b/src/libs/3rdparty/libptyqt/LICENSE
new file mode 100644
index 0000000000..73996c7c90
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Vitaly Petrov, v31337@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.cpp b/src/libs/3rdparty/libptyqt/conptyprocess.cpp
new file mode 100644
index 0000000000..cb18b33206
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/conptyprocess.cpp
@@ -0,0 +1,343 @@
+#include "conptyprocess.h"
+#include <QFile>
+#include <QFileInfo>
+#include <QThread>
+#include <sstream>
+#include <QTimer>
+#include <QMutexLocker>
+#include <QCoreApplication>
+#include <QWinEventNotifier>
+
+#include <qt_windows.h>
+
+#define READ_INTERVAL_MSEC 500
+
+HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows)
+{
+ HRESULT hr{ E_UNEXPECTED };
+ HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE };
+ HANDLE hPipePTYOut{ INVALID_HANDLE_VALUE };
+
+ // Create the pipes to which the ConPTY will connect
+ if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
+ CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
+ {
+ // Create the Pseudo Console of the required size, attached to the PTY-end of the pipes
+ hr = m_winContext.createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, 0, phPC);
+
+ // Note: We can close the handles to the PTY-end of the pipes here
+ // because the handles are dup'ed into the ConHost and will be released
+ // when the ConPTY is destroyed.
+ if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
+ if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
+ }
+
+ return hr;
+}
+
+// Initializes the specified startup info struct with the required properties and
+// updates its thread attribute list with the specified ConPTY handle
+HRESULT ConPtyProcess::initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
+{
+ HRESULT hr{ E_UNEXPECTED };
+
+ if (pStartupInfo)
+ {
+ SIZE_T attrListSize{};
+
+ pStartupInfo->StartupInfo.hStdInput = m_hPipeIn;
+ pStartupInfo->StartupInfo.hStdError = m_hPipeOut;
+ pStartupInfo->StartupInfo.hStdOutput = m_hPipeOut;
+ pStartupInfo->StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
+
+ // Get the size of the thread attribute list.
+ InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
+
+ // Allocate a thread attribute list of the correct size
+ pStartupInfo->lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
+ HeapAlloc(GetProcessHeap(), 0, attrListSize));
+
+ // Initialize thread attribute list
+ if (pStartupInfo->lpAttributeList
+ && InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize))
+ {
+ // Set Pseudo Console attribute
+ hr = UpdateProcThreadAttribute(
+ pStartupInfo->lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+ hPC,
+ sizeof(HPCON),
+ NULL,
+ NULL)
+ ? S_OK
+ : HRESULT_FROM_WIN32(GetLastError());
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ return hr;
+}
+
+Q_DECLARE_METATYPE(HANDLE)
+
+ConPtyProcess::ConPtyProcess()
+ : IPtyProcess()
+ , m_ptyHandler { INVALID_HANDLE_VALUE }
+ , m_hPipeIn { INVALID_HANDLE_VALUE }
+ , m_hPipeOut { INVALID_HANDLE_VALUE }
+ , m_readThread(nullptr)
+{
+ qRegisterMetaType<HANDLE>("HANDLE");
+}
+
+ConPtyProcess::~ConPtyProcess()
+{
+ kill();
+}
+
+bool ConPtyProcess::startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable()) {
+ m_lastError = m_winContext.lastError();
+ return false;
+ }
+
+ //already running
+ if (m_ptyHandler != INVALID_HANDLE_VALUE)
+ return false;
+
+ QFileInfo fi(executable);
+ if (fi.isRelative() || !QFile::exists(executable)) {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("ConPty Error: shell file path '%1' must be absolute").arg(executable);
+ return false;
+ }
+
+ m_shellPath = executable;
+ m_shellPath.replace('/', '\\');
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+ //env
+ const QString env = environment.join(QChar(QChar::Null)) + QChar(QChar::Null);
+ LPVOID envPtr = env.isEmpty() ? nullptr : (LPVOID) env.utf16();
+
+ LPCWSTR workingDirPtr = workingDir.isEmpty() ? nullptr : (LPCWSTR) workingDir.utf16();
+
+ QStringList exeAndArgs = arguments;
+ exeAndArgs.prepend(m_shellPath);
+ std::wstring cmdArg{(LPCWSTR) (exeAndArgs.join(QLatin1String(" ")).utf16())};
+
+ HRESULT hr{E_UNEXPECTED};
+
+ // Create the Pseudo Console and pipes to it
+ hr = createPseudoConsoleAndPipes(&m_ptyHandler, &m_hPipeIn, &m_hPipeOut, cols, rows);
+
+ if (S_OK != hr) {
+ m_lastError = QString("ConPty Error: CreatePseudoConsoleAndPipes fail");
+ return false;
+ }
+
+ // Initialize the necessary startup info struct
+ if (S_OK != initializeStartupInfoAttachedToPseudoConsole(&m_shellStartupInfo, m_ptyHandler)) {
+ m_lastError = QString("ConPty Error: InitializeStartupInfoAttachedToPseudoConsole fail");
+ return false;
+ }
+
+ hr = CreateProcessW(nullptr, // No module name - use Command Line
+ cmdArg.data(), // Command Line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ FALSE, // Inherit handles
+ EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // Creation flags
+ envPtr, // Environment block
+ workingDirPtr, // Use parent's starting directory
+ &m_shellStartupInfo.StartupInfo, // Pointer to STARTUPINFO
+ &m_shellProcessInformation) // Pointer to PROCESS_INFORMATION
+ ? S_OK
+ : GetLastError();
+
+ if (S_OK != hr) {
+ m_lastError = QString("ConPty Error: Cannot create process -> %1").arg(hr);
+ return false;
+ }
+
+ m_pid = m_shellProcessInformation.dwProcessId;
+
+ // Notify when the shell process has been terminated
+ m_shellCloseWaitNotifier = new QWinEventNotifier(m_shellProcessInformation.hProcess, notifier());
+ QObject::connect(m_shellCloseWaitNotifier,
+ &QWinEventNotifier::activated,
+ notifier(),
+ [this](HANDLE hEvent) {
+ DWORD exitCode = 0;
+ GetExitCodeProcess(hEvent, &exitCode);
+ m_exitCode = exitCode;
+ // Do not respawn if the object is about to be destructed
+ if (!m_aboutToDestruct)
+ emit notifier()->aboutToClose();
+ m_shellCloseWaitNotifier->setEnabled(false);
+ }, Qt::QueuedConnection);
+
+ //this code runned in separate thread
+ m_readThread = QThread::create([this]() {
+ //buffers
+ const DWORD BUFF_SIZE{1024};
+ char szBuffer[BUFF_SIZE]{};
+
+ forever {
+ DWORD dwBytesRead{};
+
+ // Read from the pipe
+ BOOL result = ReadFile(m_hPipeIn, szBuffer, BUFF_SIZE, &dwBytesRead, NULL);
+
+ const bool needMoreData = !result && GetLastError() == ERROR_MORE_DATA;
+ if (result || needMoreData) {
+ QMutexLocker locker(&m_bufferMutex);
+ m_buffer.m_readBuffer.append(szBuffer, dwBytesRead);
+ m_buffer.emitReadyRead();
+ }
+
+ const bool brokenPipe = !result && GetLastError() == ERROR_BROKEN_PIPE;
+ if (QThread::currentThread()->isInterruptionRequested() || brokenPipe)
+ break;
+ }
+
+ CancelIoEx(m_hPipeIn, nullptr);
+ });
+
+ //start read thread
+ m_readThread->start();
+
+ return true;
+}
+
+bool ConPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ if (m_ptyHandler == nullptr)
+ {
+ return false;
+ }
+
+ bool res = SUCCEEDED(m_winContext.resizePseudoConsole(m_ptyHandler, {cols, rows}));
+
+ if (res)
+ {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+
+ return true;
+}
+
+bool ConPtyProcess::kill()
+{
+ bool exitCode = false;
+
+ if (m_ptyHandler != INVALID_HANDLE_VALUE) {
+ m_aboutToDestruct = true;
+
+ // Close ConPTY - this will terminate client process if running
+ m_winContext.closePseudoConsole(m_ptyHandler);
+
+ // Clean-up the pipes
+ if (INVALID_HANDLE_VALUE != m_hPipeOut)
+ CloseHandle(m_hPipeOut);
+ if (INVALID_HANDLE_VALUE != m_hPipeIn)
+ CloseHandle(m_hPipeIn);
+
+ if (m_readThread) {
+ m_readThread->requestInterruption();
+ if (!m_readThread->wait(1000))
+ m_readThread->terminate();
+ m_readThread->deleteLater();
+ m_readThread = nullptr;
+ }
+
+ delete m_shellCloseWaitNotifier;
+ m_shellCloseWaitNotifier = nullptr;
+
+ m_pid = 0;
+ m_ptyHandler = INVALID_HANDLE_VALUE;
+ m_hPipeIn = INVALID_HANDLE_VALUE;
+ m_hPipeOut = INVALID_HANDLE_VALUE;
+
+ CloseHandle(m_shellProcessInformation.hThread);
+ CloseHandle(m_shellProcessInformation.hProcess);
+
+ // Cleanup attribute list
+ if (m_shellStartupInfo.lpAttributeList) {
+ DeleteProcThreadAttributeList(m_shellStartupInfo.lpAttributeList);
+ HeapFree(GetProcessHeap(), 0, m_shellStartupInfo.lpAttributeList);
+ }
+
+ exitCode = true;
+ }
+
+ return exitCode;
+}
+
+IPtyProcess::PtyType ConPtyProcess::type()
+{
+ return PtyType::ConPty;
+}
+
+QString ConPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, Type: %2, Cols: %3, Rows: %4")
+ .arg(m_pid).arg(type())
+ .arg(m_size.first).arg(m_size.second);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *ConPtyProcess::notifier()
+{
+ return &m_buffer;
+}
+
+QByteArray ConPtyProcess::readAll()
+{
+ QByteArray result;
+ {
+ QMutexLocker locker(&m_bufferMutex);
+ result.swap(m_buffer.m_readBuffer);
+ }
+ return result;
+}
+
+qint64 ConPtyProcess::write(const QByteArray &byteArray)
+{
+ DWORD dwBytesWritten{};
+ WriteFile(m_hPipeOut, byteArray.data(), byteArray.size(), &dwBytesWritten, NULL);
+ return dwBytesWritten;
+}
+
+bool ConPtyProcess::isAvailable()
+{
+#ifdef TOO_OLD_WINSDK
+ return false; //very importnant! ConPty can be built, but it doesn't work if built with old sdk and Win10 < 1903
+#endif
+
+ qint32 buildNumber = QSysInfo::kernelVersion().split(".").last().toInt();
+ if (buildNumber < CONPTY_MINIMAL_WINDOWS_VERSION)
+ return false;
+ return m_winContext.init();
+}
+
+void ConPtyProcess::moveToThread(QThread *targetThread)
+{
+ //nothing for now...
+}
diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.h b/src/libs/3rdparty/libptyqt/conptyprocess.h
new file mode 100644
index 0000000000..d4ffd62b7e
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/conptyprocess.h
@@ -0,0 +1,165 @@
+#ifndef CONPTYPROCESS_H
+#define CONPTYPROCESS_H
+
+#include "iptyprocess.h"
+#include <windows.h>
+#include <process.h>
+#include <stdio.h>
+
+#include <QIODevice>
+#include <QLibrary>
+#include <QMutex>
+#include <QTimer>
+#include <QThread>
+
+//Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= 17733
+//Just for compile, ConPty doesn't work with Windows SDK < 17733
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE \
+ ProcThreadAttributeValue(22, FALSE, TRUE, FALSE)
+
+typedef VOID* HPCON;
+
+#define TOO_OLD_WINSDK
+#endif
+
+class QWinEventNotifier;
+
+template <typename T>
+std::vector<T> vectorFromString(const std::basic_string<T> &str)
+{
+ return std::vector<T>(str.begin(), str.end());
+}
+
+//ConPTY available only on Windows 10 releazed after 1903 (19H1) Windows release
+class WindowsContext
+{
+public:
+ typedef HRESULT (*CreatePseudoConsolePtr)(
+ COORD size, // ConPty Dimensions
+ HANDLE hInput, // ConPty Input
+ HANDLE hOutput, // ConPty Output
+ DWORD dwFlags, // ConPty Flags
+ HPCON* phPC); // ConPty Reference
+
+ typedef HRESULT (*ResizePseudoConsolePtr)(HPCON hPC, COORD size);
+
+ typedef VOID (*ClosePseudoConsolePtr)(HPCON hPC);
+
+ WindowsContext()
+ : createPseudoConsole(nullptr)
+ , resizePseudoConsole(nullptr)
+ , closePseudoConsole(nullptr)
+ {
+
+ }
+
+ bool init()
+ {
+ //already initialized
+ if (createPseudoConsole)
+ return true;
+
+ //try to load symbols from library
+ //if it fails -> we can't use ConPty API
+ HANDLE kernel32Handle = LoadLibraryExW(L"kernel32.dll", 0, 0);
+
+ if (kernel32Handle != nullptr)
+ {
+ createPseudoConsole = (CreatePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "CreatePseudoConsole");
+ resizePseudoConsole = (ResizePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ResizePseudoConsole");
+ closePseudoConsole = (ClosePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ClosePseudoConsole");
+ if (createPseudoConsole == NULL || resizePseudoConsole == NULL || closePseudoConsole == NULL)
+ {
+ m_lastError = QString("WindowsContext/ConPty error: %1").arg("Invalid on load API functions");
+ return false;
+ }
+ }
+ else
+ {
+ m_lastError = QString("WindowsContext/ConPty error: %1").arg("Unable to load kernel32");
+ return false;
+ }
+
+ return true;
+ }
+
+ QString lastError()
+ {
+ return m_lastError;
+ }
+
+public:
+ //vars
+ CreatePseudoConsolePtr createPseudoConsole;
+ ResizePseudoConsolePtr resizePseudoConsole;
+ ClosePseudoConsolePtr closePseudoConsole;
+
+private:
+ QString m_lastError;
+};
+
+class PtyBuffer : public QIODevice
+{
+ friend class ConPtyProcess;
+ Q_OBJECT
+public:
+
+ //just empty realization, we need only 'readyRead' signal of this class
+ qint64 readData(char *data, qint64 maxlen) { return 0; }
+ qint64 writeData(const char *data, qint64 len) { return 0; }
+
+ bool isSequential() const { return true; }
+ qint64 bytesAvailable() const { return m_readBuffer.size(); }
+ qint64 size() const { return m_readBuffer.size(); }
+
+ void emitReadyRead()
+ {
+ emit readyRead();
+ }
+
+private:
+ QByteArray m_readBuffer;
+};
+
+class ConPtyProcess : public IPtyProcess
+{
+public:
+ ConPtyProcess();
+ ~ConPtyProcess();
+
+ bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows);
+ bool resize(qint16 cols, qint16 rows);
+ bool kill();
+ PtyType type();
+ QString dumpDebugInfo();
+ virtual QIODevice *notifier();
+ virtual QByteArray readAll();
+ virtual qint64 write(const QByteArray &byteArray);
+ bool isAvailable();
+ void moveToThread(QThread *targetThread);
+
+private:
+ HRESULT createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows);
+ HRESULT initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC);
+
+private:
+ WindowsContext m_winContext;
+ HPCON m_ptyHandler{INVALID_HANDLE_VALUE};
+ HANDLE m_hPipeIn{INVALID_HANDLE_VALUE}, m_hPipeOut{INVALID_HANDLE_VALUE};
+
+ QThread *m_readThread{nullptr};
+ QMutex m_bufferMutex;
+ PtyBuffer m_buffer;
+ bool m_aboutToDestruct{false};
+ PROCESS_INFORMATION m_shellProcessInformation{};
+ QWinEventNotifier *m_shellCloseWaitNotifier{nullptr};
+ STARTUPINFOEX m_shellStartupInfo{};
+};
+
+#endif // CONPTYPROCESS_H
diff --git a/src/libs/3rdparty/libptyqt/iptyprocess.h b/src/libs/3rdparty/libptyqt/iptyprocess.h
new file mode 100644
index 0000000000..3d974908c8
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/iptyprocess.h
@@ -0,0 +1,56 @@
+#ifndef IPTYPROCESS_H
+#define IPTYPROCESS_H
+
+#include <QDebug>
+#include <QString>
+#include <QThread>
+
+#define CONPTY_MINIMAL_WINDOWS_VERSION 18309
+
+class IPtyProcess
+{
+public:
+ enum PtyType { UnixPty = 0, WinPty = 1, ConPty = 2, AutoPty = 3 };
+
+ IPtyProcess() = default;
+ IPtyProcess(const IPtyProcess &) = delete;
+ IPtyProcess &operator=(const IPtyProcess &) = delete;
+
+ virtual ~IPtyProcess() {}
+
+ virtual bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+ = 0;
+ virtual bool resize(qint16 cols, qint16 rows) = 0;
+ virtual bool kill() = 0;
+ virtual PtyType type() = 0;
+ virtual QString dumpDebugInfo() = 0;
+ virtual QIODevice *notifier() = 0;
+ virtual QByteArray readAll() = 0;
+ virtual qint64 write(const QByteArray &byteArray) = 0;
+ virtual bool isAvailable() = 0;
+ virtual void moveToThread(QThread *targetThread) = 0;
+ qint64 pid() { return m_pid; }
+ QPair<qint16, qint16> size() { return m_size; }
+ const QString lastError() { return m_lastError; }
+ int exitCode() { return m_exitCode; }
+ bool toggleTrace()
+ {
+ m_trace = !m_trace;
+ return m_trace;
+ }
+
+protected:
+ QString m_shellPath;
+ QString m_lastError;
+ qint64 m_pid{0};
+ int m_exitCode{0};
+ QPair<qint16, qint16> m_size; //cols / rows
+ bool m_trace{false};
+};
+
+#endif // IPTYPROCESS_H
diff --git a/src/libs/3rdparty/libptyqt/ptyqt.cpp b/src/libs/3rdparty/libptyqt/ptyqt.cpp
new file mode 100644
index 0000000000..b3e7aa1b16
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/ptyqt.cpp
@@ -0,0 +1,45 @@
+#include "ptyqt.h"
+#include <utility>
+
+#ifdef Q_OS_WIN
+#include "winptyprocess.h"
+#include "conptyprocess.h"
+#endif
+
+#ifdef Q_OS_UNIX
+#include "unixptyprocess.h"
+#endif
+
+
+IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType)
+{
+ switch (ptyType)
+ {
+#ifdef Q_OS_WIN
+ case IPtyProcess::PtyType::WinPty:
+ return new WinPtyProcess();
+ break;
+ case IPtyProcess::PtyType::ConPty:
+ return new ConPtyProcess();
+ break;
+#endif
+#ifdef Q_OS_UNIX
+ case IPtyProcess::PtyType::UnixPty:
+ return new UnixPtyProcess();
+ break;
+#endif
+ case IPtyProcess::PtyType::AutoPty:
+ default:
+ break;
+ }
+
+#ifdef Q_OS_WIN
+ if (ConPtyProcess().isAvailable() && qgetenv("QTC_USE_WINPTY").isEmpty())
+ return new ConPtyProcess();
+ else
+ return new WinPtyProcess();
+#endif
+#ifdef Q_OS_UNIX
+ return new UnixPtyProcess();
+#endif
+}
diff --git a/src/libs/3rdparty/libptyqt/ptyqt.h b/src/libs/3rdparty/libptyqt/ptyqt.h
new file mode 100644
index 0000000000..23b80d346b
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/ptyqt.h
@@ -0,0 +1,12 @@
+#ifndef PTYQT_H
+#define PTYQT_H
+
+#include "iptyprocess.h"
+
+class PtyQt
+{
+public:
+ static IPtyProcess *createPtyProcess(IPtyProcess::PtyType ptyType);
+};
+
+#endif // PTYQT_H
diff --git a/src/libs/3rdparty/libptyqt/ptyqt.qbs b/src/libs/3rdparty/libptyqt/ptyqt.qbs
new file mode 100644
index 0000000000..7ff4da9f56
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/ptyqt.qbs
@@ -0,0 +1,45 @@
+import qbs
+
+Project {
+ name: "ptyqt"
+
+ QtcLibrary {
+ Depends { name: "Qt.core" }
+ Depends { name: "Qt.network"; condition: qbs.targetOS.contains("windows") }
+ Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") }
+
+ type: "staticlibrary"
+
+ files: [
+ "iptyprocess.h",
+ "ptyqt.cpp",
+ "ptyqt.h",
+ ]
+
+ Group {
+ name: "ptyqt UNIX files"
+ condition: qbs.targetOS.contains("unix")
+ files: [
+ "unixptyprocess.cpp",
+ "unixptyprocess.h",
+ ]
+ }
+
+ Group {
+ name: "ptyqt Windows files"
+ condition: qbs.targetOS.contains("windows")
+ files: [
+ "conptyprocess.cpp",
+ "conptyprocess.h",
+ "winptyprocess.cpp",
+ "winptyprocess.h",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") }
+ cpp.includePaths: base.concat(exportingProduct.sourceDirectory)
+ }
+ }
+}
diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
new file mode 100644
index 0000000000..8c018daf8c
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
@@ -0,0 +1,374 @@
+#include "unixptyprocess.h"
+#include <QStandardPaths>
+
+#include <errno.h>
+#include <termios.h>
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_FREEBSD)
+#include <utmpx.h>
+#endif
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <QCoreApplication>
+#include <QFileInfo>
+
+UnixPtyProcess::UnixPtyProcess()
+ : IPtyProcess()
+ , m_readMasterNotify(0)
+{
+ m_shellProcess.setWorkingDirectory(
+ QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
+}
+
+UnixPtyProcess::~UnixPtyProcess()
+{
+ kill();
+}
+
+bool UnixPtyProcess::startProcess(const QString &shellPath,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable()) {
+ m_lastError = QString("UnixPty Error: unavailable");
+ return false;
+ }
+
+ if (m_shellProcess.state() == QProcess::Running)
+ return false;
+
+ QFileInfo fi(shellPath);
+ if (fi.isRelative() || !QFile::exists(shellPath)) {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("UnixPty Error: shell file path must be absolute");
+ return false;
+ }
+
+ m_shellPath = shellPath;
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+ int rc = 0;
+
+ m_shellProcess.m_handleMaster = ::posix_openpt(O_RDWR | O_NOCTTY);
+ if (m_shellProcess.m_handleMaster <= 0) {
+ m_lastError = QString("UnixPty Error: unable to open master -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ m_shellProcess.m_handleSlaveName = QLatin1String(ptsname(m_shellProcess.m_handleMaster));
+ if (m_shellProcess.m_handleSlaveName.isEmpty()) {
+ m_lastError = QString("UnixPty Error: unable to get slave name -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = grantpt(m_shellProcess.m_handleMaster);
+ if (rc != 0) {
+ m_lastError
+ = QString("UnixPty Error: unable to change perms for slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = unlockpt(m_shellProcess.m_handleMaster);
+ if (rc != 0) {
+ m_lastError = QString("UnixPty Error: unable to unlock slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ m_shellProcess.m_handleSlave = ::open(m_shellProcess.m_handleSlaveName.toLatin1().data(),
+ O_RDWR | O_NOCTTY);
+ if (m_shellProcess.m_handleSlave < 0) {
+ m_lastError = QString("UnixPty Error: unable to open slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = fcntl(m_shellProcess.m_handleMaster, F_SETFD, FD_CLOEXEC);
+ if (rc == -1) {
+ m_lastError
+ = QString("UnixPty Error: unable to set flags for master -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = fcntl(m_shellProcess.m_handleSlave, F_SETFD, FD_CLOEXEC);
+ if (rc == -1) {
+ m_lastError
+ = QString("UnixPty Error: unable to set flags for slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ struct ::termios ttmode;
+ rc = tcgetattr(m_shellProcess.m_handleMaster, &ttmode);
+ if (rc != 0) {
+ m_lastError = QString("UnixPty Error: termios fail -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ ttmode.c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
+#if defined(IUTF8)
+ ttmode.c_iflag |= IUTF8;
+#endif
+
+ ttmode.c_oflag = OPOST | ONLCR;
+ ttmode.c_cflag = CREAD | CS8 | HUPCL;
+ ttmode.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL;
+
+ ttmode.c_cc[VEOF] = 4;
+ ttmode.c_cc[VEOL] = -1;
+ ttmode.c_cc[VEOL2] = -1;
+ ttmode.c_cc[VERASE] = 0x7f;
+ ttmode.c_cc[VWERASE] = 23;
+ ttmode.c_cc[VKILL] = 21;
+ ttmode.c_cc[VREPRINT] = 18;
+ ttmode.c_cc[VINTR] = 3;
+ ttmode.c_cc[VQUIT] = 0x1c;
+ ttmode.c_cc[VSUSP] = 26;
+ ttmode.c_cc[VSTART] = 17;
+ ttmode.c_cc[VSTOP] = 19;
+ ttmode.c_cc[VLNEXT] = 22;
+ ttmode.c_cc[VDISCARD] = 15;
+ ttmode.c_cc[VMIN] = 1;
+ ttmode.c_cc[VTIME] = 0;
+
+#if (__APPLE__)
+ ttmode.c_cc[VDSUSP] = 25;
+ ttmode.c_cc[VSTATUS] = 20;
+#endif
+
+ cfsetispeed(&ttmode, B38400);
+ cfsetospeed(&ttmode, B38400);
+
+ rc = tcsetattr(m_shellProcess.m_handleMaster, TCSANOW, &ttmode);
+ if (rc != 0) {
+ m_lastError
+ = QString("UnixPty Error: unabble to set associated params -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ m_readMasterNotify = new QSocketNotifier(m_shellProcess.m_handleMaster,
+ QSocketNotifier::Read,
+ &m_shellProcess);
+ m_readMasterNotify->setEnabled(true);
+ m_readMasterNotify->moveToThread(m_shellProcess.thread());
+ QObject::connect(m_readMasterNotify, &QSocketNotifier::activated, [this](int socket) {
+ Q_UNUSED(socket)
+
+ const size_t maxRead = 16 * 1024;
+ static std::array<char, maxRead> buffer;
+
+ int len = ::read(m_shellProcess.m_handleMaster, buffer.data(), buffer.size());
+ if (len > 0) {
+ m_shellReadBuffer.append(buffer.data(), len);
+ m_shellProcess.emitReadyRead();
+ }
+ });
+
+ QObject::connect(&m_shellProcess, &QProcess::finished, &m_shellProcess, [this](int exitCode) {
+ m_exitCode = exitCode;
+ emit m_shellProcess.aboutToClose();
+ m_readMasterNotify->disconnect();
+ });
+
+ QStringList defaultVars;
+
+ defaultVars.append("TERM=xterm-256color");
+ defaultVars.append("ITERM_PROFILE=Default");
+ defaultVars.append("XPC_FLAGS=0x0");
+ defaultVars.append("XPC_SERVICE_NAME=0");
+ defaultVars.append("LANG=en_US.UTF-8");
+ defaultVars.append("LC_ALL=en_US.UTF-8");
+ defaultVars.append("LC_CTYPE=UTF-8");
+ defaultVars.append("INIT_CWD=" + QCoreApplication::applicationDirPath());
+ defaultVars.append("COMMAND_MODE=unix2003");
+ defaultVars.append("COLORTERM=truecolor");
+
+ QStringList varNames;
+ foreach (QString line, environment) {
+ varNames.append(line.split("=").first());
+ }
+
+ //append default env vars only if they don't exists in current env
+ foreach (QString defVar, defaultVars) {
+ if (!varNames.contains(defVar.split("=").first()))
+ environment.append(defVar);
+ }
+
+ QProcessEnvironment envFormat;
+ foreach (QString line, environment) {
+ envFormat.insert(line.split("=").first(), line.split("=").last());
+ }
+ m_shellProcess.setWorkingDirectory(workingDir);
+ m_shellProcess.setProcessEnvironment(envFormat);
+ m_shellProcess.setReadChannel(QProcess::StandardOutput);
+ m_shellProcess.start(m_shellPath, arguments);
+ if (!m_shellProcess.waitForStarted())
+ return false;
+
+ m_pid = m_shellProcess.processId();
+
+ resize(cols, rows);
+
+ return true;
+}
+
+bool UnixPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ struct winsize winp;
+ winp.ws_col = cols;
+ winp.ws_row = rows;
+ winp.ws_xpixel = 0;
+ winp.ws_ypixel = 0;
+
+ bool res = ((ioctl(m_shellProcess.m_handleMaster, TIOCSWINSZ, &winp) != -1)
+ && (ioctl(m_shellProcess.m_handleSlave, TIOCSWINSZ, &winp) != -1));
+
+ if (res) {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+}
+
+bool UnixPtyProcess::kill()
+{
+ m_shellProcess.m_handleSlaveName = QString();
+ if (m_shellProcess.m_handleSlave >= 0) {
+ ::close(m_shellProcess.m_handleSlave);
+ m_shellProcess.m_handleSlave = -1;
+ }
+ if (m_shellProcess.m_handleMaster >= 0) {
+ ::close(m_shellProcess.m_handleMaster);
+ m_shellProcess.m_handleMaster = -1;
+ }
+
+ if (m_shellProcess.state() == QProcess::Running) {
+ m_readMasterNotify->disconnect();
+ m_readMasterNotify->deleteLater();
+
+ m_shellProcess.terminate();
+ m_shellProcess.waitForFinished(1000);
+
+ if (m_shellProcess.state() == QProcess::Running) {
+ QProcess::startDetached(QString("kill -9 %1").arg(pid()));
+ m_shellProcess.kill();
+ m_shellProcess.waitForFinished(1000);
+ }
+
+ return (m_shellProcess.state() == QProcess::NotRunning);
+ }
+ return false;
+}
+
+IPtyProcess::PtyType UnixPtyProcess::type()
+{
+ return IPtyProcess::UnixPty;
+}
+
+QString UnixPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, In: %2, Out: %3, Type: %4, Cols: %5, Rows: %6, IsRunning: %7, Shell: "
+ "%8, SlaveName: %9")
+ .arg(m_pid)
+ .arg(m_shellProcess.m_handleMaster)
+ .arg(m_shellProcess.m_handleSlave)
+ .arg(type())
+ .arg(m_size.first)
+ .arg(m_size.second)
+ .arg(m_shellProcess.state() == QProcess::Running)
+ .arg(m_shellPath)
+ .arg(m_shellProcess.m_handleSlaveName);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *UnixPtyProcess::notifier()
+{
+ return &m_shellProcess;
+}
+
+QByteArray UnixPtyProcess::readAll()
+{
+ QByteArray tmpBuffer = m_shellReadBuffer;
+ m_shellReadBuffer.clear();
+ return tmpBuffer;
+}
+
+qint64 UnixPtyProcess::write(const QByteArray &byteArray)
+{
+ int result = ::write(m_shellProcess.m_handleMaster, byteArray.constData(), byteArray.size());
+ Q_UNUSED(result)
+
+ return byteArray.size();
+}
+
+bool UnixPtyProcess::isAvailable()
+{
+ //todo check something more if required
+ return true;
+}
+
+void UnixPtyProcess::moveToThread(QThread *targetThread)
+{
+ m_shellProcess.moveToThread(targetThread);
+}
+
+void ShellProcess::configChildProcess()
+{
+ dup2(m_handleSlave, STDIN_FILENO);
+ dup2(m_handleSlave, STDOUT_FILENO);
+ dup2(m_handleSlave, STDERR_FILENO);
+
+ pid_t sid = setsid();
+ ioctl(m_handleSlave, TIOCSCTTY, 0);
+ tcsetpgrp(m_handleSlave, sid);
+
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_FREEBSD)
+ // on Android imposible to put record to the 'utmp' file
+ struct utmpx utmpxInfo;
+ memset(&utmpxInfo, 0, sizeof(utmpxInfo));
+
+ strncpy(utmpxInfo.ut_user, qgetenv("USER"), sizeof(utmpxInfo.ut_user) - 1);
+
+ QString device(m_handleSlaveName);
+ if (device.startsWith("/dev/"))
+ device = device.mid(5);
+
+ const auto deviceAsLatin1 = device.toLatin1();
+ const char *d = deviceAsLatin1.constData();
+
+ strncpy(utmpxInfo.ut_line, d, sizeof(utmpxInfo.ut_line) - 1);
+
+ strncpy(utmpxInfo.ut_id, d + strlen(d) - sizeof(utmpxInfo.ut_id), sizeof(utmpxInfo.ut_id));
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ utmpxInfo.ut_tv.tv_sec = tv.tv_sec;
+ utmpxInfo.ut_tv.tv_usec = tv.tv_usec;
+
+ utmpxInfo.ut_type = USER_PROCESS;
+ utmpxInfo.ut_pid = getpid();
+
+ utmpxname(_PATH_UTMPX);
+ setutxent();
+ pututxline(&utmpxInfo);
+ endutxent();
+
+#if !defined(Q_OS_UNIX)
+ updwtmpx(_PATH_UTMPX, &loginInfo);
+#endif
+
+#endif
+}
diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.h b/src/libs/3rdparty/libptyqt/unixptyprocess.h
new file mode 100644
index 0000000000..e4df0d2f74
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/unixptyprocess.h
@@ -0,0 +1,66 @@
+#ifndef UNIXPTYPROCESS_H
+#define UNIXPTYPROCESS_H
+
+#include "iptyprocess.h"
+#include <QProcess>
+#include <QSocketNotifier>
+
+// support for build with MUSL on Alpine Linux
+#ifndef _PATH_UTMPX
+#include <sys/time.h>
+#define _PATH_UTMPX "/var/log/utmp"
+#endif
+
+class ShellProcess : public QProcess
+{
+ friend class UnixPtyProcess;
+ Q_OBJECT
+public:
+ ShellProcess()
+ : QProcess()
+ , m_handleMaster(-1)
+ , m_handleSlave(-1)
+ {
+ setProcessChannelMode(QProcess::SeparateChannels);
+ setChildProcessModifier([this]() { configChildProcess(); });
+ }
+
+ void emitReadyRead() { emit readyRead(); }
+
+protected:
+ void configChildProcess();
+
+private:
+ int m_handleMaster, m_handleSlave;
+ QString m_handleSlaveName;
+};
+
+class UnixPtyProcess : public IPtyProcess
+{
+public:
+ UnixPtyProcess();
+ virtual ~UnixPtyProcess();
+
+ virtual bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows);
+ virtual bool resize(qint16 cols, qint16 rows);
+ virtual bool kill();
+ virtual PtyType type();
+ virtual QString dumpDebugInfo();
+ virtual QIODevice *notifier();
+ virtual QByteArray readAll();
+ virtual qint64 write(const QByteArray &byteArray);
+ virtual bool isAvailable();
+ void moveToThread(QThread *targetThread);
+
+private:
+ ShellProcess m_shellProcess;
+ QSocketNotifier *m_readMasterNotify;
+ QByteArray m_shellReadBuffer;
+};
+
+#endif // UNIXPTYPROCESS_H
diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.cpp b/src/libs/3rdparty/libptyqt/winptyprocess.cpp
new file mode 100644
index 0000000000..0509bb77c3
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/winptyprocess.cpp
@@ -0,0 +1,278 @@
+#include "winptyprocess.h"
+#include <QFile>
+#include <QFileInfo>
+#include <sstream>
+#include <QCoreApplication>
+#include <QLocalSocket>
+#include <QWinEventNotifier>
+
+#define DEBUG_VAR_LEGACY "WINPTYDBG"
+#define DEBUG_VAR_ACTUAL "WINPTY_DEBUG"
+#define SHOW_CONSOLE_VAR "WINPTY_SHOW_CONSOLE"
+#define WINPTY_AGENT_NAME "winpty-agent.exe"
+#define WINPTY_DLL_NAME "winpty.dll"
+
+QString castErrorToString(winpty_error_ptr_t error_ptr)
+{
+ return QString::fromStdWString(winpty_error_msg(error_ptr));
+}
+
+WinPtyProcess::WinPtyProcess()
+ : IPtyProcess()
+ , m_ptyHandler(nullptr)
+ , m_innerHandle(nullptr)
+ , m_inSocket(nullptr)
+ , m_outSocket(nullptr)
+{
+#ifdef PTYQT_DEBUG
+ m_trace = true;
+#endif
+}
+
+WinPtyProcess::~WinPtyProcess()
+{
+ kill();
+}
+
+bool WinPtyProcess::startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable())
+ {
+ m_lastError = QString("WinPty Error: winpty-agent.exe or winpty.dll not found!");
+ return false;
+ }
+
+ //already running
+ if (m_ptyHandler != nullptr)
+ return false;
+
+ QFileInfo fi(executable);
+ if (fi.isRelative() || !QFile::exists(executable))
+ {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("WinPty Error: shell file path must be absolute");
+ return false;
+ }
+
+ m_shellPath = executable;
+ m_shellPath.replace('/', '\\');
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+#ifdef PTYQT_DEBUG
+ if (m_trace)
+ {
+ environment.append(QString("%1=1").arg(DEBUG_VAR_LEGACY));
+ environment.append(QString("%1=trace").arg(DEBUG_VAR_ACTUAL));
+ environment.append(QString("%1=1").arg(SHOW_CONSOLE_VAR));
+ SetEnvironmentVariableA(DEBUG_VAR_LEGACY, "1");
+ SetEnvironmentVariableA(DEBUG_VAR_ACTUAL, "trace");
+ SetEnvironmentVariableA(SHOW_CONSOLE_VAR, "1");
+ }
+#endif
+
+ //env
+ std::wstringstream envBlock;
+ foreach (QString line, environment)
+ {
+ envBlock << line.toStdWString() << L'\0';
+ }
+ std::wstring env = envBlock.str();
+
+ //create start config
+ winpty_error_ptr_t errorPtr = nullptr;
+ winpty_config_t* startConfig = winpty_config_new(0, &errorPtr);
+ if (startConfig == nullptr)
+ {
+ m_lastError = QString("WinPty Error: create start config -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //set params
+ winpty_config_set_initial_size(startConfig, cols, rows);
+ winpty_config_set_mouse_mode(startConfig, WINPTY_MOUSE_MODE_AUTO);
+ //winpty_config_set_agent_timeout();
+
+ //start agent
+ m_ptyHandler = winpty_open(startConfig, &errorPtr);
+ winpty_config_free(startConfig); //start config is local var, free it after use
+
+ if (m_ptyHandler == nullptr)
+ {
+ m_lastError = QString("WinPty Error: start agent -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //create spawn config
+ winpty_spawn_config_t* spawnConfig = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, m_shellPath.toStdWString().c_str(),
+ //commandLine.toStdWString().c_str(), cwd.toStdWString().c_str(),
+ arguments.join(" ").toStdWString().c_str(), workingDir.toStdWString().c_str(),
+ env.c_str(),
+ &errorPtr);
+
+ if (spawnConfig == nullptr)
+ {
+ m_lastError = QString("WinPty Error: create spawn config -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //spawn the new process
+ BOOL spawnSuccess = winpty_spawn(m_ptyHandler, spawnConfig, &m_innerHandle, nullptr, nullptr, &errorPtr);
+ winpty_spawn_config_free(spawnConfig); //spawn config is local var, free it after use
+ if (!spawnSuccess)
+ {
+ m_lastError = QString("WinPty Error: start terminal process -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ m_pid = (int)GetProcessId(m_innerHandle);
+
+ m_outSocket = new QLocalSocket();
+
+ // Notify when the shell process has been terminated
+ m_shellCloseWaitNotifier = new QWinEventNotifier(m_innerHandle, notifier());
+ QObject::connect(m_shellCloseWaitNotifier,
+ &QWinEventNotifier::activated,
+ notifier(),
+ [this](HANDLE hEvent) {
+ DWORD exitCode = 0;
+ GetExitCodeProcess(hEvent, &exitCode);
+ m_exitCode = exitCode;
+ // Do not respawn if the object is about to be destructed
+ if (!m_aboutToDestruct)
+ emit notifier()->aboutToClose();
+ m_shellCloseWaitNotifier->setEnabled(false);
+ });
+
+ //get pipe names
+ LPCWSTR conInPipeName = winpty_conin_name(m_ptyHandler);
+ m_conInName = QString::fromStdWString(std::wstring(conInPipeName));
+ m_inSocket = new QLocalSocket();
+ m_inSocket->connectToServer(m_conInName, QIODevice::WriteOnly);
+ m_inSocket->waitForConnected();
+
+ LPCWSTR conOutPipeName = winpty_conout_name(m_ptyHandler);
+ m_conOutName = QString::fromStdWString(std::wstring(conOutPipeName));
+ m_outSocket->connectToServer(m_conOutName, QIODevice::ReadOnly);
+ m_outSocket->waitForConnected();
+
+ if (m_inSocket->state() != QLocalSocket::ConnectedState && m_outSocket->state() != QLocalSocket::ConnectedState)
+ {
+ m_lastError = QString("WinPty Error: Unable to connect local sockets -> %1 / %2").arg(m_inSocket->errorString()).arg(m_outSocket->errorString());
+ m_inSocket->deleteLater();
+ m_outSocket->deleteLater();
+ m_inSocket = nullptr;
+ m_outSocket = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+bool WinPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ if (m_ptyHandler == nullptr)
+ {
+ return false;
+ }
+
+ bool res = winpty_set_size(m_ptyHandler, cols, rows, nullptr);
+
+ if (res)
+ {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+}
+
+bool WinPtyProcess::kill()
+{
+ bool exitCode = false;
+ if (m_innerHandle != nullptr && m_ptyHandler != nullptr) {
+ m_aboutToDestruct = true;
+ //disconnect all signals (readyRead, ...)
+ m_inSocket->disconnect();
+ m_outSocket->disconnect();
+
+ //disconnect for server
+ m_inSocket->disconnectFromServer();
+ m_outSocket->disconnectFromServer();
+
+ m_inSocket->deleteLater();
+ m_outSocket->deleteLater();
+
+ m_inSocket = nullptr;
+ m_outSocket = nullptr;
+
+ winpty_free(m_ptyHandler);
+ exitCode = CloseHandle(m_innerHandle);
+
+ delete m_shellCloseWaitNotifier;
+ m_shellCloseWaitNotifier = nullptr;
+
+ m_ptyHandler = nullptr;
+ m_innerHandle = nullptr;
+ m_conInName = QString();
+ m_conOutName = QString();
+ m_pid = 0;
+ }
+ return exitCode;
+}
+
+IPtyProcess::PtyType WinPtyProcess::type()
+{
+ return PtyType::WinPty;
+}
+
+QString WinPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, ConIn: %2, ConOut: %3, Type: %4, Cols: %5, Rows: %6, IsRunning: %7, Shell: %8")
+ .arg(m_pid).arg(m_conInName).arg(m_conOutName).arg(type())
+ .arg(m_size.first).arg(m_size.second).arg(m_ptyHandler != nullptr)
+ .arg(m_shellPath);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *WinPtyProcess::notifier()
+{
+ return m_outSocket;
+}
+
+QByteArray WinPtyProcess::readAll()
+{
+ return m_outSocket->readAll();
+}
+
+qint64 WinPtyProcess::write(const QByteArray &byteArray)
+{
+ return m_inSocket->write(byteArray);
+}
+
+bool WinPtyProcess::isAvailable()
+{
+#ifdef PTYQT_BUILD_STATIC
+ return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME);
+#elif PTYQT_BUILD_DYNAMIC
+ return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME)
+ && QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_DLL_NAME);
+#endif
+ return true;
+}
+
+void WinPtyProcess::moveToThread(QThread *targetThread)
+{
+ m_inSocket->moveToThread(targetThread);
+ m_outSocket->moveToThread(targetThread);
+}
diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.h b/src/libs/3rdparty/libptyqt/winptyprocess.h
new file mode 100644
index 0000000000..0bfb27c02c
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/winptyprocess.h
@@ -0,0 +1,43 @@
+#ifndef WINPTYPROCESS_H
+#define WINPTYPROCESS_H
+
+#include "iptyprocess.h"
+#include "winpty.h"
+
+class QLocalSocket;
+class QWinEventNotifier;
+
+class WinPtyProcess : public IPtyProcess
+{
+public:
+ WinPtyProcess();
+ ~WinPtyProcess();
+
+ bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows);
+ bool resize(qint16 cols, qint16 rows);
+ bool kill();
+ PtyType type();
+ QString dumpDebugInfo();
+ QIODevice *notifier();
+ QByteArray readAll();
+ qint64 write(const QByteArray &byteArray);
+ bool isAvailable();
+ void moveToThread(QThread *targetThread);
+
+private:
+ winpty_t *m_ptyHandler;
+ HANDLE m_innerHandle;
+ QString m_conInName;
+ QString m_conOutName;
+ QLocalSocket *m_inSocket;
+ QLocalSocket *m_outSocket;
+ bool m_aboutToDestruct{false};
+ QWinEventNotifier* m_shellCloseWaitNotifier;
+};
+
+#endif // WINPTYPROCESS_H
diff --git a/src/libs/3rdparty/libvterm/CMakeLists.txt b/src/libs/3rdparty/libvterm/CMakeLists.txt
new file mode 100644
index 0000000000..232217d9f5
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_qtc_library(libvterm STATIC
+ PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ SOURCES
+ src/encoding.c
+ src/fullwidth.inc
+ src/keyboard.c
+ src/mouse.c
+ src/parser.c
+ src/pen.c
+ src/rect.h
+ src/screen.c
+ src/state.c
+ src/unicode.c
+ src/utf8.h
+ src/vterm.c
+ src/vterm_internal.h
+)
diff --git a/src/libs/3rdparty/libvterm/CONTRIBUTING b/src/libs/3rdparty/libvterm/CONTRIBUTING
new file mode 100644
index 0000000000..e9a8f0c331
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/CONTRIBUTING
@@ -0,0 +1,22 @@
+How to Contribute
+-----------------
+
+The main resources for this library are:
+
+ Launchpad
+ https://launchpad.net/libvterm
+
+ IRC:
+ ##tty or #tickit on irc.libera.chat
+
+ Email:
+ Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
+
+
+Bug reports and feature requests can be sent to any of the above resources.
+
+New features, bug patches, etc.. should in the first instance be discussed via
+any of the resources listed above, before starting work on the actual code.
+There may be future plans or development already in-progress that could be
+affected so it is better to discuss the ideas first before starting work
+actually writing any code.
diff --git a/src/libs/3rdparty/libvterm/LICENSE b/src/libs/3rdparty/libvterm/LICENSE
new file mode 100644
index 0000000000..0d051634b2
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/LICENSE
@@ -0,0 +1,23 @@
+
+
+The MIT License
+
+Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/libs/3rdparty/libvterm/include/vterm.h b/src/libs/3rdparty/libvterm/include/vterm.h
new file mode 100644
index 0000000000..cb16ff2a04
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/include/vterm.h
@@ -0,0 +1,637 @@
+#ifndef __VTERM_H__
+#define __VTERM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "vterm_keycodes.h"
+
+#define VTERM_VERSION_MAJOR 0
+#define VTERM_VERSION_MINOR 3
+
+#define VTERM_CHECK_VERSION \
+ vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
+
+/* Any cell can contain at most one basic printing character and 5 combining
+ * characters. This number could be changed but will be ABI-incompatible if
+ * you do */
+#define VTERM_MAX_CHARS_PER_CELL 6
+
+typedef struct VTerm VTerm;
+typedef struct VTermState VTermState;
+typedef struct VTermScreen VTermScreen;
+
+typedef struct {
+ int row;
+ int col;
+} VTermPos;
+
+/* some small utility functions; we can just keep these static here */
+
+/* order points by on-screen flow order */
+static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
+{
+ return (a.row == b.row) ? a.col - b.col : a.row - b.row;
+}
+
+typedef struct {
+ int start_row;
+ int end_row;
+ int start_col;
+ int end_col;
+} VTermRect;
+
+/* true if the rect contains the point */
+static inline int vterm_rect_contains(VTermRect r, VTermPos p)
+{
+ return p.row >= r.start_row && p.row < r.end_row &&
+ p.col >= r.start_col && p.col < r.end_col;
+}
+
+/* move a rect */
+static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
+{
+ rect->start_row += row_delta; rect->end_row += row_delta;
+ rect->start_col += col_delta; rect->end_col += col_delta;
+}
+
+/**
+ * Bit-field describing the content of the tagged union `VTermColor`.
+ */
+typedef enum {
+ /**
+ * If the lower bit of `type` is not set, the colour is 24-bit RGB.
+ */
+ VTERM_COLOR_RGB = 0x00,
+
+ /**
+ * The colour is an index into a palette of 256 colours.
+ */
+ VTERM_COLOR_INDEXED = 0x01,
+
+ /**
+ * Mask that can be used to extract the RGB/Indexed bit.
+ */
+ VTERM_COLOR_TYPE_MASK = 0x01,
+
+ /**
+ * If set, indicates that this colour should be the default foreground
+ * color, i.e. there was no SGR request for another colour. When
+ * rendering this colour it is possible to ignore "idx" and just use a
+ * colour that is not in the palette.
+ */
+ VTERM_COLOR_DEFAULT_FG = 0x02,
+
+ /**
+ * If set, indicates that this colour should be the default background
+ * color, i.e. there was no SGR request for another colour. A common
+ * option when rendering this colour is to not render a background at
+ * all, for example by rendering the window transparently at this spot.
+ */
+ VTERM_COLOR_DEFAULT_BG = 0x04,
+
+ /**
+ * Mask that can be used to extract the default foreground/background bit.
+ */
+ VTERM_COLOR_DEFAULT_MASK = 0x06
+} VTermColorType;
+
+/**
+ * Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
+ * given VTermColor instance is an indexed colour.
+ */
+#define VTERM_COLOR_IS_INDEXED(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
+
+/**
+ * Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
+ * the given VTermColor instance is an rgb colour.
+ */
+#define VTERM_COLOR_IS_RGB(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
+
+/**
+ * Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
+ * that the given VTermColor instance corresponds to the default foreground
+ * color.
+ */
+#define VTERM_COLOR_IS_DEFAULT_FG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_FG))
+
+/**
+ * Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
+ * that the given VTermColor instance corresponds to the default background
+ * color.
+ */
+#define VTERM_COLOR_IS_DEFAULT_BG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_BG))
+
+/**
+ * Tagged union storing either an RGB color or an index into a colour palette.
+ * In order to convert indexed colours to RGB, you may use the
+ * vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb()
+ * functions which lookup the RGB colour from the palette maintained by a
+ * VTermState or VTermScreen instance.
+ */
+typedef union {
+ /**
+ * Tag indicating which union member is actually valid. This variable
+ * coincides with the `type` member of the `rgb` and the `indexed` struct
+ * in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether
+ * a particular type flag is set.
+ */
+ uint8_t type;
+
+ /**
+ * Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
+ */
+ struct {
+ /**
+ * Same as the top-level `type` member stored in VTermColor.
+ */
+ uint8_t type;
+
+ /**
+ * The actual 8-bit red, green, blue colour values.
+ */
+ uint8_t red, green, blue;
+ } rgb;
+
+ /**
+ * If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into
+ * the colour palette.
+ */
+ struct {
+ /**
+ * Same as the top-level `type` member stored in VTermColor.
+ */
+ uint8_t type;
+
+ /**
+ * Index into the colour map.
+ */
+ uint8_t idx;
+ } indexed;
+} VTermColor;
+
+/**
+ * Constructs a new VTermColor instance representing the given RGB values.
+ */
+static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green,
+ uint8_t blue)
+{
+ col->type = VTERM_COLOR_RGB;
+ col->rgb.red = red;
+ col->rgb.green = green;
+ col->rgb.blue = blue;
+}
+
+/**
+ * Construct a new VTermColor instance representing an indexed color with the
+ * given index.
+ */
+static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
+{
+ col->type = VTERM_COLOR_INDEXED;
+ col->indexed.idx = idx;
+}
+
+/**
+ * Compares two colours. Returns true if the colors are equal, false otherwise.
+ */
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
+
+typedef enum {
+ /* VTERM_VALUETYPE_NONE = 0 */
+ VTERM_VALUETYPE_BOOL = 1,
+ VTERM_VALUETYPE_INT,
+ VTERM_VALUETYPE_STRING,
+ VTERM_VALUETYPE_COLOR,
+
+ VTERM_N_VALUETYPES
+} VTermValueType;
+
+typedef struct {
+ const char *str;
+ size_t len : 30;
+ bool initial : 1;
+ bool final : 1;
+} VTermStringFragment;
+
+typedef union {
+ int boolean;
+ int number;
+ VTermStringFragment string;
+ VTermColor color;
+} VTermValue;
+
+typedef enum {
+ /* VTERM_ATTR_NONE = 0 */
+ VTERM_ATTR_BOLD = 1, // bool: 1, 22
+ VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
+ VTERM_ATTR_ITALIC, // bool: 3, 23
+ VTERM_ATTR_BLINK, // bool: 5, 25
+ VTERM_ATTR_REVERSE, // bool: 7, 27
+ VTERM_ATTR_CONCEAL, // bool: 8, 28
+ VTERM_ATTR_STRIKE, // bool: 9, 29
+ VTERM_ATTR_FONT, // number: 10-19
+ VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
+ VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
+ VTERM_ATTR_SMALL, // bool: 73, 74, 75
+ VTERM_ATTR_BASELINE, // number: 73, 74, 75
+
+ VTERM_N_ATTRS
+} VTermAttr;
+
+typedef enum {
+ /* VTERM_PROP_NONE = 0 */
+ VTERM_PROP_CURSORVISIBLE = 1, // bool
+ VTERM_PROP_CURSORBLINK, // bool
+ VTERM_PROP_ALTSCREEN, // bool
+ VTERM_PROP_TITLE, // string
+ VTERM_PROP_ICONNAME, // string
+ VTERM_PROP_REVERSE, // bool
+ VTERM_PROP_CURSORSHAPE, // number
+ VTERM_PROP_MOUSE, // number
+
+ VTERM_N_PROPS
+} VTermProp;
+
+enum {
+ VTERM_PROP_CURSORSHAPE_BLOCK = 1,
+ VTERM_PROP_CURSORSHAPE_UNDERLINE,
+ VTERM_PROP_CURSORSHAPE_BAR_LEFT,
+
+ VTERM_N_PROP_CURSORSHAPES
+};
+
+enum {
+ VTERM_PROP_MOUSE_NONE = 0,
+ VTERM_PROP_MOUSE_CLICK,
+ VTERM_PROP_MOUSE_DRAG,
+ VTERM_PROP_MOUSE_MOVE,
+
+ VTERM_N_PROP_MOUSES
+};
+
+typedef enum {
+ VTERM_SELECTION_CLIPBOARD = (1<<0),
+ VTERM_SELECTION_PRIMARY = (1<<1),
+ VTERM_SELECTION_SECONDARY = (1<<2),
+ VTERM_SELECTION_SELECT = (1<<3),
+ VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */
+} VTermSelectionMask;
+
+typedef struct {
+ const uint32_t *chars;
+ int width;
+ unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
+ unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
+ unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
+} VTermGlyphInfo;
+
+typedef struct {
+ unsigned int doublewidth:1; /* DECDWL or DECDHL line */
+ unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
+ unsigned int continuation:1; /* Line is a flow continuation of the previous */
+} VTermLineInfo;
+
+/* Copies of VTermState fields that the 'resize' callback might have reason to
+ * edit. 'resize' callback gets total control of these fields and may
+ * free-and-reallocate them if required. They will be copied back from the
+ * struct after the callback has returned.
+ */
+typedef struct {
+ VTermPos pos; /* current cursor position */
+ VTermLineInfo *lineinfos[2]; /* [1] may be NULL */
+} VTermStateFields;
+
+typedef struct {
+ /* libvterm relies on this memory to be zeroed out before it is returned
+ * by the allocator. */
+ void *(*malloc)(size_t size, void *allocdata);
+ void (*free)(void *ptr, void *allocdata);
+} VTermAllocatorFunctions;
+
+void vterm_check_version(int major, int minor);
+
+struct VTermBuilder {
+ int ver; /* currently unused but reserved for some sort of ABI version flag */
+
+ int rows, cols;
+
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ /* Override default sizes for various structures */
+ size_t outbuffer_len; /* default: 4096 */
+ size_t tmpbuffer_len; /* default: 4096 */
+};
+
+VTerm *vterm_build(const struct VTermBuilder *builder);
+
+/* A convenient shortcut for default cases */
+VTerm *vterm_new(int rows, int cols);
+/* This shortcuts are generally discouraged in favour of just using vterm_build() */
+VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
+
+void vterm_free(VTerm* vt);
+
+void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
+void vterm_set_size(VTerm *vt, int rows, int cols);
+
+int vterm_get_utf8(const VTerm *vt);
+void vterm_set_utf8(VTerm *vt, int is_utf8);
+
+size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
+
+/* Setting output callback will override the buffer logic */
+typedef void VTermOutputCallback(const char *s, size_t len, void *user);
+void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user);
+
+/* These buffer functions only work if output callback is NOT set
+ * These are deprecated and will be removed in a later version */
+size_t vterm_output_get_buffer_size(const VTerm *vt);
+size_t vterm_output_get_buffer_current(const VTerm *vt);
+size_t vterm_output_get_buffer_remaining(const VTerm *vt);
+
+/* This too */
+size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
+void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
+
+void vterm_keyboard_start_paste(VTerm *vt);
+void vterm_keyboard_end_paste(VTerm *vt);
+
+void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
+void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
+
+// ------------
+// Parser layer
+// ------------
+
+/* Flag to indicate non-final subparameters in a single CSI parameter.
+ * Consider
+ * CSI 1;2:3:4;5a
+ * 1 4 and 5 are final.
+ * 2 and 3 are non-final and will have this bit set
+ *
+ * Don't confuse this with the final byte of the CSI escape; 'a' in this case.
+ */
+#define CSI_ARG_FLAG_MORE (1U<<31)
+#define CSI_ARG_MASK (~(1U<<31))
+
+#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
+#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
+
+/* Can't use -1 to indicate a missing argument; use this instead */
+#define CSI_ARG_MISSING ((1UL<<31)-1)
+
+#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
+#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
+#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
+
+typedef struct {
+ int (*text)(const char *bytes, size_t len, void *user);
+ int (*control)(unsigned char control, void *user);
+ int (*escape)(const char *bytes, size_t len, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+ int (*resize)(int rows, int cols, void *user);
+} VTermParserCallbacks;
+
+void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
+void *vterm_parser_get_cbdata(VTerm *vt);
+
+/* Normally NUL, CAN, SUB and DEL are ignored. Setting this true causes them
+ * to be emitted by the 'control' callback
+ */
+void vterm_parser_set_emit_nul(VTerm *vt, bool emit);
+
+// -----------
+// State layer
+// -----------
+
+typedef struct {
+ int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*erase)(VTermRect rect, int selective, void *user);
+ int (*initpen)(void *user);
+ int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
+ int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
+ int (*sb_clear)(void *user);
+} VTermStateCallbacks;
+
+typedef struct {
+ int (*control)(unsigned char control, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+} VTermStateFallbacks;
+
+typedef struct {
+ int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
+ int (*query)(VTermSelectionMask mask, void *user);
+} VTermSelectionCallbacks;
+
+VTermState *vterm_obtain_state(VTerm *vt);
+
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
+void *vterm_state_get_cbdata(VTermState *state);
+
+void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
+void *vterm_state_get_unrecognised_fbdata(VTermState *state);
+
+void vterm_state_reset(VTermState *state, int hard);
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
+void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
+void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
+void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
+void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
+int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
+void vterm_state_focus_in(VTermState *state);
+void vterm_state_focus_out(VTermState *state);
+const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
+
+/**
+ * Makes sure that the given color `col` is indeed an RGB colour. After this
+ * function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
+ * flags stored in `col->type` will have been reset.
+ *
+ * @param state is the VTermState instance from which the colour palette should
+ * be extracted.
+ * @param col is a pointer at the VTermColor instance that should be converted
+ * to an RGB colour.
+ */
+void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
+
+void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
+ char *buffer, size_t buflen);
+
+void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag);
+
+// ------------
+// Screen layer
+// ------------
+
+typedef struct {
+ unsigned int bold : 1;
+ unsigned int underline : 3;
+ unsigned int italic : 1;
+ unsigned int blink : 1;
+ unsigned int reverse : 1;
+ unsigned int conceal : 1;
+ unsigned int strike : 1;
+ unsigned int font : 4; /* 0 to 9 */
+ unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
+ unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
+ unsigned int small : 1;
+ unsigned int baseline : 2;
+} VTermScreenCellAttrs;
+
+enum {
+ VTERM_UNDERLINE_OFF,
+ VTERM_UNDERLINE_SINGLE,
+ VTERM_UNDERLINE_DOUBLE,
+ VTERM_UNDERLINE_CURLY,
+ VTERM_UNDERLINE_DOTTED,
+ VTERM_UNDERLINE_DASHED
+};
+
+enum {
+ VTERM_BASELINE_NORMAL,
+ VTERM_BASELINE_RAISE,
+ VTERM_BASELINE_LOWER,
+};
+
+typedef struct {
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+ char width;
+ VTermScreenCellAttrs attrs;
+ VTermColor fg, bg;
+} VTermScreenCell;
+
+typedef struct {
+ int (*damage)(VTermRect rect, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, void *user);
+ int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
+ int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
+ int (*sb_clear)(void* user);
+} VTermScreenCallbacks;
+
+VTermScreen *vterm_obtain_screen(VTerm *vt);
+
+void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
+void *vterm_screen_get_cbdata(VTermScreen *screen);
+
+void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
+void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
+
+void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow);
+
+// Back-compat alias for the brief time it was in 0.3-RC1
+#define vterm_screen_set_reflow vterm_screen_enable_reflow
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
+
+typedef enum {
+ VTERM_DAMAGE_CELL, /* every cell */
+ VTERM_DAMAGE_ROW, /* entire rows */
+ VTERM_DAMAGE_SCREEN, /* entire screen */
+ VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */
+
+ VTERM_N_DAMAGES
+} VTermDamageSize;
+
+void vterm_screen_flush_damage(VTermScreen *screen);
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
+
+void vterm_screen_reset(VTermScreen *screen, int hard);
+
+/* Neither of these functions NUL-terminate the buffer */
+size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
+size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
+
+typedef enum {
+ VTERM_ATTR_BOLD_MASK = 1 << 0,
+ VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
+ VTERM_ATTR_ITALIC_MASK = 1 << 2,
+ VTERM_ATTR_BLINK_MASK = 1 << 3,
+ VTERM_ATTR_REVERSE_MASK = 1 << 4,
+ VTERM_ATTR_STRIKE_MASK = 1 << 5,
+ VTERM_ATTR_FONT_MASK = 1 << 6,
+ VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
+ VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
+ VTERM_ATTR_CONCEAL_MASK = 1 << 9,
+ VTERM_ATTR_SMALL_MASK = 1 << 10,
+ VTERM_ATTR_BASELINE_MASK = 1 << 11,
+
+ VTERM_ALL_ATTRS_MASK = (1 << 12) - 1
+} VTermAttrMask;
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
+
+int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
+
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
+
+/**
+ * Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
+ * instance.
+ */
+void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
+
+/**
+ * Similar to vterm_state_set_default_colors(), but also resets colours in the
+ * screen buffer(s)
+ */
+void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg);
+
+// ---------
+// Utilities
+// ---------
+
+VTermValueType vterm_get_attr_type(VTermAttr attr);
+VTermValueType vterm_get_prop_type(VTermProp prop);
+
+void vterm_scroll_rect(VTermRect rect,
+ int downward,
+ int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user),
+ void *user);
+
+void vterm_copy_cells(VTermRect dest,
+ VTermRect src,
+ void (*copycell)(VTermPos dest, VTermPos src, void *user),
+ void *user);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/3rdparty/libvterm/include/vterm_keycodes.h b/src/libs/3rdparty/libvterm/include/vterm_keycodes.h
new file mode 100644
index 0000000000..661759febd
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/include/vterm_keycodes.h
@@ -0,0 +1,61 @@
+#ifndef __VTERM_INPUT_H__
+#define __VTERM_INPUT_H__
+
+typedef enum {
+ VTERM_MOD_NONE = 0x00,
+ VTERM_MOD_SHIFT = 0x01,
+ VTERM_MOD_ALT = 0x02,
+ VTERM_MOD_CTRL = 0x04,
+
+ VTERM_ALL_MODS_MASK = 0x07
+} VTermModifier;
+
+typedef enum {
+ VTERM_KEY_NONE,
+
+ VTERM_KEY_ENTER,
+ VTERM_KEY_TAB,
+ VTERM_KEY_BACKSPACE,
+ VTERM_KEY_ESCAPE,
+
+ VTERM_KEY_UP,
+ VTERM_KEY_DOWN,
+ VTERM_KEY_LEFT,
+ VTERM_KEY_RIGHT,
+
+ VTERM_KEY_INS,
+ VTERM_KEY_DEL,
+ VTERM_KEY_HOME,
+ VTERM_KEY_END,
+ VTERM_KEY_PAGEUP,
+ VTERM_KEY_PAGEDOWN,
+
+ VTERM_KEY_FUNCTION_0 = 256,
+ VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
+
+ VTERM_KEY_KP_0,
+ VTERM_KEY_KP_1,
+ VTERM_KEY_KP_2,
+ VTERM_KEY_KP_3,
+ VTERM_KEY_KP_4,
+ VTERM_KEY_KP_5,
+ VTERM_KEY_KP_6,
+ VTERM_KEY_KP_7,
+ VTERM_KEY_KP_8,
+ VTERM_KEY_KP_9,
+ VTERM_KEY_KP_MULT,
+ VTERM_KEY_KP_PLUS,
+ VTERM_KEY_KP_COMMA,
+ VTERM_KEY_KP_MINUS,
+ VTERM_KEY_KP_PERIOD,
+ VTERM_KEY_KP_DIVIDE,
+ VTERM_KEY_KP_ENTER,
+ VTERM_KEY_KP_EQUAL,
+
+ VTERM_KEY_MAX, // Must be last
+ VTERM_N_KEYS = VTERM_KEY_MAX
+} VTermKey;
+
+#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
+
+#endif
diff --git a/src/libs/3rdparty/libvterm/src/encoding.c b/src/libs/3rdparty/libvterm/src/encoding.c
new file mode 100644
index 0000000000..434ac3f99b
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/encoding.c
@@ -0,0 +1,230 @@
+#include "vterm_internal.h"
+
+#define UNICODE_INVALID 0xFFFD
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_PRINT_UTF8
+#endif
+
+struct UTF8DecoderData {
+ // number of bytes remaining in this codepoint
+ int bytes_remaining;
+
+ // number of bytes total in this codepoint once it's finished
+ // (for detecting overlongs)
+ int bytes_total;
+
+ int this_cp;
+};
+
+static void init_utf8(VTermEncoding *enc, void *data_)
+{
+ struct UTF8DecoderData *data = data_;
+
+ data->bytes_remaining = 0;
+ data->bytes_total = 0;
+}
+
+static void decode_utf8(VTermEncoding *enc, void *data_,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct UTF8DecoderData *data = data_;
+
+#ifdef DEBUG_PRINT_UTF8
+ printf("BEGIN UTF-8\n");
+#endif
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos];
+
+#ifdef DEBUG_PRINT_UTF8
+ printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
+#endif
+
+ if(c < 0x20) // C0
+ return;
+
+ else if(c >= 0x20 && c < 0x7f) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ cp[(*cpi)++] = c;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 char: U+%04x\n", c);
+#endif
+ data->bytes_remaining = 0;
+ }
+
+ else if(c == 0x7f) // DEL
+ return;
+
+ else if(c >= 0x80 && c < 0xc0) {
+ if(!data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ continue;
+ }
+
+ data->this_cp <<= 6;
+ data->this_cp |= c & 0x3f;
+ data->bytes_remaining--;
+
+ if(!data->bytes_remaining) {
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
+#endif
+ // Check for overlong sequences
+ switch(data->bytes_total) {
+ case 2:
+ if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
+ break;
+ case 3:
+ if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
+ break;
+ case 4:
+ if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
+ break;
+ case 5:
+ if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
+ break;
+ case 6:
+ if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
+ break;
+ }
+ // Now look for plain invalid ones
+ if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
+ data->this_cp == 0xFFFE ||
+ data->this_cp == 0xFFFF)
+ data->this_cp = UNICODE_INVALID;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" char: U+%04x\n", data->this_cp);
+#endif
+ cp[(*cpi)++] = data->this_cp;
+ }
+ }
+
+ else if(c >= 0xc0 && c < 0xe0) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x1f;
+ data->bytes_total = 2;
+ data->bytes_remaining = 1;
+ }
+
+ else if(c >= 0xe0 && c < 0xf0) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x0f;
+ data->bytes_total = 3;
+ data->bytes_remaining = 2;
+ }
+
+ else if(c >= 0xf0 && c < 0xf8) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x07;
+ data->bytes_total = 4;
+ data->bytes_remaining = 3;
+ }
+
+ else if(c >= 0xf8 && c < 0xfc) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x03;
+ data->bytes_total = 5;
+ data->bytes_remaining = 4;
+ }
+
+ else if(c >= 0xfc && c < 0xfe) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x01;
+ data->bytes_total = 6;
+ data->bytes_remaining = 5;
+ }
+
+ else {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+ }
+}
+
+static VTermEncoding encoding_utf8 = {
+ .init = &init_utf8,
+ .decode = &decode_utf8,
+};
+
+static void decode_usascii(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ int is_gr = bytes[*pos] & 0x80;
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos] ^ is_gr;
+
+ if(c < 0x20 || c == 0x7f || c >= 0x80)
+ return;
+
+ cp[(*cpi)++] = c;
+ }
+}
+
+static VTermEncoding encoding_usascii = {
+ .decode = &decode_usascii,
+};
+
+struct StaticTableEncoding {
+ const VTermEncoding enc;
+ const uint32_t chars[128];
+};
+
+static void decode_table(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
+ int is_gr = bytes[*pos] & 0x80;
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos] ^ is_gr;
+
+ if(c < 0x20 || c == 0x7f || c >= 0x80)
+ return;
+
+ if(table->chars[c])
+ cp[(*cpi)++] = table->chars[c];
+ else
+ cp[(*cpi)++] = c;
+ }
+}
+
+#include "encoding/DECdrawing.inc"
+#include "encoding/uk.inc"
+
+static struct {
+ VTermEncodingType type;
+ char designation;
+ VTermEncoding *enc;
+}
+encodings[] = {
+ { ENC_UTF8, 'u', &encoding_utf8 },
+ { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
+ { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
+ { ENC_SINGLE_94, 'B', &encoding_usascii },
+ { 0 },
+};
+
+/* This ought to be INTERNAL but isn't because it's used by unit testing */
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
+{
+ for(int i = 0; encodings[i].designation; i++)
+ if(encodings[i].type == type && encodings[i].designation == designation)
+ return encodings[i].enc;
+ return NULL;
+}
diff --git a/src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc b/src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc
new file mode 100644
index 0000000000..47093ed0a8
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc
@@ -0,0 +1,36 @@
+static const struct StaticTableEncoding encoding_DECdrawing = {
+ { .decode = &decode_table },
+ {
+ [0x60] = 0x25C6,
+ [0x61] = 0x2592,
+ [0x62] = 0x2409,
+ [0x63] = 0x240C,
+ [0x64] = 0x240D,
+ [0x65] = 0x240A,
+ [0x66] = 0x00B0,
+ [0x67] = 0x00B1,
+ [0x68] = 0x2424,
+ [0x69] = 0x240B,
+ [0x6a] = 0x2518,
+ [0x6b] = 0x2510,
+ [0x6c] = 0x250C,
+ [0x6d] = 0x2514,
+ [0x6e] = 0x253C,
+ [0x6f] = 0x23BA,
+ [0x70] = 0x23BB,
+ [0x71] = 0x2500,
+ [0x72] = 0x23BC,
+ [0x73] = 0x23BD,
+ [0x74] = 0x251C,
+ [0x75] = 0x2524,
+ [0x76] = 0x2534,
+ [0x77] = 0x252C,
+ [0x78] = 0x2502,
+ [0x79] = 0x2A7D,
+ [0x7a] = 0x2A7E,
+ [0x7b] = 0x03C0,
+ [0x7c] = 0x2260,
+ [0x7d] = 0x00A3,
+ [0x7e] = 0x00B7,
+ }
+};
diff --git a/src/libs/3rdparty/libvterm/src/encoding/uk.inc b/src/libs/3rdparty/libvterm/src/encoding/uk.inc
new file mode 100644
index 0000000000..da1445deca
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/encoding/uk.inc
@@ -0,0 +1,6 @@
+static const struct StaticTableEncoding encoding_uk = {
+ { .decode = &decode_table },
+ {
+ [0x23] = 0x00a3,
+ }
+};
diff --git a/src/libs/3rdparty/libvterm/src/fullwidth.inc b/src/libs/3rdparty/libvterm/src/fullwidth.inc
new file mode 100644
index 0000000000..a703529a76
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/fullwidth.inc
@@ -0,0 +1,111 @@
+ { 0x1100, 0x115f },
+ { 0x231a, 0x231b },
+ { 0x2329, 0x232a },
+ { 0x23e9, 0x23ec },
+ { 0x23f0, 0x23f0 },
+ { 0x23f3, 0x23f3 },
+ { 0x25fd, 0x25fe },
+ { 0x2614, 0x2615 },
+ { 0x2648, 0x2653 },
+ { 0x267f, 0x267f },
+ { 0x2693, 0x2693 },
+ { 0x26a1, 0x26a1 },
+ { 0x26aa, 0x26ab },
+ { 0x26bd, 0x26be },
+ { 0x26c4, 0x26c5 },
+ { 0x26ce, 0x26ce },
+ { 0x26d4, 0x26d4 },
+ { 0x26ea, 0x26ea },
+ { 0x26f2, 0x26f3 },
+ { 0x26f5, 0x26f5 },
+ { 0x26fa, 0x26fa },
+ { 0x26fd, 0x26fd },
+ { 0x2705, 0x2705 },
+ { 0x270a, 0x270b },
+ { 0x2728, 0x2728 },
+ { 0x274c, 0x274c },
+ { 0x274e, 0x274e },
+ { 0x2753, 0x2755 },
+ { 0x2757, 0x2757 },
+ { 0x2795, 0x2797 },
+ { 0x27b0, 0x27b0 },
+ { 0x27bf, 0x27bf },
+ { 0x2b1b, 0x2b1c },
+ { 0x2b50, 0x2b50 },
+ { 0x2b55, 0x2b55 },
+ { 0x2e80, 0x2e99 },
+ { 0x2e9b, 0x2ef3 },
+ { 0x2f00, 0x2fd5 },
+ { 0x2ff0, 0x2ffb },
+ { 0x3000, 0x303e },
+ { 0x3041, 0x3096 },
+ { 0x3099, 0x30ff },
+ { 0x3105, 0x312f },
+ { 0x3131, 0x318e },
+ { 0x3190, 0x31ba },
+ { 0x31c0, 0x31e3 },
+ { 0x31f0, 0x321e },
+ { 0x3220, 0x3247 },
+ { 0x3250, 0x4dbf },
+ { 0x4e00, 0xa48c },
+ { 0xa490, 0xa4c6 },
+ { 0xa960, 0xa97c },
+ { 0xac00, 0xd7a3 },
+ { 0xf900, 0xfaff },
+ { 0xfe10, 0xfe19 },
+ { 0xfe30, 0xfe52 },
+ { 0xfe54, 0xfe66 },
+ { 0xfe68, 0xfe6b },
+ { 0xff01, 0xff60 },
+ { 0xffe0, 0xffe6 },
+ { 0x16fe0, 0x16fe3 },
+ { 0x17000, 0x187f7 },
+ { 0x18800, 0x18af2 },
+ { 0x1b000, 0x1b11e },
+ { 0x1b150, 0x1b152 },
+ { 0x1b164, 0x1b167 },
+ { 0x1b170, 0x1b2fb },
+ { 0x1f004, 0x1f004 },
+ { 0x1f0cf, 0x1f0cf },
+ { 0x1f18e, 0x1f18e },
+ { 0x1f191, 0x1f19a },
+ { 0x1f200, 0x1f202 },
+ { 0x1f210, 0x1f23b },
+ { 0x1f240, 0x1f248 },
+ { 0x1f250, 0x1f251 },
+ { 0x1f260, 0x1f265 },
+ { 0x1f300, 0x1f320 },
+ { 0x1f32d, 0x1f335 },
+ { 0x1f337, 0x1f37c },
+ { 0x1f37e, 0x1f393 },
+ { 0x1f3a0, 0x1f3ca },
+ { 0x1f3cf, 0x1f3d3 },
+ { 0x1f3e0, 0x1f3f0 },
+ { 0x1f3f4, 0x1f3f4 },
+ { 0x1f3f8, 0x1f43e },
+ { 0x1f440, 0x1f440 },
+ { 0x1f442, 0x1f4fc },
+ { 0x1f4ff, 0x1f53d },
+ { 0x1f54b, 0x1f54e },
+ { 0x1f550, 0x1f567 },
+ { 0x1f57a, 0x1f57a },
+ { 0x1f595, 0x1f596 },
+ { 0x1f5a4, 0x1f5a4 },
+ { 0x1f5fb, 0x1f64f },
+ { 0x1f680, 0x1f6c5 },
+ { 0x1f6cc, 0x1f6cc },
+ { 0x1f6d0, 0x1f6d2 },
+ { 0x1f6d5, 0x1f6d5 },
+ { 0x1f6eb, 0x1f6ec },
+ { 0x1f6f4, 0x1f6fa },
+ { 0x1f7e0, 0x1f7eb },
+ { 0x1f90d, 0x1f971 },
+ { 0x1f973, 0x1f976 },
+ { 0x1f97a, 0x1f9a2 },
+ { 0x1f9a5, 0x1f9aa },
+ { 0x1f9ae, 0x1f9ca },
+ { 0x1f9cd, 0x1f9ff },
+ { 0x1fa70, 0x1fa73 },
+ { 0x1fa78, 0x1fa7a },
+ { 0x1fa80, 0x1fa82 },
+ { 0x1fa90, 0x1fa95 },
diff --git a/src/libs/3rdparty/libvterm/src/keyboard.c b/src/libs/3rdparty/libvterm/src/keyboard.c
new file mode 100644
index 0000000000..d31c8be12d
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/keyboard.c
@@ -0,0 +1,226 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+#include "utf8.h"
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
+{
+ /* The shift modifier is never important for Unicode characters
+ * apart from Space
+ */
+ if(c != ' ')
+ mod &= ~VTERM_MOD_SHIFT;
+
+ if(mod == 0) {
+ // Normal text - ignore just shift
+ char str[6];
+ int seqlen = fill_utf8(c, str);
+ vterm_push_output_bytes(vt, str, seqlen);
+ return;
+ }
+
+ int needs_CSIu;
+ switch(c) {
+ /* Special Ctrl- letters that can't be represented elsewise */
+ case 'i': case 'j': case 'm': case '[':
+ needs_CSIu = 1;
+ break;
+ /* Ctrl-\ ] ^ _ don't need CSUu */
+ case '\\': case ']': case '^': case '_':
+ needs_CSIu = 0;
+ break;
+ /* Shift-space needs CSIu */
+ case ' ':
+ needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
+ break;
+ /* All other characters needs CSIu except for letters a-z */
+ default:
+ needs_CSIu = (c < 'a' || c > 'z');
+ }
+
+ /* ALT we can just prefix with ESC; anything else requires CSI u */
+ if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
+ return;
+ }
+
+ if(mod & VTERM_MOD_CTRL)
+ c &= 0x1f;
+
+ vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
+}
+
+typedef struct {
+ enum {
+ KEYCODE_NONE,
+ KEYCODE_LITERAL,
+ KEYCODE_TAB,
+ KEYCODE_ENTER,
+ KEYCODE_SS3,
+ KEYCODE_CSI,
+ KEYCODE_CSI_CURSOR,
+ KEYCODE_CSINUM,
+ KEYCODE_KEYPAD,
+ } type;
+ char literal;
+ int csinum;
+} keycodes_s;
+
+static keycodes_s keycodes[] = {
+ { KEYCODE_NONE }, // NONE
+
+ { KEYCODE_ENTER, '\r' }, // ENTER
+ { KEYCODE_TAB, '\t' }, // TAB
+ { KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
+ { KEYCODE_LITERAL, '\x1b' }, // ESCAPE
+
+ { KEYCODE_CSI_CURSOR, 'A' }, // UP
+ { KEYCODE_CSI_CURSOR, 'B' }, // DOWN
+ { KEYCODE_CSI_CURSOR, 'D' }, // LEFT
+ { KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
+
+ { KEYCODE_CSINUM, '~', 2 }, // INS
+ { KEYCODE_CSINUM, '~', 3 }, // DEL
+ { KEYCODE_CSI_CURSOR, 'H' }, // HOME
+ { KEYCODE_CSI_CURSOR, 'F' }, // END
+ { KEYCODE_CSINUM, '~', 5 }, // PAGEUP
+ { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
+};
+
+static keycodes_s keycodes_fn[] = {
+ { KEYCODE_NONE }, // F0 - shouldn't happen
+ { KEYCODE_SS3, 'P' }, // F1
+ { KEYCODE_SS3, 'Q' }, // F2
+ { KEYCODE_SS3, 'R' }, // F3
+ { KEYCODE_SS3, 'S' }, // F4
+ { KEYCODE_CSINUM, '~', 15 }, // F5
+ { KEYCODE_CSINUM, '~', 17 }, // F6
+ { KEYCODE_CSINUM, '~', 18 }, // F7
+ { KEYCODE_CSINUM, '~', 19 }, // F8
+ { KEYCODE_CSINUM, '~', 20 }, // F9
+ { KEYCODE_CSINUM, '~', 21 }, // F10
+ { KEYCODE_CSINUM, '~', 23 }, // F11
+ { KEYCODE_CSINUM, '~', 24 }, // F12
+};
+
+static keycodes_s keycodes_kp[] = {
+ { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
+ { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
+ { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
+ { KEYCODE_KEYPAD, '3', 's' }, // KP_3
+ { KEYCODE_KEYPAD, '4', 't' }, // KP_4
+ { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
+ { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
+ { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
+ { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
+ { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
+ { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
+};
+
+void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
+{
+ if(key == VTERM_KEY_NONE)
+ return;
+
+ keycodes_s k;
+ if(key < VTERM_KEY_FUNCTION_0) {
+ if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
+ return;
+ k = keycodes[key];
+ }
+ else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
+ if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
+ return;
+ k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
+ }
+ else if(key >= VTERM_KEY_KP_0) {
+ if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
+ return;
+ k = keycodes_kp[key - VTERM_KEY_KP_0];
+ }
+
+ switch(k.type) {
+ case KEYCODE_NONE:
+ break;
+
+ case KEYCODE_TAB:
+ /* Shift-Tab is CSI Z but plain Tab is 0x09 */
+ if(mod == VTERM_MOD_SHIFT)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
+ else if(mod & VTERM_MOD_SHIFT)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
+ else
+ goto case_LITERAL;
+ break;
+
+ case KEYCODE_ENTER:
+ /* Enter is CRLF in newline mode, but just LF in linefeed */
+ if(vt->state->mode.newline)
+ vterm_push_output_sprintf(vt, "\r\n");
+ else
+ goto case_LITERAL;
+ break;
+
+ case KEYCODE_LITERAL: case_LITERAL:
+ if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
+ else
+ vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
+ break;
+
+ case KEYCODE_SS3: case_SS3:
+ if(mod == 0)
+ vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
+ else
+ goto case_CSI;
+ break;
+
+ case KEYCODE_CSI: case_CSI:
+ if(mod == 0)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
+ else
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
+ break;
+
+ case KEYCODE_CSINUM:
+ if(mod == 0)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
+ else
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
+ break;
+
+ case KEYCODE_CSI_CURSOR:
+ if(vt->state->mode.cursor)
+ goto case_SS3;
+ else
+ goto case_CSI;
+
+ case KEYCODE_KEYPAD:
+ if(vt->state->mode.keypad) {
+ k.literal = k.csinum;
+ goto case_SS3;
+ }
+ else
+ goto case_LITERAL;
+ }
+}
+
+void vterm_keyboard_start_paste(VTerm *vt)
+{
+ if(vt->state->mode.bracketpaste)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
+}
+
+void vterm_keyboard_end_paste(VTerm *vt)
+{
+ if(vt->state->mode.bracketpaste)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
+}
diff --git a/src/libs/3rdparty/libvterm/src/mouse.c b/src/libs/3rdparty/libvterm/src/mouse.c
new file mode 100644
index 0000000000..bd713f8106
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/mouse.c
@@ -0,0 +1,99 @@
+#include "vterm_internal.h"
+
+#include "utf8.h"
+
+static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
+{
+ modifiers <<= 2;
+
+ switch(state->mouse_protocol) {
+ case MOUSE_X10:
+ if(col + 0x21 > 0xff)
+ col = 0xff - 0x21;
+ if(row + 0x21 > 0xff)
+ row = 0xff - 0x21;
+
+ if(!pressed)
+ code = 3;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
+ (code | modifiers) + 0x20, col + 0x21, row + 0x21);
+ break;
+
+ case MOUSE_UTF8:
+ {
+ char utf8[18]; size_t len = 0;
+
+ if(!pressed)
+ code = 3;
+
+ len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
+ len += fill_utf8(col + 0x21, utf8 + len);
+ len += fill_utf8(row + 0x21, utf8 + len);
+ utf8[len] = 0;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
+ }
+ break;
+
+ case MOUSE_SGR:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
+ code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
+ break;
+
+ case MOUSE_RXVT:
+ if(!pressed)
+ code = 3;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
+ code | modifiers, col + 1, row + 1);
+ break;
+ }
+}
+
+void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ if(col == state->mouse_col && row == state->mouse_row)
+ return;
+
+ state->mouse_col = col;
+ state->mouse_row = row;
+
+ if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
+ (state->mouse_flags & MOUSE_WANT_MOVE)) {
+ int button = state->mouse_buttons & 0x01 ? 1 :
+ state->mouse_buttons & 0x02 ? 2 :
+ state->mouse_buttons & 0x04 ? 3 : 4;
+ output_mouse(state, button-1 + 0x20, 1, mod, col, row);
+ }
+}
+
+void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ int old_buttons = state->mouse_buttons;
+
+ if(button > 0 && button <= 3) {
+ if(pressed)
+ state->mouse_buttons |= (1 << (button-1));
+ else
+ state->mouse_buttons &= ~(1 << (button-1));
+ }
+
+ /* Most of the time we don't get button releases from 4/5 */
+ if(state->mouse_buttons == old_buttons && button < 4)
+ return;
+
+ if(!state->mouse_flags)
+ return;
+
+ if(button < 4) {
+ output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row);
+ }
+ else if(button < 6) {
+ output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
+ }
+}
diff --git a/src/libs/3rdparty/libvterm/src/parser.c b/src/libs/3rdparty/libvterm/src/parser.c
new file mode 100644
index 0000000000..b43a549cef
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/parser.c
@@ -0,0 +1,402 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#undef DEBUG_PARSER
+
+static bool is_intermed(unsigned char c)
+{
+ return c >= 0x20 && c <= 0x2f;
+}
+
+static void do_control(VTerm *vt, unsigned char control)
+{
+ if(vt->parser.callbacks && vt->parser.callbacks->control)
+ if((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
+}
+
+static void do_csi(VTerm *vt, char command)
+{
+#ifdef DEBUG_PARSER
+ printf("Parsed CSI args as:\n", arglen, args);
+ printf(" leader: %s\n", vt->parser.v.csi.leader);
+ for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
+ printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
+ if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
+ printf("\n");
+ printf(" intermed: %s\n", vt->parser.intermed);
+ }
+#endif
+
+ if(vt->parser.callbacks && vt->parser.callbacks->csi)
+ if((*vt->parser.callbacks->csi)(
+ vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
+ vt->parser.v.csi.args,
+ vt->parser.v.csi.argi,
+ vt->parser.intermedlen ? vt->parser.intermed : NULL,
+ command,
+ vt->parser.cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
+}
+
+static void do_escape(VTerm *vt, char command)
+{
+ char seq[INTERMED_MAX+1];
+
+ size_t len = vt->parser.intermedlen;
+ strncpy(seq, vt->parser.intermed, len);
+ seq[len++] = command;
+ seq[len] = 0;
+
+ if(vt->parser.callbacks && vt->parser.callbacks->escape)
+ if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
+}
+
+static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
+{
+ VTermStringFragment frag = {
+ .str = str,
+ .len = len,
+ .initial = vt->parser.string_initial,
+ .final = final,
+ };
+
+ switch(vt->parser.state) {
+ case OSC:
+ if(vt->parser.callbacks && vt->parser.callbacks->osc)
+ (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
+ break;
+
+ case DCS:
+ if(vt->parser.callbacks && vt->parser.callbacks->dcs)
+ (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
+ break;
+
+ case APC:
+ if(vt->parser.callbacks && vt->parser.callbacks->apc)
+ (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
+ break;
+
+ case PM:
+ if(vt->parser.callbacks && vt->parser.callbacks->pm)
+ (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
+ break;
+
+ case SOS:
+ if(vt->parser.callbacks && vt->parser.callbacks->sos)
+ (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
+ break;
+
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ break;
+ }
+
+ vt->parser.string_initial = false;
+}
+
+size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
+{
+ size_t pos = 0;
+ const char *string_start;
+
+ switch(vt->parser.state) {
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ string_start = NULL;
+ break;
+ case OSC:
+ case DCS:
+ case APC:
+ case PM:
+ case SOS:
+ string_start = bytes;
+ break;
+ }
+
+#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
+#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
+
+#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
+
+ for( ; pos < len; pos++) {
+ unsigned char c = bytes[pos];
+ bool c1_allowed = !vt->mode.utf8;
+
+ if(c == 0x00 || c == 0x7f) { // NUL, DEL
+ if(IS_STRING_STATE()) {
+ string_fragment(vt, string_start, bytes + pos - string_start, false);
+ string_start = bytes + pos + 1;
+ }
+ if(vt->parser.emit_nul)
+ do_control(vt, c);
+ continue;
+ }
+ if(c == 0x18 || c == 0x1a) { // CAN, SUB
+ vt->parser.in_esc = false;
+ ENTER_NORMAL_STATE();
+ if(vt->parser.emit_nul)
+ do_control(vt, c);
+ continue;
+ }
+ else if(c == 0x1b) { // ESC
+ vt->parser.intermedlen = 0;
+ if(!IS_STRING_STATE())
+ vt->parser.state = NORMAL;
+ vt->parser.in_esc = true;
+ continue;
+ }
+ else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
+ IS_STRING_STATE()) {
+ // fallthrough
+ }
+ else if(c < 0x20) { // other C0
+ if(vt->parser.state == SOS)
+ continue; // All other C0s permitted in SOS
+
+ if(IS_STRING_STATE())
+ string_fragment(vt, string_start, bytes + pos - string_start, false);
+ do_control(vt, c);
+ if(IS_STRING_STATE())
+ string_start = bytes + pos + 1;
+ continue;
+ }
+ // else fallthrough
+
+ size_t string_len = bytes + pos - string_start;
+
+ if(vt->parser.in_esc) {
+ // Hoist an ESC letter into a C1 if we're not in a string mode
+ // Always accept ESC \ == ST even in string mode
+ if(!vt->parser.intermedlen &&
+ c >= 0x40 && c < 0x60 &&
+ ((!IS_STRING_STATE() || c == 0x5c))) {
+ c += 0x40;
+ c1_allowed = true;
+ if(string_len)
+ string_len -= 1;
+ vt->parser.in_esc = false;
+ }
+ else {
+ string_start = NULL;
+ vt->parser.state = NORMAL;
+ }
+ }
+
+ switch(vt->parser.state) {
+ case CSI_LEADER:
+ /* Extract leader bytes 0x3c to 0x3f */
+ if(c >= 0x3c && c <= 0x3f) {
+ if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
+ break;
+ }
+
+ /* else fallthrough */
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
+
+ vt->parser.v.csi.argi = 0;
+ vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
+ vt->parser.state = CSI_ARGS;
+
+ /* fallthrough */
+ case CSI_ARGS:
+ /* Numerical value of argument */
+ if(c >= '0' && c <= '9') {
+ if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
+ break;
+ }
+ if(c == ':') {
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
+ c = ';';
+ }
+ if(c == ';') {
+ vt->parser.v.csi.argi++;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
+ break;
+ }
+
+ /* else fallthrough */
+ vt->parser.v.csi.argi++;
+ vt->parser.intermedlen = 0;
+ vt->parser.state = CSI_INTERMED;
+ case CSI_INTERMED:
+ if(is_intermed(c)) {
+ if(vt->parser.intermedlen < INTERMED_MAX-1)
+ vt->parser.intermed[vt->parser.intermedlen++] = c;
+ break;
+ }
+ else if(c == 0x1b) {
+ /* ESC in CSI cancels */
+ }
+ else if(c >= 0x40 && c <= 0x7e) {
+ vt->parser.intermed[vt->parser.intermedlen] = 0;
+ do_csi(vt, c);
+ }
+ /* else was invalid CSI */
+
+ ENTER_NORMAL_STATE();
+ break;
+
+ case OSC_COMMAND:
+ /* Numerical value of command */
+ if(c >= '0' && c <= '9') {
+ if(vt->parser.v.osc.command == -1)
+ vt->parser.v.osc.command = 0;
+ else
+ vt->parser.v.osc.command *= 10;
+ vt->parser.v.osc.command += c - '0';
+ break;
+ }
+ if(c == ';') {
+ vt->parser.state = OSC;
+ string_start = bytes + pos + 1;
+ break;
+ }
+
+ /* else fallthrough */
+ string_start = bytes + pos;
+ string_len = 0;
+ vt->parser.state = OSC;
+ goto string_state;
+
+ case DCS_COMMAND:
+ if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
+ vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
+
+ if(c >= 0x40 && c<= 0x7e) {
+ string_start = bytes + pos + 1;
+ vt->parser.state = DCS;
+ }
+ break;
+
+string_state:
+ case OSC:
+ case DCS:
+ case APC:
+ case PM:
+ case SOS:
+ if(c == 0x07 || (c1_allowed && c == 0x9c)) {
+ string_fragment(vt, string_start, string_len, true);
+ ENTER_NORMAL_STATE();
+ }
+ break;
+
+ case NORMAL:
+ if(vt->parser.in_esc) {
+ if(is_intermed(c)) {
+ if(vt->parser.intermedlen < INTERMED_MAX-1)
+ vt->parser.intermed[vt->parser.intermedlen++] = c;
+ }
+ else if(c >= 0x30 && c < 0x7f) {
+ do_escape(vt, c);
+ vt->parser.in_esc = 0;
+ ENTER_NORMAL_STATE();
+ }
+ else {
+ DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
+ }
+ break;
+ }
+ if(c1_allowed && c >= 0x80 && c < 0xa0) {
+ switch(c) {
+ case 0x90: // DCS
+ vt->parser.string_initial = true;
+ vt->parser.v.dcs.commandlen = 0;
+ ENTER_STATE(DCS_COMMAND);
+ break;
+ case 0x98: // SOS
+ vt->parser.string_initial = true;
+ ENTER_STATE(SOS);
+ string_start = bytes + pos + 1;
+ string_len = 0;
+ break;
+ case 0x9b: // CSI
+ vt->parser.v.csi.leaderlen = 0;
+ ENTER_STATE(CSI_LEADER);
+ break;
+ case 0x9d: // OSC
+ vt->parser.v.osc.command = -1;
+ vt->parser.string_initial = true;
+ string_start = bytes + pos + 1;
+ ENTER_STATE(OSC_COMMAND);
+ break;
+ case 0x9e: // PM
+ vt->parser.string_initial = true;
+ ENTER_STATE(PM);
+ string_start = bytes + pos + 1;
+ string_len = 0;
+ break;
+ case 0x9f: // APC
+ vt->parser.string_initial = true;
+ ENTER_STATE(APC);
+ string_start = bytes + pos + 1;
+ string_len = 0;
+ break;
+ default:
+ do_control(vt, c);
+ break;
+ }
+ }
+ else {
+ size_t eaten = 0;
+ if(vt->parser.callbacks && vt->parser.callbacks->text)
+ eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
+
+ if(!eaten) {
+ DEBUG_LOG("libvterm: Text callback did not consume any input\n");
+ /* force it to make progress */
+ eaten = 1;
+ }
+
+ pos += (eaten - 1); // we'll ++ it again in a moment
+ }
+ break;
+ }
+ }
+
+ if(string_start) {
+ size_t string_len = bytes + pos - string_start;
+ if(vt->parser.in_esc)
+ string_len -= 1;
+ string_fragment(vt, string_start, string_len, false);
+ }
+
+ return len;
+}
+
+void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
+{
+ vt->parser.callbacks = callbacks;
+ vt->parser.cbdata = user;
+}
+
+void *vterm_parser_get_cbdata(VTerm *vt)
+{
+ return vt->parser.cbdata;
+}
+
+void vterm_parser_set_emit_nul(VTerm *vt, bool emit)
+{
+ vt->parser.emit_nul = emit;
+}
diff --git a/src/libs/3rdparty/libvterm/src/pen.c b/src/libs/3rdparty/libvterm/src/pen.c
new file mode 100644
index 0000000000..891a45cec7
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/pen.c
@@ -0,0 +1,613 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+/**
+ * Structure used to store RGB triples without the additional metadata stored in
+ * VTermColor.
+ */
+typedef struct {
+ uint8_t red, green, blue;
+} VTermRGB;
+
+static const VTermRGB ansi_colors[] = {
+ /* R G B */
+ { 0, 0, 0 }, // black
+ { 224, 0, 0 }, // red
+ { 0, 224, 0 }, // green
+ { 224, 224, 0 }, // yellow
+ { 0, 0, 224 }, // blue
+ { 224, 0, 224 }, // magenta
+ { 0, 224, 224 }, // cyan
+ { 224, 224, 224 }, // white == light grey
+
+ // high intensity
+ { 128, 128, 128 }, // black
+ { 255, 64, 64 }, // red
+ { 64, 255, 64 }, // green
+ { 255, 255, 64 }, // yellow
+ { 64, 64, 255 }, // blue
+ { 255, 64, 255 }, // magenta
+ { 64, 255, 255 }, // cyan
+ { 255, 255, 255 }, // white for real
+};
+
+static int ramp6[] = {
+ 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
+};
+
+static int ramp24[] = {
+ 0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
+ 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
+};
+
+static void lookup_default_colour_ansi(long idx, VTermColor *col)
+{
+ if (idx >= 0 && idx < 16) {
+ vterm_color_rgb(
+ col,
+ ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
+ }
+}
+
+static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
+{
+ if(index >= 0 && index < 16) {
+ *col = state->colors[index];
+ return true;
+ }
+
+ return false;
+}
+
+static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
+{
+ if(index >= 0 && index < 16) {
+ // Normal 8 colours or high intensity - parse as palette 0
+ return lookup_colour_ansi(state, index, col);
+ }
+ else if(index >= 16 && index < 232) {
+ // 216-colour cube
+ index -= 16;
+
+ vterm_color_rgb(col, ramp6[index/6/6 % 6],
+ ramp6[index/6 % 6],
+ ramp6[index % 6]);
+
+ return true;
+ }
+ else if(index >= 232 && index < 256) {
+ // 24 greyscales
+ index -= 232;
+
+ vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
+
+ return true;
+ }
+
+ return false;
+}
+
+static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
+{
+ switch(palette) {
+ case 2: // RGB mode - 3 args contain colour values directly
+ if(argcount < 3)
+ return argcount;
+
+ vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
+
+ return 3;
+
+ case 5: // XTerm 256-colour mode
+ if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
+ return argcount ? 1 : 0;
+ }
+
+ vterm_color_indexed(col, args[0]);
+
+ return argcount ? 1 : 0;
+
+ default:
+ DEBUG_LOG("Unrecognised colour palette %d\n", palette);
+ return 0;
+ }
+}
+
+// Some conveniences
+
+static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
+{
+#ifdef DEBUG
+ if(type != vterm_get_attr_type(attr)) {
+ DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
+ attr, vterm_get_attr_type(attr), type);
+ return;
+ }
+#endif
+ if(state->callbacks && state->callbacks->setpenattr)
+ (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+}
+
+static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
+{
+ VTermValue val = { .boolean = boolean };
+ setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
+}
+
+static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
+{
+ VTermValue val = { .number = number };
+ setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
+}
+
+static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
+{
+ VTermValue val = { .color = color };
+ setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
+}
+
+static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
+{
+ VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
+
+ vterm_color_indexed(colp, col);
+
+ setpenattr_col(state, attr, *colp);
+}
+
+INTERNAL void vterm_state_newpen(VTermState *state)
+{
+ // 90% grey so that pure white is brighter
+ vterm_color_rgb(&state->default_fg, 240, 240, 240);
+ vterm_color_rgb(&state->default_bg, 0, 0, 0);
+ vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
+
+ for(int col = 0; col < 16; col++)
+ lookup_default_colour_ansi(col, &state->colors[col]);
+}
+
+INTERNAL void vterm_state_resetpen(VTermState *state)
+{
+ state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ state->pen.underline = 0; setpenattr_int (state, VTERM_ATTR_UNDERLINE, 0);
+ state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ state->pen.font = 0; setpenattr_int (state, VTERM_ATTR_FONT, 0);
+ state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
+ state->pen.baseline = 0; setpenattr_int (state, VTERM_ATTR_BASELINE, 0);
+
+ state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
+ state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
+}
+
+INTERNAL void vterm_state_savepen(VTermState *state, int save)
+{
+ if(save) {
+ state->saved.pen = state->pen;
+ }
+ else {
+ state->pen = state->saved.pen;
+
+ setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
+ setpenattr_int (state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
+ setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
+ setpenattr_int (state, VTERM_ATTR_FONT, state->pen.font);
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
+
+ setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ }
+}
+
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
+{
+ /* First make sure that the two colours are of the same type (RGB/Indexed) */
+ if (a->type != b->type) {
+ return false;
+ }
+
+ /* Depending on the type inspect the corresponding members */
+ if (VTERM_COLOR_IS_INDEXED(a)) {
+ return a->indexed.idx == b->indexed.idx;
+ }
+ else if (VTERM_COLOR_IS_RGB(a)) {
+ return (a->rgb.red == b->rgb.red)
+ && (a->rgb.green == b->rgb.green)
+ && (a->rgb.blue == b->rgb.blue);
+ }
+
+ return 0;
+}
+
+void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
+{
+ *default_fg = state->default_fg;
+ *default_bg = state->default_bg;
+}
+
+void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
+{
+ lookup_colour_palette(state, index, col);
+}
+
+void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
+{
+ if(default_fg) {
+ state->default_fg = *default_fg;
+ state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_FG;
+ }
+
+ if(default_bg) {
+ state->default_bg = *default_bg;
+ state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_BG;
+ }
+}
+
+void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
+{
+ if(index >= 0 && index < 16)
+ state->colors[index] = *col;
+}
+
+void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
+{
+ if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
+ lookup_colour_palette(state, col->indexed.idx, col);
+ }
+ col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
+}
+
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
+{
+ state->bold_is_highbright = bold_is_highbright;
+}
+
+INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
+{
+ // SGR - ECMA-48 8.3.117
+
+ int argi = 0;
+ int value;
+
+ while(argi < argcount) {
+ // This logic is easier to do 'done' backwards; set it true, and make it
+ // false again in the 'default' case
+ int done = 1;
+
+ long arg;
+ switch(arg = CSI_ARG(args[argi])) {
+ case CSI_ARG_MISSING:
+ case 0: // Reset
+ vterm_state_resetpen(state);
+ break;
+
+ case 1: { // Bold on
+ const VTermColor *fg = &state->pen.fg;
+ state->pen.bold = 1;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
+ if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
+ break;
+ }
+
+ case 3: // Italic on
+ state->pen.italic = 1;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
+ break;
+
+ case 4: // Underline
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ if(CSI_ARG_HAS_MORE(args[argi])) {
+ argi++;
+ switch(CSI_ARG(args[argi])) {
+ case 0:
+ state->pen.underline = 0;
+ break;
+ case 1:
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ break;
+ case 2:
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ break;
+ case 3:
+ state->pen.underline = VTERM_UNDERLINE_CURLY;
+ break;
+ case 4:
+ state->pen.underline = VTERM_UNDERLINE_DOTTED;
+ break;
+ case 5:
+ state->pen.underline = VTERM_UNDERLINE_DASHED;
+ break;
+ }
+ }
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 5: // Blink
+ state->pen.blink = 1;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
+ break;
+
+ case 7: // Reverse on
+ state->pen.reverse = 1;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
+ break;
+
+ case 8: // Conceal on
+ state->pen.conceal = 1;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
+ break;
+
+ case 9: // Strikethrough on
+ state->pen.strike = 1;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
+ break;
+
+ case 10: case 11: case 12: case 13: case 14:
+ case 15: case 16: case 17: case 18: case 19: // Select font
+ state->pen.font = CSI_ARG(args[argi]) - 10;
+ setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+ break;
+
+ case 21: // Underline double
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 22: // Bold off
+ state->pen.bold = 0;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ break;
+
+ case 23: // Italic and Gothic (currently unsupported) off
+ state->pen.italic = 0;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ break;
+
+ case 24: // Underline off
+ state->pen.underline = 0;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+ break;
+
+ case 25: // Blink off
+ state->pen.blink = 0;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ break;
+
+ case 27: // Reverse off
+ state->pen.reverse = 0;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ break;
+
+ case 28: // Conceal off (Reveal)
+ state->pen.conceal = 0;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ break;
+
+ case 29: // Strikethrough off
+ state->pen.strike = 0;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ break;
+
+ case 30: case 31: case 32: case 33:
+ case 34: case 35: case 36: case 37: // Foreground colour palette
+ value = CSI_ARG(args[argi]) - 30;
+ if(state->pen.bold && state->bold_is_highbright)
+ value += 8;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 38: // Foreground colour alternative palette
+ if(argcount - argi < 1)
+ return;
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 39: // Foreground colour default
+ state->pen.fg = state->default_fg;
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 40: case 41: case 42: case 43:
+ case 44: case 45: case 46: case 47: // Background colour palette
+ value = CSI_ARG(args[argi]) - 40;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ case 48: // Background colour alternative palette
+ if(argcount - argi < 1)
+ return;
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 49: // Default background
+ state->pen.bg = state->default_bg;
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 73: // Superscript
+ case 74: // Subscript
+ case 75: // Superscript/subscript off
+ state->pen.small = (arg != 75);
+ state->pen.baseline =
+ (arg == 73) ? VTERM_BASELINE_RAISE :
+ (arg == 74) ? VTERM_BASELINE_LOWER :
+ VTERM_BASELINE_NORMAL;
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
+ break;
+
+ case 90: case 91: case 92: case 93:
+ case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 90 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 100: case 101: case 102: case 103:
+ case 104: case 105: case 106: case 107: // Background colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 100 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ default:
+ done = 0;
+ break;
+ }
+
+ if(!done)
+ DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
+
+ while(CSI_ARG_HAS_MORE(args[argi++]));
+ }
+}
+
+static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
+{
+ /* Do nothing if the given color is the default color */
+ if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
+ (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
+ return argi;
+ }
+
+ /* Decide whether to send an indexed color or an RGB color */
+ if (VTERM_COLOR_IS_INDEXED(col)) {
+ const uint8_t idx = col->indexed.idx;
+ if (idx < 8) {
+ args[argi++] = (idx + (fg ? 30 : 40));
+ }
+ else if (idx < 16) {
+ args[argi++] = (idx - 8 + (fg ? 90 : 100));
+ }
+ else {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 5;
+ args[argi++] = idx;
+ }
+ }
+ else if (VTERM_COLOR_IS_RGB(col)) {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 2;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
+ args[argi++] = col->rgb.blue;
+ }
+ return argi;
+}
+
+INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount)
+{
+ int argi = 0;
+
+ if(state->pen.bold)
+ args[argi++] = 1;
+
+ if(state->pen.italic)
+ args[argi++] = 3;
+
+ if(state->pen.underline == VTERM_UNDERLINE_SINGLE)
+ args[argi++] = 4;
+ if(state->pen.underline == VTERM_UNDERLINE_CURLY)
+ args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
+
+ if(state->pen.blink)
+ args[argi++] = 5;
+
+ if(state->pen.reverse)
+ args[argi++] = 7;
+
+ if(state->pen.conceal)
+ args[argi++] = 8;
+
+ if(state->pen.strike)
+ args[argi++] = 9;
+
+ if(state->pen.font)
+ args[argi++] = 10 + state->pen.font;
+
+ if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
+ args[argi++] = 21;
+
+ argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
+
+ argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
+
+ if(state->pen.small) {
+ if(state->pen.baseline == VTERM_BASELINE_RAISE)
+ args[argi++] = 73;
+ else if(state->pen.baseline == VTERM_BASELINE_LOWER)
+ args[argi++] = 74;
+ }
+
+ return argi;
+}
+
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
+{
+ switch(attr) {
+ case VTERM_ATTR_BOLD:
+ val->boolean = state->pen.bold;
+ return 1;
+
+ case VTERM_ATTR_UNDERLINE:
+ val->number = state->pen.underline;
+ return 1;
+
+ case VTERM_ATTR_ITALIC:
+ val->boolean = state->pen.italic;
+ return 1;
+
+ case VTERM_ATTR_BLINK:
+ val->boolean = state->pen.blink;
+ return 1;
+
+ case VTERM_ATTR_REVERSE:
+ val->boolean = state->pen.reverse;
+ return 1;
+
+ case VTERM_ATTR_CONCEAL:
+ val->boolean = state->pen.conceal;
+ return 1;
+
+ case VTERM_ATTR_STRIKE:
+ val->boolean = state->pen.strike;
+ return 1;
+
+ case VTERM_ATTR_FONT:
+ val->number = state->pen.font;
+ return 1;
+
+ case VTERM_ATTR_FOREGROUND:
+ val->color = state->pen.fg;
+ return 1;
+
+ case VTERM_ATTR_BACKGROUND:
+ val->color = state->pen.bg;
+ return 1;
+
+ case VTERM_ATTR_SMALL:
+ val->boolean = state->pen.small;
+ return 1;
+
+ case VTERM_ATTR_BASELINE:
+ val->number = state->pen.baseline;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/libvterm/src/rect.h b/src/libs/3rdparty/libvterm/src/rect.h
new file mode 100644
index 0000000000..2114f24c1b
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/rect.h
@@ -0,0 +1,56 @@
+/*
+ * Some utility functions on VTermRect structures
+ */
+
+#define STRFrect "(%d,%d-%d,%d)"
+#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
+
+/* Expand dst to contain src as well */
+static void rect_expand(VTermRect *dst, VTermRect *src)
+{
+ if(dst->start_row > src->start_row) dst->start_row = src->start_row;
+ if(dst->start_col > src->start_col) dst->start_col = src->start_col;
+ if(dst->end_row < src->end_row) dst->end_row = src->end_row;
+ if(dst->end_col < src->end_col) dst->end_col = src->end_col;
+}
+
+/* Clip the dst to ensure it does not step outside of bounds */
+static void rect_clip(VTermRect *dst, VTermRect *bounds)
+{
+ if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
+ if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
+ if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
+ if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
+ /* Ensure it doesn't end up negatively-sized */
+ if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
+ if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
+}
+
+/* True if the two rectangles are equal */
+static int rect_equal(VTermRect *a, VTermRect *b)
+{
+ return (a->start_row == b->start_row) &&
+ (a->start_col == b->start_col) &&
+ (a->end_row == b->end_row) &&
+ (a->end_col == b->end_col);
+}
+
+/* True if small is contained entirely within big */
+static int rect_contains(VTermRect *big, VTermRect *small)
+{
+ if(small->start_row < big->start_row) return 0;
+ if(small->start_col < big->start_col) return 0;
+ if(small->end_row > big->end_row) return 0;
+ if(small->end_col > big->end_col) return 0;
+ return 1;
+}
+
+/* True if the rectangles overlap at all */
+static int rect_intersects(VTermRect *a, VTermRect *b)
+{
+ if(a->start_row > b->end_row || b->start_row > a->end_row)
+ return 0;
+ if(a->start_col > b->end_col || b->start_col > a->end_col)
+ return 0;
+ return 1;
+}
diff --git a/src/libs/3rdparty/libvterm/src/screen.c b/src/libs/3rdparty/libvterm/src/screen.c
new file mode 100644
index 0000000000..9d1028e67a
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/screen.c
@@ -0,0 +1,1183 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "rect.h"
+#include "utf8.h"
+
+#define UNICODE_SPACE 0x20
+#define UNICODE_LINEFEED 0x0a
+
+#undef DEBUG_REFLOW
+
+/* State of the pen at some moment in time, also used in a cell */
+typedef struct
+{
+ /* After the bitfield */
+ VTermColor fg, bg;
+
+ unsigned int bold : 1;
+ unsigned int underline : 3;
+ unsigned int italic : 1;
+ unsigned int blink : 1;
+ unsigned int reverse : 1;
+ unsigned int conceal : 1;
+ unsigned int strike : 1;
+ unsigned int font : 4; /* 0 to 9 */
+ unsigned int small : 1;
+ unsigned int baseline : 2;
+
+ /* Extra state storage that isn't strictly pen-related */
+ unsigned int protected_cell : 1;
+ unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
+ unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
+} ScreenPen;
+
+/* Internal representation of a screen cell */
+typedef struct
+{
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+ ScreenPen pen;
+} ScreenCell;
+
+struct VTermScreen
+{
+ VTerm *vt;
+ VTermState *state;
+
+ const VTermScreenCallbacks *callbacks;
+ void *cbdata;
+
+ VTermDamageSize damage_merge;
+ /* start_row == -1 => no damage */
+ VTermRect damaged;
+ VTermRect pending_scrollrect;
+ int pending_scroll_downward, pending_scroll_rightward;
+
+ int rows;
+ int cols;
+
+ unsigned int global_reverse : 1;
+ unsigned int reflow : 1;
+
+ /* Primary and Altscreen. buffers[1] is lazily allocated as needed */
+ ScreenCell *buffers[2];
+
+ /* buffer will == buffers[0] or buffers[1], depending on altscreen */
+ ScreenCell *buffer;
+
+ /* buffer for a single screen row used in scrollback storage callbacks */
+ VTermScreenCell *sb_buffer;
+
+ ScreenPen pen;
+};
+
+static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
+{
+ cell->chars[0] = 0;
+ cell->pen = screen->pen;
+}
+
+static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col)
+{
+ if(row < 0 || row >= screen->rows)
+ return NULL;
+ if(col < 0 || col >= screen->cols)
+ return NULL;
+ return screen->buffer + (screen->cols * row) + col;
+}
+
+static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
+{
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * rows * cols);
+
+ for(int row = 0; row < rows; row++) {
+ for(int col = 0; col < cols; col++) {
+ clearcell(screen, &new_buffer[row * cols + col]);
+ }
+ }
+
+ return new_buffer;
+}
+
+static void damagerect(VTermScreen *screen, VTermRect rect)
+{
+ VTermRect emit;
+
+ switch(screen->damage_merge) {
+ case VTERM_DAMAGE_CELL:
+ /* Always emit damage event */
+ emit = rect;
+ break;
+
+ case VTERM_DAMAGE_ROW:
+ /* Emit damage longer than one row. Try to merge with existing damage in
+ * the same row */
+ if(rect.end_row > rect.start_row + 1) {
+ // Bigger than 1 line - flush existing, emit this
+ vterm_screen_flush_damage(screen);
+ emit = rect;
+ }
+ else if(screen->damaged.start_row == -1) {
+ // None stored yet
+ screen->damaged = rect;
+ return;
+ }
+ else if(rect.start_row == screen->damaged.start_row) {
+ // Merge with the stored line
+ if(screen->damaged.start_col > rect.start_col)
+ screen->damaged.start_col = rect.start_col;
+ if(screen->damaged.end_col < rect.end_col)
+ screen->damaged.end_col = rect.end_col;
+ return;
+ }
+ else {
+ // Emit the currently stored line, store a new one
+ emit = screen->damaged;
+ screen->damaged = rect;
+ }
+ break;
+
+ case VTERM_DAMAGE_SCREEN:
+ case VTERM_DAMAGE_SCROLL:
+ /* Never emit damage event */
+ if(screen->damaged.start_row == -1)
+ screen->damaged = rect;
+ else {
+ rect_expand(&screen->damaged, &rect);
+ }
+ return;
+
+ default:
+ DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
+ return;
+ }
+
+ if(screen->callbacks && screen->callbacks->damage)
+ (*screen->callbacks->damage)(emit, screen->cbdata);
+}
+
+static void damagescreen(VTermScreen *screen)
+{
+ VTermRect rect = {
+ .start_row = 0,
+ .end_row = screen->rows,
+ .start_col = 0,
+ .end_col = screen->cols,
+ };
+
+ damagerect(screen, rect);
+}
+
+static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
+{
+ VTermScreen *screen = user;
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+
+ if(!cell)
+ return 0;
+
+ int i;
+ for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
+ cell->chars[i] = info->chars[i];
+ cell->pen = screen->pen;
+ }
+ if(i < VTERM_MAX_CHARS_PER_CELL)
+ cell->chars[i] = 0;
+
+ for(int col = 1; col < info->width; col++)
+ getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
+
+ VTermRect rect = {
+ .start_row = pos.row,
+ .end_row = pos.row+1,
+ .start_col = pos.col,
+ .end_col = pos.col+info->width,
+ };
+
+ cell->pen.protected_cell = info->protected_cell;
+ cell->pen.dwl = info->dwl;
+ cell->pen.dhl = info->dhl;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static void sb_pushline_from_row(VTermScreen *screen, int row)
+{
+ VTermPos pos = { .row = row };
+ for(pos.col = 0; pos.col < screen->cols; pos.col++)
+ vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
+
+ (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
+}
+
+static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->sb_pushline &&
+ dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
+ dest.end_col == screen->cols && // full width
+ screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
+ for(int row = 0; row < src.start_row; row++)
+ sb_pushline_from_row(screen, row);
+ }
+
+ int cols = src.end_col - src.start_col;
+ int downward = src.start_row - dest.start_row;
+
+ int init_row, test_row, inc_row;
+ if(downward < 0) {
+ init_row = dest.end_row - 1;
+ test_row = dest.start_row - 1;
+ inc_row = -1;
+ }
+ else {
+ init_row = dest.start_row;
+ test_row = dest.end_row;
+ inc_row = +1;
+ }
+
+ for(int row = init_row; row != test_row; row += inc_row)
+ memmove(getcell(screen, row, dest.start_col),
+ getcell(screen, row + downward, src.start_col),
+ cols * sizeof(ScreenCell));
+
+ return 1;
+}
+
+static int moverect_user(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->moverect) {
+ if(screen->damage_merge != VTERM_DAMAGE_SCROLL)
+ // Avoid an infinite loop
+ vterm_screen_flush_damage(screen);
+
+ if((*screen->callbacks->moverect)(dest, src, screen->cbdata))
+ return 1;
+ }
+
+ damagerect(screen, dest);
+
+ return 1;
+}
+
+static int erase_internal(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
+ const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
+
+ for(int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if(selective && cell->pen.protected_cell)
+ continue;
+
+ cell->chars[0] = 0;
+ cell->pen = (ScreenPen){
+ /* Only copy .fg and .bg; leave things like rv in reset state */
+ .fg = screen->pen.fg,
+ .bg = screen->pen.bg,
+ };
+ cell->pen.dwl = info->doublewidth;
+ cell->pen.dhl = info->doubleheight;
+ }
+ }
+
+ return 1;
+}
+
+static int erase_user(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static int erase(VTermRect rect, int selective, void *user)
+{
+ erase_internal(rect, selective, user);
+ return erase_user(rect, 0, user);
+}
+
+static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->damage_merge != VTERM_DAMAGE_SCROLL) {
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ vterm_screen_flush_damage(screen);
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_user, erase_user, screen);
+
+ return 1;
+ }
+
+ if(screen->damaged.start_row != -1 &&
+ !rect_intersects(&rect, &screen->damaged)) {
+ vterm_screen_flush_damage(screen);
+ }
+
+ if(screen->pending_scrollrect.start_row == -1) {
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ }
+ else if(rect_equal(&screen->pending_scrollrect, &rect) &&
+ ((screen->pending_scroll_downward == 0 && downward == 0) ||
+ (screen->pending_scroll_rightward == 0 && rightward == 0))) {
+ screen->pending_scroll_downward += downward;
+ screen->pending_scroll_rightward += rightward;
+ }
+ else {
+ vterm_screen_flush_damage(screen);
+
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ }
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ if(screen->damaged.start_row == -1)
+ return 1;
+
+ if(rect_contains(&rect, &screen->damaged)) {
+ /* Scroll region entirely contains the damage; just move it */
+ vterm_rect_move(&screen->damaged, -downward, -rightward);
+ rect_clip(&screen->damaged, &rect);
+ }
+ /* There are a number of possible cases here, but lets restrict this to only
+ * the common case where we might actually gain some performance by
+ * optimising it. Namely, a vertical scroll that neatly cuts the damage
+ * region in half.
+ */
+ else if(rect.start_col <= screen->damaged.start_col &&
+ rect.end_col >= screen->damaged.end_col &&
+ rightward == 0) {
+ if(screen->damaged.start_row >= rect.start_row &&
+ screen->damaged.start_row < rect.end_row) {
+ screen->damaged.start_row -= downward;
+ if(screen->damaged.start_row < rect.start_row)
+ screen->damaged.start_row = rect.start_row;
+ if(screen->damaged.start_row > rect.end_row)
+ screen->damaged.start_row = rect.end_row;
+ }
+ if(screen->damaged.end_row >= rect.start_row &&
+ screen->damaged.end_row < rect.end_row) {
+ screen->damaged.end_row -= downward;
+ if(screen->damaged.end_row < rect.start_row)
+ screen->damaged.end_row = rect.start_row;
+ if(screen->damaged.end_row > rect.end_row)
+ screen->damaged.end_row = rect.end_row;
+ }
+ }
+ else {
+ DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
+ ARGSrect(screen->damaged), ARGSrect(rect));
+ }
+
+ return 1;
+}
+
+static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->movecursor)
+ return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
+
+ return 0;
+}
+
+static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch(attr) {
+ case VTERM_ATTR_BOLD:
+ screen->pen.bold = val->boolean;
+ return 1;
+ case VTERM_ATTR_UNDERLINE:
+ screen->pen.underline = val->number;
+ return 1;
+ case VTERM_ATTR_ITALIC:
+ screen->pen.italic = val->boolean;
+ return 1;
+ case VTERM_ATTR_BLINK:
+ screen->pen.blink = val->boolean;
+ return 1;
+ case VTERM_ATTR_REVERSE:
+ screen->pen.reverse = val->boolean;
+ return 1;
+ case VTERM_ATTR_CONCEAL:
+ screen->pen.conceal = val->boolean;
+ return 1;
+ case VTERM_ATTR_STRIKE:
+ screen->pen.strike = val->boolean;
+ return 1;
+ case VTERM_ATTR_FONT:
+ screen->pen.font = val->number;
+ return 1;
+ case VTERM_ATTR_FOREGROUND:
+ screen->pen.fg = val->color;
+ return 1;
+ case VTERM_ATTR_BACKGROUND:
+ screen->pen.bg = val->color;
+ return 1;
+ case VTERM_ATTR_SMALL:
+ screen->pen.small = val->boolean;
+ return 1;
+ case VTERM_ATTR_BASELINE:
+ screen->pen.baseline = val->number;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch(prop) {
+ case VTERM_PROP_ALTSCREEN:
+ if(val->boolean && !screen->buffers[BUFIDX_ALTSCREEN])
+ return 0;
+
+ screen->buffer = val->boolean ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+ /* only send a damage event on disable; because during enable there's an
+ * erase that sends a damage anyway
+ */
+ if(!val->boolean)
+ damagescreen(screen);
+ break;
+ case VTERM_PROP_REVERSE:
+ screen->global_reverse = val->boolean;
+ damagescreen(screen);
+ break;
+ default:
+ ; /* ignore */
+ }
+
+ if(screen->callbacks && screen->callbacks->settermprop)
+ return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
+
+ return 1;
+}
+
+static int bell(void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->bell)
+ return (*screen->callbacks->bell)(screen->cbdata);
+
+ return 0;
+}
+
+/* How many cells are non-blank
+ * Returns the position of the first blank cell in the trailing blank end */
+static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
+{
+ int col = cols - 1;
+ while(col >= 0 && buffer[row * cols + col].chars[0] == 0)
+ col--;
+ return col + 1;
+}
+
+#define REFLOW (screen->reflow)
+
+static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active, VTermStateFields *statefields)
+{
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ ScreenCell *old_buffer = screen->buffers[bufidx];
+ VTermLineInfo *old_lineinfo = statefields->lineinfos[bufidx];
+
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
+
+ int old_row = old_rows - 1;
+ int new_row = new_rows - 1;
+
+ VTermPos old_cursor = statefields->pos;
+ VTermPos new_cursor = { -1, -1 };
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
+ old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
+#endif
+
+ /* Keep track of the final row that is knonw to be blank, so we know what
+ * spare space we have for scrolling into
+ */
+ int final_blank_row = new_rows;
+
+ while(old_row >= 0) {
+ int old_row_end = old_row;
+ /* TODO: Stop if dwl or dhl */
+ while(REFLOW && old_lineinfo && old_row >= 0 && old_lineinfo[old_row].continuation)
+ old_row--;
+ int old_row_start = old_row;
+
+ int width = 0;
+ for(int row = old_row_start; row <= old_row_end; row++) {
+ if(REFLOW && row < (old_rows - 1) && old_lineinfo[row + 1].continuation)
+ width += old_cols;
+ else
+ width += line_popcount(old_buffer, row, old_rows, old_cols);
+ }
+
+ if(final_blank_row == (new_row + 1) && width == 0)
+ final_blank_row = new_row;
+
+ int new_height = REFLOW
+ ? width ? (width + new_cols - 1) / new_cols : 1
+ : 1;
+
+ int new_row_end = new_row;
+ int new_row_start = new_row - new_height + 1;
+
+ old_row = old_row_start;
+ int old_col = 0;
+
+ int spare_rows = new_rows - final_blank_row;
+
+ if(new_row_start < 0 && /* we'd fall off the top */
+ spare_rows >= 0 && /* we actually have spare rows */
+ (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows))
+ {
+ /* Attempt to scroll content down into the blank rows at the bottom to
+ * make it fit
+ */
+ int downwards = -new_row_start;
+ if(downwards > spare_rows)
+ downwards = spare_rows;
+ int rowcount = new_rows - downwards;
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " scroll %d rows +%d downwards\n", rowcount, downwards);
+#endif
+
+ memmove(&new_buffer[downwards * new_cols], &new_buffer[0], rowcount * new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[downwards], &new_lineinfo[0], rowcount * sizeof(new_lineinfo[0]));
+
+ new_row += downwards;
+ new_row_start += downwards;
+ new_row_end += downwards;
+
+ if(new_cursor.row >= 0)
+ new_cursor.row += downwards;
+
+ final_blank_row += downwards;
+ }
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " rows [%d..%d] <- [%d..%d] width=%d\n",
+ new_row_start, new_row_end, old_row_start, old_row_end, width);
+#endif
+
+ if(new_row_start < 0)
+ break;
+
+ for(new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
+ int count = width >= new_cols ? new_cols : width;
+ width -= count;
+
+ int new_col = 0;
+
+ while(count) {
+ /* TODO: This could surely be done a lot faster by memcpy()'ing the entire range */
+ new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
+
+ if(old_cursor.row == old_row && old_cursor.col == old_col)
+ new_cursor.row = new_row, new_cursor.col = new_col;
+
+ old_col++;
+ if(old_col == old_cols) {
+ old_row++;
+
+ if(!REFLOW) {
+ new_col++;
+ break;
+ }
+ old_col = 0;
+ }
+
+ new_col++;
+ count--;
+ }
+
+ if(old_cursor.row == old_row && old_cursor.col >= old_col) {
+ new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
+ if(new_cursor.col >= new_cols)
+ new_cursor.col = new_cols-1;
+ }
+
+ while(new_col < new_cols) {
+ clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
+ new_col++;
+ }
+
+ new_lineinfo[new_row].continuation = (new_row > new_row_start);
+ }
+
+ old_row = old_row_start - 1;
+ new_row = new_row_start - 1;
+ }
+
+ if(old_cursor.row <= old_row) {
+ /* cursor would have moved entirely off the top of the screen; lets just
+ * bring it within range */
+ new_cursor.row = 0, new_cursor.col = old_cursor.col;
+ if(new_cursor.col >= new_cols)
+ new_cursor.col = new_cols-1;
+ }
+
+ /* We really expect the cursor position to be set by now */
+ if(active && (new_cursor.row == -1 || new_cursor.col == -1)) {
+ fprintf(stderr, "screen_resize failed to update cursor position\n");
+ abort();
+ }
+
+ if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
+ /* Push spare lines to scrollback buffer */
+ for(int row = 0; row <= old_row; row++)
+ sb_pushline_from_row(screen, row);
+ if(active)
+ statefields->pos.row -= (old_row + 1);
+ }
+ if(new_row >= 0 && bufidx == BUFIDX_PRIMARY &&
+ screen->callbacks && screen->callbacks->sb_popline) {
+ /* Try to backfill rows by popping scrollback buffer */
+ while(new_row >= 0) {
+ if(!(screen->callbacks->sb_popline(old_cols, screen->sb_buffer, screen->cbdata)))
+ break;
+
+ VTermPos pos = { .row = new_row };
+ for(pos.col = 0; pos.col < old_cols && pos.col < new_cols; pos.col += screen->sb_buffer[pos.col].width) {
+ VTermScreenCell *src = &screen->sb_buffer[pos.col];
+ ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
+
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
+ dst->chars[i] = src->chars[i];
+ if(!src->chars[i])
+ break;
+ }
+
+ dst->pen.bold = src->attrs.bold;
+ dst->pen.underline = src->attrs.underline;
+ dst->pen.italic = src->attrs.italic;
+ dst->pen.blink = src->attrs.blink;
+ dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse;
+ dst->pen.conceal = src->attrs.conceal;
+ dst->pen.strike = src->attrs.strike;
+ dst->pen.font = src->attrs.font;
+ dst->pen.small = src->attrs.small;
+ dst->pen.baseline = src->attrs.baseline;
+
+ dst->pen.fg = src->fg;
+ dst->pen.bg = src->bg;
+
+ if(src->width == 2 && pos.col < (new_cols-1))
+ (dst + 1)->chars[0] = (uint32_t) -1;
+ }
+ for( ; pos.col < new_cols; pos.col++)
+ clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
+ new_row--;
+
+ if(active)
+ statefields->pos.row++;
+ }
+ }
+ if(new_row >= 0) {
+ /* Scroll new rows back up to the top and fill in blanks at the bottom */
+ int moverows = new_rows - new_row - 1;
+ memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols], moverows * new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1], moverows * sizeof(new_lineinfo[0]));
+
+ new_cursor.row -= (new_row + 1);
+
+ for(new_row = moverows; new_row < new_rows; new_row++) {
+ for(int col = 0; col < new_cols; col++)
+ clearcell(screen, &new_buffer[new_row * new_cols + col]);
+ new_lineinfo[new_row] = (VTermLineInfo){ 0 };
+ }
+ }
+
+ vterm_allocator_free(screen->vt, old_buffer);
+ screen->buffers[bufidx] = new_buffer;
+
+ vterm_allocator_free(screen->vt, old_lineinfo);
+ statefields->lineinfos[bufidx] = new_lineinfo;
+
+ if(active)
+ statefields->pos = new_cursor;
+
+ return;
+}
+
+static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
+{
+ VTermScreen *screen = user;
+
+ int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN] && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]);
+
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ if(new_cols > old_cols) {
+ /* Ensure that ->sb_buffer is large enough for a new or and old row */
+ if(screen->sb_buffer)
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
+ }
+
+ resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
+ if(screen->buffers[BUFIDX_ALTSCREEN])
+ resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
+ else if(new_rows != old_rows) {
+ /* We don't need a full resize of the altscreen because it isn't enabled
+ * but we should at least keep the lineinfo the right size */
+ vterm_allocator_free(screen->vt, fields->lineinfos[BUFIDX_ALTSCREEN]);
+
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
+ for(int row = 0; row < new_rows; row++)
+ new_lineinfo[row] = (VTermLineInfo){ 0 };
+
+ fields->lineinfos[BUFIDX_ALTSCREEN] = new_lineinfo;
+ }
+
+ screen->buffer = altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+
+ screen->rows = new_rows;
+ screen->cols = new_cols;
+
+ if(new_cols <= old_cols) {
+ if(screen->sb_buffer)
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
+ }
+
+ /* TODO: Maaaaybe we can optimise this if there's no reflow happening */
+ damagescreen(screen);
+
+ if(screen->callbacks && screen->callbacks->resize)
+ return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
+
+ return 1;
+}
+
+static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(newinfo->doublewidth != oldinfo->doublewidth ||
+ newinfo->doubleheight != oldinfo->doubleheight) {
+ for(int col = 0; col < screen->cols; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+ cell->pen.dwl = newinfo->doublewidth;
+ cell->pen.dhl = newinfo->doubleheight;
+ }
+
+ VTermRect rect = {
+ .start_row = row,
+ .end_row = row + 1,
+ .start_col = 0,
+ .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
+ };
+ damagerect(screen, rect);
+
+ if(newinfo->doublewidth) {
+ rect.start_col = screen->cols / 2;
+ rect.end_col = screen->cols;
+
+ erase_internal(rect, 0, user);
+ }
+ }
+
+ return 1;
+}
+
+static int sb_clear(void *user) {
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->sb_clear)
+ if((*screen->callbacks->sb_clear)(screen->cbdata))
+ return 1;
+
+ return 0;
+}
+
+static VTermStateCallbacks state_cbs = {
+ .putglyph = &putglyph,
+ .movecursor = &movecursor,
+ .scrollrect = &scrollrect,
+ .erase = &erase,
+ .setpenattr = &setpenattr,
+ .settermprop = &settermprop,
+ .bell = &bell,
+ .resize = &resize,
+ .setlineinfo = &setlineinfo,
+ .sb_clear = &sb_clear,
+};
+
+static VTermScreen *screen_new(VTerm *vt)
+{
+ VTermState *state = vterm_obtain_state(vt);
+ if(!state)
+ return NULL;
+
+ VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
+ int rows, cols;
+
+ vterm_get_size(vt, &rows, &cols);
+
+ screen->vt = vt;
+ screen->state = state;
+
+ screen->damage_merge = VTERM_DAMAGE_CELL;
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+
+ screen->rows = rows;
+ screen->cols = cols;
+
+ screen->global_reverse = false;
+ screen->reflow = false;
+
+ screen->callbacks = NULL;
+ screen->cbdata = NULL;
+
+ screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
+
+ screen->buffer = screen->buffers[BUFIDX_PRIMARY];
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols);
+
+ vterm_state_set_callbacks(screen->state, &state_cbs, screen);
+
+ return screen;
+}
+
+INTERNAL void vterm_screen_free(VTermScreen *screen)
+{
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_PRIMARY]);
+ if(screen->buffers[BUFIDX_ALTSCREEN])
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_ALTSCREEN]);
+
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ vterm_allocator_free(screen->vt, screen);
+}
+
+void vterm_screen_reset(VTermScreen *screen, int hard)
+{
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+ vterm_state_reset(screen->state, hard);
+ vterm_screen_flush_damage(screen);
+}
+
+static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
+{
+ size_t outpos = 0;
+ int padding = 0;
+
+#define PUT(c) \
+ if(utf8) { \
+ size_t thislen = utf8_seqlen(c); \
+ if(buffer && outpos + thislen <= len) \
+ outpos += fill_utf8((c), (char *)buffer + outpos); \
+ else \
+ outpos += thislen; \
+ } \
+ else { \
+ if(buffer && outpos + 1 <= len) \
+ ((uint32_t*)buffer)[outpos++] = (c); \
+ else \
+ outpos++; \
+ }
+
+ for(int row = rect.start_row; row < rect.end_row; row++) {
+ for(int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if(cell->chars[0] == 0)
+ // Erased cell, might need a space
+ padding++;
+ else if(cell->chars[0] == (uint32_t)-1)
+ // Gap behind a double-width char, do nothing
+ ;
+ else {
+ while(padding) {
+ PUT(UNICODE_SPACE);
+ padding--;
+ }
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
+ PUT(cell->chars[i]);
+ }
+ }
+ }
+
+ if(row < rect.end_row - 1) {
+ PUT(UNICODE_LINEFEED);
+ padding = 0;
+ }
+ }
+
+ return outpos;
+}
+
+size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
+{
+ return _get_chars(screen, 0, chars, len, rect);
+}
+
+size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
+{
+ return _get_chars(screen, 1, str, len, rect);
+}
+
+/* Copy internal to external representation of a screen cell */
+int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
+{
+ ScreenCell *intcell = getcell(screen, pos.row, pos.col);
+ if(!intcell)
+ return 0;
+
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
+ cell->chars[i] = intcell->chars[i];
+ if(!intcell->chars[i])
+ break;
+ }
+
+ cell->attrs.bold = intcell->pen.bold;
+ cell->attrs.underline = intcell->pen.underline;
+ cell->attrs.italic = intcell->pen.italic;
+ cell->attrs.blink = intcell->pen.blink;
+ cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
+ cell->attrs.conceal = intcell->pen.conceal;
+ cell->attrs.strike = intcell->pen.strike;
+ cell->attrs.font = intcell->pen.font;
+ cell->attrs.small = intcell->pen.small;
+ cell->attrs.baseline = intcell->pen.baseline;
+
+ cell->attrs.dwl = intcell->pen.dwl;
+ cell->attrs.dhl = intcell->pen.dhl;
+
+ cell->fg = intcell->pen.fg;
+ cell->bg = intcell->pen.bg;
+
+ if(pos.col < (screen->cols - 1) &&
+ getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1)
+ cell->width = 2;
+ else
+ cell->width = 1;
+
+ return 1;
+}
+
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
+{
+ /* This cell is EOL if this and every cell to the right is black */
+ for(; pos.col < screen->cols; pos.col++) {
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+ if(cell->chars[0] != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+VTermScreen *vterm_obtain_screen(VTerm *vt)
+{
+ if(vt->screen)
+ return vt->screen;
+
+ VTermScreen *screen = screen_new(vt);
+ vt->screen = screen;
+
+ return screen;
+}
+
+void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow)
+{
+ screen->reflow = reflow;
+}
+
+#undef vterm_screen_set_reflow
+void vterm_screen_set_reflow(VTermScreen *screen, bool reflow)
+{
+ vterm_screen_enable_reflow(screen, reflow);
+}
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
+{
+ if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
+ int rows, cols;
+ vterm_get_size(screen->vt, &rows, &cols);
+
+ screen->buffers[BUFIDX_ALTSCREEN] = alloc_buffer(screen, rows, cols);
+ }
+}
+
+void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user)
+{
+ screen->callbacks = callbacks;
+ screen->cbdata = user;
+}
+
+void *vterm_screen_get_cbdata(VTermScreen *screen)
+{
+ return screen->cbdata;
+}
+
+void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
+{
+ vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
+}
+
+void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen)
+{
+ return vterm_state_get_unrecognised_fbdata(screen->state);
+}
+
+void vterm_screen_flush_damage(VTermScreen *screen)
+{
+ if(screen->pending_scrollrect.start_row != -1) {
+ vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward,
+ moverect_user, erase_user, screen);
+
+ screen->pending_scrollrect.start_row = -1;
+ }
+
+ if(screen->damaged.start_row != -1) {
+ if(screen->callbacks && screen->callbacks->damage)
+ (*screen->callbacks->damage)(screen->damaged, screen->cbdata);
+
+ screen->damaged.start_row = -1;
+ }
+}
+
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
+{
+ vterm_screen_flush_damage(screen);
+ screen->damage_merge = size;
+}
+
+static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
+{
+ if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
+ return 1;
+ if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
+ return 1;
+ if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
+ return 1;
+ if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
+ return 1;
+ if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
+ return 1;
+ if((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal))
+ return 1;
+ if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
+ return 1;
+ if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
+ return 1;
+ if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg))
+ return 1;
+ if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
+ return 1;
+ if((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small))
+ return 1;
+ if((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline))
+ return 1;
+
+ return 0;
+}
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
+{
+ ScreenCell *target = getcell(screen, pos.row, pos.col);
+
+ // TODO: bounds check
+ extent->start_row = pos.row;
+ extent->end_row = pos.row + 1;
+
+ if(extent->start_col < 0)
+ extent->start_col = 0;
+ if(extent->end_col < 0)
+ extent->end_col = screen->cols;
+
+ int col;
+
+ for(col = pos.col - 1; col >= extent->start_col; col--)
+ if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
+ break;
+ extent->start_col = col + 1;
+
+ for(col = pos.col + 1; col < extent->end_col; col++)
+ if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
+ break;
+ extent->end_col = col - 1;
+
+ return 1;
+}
+
+void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
+{
+ vterm_state_convert_color_to_rgb(screen->state, col);
+}
+
+static void reset_default_colours(VTermScreen *screen, ScreenCell *buffer)
+{
+ for(int row = 0; row <= screen->rows - 1; row++)
+ for(int col = 0; col <= screen->cols - 1; col++) {
+ ScreenCell *cell = &buffer[row * screen->cols + col];
+ if(VTERM_COLOR_IS_DEFAULT_FG(&cell->pen.fg))
+ cell->pen.fg = screen->pen.fg;
+ if(VTERM_COLOR_IS_DEFAULT_BG(&cell->pen.bg))
+ cell->pen.bg = screen->pen.bg;
+ }
+}
+
+void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg)
+{
+ vterm_state_set_default_colors(screen->state, default_fg, default_bg);
+
+ if(default_fg && VTERM_COLOR_IS_DEFAULT_FG(&screen->pen.fg)) {
+ screen->pen.fg = *default_fg;
+ screen->pen.fg.type = (screen->pen.fg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_FG;
+ }
+
+ if(default_bg && VTERM_COLOR_IS_DEFAULT_BG(&screen->pen.bg)) {
+ screen->pen.bg = *default_bg;
+ screen->pen.bg.type = (screen->pen.bg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_BG;
+ }
+
+ reset_default_colours(screen, screen->buffers[0]);
+ if(screen->buffers[1])
+ reset_default_colours(screen, screen->buffers[1]);
+}
diff --git a/src/libs/3rdparty/libvterm/src/state.c b/src/libs/3rdparty/libvterm/src/state.c
new file mode 100644
index 0000000000..313e746e77
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/state.c
@@ -0,0 +1,2315 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define strneq(a,b,n) (strncmp(a,b,n)==0)
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_GLYPH_COMBINE
+#endif
+
+/* Some convenient wrappers to make callback functions easier */
+
+static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
+{
+ VTermGlyphInfo info = {
+ .chars = chars,
+ .width = width,
+ .protected_cell = state->protected_cell,
+ .dwl = state->lineinfo[pos.row].doublewidth,
+ .dhl = state->lineinfo[pos.row].doubleheight,
+ };
+
+ if(state->callbacks && state->callbacks->putglyph)
+ if((*state->callbacks->putglyph)(&info, pos, state->cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
+}
+
+static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
+{
+ if(state->pos.col == oldpos->col && state->pos.row == oldpos->row)
+ return;
+
+ if(cancel_phantom)
+ state->at_phantom = 0;
+
+ if(state->callbacks && state->callbacks->movecursor)
+ if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata))
+ return;
+}
+
+static void erase(VTermState *state, VTermRect rect, int selective)
+{
+ if(rect.end_col == state->cols) {
+ /* If we're erasing the final cells of any lines, cancel the continuation
+ * marker on the subsequent line
+ */
+ for(int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
+ state->lineinfo[row].continuation = 0;
+ }
+
+ if(state->callbacks && state->callbacks->erase)
+ if((*state->callbacks->erase)(rect, selective, state->cbdata))
+ return;
+}
+
+static VTermState *vterm_state_new(VTerm *vt)
+{
+ VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
+
+ state->vt = vt;
+
+ state->rows = vt->rows;
+ state->cols = vt->cols;
+
+ state->mouse_col = 0;
+ state->mouse_row = 0;
+ state->mouse_buttons = 0;
+
+ state->mouse_protocol = MOUSE_X10;
+
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+
+ state->selection.callbacks = NULL;
+ state->selection.user = NULL;
+ state->selection.buffer = NULL;
+
+ vterm_state_newpen(state);
+
+ state->bold_is_highbright = 0;
+
+ state->combine_chars_size = 16;
+ state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
+
+ state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
+
+ state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
+ /* TODO: Make an 'enable' function */
+ state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
+ state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
+
+ state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
+ if(*state->encoding_utf8.enc->init)
+ (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
+
+ return state;
+}
+
+INTERNAL void vterm_state_free(VTermState *state)
+{
+ vterm_allocator_free(state->vt, state->tabstops);
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
+ if(state->lineinfos[BUFIDX_ALTSCREEN])
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
+ vterm_allocator_free(state->vt, state->combine_chars);
+ vterm_allocator_free(state->vt, state);
+}
+
+static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
+{
+ if(!downward && !rightward)
+ return;
+
+ int rows = rect.end_row - rect.start_row;
+ if(downward > rows)
+ downward = rows;
+ else if(downward < -rows)
+ downward = -rows;
+
+ int cols = rect.end_col - rect.start_col;
+ if(rightward > cols)
+ rightward = cols;
+ else if(rightward < -cols)
+ rightward = -cols;
+
+ // Update lineinfo if full line
+ if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
+ int height = rect.end_row - rect.start_row - abs(downward);
+
+ if(downward > 0) {
+ memmove(state->lineinfo + rect.start_row,
+ state->lineinfo + rect.start_row + downward,
+ height * sizeof(state->lineinfo[0]));
+ for(int row = rect.end_row - downward; row < rect.end_row; row++)
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ else {
+ memmove(state->lineinfo + rect.start_row - downward,
+ state->lineinfo + rect.start_row,
+ height * sizeof(state->lineinfo[0]));
+ for(int row = rect.start_row; row < rect.start_row - downward; row++)
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ }
+
+ if(state->callbacks && state->callbacks->scrollrect)
+ if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata))
+ return;
+
+ if(state->callbacks)
+ vterm_scroll_rect(rect, downward, rightward,
+ state->callbacks->moverect, state->callbacks->erase, state->cbdata);
+}
+
+static void linefeed(VTermState *state)
+{
+ if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, 1, 0);
+ }
+ else if(state->pos.row < state->rows-1)
+ state->pos.row++;
+}
+
+static void grow_combine_buffer(VTermState *state)
+{
+ size_t new_size = state->combine_chars_size * 2;
+ uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0]));
+
+ memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0]));
+
+ vterm_allocator_free(state->vt, state->combine_chars);
+
+ state->combine_chars = new_chars;
+ state->combine_chars_size = new_size;
+}
+
+static void set_col_tabstop(VTermState *state, int col)
+{
+ unsigned char mask = 1 << (col & 7);
+ state->tabstops[col >> 3] |= mask;
+}
+
+static void clear_col_tabstop(VTermState *state, int col)
+{
+ unsigned char mask = 1 << (col & 7);
+ state->tabstops[col >> 3] &= ~mask;
+}
+
+static int is_col_tabstop(VTermState *state, int col)
+{
+ unsigned char mask = 1 << (col & 7);
+ return state->tabstops[col >> 3] & mask;
+}
+
+static int is_cursor_in_scrollregion(const VTermState *state)
+{
+ if(state->pos.row < state->scrollregion_top ||
+ state->pos.row >= SCROLLREGION_BOTTOM(state))
+ return 0;
+ if(state->pos.col < SCROLLREGION_LEFT(state) ||
+ state->pos.col >= SCROLLREGION_RIGHT(state))
+ return 0;
+
+ return 1;
+}
+
+static void tab(VTermState *state, int count, int direction)
+{
+ while(count > 0) {
+ if(direction > 0) {
+ if(state->pos.col >= THISROWWIDTH(state)-1)
+ return;
+
+ state->pos.col++;
+ }
+ else if(direction < 0) {
+ if(state->pos.col < 1)
+ return;
+
+ state->pos.col--;
+ }
+
+ if(is_col_tabstop(state, state->pos.col))
+ count--;
+ }
+}
+
+#define NO_FORCE 0
+#define FORCE 1
+
+#define DWL_OFF 0
+#define DWL_ON 1
+
+#define DHL_OFF 0
+#define DHL_TOP 1
+#define DHL_BOTTOM 2
+
+static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
+{
+ VTermLineInfo info = state->lineinfo[row];
+
+ if(dwl == DWL_OFF)
+ info.doublewidth = DWL_OFF;
+ else if(dwl == DWL_ON)
+ info.doublewidth = DWL_ON;
+ // else -1 to ignore
+
+ if(dhl == DHL_OFF)
+ info.doubleheight = DHL_OFF;
+ else if(dhl == DHL_TOP)
+ info.doubleheight = DHL_TOP;
+ else if(dhl == DHL_BOTTOM)
+ info.doubleheight = DHL_BOTTOM;
+
+ if((state->callbacks &&
+ state->callbacks->setlineinfo &&
+ (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
+ || force)
+ state->lineinfo[row] = info;
+}
+
+static int on_text(const char bytes[], size_t len, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
+ size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
+
+ int npoints = 0;
+ size_t eaten = 0;
+
+ VTermEncodingInstance *encoding =
+ state->gsingle_set ? &state->encoding[state->gsingle_set] :
+ !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] :
+ state->vt->mode.utf8 ? &state->encoding_utf8 :
+ &state->encoding[state->gr_set];
+
+ (*encoding->enc->decode)(encoding->enc, encoding->data,
+ codepoints, &npoints, state->gsingle_set ? 1 : maxpoints,
+ bytes, &eaten, len);
+
+ /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet
+ * for even a single codepoint
+ */
+ if(!npoints)
+ return eaten;
+
+ if(state->gsingle_set && npoints)
+ state->gsingle_set = 0;
+
+ int i = 0;
+
+ /* This is a combining char. that needs to be merged with the previous
+ * glyph output */
+ if(vterm_unicode_is_combining(codepoints[i])) {
+ /* See if the cursor has moved since */
+ if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) {
+#ifdef DEBUG_GLYPH_COMBINE
+ int printpos;
+ printf("DEBUG: COMBINING SPLIT GLYPH of chars {");
+ for(printpos = 0; state->combine_chars[printpos]; printpos++)
+ printf("U+%04x ", state->combine_chars[printpos]);
+ printf("} + {");
+#endif
+
+ /* Find where we need to append these combining chars */
+ int saved_i = 0;
+ while(state->combine_chars[saved_i])
+ saved_i++;
+
+ /* Add extra ones */
+ while(i < npoints && vterm_unicode_is_combining(codepoints[i])) {
+ if(saved_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[saved_i++] = codepoints[i++];
+ }
+ if(saved_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[saved_i] = 0;
+
+#ifdef DEBUG_GLYPH_COMBINE
+ for(; state->combine_chars[printpos]; printpos++)
+ printf("U+%04x ", state->combine_chars[printpos]);
+ printf("}\n");
+#endif
+
+ /* Now render it */
+ putglyph(state, state->combine_chars, state->combine_width, state->combine_pos);
+ }
+ else {
+ DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
+ }
+ }
+
+ for(; i < npoints; i++) {
+ // Try to find combining characters following this
+ int glyph_starts = i;
+ int glyph_ends;
+ for(glyph_ends = i + 1;
+ (glyph_ends < npoints) && (glyph_ends < glyph_starts + VTERM_MAX_CHARS_PER_CELL);
+ glyph_ends++)
+ if(!vterm_unicode_is_combining(codepoints[glyph_ends]))
+ break;
+
+ int width = 0;
+
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL + 1];
+
+ for( ; i < glyph_ends; i++) {
+ chars[i - glyph_starts] = codepoints[i];
+ int this_width = vterm_unicode_width(codepoints[i]);
+#ifdef DEBUG
+ if(this_width < 0) {
+ fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]);
+ abort();
+ }
+#endif
+ width += this_width;
+ }
+
+ while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
+ i++;
+
+ chars[glyph_ends - glyph_starts] = 0;
+ i--;
+
+#ifdef DEBUG_GLYPH_COMBINE
+ int printpos;
+ printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts);
+ for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++)
+ printf("U+%04x ", chars[printpos]);
+ printf("}, onscreen width %d\n", width);
+#endif
+
+ if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
+ linefeed(state);
+ state->pos.col = 0;
+ state->at_phantom = 0;
+ state->lineinfo[state->pos.row].continuation = 1;
+ }
+
+ if(state->mode.insert) {
+ /* TODO: This will be a little inefficient for large bodies of text, as
+ * it'll have to 'ICH' effectively before every glyph. We should scan
+ * ahead and ICH as many times as required
+ */
+ VTermRect rect = {
+ .start_row = state->pos.row,
+ .end_row = state->pos.row + 1,
+ .start_col = state->pos.col,
+ .end_col = THISROWWIDTH(state),
+ };
+ scroll(state, rect, 0, -1);
+ }
+
+ putglyph(state, chars, width, state->pos);
+
+ if(i == npoints - 1) {
+ /* End of the buffer. Save the chars in case we have to combine with
+ * more on the next call */
+ int save_i;
+ for(save_i = 0; chars[save_i]; save_i++) {
+ if(save_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[save_i] = chars[save_i];
+ }
+ if(save_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[save_i] = 0;
+ state->combine_width = width;
+ state->combine_pos = state->pos;
+ }
+
+ if(state->pos.col + width >= THISROWWIDTH(state)) {
+ if(state->mode.autowrap)
+ state->at_phantom = 1;
+ }
+ else {
+ state->pos.col += width;
+ }
+ }
+
+ updatecursor(state, &oldpos, 0);
+
+#ifdef DEBUG
+ if(state->pos.row < 0 || state->pos.row >= state->rows ||
+ state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
+ state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return eaten;
+}
+
+static int on_control(unsigned char control, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ switch(control) {
+ case 0x07: // BEL - ECMA-48 8.3.3
+ if(state->callbacks && state->callbacks->bell)
+ (*state->callbacks->bell)(state->cbdata);
+ break;
+
+ case 0x08: // BS - ECMA-48 8.3.5
+ if(state->pos.col > 0)
+ state->pos.col--;
+ break;
+
+ case 0x09: // HT - ECMA-48 8.3.60
+ tab(state, 1, +1);
+ break;
+
+ case 0x0a: // LF - ECMA-48 8.3.74
+ case 0x0b: // VT
+ case 0x0c: // FF
+ linefeed(state);
+ if(state->mode.newline)
+ state->pos.col = 0;
+ break;
+
+ case 0x0d: // CR - ECMA-48 8.3.15
+ state->pos.col = 0;
+ break;
+
+ case 0x0e: // LS1 - ECMA-48 8.3.76
+ state->gl_set = 1;
+ break;
+
+ case 0x0f: // LS0 - ECMA-48 8.3.75
+ state->gl_set = 0;
+ break;
+
+ case 0x84: // IND - DEPRECATED but implemented for completeness
+ linefeed(state);
+ break;
+
+ case 0x85: // NEL - ECMA-48 8.3.86
+ linefeed(state);
+ state->pos.col = 0;
+ break;
+
+ case 0x88: // HTS - ECMA-48 8.3.62
+ set_col_tabstop(state, state->pos.col);
+ break;
+
+ case 0x8d: // RI - ECMA-48 8.3.104
+ if(state->pos.row == state->scrollregion_top) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, -1, 0);
+ }
+ else if(state->pos.row > 0)
+ state->pos.row--;
+ break;
+
+ case 0x8e: // SS2 - ECMA-48 8.3.141
+ state->gsingle_set = 2;
+ break;
+
+ case 0x8f: // SS3 - ECMA-48 8.3.142
+ state->gsingle_set = 3;
+ break;
+
+ default:
+ if(state->fallbacks && state->fallbacks->control)
+ if((*state->fallbacks->control)(control, state->fbdata))
+ return 1;
+
+ return 0;
+ }
+
+ updatecursor(state, &oldpos, 1);
+
+#ifdef DEBUG
+ if(state->pos.row < 0 || state->pos.row >= state->rows ||
+ state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
+ control, state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static int settermprop_bool(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .boolean = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_int(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .number = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
+{
+ VTermValue val = { .string = frag };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static void savecursor(VTermState *state, int save)
+{
+ if(save) {
+ state->saved.pos = state->pos;
+ state->saved.mode.cursor_visible = state->mode.cursor_visible;
+ state->saved.mode.cursor_blink = state->mode.cursor_blink;
+ state->saved.mode.cursor_shape = state->mode.cursor_shape;
+
+ vterm_state_savepen(state, 1);
+ }
+ else {
+ VTermPos oldpos = state->pos;
+
+ state->pos = state->saved.pos;
+
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
+
+ vterm_state_savepen(state, 0);
+
+ updatecursor(state, &oldpos, 1);
+ }
+}
+
+static int on_escape(const char *bytes, size_t len, void *user)
+{
+ VTermState *state = user;
+
+ /* Easier to decode this from the first byte, even though the final
+ * byte terminates it
+ */
+ switch(bytes[0]) {
+ case ' ':
+ if(len != 2)
+ return 0;
+
+ switch(bytes[1]) {
+ case 'F': // S7C1T
+ state->vt->mode.ctrl8bit = 0;
+ break;
+
+ case 'G': // S8C1T
+ state->vt->mode.ctrl8bit = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '#':
+ if(len != 2)
+ return 0;
+
+ switch(bytes[1]) {
+ case '3': // DECDHL top
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
+ break;
+
+ case '4': // DECDHL bottom
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
+ break;
+
+ case '5': // DECSWL
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
+ break;
+
+ case '6': // DECDWL
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
+ break;
+
+ case '8': // DECALN
+ {
+ VTermPos pos;
+ uint32_t E[] = { 'E', 0 };
+ for(pos.row = 0; pos.row < state->rows; pos.row++)
+ for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++)
+ putglyph(state, E, 1, pos);
+ break;
+ }
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '(': case ')': case '*': case '+': // SCS
+ if(len != 2)
+ return 0;
+
+ {
+ int setnum = bytes[0] - 0x28;
+ VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
+
+ if(newenc) {
+ state->encoding[setnum].enc = newenc;
+
+ if(newenc->init)
+ (*newenc->init)(newenc, state->encoding[setnum].data);
+ }
+ }
+
+ return 2;
+
+ case '7': // DECSC
+ savecursor(state, 1);
+ return 1;
+
+ case '8': // DECRC
+ savecursor(state, 0);
+ return 1;
+
+ case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
+ return 1;
+
+ case '=': // DECKPAM
+ state->mode.keypad = 1;
+ return 1;
+
+ case '>': // DECKPNM
+ state->mode.keypad = 0;
+ return 1;
+
+ case 'c': // RIS - ECMA-48 8.3.105
+ {
+ VTermPos oldpos = state->pos;
+ vterm_state_reset(state, 1);
+ if(state->callbacks && state->callbacks->movecursor)
+ (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata);
+ return 1;
+ }
+
+ case 'n': // LS2 - ECMA-48 8.3.78
+ state->gl_set = 2;
+ return 1;
+
+ case 'o': // LS3 - ECMA-48 8.3.80
+ state->gl_set = 3;
+ return 1;
+
+ case '~': // LS1R - ECMA-48 8.3.77
+ state->gr_set = 1;
+ return 1;
+
+ case '}': // LS2R - ECMA-48 8.3.79
+ state->gr_set = 2;
+ return 1;
+
+ case '|': // LS3R - ECMA-48 8.3.81
+ state->gr_set = 3;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void set_mode(VTermState *state, int num, int val)
+{
+ switch(num) {
+ case 4: // IRM - ECMA-48 7.2.10
+ state->mode.insert = val;
+ break;
+
+ case 20: // LNM - ANSI X3.4-1977
+ state->mode.newline = val;
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown mode %d\n", num);
+ return;
+ }
+}
+
+static void set_dec_mode(VTermState *state, int num, int val)
+{
+ switch(num) {
+ case 1:
+ state->mode.cursor = val;
+ break;
+
+ case 5: // DECSCNM - screen mode
+ settermprop_bool(state, VTERM_PROP_REVERSE, val);
+ break;
+
+ case 6: // DECOM - origin mode
+ {
+ VTermPos oldpos = state->pos;
+ state->mode.origin = val;
+ state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
+ state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
+ updatecursor(state, &oldpos, 1);
+ }
+ break;
+
+ case 7:
+ state->mode.autowrap = val;
+ break;
+
+ case 12:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
+ break;
+
+ case 25:
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
+ break;
+
+ case 69: // DECVSSM - vertical split screen mode
+ // DECLRMM - left/right margin mode
+ state->mode.leftrightmargin = val;
+ if(val) {
+ // Setting DECVSSM must clear doublewidth/doubleheight state of every line
+ for(int row = 0; row < state->rows; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ }
+
+ break;
+
+ case 1000:
+ case 1002:
+ case 1003:
+ settermprop_int(state, VTERM_PROP_MOUSE,
+ !val ? VTERM_PROP_MOUSE_NONE :
+ (num == 1000) ? VTERM_PROP_MOUSE_CLICK :
+ (num == 1002) ? VTERM_PROP_MOUSE_DRAG :
+ VTERM_PROP_MOUSE_MOVE);
+ break;
+
+ case 1004:
+ state->mode.report_focus = val;
+ break;
+
+ case 1005:
+ state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
+ break;
+
+ case 1006:
+ state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
+ break;
+
+ case 1015:
+ state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
+ break;
+
+ case 1047:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ break;
+
+ case 1048:
+ savecursor(state, val);
+ break;
+
+ case 1049:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ savecursor(state, val);
+ break;
+
+ case 2004:
+ state->mode.bracketpaste = val;
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
+ return;
+ }
+}
+
+static void request_dec_mode(VTermState *state, int num)
+{
+ int reply;
+
+ switch(num) {
+ case 1:
+ reply = state->mode.cursor;
+ break;
+
+ case 5:
+ reply = state->mode.screen;
+ break;
+
+ case 6:
+ reply = state->mode.origin;
+ break;
+
+ case 7:
+ reply = state->mode.autowrap;
+ break;
+
+ case 12:
+ reply = state->mode.cursor_blink;
+ break;
+
+ case 25:
+ reply = state->mode.cursor_visible;
+ break;
+
+ case 69:
+ reply = state->mode.leftrightmargin;
+ break;
+
+ case 1000:
+ reply = state->mouse_flags == MOUSE_WANT_CLICK;
+ break;
+
+ case 1002:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
+ break;
+
+ case 1003:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
+ break;
+
+ case 1004:
+ reply = state->mode.report_focus;
+ break;
+
+ case 1005:
+ reply = state->mouse_protocol == MOUSE_UTF8;
+ break;
+
+ case 1006:
+ reply = state->mouse_protocol == MOUSE_SGR;
+ break;
+
+ case 1015:
+ reply = state->mouse_protocol == MOUSE_RXVT;
+ break;
+
+ case 1047:
+ reply = state->mode.alt_screen;
+ break;
+
+ case 2004:
+ reply = state->mode.bracketpaste;
+ break;
+
+ default:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
+ return;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
+}
+
+static void request_version_string(VTermState *state)
+{
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, ">|libvterm(%d.%d)",
+ VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR);
+}
+
+static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
+{
+ VTermState *state = user;
+ int leader_byte = 0;
+ int intermed_byte = 0;
+ int cancel_phantom = 1;
+
+ if(leader && leader[0]) {
+ if(leader[1]) // longer than 1 char
+ return 0;
+
+ switch(leader[0]) {
+ case '?':
+ case '>':
+ leader_byte = leader[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if(intermed && intermed[0]) {
+ if(intermed[1]) // longer than 1 char
+ return 0;
+
+ switch(intermed[0]) {
+ case ' ':
+ case '"':
+ case '$':
+ case '\'':
+ intermed_byte = intermed[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ VTermPos oldpos = state->pos;
+
+ // Some temporaries for later code
+ int count, val;
+ int row, col;
+ VTermRect rect;
+ int selective;
+
+#define LBOUND(v,min) if((v) < (min)) (v) = (min)
+#define UBOUND(v,max) if((v) > (max)) (v) = (max)
+
+#define LEADER(l,b) ((l << 8) | b)
+#define INTERMED(i,b) ((i << 16) | b)
+
+ switch(intermed_byte << 16 | leader_byte << 8 | command) {
+ case 0x40: // ICH - ECMA-48 8.3.64
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if(state->mode.leftrightmargin)
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ else
+ rect.end_col = THISROWWIDTH(state);
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case 0x41: // CUU - ECMA-48 8.3.22
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x42: // CUD - ECMA-48 8.3.19
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x43: // CUF - ECMA-48 8.3.20
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x44: // CUB - ECMA-48 8.3.18
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x45: // CNL - ECMA-48 8.3.12
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x46: // CPL - ECMA-48 8.3.13
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x47: // CHA - ECMA-48 8.3.9
+ val = CSI_ARG_OR(args[0], 1);
+ state->pos.col = val-1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x48: // CUP - ECMA-48 8.3.21
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row-1;
+ state->pos.col = col-1;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x49: // CHT - ECMA-48 8.3.10
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, +1);
+ break;
+
+ case 0x4a: // ED - ECMA-48 8.3.39
+ case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
+ selective = (leader_byte == '?');
+ switch(CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col; rect.end_col = state->cols;
+ if(rect.end_col > rect.start_col)
+ erase(state, rect, selective);
+
+ rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
+ rect.start_col = 0;
+ for(int row = rect.start_row; row < rect.end_row; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ if(rect.end_row > rect.start_row)
+ erase(state, rect, selective);
+ break;
+
+ case 1:
+ rect.start_row = 0; rect.end_row = state->pos.row;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for(int row = rect.start_row; row < rect.end_row; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ if(rect.end_col > rect.start_col)
+ erase(state, rect, selective);
+
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.end_col = state->pos.col + 1;
+ if(rect.end_row > rect.start_row)
+ erase(state, rect, selective);
+ break;
+
+ case 2:
+ rect.start_row = 0; rect.end_row = state->rows;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for(int row = rect.start_row; row < rect.end_row; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ erase(state, rect, selective);
+ break;
+
+ case 3:
+ if(state->callbacks && state->callbacks->sb_clear)
+ if((*state->callbacks->sb_clear)(state->cbdata))
+ return 1;
+ break;
+ }
+ break;
+
+ case 0x4b: // EL - ECMA-48 8.3.41
+ case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
+ selective = (leader_byte == '?');
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+
+ switch(CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
+ case 1:
+ rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
+ case 2:
+ rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
+ default:
+ return 0;
+ }
+
+ if(rect.end_col > rect.start_col)
+ erase(state, rect, selective);
+
+ break;
+
+ case 0x4c: // IL - ECMA-48 8.3.67
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x4d: // DL - ECMA-48 8.3.32
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x50: // DCH - ECMA-48 8.3.26
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if(state->mode.leftrightmargin)
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ else
+ rect.end_col = THISROWWIDTH(state);
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ case 0x53: // SU - ECMA-48 8.3.147
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x54: // SD - ECMA-48 8.3.113
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x58: // ECH - ECMA-48 8.3.38
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ rect.end_col = state->pos.col + count;
+ UBOUND(rect.end_col, THISROWWIDTH(state));
+
+ erase(state, rect, 0);
+ break;
+
+ case 0x5a: // CBT - ECMA-48 8.3.7
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, -1);
+ break;
+
+ case 0x60: // HPA - ECMA-48 8.3.57
+ col = CSI_ARG_OR(args[0], 1);
+ state->pos.col = col-1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x61: // HPR - ECMA-48 8.3.59
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x62: { // REP - ECMA-48 8.3.103
+ const int row_width = THISROWWIDTH(state);
+ count = CSI_ARG_COUNT(args[0]);
+ col = state->pos.col + count;
+ UBOUND(col, row_width);
+ while (state->pos.col < col) {
+ putglyph(state, state->combine_chars, state->combine_width, state->pos);
+ state->pos.col += state->combine_width;
+ }
+ if (state->pos.col + state->combine_width >= row_width) {
+ if (state->mode.autowrap) {
+ state->at_phantom = 1;
+ cancel_phantom = 0;
+ }
+ }
+ break;
+ }
+
+ case 0x63: // DA - ECMA-48 8.3.24
+ val = CSI_ARG_OR(args[0], 0);
+ if(val == 0)
+ // DEC VT100 response
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
+ break;
+
+ case LEADER('>', 0x63): // DEC secondary Device Attributes
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
+ break;
+
+ case 0x64: // VPA - ECMA-48 8.3.158
+ row = CSI_ARG_OR(args[0], 1);
+ state->pos.row = row-1;
+ if(state->mode.origin)
+ state->pos.row += state->scrollregion_top;
+ state->at_phantom = 0;
+ break;
+
+ case 0x65: // VPR - ECMA-48 8.3.160
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x66: // HVP - ECMA-48 8.3.63
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row-1;
+ state->pos.col = col-1;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x67: // TBC - ECMA-48 8.3.154
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch(val) {
+ case 0:
+ clear_col_tabstop(state, state->pos.col);
+ break;
+ case 3:
+ case 5:
+ for(col = 0; col < state->cols; col++)
+ clear_col_tabstop(state, col);
+ break;
+ case 1:
+ case 2:
+ case 4:
+ break;
+ /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */
+ default:
+ return 0;
+ }
+ break;
+
+ case 0x68: // SM - ECMA-48 8.3.125
+ if(!CSI_ARG_IS_MISSING(args[0]))
+ set_mode(state, CSI_ARG(args[0]), 1);
+ break;
+
+ case LEADER('?', 0x68): // DEC private mode set
+ if(!CSI_ARG_IS_MISSING(args[0]))
+ set_dec_mode(state, CSI_ARG(args[0]), 1);
+ break;
+
+ case 0x6a: // HPB - ECMA-48 8.3.58
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6b: // VPB - ECMA-48 8.3.159
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6c: // RM - ECMA-48 8.3.106
+ if(!CSI_ARG_IS_MISSING(args[0]))
+ set_mode(state, CSI_ARG(args[0]), 0);
+ break;
+
+ case LEADER('?', 0x6c): // DEC private mode reset
+ if(!CSI_ARG_IS_MISSING(args[0]))
+ set_dec_mode(state, CSI_ARG(args[0]), 0);
+ break;
+
+ case 0x6d: // SGR - ECMA-48 8.3.117
+ vterm_state_setpen(state, args, argcount);
+ break;
+
+ case LEADER('?', 0x6d): // DECSGR
+ /* No actual DEC terminal recognised these, but some printers did. These
+ * are alternative ways to request subscript/superscript/off
+ */
+ for(int argi = 0; argi < argcount; argi++) {
+ long arg;
+ switch(arg = CSI_ARG(args[argi])) {
+ case 4: // Superscript on
+ arg = 73;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 5: // Subscript on
+ arg = 74;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 24: // Super+subscript off
+ arg = 75;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ }
+ }
+ break;
+
+ case 0x6e: // DSR - ECMA-48 8.3.35
+ case LEADER('?', 0x6e): // DECDSR
+ val = CSI_ARG_OR(args[0], 0);
+
+ {
+ char *qmark = (leader_byte == '?') ? "?" : "";
+
+ switch(val) {
+ case 0: case 1: case 2: case 3: case 4:
+ // ignore - these are replies
+ break;
+ case 5:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
+ break;
+ case 6: // CPR - cursor position report
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1);
+ break;
+ }
+ }
+ break;
+
+
+ case LEADER('!', 0x70): // DECSTR - DEC soft terminal reset
+ vterm_state_reset(state, 0);
+ break;
+
+ case LEADER('?', INTERMED('$', 0x70)):
+ request_dec_mode(state, CSI_ARG(args[0]));
+ break;
+
+ case LEADER('>', 0x71): // XTVERSION - xterm query version string
+ request_version_string(state);
+ break;
+
+ case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
+ val = CSI_ARG_OR(args[0], 1);
+
+ switch(val) {
+ case 0: case 1:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 2:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 3:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 4:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 5:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ case 6:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ }
+
+ break;
+
+ case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch(val) {
+ case 0: case 2:
+ state->protected_cell = 0;
+ break;
+ case 1:
+ state->protected_cell = 1;
+ break;
+ }
+
+ break;
+
+ case 0x72: // DECSTBM - DEC custom
+ state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_top, 0);
+ UBOUND(state->scrollregion_top, state->rows);
+ LBOUND(state->scrollregion_bottom, -1);
+ if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows)
+ state->scrollregion_bottom = -1;
+ else
+ UBOUND(state->scrollregion_bottom, state->rows);
+
+ if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ // Invalid
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case 0x73: // DECSLRM - DEC custom
+ // Always allow setting these margins, just they won't take effect without DECVSSM
+ state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_left, 0);
+ UBOUND(state->scrollregion_left, state->cols);
+ LBOUND(state->scrollregion_right, -1);
+ if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols)
+ state->scrollregion_right = -1;
+ else
+ UBOUND(state->scrollregion_right, state->cols);
+
+ if(state->scrollregion_right > -1 &&
+ state->scrollregion_right <= state->scrollregion_left) {
+ // Invalid
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case INTERMED('\'', 0x7D): // DECIC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case INTERMED('\'', 0x7E): // DECDC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ default:
+ if(state->fallbacks && state->fallbacks->csi)
+ if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata))
+ return 1;
+
+ return 0;
+ }
+
+ if(state->mode.origin) {
+ LBOUND(state->pos.row, state->scrollregion_top);
+ UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1);
+ LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
+ UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1);
+ }
+ else {
+ LBOUND(state->pos.row, 0);
+ UBOUND(state->pos.row, state->rows-1);
+ LBOUND(state->pos.col, 0);
+ UBOUND(state->pos.col, THISROWWIDTH(state)-1);
+ }
+
+ updatecursor(state, &oldpos, cancel_phantom);
+
+#ifdef DEBUG
+ if(state->pos.row < 0 || state->pos.row >= state->rows ||
+ state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
+ command, state->pos.row, state->pos.col);
+ abort();
+ }
+
+ if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
+ abort();
+ }
+
+ if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
+ fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static char base64_one(uint8_t b)
+{
+ if(b < 26)
+ return 'A' + b;
+ else if(b < 52)
+ return 'a' + b - 26;
+ else if(b < 62)
+ return '0' + b - 52;
+ else if(b == 62)
+ return '+';
+ else if(b == 63)
+ return '/';
+ return 0;
+}
+
+static uint8_t unbase64one(char c)
+{
+ if(c >= 'A' && c <= 'Z')
+ return c - 'A';
+ else if(c >= 'a' && c <= 'z')
+ return c - 'a' + 26;
+ else if(c >= '0' && c <= '9')
+ return c - '0' + 52;
+ else if(c == '+')
+ return 62;
+ else if(c == '/')
+ return 63;
+
+ return 0xFF;
+}
+
+static void osc_selection(VTermState *state, VTermStringFragment frag)
+{
+ if(frag.initial) {
+ state->tmp.selection.mask = 0;
+ state->tmp.selection.state = SELECTION_INITIAL;
+ }
+
+ while(!state->tmp.selection.state && frag.len) {
+ /* Parse selection parameter */
+ switch(frag.str[0]) {
+ case 'c':
+ state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD;
+ break;
+ case 'p':
+ state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY;
+ break;
+ case 'q':
+ state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY;
+ break;
+ case 's':
+ state->tmp.selection.mask |= VTERM_SELECTION_SELECT;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
+ break;
+
+ case ';':
+ state->tmp.selection.state = SELECTION_SELECTED;
+ if(!state->tmp.selection.mask)
+ state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0;
+ break;
+ }
+
+ frag.str++;
+ frag.len--;
+ }
+
+ if(!frag.len)
+ return;
+
+ if(state->tmp.selection.state == SELECTION_SELECTED) {
+ if(frag.str[0] == '?') {
+ state->tmp.selection.state = SELECTION_QUERY;
+ }
+ else {
+ state->tmp.selection.state = SELECTION_SET_INITIAL;
+ state->tmp.selection.recvpartial = 0;
+ }
+ }
+
+ if(state->tmp.selection.state == SELECTION_QUERY) {
+ if(state->selection.callbacks->query)
+ (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
+ return;
+ }
+
+ if(state->selection.callbacks->set) {
+ size_t bufcur = 0;
+ char *buffer = state->selection.buffer;
+
+ uint32_t x = 0; /* Current decoding value */
+ int n = 0; /* Number of sextets consumed */
+
+ if(state->tmp.selection.recvpartial) {
+ n = state->tmp.selection.recvpartial >> 24;
+ x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */
+
+ state->tmp.selection.recvpartial = 0;
+ }
+
+ while((state->selection.buflen - bufcur) >= 3 && frag.len) {
+ if(frag.str[0] == '=') {
+ if(n == 2) {
+ buffer[0] = (x >> 4) & 0xFF;
+ buffer += 1, bufcur += 1;
+ }
+ if(n == 3) {
+ buffer[0] = (x >> 10) & 0xFF;
+ buffer[1] = (x >> 2) & 0xFF;
+ buffer += 2, bufcur += 2;
+ }
+
+ while(frag.len && frag.str[0] == '=')
+ frag.str++, frag.len--;
+
+ n = 0;
+ }
+ else {
+ uint8_t b = unbase64one(frag.str[0]);
+ if(b == 0xFF) {
+ DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
+ }
+ else {
+ x = (x << 6) | b;
+ n++;
+ }
+ frag.str++, frag.len--;
+
+ if(n == 4) {
+ buffer[0] = (x >> 16) & 0xFF;
+ buffer[1] = (x >> 8) & 0xFF;
+ buffer[2] = (x >> 0) & 0xFF;
+
+ buffer += 3, bufcur += 3;
+ x = 0;
+ n = 0;
+ }
+ }
+
+ if(!frag.len || (state->selection.buflen - bufcur) < 3) {
+ if(bufcur) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = state->selection.buffer,
+ .len = bufcur,
+ .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
+ .final = frag.final,
+ }, state->selection.user);
+ state->tmp.selection.state = SELECTION_SET;
+ }
+
+ buffer = state->selection.buffer;
+ bufcur = 0;
+ }
+ }
+
+ if(n)
+ state->tmp.selection.recvpartial = (n << 24) | x;
+ }
+}
+
+static int on_osc(int command, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ switch(command) {
+ case 0:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 1:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ return 1;
+
+ case 2:
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 52:
+ if(state->selection.callbacks)
+ osc_selection(state, frag);
+
+ return 1;
+
+ default:
+ if(state->fallbacks && state->fallbacks->osc)
+ if((*state->fallbacks->osc)(command, frag, state->fbdata))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void request_status_string(VTermState *state, VTermStringFragment frag)
+{
+ VTerm *vt = state->vt;
+
+ char *tmp = state->tmp.decrqss;
+
+ if(frag.initial)
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+
+ int i = 0;
+ while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
+ i++;
+ while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
+ tmp[i++] = (frag.str++)[0];
+ tmp[i] = 0;
+
+ if(!frag.final)
+ return;
+
+ switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
+ case 'm': {
+ // Query SGR
+ long args[20];
+ int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
+ size_t cur = 0;
+
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ for(int argi = 0; argi < argc; argi++) {
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ argi == argc - 1 ? "%ld" :
+ CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
+ "%ld;",
+ CSI_ARG(args[argi]));
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+ return;
+ }
+
+ case 'r':
+ // Query DECSTBM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
+ return;
+
+ case 's':
+ // Query DECSLRM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
+ return;
+
+ case ' '|('q'<<8): {
+ // Query DECSCUSR
+ int reply = 2;
+ switch(state->mode.cursor_shape) {
+ case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
+ case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break;
+ case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break;
+ }
+ if(state->mode.cursor_blink)
+ reply--;
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d q", reply);
+ return;
+ }
+
+ case '\"'|('q'<<8):
+ // Query DECSCA
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d\"q", state->protected_cell ? 1 : 2);
+ return;
+ }
+
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r");
+}
+
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(commandlen == 2 && strneq(command, "$q", 2)) {
+ request_status_string(state, frag);
+ return 1;
+ }
+ else if(state->fallbacks && state->fallbacks->dcs)
+ if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
+ return 1;
+
+ DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
+ return 0;
+}
+
+static int on_apc(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(state->fallbacks && state->fallbacks->apc)
+ if((*state->fallbacks->apc)(frag, state->fbdata))
+ return 1;
+
+ /* No DEBUG_LOG because all APCs are unhandled */
+ return 0;
+}
+
+static int on_pm(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(state->fallbacks && state->fallbacks->pm)
+ if((*state->fallbacks->pm)(frag, state->fbdata))
+ return 1;
+
+ /* No DEBUG_LOG because all PMs are unhandled */
+ return 0;
+}
+
+static int on_sos(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(state->fallbacks && state->fallbacks->sos)
+ if((*state->fallbacks->sos)(frag, state->fbdata))
+ return 1;
+
+ /* No DEBUG_LOG because all SOSs are unhandled */
+ return 0;
+}
+
+static int on_resize(int rows, int cols, void *user)
+{
+ VTermState *state = user;
+ VTermPos oldpos = state->pos;
+
+ if(cols != state->cols) {
+ unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
+
+ /* TODO: This can all be done much more efficiently bytewise */
+ int col;
+ for(col = 0; col < state->cols && col < cols; col++) {
+ unsigned char mask = 1 << (col & 7);
+ if(state->tabstops[col >> 3] & mask)
+ newtabstops[col >> 3] |= mask;
+ else
+ newtabstops[col >> 3] &= ~mask;
+ }
+
+ for( ; col < cols; col++) {
+ unsigned char mask = 1 << (col & 7);
+ if(col % 8 == 0)
+ newtabstops[col >> 3] |= mask;
+ else
+ newtabstops[col >> 3] &= ~mask;
+ }
+
+ vterm_allocator_free(state->vt, state->tabstops);
+ state->tabstops = newtabstops;
+ }
+
+ state->rows = rows;
+ state->cols = cols;
+
+ if(state->scrollregion_bottom > -1)
+ UBOUND(state->scrollregion_bottom, state->rows);
+ if(state->scrollregion_right > -1)
+ UBOUND(state->scrollregion_right, state->cols);
+
+ VTermStateFields fields = {
+ .pos = state->pos,
+ .lineinfos = { [0] = state->lineinfos[0], [1] = state->lineinfos[1] },
+ };
+
+ if(state->callbacks && state->callbacks->resize) {
+ (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
+ state->pos = fields.pos;
+
+ state->lineinfos[0] = fields.lineinfos[0];
+ state->lineinfos[1] = fields.lineinfos[1];
+ }
+ else {
+ if(rows != state->rows) {
+ for(int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
+ VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
+ if(!oldlineinfo)
+ continue;
+
+ VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
+
+ int row;
+ for(row = 0; row < state->rows && row < rows; row++) {
+ newlineinfo[row] = oldlineinfo[row];
+ }
+
+ for( ; row < rows; row++) {
+ newlineinfo[row] = (VTermLineInfo){
+ .doublewidth = 0,
+ };
+ }
+
+ vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
+ state->lineinfos[bufidx] = newlineinfo;
+ }
+ }
+ }
+
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+
+ if(state->at_phantom && state->pos.col < cols-1) {
+ state->at_phantom = 0;
+ state->pos.col++;
+ }
+
+ if(state->pos.row < 0)
+ state->pos.row = 0;
+ if(state->pos.row >= rows)
+ state->pos.row = rows - 1;
+ if(state->pos.col < 0)
+ state->pos.col = 0;
+ if(state->pos.col >= cols)
+ state->pos.col = cols - 1;
+
+ updatecursor(state, &oldpos, 1);
+
+ return 1;
+}
+
+static const VTermParserCallbacks parser_callbacks = {
+ .text = on_text,
+ .control = on_control,
+ .escape = on_escape,
+ .csi = on_csi,
+ .osc = on_osc,
+ .dcs = on_dcs,
+ .apc = on_apc,
+ .pm = on_pm,
+ .sos = on_sos,
+ .resize = on_resize,
+};
+
+VTermState *vterm_obtain_state(VTerm *vt)
+{
+ if(vt->state)
+ return vt->state;
+
+ VTermState *state = vterm_state_new(vt);
+ vt->state = state;
+
+ vterm_parser_set_callbacks(vt, &parser_callbacks, state);
+
+ return state;
+}
+
+void vterm_state_reset(VTermState *state, int hard)
+{
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+
+ state->mode.keypad = 0;
+ state->mode.cursor = 0;
+ state->mode.autowrap = 1;
+ state->mode.insert = 0;
+ state->mode.newline = 0;
+ state->mode.alt_screen = 0;
+ state->mode.origin = 0;
+ state->mode.leftrightmargin = 0;
+ state->mode.bracketpaste = 0;
+ state->mode.report_focus = 0;
+
+ state->mouse_flags = 0;
+
+ state->vt->mode.ctrl8bit = 0;
+
+ for(int col = 0; col < state->cols; col++)
+ if(col % 8 == 0)
+ set_col_tabstop(state, col);
+ else
+ clear_col_tabstop(state, col);
+
+ for(int row = 0; row < state->rows; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+
+ if(state->callbacks && state->callbacks->initpen)
+ (*state->callbacks->initpen)(state->cbdata);
+
+ vterm_state_resetpen(state);
+
+ VTermEncoding *default_enc = state->vt->mode.utf8 ?
+ vterm_lookup_encoding(ENC_UTF8, 'u') :
+ vterm_lookup_encoding(ENC_SINGLE_94, 'B');
+
+ for(int i = 0; i < 4; i++) {
+ state->encoding[i].enc = default_enc;
+ if(default_enc->init)
+ (*default_enc->init)(default_enc, state->encoding[i].data);
+ }
+
+ state->gl_set = 0;
+ state->gr_set = 1;
+ state->gsingle_set = 0;
+
+ state->protected_cell = 0;
+
+ // Initialise the props
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+
+ if(hard) {
+ state->pos.row = 0;
+ state->pos.col = 0;
+ state->at_phantom = 0;
+
+ VTermRect rect = { 0, state->rows, 0, state->cols };
+ erase(state, rect, 0);
+ }
+}
+
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
+{
+ *cursorpos = state->pos;
+}
+
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
+{
+ if(callbacks) {
+ state->callbacks = callbacks;
+ state->cbdata = user;
+
+ if(state->callbacks && state->callbacks->initpen)
+ (*state->callbacks->initpen)(state->cbdata);
+ }
+ else {
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+ }
+}
+
+void *vterm_state_get_cbdata(VTermState *state)
+{
+ return state->cbdata;
+}
+
+void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
+{
+ if(fallbacks) {
+ state->fallbacks = fallbacks;
+ state->fbdata = user;
+ }
+ else {
+ state->fallbacks = NULL;
+ state->fbdata = NULL;
+ }
+}
+
+void *vterm_state_get_unrecognised_fbdata(VTermState *state)
+{
+ return state->fbdata;
+}
+
+int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
+{
+ /* Only store the new value of the property if usercode said it was happy.
+ * This is especially important for altscreen switching */
+ if(state->callbacks && state->callbacks->settermprop)
+ if(!(*state->callbacks->settermprop)(prop, val, state->cbdata))
+ return 0;
+
+ switch(prop) {
+ case VTERM_PROP_TITLE:
+ case VTERM_PROP_ICONNAME:
+ // we don't store these, just transparently pass through
+ return 1;
+ case VTERM_PROP_CURSORVISIBLE:
+ state->mode.cursor_visible = val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORBLINK:
+ state->mode.cursor_blink = val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORSHAPE:
+ state->mode.cursor_shape = val->number;
+ return 1;
+ case VTERM_PROP_REVERSE:
+ state->mode.screen = val->boolean;
+ return 1;
+ case VTERM_PROP_ALTSCREEN:
+ state->mode.alt_screen = val->boolean;
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+ if(state->mode.alt_screen) {
+ VTermRect rect = {
+ .start_row = 0,
+ .start_col = 0,
+ .end_row = state->rows,
+ .end_col = state->cols,
+ };
+ erase(state, rect, 0);
+ }
+ return 1;
+ case VTERM_PROP_MOUSE:
+ state->mouse_flags = 0;
+ if(val->number)
+ state->mouse_flags |= MOUSE_WANT_CLICK;
+ if(val->number == VTERM_PROP_MOUSE_DRAG)
+ state->mouse_flags |= MOUSE_WANT_DRAG;
+ if(val->number == VTERM_PROP_MOUSE_MOVE)
+ state->mouse_flags |= MOUSE_WANT_MOVE;
+ return 1;
+
+ case VTERM_N_PROPS:
+ return 0;
+ }
+
+ return 0;
+}
+
+void vterm_state_focus_in(VTermState *state)
+{
+ if(state->mode.report_focus)
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
+}
+
+void vterm_state_focus_out(VTermState *state)
+{
+ if(state->mode.report_focus)
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
+}
+
+const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
+{
+ return state->lineinfo + row;
+}
+
+void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
+ char *buffer, size_t buflen)
+{
+ if(buflen && !buffer)
+ buffer = vterm_allocator_malloc(state->vt, buflen);
+
+ state->selection.callbacks = callbacks;
+ state->selection.user = user;
+ state->selection.buffer = buffer;
+ state->selection.buflen = buflen;
+}
+
+void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag)
+{
+ VTerm *vt = state->vt;
+
+ if(frag.initial) {
+ /* TODO: support sending more than one mask bit */
+ const static char selection_chars[] = "cpqs";
+ int idx;
+ for(idx = 0; idx < 4; idx++)
+ if(mask & (1 << idx))
+ break;
+
+ vterm_push_output_sprintf_str(vt, C1_OSC, false, "52;%c;", selection_chars[idx]);
+
+ state->tmp.selection.sendpartial = 0;
+ }
+
+ if(frag.len) {
+ size_t bufcur = 0;
+ char *buffer = state->selection.buffer;
+
+ uint32_t x = 0;
+ int n = 0;
+
+ if(state->tmp.selection.sendpartial) {
+ n = state->tmp.selection.sendpartial >> 24;
+ x = state->tmp.selection.sendpartial & 0xFFFFFF;
+
+ state->tmp.selection.sendpartial = 0;
+ }
+
+ while((state->selection.buflen - bufcur) >= 4 && frag.len) {
+ x = (x << 8) | frag.str[0];
+ n++;
+ frag.str++, frag.len--;
+
+ if(n == 3) {
+ buffer[0] = base64_one((x >> 18) & 0x3F);
+ buffer[1] = base64_one((x >> 12) & 0x3F);
+ buffer[2] = base64_one((x >> 6) & 0x3F);
+ buffer[3] = base64_one((x >> 0) & 0x3F);
+
+ buffer += 4, bufcur += 4;
+ x = 0;
+ n = 0;
+ }
+
+ if(!frag.len || (state->selection.buflen - bufcur) < 4) {
+ if(bufcur)
+ vterm_push_output_bytes(vt, state->selection.buffer, bufcur);
+
+ buffer = state->selection.buffer;
+ bufcur = 0;
+ }
+ }
+
+ if(n)
+ state->tmp.selection.sendpartial = (n << 24) | x;
+ }
+
+ if(frag.final) {
+ if(state->tmp.selection.sendpartial) {
+ int n = state->tmp.selection.sendpartial >> 24;
+ uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF;
+ char *buffer = state->selection.buffer;
+
+ /* n is either 1 or 2 now */
+ x <<= (n == 1) ? 16 : 8;
+
+ buffer[0] = base64_one((x >> 18) & 0x3F);
+ buffer[1] = base64_one((x >> 12) & 0x3F);
+ buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F);
+ buffer[3] = '=';
+
+ vterm_push_output_sprintf_str(vt, 0, true, "%.*s", 4, buffer);
+ }
+ else
+ vterm_push_output_sprintf_str(vt, 0, true, "");
+ }
+}
diff --git a/src/libs/3rdparty/libvterm/src/unicode.c b/src/libs/3rdparty/libvterm/src/unicode.c
new file mode 100644
index 0000000000..269244ff6b
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/unicode.c
@@ -0,0 +1,313 @@
+#include "vterm_internal.h"
+
+// ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+// With modifications:
+// made functions static
+// moved 'combining' table to file scope, so other functions can see it
+// ###################################################################
+
+/*
+ * This is an implementation of wcwidth() and wcswidth() (defined in
+ * IEEE Std 1002.1-2001) for Unicode.
+ *
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
+ *
+ * In fixed-width output devices, Latin characters all occupy a single
+ * "cell" position of equal width, whereas ideographic CJK characters
+ * occupy two such cells. Interoperability between terminal-line
+ * applications and (teletype-style) character terminals using the
+ * UTF-8 encoding requires agreement on which character should advance
+ * the cursor by how many cell positions. No established formal
+ * standards exist at present on which Unicode character shall occupy
+ * how many cell positions on character terminals. These routines are
+ * a first attempt of defining such behavior based on simple rules
+ * applied to data provided by the Unicode Consortium.
+ *
+ * For some graphical characters, the Unicode standard explicitly
+ * defines a character-cell width via the definition of the East Asian
+ * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
+ * In all these cases, there is no ambiguity about which width a
+ * terminal shall use. For characters in the East Asian Ambiguous (A)
+ * class, the width choice depends purely on a preference of backward
+ * compatibility with either historic CJK or Western practice.
+ * Choosing single-width for these characters is easy to justify as
+ * the appropriate long-term solution, as the CJK practice of
+ * displaying these characters as double-width comes from historic
+ * implementation simplicity (8-bit encoded characters were displayed
+ * single-width and 16-bit ones double-width, even for Greek,
+ * Cyrillic, etc.) and not any typographic considerations.
+ *
+ * Much less clear is the choice of width for the Not East Asian
+ * (Neutral) class. Existing practice does not dictate a width for any
+ * of these characters. It would nevertheless make sense
+ * typographically to allocate two character cells to characters such
+ * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
+ * represented adequately with a single-width glyph. The following
+ * routines at present merely assign a single-cell width to all
+ * neutral characters, in the interest of simplicity. This is not
+ * entirely satisfactory and should be reconsidered before
+ * establishing a formal standard in this area. At the moment, the
+ * decision which Not East Asian (Neutral) characters should be
+ * represented by double-width glyphs cannot yet be answered by
+ * applying a simple rule from the Unicode database content. Setting
+ * up a proper standard for the behavior of UTF-8 character terminals
+ * will require a careful analysis not only of each Unicode character,
+ * but also of each presentation form, something the author of these
+ * routines has avoided to do so far.
+ *
+ * http://www.unicode.org/unicode/reports/tr11/
+ *
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * for any purpose and without fee is hereby granted. The author
+ * disclaims all warranties with regard to this software.
+ *
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+ */
+
+struct interval {
+ int first;
+ int last;
+};
+
+/* sorted list of non-overlapping intervals of non-spacing characters */
+/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
+static const struct interval combining[] = {
+ { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
+ { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
+ { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
+ { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
+ { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
+ { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
+ { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
+ { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
+ { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
+ { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
+ { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
+ { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
+ { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
+ { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
+ { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
+ { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
+ { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
+ { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
+ { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
+ { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
+ { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
+ { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
+ { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
+ { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
+ { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
+ { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
+ { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
+ { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
+ { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
+ { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
+ { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
+ { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
+ { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
+ { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
+ { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
+ { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
+ { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
+ { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
+ { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
+ { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
+ { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
+ { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
+ { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
+ { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
+ { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
+ { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
+ { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
+ { 0xE0100, 0xE01EF }
+};
+
+
+/* auxiliary function for binary search in interval table */
+static int bisearch(uint32_t ucs, const struct interval *table, int max) {
+ int min = 0;
+ int mid;
+
+ if (ucs < table[0].first || ucs > table[max].last)
+ return 0;
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (ucs > table[mid].last)
+ min = mid + 1;
+ else if (ucs < table[mid].first)
+ max = mid - 1;
+ else
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* The following two functions define the column width of an ISO 10646
+ * character as follows:
+ *
+ * - The null character (U+0000) has a column width of 0.
+ *
+ * - Other C0/C1 control characters and DEL will lead to a return
+ * value of -1.
+ *
+ * - Non-spacing and enclosing combining characters (general
+ * category code Mn or Me in the Unicode database) have a
+ * column width of 0.
+ *
+ * - SOFT HYPHEN (U+00AD) has a column width of 1.
+ *
+ * - Other format characters (general category code Cf in the Unicode
+ * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+ *
+ * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+ * have a column width of 0.
+ *
+ * - Spacing characters in the East Asian Wide (W) or East Asian
+ * Full-width (F) category as defined in Unicode Technical
+ * Report #11 have a column width of 2.
+ *
+ * - All remaining characters (including all printable
+ * ISO 8859-1 and WGL4 characters, Unicode control characters,
+ * etc.) have a column width of 1.
+ *
+ * This implementation assumes that uint32_t characters are encoded
+ * in ISO 10646.
+ */
+
+
+static int mk_wcwidth(uint32_t ucs)
+{
+ /* test for 8-bit control characters */
+ if (ucs == 0)
+ return 0;
+ if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
+ return -1;
+
+ /* binary search in table of non-spacing characters */
+ if (bisearch(ucs, combining,
+ sizeof(combining) / sizeof(struct interval) - 1))
+ return 0;
+
+ /* if we arrive here, ucs is not a combining or C0/C1 control character */
+
+ return 1 +
+ (ucs >= 0x1100 &&
+ (ucs <= 0x115f || /* Hangul Jamo init. consonants */
+ ucs == 0x2329 || ucs == 0x232a ||
+ (ucs >= 0x2e80 && ucs <= 0xa4cf &&
+ ucs != 0x303f) || /* CJK ... Yi */
+ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
+ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
+ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
+ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
+ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
+ (ucs >= 0xffe0 && ucs <= 0xffe6) ||
+ (ucs >= 0x20000 && ucs <= 0x2fffd) ||
+ (ucs >= 0x30000 && ucs <= 0x3fffd)));
+}
+
+
+#ifdef USE_MK_WCWIDTH_CJK
+
+/*
+ * The following functions are the same as mk_wcwidth() and
+ * mk_wcswidth(), except that spacing characters in the East Asian
+ * Ambiguous (A) category as defined in Unicode Technical Report #11
+ * have a column width of 2. This variant might be useful for users of
+ * CJK legacy encodings who want to migrate to UCS without changing
+ * the traditional terminal character-width behaviour. It is not
+ * otherwise recommended for general use.
+ */
+static int mk_wcwidth_cjk(uint32_t ucs)
+{
+ /* sorted list of non-overlapping intervals of East Asian Ambiguous
+ * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
+ static const struct interval ambiguous[] = {
+ { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
+ { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
+ { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
+ { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
+ { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
+ { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
+ { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
+ { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
+ { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
+ { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
+ { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
+ { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
+ { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
+ { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
+ { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
+ { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
+ { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
+ { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
+ { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
+ { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
+ { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
+ { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
+ { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
+ { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
+ { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
+ { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
+ { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
+ { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
+ { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
+ { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
+ { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
+ { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
+ { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
+ { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
+ { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
+ { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
+ { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
+ { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
+ { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
+ { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
+ { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
+ { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
+ { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
+ { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
+ { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
+ { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
+ { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
+ { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
+ { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
+ { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
+ { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
+ { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
+ };
+
+ /* binary search in table of non-spacing characters */
+ if (bisearch(ucs, ambiguous,
+ sizeof(ambiguous) / sizeof(struct interval) - 1))
+ return 2;
+
+ return mk_wcwidth(ucs);
+}
+
+#endif
+
+// ################################
+// ### The rest added by Paul Evans
+
+static const struct interval fullwidth[] = {
+#include "fullwidth.inc"
+};
+
+INTERNAL int vterm_unicode_width(uint32_t codepoint)
+{
+ if(bisearch(codepoint, fullwidth, sizeof(fullwidth) / sizeof(fullwidth[0]) - 1))
+ return 2;
+
+ return mk_wcwidth(codepoint);
+}
+
+INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
+{
+ return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
+}
diff --git a/src/libs/3rdparty/libvterm/src/utf8.h b/src/libs/3rdparty/libvterm/src/utf8.h
new file mode 100644
index 0000000000..9a336d357f
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/utf8.h
@@ -0,0 +1,39 @@
+/* The following functions copied and adapted from libtermkey
+ *
+ * http://www.leonerd.org.uk/code/libtermkey/
+ */
+static inline unsigned int utf8_seqlen(long codepoint)
+{
+ if(codepoint < 0x0000080) return 1;
+ if(codepoint < 0x0000800) return 2;
+ if(codepoint < 0x0010000) return 3;
+ if(codepoint < 0x0200000) return 4;
+ if(codepoint < 0x4000000) return 5;
+ return 6;
+}
+
+/* Does NOT NUL-terminate the buffer */
+static int fill_utf8(long codepoint, char *str)
+{
+ int nbytes = utf8_seqlen(codepoint);
+
+ // This is easier done backwards
+ int b = nbytes;
+ while(b > 1) {
+ b--;
+ str[b] = 0x80 | (codepoint & 0x3f);
+ codepoint >>= 6;
+ }
+
+ switch(nbytes) {
+ case 1: str[0] = (codepoint & 0x7f); break;
+ case 2: str[0] = 0xc0 | (codepoint & 0x1f); break;
+ case 3: str[0] = 0xe0 | (codepoint & 0x0f); break;
+ case 4: str[0] = 0xf0 | (codepoint & 0x07); break;
+ case 5: str[0] = 0xf8 | (codepoint & 0x03); break;
+ case 6: str[0] = 0xfc | (codepoint & 0x01); break;
+ }
+
+ return nbytes;
+}
+/* end copy */
diff --git a/src/libs/3rdparty/libvterm/src/vterm.c b/src/libs/3rdparty/libvterm/src/vterm.c
new file mode 100644
index 0000000000..0997887f5f
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/vterm.c
@@ -0,0 +1,429 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/*****************
+ * API functions *
+ *****************/
+
+static void *default_malloc(size_t size, void *allocdata)
+{
+ void *ptr = malloc(size);
+ if(ptr)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+static void default_free(void *ptr, void *allocdata)
+{
+ free(ptr);
+}
+
+static VTermAllocatorFunctions default_allocator = {
+ .malloc = &default_malloc,
+ .free = &default_free,
+};
+
+VTerm *vterm_new(int rows, int cols)
+{
+ return vterm_build(&(const struct VTermBuilder){
+ .rows = rows,
+ .cols = cols,
+ });
+}
+
+VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
+{
+ return vterm_build(&(const struct VTermBuilder){
+ .rows = rows,
+ .cols = cols,
+ .allocator = funcs,
+ .allocdata = allocdata,
+ });
+}
+
+/* A handy macro for defaulting values out of builder fields */
+#define DEFAULT(v, def) ((v) ? (v) : (def))
+
+VTerm *vterm_build(const struct VTermBuilder *builder)
+{
+ const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
+
+ /* Need to bootstrap using the allocator function directly */
+ VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
+
+ vt->allocator = allocator;
+ vt->allocdata = builder->allocdata;
+
+ vt->rows = builder->rows;
+ vt->cols = builder->cols;
+
+ vt->parser.state = NORMAL;
+
+ vt->parser.callbacks = NULL;
+ vt->parser.cbdata = NULL;
+
+ vt->parser.emit_nul = false;
+
+ vt->outfunc = NULL;
+ vt->outdata = NULL;
+
+ vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
+ vt->outbuffer_cur = 0;
+ vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
+
+ vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
+ vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
+
+ return vt;
+}
+
+void vterm_free(VTerm *vt)
+{
+ if(vt->screen)
+ vterm_screen_free(vt->screen);
+
+ if(vt->state)
+ vterm_state_free(vt->state);
+
+ vterm_allocator_free(vt, vt->outbuffer);
+ vterm_allocator_free(vt, vt->tmpbuffer);
+
+ vterm_allocator_free(vt, vt);
+}
+
+INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
+{
+ return (*vt->allocator->malloc)(size, vt->allocdata);
+}
+
+INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
+{
+ (*vt->allocator->free)(ptr, vt->allocdata);
+}
+
+void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
+{
+ if(rowsp)
+ *rowsp = vt->rows;
+ if(colsp)
+ *colsp = vt->cols;
+}
+
+void vterm_set_size(VTerm *vt, int rows, int cols)
+{
+ if(rows < 1 || cols < 1)
+ return;
+
+ vt->rows = rows;
+ vt->cols = cols;
+
+ if(vt->parser.callbacks && vt->parser.callbacks->resize)
+ (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
+}
+
+int vterm_get_utf8(const VTerm *vt)
+{
+ return vt->mode.utf8;
+}
+
+void vterm_set_utf8(VTerm *vt, int is_utf8)
+{
+ vt->mode.utf8 = is_utf8;
+}
+
+void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
+{
+ vt->outfunc = func;
+ vt->outdata = user;
+}
+
+INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
+{
+ if(vt->outfunc) {
+ (vt->outfunc)(bytes, len, vt->outdata);
+ return;
+ }
+
+ if(len > vt->outbuffer_len - vt->outbuffer_cur)
+ return;
+
+ memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
+ vt->outbuffer_cur += len;
+}
+
+INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
+{
+ size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ format, args);
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, len);
+}
+
+INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vterm_push_output_vsprintf(vt, format, args);
+ va_end(args);
+}
+
+INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
+{
+ size_t cur;
+
+ if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ ESC_S "%c", ctrl - 0x40);
+ else
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ "%c", ctrl);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ va_list args;
+ va_start(args, fmt);
+ cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ fmt, args);
+ va_end(args);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...)
+{
+ size_t cur = 0;
+
+ if(ctrl) {
+ if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ ESC_S "%c", ctrl - 0x40);
+ else
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ "%c", ctrl);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ fmt, args);
+ va_end(args);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ if(term) {
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+size_t vterm_output_get_buffer_size(const VTerm *vt)
+{
+ return vt->outbuffer_len;
+}
+
+size_t vterm_output_get_buffer_current(const VTerm *vt)
+{
+ return vt->outbuffer_cur;
+}
+
+size_t vterm_output_get_buffer_remaining(const VTerm *vt)
+{
+ return vt->outbuffer_len - vt->outbuffer_cur;
+}
+
+size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
+{
+ if(len > vt->outbuffer_cur)
+ len = vt->outbuffer_cur;
+
+ memcpy(buffer, vt->outbuffer, len);
+
+ if(len < vt->outbuffer_cur)
+ memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
+
+ vt->outbuffer_cur -= len;
+
+ return len;
+}
+
+VTermValueType vterm_get_attr_type(VTermAttr attr)
+{
+ switch(attr) {
+ case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_SMALL: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BASELINE: return VTERM_VALUETYPE_INT;
+
+ case VTERM_N_ATTRS: return 0;
+ }
+ return 0; /* UNREACHABLE */
+}
+
+VTermValueType vterm_get_prop_type(VTermProp prop)
+{
+ switch(prop) {
+ case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
+
+ case VTERM_N_PROPS: return 0;
+ }
+ return 0; /* UNREACHABLE */
+}
+
+void vterm_scroll_rect(VTermRect rect,
+ int downward,
+ int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user),
+ void *user)
+{
+ VTermRect src;
+ VTermRect dest;
+
+ if(abs(downward) >= rect.end_row - rect.start_row ||
+ abs(rightward) >= rect.end_col - rect.start_col) {
+ /* Scroll more than area; just erase the lot */
+ (*eraserect)(rect, 0, user);
+ return;
+ }
+
+ if(rightward >= 0) {
+ /* rect: [XXX................]
+ * src: [----------------]
+ * dest: [----------------]
+ */
+ dest.start_col = rect.start_col;
+ dest.end_col = rect.end_col - rightward;
+ src.start_col = rect.start_col + rightward;
+ src.end_col = rect.end_col;
+ }
+ else {
+ /* rect: [................XXX]
+ * src: [----------------]
+ * dest: [----------------]
+ */
+ int leftward = -rightward;
+ dest.start_col = rect.start_col + leftward;
+ dest.end_col = rect.end_col;
+ src.start_col = rect.start_col;
+ src.end_col = rect.end_col - leftward;
+ }
+
+ if(downward >= 0) {
+ dest.start_row = rect.start_row;
+ dest.end_row = rect.end_row - downward;
+ src.start_row = rect.start_row + downward;
+ src.end_row = rect.end_row;
+ }
+ else {
+ int upward = -downward;
+ dest.start_row = rect.start_row + upward;
+ dest.end_row = rect.end_row;
+ src.start_row = rect.start_row;
+ src.end_row = rect.end_row - upward;
+ }
+
+ if(moverect)
+ (*moverect)(dest, src, user);
+
+ if(downward > 0)
+ rect.start_row = rect.end_row - downward;
+ else if(downward < 0)
+ rect.end_row = rect.start_row - downward;
+
+ if(rightward > 0)
+ rect.start_col = rect.end_col - rightward;
+ else if(rightward < 0)
+ rect.end_col = rect.start_col - rightward;
+
+ (*eraserect)(rect, 0, user);
+}
+
+void vterm_copy_cells(VTermRect dest,
+ VTermRect src,
+ void (*copycell)(VTermPos dest, VTermPos src, void *user),
+ void *user)
+{
+ int downward = src.start_row - dest.start_row;
+ int rightward = src.start_col - dest.start_col;
+
+ int init_row, test_row, init_col, test_col;
+ int inc_row, inc_col;
+
+ if(downward < 0) {
+ init_row = dest.end_row - 1;
+ test_row = dest.start_row - 1;
+ inc_row = -1;
+ }
+ else /* downward >= 0 */ {
+ init_row = dest.start_row;
+ test_row = dest.end_row;
+ inc_row = +1;
+ }
+
+ if(rightward < 0) {
+ init_col = dest.end_col - 1;
+ test_col = dest.start_col - 1;
+ inc_col = -1;
+ }
+ else /* rightward >= 0 */ {
+ init_col = dest.start_col;
+ test_col = dest.end_col;
+ inc_col = +1;
+ }
+
+ VTermPos pos;
+ for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
+ for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
+ VTermPos srcpos = { pos.row + downward, pos.col + rightward };
+ (*copycell)(pos, srcpos, user);
+ }
+}
+
+void vterm_check_version(int major, int minor)
+{
+ if(major != VTERM_VERSION_MAJOR) {
+ fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n",
+ major, VTERM_VERSION_MAJOR);
+ exit(1);
+ }
+
+ if(minor > VTERM_VERSION_MINOR) {
+ fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n",
+ minor, VTERM_VERSION_MINOR);
+ exit(1);
+ }
+
+ // Happy
+}
diff --git a/src/libs/3rdparty/libvterm/src/vterm_internal.h b/src/libs/3rdparty/libvterm/src/vterm_internal.h
new file mode 100644
index 0000000000..ad61bff8b0
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/vterm_internal.h
@@ -0,0 +1,296 @@
+#ifndef __VTERM_INTERNAL_H__
+#define __VTERM_INTERNAL_H__
+
+#include "vterm.h"
+
+#include <stdarg.h>
+
+#if defined(__GNUC__)
+# define INTERNAL __attribute__((visibility("internal")))
+#else
+# define INTERNAL
+#endif
+
+#ifdef DEBUG
+# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DEBUG_LOG(...)
+#endif
+
+#define ESC_S "\x1b"
+
+#define INTERMED_MAX 16
+
+#define CSI_ARGS_MAX 16
+#define CSI_LEADER_MAX 16
+
+#define BUFIDX_PRIMARY 0
+#define BUFIDX_ALTSCREEN 1
+
+typedef struct VTermEncoding VTermEncoding;
+
+typedef struct {
+ VTermEncoding *enc;
+
+ // This size should be increased if required by other stateful encodings
+ char data[4*sizeof(uint32_t)];
+} VTermEncodingInstance;
+
+struct VTermPen
+{
+ VTermColor fg;
+ VTermColor bg;
+ unsigned int bold:1;
+ unsigned int underline:3;
+ unsigned int italic:1;
+ unsigned int blink:1;
+ unsigned int reverse:1;
+ unsigned int conceal:1;
+ unsigned int strike:1;
+ unsigned int font:4; /* To store 0-9 */
+ unsigned int small:1;
+ unsigned int baseline:2;
+};
+
+struct VTermState
+{
+ VTerm *vt;
+
+ const VTermStateCallbacks *callbacks;
+ void *cbdata;
+
+ const VTermStateFallbacks *fallbacks;
+ void *fbdata;
+
+ int rows;
+ int cols;
+
+ /* Current cursor position */
+ VTermPos pos;
+
+ int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
+
+ int scrollregion_top;
+ int scrollregion_bottom; /* -1 means unbounded */
+#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
+ int scrollregion_left;
+#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
+ int scrollregion_right; /* -1 means unbounded */
+#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
+
+ /* Bitvector of tab stops */
+ unsigned char *tabstops;
+
+ /* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */
+ VTermLineInfo *lineinfos[2];
+
+ /* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */
+ VTermLineInfo *lineinfo;
+#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
+#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
+
+ /* Mouse state */
+ int mouse_col, mouse_row;
+ int mouse_buttons;
+ int mouse_flags;
+#define MOUSE_WANT_CLICK 0x01
+#define MOUSE_WANT_DRAG 0x02
+#define MOUSE_WANT_MOVE 0x04
+
+ enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
+
+ /* Last glyph output, for Unicode recombining purposes */
+ uint32_t *combine_chars;
+ size_t combine_chars_size; // Number of ELEMENTS in the above
+ int combine_width; // The width of the glyph above
+ VTermPos combine_pos; // Position before movement
+
+ struct {
+ unsigned int keypad:1;
+ unsigned int cursor:1;
+ unsigned int autowrap:1;
+ unsigned int insert:1;
+ unsigned int newline:1;
+ unsigned int cursor_visible:1;
+ unsigned int cursor_blink:1;
+ unsigned int cursor_shape:2;
+ unsigned int alt_screen:1;
+ unsigned int origin:1;
+ unsigned int screen:1;
+ unsigned int leftrightmargin:1;
+ unsigned int bracketpaste:1;
+ unsigned int report_focus:1;
+ } mode;
+
+ VTermEncodingInstance encoding[4], encoding_utf8;
+ int gl_set, gr_set, gsingle_set;
+
+ struct VTermPen pen;
+
+ VTermColor default_fg;
+ VTermColor default_bg;
+ VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
+
+ int bold_is_highbright;
+
+ unsigned int protected_cell : 1;
+
+ /* Saved state under DEC mode 1048/1049 */
+ struct {
+ VTermPos pos;
+ struct VTermPen pen;
+
+ struct {
+ unsigned int cursor_visible:1;
+ unsigned int cursor_blink:1;
+ unsigned int cursor_shape:2;
+ } mode;
+ } saved;
+
+ /* Temporary state for DECRQSS parsing */
+ union {
+ char decrqss[4];
+ struct {
+ uint16_t mask;
+ enum {
+ SELECTION_INITIAL,
+ SELECTION_SELECTED,
+ SELECTION_QUERY,
+ SELECTION_SET_INITIAL,
+ SELECTION_SET,
+ } state : 8;
+ uint32_t recvpartial;
+ uint32_t sendpartial;
+ } selection;
+ } tmp;
+
+ struct {
+ const VTermSelectionCallbacks *callbacks;
+ void *user;
+ char *buffer;
+ size_t buflen;
+ } selection;
+};
+
+struct VTerm
+{
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ int rows;
+ int cols;
+
+ struct {
+ unsigned int utf8:1;
+ unsigned int ctrl8bit:1;
+ } mode;
+
+ struct {
+ enum VTermParserState {
+ NORMAL,
+ CSI_LEADER,
+ CSI_ARGS,
+ CSI_INTERMED,
+ DCS_COMMAND,
+ /* below here are the "string states" */
+ OSC_COMMAND,
+ OSC,
+ DCS,
+ APC,
+ PM,
+ SOS,
+ } state;
+
+ bool in_esc : 1;
+
+ int intermedlen;
+ char intermed[INTERMED_MAX];
+
+ union {
+ struct {
+ int leaderlen;
+ char leader[CSI_LEADER_MAX];
+
+ int argi;
+ long args[CSI_ARGS_MAX];
+ } csi;
+ struct {
+ int command;
+ } osc;
+ struct {
+ int commandlen;
+ char command[CSI_LEADER_MAX];
+ } dcs;
+ } v;
+
+ const VTermParserCallbacks *callbacks;
+ void *cbdata;
+
+ bool string_initial;
+
+ bool emit_nul;
+ } parser;
+
+ /* len == malloc()ed size; cur == number of valid bytes */
+
+ VTermOutputCallback *outfunc;
+ void *outdata;
+
+ char *outbuffer;
+ size_t outbuffer_len;
+ size_t outbuffer_cur;
+
+ char *tmpbuffer;
+ size_t tmpbuffer_len;
+
+ VTermState *state;
+ VTermScreen *screen;
+};
+
+struct VTermEncoding {
+ void (*init) (VTermEncoding *enc, void *data);
+ void (*decode)(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t len);
+};
+
+typedef enum {
+ ENC_UTF8,
+ ENC_SINGLE_94
+} VTermEncodingType;
+
+void *vterm_allocator_malloc(VTerm *vt, size_t size);
+void vterm_allocator_free(VTerm *vt, void *ptr);
+
+void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
+void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
+void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
+void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
+void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...);
+
+void vterm_state_free(VTermState *state);
+
+void vterm_state_newpen(VTermState *state);
+void vterm_state_resetpen(VTermState *state);
+void vterm_state_setpen(VTermState *state, const long args[], int argcount);
+int vterm_state_getpen(VTermState *state, long args[], int argcount);
+void vterm_state_savepen(VTermState *state, int save);
+
+enum {
+ C1_SS3 = 0x8f,
+ C1_DCS = 0x90,
+ C1_CSI = 0x9b,
+ C1_ST = 0x9c,
+ C1_OSC = 0x9d,
+};
+
+void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
+
+void vterm_screen_free(VTermScreen *screen);
+
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
+
+int vterm_unicode_width(uint32_t codepoint);
+int vterm_unicode_is_combining(uint32_t codepoint);
+
+#endif
diff --git a/src/libs/3rdparty/libvterm/vterm.pc.in b/src/libs/3rdparty/libvterm/vterm.pc.in
new file mode 100644
index 0000000000..681a270d51
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/vterm.pc.in
@@ -0,0 +1,8 @@
+libdir=@LIBDIR@
+includedir=@INCDIR@
+
+Name: vterm
+Description: Abstract VT220/Xterm/ECMA-48 emulation library
+Version: 0.3.1
+Libs: -L${libdir} -lvterm
+Cflags: -I${includedir}
diff --git a/src/libs/3rdparty/libvterm/vterm.qbs b/src/libs/3rdparty/libvterm/vterm.qbs
new file mode 100644
index 0000000000..18ccb638aa
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/vterm.qbs
@@ -0,0 +1,34 @@
+Project {
+ QtcLibrary {
+ name: "vterm"
+ type: "staticlibrary"
+
+ Depends { name: "cpp" }
+ cpp.includePaths: base.concat("include")
+ cpp.warningLevel: "none"
+
+ Group {
+ prefix: "src/"
+ files: [
+ "encoding.c",
+ "fullwidth.inc",
+ "keyboard.c",
+ "mouse.c",
+ "parser.c",
+ "pen.c",
+ "rect.h",
+ "screen.c",
+ "state.c",
+ "unicode.c",
+ "utf8.h",
+ "vterm.c",
+ "vterm_internal.h",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: base.concat("include")
+ }
+ }
+}
diff --git a/src/libs/3rdparty/sqlite/carray.c b/src/libs/3rdparty/sqlite/carray.c
index b6eb0453a0..709c894f27 100644
--- a/src/libs/3rdparty/sqlite/carray.c
+++ b/src/libs/3rdparty/sqlite/carray.c
@@ -28,7 +28,7 @@
**
** There is an optional third parameter to determine the datatype of
** the C-language array. Allowed values of the third parameter are
-** 'int32', 'int64', 'double', 'char*'. Example:
+** 'int32', 'int64', 'double', 'char*', 'struct iovec'. Example:
**
** SELECT * FROM carray($ptr,10,'char*');
**
@@ -56,6 +56,14 @@
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
+#ifdef _WIN32
+ struct iovec {
+ void *iov_base;
+ size_t iov_len;
+ };
+#else
+# include <sys/uio.h>
+#endif
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
** Must exactly match the definitions in carray.h.
@@ -65,6 +73,7 @@ SQLITE_EXTENSION_INIT1
# define CARRAY_INT64 1 /* Data is 64-bit signed integers */
# define CARRAY_DOUBLE 2 /* Data is doubles */
# define CARRAY_TEXT 3 /* Data is char* */
+# define CARRAY_BLOB 4 /* Data is struct iovec* */
#endif
#ifndef SQLITE_API
@@ -80,7 +89,8 @@ SQLITE_EXTENSION_INIT1
/*
** Names of allowed datatypes
*/
-static const char *azType[] = { "int32", "int64", "double", "char*" };
+static const char *azType[] = { "int32", "int64", "double", "char*",
+ "struct iovec" };
/*
** Structure used to hold the sqlite3_carray_bind() information
@@ -224,6 +234,12 @@ static int carrayColumn(
sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
return SQLITE_OK;
}
+ case CARRAY_BLOB: {
+ const struct iovec *p = (struct iovec*)pCur->pPtr;
+ sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base,
+ (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT);
+ return SQLITE_OK;
+ }
}
}
}
@@ -268,7 +284,7 @@ static int carrayFilter(
if( pBind==0 ) break;
pCur->pPtr = pBind->aData;
pCur->iCnt = pBind->nData;
- pCur->eType = pBind->mFlags & 0x03;
+ pCur->eType = pBind->mFlags & 0x07;
break;
}
case 2:
@@ -431,24 +447,29 @@ SQLITE_API int sqlite3_carray_bind(
pNew->mFlags = mFlags;
if( xDestroy==SQLITE_TRANSIENT ){
sqlite3_int64 sz = nData;
- switch( mFlags & 0x03 ){
- case CARRAY_INT32: sz *= 4; break;
- case CARRAY_INT64: sz *= 8; break;
- case CARRAY_DOUBLE: sz *= 8; break;
- case CARRAY_TEXT: sz *= sizeof(char*); break;
+ switch( mFlags & 0x07 ){
+ case CARRAY_INT32: sz *= 4; break;
+ case CARRAY_INT64: sz *= 8; break;
+ case CARRAY_DOUBLE: sz *= 8; break;
+ case CARRAY_TEXT: sz *= sizeof(char*); break;
+ case CARRAY_BLOB: sz *= sizeof(struct iovec); break;
}
- if( (mFlags & 0x03)==CARRAY_TEXT ){
+ if( (mFlags & 0x07)==CARRAY_TEXT ){
for(i=0; i<nData; i++){
const char *z = ((char**)aData)[i];
if( z ) sz += strlen(z) + 1;
}
+ }else if( (mFlags & 0x07)==CARRAY_BLOB ){
+ for(i=0; i<nData; i++){
+ sz += ((struct iovec*)aData)[i].iov_len;
+ }
}
pNew->aData = sqlite3_malloc64( sz );
if( pNew->aData==0 ){
sqlite3_free(pNew);
return SQLITE_NOMEM;
}
- if( (mFlags & 0x03)==CARRAY_TEXT ){
+ if( (mFlags & 0x07)==CARRAY_TEXT ){
char **az = (char**)pNew->aData;
char *z = (char*)&az[nData];
for(i=0; i<nData; i++){
@@ -463,6 +484,16 @@ SQLITE_API int sqlite3_carray_bind(
memcpy(z, zData, n+1);
z += n+1;
}
+ }else if( (mFlags & 0x07)==CARRAY_BLOB ){
+ struct iovec *p = (struct iovec*)pNew->aData;
+ unsigned char *z = (unsigned char*)&p[nData];
+ for(i=0; i<nData; i++){
+ size_t n = ((struct iovec*)aData)[i].iov_len;
+ p[i].iov_len = n;
+ p[i].iov_base = z;
+ z += n;
+ memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n);
+ }
}else{
memcpy(pNew->aData, aData, sz);
}
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index b8f98c7c1e..b47891c381 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.39.2. By combining all the individual C code files into this
+** version 3.41.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
@@ -452,9 +452,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.39.2"
-#define SQLITE_VERSION_NUMBER 3039002
-#define SQLITE_SOURCE_ID "2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668e6603"
+#define SQLITE_VERSION "3.41.0"
+#define SQLITE_VERSION_NUMBER 3041000
+#define SQLITE_SOURCE_ID "2023-02-21 18:09:37 05941c2a04037fc3ed2ffae11f5d2260706f89431f463518740f72ada350866d"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -869,6 +869,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
@@ -976,13 +977,17 @@ SQLITE_API int sqlite3_exec(
**
** SQLite uses one of these integer values as the second
** argument to calls it makes to the xLock() and xUnlock() methods
-** of an [sqlite3_io_methods] object.
+** of an [sqlite3_io_methods] object. These values are ordered from
+** lest restrictive to most restrictive.
+**
+** The argument to xLock() is always SHARED or higher. The argument to
+** xUnlock is either SHARED or NONE.
*/
-#define SQLITE_LOCK_NONE 0
-#define SQLITE_LOCK_SHARED 1
-#define SQLITE_LOCK_RESERVED 2
-#define SQLITE_LOCK_PENDING 3
-#define SQLITE_LOCK_EXCLUSIVE 4
+#define SQLITE_LOCK_NONE 0 /* xUnlock() only */
+#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
+#define SQLITE_LOCK_RESERVED 2 /* xLock() only */
+#define SQLITE_LOCK_PENDING 3 /* xLock() only */
+#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
/*
** CAPI3REF: Synchronization Type Flags
@@ -1060,7 +1065,14 @@ struct sqlite3_file {
** <li> [SQLITE_LOCK_PENDING], or
** <li> [SQLITE_LOCK_EXCLUSIVE].
** </ul>
-** xLock() increases the lock. xUnlock() decreases the lock.
+** 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
+** 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
+** 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,
** PENDING, or EXCLUSIVE lock on the file. It returns true
@@ -1165,9 +1177,8 @@ struct sqlite3_io_methods {
** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
-** into an integer that the pArg argument points to. This capability
-** is used during testing and is only available when the SQLITE_TEST
-** compile-time option is used.
+** into an integer that the pArg argument points to.
+** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
**
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
@@ -1471,7 +1482,6 @@ struct sqlite3_io_methods {
** in wal mode after the client has finished copying pages from the wal
** file to the database file, but before the *-shm file is updated to
** record the fact that the pages have been checkpointed.
-** </ul>
**
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
@@ -1484,10 +1494,16 @@ struct sqlite3_io_methods {
** the database is not a wal-mode db, or if there is no such connection in any
** other process. This opcode cannot be used to detect transactions opened
** by clients within the current process, only within other processes.
-** </ul>
**
** <li>[[SQLITE_FCNTL_CKSM_FILE]]
-** Used by the cksmvfs VFS module only.
+** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the
+** [checksum VFS shim] only.
+**
+** <li>[[SQLITE_FCNTL_RESET_CACHE]]
+** If there is currently no transaction open on the database, and the
+** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
+** purges the contents of the in-memory page cache. If there is an open
+** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1530,6 +1546,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_CKPT_START 39
#define SQLITE_FCNTL_EXTERNAL_READER 40
#define SQLITE_FCNTL_CKSM_FILE 41
+#define SQLITE_FCNTL_RESET_CACHE 42
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1560,6 +1577,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
+** CAPI3REF: File Name
+**
+** Type [sqlite3_filename] is used by SQLite to pass filenames to the
+** xOpen method of a [VFS]. It may be cast to (const char*) and treated
+** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
+** may also be passed to special APIs such as:
+**
+** <ul>
+** <li> sqlite3_filename_database()
+** <li> sqlite3_filename_journal()
+** <li> sqlite3_filename_wal()
+** <li> sqlite3_uri_parameter()
+** <li> sqlite3_uri_boolean()
+** <li> sqlite3_uri_int64()
+** <li> sqlite3_uri_key()
+** </ul>
+*/
+typedef const char *sqlite3_filename;
+
+/*
** CAPI3REF: OS Interface Object
**
** An instance of the sqlite3_vfs object defines the interface between
@@ -1737,7 +1774,7 @@ struct sqlite3_vfs {
sqlite3_vfs *pNext; /* Next registered VFS */
const char *zName; /* Name of this virtual file system */
void *pAppData; /* Pointer to application-specific data */
- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
+ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
int flags, int *pOutFlags);
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
@@ -2453,7 +2490,7 @@ struct sqlite3_mem_methods {
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
** when the "current value" returned by
-** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
+** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
** Any attempt to change the lookaside memory configuration when lookaside
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
@@ -2603,8 +2640,12 @@ struct sqlite3_mem_methods {
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
** </ol>
** Because resetting a database is destructive and irreversible, the
-** process requires the use of this obscure API and multiple steps to help
-** ensure that it does not happen by accident.
+** process requires the use of this obscure API and multiple steps to
+** help ensure that it does not happen by accident. Because this
+** feature must be capable of resetting corrupt databases, and
+** shutting down virtual tables may require access to that corrupt
+** storage, the library must abandon any installed virtual tables
+** without calling their xDestroy() methods.
**
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
@@ -2615,6 +2656,7 @@ struct sqlite3_mem_methods {
** <ul>
** <li> The [PRAGMA writable_schema=ON] statement.
** <li> The [PRAGMA journal_mode=OFF] statement.
+** <li> The [PRAGMA schema_version=N] statement.
** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables].
** </ul>
@@ -2942,8 +2984,12 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
** ^A call to sqlite3_interrupt(D) that occurs when there are no running
** SQL statements is a no-op and has no effect on SQL statements
** that are started after the sqlite3_interrupt() call returns.
+**
+** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether
+** or not an interrupt is currently in effect for [database connection] D.
*/
SQLITE_API void sqlite3_interrupt(sqlite3*);
+SQLITE_API int sqlite3_is_interrupted(sqlite3*);
/*
** CAPI3REF: Determine If An SQL Statement Is Complete
@@ -3561,8 +3607,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
** information as is provided by the [sqlite3_profile()] callback.
** ^The P argument is a pointer to the [prepared statement] and the
-** X argument points to a 64-bit integer which is the estimated of
-** the number of nanosecond that the prepared statement took to run.
+** X argument points to a 64-bit integer which is approximately
+** the number of nanoseconds that the prepared statement took to run.
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
**
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
@@ -3625,7 +3671,7 @@ SQLITE_API int sqlite3_trace_v2(
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
-** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for
+** [sqlite3_step()] and [sqlite3_prepare()] and similar for
** database connection D. An example use for this
** interface is to keep a GUI updated during a large query.
**
@@ -3650,6 +3696,13 @@ SQLITE_API int sqlite3_trace_v2(
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
** database connections for the meaning of "modify" in this paragraph.
**
+** The progress handler callback would originally only be invoked from the
+** bytecode engine. It still might be invoked during [sqlite3_prepare()]
+** and similar because those routines might force a reparse of the schema
+** which involves running the bytecode engine. However, beginning with
+** SQLite version 3.41.0, the progress handler callback might also be
+** invoked directly from [sqlite3_prepare()] while analyzing and generating
+** code for complex queries.
*/
SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
@@ -3686,13 +3739,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
**
** <dl>
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
-** <dd>The database is opened in read-only mode. If the database does not
-** already exist, an error is returned.</dd>)^
+** <dd>The database is opened in read-only mode. If the database does
+** not already exist, an error is returned.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
-** <dd>The database is opened for reading and writing if possible, or reading
-** only if the file is write protected by the operating system. In either
-** case the database must already exist, otherwise an error is returned.</dd>)^
+** <dd>The database is opened for reading and writing if possible, or
+** reading only if the file is write protected by the operating
+** system. In either case the database must already exist, otherwise
+** an error is returned. For historical reasons, if opening in
+** read-write mode fails due to OS-level permissions, an attempt is
+** made to open it in read-only mode. [sqlite3_db_readonly()] can be
+** used to determine whether the database is actually
+** read-write.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
** <dd>The database is opened for reading and writing, and is created if
@@ -3730,6 +3788,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** <dd>The database is opened [shared cache] enabled, overriding
** the default shared cache setting provided by
** [sqlite3_enable_shared_cache()].)^
+** The [use of shared cache mode is discouraged] and hence shared cache
+** capabilities may be omitted from many builds of SQLite. In such cases,
+** this option is a no-op.
**
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
** <dd>The database is opened [shared cache] disabled, overriding
@@ -3745,7 +3806,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** to return an extended result code.</dd>
**
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
-** <dd>The database filename is not allowed to be a symbolic link</dd>
+** <dd>The database filename is not allowed to contain a symbolic link</dd>
** </dl>)^
**
** If the 3rd parameter to sqlite3_open_v2() is not one of the
@@ -4004,10 +4065,10 @@ SQLITE_API int sqlite3_open_v2(
**
** See the [URI filename] documentation for additional information.
*/
-SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
-SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
-SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
-SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
+SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
+SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
+SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
/*
** CAPI3REF: Translate filenames
@@ -4036,9 +4097,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
** return value from [sqlite3_db_filename()], then the result is
** undefined and is likely a memory access violation.
*/
-SQLITE_API const char *sqlite3_filename_database(const char*);
-SQLITE_API const char *sqlite3_filename_journal(const char*);
-SQLITE_API const char *sqlite3_filename_wal(const char*);
+SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
/*
** CAPI3REF: Database File Corresponding To A Journal
@@ -4104,14 +4165,14 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y).
*/
-SQLITE_API char *sqlite3_create_filename(
+SQLITE_API sqlite3_filename sqlite3_create_filename(
const char *zDatabase,
const char *zJournal,
const char *zWal,
int nParam,
const char **azParam
);
-SQLITE_API void sqlite3_free_filename(char*);
+SQLITE_API void sqlite3_free_filename(sqlite3_filename);
/*
** CAPI3REF: Error Codes And Messages
@@ -5670,10 +5731,21 @@ SQLITE_API int sqlite3_create_window_function(
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in
** schema structures such as [CHECK constraints], [DEFAULT clauses],
** [expression indexes], [partial indexes], or [generated columns].
-** The SQLITE_DIRECTONLY flags is a security feature which is recommended
-** for all [application-defined SQL functions], and especially for functions
-** that have side-effects or that could potentially leak sensitive
-** information.
+** <p>
+** The SQLITE_DIRECTONLY flag is recommended for any
+** [application-defined SQL function]
+** that has side-effects or that could potentially leak sensitive information.
+** This will prevent attacks in which an application is tricked
+** into using a database file that has had its schema surreptiously
+** modified to invoke the application-defined function in ways that are
+** harmful.
+** <p>
+** Some people say it is good practice to set SQLITE_DIRECTONLY on all
+** [application-defined SQL functions], regardless of whether or not they
+** are security sensitive, as doing so prevents those functions from being used
+** inside of the database schema, and thus ensures that the database
+** can be inspected and modified using generic tools (such as the [CLI])
+** that do not have access to the application-defined functions.
** </dd>
**
** [[SQLITE_INNOCUOUS]] <dt>SQLITE_INNOCUOUS</dt><dd>
@@ -5880,6 +5952,28 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
/*
+** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
+** METHOD: sqlite3_value
+**
+** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
+** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
+** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
+** returns something other than SQLITE_TEXT, then the return value from
+** sqlite3_value_encoding(X) is meaningless. ^Calls to
+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
+** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
+** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
+** thus change the return from subsequent calls to sqlite3_value_encoding(X).
+**
+** This routine is intended for used by applications that test and validate
+** the SQLite implementation. This routine is inquiring about the opaque
+** internal state of an [sqlite3_value] object. Ordinary applications should
+** not need to know what the internal state of an sqlite3_value object is and
+** hence should not need to use this interface.
+*/
+SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
+
+/*
** CAPI3REF: Finding The Subtype Of SQL Values
** METHOD: sqlite3_value
**
@@ -5931,7 +6025,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
**
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
** when first called if N is less than or equal to zero or if a memory
-** allocate error occurs.
+** allocation error occurs.
**
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the
@@ -6136,9 +6230,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
-** ^If the 3rd parameter to the sqlite3_result_text* interfaces
-** is negative, then SQLite takes result text from the 2nd parameter
-** through the first zero character.
+** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
+** other than sqlite3_result_text64() is negative, then SQLite computes
+** the string length itself by searching the 2nd parameter for the first
+** zero character.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
@@ -6634,7 +6729,7 @@ SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
** <li> [sqlite3_filename_wal()]
** </ul>
*/
-SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
+SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Determine if a database is read-only
@@ -6771,7 +6866,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** function C that is invoked prior to each autovacuum of the database
** file. ^The callback is passed a copy of the generic data pointer (P),
** the schema-name of the attached database that is being autovacuumed,
-** the the size of the database file in pages, the number of free pages,
+** the size of the database file in pages, the number of free pages,
** and the number of bytes per page, respectively. The callback should
** return the number of free pages that should be removed by the
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
@@ -6892,6 +6987,11 @@ SQLITE_API void *sqlite3_update_hook(
** to the same database. Sharing is enabled if the argument is true
** and disabled if the argument is false.)^
**
+** This interface is omitted if SQLite is compiled with
+** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
+** compile-time option is recommended because the
+** [use of shared cache mode is discouraged].
+**
** ^Cache sharing is enabled and disabled for an entire process.
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
** In prior versions of SQLite,
@@ -6990,7 +7090,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** ^The soft heap limit may not be greater than the hard heap limit.
** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
** is invoked with a value of N that is greater than the hard heap limit,
-** the the soft heap limit is set to the value of the hard heap limit.
+** the soft heap limit is set to the value of the hard heap limit.
** ^The soft heap limit is automatically enabled whenever the hard heap
** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
** the soft heap limit is outside the range of 1..N, then the soft heap
@@ -7252,15 +7352,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
SQLITE_API void sqlite3_reset_auto_extension(void);
/*
-** The interface to the virtual-table mechanism is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** Structures used by the virtual table interface
*/
typedef struct sqlite3_vtab sqlite3_vtab;
@@ -7378,10 +7469,10 @@ struct sqlite3_module {
** when the omit flag is true there is no guarantee that the constraint will
** not be checked again using byte code.)^
**
-** ^The idxNum and idxPtr values are recorded and passed into the
+** ^The idxNum and idxStr values are recorded and passed into the
** [xFilter] method.
-** ^[sqlite3_free()] is used to free idxPtr if and only if
-** needToFreeIdxPtr is true.
+** ^[sqlite3_free()] is used to free idxStr if and only if
+** needToFreeIdxStr is true.
**
** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
** the correct order to satisfy the ORDER BY clause so that no separate
@@ -7501,7 +7592,7 @@ struct sqlite3_index_info {
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
** tables, the collating sequence of constraints does not matter (for example
** because the constraints are numeric) and so the sqlite3_vtab_collation()
-** interface is no commonly needed.
+** interface is not commonly needed.
*/
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
@@ -7661,16 +7752,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
/*
-** The interface to the virtual-table mechanism defined above (back up
-** to a comment remarkably similar to this one) is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** CAPI3REF: A Handle To An Open BLOB
** KEYWORDS: {BLOB handle} {BLOB handles}
**
@@ -9285,7 +9366,7 @@ typedef struct sqlite3_backup sqlite3_backup;
** if the application incorrectly accesses the destination [database connection]
** and so no error code is reported, but the operations may malfunction
** nevertheless. Use of the destination database connection while a
-** backup is in progress might also also cause a mutex deadlock.
+** backup is in progress might also cause a mutex deadlock.
**
** If running in [shared cache mode], the application must
** guarantee that the shared cache used by the destination database
@@ -9713,7 +9794,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
*/
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
-#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
@@ -9873,7 +9954,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
** <li><p> Otherwise, "BINARY" is returned.
** </ol>
*/
-SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
/*
** CAPI3REF: Determine if a virtual table query is DISTINCT
@@ -10030,21 +10111,20 @@ SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
** is undefined and probably harmful.
**
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
-** sqlite3_vtab_in_next(X,P) must be one of the parameters to the
+** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
** xFilter method which invokes these routines, and specifically
** a parameter that was previously selected for all-at-once IN constraint
** processing use the [sqlite3_vtab_in()] interface in the
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
** an xFilter argument that was selected for all-at-once IN constraint
-** processing, then these routines return [SQLITE_MISUSE])^ or perhaps
-** exhibit some other undefined or harmful behavior.
+** processing, then these routines return [SQLITE_ERROR].)^
**
** ^(Use these routines to access all values on the right-hand side
** of the IN constraint using code like the following:
**
** <blockquote><pre>
** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
-** &nbsp; rc==SQLITE_OK && pVal
+** &nbsp; rc==SQLITE_OK && pVal;
** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
** &nbsp; ){
** &nbsp; // do something with pVal
@@ -10142,6 +10222,10 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
** managed by the prepared statement S and will be automatically freed when
** S is finalized.
**
+** Not all values are available for all query elements. When a value is
+** not available, the output variable is set to -1 if the value is numeric,
+** or to NULL if it is a string (SQLITE_SCANSTAT_NAME).
+**
** <dl>
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
@@ -10169,12 +10253,24 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
** description for the X-th loop.
**
-** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
+** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt>
** <dd>^The "int" variable pointed to by the V parameter will be set to the
-** "select-id" for the X-th loop. The select-id identifies which query or
-** subquery the loop is part of. The main query has a select-id of zero.
-** The select-id is the same value as is output in the first column
-** of an [EXPLAIN QUERY PLAN] query.
+** id for the X-th query plan element. The id value is unique within the
+** statement. The select-id is the same value as is output in the first
+** column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt>
+** <dd>The "int" variable pointed to by the V parameter will be set to the
+** the id of the parent of the current query element, if applicable, or
+** to zero if the query element has no parent. This is the same value as
+** returned in the second column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt>
+** <dd>The sqlite3_int64 output value is set to the number of cycles,
+** according to the processor time-stamp counter, that elapsed while the
+** query element was being processed. This value is not available for
+** all query elements - if it is unavailable the output variable is
+** set to -1.
** </dl>
*/
#define SQLITE_SCANSTAT_NLOOP 0
@@ -10183,12 +10279,14 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
#define SQLITE_SCANSTAT_NAME 3
#define SQLITE_SCANSTAT_EXPLAIN 4
#define SQLITE_SCANSTAT_SELECTID 5
+#define SQLITE_SCANSTAT_PARENTID 6
+#define SQLITE_SCANSTAT_NCYCLE 7
/*
** CAPI3REF: Prepared Statement Scan Status
** METHOD: sqlite3_stmt
**
-** This interface returns information about the predicted and measured
+** These interfaces return information about the predicted and measured
** performance for pStmt. Advanced applications can use this
** interface to compare the predicted and the measured performance and
** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
@@ -10199,19 +10297,25 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
**
** The "iScanStatusOp" parameter determines which status information to return.
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
-** of this interface is undefined.
-** ^The requested measurement is written into a variable pointed to by
-** the "pOut" parameter.
-** Parameter "idx" identifies the specific loop to retrieve statistics for.
-** Loops are numbered starting from zero. ^If idx is out of range - less than
-** zero or greater than or equal to the total number of loops used to implement
-** the statement - a non-zero value is returned and the variable that pOut
-** points to is unchanged.
-**
-** ^Statistics might not be available for all loops in all statements. ^In cases
-** where there exist loops with no available statistics, this function behaves
-** as if the loop did not exist - it returns non-zero and leave the variable
-** that pOut points to unchanged.
+** of this interface is undefined. ^The requested measurement is written into
+** a variable pointed to by the "pOut" parameter.
+**
+** The "flags" parameter must be passed a mask of flags. At present only
+** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
+** is specified, then status information is available for all elements
+** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
+** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
+** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
+** the EXPLAIN QUERY PLAN output) are available. Invoking API
+** sqlite3_stmt_scanstatus() is equivalent to calling
+** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter.
+**
+** Parameter "idx" identifies the specific query element to retrieve statistics
+** for. Query elements are numbered starting from zero. A value of -1 may be
+** to query for statistics regarding the entire query. ^If idx is out of range
+** - less than -1 or greater than or equal to the total number of query
+** elements used to implement the statement - a non-zero value is returned and
+** the variable that pOut points to is unchanged.
**
** See also: [sqlite3_stmt_scanstatus_reset()]
*/
@@ -10221,6 +10325,19 @@ SQLITE_API int sqlite3_stmt_scanstatus(
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
void *pOut /* Result written here */
);
+SQLITE_API int sqlite3_stmt_scanstatus_v2(
+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
+ int flags, /* Mask of flags defined below */
+ void *pOut /* Result written here */
+);
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** KEYWORDS: {scan status flags}
+*/
+#define SQLITE_SCANSTAT_COMPLEX 0x0001
/*
** CAPI3REF: Zero Scan-Status Counters
@@ -10311,6 +10428,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** function is not defined for operations on WITHOUT ROWID tables, or for
** DELETE operations on rowid tables.
**
+** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from
+** the previous call on the same [database connection] D, or NULL for
+** the first call on D.
+**
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
** provide additional information about a preupdate event. These routines
@@ -10716,6 +10837,19 @@ SQLITE_API int sqlite3_deserialize(
# undef double
#endif
+#if defined(__wasi__)
+# undef SQLITE_WASI
+# define SQLITE_WASI 1
+# undef SQLITE_OMIT_WAL
+# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */
+# ifndef SQLITE_OMIT_LOAD_EXTENSION
+# define SQLITE_OMIT_LOAD_EXTENSION
+# endif
+# ifndef SQLITE_THREADSAFE
+# define SQLITE_THREADSAFE 0
+# endif
+#endif
+
#if 0
} /* End of the 'extern "C"' block */
#endif
@@ -13145,11 +13279,16 @@ struct fts5_api {
/************** Continuing where we left off in sqliteInt.h ******************/
/*
+** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory.
+*/
+#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1
+
+/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
*/
#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H)
-#include "config.h"
+#include "sqlite_cfg.h"
#define SQLITECONFIG_H 1
#endif
@@ -14262,15 +14401,9 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
/*
** The datatype used to store estimates of the number of rows in a
-** table or index. This is an unsigned integer type. For 99.9% of
-** the world, a 32-bit integer is sufficient. But a 64-bit integer
-** can be used at compile-time if desired.
+** table or index.
*/
-#ifdef SQLITE_64BIT_STATS
- typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */
-#else
- typedef u32 tRowcnt; /* 32-bit is the default */
-#endif
+typedef u64 tRowcnt;
/*
** Estimated quantities used for query planning are stored as 16-bit
@@ -14416,9 +14549,9 @@ typedef INT16_TYPE LogEst;
** pointers. In that case, only verify 4-byte alignment.
*/
#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
-# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0)
+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
#else
-# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0)
+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
#endif
/*
@@ -14472,15 +14605,38 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace;
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \
|| defined(SQLITE_ENABLE_TREETRACE))
# define TREETRACE_ENABLED 1
-# define SELECTTRACE(K,P,S,X) \
+# define TREETRACE(K,P,S,X) \
if(sqlite3TreeTrace&(K)) \
sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
sqlite3DebugPrintf X
#else
-# define SELECTTRACE(K,P,S,X)
+# define TREETRACE(K,P,S,X)
# define TREETRACE_ENABLED 0
#endif
+/* TREETRACE flag meanings:
+**
+** 0x00000001 Beginning and end of SELECT processing
+** 0x00000002 WHERE clause processing
+** 0x00000004 Query flattener
+** 0x00000008 Result-set wildcard expansion
+** 0x00000010 Query name resolution
+** 0x00000020 Aggregate analysis
+** 0x00000040 Window functions
+** 0x00000080 Generated column names
+** 0x00000100 Move HAVING terms into WHERE
+** 0x00000200 Count-of-view optimization
+** 0x00000400 Compound SELECT processing
+** 0x00000800 Drop superfluous ORDER BY
+** 0x00001000 LEFT JOIN simplifies to JOIN
+** 0x00002000 Constant propagation
+** 0x00004000 Push-down optimization
+** 0x00008000 After all FROM-clause analysis
+** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
+** 0x00020000 Transform DISTINCT into GROUP BY
+** 0x00040000 SELECT tree dump after all code has been generated
+*/
+
/*
** Macros for "wheretrace"
*/
@@ -14493,6 +14649,36 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace;
# define WHERETRACE(K,X)
#endif
+/*
+** Bits for the sqlite3WhereTrace mask:
+**
+** (---any--) Top-level block structure
+** 0x-------F High-level debug messages
+** 0x----FFF- More detail
+** 0xFFFF---- Low-level debug messages
+**
+** 0x00000001 Code generation
+** 0x00000002 Solver
+** 0x00000004 Solver costs
+** 0x00000008 WhereLoop inserts
+**
+** 0x00000010 Display sqlite3_index_info xBestIndex calls
+** 0x00000020 Range an equality scan metrics
+** 0x00000040 IN operator decisions
+** 0x00000080 WhereLoop cost adjustements
+** 0x00000100
+** 0x00000200 Covering index decisions
+** 0x00000400 OR optimization
+** 0x00000800 Index scanner
+** 0x00001000 More details associated with code generation
+** 0x00002000
+** 0x00004000 Show all WHERE terms at key points
+** 0x00008000 Show the full SELECT statement at key places
+**
+** 0x00010000 Show more detail when printing WHERE terms
+** 0x00020000 Show WHERE terms returned from whereScanNext()
+*/
+
/*
** An instance of the following structure is used to store the busy-handler
@@ -14632,6 +14818,7 @@ typedef struct FuncDef FuncDef;
typedef struct FuncDefHash FuncDefHash;
typedef struct IdList IdList;
typedef struct Index Index;
+typedef struct IndexedExpr IndexedExpr;
typedef struct IndexSample IndexSample;
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
@@ -14697,6 +14884,7 @@ typedef struct With With;
#define MASKBIT32(n) (((unsigned int)1)<<(n))
#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0)
#define ALLBITS ((Bitmask)-1)
+#define TOPBIT (((Bitmask)1)<<(BMS-1))
/* A VList object records a mapping between parameters/variables/wildcards
** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer
@@ -14711,6 +14899,331 @@ typedef int VList;
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
** pointer types (i.e. FuncDef) defined above.
*/
+/************** Include os.h in the middle of sqliteInt.h ********************/
+/************** Begin file os.h **********************************************/
+/*
+** 2001 September 16
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This header file (together with is companion C source-code file
+** "os.c") attempt to abstract the underlying operating system so that
+** the SQLite library will work on both POSIX and windows systems.
+**
+** This header file is #include-ed by sqliteInt.h and thus ends up
+** being included by every source file.
+*/
+#ifndef _SQLITE_OS_H_
+#define _SQLITE_OS_H_
+
+/*
+** Attempt to automatically detect the operating system and setup the
+** necessary pre-processor macros for it.
+*/
+/************** Include os_setup.h in the middle of os.h *********************/
+/************** Begin file os_setup.h ****************************************/
+/*
+** 2013 November 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains pre-processor directives related to operating system
+** detection and/or setup.
+*/
+#ifndef SQLITE_OS_SETUP_H
+#define SQLITE_OS_SETUP_H
+
+/*
+** Figure out if we are dealing with Unix, Windows, or some other operating
+** system.
+**
+** After the following block of preprocess macros, all of
+**
+** SQLITE_OS_KV
+** SQLITE_OS_OTHER
+** SQLITE_OS_UNIX
+** SQLITE_OS_WIN
+**
+** will defined to either 1 or 0. One of them will be 1. The others will be 0.
+** If none of the macros are initially defined, then select either
+** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform.
+**
+** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application
+** must provide its own VFS implementation together with sqlite3_os_init()
+** and sqlite3_os_end() routines.
+*/
+#if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \
+ !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN)
+# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
+ defined(__MINGW32__) || defined(__BORLANDC__)
+# define SQLITE_OS_WIN 1
+# define SQLITE_OS_UNIX 0
+# else
+# define SQLITE_OS_WIN 0
+# define SQLITE_OS_UNIX 1
+# endif
+#endif
+#if SQLITE_OS_OTHER+1>1
+# undef SQLITE_OS_KV
+# define SQLITE_OS_KV 0
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+#endif
+#if SQLITE_OS_KV+1>1
+# undef SQLITE_OS_OTHER
+# define SQLITE_OS_OTHER 0
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# define SQLITE_OMIT_LOAD_EXTENSION 1
+# define SQLITE_OMIT_WAL 1
+# define SQLITE_OMIT_DEPRECATED 1
+# undef SQLITE_TEMP_STORE
+# define SQLITE_TEMP_STORE 3 /* Always use memory for temporary storage */
+# define SQLITE_DQS 0
+# define SQLITE_OMIT_SHARED_CACHE 1
+# define SQLITE_OMIT_AUTOINIT 1
+#endif
+#if SQLITE_OS_UNIX+1>1
+# undef SQLITE_OS_KV
+# define SQLITE_OS_KV 0
+# undef SQLITE_OS_OTHER
+# define SQLITE_OS_OTHER 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+#endif
+#if SQLITE_OS_WIN+1>1
+# undef SQLITE_OS_KV
+# define SQLITE_OS_KV 0
+# undef SQLITE_OS_OTHER
+# define SQLITE_OS_OTHER 0
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+#endif
+
+
+#endif /* SQLITE_OS_SETUP_H */
+
+/************** End of os_setup.h ********************************************/
+/************** Continuing where we left off in os.h *************************/
+
+/* If the SET_FULLSYNC macro is not defined above, then make it
+** a no-op
+*/
+#ifndef SET_FULLSYNC
+# define SET_FULLSYNC(x,y)
+#endif
+
+/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h
+*/
+#ifndef SQLITE_MAX_PATHLEN
+# define SQLITE_MAX_PATHLEN FILENAME_MAX
+#endif
+
+/* Maximum number of symlinks that will be resolved while trying to
+** expand a filename in xFullPathname() in the VFS.
+*/
+#ifndef SQLITE_MAX_SYMLINK
+# define SQLITE_MAX_SYMLINK 200
+#endif
+
+/*
+** The default size of a disk sector
+*/
+#ifndef SQLITE_DEFAULT_SECTOR_SIZE
+# define SQLITE_DEFAULT_SECTOR_SIZE 4096
+#endif
+
+/*
+** Temporary files are named starting with this prefix followed by 16 random
+** alphanumeric characters, and no file extension. They are stored in the
+** OS's standard temporary file directory, and are deleted prior to exit.
+** If sqlite is being embedded in another program, you may wish to change the
+** prefix to reflect your program's name, so that if your program exits
+** prematurely, old temporary files can be easily identified. This can be done
+** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
+**
+** 2006-10-31: The default prefix used to be "sqlite_". But then
+** Mcafee started using SQLite in their anti-virus product and it
+** started putting files with the "sqlite" name in the c:/temp folder.
+** This annoyed many windows users. Those users would then do a
+** Google search for "sqlite", find the telephone numbers of the
+** developers and call to wake them up at night and complain.
+** For this reason, the default name prefix is changed to be "sqlite"
+** spelled backwards. So the temp files are still identified, but
+** anybody smart enough to figure out the code is also likely smart
+** enough to know that calling the developer will not help get rid
+** of the file.
+*/
+#ifndef SQLITE_TEMP_FILE_PREFIX
+# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
+#endif
+
+/*
+** The following values may be passed as the second argument to
+** sqlite3OsLock(). The various locks exhibit the following semantics:
+**
+** SHARED: Any number of processes may hold a SHARED lock simultaneously.
+** RESERVED: A single process may hold a RESERVED lock on a file at
+** any time. Other processes may hold and obtain new SHARED locks.
+** PENDING: A single process may hold a PENDING lock on a file at
+** any one time. Existing SHARED locks may persist, but no new
+** SHARED locks may be obtained by other processes.
+** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
+**
+** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
+** process that requests an EXCLUSIVE lock may actually obtain a PENDING
+** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
+** sqlite3OsLock().
+*/
+#define NO_LOCK 0
+#define SHARED_LOCK 1
+#define RESERVED_LOCK 2
+#define PENDING_LOCK 3
+#define EXCLUSIVE_LOCK 4
+
+/*
+** File Locking Notes: (Mostly about windows but also some info for Unix)
+**
+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
+** those functions are not available. So we use only LockFile() and
+** UnlockFile().
+**
+** LockFile() prevents not just writing but also reading by other processes.
+** A SHARED_LOCK is obtained by locking a single randomly-chosen
+** byte out of a specific range of bytes. The lock byte is obtained at
+** random so two separate readers can probably access the file at the
+** same time, unless they are unlucky and choose the same lock byte.
+** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
+** There can only be one writer. A RESERVED_LOCK is obtained by locking
+** a single byte of the file that is designated as the reserved lock byte.
+** A PENDING_LOCK is obtained by locking a designated byte different from
+** the RESERVED_LOCK byte.
+**
+** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
+** which means we can use reader/writer locks. When reader/writer locks
+** are used, the lock is placed on the same range of bytes that is used
+** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
+** will support two or more Win95 readers or two or more WinNT readers.
+** But a single Win95 reader will lock out all WinNT readers and a single
+** WinNT reader will lock out all other Win95 readers.
+**
+** The following #defines specify the range of bytes used for locking.
+** SHARED_SIZE is the number of bytes available in the pool from which
+** a random byte is selected for a shared lock. The pool of bytes for
+** shared locks begins at SHARED_FIRST.
+**
+** The same locking strategy and
+** byte ranges are used for Unix. This leaves open the possibility of having
+** clients on win95, winNT, and unix all talking to the same shared file
+** and all locking correctly. To do so would require that samba (or whatever
+** tool is being used for file sharing) implements locks correctly between
+** windows and unix. I'm guessing that isn't likely to happen, but by
+** using the same locking range we are at least open to the possibility.
+**
+** Locking in windows is manditory. For this reason, we cannot store
+** actual data in the bytes used for locking. The pager never allocates
+** the pages involved in locking therefore. SHARED_SIZE is selected so
+** that all locks will fit on a single page even at the minimum page size.
+** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
+** is set high so that we don't have to allocate an unused page except
+** for very large databases. But one should test the page skipping logic
+** by setting PENDING_BYTE low and running the entire regression suite.
+**
+** Changing the value of PENDING_BYTE results in a subtly incompatible
+** file format. Depending on how it is changed, you might not notice
+** the incompatibility right away, even running a full regression test.
+** The default location of PENDING_BYTE is the first byte past the
+** 1GB boundary.
+**
+*/
+#ifdef SQLITE_OMIT_WSD
+# define PENDING_BYTE (0x40000000)
+#else
+# define PENDING_BYTE sqlite3PendingByte
+#endif
+#define RESERVED_BYTE (PENDING_BYTE+1)
+#define SHARED_FIRST (PENDING_BYTE+2)
+#define SHARED_SIZE 510
+
+/*
+** Wrapper around OS specific sqlite3_os_init() function.
+*/
+SQLITE_PRIVATE int sqlite3OsInit(void);
+
+/*
+** Functions for accessing sqlite3_file methods
+*/
+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*);
+SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
+SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
+SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size);
+SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int);
+SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize);
+SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int);
+SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int);
+SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
+SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*);
+SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
+#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
+SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id);
+SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
+SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
+SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id);
+SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int);
+#endif /* SQLITE_OMIT_WAL */
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
+
+
+/*
+** Functions for accessing sqlite3_vfs methods
+*/
+SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *);
+SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int);
+SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut);
+SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *);
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *);
+SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *);
+SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);
+SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *);
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
+SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int);
+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*);
+SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
+
+/*
+** Convenience functions for opening and closing files using
+** sqlite3_malloc() to obtain space for the file-handle structure.
+*/
+SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
+
+#endif /* _SQLITE_OS_H_ */
+
+/************** End of os.h **************************************************/
+/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include pager.h in the middle of sqliteInt.h *****************/
/************** Begin file pager.h *******************************************/
/*
@@ -15146,7 +15659,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
** reduce network bandwidth.
**
** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
-** standard SQLite. The other hints are provided for extentions that use
+** standard SQLite. The other hints are provided for extensions that use
** the SQLite parser and code generator but substitute their own storage
** engine.
*/
@@ -15292,7 +15805,15 @@ SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*);
SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*);
-SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*);
+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 */
+ 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 */
+ char **pzOut /* OUT: Write the error message string here */
+);
SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*);
SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*);
@@ -15331,6 +15852,8 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64);
+SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree*);
+
/*
** If we are not using shared cache, then there is no need to
** use mutexes to access the BtShared structures. So make the
@@ -15447,14 +15970,14 @@ struct VdbeOp {
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
char *zComment; /* Comment to improve readability */
#endif
-#ifdef VDBE_PROFILE
- u32 cnt; /* Number of times this instruction was executed */
- u64 cycles; /* Total time spent executing this instruction */
-#endif
#ifdef SQLITE_VDBE_COVERAGE
u32 iSrcLine; /* Source-code line that generated this opcode
** with flags in the upper 8 bits */
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ u64 nExec;
+ u64 nCycle;
+#endif
};
typedef struct VdbeOp VdbeOp;
@@ -15555,48 +16078,48 @@ 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_Goto 8 /* jump */
-#define OP_Gosub 9 /* jump */
-#define OP_InitCoroutine 10 /* jump */
-#define OP_Yield 11 /* jump */
-#define OP_MustBeInt 12 /* jump */
-#define OP_Jump 13 /* jump */
-#define OP_Once 14 /* jump */
-#define OP_If 15 /* jump */
-#define OP_IfNot 16 /* jump */
-#define OP_IsNullOrType 17 /* jump, synopsis: if typeof(r[P1]) IN (P3,5) goto P2 */
-#define OP_IfNullRow 18 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
+#define OP_Init 8 /* jump, 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_Jump 14 /* jump */
+#define OP_Once 15 /* jump */
+#define OP_If 16 /* jump */
+#define OP_IfNot 17 /* jump */
+#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_SeekLT 20 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekLE 21 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGE 22 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGT 23 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IfNotOpen 24 /* jump, synopsis: if( !csr[P1] ) goto P2 */
-#define OP_IfNoHope 25 /* jump, synopsis: key=r[P3@P4] */
-#define OP_NoConflict 26 /* jump, synopsis: key=r[P3@P4] */
-#define OP_NotFound 27 /* jump, synopsis: key=r[P3@P4] */
-#define OP_Found 28 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekRowid 29 /* jump, synopsis: intkey=r[P3] */
-#define OP_NotExists 30 /* jump, synopsis: intkey=r[P3] */
-#define OP_Last 31 /* jump */
-#define OP_IfSmaller 32 /* jump */
-#define OP_SorterSort 33 /* jump */
-#define OP_Sort 34 /* jump */
-#define OP_Rewind 35 /* jump */
-#define OP_SorterNext 36 /* jump */
-#define OP_Prev 37 /* jump */
-#define OP_Next 38 /* jump */
-#define OP_IdxLE 39 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IdxGT 40 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IdxLT 41 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IdxGE 42 /* jump, synopsis: key=r[P3@P4] */
+#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_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_NotExists 31 /* jump, synopsis: intkey=r[P3] */
+#define OP_Last 32 /* jump */
+#define OP_IfSmaller 33 /* jump */
+#define OP_SorterSort 34 /* jump */
+#define OP_Sort 35 /* jump */
+#define OP_Rewind 36 /* jump */
+#define OP_SorterNext 37 /* jump */
+#define OP_Prev 38 /* jump */
+#define OP_Next 39 /* jump */
+#define OP_IdxLE 40 /* jump, synopsis: key=r[P3@P4] */
+#define OP_IdxGT 41 /* jump, synopsis: key=r[P3@P4] */
+#define OP_IdxLT 42 /* jump, synopsis: key=r[P3@P4] */
#define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
#define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
-#define OP_RowSetRead 45 /* jump, synopsis: r[P3]=rowset(P1) */
-#define OP_RowSetTest 46 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
-#define OP_Program 47 /* jump */
-#define OP_FkIfZero 48 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
-#define OP_IfPos 49 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#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_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 */
#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */
@@ -15606,12 +16129,12 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]<r[P1] */
#define OP_Ge 57 /* jump, same as TK_GE, synopsis: IF r[P3]>=r[P1] */
#define OP_ElseEq 58 /* jump, same as TK_ESCAPE */
-#define OP_IfNotZero 59 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
-#define OP_DecrJumpZero 60 /* jump, synopsis: if (--r[P1])==0 goto P2 */
-#define OP_IncrVacuum 61 /* jump */
-#define OP_VNext 62 /* jump */
-#define OP_Filter 63 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */
-#define OP_Init 64 /* jump, synopsis: Start at P2 */
+#define OP_IfPos 59 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#define OP_IfNotZero 60 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
+#define OP_DecrJumpZero 61 /* jump, synopsis: if (--r[P1])==0 goto P2 */
+#define OP_IncrVacuum 62 /* jump */
+#define OP_VNext 63 /* jump */
+#define OP_Filter 64 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */
#define OP_PureFunc 65 /* synopsis: r[P3]=func(r[P2@NP]) */
#define OP_Function 66 /* synopsis: r[P3]=func(r[P2@NP]) */
#define OP_Return 67
@@ -15745,29 +16268,30 @@ typedef struct VdbeOpList VdbeOpList;
#define OPFLG_IN3 0x08 /* in3: P3 is an input */
#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_INITIALIZER {\
-/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,\
-/* 8 */ 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01, 0x03,\
-/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x09, 0x09, 0x09, 0x09,\
-/* 24 */ 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x01,\
-/* 32 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\
-/* 40 */ 0x01, 0x01, 0x01, 0x26, 0x26, 0x23, 0x0b, 0x01,\
-/* 48 */ 0x01, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
-/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x01, 0x01, 0x01,\
+/* 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, 0x01, 0x01, 0x41, 0x01, 0x41, 0x41,\
+/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\
+/* 48 */ 0x01, 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,\
/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\
-/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x00, 0x00,\
-/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x26, 0x26,\
+/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\
+/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26, 0x26,\
/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
-/* 112 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00,\
-/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
-/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
-/* 136 */ 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00,\
+/* 112 */ 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40, 0x00,\
+/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\
+/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,\
+/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\
/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,\
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x40,\
/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\
/* 184 */ 0x00, 0x00, 0x00,}
@@ -15822,14 +16346,20 @@ SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int);
#endif
SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
#ifndef SQLITE_OMIT_EXPLAIN
-SQLITE_PRIVATE void sqlite3VdbeExplain(Parse*,u8,const char*,...);
+SQLITE_PRIVATE int sqlite3VdbeExplain(Parse*,u8,const char*,...);
SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse*);
SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*);
# define ExplainQueryPlan(P) sqlite3VdbeExplain P
+# ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+# define ExplainQueryPlan2(V,P) (V = sqlite3VdbeExplain P)
+# else
+# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P)
+# endif
# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
#else
# define ExplainQueryPlan(P)
+# define ExplainQueryPlan2(V,P)
# define ExplainQueryPlanPop(P)
# define ExplainQueryPlanParent(P) 0
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
@@ -15845,6 +16375,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
+SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe*, int);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr);
SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
@@ -15859,6 +16390,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type);
SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse*);
SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*);
@@ -16000,8 +16532,12 @@ SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
SQLITE_PRIVATE void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*);
+SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(Vdbe*, int, int, int);
+SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int);
#else
-# define sqlite3VdbeScanStatus(a,b,c,d,e)
+# define sqlite3VdbeScanStatus(a,b,c,d,e,f)
+# define sqlite3VdbeScanStatusRange(a,b,c,d)
+# define sqlite3VdbeScanStatusCounters(a,b,c,d)
#endif
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
@@ -16207,297 +16743,6 @@ SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache);
/************** End of pcache.h **********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
-/************** Include os.h in the middle of sqliteInt.h ********************/
-/************** Begin file os.h **********************************************/
-/*
-** 2001 September 16
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This header file (together with is companion C source-code file
-** "os.c") attempt to abstract the underlying operating system so that
-** the SQLite library will work on both POSIX and windows systems.
-**
-** This header file is #include-ed by sqliteInt.h and thus ends up
-** being included by every source file.
-*/
-#ifndef _SQLITE_OS_H_
-#define _SQLITE_OS_H_
-
-/*
-** Attempt to automatically detect the operating system and setup the
-** necessary pre-processor macros for it.
-*/
-/************** Include os_setup.h in the middle of os.h *********************/
-/************** Begin file os_setup.h ****************************************/
-/*
-** 2013 November 25
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains pre-processor directives related to operating system
-** detection and/or setup.
-*/
-#ifndef SQLITE_OS_SETUP_H
-#define SQLITE_OS_SETUP_H
-
-/*
-** Figure out if we are dealing with Unix, Windows, or some other operating
-** system.
-**
-** After the following block of preprocess macros, all of SQLITE_OS_UNIX,
-** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of
-** the three will be 1. The other two will be 0.
-*/
-#if defined(SQLITE_OS_OTHER)
-# if SQLITE_OS_OTHER==1
-# undef SQLITE_OS_UNIX
-# define SQLITE_OS_UNIX 0
-# undef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# else
-# undef SQLITE_OS_OTHER
-# endif
-#endif
-#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
-# define SQLITE_OS_OTHER 0
-# ifndef SQLITE_OS_WIN
-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
- defined(__MINGW32__) || defined(__BORLANDC__)
-# define SQLITE_OS_WIN 1
-# define SQLITE_OS_UNIX 0
-# else
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 1
-# endif
-# else
-# define SQLITE_OS_UNIX 0
-# endif
-#else
-# ifndef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# endif
-#endif
-
-#endif /* SQLITE_OS_SETUP_H */
-
-/************** End of os_setup.h ********************************************/
-/************** Continuing where we left off in os.h *************************/
-
-/* If the SET_FULLSYNC macro is not defined above, then make it
-** a no-op
-*/
-#ifndef SET_FULLSYNC
-# define SET_FULLSYNC(x,y)
-#endif
-
-/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h
-*/
-#ifndef SQLITE_MAX_PATHLEN
-# define SQLITE_MAX_PATHLEN FILENAME_MAX
-#endif
-
-/* Maximum number of symlinks that will be resolved while trying to
-** expand a filename in xFullPathname() in the VFS.
-*/
-#ifndef SQLITE_MAX_SYMLINK
-# define SQLITE_MAX_SYMLINK 200
-#endif
-
-/*
-** The default size of a disk sector
-*/
-#ifndef SQLITE_DEFAULT_SECTOR_SIZE
-# define SQLITE_DEFAULT_SECTOR_SIZE 4096
-#endif
-
-/*
-** Temporary files are named starting with this prefix followed by 16 random
-** alphanumeric characters, and no file extension. They are stored in the
-** OS's standard temporary file directory, and are deleted prior to exit.
-** If sqlite is being embedded in another program, you may wish to change the
-** prefix to reflect your program's name, so that if your program exits
-** prematurely, old temporary files can be easily identified. This can be done
-** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
-**
-** 2006-10-31: The default prefix used to be "sqlite_". But then
-** Mcafee started using SQLite in their anti-virus product and it
-** started putting files with the "sqlite" name in the c:/temp folder.
-** This annoyed many windows users. Those users would then do a
-** Google search for "sqlite", find the telephone numbers of the
-** developers and call to wake them up at night and complain.
-** For this reason, the default name prefix is changed to be "sqlite"
-** spelled backwards. So the temp files are still identified, but
-** anybody smart enough to figure out the code is also likely smart
-** enough to know that calling the developer will not help get rid
-** of the file.
-*/
-#ifndef SQLITE_TEMP_FILE_PREFIX
-# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
-#endif
-
-/*
-** The following values may be passed as the second argument to
-** sqlite3OsLock(). The various locks exhibit the following semantics:
-**
-** SHARED: Any number of processes may hold a SHARED lock simultaneously.
-** RESERVED: A single process may hold a RESERVED lock on a file at
-** any time. Other processes may hold and obtain new SHARED locks.
-** PENDING: A single process may hold a PENDING lock on a file at
-** any one time. Existing SHARED locks may persist, but no new
-** SHARED locks may be obtained by other processes.
-** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
-**
-** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
-** process that requests an EXCLUSIVE lock may actually obtain a PENDING
-** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
-** sqlite3OsLock().
-*/
-#define NO_LOCK 0
-#define SHARED_LOCK 1
-#define RESERVED_LOCK 2
-#define PENDING_LOCK 3
-#define EXCLUSIVE_LOCK 4
-
-/*
-** File Locking Notes: (Mostly about windows but also some info for Unix)
-**
-** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
-** those functions are not available. So we use only LockFile() and
-** UnlockFile().
-**
-** LockFile() prevents not just writing but also reading by other processes.
-** A SHARED_LOCK is obtained by locking a single randomly-chosen
-** byte out of a specific range of bytes. The lock byte is obtained at
-** random so two separate readers can probably access the file at the
-** same time, unless they are unlucky and choose the same lock byte.
-** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
-** There can only be one writer. A RESERVED_LOCK is obtained by locking
-** a single byte of the file that is designated as the reserved lock byte.
-** A PENDING_LOCK is obtained by locking a designated byte different from
-** the RESERVED_LOCK byte.
-**
-** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
-** which means we can use reader/writer locks. When reader/writer locks
-** are used, the lock is placed on the same range of bytes that is used
-** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
-** will support two or more Win95 readers or two or more WinNT readers.
-** But a single Win95 reader will lock out all WinNT readers and a single
-** WinNT reader will lock out all other Win95 readers.
-**
-** The following #defines specify the range of bytes used for locking.
-** SHARED_SIZE is the number of bytes available in the pool from which
-** a random byte is selected for a shared lock. The pool of bytes for
-** shared locks begins at SHARED_FIRST.
-**
-** The same locking strategy and
-** byte ranges are used for Unix. This leaves open the possibility of having
-** clients on win95, winNT, and unix all talking to the same shared file
-** and all locking correctly. To do so would require that samba (or whatever
-** tool is being used for file sharing) implements locks correctly between
-** windows and unix. I'm guessing that isn't likely to happen, but by
-** using the same locking range we are at least open to the possibility.
-**
-** Locking in windows is manditory. For this reason, we cannot store
-** actual data in the bytes used for locking. The pager never allocates
-** the pages involved in locking therefore. SHARED_SIZE is selected so
-** that all locks will fit on a single page even at the minimum page size.
-** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
-** is set high so that we don't have to allocate an unused page except
-** for very large databases. But one should test the page skipping logic
-** by setting PENDING_BYTE low and running the entire regression suite.
-**
-** Changing the value of PENDING_BYTE results in a subtly incompatible
-** file format. Depending on how it is changed, you might not notice
-** the incompatibility right away, even running a full regression test.
-** The default location of PENDING_BYTE is the first byte past the
-** 1GB boundary.
-**
-*/
-#ifdef SQLITE_OMIT_WSD
-# define PENDING_BYTE (0x40000000)
-#else
-# define PENDING_BYTE sqlite3PendingByte
-#endif
-#define RESERVED_BYTE (PENDING_BYTE+1)
-#define SHARED_FIRST (PENDING_BYTE+2)
-#define SHARED_SIZE 510
-
-/*
-** Wrapper around OS specific sqlite3_os_init() function.
-*/
-SQLITE_PRIVATE int sqlite3OsInit(void);
-
-/*
-** Functions for accessing sqlite3_file methods
-*/
-SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*);
-SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
-SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
-SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size);
-SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int);
-SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize);
-SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int);
-SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int);
-SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
-SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*);
-SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
-#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
-SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id);
-SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
-#ifndef SQLITE_OMIT_WAL
-SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
-SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
-SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id);
-SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int);
-#endif /* SQLITE_OMIT_WAL */
-SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
-SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
-
-
-/*
-** Functions for accessing sqlite3_vfs methods
-*/
-SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *);
-SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int);
-SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut);
-SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *);
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *);
-SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *);
-SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);
-SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *);
-#endif /* SQLITE_OMIT_LOAD_EXTENSION */
-SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
-SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int);
-SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*);
-SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
-
-/*
-** Convenience functions for opening and closing files using
-** sqlite3_malloc() to obtain space for the file-handle structure.
-*/
-SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
-SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
-
-#endif /* _SQLITE_OS_H_ */
-
-/************** End of os.h **************************************************/
-/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include mutex.h in the middle of sqliteInt.h *****************/
/************** Begin file mutex.h *******************************************/
/*
@@ -16743,6 +16988,7 @@ struct Lookaside {
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
void *pStart; /* First byte of available memory space */
void *pEnd; /* First byte past end of available space */
+ void *pTrueEnd; /* True value of pEnd, when db->pnBytesFreed!=0 */
};
struct LookasideSlot {
LookasideSlot *pNext; /* Next buffer in the list of free buffers */
@@ -17087,6 +17333,8 @@ struct sqlite3 {
#define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */
#define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */
/* TH3 expects this value ^^^^^^^^^^ See flatten04.test */
+#define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */
+#define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
@@ -17171,8 +17419,14 @@ struct FuncDestructor {
** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
-** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS
+** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!!
** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
+**
+** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the
+** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is
+** used internally and if set means tha the function has side effects.
+** SQLITE_INNOCUOUS is used by application code and means "not unsafe".
+** See multiple instances of tag-20230109-1.
*/
#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
#define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */
@@ -17289,7 +17543,7 @@ struct FuncDestructor {
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define JFUNCTION(zName, nArg, iArg, xFunc) \
- {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|\
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\
SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
@@ -17481,6 +17735,7 @@ struct CollSeq {
#define SQLITE_AFF_NUMERIC 0x43 /* 'C' */
#define SQLITE_AFF_INTEGER 0x44 /* 'D' */
#define SQLITE_AFF_REAL 0x45 /* 'E' */
+#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
@@ -17659,7 +17914,7 @@ struct Table {
#ifndef SQLITE_OMIT_VIRTUALTABLE
# define IsVirtual(X) ((X)->eTabType==TABTYP_VTAB)
# define ExprIsVtab(X) \
- ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->eTabType==TABTYP_VTAB)
+ ((X)->op==TK_COLUMN && (X)->y.pTab->eTabType==TABTYP_VTAB)
#else
# define IsVirtual(X) 0
# define ExprIsVtab(X) 0
@@ -17876,10 +18131,22 @@ struct UnpackedRecord {
** The Index.onError field determines whether or not the indexed columns
** must be unique and what to do if they are not. When Index.onError=OE_None,
** it means this is not a unique index. Otherwise it is a unique index
-** and the value of Index.onError indicate the which conflict resolution
-** algorithm to employ whenever an attempt is made to insert a non-unique
+** and the value of Index.onError indicates which conflict resolution
+** algorithm to employ when an attempt is made to insert a non-unique
** element.
**
+** The colNotIdxed bitmask is used in combination with SrcItem.colUsed
+** for a fast test to see if an index can serve as a covering index.
+** colNotIdxed has a 1 bit for every column of the original table that
+** is *not* available in the index. Thus the expression
+** "colUsed & colNotIdxed" will be non-zero if the index is not a
+** covering index. The most significant bit of of colNotIdxed will always
+** be true (note-20221022-a). If a column beyond the 63rd column of the
+** table is used, the "colUsed & colNotIdxed" test will always be non-zero
+** and we have to assume either that the index is not covering, or use
+** an alternative (slower) algorithm to determine whether or not
+** the index is covering.
+**
** While parsing a CREATE TABLE or CREATE INDEX statement in order to
** generate VDBE code (as opposed to parsing one read from an sqlite_schema
** table as part of parsing an existing database schema), transient instances
@@ -17915,6 +18182,8 @@ struct Index {
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
+ unsigned bHasExpr:1; /* Index contains an expression, either a literal
+ ** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
@@ -17923,7 +18192,7 @@ struct Index {
tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
#endif
- Bitmask colNotIdxed; /* 0 for unindexed columns in pTab */
+ Bitmask colNotIdxed; /* Unindexed columns in pTab */
};
/*
@@ -17998,16 +18267,15 @@ struct AggInfo {
** from source tables rather than from accumulators */
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
** than the source table */
+ u16 nSortingColumn; /* Number of columns in the sorting index */
int sortingIdx; /* Cursor number of the sorting index */
int sortingIdxPTab; /* Cursor number of pseudo-table */
- int nSortingColumn; /* Number of columns in the sorting index */
- int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
+ int iFirstReg; /* First register in range for aCol[] and aFunc[] */
ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */
Expr *pCExpr; /* The original expression */
int iTable; /* Cursor number of the source table */
- int iMem; /* Memory location that acts as accumulator */
i16 iColumn; /* Column number within the source table */
i16 iSorterColumn; /* Column number in the sorting index */
} *aCol;
@@ -18018,15 +18286,28 @@ struct AggInfo {
struct AggInfo_func { /* For each aggregate function */
Expr *pFExpr; /* Expression encoding the function */
FuncDef *pFunc; /* The aggregate function implementation */
- int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
+#ifdef SQLITE_DEBUG
+ Select *pSelect; /* SELECT statement that this AggInfo supports */
+#endif
};
/*
+** Macros to compute aCol[] and aFunc[] register numbers.
+**
+** These macros should not be used prior to the call to
+** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg.
+** The assert()s that are part of this macro verify that constraint.
+*/
+#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I))
+#define AggInfoFuncReg(A,I) \
+ (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I))
+
+/*
** The datatype ynVar is a signed integer, either 16-bit or 32-bit.
** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater
** than 32767 we have to make it 32-bit. 16-bit is preferred because
@@ -18191,7 +18472,7 @@ struct Expr {
#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
#define EP_Win 0x008000 /* Contains window functions */
#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
-#define EP_MemToken 0x020000 /* Need to sqlite3DbFree() Expr.zToken */
+ /* 0x020000 // Available for reuse */
#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */
#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */
#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
@@ -18376,6 +18657,14 @@ struct IdList {
** The SrcItem object represents a single term in the FROM clause of a query.
** The SrcList object is mostly an array of SrcItems.
**
+** The jointype starts out showing the join type between the current table
+** and the next table on the list. The parser builds the list this way.
+** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each
+** jointype expresses the join between the table and the previous table.
+**
+** In the colUsed field, the high-order bit (bit 63) is set if the table
+** contains more than 63 columns and the 64-th or later column is used.
+**
** Union member validity:
**
** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
@@ -18415,14 +18704,14 @@ struct SrcItem {
Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */
IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */
} u3;
- Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
+ Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
union {
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
ExprList *pFuncArg; /* Arguments to table-valued-function */
} u1;
union {
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
- CteUse *pCteUse; /* CTE Usage info info fg.isCte is true */
+ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */
} u2;
};
@@ -18436,23 +18725,11 @@ struct OnOrUsing {
};
/*
-** The following structure describes the FROM clause of a SELECT statement.
-** Each table or subquery in the FROM clause is a separate element of
-** the SrcList.a[] array.
+** This object represents one or more tables that are the source of
+** content for an SQL statement. For example, a single SrcList object
+** is used to hold the FROM clause of a SELECT statement. SrcList also
+** represents the target tables for DELETE, INSERT, and UPDATE statements.
**
-** With the addition of multiple database support, the following structure
-** can also be used to describe a particular table such as the table that
-** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL,
-** such a table must be a simple name: ID. But in SQLite, the table can
-** now be identified by a database name, a dot, then the table name: ID.ID.
-**
-** The jointype starts out showing the join type between the current table
-** and the next table on the list. The parser builds the list this way.
-** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each
-** jointype expresses the join between the table and the previous table.
-**
-** In the colUsed field, the high-order bit (bit 63) is set if the table
-** contains more than 63 columns and the 64-th or later column is used.
*/
struct SrcList {
int nSrc; /* Number of tables or subqueries in the FROM clause */
@@ -18689,6 +18966,7 @@ struct Select {
#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 */
/* True if S exists and has SF_NestedFrom */
#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
@@ -18797,7 +19075,7 @@ struct SelectDest {
int iSDParm2; /* A second parameter for the eDest disposal method */
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
- char *zAffSdst; /* Affinity used when eDest==SRT_Set */
+ char *zAffSdst; /* Affinity used for SRT_Set */
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
};
@@ -18856,11 +19134,33 @@ struct TriggerPrg {
#else
typedef unsigned int yDbMask;
# define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0)
-# define DbMaskZero(M) (M)=0
-# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I))
-# define DbMaskAllZero(M) (M)==0
-# define DbMaskNonZero(M) (M)!=0
+# define DbMaskZero(M) ((M)=0)
+# define DbMaskSet(M,I) ((M)|=(((yDbMask)1)<<(I)))
+# define DbMaskAllZero(M) ((M)==0)
+# define DbMaskNonZero(M) ((M)!=0)
+#endif
+
+/*
+** For each index X that has as one of its arguments either an expression
+** or the name of a virtual generated column, and if X is in scope such that
+** the value of the expression can simply be read from the index, then
+** there is an instance of this object on the Parse.pIdxExpr list.
+**
+** During code generation, while generating code to evaluate expressions,
+** this list is consulted and if a matching expression is found, the value
+** is read from the index rather than being recomputed.
+*/
+struct IndexedExpr {
+ Expr *pExpr; /* The expression contained in the index */
+ int iDataCur; /* The data cursor associated with the index */
+ int iIdxCur; /* The index cursor */
+ int iIdxCol; /* The index column that contains value of pExpr */
+ u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */
+ IndexedExpr *pIENext; /* Next in a list of all indexed expressions */
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ const char *zIdxName; /* Name of index, used only for bytecode comments */
#endif
+};
/*
** An instance of the ParseCleanup object specifies an operation that
@@ -18903,7 +19203,7 @@ struct Parse {
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
- u8 disableVtab; /* Disable all virtual tables for this parse */
+ u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
@@ -18920,6 +19220,7 @@ struct Parse {
int nLabelAlloc; /* Number of slots in aLabel */
int *aLabel; /* Space to hold the labels */
ExprList *pConstExpr;/* Constant expressions */
+ IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
@@ -18943,6 +19244,9 @@ struct Parse {
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */
+#endif
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 bReturning; /* Coding a RETURNING trigger */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
@@ -19355,15 +19659,15 @@ struct Walker {
struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */
int *aiCol; /* array of column indexes */
struct IdxCover *pIdxCover; /* Check for index coverage */
- struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */
ExprList *pGroupBy; /* GROUP BY clause */
Select *pSelect; /* HAVING to WHERE clause ctx */
struct WindowRewrite *pRewrite; /* Window rewrite context */
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
+ struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
SrcItem *pSrcItem; /* A single FROM clause item */
- DbFixer *pFix;
+ DbFixer *pFix; /* See sqlite3FixSelect() */
} u;
};
@@ -19669,6 +19973,7 @@ SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64);
SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*);
SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*);
+SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3*, void*);
SQLITE_PRIVATE int sqlite3MallocSize(const void*);
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, const void*);
SQLITE_PRIVATE void *sqlite3PageMalloc(int);
@@ -19689,12 +19994,14 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void);
*/
#ifdef SQLITE_USE_ALLOCA
# define sqlite3StackAllocRaw(D,N) alloca(N)
-# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N)
+# define sqlite3StackAllocRawNN(D,N) alloca(N)
# define sqlite3StackFree(D,P)
+# define sqlite3StackFreeNN(D,P)
#else
# define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N)
-# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N)
+# define sqlite3StackAllocRawNN(D,N) sqlite3DbMallocRawNN(D,N)
# define sqlite3StackFree(D,P) sqlite3DbFree(D,P)
+# define sqlite3StackFreeNN(D,P) sqlite3DbFreeNN(D,P)
#endif
/* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they
@@ -19817,6 +20124,7 @@ SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window*);
#endif
SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*);
+SQLITE_PRIVATE void sqlite3ProgressCheck(Parse*);
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...);
SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int);
SQLITE_PRIVATE void sqlite3Dequote(char*);
@@ -19874,7 +20182,7 @@ SQLITE_PRIVATE const char *sqlite3ColumnColl(Column*);
SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect);
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char);
+SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
@@ -20193,7 +20501,8 @@ SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*);
SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*);
SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64);
-SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*);
+SQLITE_PRIVATE i64 sqlite3RealToI64(double);
+SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*);
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8);
SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*);
SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*);
@@ -20238,11 +20547,13 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v);
SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
+SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3*,const Table*);
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int);
SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2);
SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity);
SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table*,int);
SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
@@ -20259,6 +20570,9 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int);
#ifndef SQLITE_OMIT_DESERIALIZE
SQLITE_PRIVATE int sqlite3MemdbInit(void);
+SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs*);
+#else
+# define sqlite3IsMemdb(X) 0
#endif
SQLITE_PRIVATE const char *sqlite3ErrStr(int);
@@ -20309,7 +20623,6 @@ SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[];
SQLITE_PRIVATE const char sqlite3StdTypeAffinity[];
-SQLITE_PRIVATE const char sqlite3StdTypeMap[];
SQLITE_PRIVATE const char *sqlite3StdType[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char *sqlite3aLTb;
@@ -20399,7 +20712,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
-SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, int);
+SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64);
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8);
SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*);
@@ -20753,6 +21066,16 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse*, Expr*);
SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt);
#endif
+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
+SQLITE_PRIVATE int sqlite3KvvfsInit(void);
+#endif
+
+#if defined(VDBE_PROFILE) \
+ || defined(SQLITE_PERFORMANCE_TRACE) \
+ || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void);
+#endif
+
#endif /* SQLITEINT_H */
/************** End of sqliteInt.h *******************************************/
@@ -20794,101 +21117,6 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt);
*/
#ifdef SQLITE_PERFORMANCE_TRACE
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains inline asm code for retrieving "high-performance"
-** counters for x86 and x86_64 class CPUs.
-*/
-#ifndef SQLITE_HWTIME_H
-#define SQLITE_HWTIME_H
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if !defined(__STRICT_ANSI__) && \
- (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- /*
- ** asm() is needed for hardware timing support. Without asm(),
- ** disable the sqlite3Hwtime() routine.
- **
- ** sqlite3Hwtime() is only used for some obscure debugging
- ** and analysis configurations, not in any deliverable, so this
- ** should not be a great loss.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(SQLITE_HWTIME_H) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
static sqlite_uint64 g_start;
static sqlite_uint64 g_elapsed;
#define TIMER_START g_start=sqlite3Hwtime()
@@ -20984,7 +21212,7 @@ SQLITE_API extern int sqlite3_open_file_count;
** autoconf-based build
*/
#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H)
-/* #include "config.h" */
+/* #include "sqlite_cfg.h" */
#define SQLITECONFIG_H 1
#endif
@@ -21149,6 +21377,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
"DISABLE_SKIPAHEAD_DISTINCT",
#endif
+#ifdef SQLITE_DQS
+ "DQS=" CTIMEOPT_VAL(SQLITE_DQS),
+#endif
#ifdef SQLITE_ENABLE_8_3_NAMES
"ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES),
#endif
@@ -21639,9 +21870,6 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_OMIT_XFER_OPT
"OMIT_XFER_OPT",
#endif
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- "PCACHE_SEPARATE_HEADER",
-#endif
#ifdef SQLITE_PERFORMANCE_TRACE
"PERFORMANCE_TRACE",
#endif
@@ -22121,10 +22349,6 @@ SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY";
**
** sqlite3StdTypeAffinity[] The affinity associated with each entry
** in sqlite3StdType[].
-**
-** sqlite3StdTypeMap[] The type value (as returned from
-** sqlite3_column_type() or sqlite3_value_type())
-** for each entry in sqlite3StdType[].
*/
SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 };
SQLITE_PRIVATE const char sqlite3StdTypeAffinity[] = {
@@ -22135,14 +22359,6 @@ SQLITE_PRIVATE const char sqlite3StdTypeAffinity[] = {
SQLITE_AFF_REAL,
SQLITE_AFF_TEXT
};
-SQLITE_PRIVATE const char sqlite3StdTypeMap[] = {
- 0,
- SQLITE_BLOB,
- SQLITE_INTEGER,
- SQLITE_INTEGER,
- SQLITE_FLOAT,
- SQLITE_TEXT
-};
SQLITE_PRIVATE const char *sqlite3StdType[] = {
"ANY",
"BLOB",
@@ -22345,7 +22561,6 @@ struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
Op *aOp; /* Program instructions for parent frame */
- i64 *anExec; /* Event counters from parent frame */
Mem *aMem; /* Array of memory cells for parent frame */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u8 *aOnce; /* Bitmask used by OP_Once */
@@ -22561,10 +22776,19 @@ typedef unsigned bft; /* Bit Field Type */
/* The ScanStatus object holds a single value for the
** sqlite3_stmt_scanstatus() interface.
+**
+** aAddrRange[]:
+** This array is used by ScanStatus elements associated with EQP
+** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is
+** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[]
+** values should be summed to calculate the NCYCLE value. Each pair of
+** integer addresses is a start and end address (both inclusive) for a range
+** instructions. A start value of 0 indicates an empty range.
*/
typedef struct ScanStatus ScanStatus;
struct ScanStatus {
int addrExplain; /* OP_Explain for loop */
+ int aAddrRange[6];
int addrLoop; /* Address of "loops" counter */
int addrVisit; /* Address of "rows visited" counter */
int iSelectID; /* The "Select-ID" for this loop */
@@ -22594,7 +22818,7 @@ struct DblquoteStr {
*/
struct Vdbe {
sqlite3 *db; /* The database connection that owns this statement */
- Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
+ Vdbe **ppVPrev,*pVNext; /* Linked list of VDBEs with the same Vdbe.db */
Parse *pParse; /* Parsing context used to create this Vdbe */
ynVar nVar; /* Number of entries in aVar[] */
int nMem; /* Number of memory locations currently allocated */
@@ -22620,7 +22844,7 @@ struct Vdbe {
int nOp; /* Number of instructions in the program */
int nOpAlloc; /* Slots allocated for aOp[] */
Mem *aColName; /* Column names to return */
- Mem *pResultSet; /* Pointer to an array of results */
+ Mem *pResultRow; /* Current output row */
char *zErrMsg; /* Error message written here */
VList *pVList; /* Name of variables */
#ifndef SQLITE_OMIT_TRACE
@@ -22657,7 +22881,6 @@ struct Vdbe {
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
AuxData *pAuxData; /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- i64 *anExec; /* Number of times each op has been executed */
int nScan; /* Entries in aScan[] */
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
@@ -22824,6 +23047,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
+SQLITE_PRIVATE void sqlite3VdbeValueListFree(void*);
+
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*);
@@ -23152,6 +23377,8 @@ SQLITE_API int sqlite3_db_status(
sqlite3BtreeEnterAll(db);
db->pnBytesFreed = &nByte;
+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
+ db->lookaside.pEnd = db->lookaside.pStart;
for(i=0; i<db->nDb; i++){
Schema *pSchema = db->aDb[i].pSchema;
if( ALWAYS(pSchema!=0) ){
@@ -23177,6 +23404,7 @@ SQLITE_API int sqlite3_db_status(
}
}
db->pnBytesFreed = 0;
+ db->lookaside.pEnd = db->lookaside.pTrueEnd;
sqlite3BtreeLeaveAll(db);
*pHighwater = 0;
@@ -23194,9 +23422,12 @@ SQLITE_API int sqlite3_db_status(
int nByte = 0; /* Used to accumulate return value */
db->pnBytesFreed = &nByte;
- for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){
+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
+ db->lookaside.pEnd = db->lookaside.pStart;
+ for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){
sqlite3VdbeDelete(pVdbe);
}
+ db->lookaside.pEnd = db->lookaside.pTrueEnd;
db->pnBytesFreed = 0;
*pHighwater = 0; /* IMP: R-64479-57858 */
@@ -23532,7 +23763,7 @@ static void computeJD(DateTime *p){
p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000);
p->validJD = 1;
if( p->validHMS ){
- p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000);
+ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5);
if( p->validTZ ){
p->iJD -= p->tz*60000;
p->validYMD = 0;
@@ -24005,7 +24236,7 @@ static int parseModifier(
i64 iOrigJD; /* Original localtime */
i64 iGuess; /* Guess at the corresponding utc time */
int cnt = 0; /* Safety to prevent infinite loop */
- int iErr; /* Guess is off by this much */
+ i64 iErr; /* Guess is off by this much */
computeJD(p);
iGuess = iOrigJD = p->iJD;
@@ -24041,7 +24272,7 @@ static int parseModifier(
*/
if( sqlite3_strnicmp(z, "weekday ", 8)==0
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0
- && (n=(int)r)==r && n>=0 && r<7 ){
+ && r>=0.0 && r<7.0 && (n=(int)r)==r ){
sqlite3_int64 Z;
computeYMD_HMS(p);
p->validTZ = 0;
@@ -24722,9 +24953,11 @@ SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){
}
SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){
DO_OS_MALLOC_TEST(id);
+ assert( lockType>=SQLITE_LOCK_SHARED && lockType<=SQLITE_LOCK_EXCLUSIVE );
return id->pMethods->xLock(id, lockType);
}
SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){
+ assert( lockType==SQLITE_LOCK_NONE || lockType==SQLITE_LOCK_SHARED );
return id->pMethods->xUnlock(id, lockType);
}
SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
@@ -24839,6 +25072,7 @@ SQLITE_PRIVATE int sqlite3OsOpen(
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
** reaching the VFS. */
+ assert( zPath || (flags & SQLITE_OPEN_EXCLUSIVE) );
rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut);
assert( rc==SQLITE_OK || pFile->pMethods==0 );
return rc;
@@ -27154,9 +27388,13 @@ static int memsys5Roundup(int n){
if( n<=mem5.szAtom ) return mem5.szAtom;
return mem5.szAtom*2;
}
- if( n>0x40000000 ) return 0;
+ if( n>0x10000000 ){
+ if( n>0x40000000 ) return 0;
+ if( n>0x20000000 ) return 0x40000000;
+ return 0x20000000;
+ }
for(iFullSz=mem5.szAtom*8; iFullSz<n; iFullSz *= 4);
- if( (iFullSz/2)>=n ) return iFullSz/2;
+ if( (iFullSz/2)>=(i64)n ) return iFullSz/2;
return iFullSz;
}
@@ -29058,17 +29296,33 @@ static void mallocWithAlarm(int n, void **pp){
}
/*
+** Maximum size of any single memory allocation.
+**
+** This is not a limit on the total amount of memory used. This is
+** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc().
+**
+** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391
+** This provides a 256-byte safety margin for defense against 32-bit
+** signed integer overflow bugs when computing memory allocation sizes.
+** Paranoid applications might want to reduce the maximum allocation size
+** further for an even larger safety margin. 0x3fffffff or 0x0fffffff
+** or even smaller would be reasonable upper bounds on the size of a memory
+** allocations for most applications.
+*/
+#ifndef SQLITE_MAX_ALLOCATION_SIZE
+# define SQLITE_MAX_ALLOCATION_SIZE 2147483391
+#endif
+#if SQLITE_MAX_ALLOCATION_SIZE>2147483391
+# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391
+#endif
+
+/*
** Allocate memory. This routine is like sqlite3_malloc() except that it
** assumes the memory subsystem has already been initialized.
*/
SQLITE_PRIVATE void *sqlite3Malloc(u64 n){
void *p;
- if( n==0 || n>=0x7fffff00 ){
- /* A memory allocation of a number of bytes which is near the maximum
- ** signed integer value might cause an integer overflow inside of the
- ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
- ** 255 bytes of overhead. SQLite itself will never use anything near
- ** this amount. The only way to reach the limit is with sqlite3_malloc() */
+ if( n==0 || n>SQLITE_MAX_ALLOCATION_SIZE ){
p = 0;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
@@ -29104,7 +29358,7 @@ SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, const void *p){
- return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd);
+ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pTrueEnd);
}
#else
#define isLookaside(A,B) 0
@@ -29128,18 +29382,16 @@ static int lookasideMallocSize(sqlite3 *db, const void *p){
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, const void *p){
assert( p!=0 );
#ifdef SQLITE_DEBUG
- if( db==0 || !isLookaside(db,p) ){
- if( db==0 ){
- assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- }else{
- assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
- assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
- }
+ if( db==0 ){
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ }else if( !isLookaside(db,p) ){
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
}
#endif
if( db ){
- if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
+ if( ((uptr)p)<(uptr)(db->lookaside.pTrueEnd) ){
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
assert( sqlite3_mutex_held(db->mutex) );
@@ -29195,14 +29447,11 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
assert( p!=0 );
if( db ){
- if( db->pnBytesFreed ){
- measureAllocationSize(db, p);
- return;
- }
if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
#ifdef SQLITE_DEBUG
memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */
#endif
@@ -29213,6 +29462,7 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
#ifdef SQLITE_DEBUG
memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */
#endif
@@ -29221,6 +29471,10 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){
return;
}
}
+ if( db->pnBytesFreed ){
+ measureAllocationSize(db, p);
+ return;
+ }
}
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
@@ -29228,6 +29482,43 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
sqlite3_free(p);
}
+SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3 *db, void *p){
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( p!=0 );
+ if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
+#ifdef SQLITE_DEBUG
+ memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */
+#endif
+ pBuf->pNext = db->lookaside.pSmallFree;
+ db->lookaside.pSmallFree = pBuf;
+ return;
+ }
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
+ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
+#ifdef SQLITE_DEBUG
+ memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */
+#endif
+ pBuf->pNext = db->lookaside.pFree;
+ db->lookaside.pFree = pBuf;
+ return;
+ }
+ }
+ if( db->pnBytesFreed ){
+ measureAllocationSize(db, p);
+ return;
+ }
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
+ sqlite3_free(p);
+}
SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
if( p ) sqlite3DbFreeNN(db, p);
@@ -29527,9 +29818,14 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
*/
SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
int n;
+#ifdef SQLITE_DEBUG
+ /* Because of the way the parser works, the span is guaranteed to contain
+ ** at least one non-space character */
+ for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]<zEnd ); }
+#endif
while( sqlite3Isspace(zStart[0]) ) zStart++;
n = (int)(zEnd - zStart);
- while( ALWAYS(n>0) && sqlite3Isspace(zStart[n-1]) ) n--;
+ while( sqlite3Isspace(zStart[n-1]) ) n--;
return sqlite3DbStrNDup(db, zStart, n);
}
@@ -29563,8 +29859,13 @@ SQLITE_PRIVATE void *sqlite3OomFault(sqlite3 *db){
}
DisableLookaside;
if( db->pParse ){
+ Parse *pParse;
sqlite3ErrorMsg(db->pParse, "out of memory");
db->pParse->rc = SQLITE_NOMEM_BKPT;
+ for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){
+ pParse->nErr++;
+ pParse->rc = SQLITE_NOMEM;
+ }
}
}
return 0;
@@ -30363,13 +30664,26 @@ SQLITE_API void sqlite3_str_vappendf(
}
}
if( precision>1 ){
+ i64 nPrior = 1;
width -= precision-1;
if( width>1 && !flag_leftjustify ){
sqlite3_str_appendchar(pAccum, width-1, ' ');
width = 0;
}
- while( precision-- > 1 ){
- sqlite3_str_append(pAccum, buf, length);
+ sqlite3_str_append(pAccum, buf, length);
+ precision--;
+ while( precision > 1 ){
+ i64 nCopyBytes;
+ if( nPrior > precision-1 ) nPrior = precision - 1;
+ nCopyBytes = length*nPrior;
+ if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){
+ sqlite3StrAccumEnlarge(pAccum, nCopyBytes);
+ }
+ if( pAccum->accError ) break;
+ sqlite3_str_append(pAccum,
+ &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes);
+ precision -= nPrior;
+ nPrior *= 2;
}
}
bufpt = buf;
@@ -30597,9 +30911,9 @@ SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExp
** Return the number of bytes of text that StrAccum is able to accept
** after the attempted enlargement. The value returned might be zero.
*/
-SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, int N){
+SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, i64 N){
char *zNew;
- assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
+ assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */
if( p->accError ){
testcase(p->accError==SQLITE_TOOBIG);
testcase(p->accError==SQLITE_NOMEM);
@@ -30610,8 +30924,7 @@ SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, int N){
return p->nAlloc - p->nChar - 1;
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
- i64 szNew = p->nChar;
- szNew += (sqlite3_int64)N + 1;
+ i64 szNew = p->nChar + N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
** to avoid having to call this routine too often */
@@ -30641,7 +30954,8 @@ SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, int N){
return 0;
}
}
- return N;
+ assert( N>=0 && N<=0x7fffffff );
+ return (int)N;
}
/*
@@ -31237,6 +31551,13 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
sqlite3_str_appendf(&x, " ON");
}
+ if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
+ if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
+ if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
+ if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
+ if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte");
+ if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom");
+
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
@@ -31506,7 +31827,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewPop(&pView);
return;
}
- if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){
+ if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){
StrAccum x;
sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0);
sqlite3_str_appendf(&x, " fg.af=%x.%c",
@@ -31523,6 +31844,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
if( ExprHasVVAProperty(pExpr, EP_Immutable) ){
sqlite3_str_appendf(&x, " IMMUTABLE");
}
+ if( pExpr->pAggInfo!=0 ){
+ sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg);
+ }
sqlite3StrAccumFinish(&x);
}else{
zFlgs[0] = 0;
@@ -32334,16 +32658,41 @@ SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window *p){ sqlite3TreeViewWinFunc(
** This structure is the current state of the generator.
*/
static SQLITE_WSD struct sqlite3PrngType {
- unsigned char isInit; /* True if initialized */
- unsigned char i, j; /* State variables */
- unsigned char s[256]; /* State variables */
+ u32 s[16]; /* 64 bytes of chacha20 state */
+ u8 out[64]; /* Output bytes */
+ u8 n; /* Output bytes remaining */
} sqlite3Prng;
+
+/* The RFC-7539 ChaCha20 block function
+*/
+#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+#define QR(a, b, c, d) ( \
+ a += b, d ^= a, d = ROTL(d,16), \
+ c += d, b ^= c, b = ROTL(b,12), \
+ a += b, d ^= a, d = ROTL(d, 8), \
+ c += d, b ^= c, b = ROTL(b, 7))
+static void chacha_block(u32 *out, const u32 *in){
+ int i;
+ u32 x[16];
+ memcpy(x, in, 64);
+ for(i=0; i<10; i++){
+ QR(x[0], x[4], x[ 8], x[12]);
+ QR(x[1], x[5], x[ 9], x[13]);
+ QR(x[2], x[6], x[10], x[14]);
+ QR(x[3], x[7], x[11], x[15]);
+ QR(x[0], x[5], x[10], x[15]);
+ QR(x[1], x[6], x[11], x[12]);
+ QR(x[2], x[7], x[ 8], x[13]);
+ QR(x[3], x[4], x[ 9], x[14]);
+ }
+ for(i=0; i<16; i++) out[i] = x[i]+in[i];
+}
+
/*
** Return N random bytes.
*/
SQLITE_API void sqlite3_randomness(int N, void *pBuf){
- unsigned char t;
unsigned char *zBuf = pBuf;
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
@@ -32373,53 +32722,46 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){
sqlite3_mutex_enter(mutex);
if( N<=0 || pBuf==0 ){
- wsdPrng.isInit = 0;
+ wsdPrng.s[0] = 0;
sqlite3_mutex_leave(mutex);
return;
}
/* Initialize the state of the random number generator once,
- ** the first time this routine is called. The seed value does
- ** not need to contain a lot of randomness since we are not
- ** trying to do secure encryption or anything like that...
- **
- ** Nothing in this file or anywhere else in SQLite does any kind of
- ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
- ** number generator) not as an encryption device.
+ ** the first time this routine is called.
*/
- if( !wsdPrng.isInit ){
+ if( wsdPrng.s[0]==0 ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
- int i;
- char k[256];
- wsdPrng.j = 0;
- wsdPrng.i = 0;
+ static const u32 chacha20_init[] = {
+ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
+ };
+ memcpy(&wsdPrng.s[0], chacha20_init, 16);
if( NEVER(pVfs==0) ){
- memset(k, 0, sizeof(k));
+ memset(&wsdPrng.s[4], 0, 44);
}else{
- sqlite3OsRandomness(pVfs, 256, k);
+ sqlite3OsRandomness(pVfs, 44, (char*)&wsdPrng.s[4]);
}
- for(i=0; i<256; i++){
- wsdPrng.s[i] = (u8)i;
- }
- for(i=0; i<256; i++){
- wsdPrng.j += wsdPrng.s[i] + k[i];
- t = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = wsdPrng.s[i];
- wsdPrng.s[i] = t;
- }
- wsdPrng.isInit = 1;
+ wsdPrng.s[15] = wsdPrng.s[12];
+ wsdPrng.s[12] = 0;
+ wsdPrng.n = 0;
}
assert( N>0 );
- do{
- wsdPrng.i++;
- t = wsdPrng.s[wsdPrng.i];
- wsdPrng.j += t;
- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = t;
- t += wsdPrng.s[wsdPrng.i];
- *(zBuf++) = wsdPrng.s[t];
- }while( --N );
+ while( 1 /* exit by break */ ){
+ if( N<=wsdPrng.n ){
+ memcpy(zBuf, &wsdPrng.out[wsdPrng.n-N], N);
+ wsdPrng.n -= N;
+ break;
+ }
+ if( wsdPrng.n>0 ){
+ memcpy(zBuf, wsdPrng.out, wsdPrng.n);
+ N -= wsdPrng.n;
+ zBuf += wsdPrng.n;
+ }
+ wsdPrng.s[12]++;
+ chacha_block((u32*)wsdPrng.out, wsdPrng.s);
+ wsdPrng.n = 64;
+ }
sqlite3_mutex_leave(mutex);
}
@@ -33445,6 +33787,26 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *z
}
/*
+** Check for interrupts and invoke progress callback.
+*/
+SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){
+ sqlite3 *db = p->db;
+ if( AtomicLoad(&db->u1.isInterrupted) ){
+ p->nErr++;
+ p->rc = SQLITE_INTERRUPT;
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( db->xProgress && (++p->nProgressSteps)>=db->nProgressOps ){
+ if( db->xProgress(db->pProgressArg) ){
+ p->nErr++;
+ p->rc = SQLITE_INTERRUPT;
+ }
+ p->nProgressSteps = 0;
+ }
+#endif
+}
+
+/*
** Add an error message to pParse->zErrMsg and increment pParse->nErr.
**
** This function should be used to report any error that occurs while
@@ -33459,7 +33821,7 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
va_list ap;
sqlite3 *db = pParse->db;
assert( db!=0 );
- assert( db->pParse==pParse );
+ assert( db->pParse==pParse || db->pParse->pToplevel==pParse );
db->errByteOffset = -2;
va_start(ap, zFormat);
zMsg = sqlite3VMPrintf(db, zFormat, ap);
@@ -33901,11 +34263,14 @@ do_atof_calc:
#endif
/*
-** Render an signed 64-bit integer as text. Store the result in zOut[].
+** Render an signed 64-bit integer as text. Store the result in zOut[] and
+** return the length of the string that was stored, in bytes. The value
+** returned does not include the zero terminator at the end of the output
+** string.
**
** The caller must ensure that zOut[] is at least 21 bytes in size.
*/
-SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){
+SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){
int i;
u64 x;
char zTemp[22];
@@ -33922,6 +34287,7 @@ SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){
}while( x );
if( v<0 ) zTemp[i--] = '-';
memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i);
+ return sizeof(zTemp)-2-i;
}
/*
@@ -34983,6 +35349,104 @@ SQLITE_PRIVATE int sqlite3VListNameToNum(VList *pIn, const char *zName, int nNam
return 0;
}
+/*
+** High-resolution hardware timer used for debugging and testing only.
+*/
+#if defined(VDBE_PROFILE) \
+ || defined(SQLITE_PERFORMANCE_TRACE) \
+ || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+/************** Include hwtime.h in the middle of util.c *********************/
+/************** Begin file hwtime.h ******************************************/
+/*
+** 2008 May 27
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains inline asm code for retrieving "high-performance"
+** counters for x86 and x86_64 class CPUs.
+*/
+#ifndef SQLITE_HWTIME_H
+#define SQLITE_HWTIME_H
+
+/*
+** The following routine only works on pentium-class (or newer) processors.
+** It uses the RDTSC opcode to read the cycle count value out of the
+** processor and returns that value. This can be used for high-res
+** profiling.
+*/
+#if !defined(__STRICT_ANSI__) && \
+ (defined(__GNUC__) || defined(_MSC_VER)) && \
+ (defined(i386) || defined(__i386__) || defined(_M_IX86))
+
+ #if defined(__GNUC__)
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+ #elif defined(_MSC_VER)
+
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
+ __asm {
+ rdtsc
+ ret ; return value at EDX:EAX
+ }
+ }
+
+ #endif
+
+#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long long retval;
+ unsigned long junk;
+ __asm__ __volatile__ ("\n\
+ 1: mftbu %1\n\
+ mftb %L0\n\
+ mftbu %0\n\
+ cmpw %0,%1\n\
+ bne 1b"
+ : "=r" (retval), "=r" (junk));
+ return retval;
+ }
+
+#else
+
+ /*
+ ** asm() is needed for hardware timing support. Without asm(),
+ ** disable the sqlite3Hwtime() routine.
+ **
+ ** sqlite3Hwtime() is only used for some obscure debugging
+ ** and analysis configurations, not in any deliverable, so this
+ ** should not be a great loss.
+ */
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
+
+#endif
+
+#endif /* !defined(SQLITE_HWTIME_H) */
+
+/************** End of hwtime.h **********************************************/
+/************** Continuing where we left off in util.c ***********************/
+#endif
+
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
@@ -35153,12 +35617,13 @@ static HashElem *findElementWithHash(
count = pH->count;
}
if( pHash ) *pHash = h;
- while( count-- ){
+ while( count ){
assert( elem!=0 );
if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
return elem;
}
elem = elem->next;
+ count--;
}
return &nullElement;
}
@@ -35277,48 +35742,48 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 5 */ "Vacuum" OpHelp(""),
/* 6 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
/* 7 */ "VUpdate" OpHelp("data=r[P3@P2]"),
- /* 8 */ "Goto" OpHelp(""),
- /* 9 */ "Gosub" OpHelp(""),
- /* 10 */ "InitCoroutine" OpHelp(""),
- /* 11 */ "Yield" OpHelp(""),
- /* 12 */ "MustBeInt" OpHelp(""),
- /* 13 */ "Jump" OpHelp(""),
- /* 14 */ "Once" OpHelp(""),
- /* 15 */ "If" OpHelp(""),
- /* 16 */ "IfNot" OpHelp(""),
- /* 17 */ "IsNullOrType" OpHelp("if typeof(r[P1]) IN (P3,5) goto P2"),
- /* 18 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
+ /* 8 */ "Init" OpHelp("Start at P2"),
+ /* 9 */ "Goto" OpHelp(""),
+ /* 10 */ "Gosub" OpHelp(""),
+ /* 11 */ "InitCoroutine" OpHelp(""),
+ /* 12 */ "Yield" OpHelp(""),
+ /* 13 */ "MustBeInt" OpHelp(""),
+ /* 14 */ "Jump" OpHelp(""),
+ /* 15 */ "Once" OpHelp(""),
+ /* 16 */ "If" OpHelp(""),
+ /* 17 */ "IfNot" OpHelp(""),
+ /* 18 */ "IsType" OpHelp("if typeof(P1.P3) in P5 goto P2"),
/* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
- /* 20 */ "SeekLT" OpHelp("key=r[P3@P4]"),
- /* 21 */ "SeekLE" OpHelp("key=r[P3@P4]"),
- /* 22 */ "SeekGE" OpHelp("key=r[P3@P4]"),
- /* 23 */ "SeekGT" OpHelp("key=r[P3@P4]"),
- /* 24 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"),
- /* 25 */ "IfNoHope" OpHelp("key=r[P3@P4]"),
- /* 26 */ "NoConflict" OpHelp("key=r[P3@P4]"),
- /* 27 */ "NotFound" OpHelp("key=r[P3@P4]"),
- /* 28 */ "Found" OpHelp("key=r[P3@P4]"),
- /* 29 */ "SeekRowid" OpHelp("intkey=r[P3]"),
- /* 30 */ "NotExists" OpHelp("intkey=r[P3]"),
- /* 31 */ "Last" OpHelp(""),
- /* 32 */ "IfSmaller" OpHelp(""),
- /* 33 */ "SorterSort" OpHelp(""),
- /* 34 */ "Sort" OpHelp(""),
- /* 35 */ "Rewind" OpHelp(""),
- /* 36 */ "SorterNext" OpHelp(""),
- /* 37 */ "Prev" OpHelp(""),
- /* 38 */ "Next" OpHelp(""),
- /* 39 */ "IdxLE" OpHelp("key=r[P3@P4]"),
- /* 40 */ "IdxGT" OpHelp("key=r[P3@P4]"),
- /* 41 */ "IdxLT" OpHelp("key=r[P3@P4]"),
- /* 42 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 20 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
+ /* 21 */ "SeekLT" OpHelp("key=r[P3@P4]"),
+ /* 22 */ "SeekLE" OpHelp("key=r[P3@P4]"),
+ /* 23 */ "SeekGE" OpHelp("key=r[P3@P4]"),
+ /* 24 */ "SeekGT" OpHelp("key=r[P3@P4]"),
+ /* 25 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"),
+ /* 26 */ "IfNoHope" OpHelp("key=r[P3@P4]"),
+ /* 27 */ "NoConflict" OpHelp("key=r[P3@P4]"),
+ /* 28 */ "NotFound" OpHelp("key=r[P3@P4]"),
+ /* 29 */ "Found" OpHelp("key=r[P3@P4]"),
+ /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"),
+ /* 31 */ "NotExists" OpHelp("intkey=r[P3]"),
+ /* 32 */ "Last" OpHelp(""),
+ /* 33 */ "IfSmaller" OpHelp(""),
+ /* 34 */ "SorterSort" OpHelp(""),
+ /* 35 */ "Sort" OpHelp(""),
+ /* 36 */ "Rewind" OpHelp(""),
+ /* 37 */ "SorterNext" OpHelp(""),
+ /* 38 */ "Prev" OpHelp(""),
+ /* 39 */ "Next" OpHelp(""),
+ /* 40 */ "IdxLE" OpHelp("key=r[P3@P4]"),
+ /* 41 */ "IdxGT" OpHelp("key=r[P3@P4]"),
+ /* 42 */ "IdxLT" OpHelp("key=r[P3@P4]"),
/* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
/* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
- /* 45 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
- /* 46 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
- /* 47 */ "Program" OpHelp(""),
- /* 48 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
- /* 49 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+ /* 45 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 46 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
+ /* 47 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 48 */ "Program" OpHelp(""),
+ /* 49 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
/* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
/* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
/* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
@@ -35328,12 +35793,12 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 56 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
/* 57 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
/* 58 */ "ElseEq" OpHelp(""),
- /* 59 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
- /* 60 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
- /* 61 */ "IncrVacuum" OpHelp(""),
- /* 62 */ "VNext" OpHelp(""),
- /* 63 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"),
- /* 64 */ "Init" OpHelp("Start at P2"),
+ /* 59 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+ /* 60 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
+ /* 61 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
+ /* 62 */ "IncrVacuum" OpHelp(""),
+ /* 63 */ "VNext" OpHelp(""),
+ /* 64 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"),
/* 65 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"),
/* 66 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"),
/* 67 */ "Return" OpHelp(""),
@@ -35462,6 +35927,988 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
#endif
/************** End of opcodes.c *********************************************/
+/************** Begin file os_kv.c *******************************************/
+/*
+** 2022-09-06
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains an experimental VFS layer that operates on a
+** Key/Value storage engine where both keys and values must be pure
+** text.
+*/
+/* #include <sqliteInt.h> */
+#if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL))
+
+/*****************************************************************************
+** Debugging logic
+*/
+
+/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */
+#if 0
+#define SQLITE_KV_TRACE(X) printf X
+#else
+#define SQLITE_KV_TRACE(X)
+#endif
+
+/* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */
+#if 0
+#define SQLITE_KV_LOG(X) printf X
+#else
+#define SQLITE_KV_LOG(X)
+#endif
+
+
+/*
+** Forward declaration of objects used by this VFS implementation
+*/
+typedef struct KVVfsFile KVVfsFile;
+
+/* A single open file. There are only two files represented by this
+** VFS - the database and the rollback journal.
+*/
+struct KVVfsFile {
+ sqlite3_file base; /* IO methods */
+ const char *zClass; /* Storage class */
+ int isJournal; /* True if this is a journal file */
+ unsigned int nJrnl; /* Space allocated for aJrnl[] */
+ char *aJrnl; /* Journal content */
+ int szPage; /* Last known page size */
+ sqlite3_int64 szDb; /* Database file size. -1 means unknown */
+ char *aData; /* Buffer to hold page data */
+};
+#define SQLITE_KVOS_SZ 133073
+
+/*
+** Methods for KVVfsFile
+*/
+static int kvvfsClose(sqlite3_file*);
+static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
+static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
+static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size);
+static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size);
+static int kvvfsSyncDb(sqlite3_file*, int flags);
+static int kvvfsSyncJrnl(sqlite3_file*, int flags);
+static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize);
+static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize);
+static int kvvfsLock(sqlite3_file*, int);
+static int kvvfsUnlock(sqlite3_file*, int);
+static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
+static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg);
+static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg);
+static int kvvfsSectorSize(sqlite3_file*);
+static int kvvfsDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Methods for sqlite3_vfs
+*/
+static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
+static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int kvvfsSleep(sqlite3_vfs*, int microseconds);
+static int kvvfsCurrentTime(sqlite3_vfs*, double*);
+static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static sqlite3_vfs sqlite3OsKvvfsObject = {
+ 1, /* iVersion */
+ sizeof(KVVfsFile), /* szOsFile */
+ 1024, /* mxPathname */
+ 0, /* pNext */
+ "kvvfs", /* zName */
+ 0, /* pAppData */
+ kvvfsOpen, /* xOpen */
+ kvvfsDelete, /* xDelete */
+ kvvfsAccess, /* xAccess */
+ kvvfsFullPathname, /* xFullPathname */
+ kvvfsDlOpen, /* xDlOpen */
+ 0, /* xDlError */
+ 0, /* xDlSym */
+ 0, /* xDlClose */
+ kvvfsRandomness, /* xRandomness */
+ kvvfsSleep, /* xSleep */
+ kvvfsCurrentTime, /* xCurrentTime */
+ 0, /* xGetLastError */
+ kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */
+};
+
+/* Methods for sqlite3_file objects referencing a database file
+*/
+static sqlite3_io_methods kvvfs_db_io_methods = {
+ 1, /* iVersion */
+ kvvfsClose, /* xClose */
+ kvvfsReadDb, /* xRead */
+ kvvfsWriteDb, /* xWrite */
+ kvvfsTruncateDb, /* xTruncate */
+ kvvfsSyncDb, /* xSync */
+ kvvfsFileSizeDb, /* xFileSize */
+ kvvfsLock, /* xLock */
+ kvvfsUnlock, /* xUnlock */
+ kvvfsCheckReservedLock, /* xCheckReservedLock */
+ kvvfsFileControlDb, /* xFileControl */
+ kvvfsSectorSize, /* xSectorSize */
+ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0, /* xShmUnmap */
+ 0, /* xFetch */
+ 0 /* xUnfetch */
+};
+
+/* Methods for sqlite3_file objects referencing a rollback journal
+*/
+static sqlite3_io_methods kvvfs_jrnl_io_methods = {
+ 1, /* iVersion */
+ kvvfsClose, /* xClose */
+ kvvfsReadJrnl, /* xRead */
+ kvvfsWriteJrnl, /* xWrite */
+ kvvfsTruncateJrnl, /* xTruncate */
+ kvvfsSyncJrnl, /* xSync */
+ kvvfsFileSizeJrnl, /* xFileSize */
+ kvvfsLock, /* xLock */
+ kvvfsUnlock, /* xUnlock */
+ kvvfsCheckReservedLock, /* xCheckReservedLock */
+ kvvfsFileControlJrnl, /* xFileControl */
+ kvvfsSectorSize, /* xSectorSize */
+ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0, /* xShmUnmap */
+ 0, /* xFetch */
+ 0 /* xUnfetch */
+};
+
+/****** Storage subsystem **************************************************/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Forward declarations for the low-level storage engine
+*/
+static int kvstorageWrite(const char*, const char *zKey, const char *zData);
+static int kvstorageDelete(const char*, const char *zKey);
+static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
+#define KVSTORAGE_KEY_SZ 32
+
+/* Expand the key name with an appropriate prefix and put the result
+** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
+** KVSTORAGE_KEY_SZ bytes.
+*/
+static void kvstorageMakeKey(
+ const char *zClass,
+ const char *zKeyIn,
+ char *zKeyOut
+){
+ sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn);
+}
+
+/* Write content into a key. zClass is the particular namespace of the
+** underlying key/value store to use - either "local" or "session".
+**
+** Both zKey and zData are zero-terminated pure text strings.
+**
+** Return the number of errors.
+*/
+static int kvstorageWrite(
+ const char *zClass,
+ const char *zKey,
+ const char *zData
+){
+ FILE *fd;
+ char zXKey[KVSTORAGE_KEY_SZ];
+ kvstorageMakeKey(zClass, zKey, zXKey);
+ fd = fopen(zXKey, "wb");
+ if( fd ){
+ SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey,
+ (int)strlen(zData), zData,
+ strlen(zData)>50 ? "..." : ""));
+ fputs(zData, fd);
+ fclose(fd);
+ return 0;
+ }else{
+ return 1;
+ }
+}
+
+/* Delete a key (with its corresponding data) from the key/value
+** namespace given by zClass. If the key does not previously exist,
+** this routine is a no-op.
+*/
+static int kvstorageDelete(const char *zClass, const char *zKey){
+ char zXKey[KVSTORAGE_KEY_SZ];
+ kvstorageMakeKey(zClass, zKey, zXKey);
+ unlink(zXKey);
+ SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey));
+ return 0;
+}
+
+/* Read the value associated with a zKey from the key/value namespace given
+** by zClass and put the text data associated with that key in the first
+** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large
+** enough to hold it all. The value put into zBuf must always be zero
+** terminated, even if it gets truncated because nBuf is not large enough.
+**
+** Return the total number of bytes in the data, without truncation, and
+** not counting the final zero terminator. Return -1 if the key does
+** not exist.
+**
+** If nBuf<=0 then this routine simply returns the size of the data without
+** actually reading it.
+*/
+static int kvstorageRead(
+ const char *zClass,
+ const char *zKey,
+ char *zBuf,
+ int nBuf
+){
+ FILE *fd;
+ struct stat buf;
+ char zXKey[KVSTORAGE_KEY_SZ];
+ kvstorageMakeKey(zClass, zKey, zXKey);
+ if( access(zXKey, R_OK)!=0
+ || stat(zXKey, &buf)!=0
+ || !S_ISREG(buf.st_mode)
+ ){
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey));
+ return -1;
+ }
+ if( nBuf<=0 ){
+ return (int)buf.st_size;
+ }else if( nBuf==1 ){
+ zBuf[0] = 0;
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey,
+ (int)buf.st_size));
+ return (int)buf.st_size;
+ }
+ if( nBuf > buf.st_size + 1 ){
+ nBuf = buf.st_size + 1;
+ }
+ fd = fopen(zXKey, "rb");
+ if( fd==0 ){
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey));
+ return -1;
+ }else{
+ sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd);
+ fclose(fd);
+ zBuf[n] = 0;
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey,
+ n, zBuf, n>50 ? "..." : ""));
+ return (int)n;
+ }
+}
+
+/*
+** An internal level of indirection which enables us to replace the
+** kvvfs i/o methods with JavaScript implementations in WASM builds.
+** Maintenance reminder: if this struct changes in any way, the JSON
+** rendering of its structure must be updated in
+** sqlite3_wasm_enum_json(). There are no binary compatibility
+** concerns, so it does not need an iVersion member. This file is
+** necessarily always compiled together with sqlite3_wasm_enum_json(),
+** and JS code dynamically creates the mapping of members based on
+** that JSON description.
+*/
+typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
+struct sqlite3_kvvfs_methods {
+ int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
+ int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
+ int (*xDelete)(const char *zClass, const char *zKey);
+ const int nKeySize;
+};
+
+/*
+** This object holds the kvvfs I/O methods which may be swapped out
+** for JavaScript-side implementations in WASM builds. In such builds
+** it cannot be const, but in native builds it should be so that
+** the compiler can hopefully optimize this level of indirection out.
+** That said, kvvfs is intended primarily for use in WASM builds.
+**
+** Note that this is not explicitly flagged as static because the
+** amalgamation build will tag it with SQLITE_PRIVATE.
+*/
+#ifndef SQLITE_WASM
+const
+#endif
+SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
+kvstorageRead,
+kvstorageWrite,
+kvstorageDelete,
+KVSTORAGE_KEY_SZ
+};
+
+/****** Utility subroutines ************************************************/
+
+/*
+** Encode binary into the text encoded used to persist on disk.
+** The output text is stored in aOut[], which must be at least
+** nData+1 bytes in length.
+**
+** Return the actual length of the encoded text, not counting the
+** zero terminator at the end.
+**
+** Encoding format
+** ---------------
+**
+** * Non-zero bytes are encoded as upper-case hexadecimal
+**
+** * A sequence of one or more zero-bytes that are not at the
+** beginning of the buffer are encoded as a little-endian
+** base-26 number using a..z. "a" means 0. "b" means 1,
+** "z" means 25. "ab" means 26. "ac" means 52. And so forth.
+**
+** * Because there is no overlap between the encoding characters
+** of hexadecimal and base-26 numbers, it is always clear where
+** one stops and the next begins.
+*/
+static int kvvfsEncode(const char *aData, int nData, char *aOut){
+ int i, j;
+ const unsigned char *a = (const unsigned char*)aData;
+ for(i=j=0; i<nData; i++){
+ unsigned char c = a[i];
+ if( c!=0 ){
+ aOut[j++] = "0123456789ABCDEF"[c>>4];
+ aOut[j++] = "0123456789ABCDEF"[c&0xf];
+ }else{
+ /* A sequence of 1 or more zeros is stored as a little-endian
+ ** base-26 number using a..z as the digits. So one zero is "b".
+ ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb",
+ ** and so forth.
+ */
+ int k;
+ for(k=1; i+k<nData && a[i+k]==0; k++){}
+ i += k-1;
+ while( k>0 ){
+ aOut[j++] = 'a'+(k%26);
+ k /= 26;
+ }
+ }
+ }
+ aOut[j] = 0;
+ return j;
+}
+
+static const signed char kvvfsHexValue[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/*
+** Decode the text encoding back to binary. The binary content is
+** written into pOut, which must be at least nOut bytes in length.
+**
+** The return value is the number of bytes actually written into aOut[].
+*/
+static int kvvfsDecode(const char *a, char *aOut, int nOut){
+ int i, j;
+ int c;
+ const unsigned char *aIn = (const unsigned char*)a;
+ i = 0;
+ j = 0;
+ while( 1 ){
+ c = kvvfsHexValue[aIn[i]];
+ if( c<0 ){
+ int n = 0;
+ int mult = 1;
+ c = aIn[i];
+ if( c==0 ) break;
+ while( c>='a' && c<='z' ){
+ n += (c - 'a')*mult;
+ mult *= 26;
+ c = aIn[++i];
+ }
+ if( j+n>nOut ) return -1;
+ memset(&aOut[j], 0, n);
+ j += n;
+ if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */
+ }else{
+ aOut[j] = c<<4;
+ c = kvvfsHexValue[aIn[++i]];
+ if( c<0 ) break;
+ aOut[j++] += c;
+ i++;
+ }
+ }
+ return j;
+}
+
+/*
+** Decode a complete journal file. Allocate space in pFile->aJrnl
+** and store the decoding there. Or leave pFile->aJrnl set to NULL
+** if an error is encountered.
+**
+** The first few characters of the text encoding will be a little-endian
+** base-26 number (digits a..z) that is the total number of bytes
+** in the decoded journal file image. This base-26 number is followed
+** by a single space, then the encoding of the journal. The space
+** separator is required to act as a terminator for the base-26 number.
+*/
+static void kvvfsDecodeJournal(
+ KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */
+ const char *zTxt, /* Text encoding. Zero-terminated */
+ int nTxt /* Bytes in zTxt, excluding zero terminator */
+){
+ unsigned int n = 0;
+ int c, i, mult;
+ i = 0;
+ mult = 1;
+ while( (c = zTxt[i++])>='a' && c<='z' ){
+ n += (zTxt[i] - 'a')*mult;
+ mult *= 26;
+ }
+ sqlite3_free(pFile->aJrnl);
+ pFile->aJrnl = sqlite3_malloc64( n );
+ if( pFile->aJrnl==0 ){
+ pFile->nJrnl = 0;
+ return;
+ }
+ pFile->nJrnl = n;
+ n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl);
+ if( n<pFile->nJrnl ){
+ sqlite3_free(pFile->aJrnl);
+ pFile->aJrnl = 0;
+ pFile->nJrnl = 0;
+ }
+}
+
+/*
+** Read or write the "sz" element, containing the database file size.
+*/
+static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){
+ char zData[50];
+ zData[0] = 0;
+ sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1);
+ return strtoll(zData, 0, 0);
+}
+static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){
+ char zData[50];
+ sqlite3_snprintf(sizeof(zData), zData, "%lld", sz);
+ return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData);
+}
+
+/****** sqlite3_io_methods methods ******************************************/
+
+/*
+** Close an kvvfs-file.
+*/
+static int kvvfsClose(sqlite3_file *pProtoFile){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+
+ SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass,
+ pFile->isJournal ? "journal" : "db"));
+ sqlite3_free(pFile->aJrnl);
+ sqlite3_free(pFile->aData);
+ return SQLITE_OK;
+}
+
+/*
+** Read from the -journal file.
+*/
+static int kvvfsReadJrnl(
+ sqlite3_file *pProtoFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ assert( pFile->isJournal );
+ SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ if( pFile->aJrnl==0 ){
+ int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0);
+ char *aTxt;
+ if( szTxt<=4 ){
+ return SQLITE_IOERR;
+ }
+ aTxt = sqlite3_malloc64( szTxt+1 );
+ if( aTxt==0 ) return SQLITE_NOMEM;
+ kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1);
+ kvvfsDecodeJournal(pFile, aTxt, szTxt);
+ sqlite3_free(aTxt);
+ if( pFile->aJrnl==0 ) return SQLITE_IOERR;
+ }
+ if( iOfst+iAmt>pFile->nJrnl ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ memcpy(zBuf, pFile->aJrnl+iOfst, iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Read from the database file.
+*/
+static int kvvfsReadDb(
+ sqlite3_file *pProtoFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ unsigned int pgno;
+ int got, n;
+ char zKey[30];
+ char *aData = pFile->aData;
+ assert( iOfst>=0 );
+ assert( iAmt>=0 );
+ SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ if( iOfst+iAmt>=512 ){
+ if( (iOfst % iAmt)!=0 ){
+ return SQLITE_IOERR_READ;
+ }
+ if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){
+ return SQLITE_IOERR_READ;
+ }
+ pFile->szPage = iAmt;
+ pgno = 1 + iOfst/iAmt;
+ }else{
+ pgno = 1;
+ }
+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
+ got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey,
+ aData, SQLITE_KVOS_SZ-1);
+ if( got<0 ){
+ n = 0;
+ }else{
+ aData[got] = 0;
+ if( iOfst+iAmt<512 ){
+ int k = iOfst+iAmt;
+ aData[k*2] = 0;
+ n = kvvfsDecode(aData, &aData[2000], SQLITE_KVOS_SZ-2000);
+ if( n>=iOfst+iAmt ){
+ memcpy(zBuf, &aData[2000+iOfst], iAmt);
+ n = iAmt;
+ }else{
+ n = 0;
+ }
+ }else{
+ n = kvvfsDecode(aData, zBuf, iAmt);
+ }
+ }
+ if( n<iAmt ){
+ memset(zBuf+n, 0, iAmt-n);
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ return SQLITE_OK;
+}
+
+
+/*
+** Write into the -journal file.
+*/
+static int kvvfsWriteJrnl(
+ sqlite3_file *pProtoFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ sqlite3_int64 iEnd = iOfst+iAmt;
+ SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ if( iEnd>=0x10000000 ) return SQLITE_FULL;
+ if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){
+ char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd);
+ if( aNew==0 ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ pFile->aJrnl = aNew;
+ if( pFile->nJrnl<iOfst ){
+ memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl);
+ }
+ pFile->nJrnl = iEnd;
+ }
+ memcpy(pFile->aJrnl+iOfst, zBuf, iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Write into the database file.
+*/
+static int kvvfsWriteDb(
+ sqlite3_file *pProtoFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ unsigned int pgno;
+ char zKey[30];
+ char *aData = pFile->aData;
+ SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ assert( iAmt>=512 && iAmt<=65536 );
+ assert( (iAmt & (iAmt-1))==0 );
+ assert( pFile->szPage<0 || pFile->szPage==iAmt );
+ pFile->szPage = iAmt;
+ pgno = 1 + iOfst/iAmt;
+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
+ kvvfsEncode(zBuf, iAmt, aData);
+ if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){
+ return SQLITE_IOERR;
+ }
+ if( iOfst+iAmt > pFile->szDb ){
+ pFile->szDb = iOfst + iAmt;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Truncate an kvvfs-file.
+*/
+static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size));
+ assert( size==0 );
+ sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl");
+ sqlite3_free(pFile->aJrnl);
+ pFile->aJrnl = 0;
+ pFile->nJrnl = 0;
+ return SQLITE_OK;
+}
+static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ if( pFile->szDb>size
+ && pFile->szPage>0
+ && (size % pFile->szPage)==0
+ ){
+ char zKey[50];
+ unsigned int pgno, pgnoMax;
+ SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size));
+ pgno = 1 + size/pFile->szPage;
+ pgnoMax = 2 + pFile->szDb/pFile->szPage;
+ while( pgno<=pgnoMax ){
+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
+ sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey);
+ pgno++;
+ }
+ pFile->szDb = size;
+ return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK;
+ }
+ return SQLITE_IOERR;
+}
+
+/*
+** Sync an kvvfs-file.
+*/
+static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){
+ int i, n;
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ char *zOut;
+ SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass));
+ if( pFile->nJrnl<=0 ){
+ return kvvfsTruncateJrnl(pProtoFile, 0);
+ }
+ zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 );
+ if( zOut==0 ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ n = pFile->nJrnl;
+ i = 0;
+ do{
+ zOut[i++] = 'a' + (n%26);
+ n /= 26;
+ }while( n>0 );
+ zOut[i++] = ' ';
+ kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]);
+ i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut);
+ sqlite3_free(zOut);
+ return i ? SQLITE_IOERR : SQLITE_OK;
+}
+static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current file-size of an kvvfs-file.
+*/
+static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass));
+ *pSize = pFile->nJrnl;
+ return SQLITE_OK;
+}
+static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass));
+ if( pFile->szDb>=0 ){
+ *pSize = pFile->szDb;
+ }else{
+ *pSize = kvvfsReadFileSize(pFile);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Lock an kvvfs-file.
+*/
+static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ assert( !pFile->isJournal );
+ SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock));
+
+ if( eLock!=SQLITE_LOCK_NONE ){
+ pFile->szDb = kvvfsReadFileSize(pFile);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Unlock an kvvfs-file.
+*/
+static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ assert( !pFile->isJournal );
+ SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock));
+ if( eLock==SQLITE_LOCK_NONE ){
+ pFile->szDb = -1;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an kvvfs-file.
+*/
+static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){
+ SQLITE_KV_LOG(("xCheckReservedLock\n"));
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on an kvvfs-file.
+*/
+static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){
+ SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op));
+ return SQLITE_NOTFOUND;
+}
+static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){
+ SQLITE_KV_LOG(("xFileControl(%d) on database\n", op));
+ if( op==SQLITE_FCNTL_SYNC ){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ int rc = SQLITE_OK;
+ SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass));
+ if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){
+ rc = SQLITE_IOERR;
+ }
+ return rc;
+ }
+ return SQLITE_NOTFOUND;
+}
+
+/*
+** Return the sector-size in bytes for an kvvfs-file.
+*/
+static int kvvfsSectorSize(sqlite3_file *pFile){
+ return 512;
+}
+
+/*
+** Return the device characteristic flags supported by an kvvfs-file.
+*/
+static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){
+ return 0;
+}
+
+/****** sqlite3_vfs methods *************************************************/
+
+/*
+** Open an kvvfs file handle.
+*/
+static int kvvfsOpen(
+ sqlite3_vfs *pProtoVfs,
+ const char *zName,
+ sqlite3_file *pProtoFile,
+ int flags,
+ int *pOutFlags
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ if( zName==0 ) zName = "";
+ SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName));
+ if( strcmp(zName, "local")==0
+ || strcmp(zName, "session")==0
+ ){
+ pFile->isJournal = 0;
+ pFile->base.pMethods = &kvvfs_db_io_methods;
+ }else
+ if( strcmp(zName, "local-journal")==0
+ || strcmp(zName, "session-journal")==0
+ ){
+ pFile->isJournal = 1;
+ pFile->base.pMethods = &kvvfs_jrnl_io_methods;
+ }else{
+ return SQLITE_CANTOPEN;
+ }
+ if( zName[0]=='s' ){
+ pFile->zClass = "session";
+ }else{
+ pFile->zClass = "local";
+ }
+ pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ);
+ if( pFile->aData==0 ){
+ return SQLITE_NOMEM;
+ }
+ pFile->aJrnl = 0;
+ pFile->nJrnl = 0;
+ pFile->szPage = -1;
+ pFile->szDb = -1;
+ return SQLITE_OK;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ if( strcmp(zPath, "local-journal")==0 ){
+ sqlite3KvvfsMethods.xDelete("local", "jrnl");
+ }else
+ if( strcmp(zPath, "session-journal")==0 ){
+ sqlite3KvvfsMethods.xDelete("session", "jrnl");
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int kvvfsAccess(
+ sqlite3_vfs *pProtoVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath));
+ if( strcmp(zPath, "local-journal")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0;
+ }else
+ if( strcmp(zPath, "session-journal")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0;
+ }else
+ if( strcmp(zPath, "local")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0;
+ }else
+ if( strcmp(zPath, "session")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0;
+ }else
+ {
+ *pResOut = 0;
+ }
+ SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut));
+ return SQLITE_OK;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (INST_MAX_PATHNAME+1) bytes.
+*/
+static int kvvfsFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ size_t nPath;
+#ifdef SQLITE_OS_KV_ALWAYS_LOCAL
+ zPath = "local";
+#endif
+ nPath = strlen(zPath);
+ SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath));
+ if( nOut<nPath+1 ) nPath = nOut - 1;
+ memcpy(zOut, zPath, nPath);
+ zOut[nPath] = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return 0;
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ memset(zBufOut, 0, nByte);
+ return nByte;
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ sqlite3_int64 i = 0;
+ int rc;
+ rc = kvvfsCurrentTimeInt64(0, &i);
+ *pTimeOut = i/86400000.0;
+ return rc;
+}
+#include <sys/time.h>
+static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
+ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
+ struct timeval sNow;
+ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */
+ *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
+ return SQLITE_OK;
+}
+#endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */
+
+#if SQLITE_OS_KV
+/*
+** This routine is called initialize the KV-vfs as the default VFS.
+*/
+SQLITE_API int sqlite3_os_init(void){
+ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1);
+}
+SQLITE_API int sqlite3_os_end(void){
+ return SQLITE_OK;
+}
+#endif /* SQLITE_OS_KV */
+
+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
+SQLITE_PRIVATE int sqlite3KvvfsInit(void){
+ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0);
+}
+#endif
+
+/************** End of os_kv.c ***********************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
@@ -35552,15 +36999,16 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/*
** standard include files.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <sys/types.h> /* amalgamator: keep */
+#include <sys/stat.h> /* amalgamator: keep */
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <unistd.h>
+#include <unistd.h> /* amalgamator: keep */
/* #include <time.h> */
-#include <sys/time.h>
+#include <sys/time.h> /* amalgamator: keep */
#include <errno.h>
-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
+ && !defined(SQLITE_WASI)
# include <sys/mman.h>
#endif
@@ -35648,9 +37096,46 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
*/
#define SQLITE_MAX_SYMLINKS 100
+/*
+** Remove and stub certain info for WASI (WebAssembly System
+** Interface) builds.
+*/
+#ifdef SQLITE_WASI
+# undef HAVE_FCHMOD
+# undef HAVE_FCHOWN
+# undef HAVE_MREMAP
+# define HAVE_MREMAP 0
+# ifndef SQLITE_DEFAULT_UNIX_VFS
+# define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile"
+ /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */
+# endif
+# ifndef F_RDLCK
+# define F_RDLCK 0
+# define F_WRLCK 1
+# define F_UNLCK 2
+# if __LONG_MAX == 0x7fffffffL
+# define F_GETLK 12
+# define F_SETLK 13
+# define F_SETLKW 14
+# else
+# define F_GETLK 5
+# define F_SETLK 6
+# define F_SETLKW 7
+# endif
+# endif
+#else /* !SQLITE_WASI */
+# ifndef HAVE_FCHMOD
+# define HAVE_FCHMOD
+# endif
+#endif /* SQLITE_WASI */
+
+#ifdef SQLITE_WASI
+# define osGetpid(X) (pid_t)1
+#else
/* Always cast the getpid() return type for compatibility with
** kernel modules in VxWorks. */
-#define osGetpid(X) (pid_t)getpid()
+# define osGetpid(X) (pid_t)getpid()
+#endif
/*
** Only set the lastErrno if the error code is a real error and not
@@ -35922,7 +37407,11 @@ static struct unix_syscall {
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
+#if defined(HAVE_FCHMOD)
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
+#else
+ { "fchmod", (sqlite3_syscall_ptr)0, 0 },
+#endif
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
@@ -35958,14 +37447,16 @@ static struct unix_syscall {
#endif
#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
+ && !defined(SQLITE_WASI)
{ "mmap", (sqlite3_syscall_ptr)mmap, 0 },
#else
{ "mmap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent)
-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
+ && !defined(SQLITE_WASI)
{ "munmap", (sqlite3_syscall_ptr)munmap, 0 },
#else
{ "munmap", (sqlite3_syscall_ptr)0, 0 },
@@ -36151,6 +37642,9 @@ static int robust_open(const char *z, int f, mode_t m){
break;
}
if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
+ if( (f & (O_EXCL|O_CREAT))==(O_EXCL|O_CREAT) ){
+ (void)osUnlink(z);
+ }
osClose(fd);
sqlite3_log(SQLITE_WARNING,
"attempt to open \"%s\" as file descriptor %d", z, fd);
@@ -37113,7 +38607,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
**
** UNLOCKED -> SHARED
** SHARED -> RESERVED
-** SHARED -> (PENDING) -> EXCLUSIVE
+** SHARED -> EXCLUSIVE
** RESERVED -> (PENDING) -> EXCLUSIVE
** PENDING -> EXCLUSIVE
**
@@ -37146,19 +38640,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** A RESERVED lock is implemented by grabbing a write-lock on the
** 'reserved byte'.
**
- ** A process may only obtain a PENDING lock after it has obtained a
- ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
- ** on the 'pending byte'. This ensures that no new SHARED locks can be
- ** obtained, but existing SHARED locks are allowed to persist. A process
- ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
- ** This property is used by the algorithm for rolling back a journal file
- ** after a crash.
+ ** An EXCLUSIVE lock may only be requested after either a SHARED or
+ ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining
+ ** a write-lock on the entire 'shared byte range'. Since all other locks
+ ** require a read-lock on one of the bytes within this range, this ensures
+ ** that no other locks are held on the database.
**
- ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
- ** implemented by obtaining a write-lock on the entire 'shared byte
- ** range'. Since all other locks require a read-lock on one of the bytes
- ** within this range, this ensures that no other locks are held on the
- ** database.
+ ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then
+ ** a PENDING lock is obtained first. A PENDING lock is implemented by
+ ** obtaining a write-lock on the 'pending byte'. This ensures that no new
+ ** SHARED locks can be obtained, but existing SHARED locks are allowed to
+ ** persist. If the call to this function fails to obtain the EXCLUSIVE
+ ** lock in this case, it holds the PENDING lock intead. The client may
+ ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED
+ ** locks have cleared.
*/
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
@@ -37229,7 +38724,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
lock.l_len = 1L;
lock.l_whence = SEEK_SET;
if( eFileLock==SHARED_LOCK
- || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
+ || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock==RESERVED_LOCK)
){
lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
lock.l_start = PENDING_BYTE;
@@ -37240,6 +38735,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){
storeLastErrno(pFile, tErrno);
}
goto end_lock;
+ }else if( eFileLock==EXCLUSIVE_LOCK ){
+ pFile->eFileLock = PENDING_LOCK;
+ pInode->eFileLock = PENDING_LOCK;
}
}
@@ -37327,13 +38825,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){
}
#endif
-
if( rc==SQLITE_OK ){
pFile->eFileLock = eFileLock;
pInode->eFileLock = eFileLock;
- }else if( eFileLock==EXCLUSIVE_LOCK ){
- pFile->eFileLock = PENDING_LOCK;
- pInode->eFileLock = PENDING_LOCK;
}
end_lock:
@@ -41320,6 +42814,7 @@ static const char *unixTempFileDir(void){
static int unixGetTempname(int nBuf, char *zBuf){
const char *zDir;
int iLimit = 0;
+ int rc = SQLITE_OK;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
@@ -41328,18 +42823,26 @@ static int unixGetTempname(int nBuf, char *zBuf){
zBuf[0] = 0;
SimulateIOError( return SQLITE_IOERR );
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
zDir = unixTempFileDir();
- if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
- do{
- u64 r;
- sqlite3_randomness(sizeof(r), &r);
- assert( nBuf>2 );
- zBuf[nBuf-2] = 0;
- sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
- zDir, r, 0);
- if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR;
- }while( osAccess(zBuf,0)==0 );
- return SQLITE_OK;
+ if( zDir==0 ){
+ rc = SQLITE_IOERR_GETTEMPPATH;
+ }else{
+ do{
+ u64 r;
+ sqlite3_randomness(sizeof(r), &r);
+ assert( nBuf>2 );
+ zBuf[nBuf-2] = 0;
+ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
+ zDir, r, 0);
+ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+ }while( osAccess(zBuf,0)==0 );
+ }
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ return rc;
}
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
@@ -41915,12 +43418,10 @@ static void appendOnePathElement(
if( zName[0]=='.' ){
if( nName==1 ) return;
if( zName[1]=='.' && nName==2 ){
- if( pPath->nUsed<=1 ){
- pPath->rc = SQLITE_ERROR;
- return;
+ if( pPath->nUsed>1 ){
+ assert( pPath->zOut[0]=='/' );
+ while( pPath->zOut[--pPath->nUsed]!='/' ){}
}
- assert( pPath->zOut[0]=='/' );
- while( pPath->zOut[--pPath->nUsed]!='/' ){}
return;
}
}
@@ -42132,7 +43633,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
** than the argument.
*/
static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
-#if OS_VXWORKS
+#if OS_VXWORKS || _POSIX_C_SOURCE >= 199309L
struct timespec sp;
sp.tv_sec = microseconds / 1000000;
@@ -43514,8 +45015,16 @@ SQLITE_API int sqlite3_os_init(void){
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
+#ifdef SQLITE_DEFAULT_UNIX_VFS
+ sqlite3_vfs_register(&aVfs[i],
+ 0==strcmp(aVfs[i].zName,SQLITE_DEFAULT_UNIX_VFS));
+#else
sqlite3_vfs_register(&aVfs[i], i==0);
+#endif
}
+#ifdef SQLITE_OS_KV_OPTIONAL
+ sqlite3KvvfsInit();
+#endif
unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
#ifndef SQLITE_OMIT_WAL
@@ -45478,10 +46987,12 @@ SQLITE_API int sqlite3_win32_set_directory8(
const char *zValue /* New value for directory being set or reset */
){
char **ppDirectory = 0;
+ int rc;
#ifndef SQLITE_OMIT_AUTOINIT
- int rc = sqlite3_initialize();
+ rc = sqlite3_initialize();
if( rc ) return rc;
#endif
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
ppDirectory = &sqlite3_data_directory;
}else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
@@ -45496,14 +47007,19 @@ SQLITE_API int sqlite3_win32_set_directory8(
if( zValue && zValue[0] ){
zCopy = sqlite3_mprintf("%s", zValue);
if ( zCopy==0 ){
- return SQLITE_NOMEM_BKPT;
+ rc = SQLITE_NOMEM_BKPT;
+ goto set_directory8_done;
}
}
sqlite3_free(*ppDirectory);
*ppDirectory = zCopy;
- return SQLITE_OK;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_ERROR;
}
- return SQLITE_ERROR;
+set_directory8_done:
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ return rc;
}
/*
@@ -48278,6 +49794,19 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){
}
/*
+** If sqlite3_temp_directory is defined, take the mutex and return true.
+**
+** If sqlite3_temp_directory is NULL (undefined), omit the mutex and
+** return false.
+*/
+static int winTempDirDefined(void){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ if( sqlite3_temp_directory!=0 ) return 1;
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ return 0;
+}
+
+/*
** Create a temporary file name and store the resulting pointer into pzBuf.
** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
@@ -48313,20 +49842,23 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
*/
nDir = nMax - (nPre + 15);
assert( nDir>0 );
- if( sqlite3_temp_directory ){
+ if( winTempDirDefined() ){
int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
if( nDirLen>0 ){
if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
nDirLen++;
}
if( nDirLen>nDir ){
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
}
sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
}
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
}
+
#if defined(__CYGWIN__)
else{
static const char *azDirs[] = {
@@ -49115,7 +50647,7 @@ static BOOL winIsVerbatimPathname(
** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
** bytes in size.
*/
-static int winFullPathname(
+static int winFullPathnameNoMutex(
sqlite3_vfs *pVfs, /* Pointer to vfs object */
const char *zRelative, /* Possibly relative input path */
int nFull, /* Size of output buffer in bytes */
@@ -49294,6 +50826,20 @@ static int winFullPathname(
}
#endif
}
+static int winFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zRelative, /* Possibly relative input path */
+ int nFull, /* Size of output buffer in bytes */
+ char *zFull /* Output buffer */
+){
+ int rc;
+ MUTEX_LOGIC( sqlite3_mutex *pMutex; )
+ MUTEX_LOGIC( pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); )
+ sqlite3_mutex_enter(pMutex);
+ rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull);
+ sqlite3_mutex_leave(pMutex);
+ return rc;
+}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
@@ -49830,6 +51376,7 @@ static int memdbTruncate(sqlite3_file*, sqlite3_int64 size);
static int memdbSync(sqlite3_file*, int flags);
static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int memdbLock(sqlite3_file*, int);
+static int memdbUnlock(sqlite3_file*, int);
/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */
static int memdbFileControl(sqlite3_file*, int op, void *pArg);
/* static int memdbSectorSize(sqlite3_file*); // not used */
@@ -49888,7 +51435,7 @@ static const sqlite3_io_methods memdb_io_methods = {
memdbSync, /* xSync */
memdbFileSize, /* xFileSize */
memdbLock, /* xLock */
- memdbLock, /* xUnlock - same as xLock in this case */
+ memdbUnlock, /* xUnlock */
0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */
memdbFileControl, /* xFileControl */
0, /* memdbSectorSize,*/ /* xSectorSize */
@@ -50089,39 +51636,81 @@ static int memdbLock(sqlite3_file *pFile, int eLock){
MemFile *pThis = (MemFile*)pFile;
MemStore *p = pThis->pStore;
int rc = SQLITE_OK;
- if( eLock==pThis->eLock ) return SQLITE_OK;
+ if( eLock<=pThis->eLock ) return SQLITE_OK;
memdbEnter(p);
- if( eLock>SQLITE_LOCK_SHARED ){
- if( p->mFlags & SQLITE_DESERIALIZE_READONLY ){
- rc = SQLITE_READONLY;
- }else if( pThis->eLock<=SQLITE_LOCK_SHARED ){
- if( p->nWrLock ){
- rc = SQLITE_BUSY;
- }else{
- p->nWrLock = 1;
+
+ assert( p->nWrLock==0 || p->nWrLock==1 );
+ assert( pThis->eLock<=SQLITE_LOCK_SHARED || p->nWrLock==1 );
+ assert( pThis->eLock==SQLITE_LOCK_NONE || p->nRdLock>=1 );
+
+ if( eLock>SQLITE_LOCK_SHARED && (p->mFlags & SQLITE_DESERIALIZE_READONLY) ){
+ rc = SQLITE_READONLY;
+ }else{
+ switch( eLock ){
+ case SQLITE_LOCK_SHARED: {
+ assert( pThis->eLock==SQLITE_LOCK_NONE );
+ if( p->nWrLock>0 ){
+ rc = SQLITE_BUSY;
+ }else{
+ p->nRdLock++;
+ }
+ break;
+ };
+
+ case SQLITE_LOCK_RESERVED:
+ case SQLITE_LOCK_PENDING: {
+ assert( pThis->eLock>=SQLITE_LOCK_SHARED );
+ if( ALWAYS(pThis->eLock==SQLITE_LOCK_SHARED) ){
+ if( p->nWrLock>0 ){
+ rc = SQLITE_BUSY;
+ }else{
+ p->nWrLock = 1;
+ }
+ }
+ break;
+ }
+
+ default: {
+ assert( eLock==SQLITE_LOCK_EXCLUSIVE );
+ assert( pThis->eLock>=SQLITE_LOCK_SHARED );
+ if( p->nRdLock>1 ){
+ rc = SQLITE_BUSY;
+ }else if( pThis->eLock==SQLITE_LOCK_SHARED ){
+ p->nWrLock = 1;
+ }
+ break;
}
}
- }else if( eLock==SQLITE_LOCK_SHARED ){
- if( pThis->eLock > SQLITE_LOCK_SHARED ){
- assert( p->nWrLock==1 );
- p->nWrLock = 0;
- }else if( p->nWrLock ){
- rc = SQLITE_BUSY;
- }else{
- p->nRdLock++;
+ }
+ if( rc==SQLITE_OK ) pThis->eLock = eLock;
+ memdbLeave(p);
+ return rc;
+}
+
+/*
+** Unlock an memdb-file.
+*/
+static int memdbUnlock(sqlite3_file *pFile, int eLock){
+ MemFile *pThis = (MemFile*)pFile;
+ MemStore *p = pThis->pStore;
+ if( eLock>=pThis->eLock ) return SQLITE_OK;
+ memdbEnter(p);
+
+ assert( eLock==SQLITE_LOCK_SHARED || eLock==SQLITE_LOCK_NONE );
+ if( eLock==SQLITE_LOCK_SHARED ){
+ if( ALWAYS(pThis->eLock>SQLITE_LOCK_SHARED) ){
+ p->nWrLock--;
}
}else{
- assert( eLock==SQLITE_LOCK_NONE );
if( pThis->eLock>SQLITE_LOCK_SHARED ){
- assert( p->nWrLock==1 );
- p->nWrLock = 0;
+ p->nWrLock--;
}
- assert( p->nRdLock>0 );
p->nRdLock--;
}
- if( rc==SQLITE_OK ) pThis->eLock = eLock;
+
+ pThis->eLock = eLock;
memdbLeave(p);
- return rc;
+ return SQLITE_OK;
}
#if 0
@@ -50231,7 +51820,7 @@ static int memdbOpen(
memset(pFile, 0, sizeof(*pFile));
szName = sqlite3Strlen30(zName);
- if( szName>1 && zName[0]=='/' ){
+ if( szName>1 && (zName[0]=='/' || zName[0]=='\\') ){
int i;
#ifndef SQLITE_MUTEX_OMIT
sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
@@ -50579,6 +52168,13 @@ end_deserialize:
}
/*
+** Return true if the VFS is the memvfs.
+*/
+SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs *pVfs){
+ return pVfs==&memdb_vfs;
+}
+
+/*
** This routine is called when the extension is loaded.
** Register the new VFS.
*/
@@ -51082,12 +52678,20 @@ struct PCache {
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
- void pcacheDump(PCache *pCache){
- int N;
- int i, j;
- sqlite3_pcache_page *pLower;
+ static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){
PgHdr *pPg;
unsigned char *a;
+ int j;
+ pPg = (PgHdr*)pLower->pExtra;
+ printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
+ a = (unsigned char *)pLower->pBuf;
+ for(j=0; j<12; j++) printf("%02x", a[j]);
+ printf(" ptr %p\n", pPg);
+ }
+ static void pcacheDump(PCache *pCache){
+ int N;
+ int i;
+ sqlite3_pcache_page *pLower;
if( sqlite3PcacheTrace<2 ) return;
if( pCache->pCache==0 ) return;
@@ -51096,22 +52700,33 @@ struct PCache {
for(i=1; i<=N; i++){
pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
if( pLower==0 ) continue;
- pPg = (PgHdr*)pLower->pExtra;
- printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
- a = (unsigned char *)pLower->pBuf;
- for(j=0; j<12; j++) printf("%02x", a[j]);
- printf("\n");
- if( pPg->pPage==0 ){
+ pcachePageTrace(i, pLower);
+ if( ((PgHdr*)pLower)->pPage==0 ){
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
}
}
}
- #else
+#else
# define pcacheTrace(X)
+# define pcachePageTrace(PGNO, X)
# define pcacheDump(X)
#endif
/*
+** Return 1 if pPg is on the dirty list for pCache. Return 0 if not.
+** This routine runs inside of assert() statements only.
+*/
+#ifdef SQLITE_DEBUG
+static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){
+ PgHdr *p;
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){
+ if( p==pPg ) return 1;
+ }
+ return 0;
+}
+#endif
+
+/*
** Check invariants on a PgHdr entry. Return true if everything is OK.
** Return false if any invariant is violated.
**
@@ -51129,8 +52744,13 @@ SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){
assert( pCache!=0 ); /* Every page has an associated PCache */
if( pPg->flags & PGHDR_CLEAN ){
assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
- assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
- assert( pCache->pDirtyTail!=pPg );
+ assert( !pageOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirty list */
+ }else{
+ assert( (pPg->flags & PGHDR_DIRTY)!=0 );/* If not CLEAN must be DIRTY */
+ assert( pPg->pDirtyNext==0 || pPg->pDirtyNext->pDirtyPrev==pPg );
+ assert( pPg->pDirtyPrev==0 || pPg->pDirtyPrev->pDirtyNext==pPg );
+ assert( pPg->pDirtyPrev!=0 || pCache->pDirty==pPg );
+ assert( pageOnDirtyList(pCache, pPg) );
}
/* WRITEABLE pages must also be DIRTY */
if( pPg->flags & PGHDR_WRITEABLE ){
@@ -51404,8 +53024,9 @@ SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
assert( createFlag==0 || pCache->eCreate==eCreate );
assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
- pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
+ pcacheTrace(("%p.FETCH %d%s (result: %p) ",pCache,pgno,
createFlag?" create":"",pRes));
+ pcachePageTrace(pgno, pRes);
return pRes;
}
@@ -51533,6 +53154,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
pcacheUnpin(p);
}else{
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
+ assert( sqlite3PcachePageSanity(p) );
}
}
}
@@ -51576,6 +53198,7 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
+ assert( sqlite3PcachePageSanity(p) );
}
assert( sqlite3PcachePageSanity(p) );
}
@@ -51638,14 +53261,24 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){
*/
SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
PCache *pCache = p->pCache;
+ sqlite3_pcache_page *pOther;
assert( p->nRef>0 );
assert( newPgno>0 );
assert( sqlite3PcachePageSanity(p) );
pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
+ pOther = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0);
+ if( pOther ){
+ PgHdr *pXPage = (PgHdr*)pOther->pExtra;
+ assert( pXPage->nRef==0 );
+ pXPage->nRef++;
+ pCache->nRefSum++;
+ sqlite3PcacheDrop(pXPage);
+ }
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
p->pgno = newPgno;
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
+ assert( sqlite3PcachePageSanity(p) );
}
}
@@ -51943,12 +53576,13 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd
** size can vary according to architecture, compile-time options, and
** SQLite library version number.
**
-** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained
-** using a separate memory allocation from the database page content. This
-** seeks to overcome the "clownshoe" problem (also called "internal
-** fragmentation" in academic literature) of allocating a few bytes more
-** than a power of two with the memory allocator rounding up to the next
-** power of two, and leaving the rounded-up space unused.
+** Historical note: It used to be that if the SQLITE_PCACHE_SEPARATE_HEADER
+** was defined, then the page content would be held in a separate memory
+** allocation from the PgHdr1. This was intended to avoid clownshoe memory
+** allocations. However, the btree layer needs a small (16-byte) overrun
+** area after the page content buffer. The header serves as that overrun
+** area. Therefore SQLITE_PCACHE_SEPARATE_HEADER was discontinued to avoid
+** any possibility of a memory error.
**
** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates
** with this module. Information is passed back and forth as PgHdr1 pointers.
@@ -51993,30 +53627,40 @@ typedef struct PGroup PGroup;
/*
** Each cache entry is represented by an instance of the following
-** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
-** PgHdr1.pCache->szPage bytes is allocated directly before this structure
-** in memory.
+** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
+** directly before this structure and is used to cache the page content.
+**
+** When reading a corrupt database file, it is possible that SQLite might
+** read a few bytes (no more than 16 bytes) past the end of the page buffer.
+** It will only read past the end of the page buffer, never write. This
+** object is positioned immediately after the page buffer to serve as an
+** overrun area, so that overreads are harmless.
**
-** Note: Variables isBulkLocal and isAnchor were once type "u8". That works,
+** Variables isBulkLocal and isAnchor were once type "u8". That works,
** but causes a 2-byte gap in the structure for most architectures (since
** pointers must be either 4 or 8-byte aligned). As this structure is located
** in memory directly after the associated page data, if the database is
** corrupt, code at the b-tree layer may overread the page buffer and
** read part of this structure before the corruption is detected. This
** can cause a valgrind error if the unitialized gap is accessed. Using u16
-** ensures there is no such gap, and therefore no bytes of unitialized memory
-** in the structure.
+** ensures there is no such gap, and therefore no bytes of uninitialized
+** memory in the structure.
+**
+** The pLruNext and pLruPrev pointers form a double-linked circular list
+** of all pages that are unpinned. The PGroup.lru element (which should be
+** the only element on the list with PgHdr1.isAnchor set to 1) forms the
+** beginning and the end of the list.
*/
struct PgHdr1 {
- sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
- unsigned int iKey; /* Key value (page number) */
- u16 isBulkLocal; /* This page from bulk local storage */
- u16 isAnchor; /* This is the PGroup.lru element */
- PgHdr1 *pNext; /* Next in hash table chain */
- PCache1 *pCache; /* Cache that currently owns this page */
- PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
- PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
- /* NB: pLruPrev is only valid if pLruNext!=0 */
+ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
+ unsigned int iKey; /* Key value (page number) */
+ u16 isBulkLocal; /* This page from bulk local storage */
+ u16 isAnchor; /* This is the PGroup.lru element */
+ PgHdr1 *pNext; /* Next in hash table chain */
+ PCache1 *pCache; /* Cache that currently owns this page */
+ PgHdr1 *pLruNext; /* Next in circular LRU list of unpinned pages */
+ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
+ /* NB: pLruPrev is only valid if pLruNext!=0 */
};
/*
@@ -52342,25 +53986,13 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
pcache1LeaveMutex(pCache->pGroup);
#endif
if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- pPg = pcache1Alloc(pCache->szPage);
- p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
- if( !pPg || !p ){
- pcache1Free(pPg);
- sqlite3_free(p);
- pPg = 0;
- }
-#else
pPg = pcache1Alloc(pCache->szAlloc);
-#endif
if( benignMalloc ){ sqlite3EndBenignMalloc(); }
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
pcache1EnterMutex(pCache->pGroup);
#endif
if( pPg==0 ) return 0;
-#ifndef SQLITE_PCACHE_SEPARATE_HEADER
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
-#endif
p->page.pBuf = pPg;
p->page.pExtra = &p[1];
p->isBulkLocal = 0;
@@ -52384,9 +54016,6 @@ static void pcache1FreePage(PgHdr1 *p){
pCache->pFree = p;
}else{
pcache1Free(p->page.pBuf);
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- sqlite3_free(p);
-#endif
}
(*pCache->pnPurgeable)--;
}
@@ -53027,23 +54656,26 @@ static void pcache1Rekey(
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = (PgHdr1 *)pPg;
PgHdr1 **pp;
- unsigned int h;
+ unsigned int hOld, hNew;
assert( pPage->iKey==iOld );
assert( pPage->pCache==pCache );
+ assert( iOld!=iNew ); /* The page number really is changing */
pcache1EnterMutex(pCache->pGroup);
- h = iOld%pCache->nHash;
- pp = &pCache->apHash[h];
+ assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */
+ hOld = iOld%pCache->nHash;
+ pp = &pCache->apHash[hOld];
while( (*pp)!=pPage ){
pp = &(*pp)->pNext;
}
*pp = pPage->pNext;
- h = iNew%pCache->nHash;
+ assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */
+ hNew = iNew%pCache->nHash;
pPage->iKey = iNew;
- pPage->pNext = pCache->apHash[h];
- pCache->apHash[h] = pPage;
+ pPage->pNext = pCache->apHash[hNew];
+ pCache->apHash[hNew] = pPage;
if( iNew>pCache->iMaxKey ){
pCache->iMaxKey = iNew;
}
@@ -53150,9 +54782,6 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
&& p->isAnchor==0
){
nFree += pcache1MemSize(p->page.pBuf);
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- nFree += sqlite3MemSize(p);
-#endif
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p);
pcache1RemoveFromHash(p, 1);
@@ -56790,7 +58419,7 @@ end_playback:
** see if it is possible to delete the super-journal.
*/
assert( zSuper==&pPager->pTmpSpace[4] );
- memset(&zSuper[-4], 0, 4);
+ memset(pPager->pTmpSpace, 0, 4);
rc = pager_delsuper(pPager, zSuper);
testcase( rc!=SQLITE_OK );
}
@@ -57411,7 +59040,6 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
** Numeric values associated with these states are OFF==1, NORMAL=2,
** and FULL=3.
*/
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
unsigned pgFlags /* Various flags */
@@ -57446,7 +59074,6 @@ SQLITE_PRIVATE void sqlite3PagerSetFlags(
pPager->doNotSpill |= SPILLFLAG_OFF;
}
}
-#endif
/*
** The following global variable is incremented whenever the library
@@ -58548,7 +60175,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
const char *zUri = 0; /* URI args to copy */
int nUriByte = 1; /* Number of bytes of URI args at *zUri */
- int nUri = 0; /* Number of URI parameters */
/* Figure out how much space is required for each journal file-handle
** (there are two of them, the main journal and the sub-journal). */
@@ -58596,7 +60222,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
while( *z ){
z += strlen(z)+1;
z += strlen(z)+1;
- nUri++;
}
nUriByte = (int)(&z[1] - zUri);
assert( nUriByte>=1 );
@@ -58852,18 +60477,7 @@ act_like_temp_file:
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
- pPager->noSync = pPager->tempFile;
- if( pPager->noSync ){
- assert( pPager->fullSync==0 );
- assert( pPager->extraSync==0 );
- assert( pPager->syncFlags==0 );
- assert( pPager->walSyncFlags==0 );
- }else{
- pPager->fullSync = 1;
- pPager->extraSync = 0;
- pPager->syncFlags = SQLITE_SYNC_NORMAL;
- pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2);
- }
+ sqlite3PagerSetFlags(pPager, (SQLITE_DEFAULT_SYNCHRONOUS+1)|PAGER_CACHESPILL);
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@@ -59641,6 +61255,7 @@ static int pager_open_journal(Pager *pPager){
if( pPager->tempFile ){
flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
+ flags |= SQLITE_OPEN_EXCLUSIVE;
nSpill = sqlite3Config.nStmtSpill;
}else{
flags |= SQLITE_OPEN_MAIN_JOURNAL;
@@ -59676,6 +61291,7 @@ static int pager_open_journal(Pager *pPager){
if( rc!=SQLITE_OK ){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
+ pPager->journalOff = 0;
}else{
assert( pPager->eState==PAGER_WRITER_LOCKED );
pPager->eState = PAGER_WRITER_CACHEMOD;
@@ -60122,7 +61738,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
# define DIRECT_MODE isDirectMode
#endif
- if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
+ if( !pPager->changeCountDone && pPager->dbSize>0 ){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -60862,7 +62478,11 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
*/
SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){
static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename;
+ if( nullIfMemDb && (pPager->memDb || sqlite3IsMemdb(pPager->pVfs)) ){
+ return &zFake[4];
+ }else{
+ return pPager->zFilename;
+ }
}
/*
@@ -66445,15 +68065,15 @@ struct BtCursor {
** So, this macro is defined instead.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
-#define ISAUTOVACUUM (pBt->autoVacuum)
+#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#else
-#define ISAUTOVACUUM 0
+#define ISAUTOVACUUM(pBt) 0
#endif
/*
-** This structure is passed around through all the sanity checking routines
-** in order to keep track of some global state information.
+** This structure is passed around through all the PRAGMA integrity_check
+** checking routines in order to keep track of some global state information.
**
** The aRef[] array is allocated so that there is 1 bit for each page in
** the database. As the integrity-check proceeds, for each page used in
@@ -66469,7 +68089,8 @@ struct IntegrityCk {
Pgno nPage; /* Number of pages in the database */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
- int bOomFault; /* A memory allocation error has occurred */
+ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */
+ u32 nStep; /* Number of steps into the integrity_check process */
const char *zPfx; /* Error message prefix */
Pgno v1; /* Value for first %u substitution in zPfx */
int v2; /* Value for second %d substitution in zPfx */
@@ -66739,6 +68360,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){
Btree *p;
assert( db!=0 );
+ if( db->pVfs==0 && db->nDb==0 ) return 1;
if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema);
assert( iDb>=0 && iDb<db->nDb );
if( !sqlite3_mutex_held(db->mutex) ) return 0;
@@ -68311,8 +69933,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- temp = 0;
- src = data = pPage->aData;
+ data = pPage->aData;
hdr = pPage->hdrOffset;
cellOffset = pPage->cellOffset;
nCell = pPage->nCell;
@@ -68346,7 +69967,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
sz += sz2;
- }else if( NEVER(iFree+sz>usableSize) ){
+ }else if( iFree+sz>usableSize ){
return SQLITE_CORRUPT_PAGE(pPage);
}
@@ -68366,39 +69987,38 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
cbrk = usableSize;
iCellLast = usableSize - 4;
iCellStart = get2byte(&data[hdr+5]);
- for(i=0; i<nCell; i++){
- u8 *pAddr; /* The i-th cell pointer */
- pAddr = &data[cellOffset + i*2];
- pc = get2byte(pAddr);
- testcase( pc==iCellFirst );
- testcase( pc==iCellLast );
- /* These conditions have already been verified in btreeInitPage()
- ** if PRAGMA cell_size_check=ON.
- */
- if( pc<iCellStart || pc>iCellLast ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- assert( pc>=iCellStart && pc<=iCellLast );
- size = pPage->xCellSize(pPage, &src[pc]);
- cbrk -= size;
- if( cbrk<iCellStart || pc+size>usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- assert( cbrk+size<=usableSize && cbrk>=iCellStart );
- testcase( cbrk+size==usableSize );
- testcase( pc+size==usableSize );
- put2byte(pAddr, cbrk);
- if( temp==0 ){
- if( cbrk==pc ) continue;
- temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
- memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart);
- src = temp;
+ if( nCell>0 ){
+ temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
+ memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart);
+ src = temp;
+ for(i=0; i<nCell; i++){
+ u8 *pAddr; /* The i-th cell pointer */
+ pAddr = &data[cellOffset + i*2];
+ pc = get2byte(pAddr);
+ testcase( pc==iCellFirst );
+ testcase( pc==iCellLast );
+ /* These conditions have already been verified in btreeInitPage()
+ ** if PRAGMA cell_size_check=ON.
+ */
+ if( pc<iCellStart || pc>iCellLast ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ assert( pc>=iCellStart && pc<=iCellLast );
+ size = pPage->xCellSize(pPage, &src[pc]);
+ cbrk -= size;
+ if( cbrk<iCellStart || pc+size>usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ assert( cbrk+size<=usableSize && cbrk>=iCellStart );
+ testcase( cbrk+size==usableSize );
+ testcase( pc+size==usableSize );
+ put2byte(pAddr, cbrk);
+ memcpy(&data[cbrk], &src[pc], size);
}
- memcpy(&data[cbrk], &src[pc], size);
}
data[hdr+7] = 0;
- defragment_out:
+defragment_out:
assert( pPage->nFree>=0 );
if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){
return SQLITE_CORRUPT_PAGE(pPage);
@@ -68455,7 +70075,6 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
** fragmented bytes within the page. */
memcpy(&aData[iAddr], &aData[pc], 2);
aData[hdr+7] += (u8)x;
- testcase( pc+x>maxPC );
return &aData[pc];
}else if( x+pc > maxPC ){
/* This slot extends off the end of the usable part of the page */
@@ -68471,9 +70090,9 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
iAddr = pc;
pTmp = &aData[pc];
pc = get2byte(pTmp);
- if( pc<=iAddr+size ){
+ if( pc<=iAddr ){
if( pc ){
- /* The next slot in the chain is not past the end of the current slot */
+ /* The next slot in the chain comes before the current slot */
*pRc = SQLITE_CORRUPT_PAGE(pPg);
}
return 0;
@@ -68625,7 +70244,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
}else{
while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
- if( iFreeBlk<iPtr+4 ){
+ if( iFreeBlk<=iPtr ){
if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */
return SQLITE_CORRUPT_PAGE(pPage);
}
@@ -68701,62 +70320,67 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
** Only the following combinations are supported. Anything different
** indicates a corrupt database files:
**
-** PTF_ZERODATA
-** PTF_ZERODATA | PTF_LEAF
-** PTF_LEAFDATA | PTF_INTKEY
-** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF
+** PTF_ZERODATA (0x02, 2)
+** PTF_LEAFDATA | PTF_INTKEY (0x05, 5)
+** PTF_ZERODATA | PTF_LEAF (0x0a, 10)
+** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13)
*/
static int decodeFlags(MemPage *pPage, int flagByte){
BtShared *pBt; /* A copy of pPage->pBt */
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 );
- flagByte &= ~PTF_LEAF;
- pPage->childPtrSize = 4-4*pPage->leaf;
pBt = pPage->pBt;
- if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
- /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
- ** interior table b-tree page. */
- assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
- /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
- ** leaf table b-tree page. */
- assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
- pPage->intKey = 1;
- if( pPage->leaf ){
+ pPage->max1bytePayload = pBt->max1bytePayload;
+ if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){
+ pPage->childPtrSize = 0;
+ pPage->leaf = 1;
+ if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){
pPage->intKeyLeaf = 1;
pPage->xCellSize = cellSizePtrTableLeaf;
pPage->xParseCell = btreeParseCellPtr;
+ pPage->intKey = 1;
+ pPage->maxLocal = pBt->maxLeaf;
+ pPage->minLocal = pBt->minLeaf;
+ }else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtr;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ pPage->maxLocal = pBt->maxLocal;
+ pPage->minLocal = pBt->minLocal;
}else{
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtr;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ }else{
+ pPage->childPtrSize = 4;
+ pPage->leaf = 0;
+ if( flagByte==(PTF_ZERODATA) ){
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtr;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ pPage->maxLocal = pBt->maxLocal;
+ pPage->minLocal = pBt->minLocal;
+ }else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtrNoPayload;
pPage->xParseCell = btreeParseCellPtrNoPayload;
+ pPage->intKey = 1;
+ pPage->maxLocal = pBt->maxLeaf;
+ pPage->minLocal = pBt->minLeaf;
+ }else{
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtr;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
- pPage->maxLocal = pBt->maxLeaf;
- pPage->minLocal = pBt->minLeaf;
- }else if( flagByte==PTF_ZERODATA ){
- /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
- ** interior index b-tree page. */
- assert( (PTF_ZERODATA)==2 );
- /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
- ** leaf index b-tree page. */
- assert( (PTF_ZERODATA|PTF_LEAF)==10 );
- pPage->intKey = 0;
- pPage->intKeyLeaf = 0;
- pPage->xCellSize = cellSizePtr;
- pPage->xParseCell = btreeParseCellPtrIndex;
- pPage->maxLocal = pBt->maxLocal;
- pPage->minLocal = pBt->minLocal;
- }else{
- /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
- ** an error. */
- pPage->intKey = 0;
- pPage->intKeyLeaf = 0;
- pPage->xCellSize = cellSizePtr;
- pPage->xParseCell = btreeParseCellPtrIndex;
- return SQLITE_CORRUPT_PAGE(pPage);
}
- pPage->max1bytePayload = pBt->max1bytePayload;
return SQLITE_OK;
}
@@ -69107,9 +70731,7 @@ getAndInitPage_error1:
pCur->pPage = pCur->apPage[pCur->iPage];
}
testcase( pgno==0 );
- assert( pgno!=0 || rc==SQLITE_CORRUPT
- || rc==SQLITE_IOERR_NOMEM
- || rc==SQLITE_NOMEM );
+ assert( pgno!=0 || rc!=SQLITE_OK );
return rc;
}
@@ -70545,6 +72167,9 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
}
}
}else{
+ if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
if( get4byte(pCell)==iFrom ){
put4byte(pCell, iTo);
break;
@@ -72051,8 +73676,6 @@ SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){
** vice-versa).
*/
static int moveToChild(BtCursor *pCur, u32 newPgno){
- BtShared *pBt = pCur->pBt;
-
assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
@@ -72066,7 +73689,8 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
pCur->apPage[pCur->iPage] = pCur->pPage;
pCur->ix = 0;
pCur->iPage++;
- return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags);
+ return getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur,
+ pCur->curPagerFlags);
}
#ifdef SQLITE_DEBUG
@@ -72172,7 +73796,7 @@ static int moveToRoot(BtCursor *pCur){
}
sqlite3BtreeClearCursor(pCur);
}
- rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage,
+ rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage,
0, pCur->curPagerFlags);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
@@ -72296,9 +73920,25 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
+static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){
+ int rc = moveToRoot(pCur);
+ if( rc==SQLITE_OK ){
+ assert( pCur->eState==CURSOR_VALID );
+ *pRes = 0;
+ rc = moveToRightmost(pCur);
+ if( rc==SQLITE_OK ){
+ pCur->curFlags |= BTCF_AtLast;
+ }else{
+ pCur->curFlags &= ~BTCF_AtLast;
+ }
+ }else if( rc==SQLITE_EMPTY ){
+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
+ *pRes = 1;
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
- int rc;
-
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
@@ -72319,23 +73959,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
*pRes = 0;
return SQLITE_OK;
}
-
- rc = moveToRoot(pCur);
- if( rc==SQLITE_OK ){
- assert( pCur->eState==CURSOR_VALID );
- *pRes = 0;
- rc = moveToRightmost(pCur);
- if( rc==SQLITE_OK ){
- pCur->curFlags |= BTCF_AtLast;
- }else{
- pCur->curFlags &= ~BTCF_AtLast;
- }
- }else if( rc==SQLITE_EMPTY ){
- assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
- *pRes = 1;
- rc = SQLITE_OK;
- }
- return rc;
+ return btreeLast(pCur, pRes);
}
/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY)
@@ -72880,14 +74504,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){
pPage = pCur->pPage;
idx = ++pCur->ix;
- if( !pPage->isInit || sqlite3FaultSim(412) ){
- /* The only known way for this to happen is for there to be a
- ** recursive SQL function that does a DELETE operation as part of a
- ** SELECT which deletes content out from under an active cursor
- ** in a corrupt database file where the table being DELETE-ed from
- ** has pages in common with the table being queried. See TH3
- ** module cov1/btree78.test testcase 220 (2018-06-08) for an
- ** example. */
+ if( NEVER(!pPage->isInit) || sqlite3FaultSim(412) ){
return SQLITE_CORRUPT_BKPT;
}
@@ -73063,8 +74680,8 @@ static int allocateBtreePage(
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
mxPage = btreePagecount(pBt);
- /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
- ** stores stores the total number of pages on the freelist. */
+ /* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36
+ ** stores the total number of pages on the freelist. */
n = get4byte(&pPage1->aData[36]);
testcase( n==mxPage-1 );
if( n>=mxPage ){
@@ -73409,7 +75026,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc);
if( rc ) goto freepage_out;
}
@@ -73813,12 +75430,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
assert( pPage->pBt->usableSize > (u32)(ptr-data) );
pc = get2byte(ptr);
hdr = pPage->hdrOffset;
-#if 0 /* Not required. Omit for efficiency */
- if( pc<hdr+pPage->nCell*2 ){
- *pRC = SQLITE_CORRUPT_BKPT;
- return;
- }
-#endif
testcase( pc==(u32)get2byte(&data[hdr+5]) );
testcase( pc+sz==pPage->pBt->usableSize );
if( pc+sz > pPage->pBt->usableSize ){
@@ -73855,24 +75466,20 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
-**
-** *pRC must be SQLITE_OK when this routine is called.
*/
-static void insertCell(
+static int insertCell(
MemPage *pPage, /* Page into which we are copying */
int i, /* New cell becomes the i-th cell of the page */
u8 *pCell, /* Content of the new cell */
int sz, /* Bytes of content in pCell */
u8 *pTemp, /* Temp storage space for pCell, if needed */
- Pgno iChild, /* If non-zero, replace first 4 bytes with this value */
- int *pRC /* Read and write return code from here */
+ Pgno iChild /* If non-zero, replace first 4 bytes with this value */
){
int idx = 0; /* Where to write new cell content in data[] */
int j; /* Loop counter */
u8 *data; /* The content of the whole page */
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
- assert( *pRC==SQLITE_OK );
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
@@ -73907,14 +75514,13 @@ static void insertCell(
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
- *pRC = rc;
- return;
+ return rc;
}
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
- if( rc ){ *pRC = rc; return; }
+ if( rc ){ return rc; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
@@ -73941,13 +75547,16 @@ static void insertCell(
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
+ int rc2 = SQLITE_OK;
/* The cell may contain a pointer to an overflow page. If so, write
** the entry for the overflow page into the pointer map.
*/
- ptrmapPutOvflPtr(pPage, pPage, pCell, pRC);
+ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2);
+ if( rc2 ) return rc2;
}
#endif
}
+ return SQLITE_OK;
}
/*
@@ -74048,14 +75657,16 @@ struct CellArray {
** computed.
*/
static void populateCellCache(CellArray *p, int idx, int N){
+ MemPage *pRef = p->pRef;
+ u16 *szCell = p->szCell;
assert( idx>=0 && idx+N<=p->nCell );
while( N>0 ){
assert( p->apCell[idx]!=0 );
- if( p->szCell[idx]==0 ){
- p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
+ if( szCell[idx]==0 ){
+ szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]);
}else{
assert( CORRUPT_DB ||
- p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
+ szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) );
}
idx++;
N--;
@@ -74257,8 +75868,8 @@ static int pageFreeArray(
int nRet = 0;
int i;
int iEnd = iFirst + nCell;
- u8 *pFree = 0;
- int szFree = 0;
+ u8 *pFree = 0; /* \__ Parameters for pending call to */
+ int szFree = 0; /* / freeSpace() */
for(i=iFirst; i<iEnd; i++){
u8 *pCell = pCArray->apCell[i];
@@ -74279,6 +75890,9 @@ static int pageFreeArray(
return 0;
}
}else{
+ /* The current cell is adjacent to and before the pFree cell.
+ ** Combine the two regions into one to reduce the number of calls
+ ** to freeSpace(). */
pFree = pCell;
szFree += sz;
}
@@ -74486,7 +76100,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
** be marked as dirty. Returning an error code will cause a
** rollback, undoing any changes made to the parent page.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc);
if( szCell>pNew->minLocal ){
ptrmapPutOvflPtr(pNew, pNew, pCell, &rc);
@@ -74514,8 +76128,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
/* Insert the new divider cell into pParent. */
if( rc==SQLITE_OK ){
- insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
- 0, pPage->pgno, &rc);
+ rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
+ 0, pPage->pgno);
}
/* Set the right-child pointer of pParent to point to the new page. */
@@ -74624,7 +76238,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
/* If this is an auto-vacuum database, update the pointer-map entries
** for any b-tree or overflow pages that pTo now contains the pointers to.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
*pRC = setChildPtrmaps(pTo);
}
}
@@ -74702,8 +76316,6 @@ static int balance_nonroot(
Pgno pgno; /* Temp var to store a page number in */
u8 abDone[NB+2]; /* True after i'th new page is populated */
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
- Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
- u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
CellArray b; /* Parsed information on cells being balanced */
memset(abDone, 0, sizeof(abDone));
@@ -75050,15 +76662,17 @@ static int balance_nonroot(
d = r + 1 - leafData;
(void)cachedCellSize(&b, d);
do{
+ int szR, szD;
assert( d<nMaxCells );
assert( r<nMaxCells );
- (void)cachedCellSize(&b, r);
+ szR = cachedCellSize(&b, r);
+ szD = b.szCell[d];
if( szRight!=0
- && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
+ && (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){
break;
}
- szRight += b.szCell[d] + 2;
- szLeft -= b.szCell[r] + 2;
+ szRight += szD + 2;
+ szLeft -= szR + 2;
cntNew[i-1] = r;
r--;
d--;
@@ -75112,7 +76726,7 @@ static int balance_nonroot(
cntOld[i] = b.nCell;
/* Set the pointer-map entry for the new sibling page. */
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc);
if( rc!=SQLITE_OK ){
goto balance_cleanup;
@@ -75127,42 +76741,39 @@ static int balance_nonroot(
** of the table is closer to a linear scan through the file. That in turn
** helps the operating system to deliver pages from the disk more rapidly.
**
- ** An O(n^2) insertion sort algorithm is used, but since n is never more
- ** than (NB+2) (a small constant), that should not be a problem.
+ ** An O(N*N) sort algorithm is used, but since N is never more than NB+2
+ ** (5), that is not a performance concern.
**
** When NB==3, this one optimization makes the database about 25% faster
** for large insertions and deletions.
*/
for(i=0; i<nNew; i++){
- aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
- aPgFlags[i] = apNew[i]->pDbPage->flags;
- for(j=0; j<i; j++){
- if( NEVER(aPgno[j]==aPgno[i]) ){
- /* This branch is taken if the set of sibling pages somehow contains
- ** duplicate entries. This can happen if the database is corrupt.
- ** It would be simpler to detect this as part of the loop below, but
- ** we do the detection here in order to avoid populating the pager
- ** cache with two separate objects associated with the same
- ** page number. */
- assert( CORRUPT_DB );
- rc = SQLITE_CORRUPT_BKPT;
- goto balance_cleanup;
- }
- }
+ aPgno[i] = apNew[i]->pgno;
+ assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE );
+ assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY );
}
- for(i=0; i<nNew; i++){
- int iBest = 0; /* aPgno[] index of page number to use */
- for(j=1; j<nNew; j++){
- if( aPgOrder[j]<aPgOrder[iBest] ) iBest = j;
+ for(i=0; i<nNew-1; i++){
+ int iB = i;
+ for(j=i+1; j<nNew; j++){
+ if( apNew[j]->pgno < apNew[iB]->pgno ) iB = j;
}
- pgno = aPgOrder[iBest];
- aPgOrder[iBest] = 0xffffffff;
- if( iBest!=i ){
- if( iBest>i ){
- sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0);
- }
- sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]);
- apNew[i]->pgno = pgno;
+
+ /* If apNew[i] has a page number that is bigger than any of the
+ ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent
+ ** entry that has the smallest page number (which we know to be
+ ** entry apNew[iB]).
+ */
+ if( iB!=i ){
+ Pgno pgnoA = apNew[i]->pgno;
+ Pgno pgnoB = apNew[iB]->pgno;
+ Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1;
+ u16 fgA = apNew[i]->pDbPage->flags;
+ u16 fgB = apNew[iB]->pDbPage->flags;
+ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB);
+ sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA);
+ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB);
+ apNew[i]->pgno = pgnoB;
+ apNew[iB]->pgno = pgnoA;
}
}
@@ -75208,7 +76819,7 @@ static int balance_nonroot(
** updated. This happens below, after the sibling pages have been
** populated, not here.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
MemPage *pOld;
MemPage *pNew = pOld = apNew[0];
int cntOldNext = pNew->nCell + pNew->nOverflow;
@@ -75305,7 +76916,7 @@ static int balance_nonroot(
rc = SQLITE_CORRUPT_BKPT;
goto balance_cleanup;
}
- insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc);
+ rc = insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno);
if( rc!=SQLITE_OK ) goto balance_cleanup;
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
}
@@ -75401,7 +77012,7 @@ static int balance_nonroot(
);
copyNodeContent(apNew[0], pParent, &rc);
freePage(apNew[0], &rc);
- }else if( ISAUTOVACUUM && !leafCorrection ){
+ }else if( ISAUTOVACUUM(pBt) && !leafCorrection ){
/* Fix the pointer map entries associated with the right-child of each
** sibling page. All other pointer map entries have already been taken
** care of. */
@@ -75422,7 +77033,7 @@ static int balance_nonroot(
}
#if 0
- if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){
+ if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){
/* The ptrmapCheckPages() contains assert() statements that verify that
** all pointer map pages are set correctly. This is helpful while
** debugging. This is usually disabled because a corrupt database may
@@ -75484,7 +77095,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
if( rc==SQLITE_OK ){
rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0);
copyNodeContent(pRoot, pChild, &rc);
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc);
}
}
@@ -75588,6 +77199,11 @@ static int balance(BtCursor *pCur){
}else{
break;
}
+ }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){
+ /* 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;
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
@@ -75718,9 +77334,13 @@ static int btreeOverwriteContent(
/*
** Overwrite the cell that cursor pCur is pointing to with fresh content
-** contained in pX.
+** contained in pX. In this variant, pCur is pointing to an overflow
+** cell.
*/
-static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
+static SQLITE_NOINLINE int btreeOverwriteOverflowCell(
+ BtCursor *pCur, /* Cursor pointing to cell to ovewrite */
+ const BtreePayload *pX /* Content to write into the cell */
+){
int iOffset; /* Next byte of pX->pData to write */
int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
int rc; /* Return code */
@@ -75729,16 +77349,12 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
Pgno ovflPgno; /* Next overflow page to write */
u32 ovflPageSize; /* Size to write on overflow page */
- if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
- || pCur->info.pPayload < pPage->aData + pPage->cellOffset
- ){
- return SQLITE_CORRUPT_BKPT;
- }
+ assert( pCur->info.nLocal<nTotal ); /* pCur is an overflow cell */
+
/* Overwrite the local portion first */
rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
0, pCur->info.nLocal);
if( rc ) return rc;
- if( pCur->info.nLocal==nTotal ) return SQLITE_OK;
/* Now overwrite the overflow pages */
iOffset = pCur->info.nLocal;
@@ -75768,6 +77384,29 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
return SQLITE_OK;
}
+/*
+** Overwrite the cell that cursor pCur is pointing to with fresh content
+** contained in pX.
+*/
+static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
+ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
+ MemPage *pPage = pCur->pPage; /* Page being written */
+
+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
+ || pCur->info.pPayload < pPage->aData + pPage->cellOffset
+ ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ if( pCur->info.nLocal==nTotal ){
+ /* The entire cell is local */
+ return btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
+ 0, pCur->info.nLocal);
+ }else{
+ /* The cell contains overflow content */
+ return btreeOverwriteOverflowCell(pCur, pX);
+ }
+}
+
/*
** Insert a new record into the BTree. The content of the new record
@@ -75811,7 +77450,6 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
int idx;
MemPage *pPage;
Btree *p = pCur->pBtree;
- BtShared *pBt = p->pBt;
unsigned char *oldCell;
unsigned char *newCell = 0;
@@ -75830,7 +77468,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** not to clear the cursor here.
*/
if( pCur->curFlags & BTCF_Multiple ){
- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc;
if( loc && pCur->iPage<0 ){
/* This can only happen if the schema is corrupt such that there is more
@@ -75854,8 +77492,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
assert( cursorOwnsBtShared(pCur) );
assert( (pCur->curFlags & BTCF_WriteFlag)!=0
- && pBt->inTransaction==TRANS_WRITE
- && (pBt->btsFlags & BTS_READ_ONLY)==0 );
+ && p->pBt->inTransaction==TRANS_WRITE
+ && (p->pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
/* Assert that the caller has been consistent. If this cursor was opened
@@ -75972,26 +77610,28 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit || CORRUPT_DB );
- newCell = pBt->pTmpSpace;
+ newCell = p->pBt->pTmpSpace;
assert( newCell!=0 );
+ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
if( flags & BTREE_PREFORMAT ){
rc = SQLITE_OK;
- szNew = pBt->nPreformatSize;
+ szNew = p->pBt->nPreformatSize;
if( szNew<4 ) szNew = 4;
- if( ISAUTOVACUUM && szNew>pPage->maxLocal ){
+ if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
CellInfo info;
pPage->xParseCell(pPage, newCell, &info);
if( info.nPayload!=info.nLocal ){
Pgno ovfl = get4byte(&newCell[szNew-4]);
- ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
+ ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
+ if( NEVER(rc) ) goto end_insert;
}
}
}else{
rc = fillInCell(pPage, newCell, pX, &szNew);
+ if( rc ) goto end_insert;
}
- if( rc ) goto end_insert;
assert( szNew==pPage->xCellSize(pPage, newCell) );
- assert( szNew <= MX_CELL_SIZE(pBt) );
+ assert( szNew <= MX_CELL_SIZE(p->pBt) );
idx = pCur->ix;
if( loc==0 ){
CellInfo info;
@@ -76011,7 +77651,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
testcase( pCur->curFlags & BTCF_ValidOvfl );
invalidateOverflowCache(pCur);
if( info.nSize==szNew && info.nLocal==info.nPayload
- && (!ISAUTOVACUUM || szNew<pPage->minLocal)
+ && (!ISAUTOVACUUM(p->pBt) || szNew<pPage->minLocal)
){
/* Overwrite the old cell with the new if they are the same size.
** We could also try to do this if the old cell is smaller, then add
@@ -76041,7 +77681,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
}else{
assert( pPage->leaf );
}
- insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
+ rc = insertCell(pPage, idx, newCell, szNew, 0, 0);
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
@@ -76114,7 +77754,6 @@ end_insert:
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
- int rc = SQLITE_OK;
BtShared *pBt = pDest->pBt;
u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
const u8 *aIn; /* Pointer to next input buffer */
@@ -76137,7 +77776,9 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
memcpy(aOut, aIn, nIn);
pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
+ return SQLITE_OK;
}else{
+ int rc = SQLITE_OK;
Pager *pSrcPager = pSrc->pBt->pPager;
u8 *pPgnoOut = 0;
Pgno ovflIn = 0;
@@ -76189,7 +77830,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
MemPage *pNew = 0;
rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
put4byte(pPgnoOut, pgnoNew);
- if( ISAUTOVACUUM && pPageOut ){
+ if( ISAUTOVACUUM(pBt) && pPageOut ){
ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
}
releasePage(pPageOut);
@@ -76205,9 +77846,8 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
releasePage(pPageOut);
sqlite3PagerUnref(pPageIn);
+ return rc;
}
-
- return rc;
}
/*
@@ -76362,7 +78002,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( pTmp!=0 );
rc = sqlite3PagerWrite(pLeaf->pDbPage);
if( rc==SQLITE_OK ){
- insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
+ rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n);
}
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
if( rc ) return rc;
@@ -76962,6 +78602,41 @@ SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
/*
+** Record an OOM error during integrity_check
+*/
+static void checkOom(IntegrityCk *pCheck){
+ pCheck->rc = SQLITE_NOMEM;
+ pCheck->mxErr = 0; /* Causes integrity_check processing to stop */
+ if( pCheck->nErr==0 ) pCheck->nErr++;
+}
+
+/*
+** Invoke the progress handler, if appropriate. Also check for an
+** interrupt.
+*/
+static void checkProgress(IntegrityCk *pCheck){
+ sqlite3 *db = pCheck->db;
+ if( AtomicLoad(&db->u1.isInterrupted) ){
+ pCheck->rc = SQLITE_INTERRUPT;
+ pCheck->nErr++;
+ pCheck->mxErr = 0;
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( db->xProgress ){
+ assert( db->nProgressOps>0 );
+ pCheck->nStep++;
+ if( (pCheck->nStep % db->nProgressOps)==0
+ && db->xProgress(db->pProgressArg)
+ ){
+ pCheck->rc = SQLITE_INTERRUPT;
+ pCheck->nErr++;
+ pCheck->mxErr = 0;
+ }
+ }
+#endif
+}
+
+/*
** Append a message to the error message string.
*/
static void checkAppendMsg(
@@ -76970,6 +78645,7 @@ static void checkAppendMsg(
...
){
va_list ap;
+ checkProgress(pCheck);
if( !pCheck->mxErr ) return;
pCheck->mxErr--;
pCheck->nErr++;
@@ -76983,7 +78659,7 @@ static void checkAppendMsg(
sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
if( pCheck->errMsg.accError==SQLITE_NOMEM ){
- pCheck->bOomFault = 1;
+ checkOom(pCheck);
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -77025,7 +78701,6 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage){
checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
return 1;
}
- if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1;
setPageReferenced(pCheck, iPage);
return 0;
}
@@ -77048,7 +78723,7 @@ static void checkPtrmap(
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1;
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck);
checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
return;
}
@@ -77155,7 +78830,9 @@ static void checkList(
** lower 16 bits are the index of the last byte of that range.
*/
static void btreeHeapInsert(u32 *aHeap, u32 x){
- u32 j, i = ++aHeap[0];
+ u32 j, i;
+ assert( aHeap!=0 );
+ i = ++aHeap[0];
aHeap[i] = x;
while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){
x = aHeap[j];
@@ -77232,6 +78909,8 @@ static int checkTreePage(
/* Check that the page exists
*/
+ checkProgress(pCheck);
+ if( pCheck->mxErr==0 ) goto end_of_check;
pBt = pCheck->pBt;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
@@ -77477,13 +79156,14 @@ end_of_check:
** the unverified btrees. Except, if aRoot[1] is 1, then the freelist
** checks are still performed.
*/
-SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
+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 */
int nRoot, /* Number of entries in aRoot[] */
int mxErr, /* Stop reporting errors after this many */
- int *pnErr /* Write number of errors seen to this variable */
+ int *pnErr, /* OUT: Write number of errors seen to this variable */
+ char **pzOut /* OUT: Write the error message string here */
){
Pgno i;
IntegrityCk sCheck;
@@ -77506,18 +79186,12 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
assert( nRef>=0 );
+ memset(&sCheck, 0, sizeof(sCheck));
sCheck.db = db;
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = btreePagecount(sCheck.pBt);
sCheck.mxErr = mxErr;
- sCheck.nErr = 0;
- sCheck.bOomFault = 0;
- sCheck.zPfx = 0;
- sCheck.v1 = 0;
- sCheck.v2 = 0;
- sCheck.aPgRef = 0;
- sCheck.heap = 0;
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
if( sCheck.nPage==0 ){
@@ -77526,12 +79200,12 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
if( !sCheck.aPgRef ){
- sCheck.bOomFault = 1;
+ checkOom(&sCheck);
goto integrity_ck_cleanup;
}
sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
if( sCheck.heap==0 ){
- sCheck.bOomFault = 1;
+ checkOom(&sCheck);
goto integrity_ck_cleanup;
}
@@ -77612,16 +79286,17 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
integrity_ck_cleanup:
sqlite3PageFree(sCheck.heap);
sqlite3_free(sCheck.aPgRef);
- if( sCheck.bOomFault ){
+ *pnErr = sCheck.nErr;
+ if( sCheck.nErr==0 ){
sqlite3_str_reset(&sCheck.errMsg);
- sCheck.nErr++;
+ *pzOut = 0;
+ }else{
+ *pzOut = sqlite3StrAccumFinish(&sCheck.errMsg);
}
- *pnErr = sCheck.nErr;
- if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg);
/* Make sure this analysis did not leave any unref() pages. */
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
sqlite3BtreeLeave(p);
- return sqlite3StrAccumFinish(&sCheck.errMsg);
+ return sCheck.rc;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -77886,6 +79561,17 @@ SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){
*/
SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
+/*
+** If no transaction is active and the database is not a temp-db, clear
+** the in-memory pager cache.
+*/
+SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree *p){
+ BtShared *pBt = p->pBt;
+ if( pBt->inTransaction==TRANS_NONE ){
+ sqlite3PagerClearCache(pBt->pPager);
+ }
+}
+
#if !defined(SQLITE_OMIT_SHARED_CACHE)
/*
** Return true if the Btree passed as the only argument is sharable.
@@ -78796,9 +80482,9 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
i64 x;
assert( (p->flags&MEM_Int)*2==sizeof(x) );
memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2);
- sqlite3Int64ToText(x, zBuf);
+ p->n = sqlite3Int64ToText(x, zBuf);
#else
- sqlite3Int64ToText(p->u.i, zBuf);
+ p->n = sqlite3Int64ToText(p->u.i, zBuf);
#endif
}else{
sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0);
@@ -78806,6 +80492,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
(p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r);
assert( acc.zText==zBuf && acc.mxAlloc<=0 );
zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */
+ p->n = acc.nChar;
}
}
@@ -78833,6 +80520,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
** This routine is for use inside of assert() statements only.
*/
SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){
+ Mem tmp;
char zBuf[100];
char *z;
int i, j, incr;
@@ -78849,7 +80537,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){
assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 );
}
if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1;
- vdbeMemRenderNum(sizeof(zBuf), zBuf, p);
+ memcpy(&tmp, p, sizeof(tmp));
+ vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp);
z = p->z;
i = j = 0;
incr = 1;
@@ -79118,7 +80807,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
vdbeMemRenderNum(nByte, pMem->z, pMem);
assert( pMem->z!=0 );
- pMem->n = sqlite3Strlen30NN(pMem->z);
+ assert( pMem->n==sqlite3Strlen30NN(pMem->z) );
pMem->enc = SQLITE_UTF8;
pMem->flags |= MEM_Str|MEM_Term;
if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
@@ -79358,32 +81047,35 @@ SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){
}
/*
-** The MEM structure is already a MEM_Real. Try to also make it a
-** MEM_Int if we can.
+** The MEM structure is already a MEM_Real or MEM_IntReal. Try to
+** make it a MEM_Int if we can.
*/
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
- i64 ix;
assert( pMem!=0 );
- assert( pMem->flags & MEM_Real );
+ assert( pMem->flags & (MEM_Real|MEM_IntReal) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- ix = doubleToInt64(pMem->u.r);
-
- /* Only mark the value as an integer if
- **
- ** (1) the round-trip conversion real->int->real is a no-op, and
- ** (2) The integer is neither the largest nor the smallest
- ** possible integer (ticket #3922)
- **
- ** The second and third terms in the following conditional enforces
- ** the second condition under the assumption that addition overflow causes
- ** values to wrap around.
- */
- if( pMem->u.r==ix && ix>SMALLEST_INT64 && ix<LARGEST_INT64 ){
- pMem->u.i = ix;
+ if( pMem->flags & MEM_IntReal ){
MemSetTypeFlag(pMem, MEM_Int);
+ }else{
+ i64 ix = doubleToInt64(pMem->u.r);
+
+ /* Only mark the value as an integer if
+ **
+ ** (1) the round-trip conversion real->int->real is a no-op, and
+ ** (2) The integer is neither the largest nor the smallest
+ ** possible integer (ticket #3922)
+ **
+ ** The second and third terms in the following conditional enforces
+ ** the second condition under the assumption that addition overflow causes
+ ** values to wrap around.
+ */
+ if( pMem->u.r==ix && ix>SMALLEST_INT64 && ix<LARGEST_INT64 ){
+ pMem->u.i = ix;
+ MemSetTypeFlag(pMem, MEM_Int);
+ }
}
}
@@ -79431,6 +81123,16 @@ SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
&& i >= -2251799813685248LL && i < 2251799813685248LL);
}
+/* Convert a floating point value to its closest integer. Do so in
+** a way that avoids 'outside the range of representable values' warnings
+** from UBSAN.
+*/
+SQLITE_PRIVATE i64 sqlite3RealToI64(double r){
+ if( r<=(double)SMALLEST_INT64 ) return SMALLEST_INT64;
+ if( r>=(double)LARGEST_INT64) return LARGEST_INT64;
+ return (i64)r;
+}
+
/*
** Convert pMem so that it has type MEM_Real or MEM_Int.
** Invalidate any prior representations.
@@ -79452,7 +81154,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
if( ((rc==0 || rc==1) && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1)
- || sqlite3RealSameAsInt(pMem->u.r, (ix = (i64)pMem->u.r))
+ || sqlite3RealSameAsInt(pMem->u.r, (ix = sqlite3RealToI64(pMem->u.r)))
){
pMem->u.i = ix;
MemSetTypeFlag(pMem, MEM_Int);
@@ -79504,6 +81206,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero);
+ if( encoding!=SQLITE_UTF8 ) pMem->n &= ~1;
return sqlite3VdbeChangeEncoding(pMem, encoding);
}
}
@@ -80173,8 +81876,6 @@ static int valueFromFunction(
goto value_from_function_out;
}
- testcase( pCtx->pParse->rc==SQLITE_ERROR );
- testcase( pCtx->pParse->rc==SQLITE_OK );
memset(&ctx, 0, sizeof(ctx));
ctx.pOut = pVal;
ctx.pFunc = pFunc;
@@ -80186,17 +81887,22 @@ static int valueFromFunction(
}else{
sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8);
assert( rc==SQLITE_OK );
+ assert( enc==pVal->enc
+ || (pVal->flags & MEM_Str)==0
+ || db->mallocFailed );
+#if 0 /* Not reachable except after a prior failure */
rc = sqlite3VdbeChangeEncoding(pVal, enc);
if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){
rc = SQLITE_TOOBIG;
pCtx->pParse->nErr++;
}
+#endif
}
- pCtx->pParse->rc = rc;
value_from_function_out:
if( rc!=SQLITE_OK ){
pVal = 0;
+ pCtx->pParse->rc = rc;
}
if( apVal ){
for(i=0; i<nVal; i++){
@@ -80639,6 +82345,9 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){
return p->n;
}
+ if( (p->flags & MEM_Str)!=0 && enc!=SQLITE_UTF8 && pVal->enc!=SQLITE_UTF8 ){
+ return p->n;
+ }
if( (p->flags & MEM_Blob)!=0 ){
if( p->flags & MEM_Zero ){
return p->n + p->u.nZero;
@@ -80684,10 +82393,10 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){
memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp));
p->db = db;
if( db->pVdbe ){
- db->pVdbe->pPrev = p;
+ db->pVdbe->ppVPrev = &p->pVNext;
}
- p->pNext = db->pVdbe;
- p->pPrev = 0;
+ p->pVNext = db->pVdbe;
+ p->ppVPrev = &db->pVdbe;
db->pVdbe = p;
assert( p->eVdbeState==VDBE_INIT_STATE );
p->pParse = pParse;
@@ -80769,21 +82478,28 @@ SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString(
#endif
/*
-** Swap all content between two VDBE structures.
+** Swap byte-code between two VDBE structures.
+**
+** This happens after pB was previously run and returned
+** SQLITE_SCHEMA. The statement was then reprepared in pA.
+** This routine transfers the new bytecode in pA over to pB
+** so that pB can be run again. The old pB byte code is
+** moved back to pA so that it will be cleaned up when pA is
+** finalized.
*/
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
- Vdbe tmp, *pTmp;
+ Vdbe tmp, *pTmp, **ppTmp;
char *zTmp;
assert( pA->db==pB->db );
tmp = *pA;
*pA = *pB;
*pB = tmp;
- pTmp = pA->pNext;
- pA->pNext = pB->pNext;
- pB->pNext = pTmp;
- pTmp = pA->pPrev;
- pA->pPrev = pB->pPrev;
- pB->pPrev = pTmp;
+ pTmp = pA->pVNext;
+ pA->pVNext = pB->pVNext;
+ pB->pVNext = pTmp;
+ ppTmp = pA->ppVPrev;
+ pA->ppVPrev = pB->ppVPrev;
+ pB->ppVPrev = ppTmp;
zTmp = pA->zSql;
pA->zSql = pB->zSql;
pB->zSql = zTmp;
@@ -80859,6 +82575,8 @@ static int growOpArray(Vdbe *v, int nOp){
*/
static void test_addop_breakpoint(int pc, Op *pOp){
static int n = 0;
+ (void)pc;
+ (void)pOp;
n++;
}
#endif
@@ -80909,16 +82627,16 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOp->zComment = 0;
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+#endif
#ifdef SQLITE_DEBUG
if( p->db->flags & SQLITE_VdbeAddopTrace ){
sqlite3VdbePrintOp(0, i, &p->aOp[i]);
test_addop_breakpoint(i, &p->aOp[i]);
}
#endif
-#ifdef VDBE_PROFILE
- pOp->cycles = 0;
- pOp->cnt = 0;
-#endif
#ifdef SQLITE_VDBE_COVERAGE
pOp->iSrcLine = 0;
#endif
@@ -81035,6 +82753,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall(
addr = sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function,
p1, p2, p3, (char*)pCtx, P4_FUNCCTX);
sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef);
+ sqlite3MayAbort(pParse);
return addr;
}
@@ -81085,8 +82804,9 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){
** If the bPush flag is true, then make this opcode the parent for
** subsequent Explains until sqlite3VdbeExplainPop() is called.
*/
-SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
-#ifndef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
+ int addr = 0;
+#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
/* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined.
** But omit them (for performance) during production builds */
if( pParse->explain==2 )
@@ -81101,13 +82821,15 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt
va_end(ap);
v = pParse->pVdbe;
iThis = v->nOp;
- sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
+ addr = sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
zMsg, P4_DYNAMIC);
- sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetOp(v,-1)->p4.z);
+ sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z);
if( bPush){
pParse->addrExplain = iThis;
}
+ sqlite3VdbeScanStatus(v, iThis, 0, 0, 0, 0);
}
+ return addr;
}
/*
@@ -81215,6 +82937,9 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){
int i;
for(i=p->nLabelAlloc; i<nNewSize; i++) p->aLabel[i] = -1;
#endif
+ if( nNewSize>=100 && (nNewSize/100)>(p->nLabelAlloc/100) ){
+ sqlite3ProgressCheck(p);
+ }
p->nLabelAlloc = nNewSize;
p->aLabel[j] = v->nOp;
}
@@ -81370,6 +83095,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|| opcode==OP_VDestroy
|| opcode==OP_VCreate
|| opcode==OP_ParseSchema
+ || opcode==OP_Function || opcode==OP_PureFunc
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
){
@@ -81460,8 +83186,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
p->readOnly = 1;
p->bIsReader = 0;
pOp = &p->aOp[p->nOp-1];
- while(1){
-
+ assert( p->aOp[0].opcode==OP_Init );
+ while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){
/* Only JUMP opcodes and the short list of special opcodes in the switch
** below need to be considered. The mkopcodeh.tcl generator script groups
** all these opcodes together near the front of the opcode list. Skip
@@ -81490,6 +83216,10 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
p->bIsReader = 1;
break;
}
+ case OP_Init: {
+ assert( pOp->p2>=0 );
+ goto resolve_p2_values_loop_exit;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
case OP_VUpdate: {
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
@@ -81522,11 +83252,12 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0);
}
- if( pOp==p->aOp ) break;
+ assert( pOp>p->aOp );
pOp--;
}
+resolve_p2_values_loop_exit:
if( aLabel ){
- sqlite3DbFreeNN(p->db, pParse->aLabel);
+ sqlite3DbNNFreeNN(p->db, pParse->aLabel);
pParse->aLabel = 0;
}
pParse->nLabel = 0;
@@ -81759,6 +83490,7 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
if( aNew ){
ScanStatus *pNew = &aNew[p->nScan++];
+ memset(pNew, 0, sizeof(ScanStatus));
pNew->addrExplain = addrExplain;
pNew->addrLoop = addrLoop;
pNew->addrVisit = addrVisit;
@@ -81767,6 +83499,62 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
p->aScan = aNew;
}
}
+
+/*
+** Add the range of instructions from addrStart to addrEnd (inclusive) to
+** the set of those corresponding to the sqlite3_stmt_scanstatus() counters
+** associated with the OP_Explain instruction at addrExplain. The
+** sum of the sqlite3Hwtime() values for each of these instructions
+** will be returned for SQLITE_SCANSTAT_NCYCLE requests.
+*/
+SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(
+ Vdbe *p,
+ int addrExplain,
+ int addrStart,
+ int addrEnd
+){
+ ScanStatus *pScan = 0;
+ int ii;
+ for(ii=p->nScan-1; ii>=0; ii--){
+ pScan = &p->aScan[ii];
+ if( pScan->addrExplain==addrExplain ) break;
+ pScan = 0;
+ }
+ if( pScan ){
+ if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1;
+ for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
+ if( pScan->aAddrRange[ii]==0 ){
+ pScan->aAddrRange[ii] = addrStart;
+ pScan->aAddrRange[ii+1] = addrEnd;
+ break;
+ }
+ }
+ }
+}
+
+/*
+** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW
+** counters for the query element associated with the OP_Explain at
+** addrExplain.
+*/
+SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(
+ Vdbe *p,
+ int addrExplain,
+ int addrLoop,
+ int addrVisit
+){
+ ScanStatus *pScan = 0;
+ int ii;
+ for(ii=p->nScan-1; ii>=0; ii--){
+ pScan = &p->aScan[ii];
+ if( pScan->addrExplain==addrExplain ) break;
+ pScan = 0;
+ }
+ if( pScan ){
+ pScan->addrLoop = addrLoop;
+ pScan->addrVisit = addrVisit;
+ }
+}
#endif
@@ -81775,15 +83563,19 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
** for a specific instruction.
*/
SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){
+ assert( addr>=0 );
sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
+ assert( addr>=0 );
sqlite3VdbeGetOp(p,addr)->p1 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
+ assert( addr>=0 || p->db->mallocFailed );
sqlite3VdbeGetOp(p,addr)->p2 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
+ assert( addr>=0 );
sqlite3VdbeGetOp(p,addr)->p3 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
@@ -81792,6 +83584,18 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
}
/*
+** If the previous opcode is an OP_Column that delivers results
+** into register iDest, then add the OPFLAG_TYPEOFARG flag to that
+** opcode.
+*/
+SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
+ VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
+ if( pOp->p3==iDest && pOp->opcode==OP_Column ){
+ pOp->p5 |= OPFLAG_TYPEOFARG;
+ }
+}
+
+/*
** Change the P2 operand of instruction addr so that it points to
** the address of the next instruction to be coded.
*/
@@ -81819,7 +83623,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){
|| p->aOp[addr].opcode==OP_FkIfZero );
assert( p->aOp[addr].p4type==0 );
#ifdef SQLITE_VDBE_COVERAGE
- sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */
+ sqlite3VdbeGetLastOp(p)->iSrcLine = 0; /* Erase VdbeCoverage() macros */
#endif
p->nOp--;
}else{
@@ -81833,8 +83637,9 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){
** the FuncDef is not ephermal, then do nothing.
*/
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
+ assert( db!=0 );
if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
- sqlite3DbFreeNN(db, pDef);
+ sqlite3DbNNFreeNN(db, pDef);
}
}
@@ -81843,11 +83648,12 @@ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
*/
static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){
+ assert( db!=0 );
freeEphemeralFunction(db, p->pFunc);
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
static void freeP4(sqlite3 *db, int p4type, void *p4){
assert( db );
@@ -81860,7 +83666,7 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
case P4_INT64:
case P4_DYNAMIC:
case P4_INTARRAY: {
- sqlite3DbFree(db, p4);
+ if( p4 ) sqlite3DbNNFreeNN(db, p4);
break;
}
case P4_KEYINFO: {
@@ -81899,6 +83705,7 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
*/
static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
assert( nOp>=0 );
+ assert( db!=0 );
if( aOp ){
Op *pOp = &aOp[nOp-1];
while(1){ /* Exit via break */
@@ -81909,7 +83716,7 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
if( pOp==aOp ) break;
pOp--;
}
- sqlite3DbFreeNN(db, aOp);
+ sqlite3DbNNFreeNN(db, aOp);
}
}
@@ -82078,7 +83885,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){
if( p->db->mallocFailed ){
freeP4(p->db, n, pP4);
}else{
- assert( pP4!=0 );
+ assert( pP4!=0 || n==P4_DYNAMIC );
assert( p->nOp>0 );
pOp = &p->aOp[p->nOp-1];
assert( pOp->p4type==P4_NOTUSED );
@@ -82140,13 +83947,13 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
** Set the value if the iSrcLine field for the previously coded instruction.
*/
SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){
- sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine;
+ sqlite3VdbeGetLastOp(v)->iSrcLine = iLine;
}
#endif /* SQLITE_VDBE_COVERAGE */
/*
-** Return the opcode for a given address. If the address is -1, then
-** return the most recently inserted opcode.
+** Return the opcode for a given address. The address must be non-negative.
+** See sqlite3VdbeGetLastOp() to get the most recently added opcode.
**
** If a memory allocation error has occurred prior to the calling of this
** routine, then a pointer to a dummy VdbeOp will be returned. That opcode
@@ -82162,9 +83969,6 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
** zeros, which is correct. MSVC generates a warning, nevertheless. */
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->eVdbeState==VDBE_INIT_STATE );
- if( addr<0 ){
- addr = p->nOp - 1;
- }
assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
if( p->db->mallocFailed ){
return (VdbeOp*)&dummy;
@@ -82173,6 +83977,12 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
}
}
+/* Return the most recently added opcode
+*/
+VdbeOp * sqlite3VdbeGetLastOp(Vdbe *p){
+ return sqlite3VdbeGetOp(p, p->nOp - 1);
+}
+
#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
/*
** Return an integer value for one of the parameters to the opcode pOp
@@ -82660,7 +84470,7 @@ static void releaseMemArray(Mem *p, int N){
sqlite3VdbeMemRelease(p);
p->flags = MEM_Undefined;
}else if( p->szMalloc ){
- sqlite3DbFreeNN(db, p->zMalloc);
+ sqlite3DbNNFreeNN(db, p->zMalloc);
p->szMalloc = 0;
p->flags = MEM_Undefined;
}
@@ -82874,7 +84684,6 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
releaseMemArray(pMem, 8);
- p->pResultSet = 0;
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -82931,7 +84740,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free);
p->nResColumn = 8;
}
- p->pResultSet = pMem;
+ p->pResultRow = pMem;
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM;
rc = SQLITE_ERROR;
@@ -83042,7 +84851,7 @@ static void *allocSpace(
** running it.
*/
SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+#if defined(SQLITE_DEBUG)
int i;
#endif
assert( p!=0 );
@@ -83071,8 +84880,8 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
p->nFkConstraint = 0;
#ifdef VDBE_PROFILE
for(i=0; i<p->nOp; i++){
- p->aOp[i].cnt = 0;
- p->aOp[i].cycles = 0;
+ p->aOp[i].nExec = 0;
+ p->aOp[i].nCycle = 0;
}
#endif
}
@@ -83181,9 +84990,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*));
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64));
-#endif
if( x.nNeeded ){
x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded);
x.nFree = x.nNeeded;
@@ -83192,9 +84998,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
-#endif
}
}
@@ -83209,9 +85012,6 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->nMem = nMem;
initMemArray(p->aMem, nMem, db, MEM_Undefined);
memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*));
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- memset(p->anExec, 0, p->nOp*sizeof(i64));
-#endif
}
sqlite3VdbeRewind(p);
}
@@ -83269,9 +85069,6 @@ static void closeCursorsInFrame(Vdbe *p){
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
closeCursorsInFrame(v);
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- v->anExec = pFrame->anExec;
-#endif
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -83652,7 +85449,7 @@ static void checkActiveVdbeCnt(sqlite3 *db){
if( p->readOnly==0 ) nWrite++;
if( p->bIsReader ) nRead++;
}
- p = p->pNext;
+ p = p->pVNext;
}
assert( cnt==db->nVdbeActive );
assert( nWrite==db->nVdbeWrite );
@@ -84075,7 +85872,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
- p->pResultSet = 0;
+ p->pResultRow = 0;
#ifdef SQLITE_DEBUG
p->nWrite = 0;
#endif
@@ -84103,10 +85900,12 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
}
for(i=0; i<p->nOp; i++){
char zHdr[100];
+ i64 cnt = p->aOp[i].nExec;
+ i64 cycles = p->aOp[i].nCycle;
sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
- p->aOp[i].cnt,
- p->aOp[i].cycles,
- p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0
+ cnt,
+ cycles,
+ cnt>0 ? cycles/cnt : 0
);
fprintf(out, "%s", zHdr);
sqlite3VdbePrintOp(out, i, &p->aOp[i]);
@@ -84181,10 +85980,11 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp,
*/
static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
+ assert( db!=0 );
assert( p->db==0 || p->db==db );
if( p->aColName ){
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
- sqlite3DbFreeNN(db, p->aColName);
+ sqlite3DbNNFreeNN(db, p->aColName);
}
for(pSub=p->pProgram; pSub; pSub=pNext){
pNext = pSub->pNext;
@@ -84193,11 +85993,11 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
}
if( p->eVdbeState!=VDBE_INIT_STATE ){
releaseMemArray(p->aVar, p->nVar);
- if( p->pVList ) sqlite3DbFreeNN(db, p->pVList);
- if( p->pFree ) sqlite3DbFreeNN(db, p->pFree);
+ if( p->pVList ) sqlite3DbNNFreeNN(db, p->pVList);
+ if( p->pFree ) sqlite3DbNNFreeNN(db, p->pFree);
}
vdbeFreeOpArray(db, p->aOp, p->nOp);
- sqlite3DbFree(db, p->zSql);
+ if( p->zSql ) sqlite3DbNNFreeNN(db, p->zSql);
#ifdef SQLITE_ENABLE_NORMALIZE
sqlite3DbFree(db, p->zNormSql);
{
@@ -84227,20 +86027,17 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
assert( p!=0 );
db = p->db;
+ assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
sqlite3VdbeClearObject(db, p);
if( db->pnBytesFreed==0 ){
- if( p->pPrev ){
- p->pPrev->pNext = p->pNext;
- }else{
- assert( db->pVdbe==p );
- db->pVdbe = p->pNext;
- }
- if( p->pNext ){
- p->pNext->pPrev = p->pPrev;
+ assert( p->ppVPrev!=0 );
+ *p->ppVPrev = p->pVNext;
+ if( p->pVNext ){
+ p->pVNext->ppVPrev = p->ppVPrev;
}
}
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
/*
@@ -85195,7 +86992,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
assert( pPKey2->pKeyInfo->aSortFlags!=0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
- do{
+ while( 1 /*exit-by-break*/ ){
u32 serial_type;
/* RHS is an integer */
@@ -85205,7 +87002,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
serial_type = aKey1[idx1];
testcase( serial_type==12 );
if( serial_type>=10 ){
- rc = +1;
+ rc = serial_type==10 ? -1 : +1;
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
@@ -85230,7 +87027,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
** numbers). Types 10 and 11 are currently "reserved for future
** use", so it doesn't really matter what the results of comparing
** them to numberic values are. */
- rc = +1;
+ rc = serial_type==10 ? -1 : +1;
}else if( serial_type==0 ){
rc = -1;
}else{
@@ -85311,7 +87108,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is null */
else{
serial_type = aKey1[idx1];
- rc = (serial_type!=0);
+ rc = (serial_type!=0 && serial_type!=10);
}
if( rc!=0 ){
@@ -85333,8 +87130,13 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
if( i==pPKey2->nField ) break;
pRhs++;
d1 += sqlite3VdbeSerialTypeLen(serial_type);
+ if( d1>(unsigned)nKey1 ) break;
idx1 += sqlite3VarintLen(serial_type);
- }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 );
+ if( idx1>=(unsigned)szHdr1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corrupt index */
+ }
+ }
/* No memory allocation is ever used on mem1. Prove this using
** the following assert(). If the assert() fails, it indicates a
@@ -85735,7 +87537,7 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe *v){
*/
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){
Vdbe *p;
- for(p = db->pVdbe; p; p=p->pNext){
+ for(p = db->pVdbe; p; p=p->pVNext){
p->expired = iCode+1;
}
}
@@ -85856,13 +87658,14 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
** the vdbeUnpackRecord() function found in vdbeapi.c.
*/
static void vdbeFreeUnpacked(sqlite3 *db, int nField, UnpackedRecord *p){
+ assert( db!=0 );
if( p ){
int i;
for(i=0; i<nField; i++){
Mem *pMem = &p->aMem[i];
if( pMem->zMalloc ) sqlite3VdbeMemReleaseMalloc(pMem);
}
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -85933,7 +87736,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
- sqlite3DbFreeNN(db, preupdate.aNew);
+ sqlite3DbNNFreeNN(db, preupdate.aNew);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -85957,6 +87760,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
*/
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */
+/* #include "opcodes.h" */
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -86050,7 +87854,9 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
sqlite3_mutex_enter(db->mutex);
checkProfileCallback(db, v);
- rc = sqlite3VdbeFinalize(v);
+ assert( v->eVdbeState>=VDBE_READY_STATE );
+ rc = sqlite3VdbeReset(v);
+ sqlite3VdbeDelete(v);
rc = sqlite3ApiExit(db, rc);
sqlite3LeaveMutexAndCloseZombie(db);
}
@@ -86258,6 +88064,9 @@ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){
#endif
return aType[pVal->flags&MEM_AffMask];
}
+SQLITE_API int sqlite3_value_encoding(sqlite3_value *pVal){
+ return pVal->enc;
+}
/* Return true if a parameter to xUpdate represents an unchanged column */
SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){
@@ -86442,7 +88251,10 @@ SQLITE_API void sqlite3_result_text64(
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ if( enc!=SQLITE_UTF8 ){
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ n &= ~(u64)1;
+ }
if( n>0x7fffffff ){
(void)invokeValueDestructor(z, xDel, pCtx);
}else{
@@ -86457,7 +88269,7 @@ SQLITE_API void sqlite3_result_text16(
void (*xDel)(void *)
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel);
+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16NATIVE, xDel);
}
SQLITE_API void sqlite3_result_text16be(
sqlite3_context *pCtx,
@@ -86466,7 +88278,7 @@ SQLITE_API void sqlite3_result_text16be(
void (*xDel)(void *)
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel);
+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16BE, xDel);
}
SQLITE_API void sqlite3_result_text16le(
sqlite3_context *pCtx,
@@ -86475,7 +88287,7 @@ SQLITE_API void sqlite3_result_text16le(
void (*xDel)(void *)
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel);
+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16LE, xDel);
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
@@ -86686,7 +88498,7 @@ static int sqlite3Step(Vdbe *p){
/* If the statement completed successfully, invoke the profile callback */
checkProfileCallback(db, p);
#endif
-
+ p->pResultRow = 0;
if( rc==SQLITE_DONE && db->autoCommit ){
assert( p->rc==SQLITE_OK );
p->rc = doWalCallbacks(db);
@@ -86816,6 +88628,17 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
}
/*
+** The destructor function for a ValueList object. This needs to be
+** a separate function, unknowable to the application, to ensure that
+** calls to sqlite3_vtab_in_first()/sqlite3_vtab_in_next() that are not
+** preceeded by activation of IN processing via sqlite3_vtab_int() do not
+** try to access a fake ValueList object inserted by a hostile extension.
+*/
+SQLITE_PRIVATE void sqlite3VdbeValueListFree(void *pToDelete){
+ sqlite3_free(pToDelete);
+}
+
+/*
** Implementation of sqlite3_vtab_in_first() (if bNext==0) and
** sqlite3_vtab_in_next() (if bNext!=0).
*/
@@ -86829,8 +88652,15 @@ static int valueFromValueList(
*ppOut = 0;
if( pVal==0 ) return SQLITE_MISUSE;
- pRhs = (ValueList*)sqlite3_value_pointer(pVal, "ValueList");
- if( pRhs==0 ) return SQLITE_MISUSE;
+ if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){
+ return SQLITE_ERROR;
+ }else{
+ assert( (pVal->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) ==
+ (MEM_Null|MEM_Term|MEM_Subtype) );
+ assert( pVal->eSubtype=='p' );
+ assert( pVal->u.zPType!=0 && strcmp(pVal->u.zPType,"ValueList")==0 );
+ pRhs = (ValueList*)pVal->z;
+ }
if( bNext ){
rc = sqlite3BtreeNext(pRhs->pCsr, 0);
}else{
@@ -87050,7 +88880,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){
*/
SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){
Vdbe *pVm = (Vdbe *)pStmt;
- if( pVm==0 || pVm->pResultSet==0 ) return 0;
+ if( pVm==0 || pVm->pResultRow==0 ) return 0;
return pVm->nResColumn;
}
@@ -87105,8 +88935,8 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
if( pVm==0 ) return (Mem*)columnNullValue();
assert( pVm->db );
sqlite3_mutex_enter(pVm->db->mutex);
- if( pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
- pOut = &pVm->pResultSet[i];
+ if( pVm->pResultRow!=0 && i<pVm->nResColumn && i>=0 ){
+ pOut = &pVm->pResultRow[i];
}else{
sqlite3Error(pVm->db, SQLITE_RANGE);
pOut = (Mem*)columnNullValue();
@@ -87372,7 +89202,7 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){
** The error code stored in database p->db is overwritten with the return
** value in any case.
*/
-static int vdbeUnbind(Vdbe *p, int i){
+static int vdbeUnbind(Vdbe *p, unsigned int i){
Mem *pVar;
if( vdbeSafetyNotNull(p) ){
return SQLITE_MISUSE_BKPT;
@@ -87385,12 +89215,11 @@ static int vdbeUnbind(Vdbe *p, int i){
"bind on a busy prepared statement: [%s]", p->zSql);
return SQLITE_MISUSE_BKPT;
}
- if( i<1 || i>p->nVar ){
+ if( i>=(unsigned int)p->nVar ){
sqlite3Error(p->db, SQLITE_RANGE);
sqlite3_mutex_leave(p->db->mutex);
return SQLITE_RANGE;
}
- i--;
pVar = &p->aVar[i];
sqlite3VdbeMemRelease(pVar);
pVar->flags = MEM_Null;
@@ -87427,7 +89256,7 @@ static int bindText(
Mem *pVar;
int rc;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
if( zData!=0 ){
pVar = &p->aVar[i-1];
@@ -87476,7 +89305,7 @@ SQLITE_API int sqlite3_bind_blob64(
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
sqlite3_mutex_leave(p->db->mutex);
@@ -87489,7 +89318,7 @@ SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
sqlite3_mutex_leave(p->db->mutex);
@@ -87499,7 +89328,7 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu
SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
int rc;
Vdbe *p = (Vdbe*)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3_mutex_leave(p->db->mutex);
}
@@ -87514,7 +89343,7 @@ SQLITE_API int sqlite3_bind_pointer(
){
int rc;
Vdbe *p = (Vdbe*)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor);
sqlite3_mutex_leave(p->db->mutex);
@@ -87541,7 +89370,10 @@ SQLITE_API int sqlite3_bind_text64(
unsigned char enc
){
assert( xDel!=SQLITE_DYNAMIC );
- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ if( enc!=SQLITE_UTF8 ){
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ nData &= ~(u16)1;
+ }
return bindText(pStmt, i, zData, nData, xDel, enc);
}
#ifndef SQLITE_OMIT_UTF16
@@ -87549,10 +89381,10 @@ SQLITE_API int sqlite3_bind_text16(
sqlite3_stmt *pStmt,
int i,
const void *zData,
- int nData,
+ int n,
void (*xDel)(void*)
){
- return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE);
+ return bindText(pStmt, i, zData, n & ~(u64)1, xDel, SQLITE_UTF16NATIVE);
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
@@ -87592,7 +89424,7 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_INCRBLOB
sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
@@ -87752,7 +89584,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
if( pStmt==0 ){
pNext = (sqlite3_stmt*)pDb->pVdbe;
}else{
- pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pNext;
+ pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pVNext;
}
sqlite3_mutex_leave(pDb->mutex);
return pNext;
@@ -87777,8 +89609,11 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
sqlite3_mutex_enter(db->mutex);
v = 0;
db->pnBytesFreed = (int*)&v;
+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
+ db->lookaside.pEnd = db->lookaside.pStart;
sqlite3VdbeDelete(pVdbe);
db->pnBytesFreed = 0;
+ db->lookaside.pEnd = db->lookaside.pTrueEnd;
sqlite3_mutex_leave(db->mutex);
}else{
v = pVdbe->aCounter[op];
@@ -88040,23 +89875,60 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
/*
** Return status data for a single loop within query pStmt.
*/
-SQLITE_API int sqlite3_stmt_scanstatus(
+SQLITE_API int sqlite3_stmt_scanstatus_v2(
sqlite3_stmt *pStmt, /* Prepared statement being queried */
- int idx, /* Index of loop to report on */
+ int iScan, /* Index of loop to report on */
int iScanStatusOp, /* Which metric to return */
+ int flags,
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
ScanStatus *pScan;
- if( idx<0 || idx>=p->nScan ) return 1;
- pScan = &p->aScan[idx];
+ int idx;
+
+ if( iScan<0 ){
+ int ii;
+ if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){
+ i64 res = 0;
+ for(ii=0; ii<p->nOp; ii++){
+ res += p->aOp[ii].nCycle;
+ }
+ *(i64*)pOut = res;
+ return 0;
+ }
+ return 1;
+ }
+ 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. */
+ for(idx=0; idx<p->nScan; idx++){
+ pScan = &p->aScan[idx];
+ if( pScan->zName ){
+ iScan--;
+ if( iScan<0 ) break;
+ }
+ }
+ }
+ if( idx>=p->nScan ) return 1;
+
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
- *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop];
+ if( pScan->addrLoop>0 ){
+ *(sqlite3_int64*)pOut = p->aOp[pScan->addrLoop].nExec;
+ }else{
+ *(sqlite3_int64*)pOut = -1;
+ }
break;
}
case SQLITE_SCANSTAT_NVISIT: {
- *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit];
+ if( pScan->addrVisit>0 ){
+ *(sqlite3_int64*)pOut = p->aOp[pScan->addrVisit].nExec;
+ }else{
+ *(sqlite3_int64*)pOut = -1;
+ }
break;
}
case SQLITE_SCANSTAT_EST: {
@@ -88089,6 +89961,45 @@ SQLITE_API int sqlite3_stmt_scanstatus(
}
break;
}
+ case SQLITE_SCANSTAT_PARENTID: {
+ if( pScan->addrExplain ){
+ *(int*)pOut = p->aOp[ pScan->addrExplain ].p2;
+ }else{
+ *(int*)pOut = -1;
+ }
+ break;
+ }
+ case SQLITE_SCANSTAT_NCYCLE: {
+ i64 res = 0;
+ if( pScan->aAddrRange[0]==0 ){
+ res = -1;
+ }else{
+ int ii;
+ for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
+ int iIns = pScan->aAddrRange[ii];
+ int iEnd = pScan->aAddrRange[ii+1];
+ if( iIns==0 ) break;
+ if( iIns>0 ){
+ while( iIns<=iEnd ){
+ res += p->aOp[iIns].nCycle;
+ iIns++;
+ }
+ }else{
+ int iOp;
+ for(iOp=0; iOp<p->nOp; iOp++){
+ Op *pOp = &p->aOp[iOp];
+ if( pOp->p1!=iEnd ) continue;
+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){
+ continue;
+ }
+ res += p->aOp[iOp].nCycle;
+ }
+ }
+ }
+ }
+ *(i64*)pOut = res;
+ break;
+ }
default: {
return 1;
}
@@ -88097,11 +90008,28 @@ SQLITE_API int sqlite3_stmt_scanstatus(
}
/*
+** Return status data for a single loop within query pStmt.
+*/
+SQLITE_API int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt, /* Prepared statement being queried */
+ int iScan, /* Index of loop to report on */
+ int iScanStatusOp, /* Which metric to return */
+ void *pOut /* OUT: Write the answer here */
+){
+ return sqlite3_stmt_scanstatus_v2(pStmt, iScan, iScanStatusOp, 0, pOut);
+}
+
+/*
** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
*/
SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
- memset(p->anExec, 0, p->nOp * sizeof(i64));
+ int ii;
+ for(ii=0; ii<p->nOp; ii++){
+ Op *pOp = &p->aOp[ii];
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+ }
}
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
@@ -88437,6 +90365,9 @@ SQLITE_API int sqlite3_found_count = 0;
*/
static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
static int n = 0;
+ (void)pc;
+ (void)pOp;
+ (void)v;
n++;
}
#endif
@@ -88618,7 +90549,8 @@ static VdbeCursor *allocateCursor(
** return false.
*/
static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){
- i64 iValue = (double)rValue;
+ i64 iValue;
+ iValue = sqlite3RealToI64(rValue);
if( sqlite3RealSameAsInt(rValue,iValue) ){
*piValue = iValue;
return 1;
@@ -88674,6 +90606,10 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){
** always preferred, even if the affinity is REAL, because
** an integer representation is more space efficient on disk.
**
+** SQLITE_AFF_FLEXNUM:
+** If the value is text, then try to convert it into a number of
+** some kind (integer or real) but do not make any other changes.
+**
** SQLITE_AFF_TEXT:
** Convert pRec to a text representation.
**
@@ -88688,11 +90624,11 @@ static void applyAffinity(
){
if( affinity>=SQLITE_AFF_NUMERIC ){
assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
- || affinity==SQLITE_AFF_NUMERIC );
+ || affinity==SQLITE_AFF_NUMERIC || affinity==SQLITE_AFF_FLEXNUM );
if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/
- if( (pRec->flags & MEM_Real)==0 ){
+ if( (pRec->flags & (MEM_Real|MEM_IntReal))==0 ){
if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1);
- }else{
+ }else if( affinity<=SQLITE_AFF_REAL ){
sqlite3VdbeIntegerAffinity(pRec);
}
}
@@ -88780,17 +90716,18 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){
** But it does set pMem->u.r and pMem->u.i appropriately.
*/
static u16 numericType(Mem *pMem){
- if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal) ){
+ assert( (pMem->flags & MEM_Null)==0
+ || pMem->db==0 || pMem->db->mallocFailed );
+ if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null) ){
testcase( pMem->flags & MEM_Int );
testcase( pMem->flags & MEM_Real );
testcase( pMem->flags & MEM_IntReal );
- return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal);
- }
- if( pMem->flags & (MEM_Str|MEM_Blob) ){
- testcase( pMem->flags & MEM_Str );
- testcase( pMem->flags & MEM_Blob );
- return computeNumericType(pMem);
+ return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null);
}
+ assert( pMem->flags & (MEM_Str|MEM_Blob) );
+ testcase( pMem->flags & MEM_Str );
+ testcase( pMem->flags & MEM_Blob );
+ return computeNumericType(pMem);
return 0;
}
@@ -88919,17 +90856,6 @@ SQLITE_PRIVATE void sqlite3VdbeRegisterDump(Vdbe *v){
# define REGISTER_TRACE(R,M)
#endif
-
-#ifdef VDBE_PROFILE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/* #include "hwtime.h" */
-
-#endif
-
#ifndef NDEBUG
/*
** This function is only called from within an assert() expression. It
@@ -89019,11 +90945,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
){
Op *aOp = p->aOp; /* Copy of p->aOp */
Op *pOp = aOp; /* Current operation */
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
- Op *pOrigOp; /* Value of pOp at the top of the loop */
-#endif
#ifdef SQLITE_DEBUG
+ Op *pOrigOp; /* Value of pOp at the top of the loop */
int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
+ u8 iCompareIsInit = 0; /* iCompare is initialized */
#endif
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
@@ -89039,13 +90964,15 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
-#ifdef VDBE_PROFILE
- u64 start; /* CPU clock count at start of opcode */
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ u64 *pnCycle = 0;
#endif
/*** INSERT STACK UNION HERE ***/
assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlite3_step() verifies this */
- sqlite3VdbeEnter(p);
+ if( DbMaskNonZero(p->lockMask) ){
+ sqlite3VdbeEnter(p);
+ }
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( db->xProgress ){
u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
@@ -89066,7 +90993,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( p->bIsReader || p->readOnly!=0 );
p->iCurrentTime = 0;
assert( p->explain==0 );
- p->pResultSet = 0;
db->busyHandler.nBusy = 0;
if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
sqlite3VdbeIOTraceSql(p);
@@ -89103,12 +91029,14 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( rc==SQLITE_OK );
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
-#ifdef VDBE_PROFILE
- start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
-#endif
nVmStep++;
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- if( p->anExec ) p->anExec[(int)(pOp-aOp)]++;
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ pOp->nExec++;
+ pnCycle = &pOp->nCycle;
+# ifdef VDBE_PROFILE
+ if( sqlite3NProfileCnt==0 )
+# endif
+ *pnCycle -= sqlite3Hwtime();
#endif
/* Only allow tracing if SQLITE_DEBUG is defined.
@@ -89170,7 +91098,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
}
#endif
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+#ifdef SQLITE_DEBUG
pOrigOp = pOp;
#endif
@@ -89454,6 +91382,12 @@ case OP_Halt: {
#ifdef SQLITE_DEBUG
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
#endif
+
+ /* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates
+ ** something is wrong with the code generator. Raise an assertion in order
+ ** to bring this to the attention of fuzzers and other testing tools. */
+ assert( pOp->p1!=SQLITE_INTERNAL );
+
if( p->pFrame && pOp->p1==SQLITE_OK ){
/* Halt the sub-program. Return control to the parent frame. */
pFrame = p->pFrame;
@@ -89895,10 +91829,10 @@ case OP_ResultRow: {
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
p->cacheCtr = (p->cacheCtr + 2)|1;
- p->pResultSet = &aMem[pOp->p1];
+ p->pResultRow = &aMem[pOp->p1];
#ifdef SQLITE_DEBUG
{
- Mem *pMem = p->pResultSet;
+ Mem *pMem = p->pResultRow;
int i;
for(i=0; i<pOp->p2; i++){
assert( memIsValid(&pMem[i]) );
@@ -90035,7 +91969,6 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */
case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */
case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
- u16 flags; /* Combined MEM_* flags from both inputs */
u16 type1; /* Numeric type of left operand */
u16 type2; /* Numeric type of right operand */
i64 iA; /* Integer value of left operand */
@@ -90044,12 +91977,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
double rB; /* Real value of right operand */
pIn1 = &aMem[pOp->p1];
- type1 = numericType(pIn1);
+ type1 = pIn1->flags;
pIn2 = &aMem[pOp->p2];
- type2 = numericType(pIn2);
+ type2 = pIn2->flags;
pOut = &aMem[pOp->p3];
- flags = pIn1->flags | pIn2->flags;
if( (type1 & type2 & MEM_Int)!=0 ){
+int_math:
iA = pIn1->u.i;
iB = pIn2->u.i;
switch( pOp->opcode ){
@@ -90071,9 +92004,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
}
pOut->u.i = iB;
MemSetTypeFlag(pOut, MEM_Int);
- }else if( (flags & MEM_Null)!=0 ){
+ }else if( ((type1 | type2) & MEM_Null)!=0 ){
goto arithmetic_result_is_null;
}else{
+ type1 = numericType(pIn1);
+ type2 = numericType(pIn2);
+ if( (type1 & type2 & MEM_Int)!=0 ) goto int_math;
fp_math:
rA = sqlite3VdbeRealValue(pIn1);
rB = sqlite3VdbeRealValue(pIn2);
@@ -90426,7 +92362,6 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
flags1 = pIn1->flags;
flags3 = pIn3->flags;
if( (flags1 & flags3 & MEM_Int)!=0 ){
- assert( (pOp->p5 & SQLITE_AFF_MASK)!=SQLITE_AFF_TEXT || CORRUPT_DB );
/* Common case of comparison of two integers */
if( pIn3->u.i > pIn1->u.i ){
if( sqlite3aGTb[pOp->opcode] ){
@@ -90434,18 +92369,21 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
goto jump_to_p2;
}
iCompare = +1;
+ VVA_ONLY( iCompareIsInit = 1; )
}else if( pIn3->u.i < pIn1->u.i ){
if( sqlite3aLTb[pOp->opcode] ){
VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3);
goto jump_to_p2;
}
iCompare = -1;
+ VVA_ONLY( iCompareIsInit = 1; )
}else{
if( sqlite3aEQb[pOp->opcode] ){
VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3);
goto jump_to_p2;
}
iCompare = 0;
+ VVA_ONLY( iCompareIsInit = 1; )
}
VdbeBranchTaken(0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
break;
@@ -90477,6 +92415,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
goto jump_to_p2;
}
iCompare = 1; /* Operands are not equal */
+ VVA_ONLY( iCompareIsInit = 1; )
break;
}
}else{
@@ -90487,14 +92426,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
if( (flags1 | flags3)&MEM_Str ){
if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn1,0);
- testcase( flags3==pIn3->flags );
+ assert( flags3==pIn3->flags || CORRUPT_DB );
flags3 = pIn3->flags;
}
if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3,0);
}
}
- }else if( affinity==SQLITE_AFF_TEXT ){
+ }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_Real );
@@ -90502,7 +92441,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
sqlite3VdbeMemStringify(pIn1, encoding, 1);
testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
- if( pIn1==pIn3 ) flags3 = flags1 | MEM_Str;
+ if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
}
if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn3->flags & MEM_Int );
@@ -90533,6 +92472,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
res2 = sqlite3aGTb[pOp->opcode];
}
iCompare = res;
+ VVA_ONLY( iCompareIsInit = 1; )
/* Undo any changes made by applyAffinity() to the input registers. */
assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) );
@@ -90571,6 +92511,7 @@ case OP_ElseEq: { /* same as TK_ESCAPE, jump */
break;
}
#endif /* SQLITE_DEBUG */
+ assert( iCompareIsInit );
VdbeBranchTaken(iCompare==0, 2);
if( iCompare==0 ) goto jump_to_p2;
break;
@@ -90665,6 +92606,7 @@ case OP_Compare: {
pColl = pKeyInfo->aColl[i];
bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC);
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
+ VVA_ONLY( iCompareIsInit = 1; )
if( iCompare ){
if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
&& ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null))
@@ -90689,6 +92631,7 @@ case OP_Compare: {
*/
case OP_Jump: { /* jump */
assert( pOp>aOp && pOp[-1].opcode==OP_Compare );
+ assert( iCompareIsInit );
if( iCompare<0 ){
VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1];
}else if( iCompare==0 ){
@@ -90888,19 +92831,90 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
break;
}
-/* Opcode: IsNullOrType P1 P2 P3 * *
-** Synopsis: if typeof(r[P1]) IN (P3,5) goto P2
+/* Opcode: IsType P1 P2 P3 P4 P5
+** Synopsis: if typeof(P1.P3) in P5 goto P2
+**
+** Jump to P2 if the type of a column in a btree is one of the types specified
+** by the P5 bitmask.
+**
+** P1 is normally a cursor on a btree for which the row decode cache is
+** valid through at least column P3. In other words, there should have been
+** a prior OP_Column for column P3 or greater. If the cursor is not valid,
+** then this opcode might give spurious results.
+** The the btree row has fewer than P3 columns, then use P4 as the
+** datatype.
+**
+** If P1 is -1, then P3 is a register number and the datatype is taken
+** from the value in that register.
+**
+** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant
+** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04.
+** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10.
+**
+** Take the jump to address P2 if and only if the datatype of the
+** value determined by P1 and P3 corresponds to one of the bits in the
+** P5 bitmask.
**
-** Jump to P2 if the value in register P1 is NULL or has a datatype P3.
-** P3 is an integer which should be one of SQLITE_INTEGER, SQLITE_FLOAT,
-** SQLITE_BLOB, SQLITE_NULL, or SQLITE_TEXT.
*/
-case OP_IsNullOrType: { /* jump, in1 */
- int doTheJump;
- pIn1 = &aMem[pOp->p1];
- doTheJump = (pIn1->flags & MEM_Null)!=0 || sqlite3_value_type(pIn1)==pOp->p3;
- VdbeBranchTaken( doTheJump, 2);
- if( doTheJump ) goto jump_to_p2;
+case OP_IsType: { /* jump */
+ VdbeCursor *pC;
+ u16 typeMask;
+ u32 serialType;
+
+ assert( pOp->p1>=(-1) && pOp->p1<p->nCursor );
+ assert( pOp->p1>=0 || (pOp->p3>=0 && pOp->p3<=(p->nMem+1 - p->nCursor)) );
+ if( pOp->p1>=0 ){
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pOp->p3>=0 );
+ if( pOp->p3<pC->nHdrParsed ){
+ serialType = pC->aType[pOp->p3];
+ if( serialType>=12 ){
+ if( serialType&1 ){
+ typeMask = 0x04; /* SQLITE_TEXT */
+ }else{
+ typeMask = 0x08; /* SQLITE_BLOB */
+ }
+ }else{
+ static const unsigned char aMask[] = {
+ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2,
+ 0x01, 0x01, 0x10, 0x10
+ };
+ testcase( serialType==0 );
+ testcase( serialType==1 );
+ testcase( serialType==2 );
+ testcase( serialType==3 );
+ testcase( serialType==4 );
+ testcase( serialType==5 );
+ testcase( serialType==6 );
+ testcase( serialType==7 );
+ testcase( serialType==8 );
+ testcase( serialType==9 );
+ testcase( serialType==10 );
+ testcase( serialType==11 );
+ typeMask = aMask[serialType];
+ }
+ }else{
+ typeMask = 1 << (pOp->p4.i - 1);
+ testcase( typeMask==0x01 );
+ testcase( typeMask==0x02 );
+ testcase( typeMask==0x04 );
+ testcase( typeMask==0x08 );
+ testcase( typeMask==0x10 );
+ }
+ }else{
+ assert( memIsValid(&aMem[pOp->p3]) );
+ typeMask = 1 << (sqlite3_value_type((sqlite3_value*)&aMem[pOp->p3])-1);
+ testcase( typeMask==0x01 );
+ testcase( typeMask==0x02 );
+ testcase( typeMask==0x04 );
+ testcase( typeMask==0x08 );
+ testcase( typeMask==0x10 );
+ }
+ VdbeBranchTaken( (typeMask & pOp->p5)!=0, 2);
+ if( typeMask & pOp->p5 ){
+ goto jump_to_p2;
+ }
break;
}
@@ -90943,11 +92957,14 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** If it is, then set register P3 to NULL and jump immediately to P2.
** If P1 is not on a NULL row, then fall through without making any
** changes.
+**
+** If P1 is not an open cursor, then this opcode is a no-op.
*/
case OP_IfNullRow: { /* jump */
+ VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( p->apCsr[pOp->p1]!=0 );
- if( p->apCsr[pOp->p1]->nullRow ){
+ pC = p->apCsr[pOp->p1];
+ if( ALWAYS(pC) && pC->nullRow ){
sqlite3VdbeMemSetNull(aMem + pOp->p3);
goto jump_to_p2;
}
@@ -90998,7 +93015,7 @@ case OP_Offset: { /* out3 */
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
** information about the format of the data.) Extract the P2-th column
-** from this record. If there are less that (P2+1)
+** from this record. If there are less than (P2+1)
** values in the record, extract a NULL.
**
** The value extracted is stored in register P3.
@@ -91007,12 +93024,14 @@ case OP_Offset: { /* out3 */
** if the P4 argument is a P4_MEM use the value of the P4 argument as
** the result.
**
-** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then
-** the result is guaranteed to only be used as the argument of a length()
-** or typeof() function, respectively. The loading of large blobs can be
-** skipped for length() and all content loading can be skipped for typeof().
+** If the OPFLAG_LENGTHARG bit is set in P5 then the result is guaranteed
+** to only be used by the length() function or the equivalent. The content
+** of large blobs is not loaded, thus saving CPU cycles. If the
+** OPFLAG_TYPEOFARG bit is set then the result will only be used by the
+** typeof() function or the IS NULL or IS NOT NULL operators or the
+** equivalent. In this case, all content loading can be omitted.
*/
-case OP_Column: {
+case OP_Column: { /* ncycle */
u32 p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */
@@ -91361,7 +93380,7 @@ case OP_TypeCheck: {
}
case COLTYPE_REAL: {
testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_Real );
- testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_IntReal );
+ assert( (pIn1->flags & MEM_IntReal)==0 );
if( pIn1->flags & MEM_Int ){
/* When applying REAL affinity, if the result is still an MEM_Int
** that will fit in 6 bytes, then change the type to MEM_IntReal
@@ -92364,7 +94383,7 @@ case OP_SetCookie: {
**
** See also: OP_OpenRead, OP_ReopenIdx
*/
-case OP_ReopenIdx: {
+case OP_ReopenIdx: { /* ncycle */
int nField;
KeyInfo *pKeyInfo;
u32 p2;
@@ -92385,7 +94404,7 @@ case OP_ReopenIdx: {
}
/* If the cursor is not currently open or is open on a different
** index, then fall through into OP_OpenRead to force a reopen */
-case OP_OpenRead:
+case OP_OpenRead: /* ncycle */
case OP_OpenWrite:
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ );
@@ -92479,7 +94498,7 @@ open_cursor_set_hints:
**
** Duplicate ephemeral cursors are used for self-joins of materialized views.
*/
-case OP_OpenDup: {
+case OP_OpenDup: { /* ncycle */
VdbeCursor *pOrig; /* The original cursor to be duplicated */
VdbeCursor *pCx; /* The new cursor */
@@ -92541,8 +94560,8 @@ case OP_OpenDup: {
** by this opcode will be used for automatically created transient
** indices in joins.
*/
-case OP_OpenAutoindex:
-case OP_OpenEphemeral: {
+case OP_OpenAutoindex: /* ncycle */
+case OP_OpenEphemeral: { /* ncycle */
VdbeCursor *pCx;
KeyInfo *pKeyInfo;
@@ -92700,7 +94719,7 @@ case OP_OpenPseudo: {
** Close a cursor previously opened as P1. If P1 is not
** currently open, this instruction is a no-op.
*/
-case OP_Close: {
+case OP_Close: { /* ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]);
p->apCsr[pOp->p1] = 0;
@@ -92817,10 +94836,10 @@ case OP_ColumnsUsed: {
**
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
-case OP_SeekLT: /* jump, in3, group */
-case OP_SeekLE: /* jump, in3, group */
-case OP_SeekGE: /* jump, in3, group */
-case OP_SeekGT: { /* jump, in3, group */
+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 */
int res; /* Comparison result */
int oc; /* Opcode */
VdbeCursor *pC; /* The cursor to seek */
@@ -92949,7 +94968,13 @@ case OP_SeekGT: { /* jump, in3, group */
r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+ {
+ int i;
+ for(i=0; i<r.nField; i++){
+ assert( memIsValid(&r.aMem[i]) );
+ if( i>0 ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]);
+ }
+ }
#endif
r.eqSeen = 0;
rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res);
@@ -93012,7 +95037,7 @@ seek_not_found:
}
-/* Opcode: SeekScan P1 P2 * * *
+/* Opcode: SeekScan P1 P2 * * P5
** Synopsis: Scan-ahead up to P1 rows
**
** This opcode is a prefix opcode to OP_SeekGE. In other words, this
@@ -93022,8 +95047,8 @@ seek_not_found:
** This opcode uses the P1 through P4 operands of the subsequent
** OP_SeekGE. In the text that follows, the operands of the subsequent
** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only
-** the P1 and P2 operands of this opcode are also used, and are called
-** This.P1 and This.P2.
+** the P1, P2 and P5 operands of this opcode are also used, and are called
+** This.P1, This.P2 and This.P5.
**
** This opcode helps to optimize IN operators on a multi-column index
** where the IN operator is on the later terms of the index by avoiding
@@ -93033,32 +95058,54 @@ seek_not_found:
**
** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which
** is the desired entry that we want the cursor SeekGE.P1 to be pointing
-** to. Call this SeekGE.P4/P5 row the "target".
+** to. Call this SeekGE.P3/P4 row the "target".
**
** If the SeekGE.P1 cursor is not currently pointing to a valid row,
** then this opcode is a no-op and control passes through into the OP_SeekGE.
**
** If the SeekGE.P1 cursor is pointing to a valid row, then that row
** might be the target row, or it might be near and slightly before the
-** target row. This opcode attempts to position the cursor on the target
-** row by, perhaps by invoking sqlite3BtreeStep() on the cursor
-** between 0 and This.P1 times.
-**
-** There are three possible outcomes from this opcode:<ol>
-**
-** <li> If after This.P1 steps, the cursor is still pointing to a place that
-** is earlier in the btree than the target row, then fall through
-** into the subsquence OP_SeekGE opcode.
-**
-** <li> If the cursor is successfully moved to the target row by 0 or more
-** sqlite3BtreeNext() calls, then jump to This.P2, which will land just
-** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE.
-**
-** <li> If the cursor ends up past the target row (indicating the the target
-** row does not exist in the btree) then jump to SeekOP.P2.
+** target row, or it might be after the target row. If the cursor is
+** currently before the target row, then this opcode attempts to position
+** the cursor on or after the target row by invoking sqlite3BtreeStep()
+** on the cursor between 1 and This.P1 times.
+**
+** The This.P5 parameter is a flag that indicates what to do if the
+** cursor ends up pointing at a valid row that is past the target
+** row. If This.P5 is false (0) then a jump is made to SeekGE.P2. If
+** This.P5 is true (non-zero) then a jump is made to This.P2. The P5==0
+** case occurs when there are no inequality constraints to the right of
+** the IN constraing. The jump to SeekGE.P2 ends the loop. The P5!=0 case
+** occurs when there are inequality constraints to the right of the IN
+** operator. In that case, the This.P2 will point either directly to or
+** to setup code prior to the OP_IdxGT or OP_IdxGE opcode that checks for
+** loop terminate.
+**
+** Possible outcomes from this opcode:<ol>
+**
+** <li> If the cursor is initally not pointed to any valid row, then
+** fall through into the subsequent OP_SeekGE opcode.
+**
+** <li> If the cursor is left pointing to a row that is before the target
+** row, even after making as many as This.P1 calls to
+** sqlite3BtreeNext(), then also fall through into OP_SeekGE.
+**
+** <li> If the cursor is left pointing at the target row, either because it
+** was at the target row to begin with or because one or more
+** sqlite3BtreeNext() calls moved the cursor to the target row,
+** then jump to This.P2..,
+**
+** <li> If the cursor started out before the target row and a call to
+** to sqlite3BtreeNext() moved the cursor off the end of the index
+** (indicating that the target row definitely does not exist in the
+** btree) then jump to SeekGE.P2, ending the loop.
+**
+** <li> If the cursor ends up on a valid row that is past the target row
+** (indicating that the target row does not exist in the btree) then
+** jump to SeekOP.P2 if This.P5==0 or to This.P2 if This.P5>0.
** </ol>
*/
-case OP_SeekScan: {
+case OP_SeekScan: { /* ncycle */
VdbeCursor *pC;
int res;
int nStep;
@@ -93066,14 +95113,25 @@ case OP_SeekScan: {
assert( pOp[1].opcode==OP_SeekGE );
- /* pOp->p2 points to the first instruction past the OP_IdxGT that
- ** follows the OP_SeekGE. */
+ /* If pOp->p5 is clear, then pOp->p2 points to the first instruction past the
+ ** OP_IdxGT that follows the OP_SeekGE. Otherwise, it points to the first
+ ** opcode past the OP_SeekGE itself. */
assert( pOp->p2>=(int)(pOp-aOp)+2 );
- assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE );
- testcase( aOp[pOp->p2-1].opcode==OP_IdxGE );
- assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
- assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
- assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
+#ifdef SQLITE_DEBUG
+ if( pOp->p5==0 ){
+ /* There are no inequality constraints following the IN constraint. */
+ assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
+ assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
+ assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
+ assert( aOp[pOp->p2-1].opcode==OP_IdxGT
+ || aOp[pOp->p2-1].opcode==OP_IdxGE );
+ testcase( aOp[pOp->p2-1].opcode==OP_IdxGE );
+ }else{
+ /* There are inequality constraints. */
+ assert( pOp->p2==(int)(pOp-aOp)+2 );
+ assert( aOp[pOp->p2-1].opcode==OP_SeekGE );
+ }
+#endif
assert( pOp->p1>0 );
pC = p->apCsr[pOp[1].p1];
@@ -93107,8 +95165,9 @@ case OP_SeekScan: {
while(1){
rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
if( rc ) goto abort_due_to_error;
- if( res>0 ){
+ if( res>0 && pOp->p5==0 ){
seekscan_search_fail:
+ /* Jump to SeekGE.P2, ending the loop */
#ifdef SQLITE_DEBUG
if( db->flags&SQLITE_VdbeTrace ){
printf("... %d steps and then skip\n", pOp->p1 - nStep);
@@ -93118,7 +95177,8 @@ case OP_SeekScan: {
pOp++;
goto jump_to_p2;
}
- if( res==0 ){
+ if( res>=0 ){
+ /* Jump to This.P2, bypassing the OP_SeekGE opcode */
#ifdef SQLITE_DEBUG
if( db->flags&SQLITE_VdbeTrace ){
printf("... %d steps and then success\n", pOp->p1 - nStep);
@@ -93167,7 +95227,7 @@ case OP_SeekScan: {
**
** P1 must be a valid b-tree cursor.
*/
-case OP_SeekHit: {
+case OP_SeekHit: { /* ncycle */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
@@ -93194,12 +95254,16 @@ case OP_SeekHit: {
/* Opcode: IfNotOpen P1 P2 * * *
** Synopsis: if( !csr[P1] ) goto P2
**
-** If cursor P1 is not open, jump to instruction P2. Otherwise, fall through.
+** If cursor P1 is not open or if P1 is set to a NULL row using the
+** OP_NullRow opcode, then jump to instruction P2. Otherwise, fall through.
*/
case OP_IfNotOpen: { /* jump */
+ VdbeCursor *pCur;
+
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- VdbeBranchTaken(p->apCsr[pOp->p1]==0, 2);
- if( !p->apCsr[pOp->p1] ){
+ pCur = p->apCsr[pOp->p1];
+ VdbeBranchTaken(pCur==0 || pCur->nullRow, 2);
+ if( pCur==0 || pCur->nullRow ){
goto jump_to_p2_and_check_for_interrupt;
}
break;
@@ -93295,7 +95359,7 @@ case OP_IfNotOpen: { /* jump */
**
** See also: NotFound, Found, NotExists
*/
-case OP_IfNoHope: { /* jump, in3 */
+case OP_IfNoHope: { /* jump, in3, ncycle */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
@@ -93309,9 +95373,9 @@ case OP_IfNoHope: { /* jump, in3 */
/* Fall through into OP_NotFound */
/* no break */ deliberate_fall_through
}
-case OP_NoConflict: /* jump, in3 */
-case OP_NotFound: /* jump, in3 */
-case OP_Found: { /* jump, in3 */
+case OP_NoConflict: /* jump, in3, ncycle */
+case OP_NotFound: /* jump, in3, ncycle */
+case OP_Found: { /* jump, in3, ncycle */
int alreadyExists;
int ii;
VdbeCursor *pC;
@@ -93441,7 +95505,7 @@ case OP_Found: { /* jump, in3 */
**
** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_SeekRowid: { /* jump, in3 */
+case OP_SeekRowid: { /* jump, in3, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -93466,7 +95530,7 @@ case OP_SeekRowid: { /* jump, in3 */
}
/* Fall through into OP_NotExists */
/* no break */ deliberate_fall_through
-case OP_NotExists: /* jump, in3 */
+case OP_NotExists: /* jump, in3, ncycle */
pIn3 = &aMem[pOp->p3];
assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
@@ -93746,8 +95810,11 @@ case OP_Insert: {
if( pOp->p5 & OPFLAG_ISNOOP ) break;
#endif
- if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
- if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
+ assert( (pOp->p5 & OPFLAG_LASTROWID)==0 || (pOp->p5 & OPFLAG_NCHANGE)!=0 );
+ if( pOp->p5 & OPFLAG_NCHANGE ){
+ p->nChange++;
+ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
+ }
assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 );
x.pData = pData->z;
x.nData = pData->n;
@@ -93758,6 +95825,7 @@ case OP_Insert: {
x.nZero = 0;
}
x.pKey = 0;
+ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
seekResult
@@ -94089,7 +96157,7 @@ case OP_RowData: {
** be a separate OP_VRowid opcode for use with virtual tables, but this
** one opcode now works for both table types.
*/
-case OP_Rowid: { /* out2 */
+case OP_Rowid: { /* out2, ncycle */
VdbeCursor *pC;
i64 v;
sqlite3_vtab *pVtab;
@@ -94188,8 +96256,8 @@ case OP_NullRow: {
** from the end toward the beginning. In other words, the cursor is
** configured to use Prev, not Next.
*/
-case OP_SeekEnd:
-case OP_Last: { /* jump */
+case OP_SeekEnd: /* ncycle */
+case OP_Last: { /* jump, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -94290,17 +96358,22 @@ case OP_Sort: { /* jump */
** If the table or index is not empty, fall through to the following
** instruction.
**
+** If P2 is zero, that is an assertion that the P1 table is never
+** empty and hence the jump will never be taken.
+**
** This opcode leaves the cursor configured to move in forward order,
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
*/
-case OP_Rewind: { /* jump */
+case OP_Rewind: { /* jump, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p5==0 );
+ assert( pOp->p2>=0 && pOp->p2<p->nOp );
+
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
@@ -94320,9 +96393,10 @@ case OP_Rewind: { /* jump */
}
if( rc ) goto abort_due_to_error;
pC->nullRow = (u8)res;
- assert( pOp->p2>0 && pOp->p2<p->nOp );
- VdbeBranchTaken(res!=0,2);
- if( res ) goto jump_to_p2;
+ if( pOp->p2>0 ){
+ VdbeBranchTaken(res!=0,2);
+ if( res ) goto jump_to_p2;
+ }
break;
}
@@ -94388,9 +96462,11 @@ case OP_SorterNext: { /* jump */
rc = sqlite3VdbeSorterNext(db, pC);
goto next_tail;
-case OP_Prev: /* jump */
+case OP_Prev: /* jump, ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( pOp->p5<ArraySize(p->aCounter) );
+ assert( pOp->p5==0
+ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP
+ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX);
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->deferredMoveto==0 );
@@ -94401,9 +96477,11 @@ case OP_Prev: /* jump */
rc = sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3);
goto next_tail;
-case OP_Next: /* jump */
+case OP_Next: /* jump, ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( pOp->p5<ArraySize(p->aCounter) );
+ assert( pOp->p5==0
+ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP
+ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX);
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->deferredMoveto==0 );
@@ -94591,8 +96669,8 @@ case OP_IdxDelete: {
**
** See also: Rowid, MakeRecord.
*/
-case OP_DeferredSeek:
-case OP_IdxRowid: { /* out2 */
+case OP_DeferredSeek: /* ncycle */
+case OP_IdxRowid: { /* out2, ncycle */
VdbeCursor *pC; /* The P1 index cursor */
VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */
i64 rowid; /* Rowid that P1 current points to */
@@ -94610,10 +96688,10 @@ case OP_IdxRowid: { /* out2 */
** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */
rc = sqlite3VdbeCursorRestore(pC);
- /* sqlite3VbeCursorRestore() can only fail if the record has been deleted
- ** out from under the cursor. That will never happens for an IdxRowid
- ** or Seek opcode */
- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+ /* sqlite3VdbeCursorRestore() may fail if the cursor has been disturbed
+ ** since it was last positioned and an error (e.g. OOM or an IO error)
+ ** occurs while trying to reposition it. */
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
if( !pC->nullRow ){
rowid = 0; /* Not needed. Only used to silence a warning. */
@@ -94654,8 +96732,8 @@ case OP_IdxRowid: { /* out2 */
** seek operation now, without further delay. If the cursor seek has
** already occurred, this instruction is a no-op.
*/
-case OP_FinishSeek: {
- VdbeCursor *pC; /* The P1 index cursor */
+case OP_FinishSeek: { /* ncycle */
+ VdbeCursor *pC; /* The P1 index cursor */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
@@ -94710,10 +96788,10 @@ case OP_FinishSeek: {
** If the P1 index entry is less than or equal to the key value then jump
** to P2. Otherwise fall through to the next instruction.
*/
-case OP_IdxLE: /* jump */
-case OP_IdxGT: /* jump */
-case OP_IdxLT: /* jump */
-case OP_IdxGE: { /* jump */
+case OP_IdxLE: /* jump, ncycle */
+case OP_IdxGT: /* jump, ncycle */
+case OP_IdxLT: /* jump, ncycle */
+case OP_IdxGE: { /* jump, ncycle */
VdbeCursor *pC;
int res;
UnpackedRecord r;
@@ -95124,13 +97202,14 @@ case OP_IntegrityCk: {
pIn1 = &aMem[pOp->p1];
assert( pOp->p5<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p5) );
- z = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
- (int)pnErr->u.i+1, &nErr);
+ rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
+ (int)pnErr->u.i+1, &nErr, &z);
sqlite3VdbeMemSetNull(pIn1);
if( nErr==0 ){
assert( z==0 );
- }else if( z==0 ){
- goto no_mem;
+ }else if( rc ){
+ sqlite3_free(z);
+ goto abort_due_to_error;
}else{
pnErr->u.i -= nErr-1;
sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free);
@@ -95334,9 +97413,6 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- pFrame->anExec = p->anExec;
-#endif
#ifdef SQLITE_DEBUG
pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
#endif
@@ -95373,9 +97449,6 @@ case OP_Program: { /* jump */
memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8);
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = 0;
-#endif
#ifdef SQLITE_DEBUG
/* Verify that second and subsequent executions of the same trigger do not
** try to reuse register values from the first use. */
@@ -95515,7 +97588,7 @@ case OP_IfPos: { /* jump, in1 */
** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)
**
** This opcode performs a commonly used computation associated with
-** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3]
+** LIMIT and OFFSET processing. r[P1] holds the limit counter. r[P3]
** holds the offset counter. The opcode computes the combined value
** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2]
** value computed is the total number of rows that will need to be
@@ -96132,7 +98205,7 @@ case OP_VDestroy: {
** P1 is a cursor number. This opcode opens a cursor to the virtual
** table and stores that cursor in P1.
*/
-case OP_VOpen: {
+case OP_VOpen: { /* ncycle */
VdbeCursor *pCur;
sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
@@ -96179,7 +98252,7 @@ case OP_VOpen: {
** cursor. Register P3 is used to hold the values returned by
** sqlite3_vtab_in_first() and sqlite3_vtab_in_next().
*/
-case OP_VInitIn: { /* out2 */
+case OP_VInitIn: { /* out2, ncycle */
VdbeCursor *pC; /* The cursor containing the RHS values */
ValueList *pRhs; /* New ValueList object to put in reg[P2] */
@@ -96190,7 +98263,7 @@ case OP_VInitIn: { /* out2 */
pRhs->pOut = &aMem[pOp->p3];
pOut = out2Prerelease(p, pOp);
pOut->flags = MEM_Null;
- sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3_free);
+ sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3VdbeValueListFree);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -96216,7 +98289,7 @@ case OP_VInitIn: { /* out2 */
**
** A jump is made to P2 if the result set after filtering would be empty.
*/
-case OP_VFilter: { /* jump */
+case OP_VFilter: { /* jump, ncycle */
int nArg;
int iQuery;
const sqlite3_module *pModule;
@@ -96276,7 +98349,7 @@ case OP_VFilter: { /* jump */
** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are
** unused by OP_VColumn.
*/
-case OP_VColumn: {
+case OP_VColumn: { /* ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
@@ -96328,7 +98401,7 @@ case OP_VColumn: {
** jump to instruction P2. Or, if the virtual table has reached
** the end of its result set, then fall through to the next instruction.
*/
-case OP_VNext: { /* jump */
+case OP_VNext: { /* jump, ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
@@ -96911,12 +98984,12 @@ default: { /* This is really OP_Noop, OP_Explain */
*****************************************************************************/
}
-#ifdef VDBE_PROFILE
- {
- u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
- if( endTime>start ) pOrigOp->cycles += endTime - start;
- pOrigOp->cnt++;
- }
+#if defined(VDBE_PROFILE)
+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
+ pnCycle = 0;
+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ *pnCycle += sqlite3Hwtime();
+ pnCycle = 0;
#endif
/* The following code adds nothing to the actual functionality
@@ -96992,6 +99065,18 @@ abort_due_to_error:
** release the mutexes on btrees that were acquired at the
** top. */
vdbe_return:
+#if defined(VDBE_PROFILE)
+ if( pnCycle ){
+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
+ pnCycle = 0;
+ }
+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ if( pnCycle ){
+ *pnCycle += sqlite3Hwtime();
+ pnCycle = 0;
+ }
+#endif
+
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
while( nVmStep>=nProgressLimit && db->xProgress!=0 ){
nProgressLimit += db->nProgressOps;
@@ -97003,7 +99088,9 @@ vdbe_return:
}
#endif
p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
- sqlite3VdbeLeave(p);
+ if( DbMaskNonZero(p->lockMask) ){
+ sqlite3VdbeLeave(p);
+ }
assert( rc!=SQLITE_OK || nExtraDelete==0
|| sqlite3_strlike("DELETE%",p->zSql,0)!=0
);
@@ -100410,6 +102497,9 @@ static int bytecodevtabConnect(
");"
};
+ (void)argc;
+ (void)argv;
+ (void)pzErr;
rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc( sizeof(*pNew) );
@@ -100645,6 +102735,7 @@ static int bytecodevtabFilter(
bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor;
bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab;
int rc = SQLITE_OK;
+ (void)idxStr;
bytecodevtabCursorClear(pCur);
pCur->iRowid = 0;
@@ -101113,6 +103204,8 @@ SQLITE_PRIVATE int sqlite3JournalOpen(
){
MemJournal *p = (MemJournal*)pJfd;
+ assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) );
+
/* Zero the file-handle object. If nSpill was passed zero, initialize
** it using the sqlite3OsOpen() function of the underlying VFS. In this
** case none of the code in this module is executed as a result of calls
@@ -101554,9 +103647,7 @@ static void resolveAlias(
pExpr->y.pWin->pOwner = pExpr;
}
}
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprDelete,
- pDup);
+ sqlite3ExprDeferredDelete(pParse, pDup);
}
}
@@ -101660,6 +103751,32 @@ static void extendFJMatch(
}
/*
+** Return TRUE (non-zero) if zTab is a valid name for the schema table pTab.
+*/
+static SQLITE_NOINLINE int isValidSchemaTableName(
+ const char *zTab, /* Name as it appears in the SQL */
+ Table *pTab, /* The schema table we are trying to match */
+ Schema *pSchema /* non-NULL if a database qualifier is present */
+){
+ const char *zLegacy;
+ assert( pTab!=0 );
+ assert( pTab->tnum==1 );
+ if( sqlite3StrNICmp(zTab, "sqlite_", 7)!=0 ) return 0;
+ zLegacy = pTab->zName;
+ if( strcmp(zLegacy+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){
+ if( sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){
+ return 1;
+ }
+ if( pSchema==0 ) return 0;
+ if( sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1;
+ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
+ }else{
+ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
+ }
+ return 0;
+}
+
+/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
** that name in the set of source tables in pSrcList and make the pExpr
** expression node refer back to that source column. The following changes
@@ -101812,15 +103929,17 @@ static int lookupName(
}
assert( zDb==0 || zTab!=0 );
if( zTab ){
- const char *zTabName;
if( zDb ){
if( pTab->pSchema!=pSchema ) continue;
if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue;
}
- zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
- assert( zTabName!=0 );
- if( sqlite3StrICmp(zTabName, zTab)!=0 ){
- continue;
+ if( pItem->zAlias!=0 ){
+ if( sqlite3StrICmp(zTab, pItem->zAlias)!=0 ){
+ continue;
+ }
+ }else if( sqlite3StrICmp(zTab, pTab->zName)!=0 ){
+ if( pTab->tnum!=1 ) continue;
+ if( !isValidSchemaTableName(zTab, pTab, pSchema) ) continue;
}
assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT && pItem->zAlias ){
@@ -101963,6 +104082,7 @@ static int lookupName(
if( pParse->bReturning ){
eNewExprOp = TK_REGISTER;
pExpr->op2 = TK_COLUMN;
+ pExpr->iColumn = iCol;
pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable +
sqlite3TableColumnToStorage(pTab, iCol) + 1;
}else{
@@ -103639,50 +105759,122 @@ SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table *pTab, int iCol){
*/
SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
int op;
- while( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
- assert( pExpr->op==TK_COLLATE
- || pExpr->op==TK_IF_NULL_ROW
- || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
- pExpr = pExpr->pLeft;
- assert( pExpr!=0 );
- }
op = pExpr->op;
- if( op==TK_REGISTER ) op = pExpr->op2;
- if( op==TK_COLUMN || op==TK_AGG_COLUMN ){
- assert( ExprUseYTab(pExpr) );
- if( pExpr->y.pTab ){
+ while( 1 /* exit-by-break */ ){
+ if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){
+ assert( ExprUseYTab(pExpr) );
+ assert( pExpr->y.pTab!=0 );
return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
}
- }
- if( op==TK_SELECT ){
- assert( ExprUseXSelect(pExpr) );
- assert( pExpr->x.pSelect!=0 );
- assert( pExpr->x.pSelect->pEList!=0 );
- assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
- return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
- }
+ if( op==TK_SELECT ){
+ assert( ExprUseXSelect(pExpr) );
+ assert( pExpr->x.pSelect!=0 );
+ assert( pExpr->x.pSelect->pEList!=0 );
+ assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
+ return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
+ }
#ifndef SQLITE_OMIT_CAST
- if( op==TK_CAST ){
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- return sqlite3AffinityType(pExpr->u.zToken, 0);
- }
+ if( op==TK_CAST ){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ return sqlite3AffinityType(pExpr->u.zToken, 0);
+ }
#endif
- if( op==TK_SELECT_COLUMN ){
- assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) );
- assert( pExpr->iColumn < pExpr->iTable );
- assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
- return sqlite3ExprAffinity(
- pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
- );
- }
- if( op==TK_VECTOR ){
- assert( ExprUseXList(pExpr) );
- return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
+ if( op==TK_SELECT_COLUMN ){
+ assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) );
+ assert( pExpr->iColumn < pExpr->iTable );
+ assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
+ return sqlite3ExprAffinity(
+ pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
+ );
+ }
+ if( op==TK_VECTOR ){
+ assert( ExprUseXList(pExpr) );
+ return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
+ }
+ if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
+ assert( pExpr->op==TK_COLLATE
+ || pExpr->op==TK_IF_NULL_ROW
+ || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
+ pExpr = pExpr->pLeft;
+ op = pExpr->op;
+ continue;
+ }
+ if( op!=TK_REGISTER || (op = pExpr->op2)==TK_REGISTER ) break;
}
return pExpr->affExpr;
}
/*
+** Make a guess at all the possible datatypes of the result that could
+** be returned by an expression. Return a bitmask indicating the answer:
+**
+** 0x01 Numeric
+** 0x02 Text
+** 0x04 Blob
+**
+** If the expression must return NULL, then 0x00 is returned.
+*/
+SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr){
+ while( pExpr ){
+ switch( pExpr->op ){
+ case TK_COLLATE:
+ case TK_IF_NULL_ROW:
+ case TK_UPLUS: {
+ pExpr = pExpr->pLeft;
+ break;
+ }
+ case TK_NULL: {
+ pExpr = 0;
+ break;
+ }
+ case TK_STRING: {
+ return 0x02;
+ }
+ case TK_BLOB: {
+ return 0x04;
+ }
+ case TK_CONCAT: {
+ return 0x06;
+ }
+ case TK_VARIABLE:
+ case TK_AGG_FUNCTION:
+ case TK_FUNCTION: {
+ return 0x07;
+ }
+ case TK_COLUMN:
+ case TK_AGG_COLUMN:
+ case TK_SELECT:
+ case TK_CAST:
+ case TK_SELECT_COLUMN:
+ case TK_VECTOR: {
+ int aff = sqlite3ExprAffinity(pExpr);
+ if( aff>=SQLITE_AFF_NUMERIC ) return 0x05;
+ if( aff==SQLITE_AFF_TEXT ) return 0x06;
+ return 0x07;
+ }
+ case TK_CASE: {
+ int res = 0;
+ int ii;
+ ExprList *pList = pExpr->x.pList;
+ assert( ExprUseXList(pExpr) && pList!=0 );
+ assert( pList->nExpr > 0);
+ for(ii=1; ii<pList->nExpr; ii+=2){
+ res |= sqlite3ExprDataType(pList->a[ii].pExpr);
+ }
+ if( pList->nExpr % 2 ){
+ res |= sqlite3ExprDataType(pList->a[pList->nExpr-1].pExpr);
+ }
+ return res;
+ }
+ default: {
+ return 0x01;
+ }
+ } /* End of switch(op) */
+ } /* End of while(pExpr) */
+ return 0x00;
+}
+
+/*
** Set the collating sequence for expression pExpr to be the collating
** sequence named by pToken. Return a pointer to a new Expr node that
** implements the COLLATE operator.
@@ -103769,18 +105961,17 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
while( p ){
int op = p->op;
if( op==TK_REGISTER ) op = p->op2;
- if( op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER ){
+ if( (op==TK_AGG_COLUMN && p->y.pTab!=0)
+ || op==TK_COLUMN || op==TK_TRIGGER
+ ){
+ int j;
assert( ExprUseYTab(p) );
- if( p->y.pTab!=0 ){
- /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally
- ** a TK_COLUMN but was previously evaluated and cached in a register */
- int j = p->iColumn;
- if( j>=0 ){
- const char *zColl = sqlite3ColumnColl(&p->y.pTab->aCol[j]);
- pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
- }
- break;
+ assert( p->y.pTab!=0 );
+ if( (j = p->iColumn)>=0 ){
+ const char *zColl = sqlite3ColumnColl(&p->y.pTab->aCol[j]);
+ pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
}
+ break;
}
if( op==TK_CAST || op==TK_UPLUS ){
p = p->pLeft;
@@ -104365,7 +106556,9 @@ static void heightOfSelect(const Select *pSelect, int *pnHeight){
*/
static void exprSetHeight(Expr *p){
int nHeight = p->pLeft ? p->pLeft->nHeight : 0;
- if( p->pRight && p->pRight->nHeight>nHeight ) nHeight = p->pRight->nHeight;
+ if( NEVER(p->pRight) && p->pRight->nHeight>nHeight ){
+ nHeight = p->pRight->nHeight;
+ }
if( ExprUseXSelect(p) ){
heightOfSelect(p->x.pSelect, &nHeight);
}else if( p->x.pList ){
@@ -104508,15 +106701,26 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(
sqlite3ExprDelete(db, pLeft);
sqlite3ExprDelete(db, pRight);
}else{
+ assert( ExprUseXList(pRoot) );
+ assert( pRoot->x.pSelect==0 );
if( pRight ){
pRoot->pRight = pRight;
pRoot->flags |= EP_Propagate & pRight->flags;
+#if SQLITE_MAX_EXPR_DEPTH>0
+ pRoot->nHeight = pRight->nHeight+1;
+ }else{
+ pRoot->nHeight = 1;
+#endif
}
if( pLeft ){
pRoot->pLeft = pLeft;
pRoot->flags |= EP_Propagate & pLeft->flags;
+#if SQLITE_MAX_EXPR_DEPTH>0
+ if( pLeft->nHeight>=pRoot->nHeight ){
+ pRoot->nHeight = pLeft->nHeight+1;
+ }
+#endif
}
- exprSetHeight(pRoot);
}
}
@@ -104802,6 +107006,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 );
assert( !ExprUseUValue(p) || p->u.iValue>=0 );
assert( !ExprUseYWin(p) || !ExprUseYSub(p) );
assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed );
@@ -104833,12 +107038,8 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
#endif
}
}
- if( ExprHasProperty(p, EP_MemToken) ){
- assert( !ExprHasProperty(p, EP_IntValue) );
- sqlite3DbFree(db, p->u.zToken);
- }
if( !ExprHasProperty(p, EP_Static) ){
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
}
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
@@ -104869,8 +107070,9 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
- pParse->pConstExpr =
- sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr);
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3ExprDelete,
+ pExpr);
}
/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
@@ -104944,7 +107146,6 @@ static int dupedExprStructSize(const Expr *p, int flags){
}else{
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
assert( !ExprHasProperty(p, EP_OuterON) );
- assert( !ExprHasProperty(p, EP_MemToken) );
assert( !ExprHasVVAProperty(p, EP_NoReduce) );
if( p->pLeft || p->x.pList ){
nSize = EXPR_REDUCEDSIZE | EP_Reduced;
@@ -105048,7 +107249,7 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
- pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static);
pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
pNew->flags |= staticFlag;
ExprClearVVAProperties(pNew);
@@ -105624,12 +107825,13 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
int i = pList->nExpr;
struct ExprList_item *pItem = pList->a;
assert( pList->nExpr>0 );
+ assert( db!=0 );
do{
sqlite3ExprDelete(db, pItem->pExpr);
- sqlite3DbFree(db, pItem->zEName);
+ if( pItem->zEName ) sqlite3DbNNFreeNN(db, pItem->zEName);
pItem++;
}while( --i>0 );
- sqlite3DbFreeNN(db, pList);
+ sqlite3DbNNFreeNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
@@ -106807,6 +109009,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
}
if( addrOnce ){
+ sqlite3VdbeAddOp1(v, OP_NullRow, iTab);
sqlite3VdbeJumpHere(v, addrOnce);
/* Subroutine return */
assert( ExprUseYSub(pExpr) );
@@ -106842,6 +109045,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
SelectDest dest; /* How to deal with SELECT result */
int nReg; /* Registers to allocate */
Expr *pLimit; /* New limit expression */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExplain; /* Address of OP_Explain instruction */
+#endif
Vdbe *v = pParse->pVdbe;
assert( v!=0 );
@@ -106894,8 +109100,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** In both cases, the query is augmented with "LIMIT 1". Any
** preexisting limit is discarded in place of the new LIMIT 1.
*/
- ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d",
+ ExplainQueryPlan2(addrExplain, (pParse, 1, "%sSCALAR SUBQUERY %d",
addrOnce?"":"CORRELATED ", pSel->selId));
+ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, -1);
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
pParse->nMem += nReg;
@@ -106920,7 +109127,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
pLimit = sqlite3PExpr(pParse, TK_NE,
sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit);
}
- sqlite3ExprDelete(db, pSel->pLimit->pLeft);
+ sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft);
pSel->pLimit->pLeft = pLimit;
}else{
/* If there is no pre-existing limit add a limit of 1 */
@@ -106938,6 +109145,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
if( addrOnce ){
sqlite3VdbeJumpHere(v, addrOnce);
}
+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
/* Subroutine return */
assert( ExprUseYSub(pExpr) );
@@ -107373,10 +109581,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
){
Column *pCol;
assert( v!=0 );
- if( pTab==0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut);
- return;
- }
+ assert( pTab!=0 );
if( iCol<0 || iCol==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
VdbeComment((v, "%s.rowid", pTab->zName));
@@ -107434,7 +109639,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
assert( pParse->pVdbe!=0 );
sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg);
if( p5 ){
- VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1);
+ VdbeOp *pOp = sqlite3VdbeGetLastOp(pParse->pVdbe);
if( pOp->opcode==OP_Column ) pOp->p5 = p5;
}
return iReg;
@@ -107503,7 +109708,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
** so that a subsequent copy will not be merged into this one.
*/
static void setDoNotMergeFlagOnCopy(Vdbe *v){
- if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){
+ if( sqlite3VdbeGetLastOp(v)->opcode==OP_Copy ){
sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */
}
}
@@ -107613,10 +109818,13 @@ static int exprCodeInlineFunction(
** the type affinity of the argument. This is used for testing of
** the SQLite type logic.
*/
- const char *azAff[] = { "blob", "text", "numeric", "integer", "real" };
+ const char *azAff[] = { "blob", "text", "numeric", "integer",
+ "real", "flexnum" };
char aff;
assert( nFarg==1 );
aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
+ assert( aff<=SQLITE_AFF_NONE
+ || (aff>=SQLITE_AFF_BLOB && aff<=SQLITE_AFF_FLEXNUM) );
sqlite3VdbeLoadString(v, target,
(aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
break;
@@ -107626,6 +109834,53 @@ static int exprCodeInlineFunction(
return target;
}
+/*
+** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
+** If it is, then resolve the expression by reading from the index and
+** return the register into which the value has been read. If pExpr is
+** not an indexed expression, then return negative.
+*/
+static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
+ Parse *pParse, /* The parsing context */
+ Expr *pExpr, /* The expression to potentially bypass */
+ int target /* Where to store the result of the expression */
+){
+ IndexedExpr *p;
+ Vdbe *v;
+ for(p=pParse->pIdxEpr; p; p=p->pIENext){
+ int iDataCur = p->iDataCur;
+ if( iDataCur<0 ) continue;
+ if( pParse->iSelfTab ){
+ if( p->iDataCur!=pParse->iSelfTab-1 ) continue;
+ iDataCur = -1;
+ }
+ if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue;
+ v = pParse->pVdbe;
+ assert( v!=0 );
+ if( p->bMaybeNullRow ){
+ /* If the index is on a NULL row due to an outer join, then we
+ ** cannot extract the value from the index. The value must be
+ ** computed using the original expression. */
+ int addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_IfNullRow, p->iIdxCur, addr+3, target);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
+ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol));
+ sqlite3VdbeGoto(v, 0);
+ p = pParse->pIdxEpr;
+ pParse->pIdxEpr = 0;
+ sqlite3ExprCode(pParse, pExpr, target);
+ pParse->pIdxEpr = p;
+ sqlite3VdbeJumpHere(v, addr+2);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
+ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol));
+ }
+ return target;
+ }
+ return -1; /* Not found */
+}
+
/*
** Generate code into the current Vdbe to evaluate the given
@@ -107654,6 +109909,11 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
expr_code_doover:
if( pExpr==0 ){
op = TK_NULL;
+ }else if( pParse->pIdxEpr!=0
+ && !ExprHasProperty(pExpr, EP_Leaf)
+ && (r1 = sqlite3IndexedExprLookup(pParse, pExpr, target))>=0
+ ){
+ return r1;
}else{
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
op = pExpr->op;
@@ -107666,13 +109926,14 @@ expr_code_doover:
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
- assert( pCol->iMem>0 );
- return pCol->iMem;
+ return AggInfoColumnReg(pAggInfo, pExpr->iAgg);
}else if( pAggInfo->useSortingIdx ){
Table *pTab = pCol->pTab;
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
- if( pCol->iColumn<0 ){
+ if( pTab==0 ){
+ /* No comment added */
+ }else if( pCol->iColumn<0 ){
VdbeComment((v,"%s.rowid",pTab->zName));
}else{
VdbeComment((v,"%s.%s",
@@ -107682,6 +109943,11 @@ expr_code_doover:
}
}
return target;
+ }else if( pExpr->y.pTab==0 ){
+ /* This case happens when the argument to an aggregate function
+ ** is rewritten by aggregateConvertIndexedExprRefToColumn() */
+ sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target);
+ return target;
}
/* Otherwise, fall thru into the TK_COLUMN case */
/* no break */ deliberate_fall_through
@@ -107699,13 +109965,10 @@ expr_code_doover:
int aff;
iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
assert( ExprUseYTab(pExpr) );
- if( pExpr->y.pTab ){
- aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
- }else{
- aff = pExpr->affExpr;
- }
+ assert( pExpr->y.pTab!=0 );
+ aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
if( aff>SQLITE_AFF_BLOB ){
- static const char zAff[] = "B\000C\000D\000E";
+ static const char zAff[] = "B\000C\000D\000E\000F";
assert( SQLITE_AFF_BLOB=='A' );
assert( SQLITE_AFF_TEXT=='B' );
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
@@ -107765,12 +110028,10 @@ expr_code_doover:
}
}
assert( ExprUseYTab(pExpr) );
+ assert( pExpr->y.pTab!=0 );
iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
pExpr->iColumn, iTab, target,
pExpr->op2);
- if( pExpr->y.pTab==0 && pExpr->affExpr==SQLITE_AFF_REAL ){
- sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
- }
return iReg;
}
case TK_INTEGER: {
@@ -107984,7 +110245,7 @@ expr_code_doover:
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr);
}else{
- return pInfo->aFunc[pExpr->iAgg].iMem;
+ return AggInfoFuncReg(pInfo, pExpr->iAgg);
}
break;
}
@@ -108269,6 +110530,21 @@ expr_code_doover:
case TK_IF_NULL_ROW: {
int addrINR;
u8 okConstFactor = pParse->okConstFactor;
+ AggInfo *pAggInfo = pExpr->pAggInfo;
+ if( pAggInfo ){
+ assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
+ if( !pAggInfo->directMode ){
+ inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg);
+ break;
+ }
+ if( pExpr->pAggInfo->useSortingIdx ){
+ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
+ pAggInfo->aCol[pExpr->iAgg].iSorterColumn,
+ target);
+ inReg = target;
+ break;
+ }
+ }
addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable);
/* Temporarily disable factoring of constant expressions, since
** even though expressions may appear to be constant, they are not
@@ -108610,7 +110886,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
if( inReg!=target+i ){
VdbeOp *pOp;
if( copyOp==OP_Copy
- && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy
+ && (pOp=sqlite3VdbeGetLastOp(v))->opcode==OP_Copy
&& pOp->p1+pOp->p3+1==inReg
&& pOp->p2+pOp->p3+1==target+i
&& pOp->p5==0 /* The do-not-merge flag must be clear */
@@ -108809,6 +111085,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
VdbeCoverageIf(v, op==TK_ISNULL);
VdbeCoverageIf(v, op==TK_NOTNULL);
@@ -108983,6 +111260,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
case TK_ISNULL:
case TK_NOTNULL: {
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
@@ -109136,7 +111414,13 @@ SQLITE_PRIVATE int sqlite3ExprCompare(
if( pB->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA,pB->pLeft,iTab)<2 ){
return 1;
}
- return 2;
+ if( pA->op==TK_AGG_COLUMN && pB->op==TK_COLUMN
+ && pB->iTable<0 && pA->iTable==iTab
+ ){
+ /* fall through */
+ }else{
+ return 2;
+ }
}
assert( !ExprHasProperty(pA, EP_IntValue) );
assert( !ExprHasProperty(pB, EP_IntValue) );
@@ -109438,10 +111722,10 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) );
assert( pRight->op!=TK_COLUMN || ExprUseYTab(pRight) );
if( (pLeft->op==TK_COLUMN
- && pLeft->y.pTab!=0
+ && ALWAYS(pLeft->y.pTab!=0)
&& IsVirtual(pLeft->y.pTab))
|| (pRight->op==TK_COLUMN
- && pRight->y.pTab!=0
+ && ALWAYS(pRight->y.pTab!=0)
&& IsVirtual(pRight->y.pTab))
){
return WRC_Prune;
@@ -109646,6 +111930,7 @@ static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){
SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){
Walker w;
struct RefSrcList x;
+ assert( pParse->db!=0 );
memset(&w, 0, sizeof(w));
memset(&x, 0, sizeof(x));
w.xExprCallback = exprRefToSrcList;
@@ -109662,7 +111947,7 @@ SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList
sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter);
}
#endif
- sqlite3DbFree(pParse->db, x.aiExclude);
+ if( x.aiExclude ) sqlite3DbNNFreeNN(pParse->db, x.aiExclude);
if( w.eCode & 0x01 ){
return 1;
}else if( w.eCode ){
@@ -109680,10 +111965,8 @@ SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList
** it does, make a copy. This is done because the pExpr argument is
** subject to change.
**
-** The copy is stored on pParse->pConstExpr with a register number of 0.
-** This will cause the expression to be deleted automatically when the
-** Parse object is destroyed, but the zero register number means that it
-** will not generate any code in the preamble.
+** The copy is scheduled for deletion using the sqlite3ExprDeferredDelete()
+** which builds on the sqlite3ParserAddCleanup() mechanism.
*/
static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced))
@@ -109693,8 +111976,7 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
int iAgg = pExpr->iAgg;
Parse *pParse = pWalker->pParse;
sqlite3 *db = pParse->db;
- assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION );
- if( pExpr->op==TK_AGG_COLUMN ){
+ if( pExpr->op!=TK_AGG_FUNCTION ){
assert( iAgg>=0 && iAgg<pAggInfo->nColumn );
if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){
pExpr = sqlite3ExprDup(db, pExpr, 0);
@@ -109704,6 +111986,7 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
}
}
}else{
+ assert( pExpr->op==TK_AGG_FUNCTION );
assert( iAgg>=0 && iAgg<pAggInfo->nFunc );
if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){
pExpr = sqlite3ExprDup(db, pExpr, 0);
@@ -109761,6 +112044,73 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
}
/*
+** Search the AggInfo object for an aCol[] entry that has iTable and iColumn.
+** Return the index in aCol[] of the entry that describes that column.
+**
+** If no prior entry is found, create a new one and return -1. The
+** new column will have an idex of pAggInfo->nColumn-1.
+*/
+static void findOrCreateAggInfoColumn(
+ Parse *pParse, /* Parsing context */
+ AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */
+ Expr *pExpr /* Expr describing the column to find or insert */
+){
+ struct AggInfo_col *pCol;
+ int k;
+
+ assert( pAggInfo->iFirstReg==0 );
+ pCol = pAggInfo->aCol;
+ for(k=0; k<pAggInfo->nColumn; k++, pCol++){
+ if( pCol->iTable==pExpr->iTable
+ && pCol->iColumn==pExpr->iColumn
+ && pExpr->op!=TK_IF_NULL_ROW
+ ){
+ goto fix_up_expr;
+ }
+ }
+ k = addAggInfoColumn(pParse->db, pAggInfo);
+ if( k<0 ){
+ /* OOM on resize */
+ assert( pParse->db->mallocFailed );
+ return;
+ }
+ pCol = &pAggInfo->aCol[k];
+ assert( ExprUseYTab(pExpr) );
+ pCol->pTab = pExpr->y.pTab;
+ pCol->iTable = pExpr->iTable;
+ pCol->iColumn = pExpr->iColumn;
+ pCol->iSorterColumn = -1;
+ pCol->pCExpr = pExpr;
+ if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){
+ int j, n;
+ ExprList *pGB = pAggInfo->pGroupBy;
+ struct ExprList_item *pTerm = pGB->a;
+ n = pGB->nExpr;
+ for(j=0; j<n; j++, pTerm++){
+ Expr *pE = pTerm->pExpr;
+ if( pE->op==TK_COLUMN
+ && pE->iTable==pExpr->iTable
+ && pE->iColumn==pExpr->iColumn
+ ){
+ pCol->iSorterColumn = j;
+ break;
+ }
+ }
+ }
+ if( pCol->iSorterColumn<0 ){
+ pCol->iSorterColumn = pAggInfo->nSortingColumn++;
+ }
+fix_up_expr:
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
+ assert( pExpr->pAggInfo==0 || pExpr->pAggInfo==pAggInfo );
+ pExpr->pAggInfo = pAggInfo;
+ if( pExpr->op==TK_COLUMN ){
+ pExpr->op = TK_AGG_COLUMN;
+ }
+ pExpr->iAgg = (i16)k;
+}
+
+/*
** This is the xExprCallback for a tree walker. It is used to
** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates
** for additional information.
@@ -109773,71 +112123,51 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
AggInfo *pAggInfo = pNC->uNC.pAggInfo;
assert( pNC->ncFlags & NC_UAggInfo );
+ assert( pAggInfo->iFirstReg==0 );
switch( pExpr->op ){
+ default: {
+ IndexedExpr *pIEpr;
+ Expr tmp;
+ assert( pParse->iSelfTab==0 );
+ if( (pNC->ncFlags & NC_InAggFunc)==0 ) break;
+ if( pParse->pIdxEpr==0 ) break;
+ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
+ int iDataCur = pIEpr->iDataCur;
+ if( iDataCur<0 ) continue;
+ if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break;
+ }
+ if( pIEpr==0 ) break;
+ if( NEVER(!ExprUseYTab(pExpr)) ) break;
+ if( pExpr->pAggInfo!=0 ) break; /* Already resolved by outer context */
+
+ /* If we reach this point, it means that expression pExpr can be
+ ** translated into a reference to an index column as described by
+ ** pIEpr.
+ */
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.op = TK_AGG_COLUMN;
+ tmp.iTable = pIEpr->iIdxCur;
+ tmp.iColumn = pIEpr->iIdxCol;
+ findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp);
+ pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr;
+ pExpr->pAggInfo = pAggInfo;
+ pExpr->iAgg = tmp.iAgg;
+ return WRC_Prune;
+ }
+ case TK_IF_NULL_ROW:
case TK_AGG_COLUMN:
case TK_COLUMN: {
testcase( pExpr->op==TK_AGG_COLUMN );
testcase( pExpr->op==TK_COLUMN );
+ testcase( pExpr->op==TK_IF_NULL_ROW );
/* Check to see if the column is in one of the tables in the FROM
** clause of the aggregate query */
if( ALWAYS(pSrcList!=0) ){
SrcItem *pItem = pSrcList->a;
for(i=0; i<pSrcList->nSrc; i++, pItem++){
- struct AggInfo_col *pCol;
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
- /* If we reach this point, it means that pExpr refers to a table
- ** that is in the FROM clause of the aggregate query.
- **
- ** Make an entry for the column in pAggInfo->aCol[] if there
- ** is not an entry there already.
- */
- int k;
- pCol = pAggInfo->aCol;
- for(k=0; k<pAggInfo->nColumn; k++, pCol++){
- if( pCol->iTable==pExpr->iTable &&
- pCol->iColumn==pExpr->iColumn ){
- break;
- }
- }
- if( (k>=pAggInfo->nColumn)
- && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
- ){
- pCol = &pAggInfo->aCol[k];
- assert( ExprUseYTab(pExpr) );
- pCol->pTab = pExpr->y.pTab;
- pCol->iTable = pExpr->iTable;
- pCol->iColumn = pExpr->iColumn;
- pCol->iMem = ++pParse->nMem;
- pCol->iSorterColumn = -1;
- pCol->pCExpr = pExpr;
- if( pAggInfo->pGroupBy ){
- int j, n;
- ExprList *pGB = pAggInfo->pGroupBy;
- struct ExprList_item *pTerm = pGB->a;
- n = pGB->nExpr;
- for(j=0; j<n; j++, pTerm++){
- Expr *pE = pTerm->pExpr;
- if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable &&
- pE->iColumn==pExpr->iColumn ){
- pCol->iSorterColumn = j;
- break;
- }
- }
- }
- if( pCol->iSorterColumn<0 ){
- pCol->iSorterColumn = pAggInfo->nSortingColumn++;
- }
- }
- /* There is now an entry for pExpr in pAggInfo->aCol[] (either
- ** because it was there before or because we just created it).
- ** Convert the pExpr to be a TK_AGG_COLUMN referring to that
- ** pAggInfo->aCol[] entry.
- */
- ExprSetVVAProperty(pExpr, EP_NoReduce);
- pExpr->pAggInfo = pAggInfo;
- pExpr->op = TK_AGG_COLUMN;
- pExpr->iAgg = (i16)k;
+ findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
break;
} /* endif pExpr->iTable==pItem->iCursor */
} /* end loop over pSrcList */
@@ -109867,7 +112197,6 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i];
pItem->pFExpr = pExpr;
- pItem->iMem = ++pParse->nMem;
assert( ExprUseUToken(pExpr) );
pItem->pFunc = sqlite3FindFunction(pParse->db,
pExpr->u.zToken,
@@ -110764,13 +113093,14 @@ static void renameTokenCheckAll(Parse *pParse, const void *pPtr){
assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
if( pParse->nErr==0 ){
const RenameToken *p;
- u8 i = 0;
+ u32 i = 1;
for(p=pParse->pRename; p; p=p->pNext){
if( p->p ){
assert( p->p!=pPtr );
- i += *(u8*)(p->p);
+ i += *(u8*)(p->p) | 1;
}
}
+ assert( i>0 );
}
}
#else
@@ -113256,6 +115586,7 @@ static void analyzeVdbeCommentIndexWithColumnName(
if( NEVER(i==XN_ROWID) ){
VdbeComment((v,"%s.rowid",pIdx->zName));
}else if( i==XN_EXPR ){
+ assert( pIdx->bHasExpr );
VdbeComment((v,"%s.expr(%d)",pIdx->zName, k));
}else{
VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName));
@@ -113899,6 +116230,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
+ assert( db!=0 );
+ assert( pIdx!=0 );
#ifdef SQLITE_ENABLE_STAT4
if( pIdx->aSample ){
int j;
@@ -113908,7 +116241,7 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
}
sqlite3DbFree(db, pIdx->aSample);
}
- if( db && db->pnBytesFreed==0 ){
+ if( db->pnBytesFreed==0 ){
pIdx->nSample = 0;
pIdx->aSample = 0;
}
@@ -114327,7 +116660,7 @@ static void attachFunc(
char *zErr = 0;
unsigned int flags;
Db *aNew; /* New array of Db pointers */
- Db *pNew; /* Db object for the newly attached database */
+ Db *pNew = 0; /* Db object for the newly attached database */
char *zErrDyn = 0;
sqlite3_vfs *pVfs;
@@ -114347,13 +116680,26 @@ static void attachFunc(
/* This is not a real ATTACH. Instead, this routine is being called
** from sqlite3_deserialize() to close database db->init.iDb and
** reopen it as a MemDB */
+ Btree *pNewBt = 0;
pVfs = sqlite3_vfs_find("memdb");
if( pVfs==0 ) return;
- pNew = &db->aDb[db->init.iDb];
- if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt);
- pNew->pBt = 0;
- pNew->pSchema = 0;
- rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB);
+ rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB);
+ if( rc==SQLITE_OK ){
+ Schema *pNewSchema = sqlite3SchemaGet(db, pNewBt);
+ if( pNewSchema ){
+ /* Both the Btree and the new Schema were allocated successfully.
+ ** Close the old db and update the aDb[] slot with the new memdb
+ ** values. */
+ pNew = &db->aDb[db->init.iDb];
+ if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt);
+ pNew->pBt = pNewBt;
+ pNew->pSchema = pNewSchema;
+ }else{
+ sqlite3BtreeClose(pNewBt);
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc ) goto attach_error;
}else{
/* This is a real ATTACH
**
@@ -114466,7 +116812,7 @@ static void attachFunc(
}
#endif
if( rc ){
- if( !REOPEN_AS_MEMDB(db) ){
+ if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){
int iDb = db->nDb - 1;
assert( iDb>=2 );
if( db->aDb[iDb].pBt ){
@@ -114583,6 +116929,8 @@ static void codeAttach(
sqlite3* db = pParse->db;
int regArgs;
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto attach_end;
+
if( pParse->nErr ) goto attach_end;
memset(&sName, 0, sizeof(NameContext));
sName.pParse = pParse;
@@ -115258,6 +117606,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask m){
SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
sqlite3 *db;
Vdbe *v;
+ int iDb, i;
assert( pParse->pToplevel==0 );
db = pParse->db;
@@ -115287,7 +117636,6 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
if( pParse->bReturning ){
Returning *pReturning = pParse->u1.pReturning;
int addrRewind;
- int i;
int reg;
if( pReturning->nRetCol ){
@@ -115324,76 +117672,69 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
** transaction on each used database and to verify the schema cookie
** on each used database.
*/
- if( db->mallocFailed==0
- && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr)
- ){
- int iDb, i;
- assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
- sqlite3VdbeJumpHere(v, 0);
- assert( db->nDb>0 );
- iDb = 0;
- do{
- Schema *pSchema;
- if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
- sqlite3VdbeUsesBtree(v, iDb);
- pSchema = db->aDb[iDb].pSchema;
- sqlite3VdbeAddOp4Int(v,
- OP_Transaction, /* Opcode */
- iDb, /* P1 */
- DbMaskTest(pParse->writeMask,iDb), /* P2 */
- pSchema->schema_cookie, /* P3 */
- pSchema->iGeneration /* P4 */
- );
- if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
- VdbeComment((v,
- "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
- }while( ++iDb<db->nDb );
+ assert( pParse->nErr>0 || sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
+ sqlite3VdbeJumpHere(v, 0);
+ assert( db->nDb>0 );
+ iDb = 0;
+ do{
+ Schema *pSchema;
+ if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
+ sqlite3VdbeUsesBtree(v, iDb);
+ pSchema = db->aDb[iDb].pSchema;
+ sqlite3VdbeAddOp4Int(v,
+ OP_Transaction, /* Opcode */
+ iDb, /* P1 */
+ DbMaskTest(pParse->writeMask,iDb), /* P2 */
+ pSchema->schema_cookie, /* P3 */
+ pSchema->iGeneration /* P4 */
+ );
+ if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
+ VdbeComment((v,
+ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
+ }while( ++iDb<db->nDb );
#ifndef SQLITE_OMIT_VIRTUALTABLE
- for(i=0; i<pParse->nVtabLock; i++){
- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
- }
- pParse->nVtabLock = 0;
+ for(i=0; i<pParse->nVtabLock; i++){
+ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
+ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
+ }
+ pParse->nVtabLock = 0;
#endif
- /* Once all the cookies have been verified and transactions opened,
- ** obtain the required table-locks. This is a no-op unless the
- ** shared-cache feature is enabled.
- */
- codeTableLocks(pParse);
+ /* Once all the cookies have been verified and transactions opened,
+ ** obtain the required table-locks. This is a no-op unless the
+ ** shared-cache feature is enabled.
+ */
+ codeTableLocks(pParse);
- /* Initialize any AUTOINCREMENT data structures required.
- */
- sqlite3AutoincrementBegin(pParse);
+ /* Initialize any AUTOINCREMENT data structures required.
+ */
+ sqlite3AutoincrementBegin(pParse);
- /* Code constant expressions that where factored out of inner loops.
- **
- ** The pConstExpr list might also contain expressions that we simply
- ** want to keep around until the Parse object is deleted. Such
- ** expressions have iConstExprReg==0. Do not generate code for
- ** those expressions, of course.
- */
- if( pParse->pConstExpr ){
- ExprList *pEL = pParse->pConstExpr;
- pParse->okConstFactor = 0;
- for(i=0; i<pEL->nExpr; i++){
- int iReg = pEL->a[i].u.iConstExprReg;
- if( iReg>0 ){
- sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
- }
- }
+ /* Code constant expressions that where factored out of inner loops.
+ **
+ ** The pConstExpr list might also contain expressions that we simply
+ ** want to keep around until the Parse object is deleted. Such
+ ** expressions have iConstExprReg==0. Do not generate code for
+ ** those expressions, of course.
+ */
+ if( pParse->pConstExpr ){
+ ExprList *pEL = pParse->pConstExpr;
+ pParse->okConstFactor = 0;
+ for(i=0; i<pEL->nExpr; i++){
+ int iReg = pEL->a[i].u.iConstExprReg;
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
}
+ }
- if( pParse->bReturning ){
- Returning *pRet = pParse->u1.pReturning;
- if( pRet->nRetCol ){
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
- }
+ if( pParse->bReturning ){
+ Returning *pRet = pParse->u1.pReturning;
+ if( pRet->nRetCol ){
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
}
-
- /* Finally, jump back to the beginning of the executable code. */
- sqlite3VdbeGoto(v, 1);
}
+
+ /* Finally, jump back to the beginning of the executable code. */
+ sqlite3VdbeGoto(v, 1);
}
/* Get the VDBE program ready for execution
@@ -115432,6 +117773,7 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
char saveBuf[PARSE_TAIL_SZ];
if( pParse->nErr ) return;
+ if( pParse->eParseMode ) return;
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
va_start(ap, zFormat);
zSql = sqlite3VMPrintf(db, zFormat, ap);
@@ -115578,7 +117920,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
/* If zName is the not the name of a table in the schema created using
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */
- if( pParse->disableVtab==0 && db->init.busy==0 ){
+ if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
pMod = sqlite3PragmaVtabRegister(db, zName);
@@ -115591,7 +117933,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
#endif
if( flags & LOCATE_NOERR ) return 0;
pParse->checkSchema = 1;
- }else if( IsVirtual(p) && pParse->disableVtab ){
+ }else if( IsVirtual(p) && (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)!=0 ){
p = 0;
}
@@ -115900,16 +118242,17 @@ SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
int i;
Column *pCol;
assert( pTable!=0 );
+ assert( db!=0 );
if( (pCol = pTable->aCol)!=0 ){
for(i=0; i<pTable->nCol; i++, pCol++){
assert( pCol->zCnName==0 || pCol->hName==sqlite3StrIHash(pCol->zCnName) );
sqlite3DbFree(db, pCol->zCnName);
}
- sqlite3DbFree(db, pTable->aCol);
+ sqlite3DbNNFreeNN(db, pTable->aCol);
if( IsOrdinaryTable(pTable) ){
sqlite3ExprListDelete(db, pTable->u.tab.pDfltList);
}
- if( db==0 || db->pnBytesFreed==0 ){
+ if( db->pnBytesFreed==0 ){
pTable->aCol = 0;
pTable->nCol = 0;
if( IsOrdinaryTable(pTable) ){
@@ -115946,7 +118289,8 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
** a Table object that was going to be marked ephemeral. So do not check
** that no lookaside memory is used in this case either. */
int nLookaside = 0;
- if( db && !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){
+ assert( db!=0 );
+ if( !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){
nLookaside = sqlite3LookasideUsed(db, 0);
}
#endif
@@ -115956,7 +118300,7 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
pNext = pIndex->pNext;
assert( pIndex->pSchema==pTable->pSchema
|| (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) );
- if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){
+ if( db->pnBytesFreed==0 && !IsVirtual(pTable) ){
char *zName = pIndex->zName;
TESTONLY ( Index *pOld = ) sqlite3HashInsert(
&pIndex->pSchema->idxHash, zName, 0
@@ -115993,8 +118337,9 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
}
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Do not delete the table until the reference count reaches zero. */
+ assert( db!=0 );
if( !pTable ) return;
- if( ((!db || db->pnBytesFreed==0) && (--pTable->nTabRef)>0) ) return;
+ if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
@@ -117126,6 +119471,13 @@ SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType
if( pCol->colFlags & COLFLAG_PRIMKEY ){
makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */
}
+ if( ALWAYS(pExpr) && pExpr->op==TK_ID ){
+ /* The value of a generated column needs to be a real expression, not
+ ** just a reference to another column, in order for covering index
+ ** optimizations to work correctly. So if the value is not an expression,
+ ** turn it into one by adding a unary "+" operator. */
+ pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0);
+ }
sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr);
pExpr = 0;
goto generated_done;
@@ -117262,7 +119614,8 @@ static char *createTableStmt(sqlite3 *db, Table *p){
/* SQLITE_AFF_TEXT */ " TEXT",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
- /* SQLITE_AFF_REAL */ " REAL"
+ /* SQLITE_AFF_REAL */ " REAL",
+ /* SQLITE_AFF_FLEXNUM */ " NUM",
};
int len;
const char *zType;
@@ -117278,10 +119631,12 @@ static char *createTableStmt(sqlite3 *db, Table *p){
testcase( pCol->affinity==SQLITE_AFF_NUMERIC );
testcase( pCol->affinity==SQLITE_AFF_INTEGER );
testcase( pCol->affinity==SQLITE_AFF_REAL );
+ testcase( pCol->affinity==SQLITE_AFF_FLEXNUM );
zType = azType[pCol->affinity - SQLITE_AFF_BLOB];
len = sqlite3Strlen30(zType);
assert( pCol->affinity==SQLITE_AFF_BLOB
+ || pCol->affinity==SQLITE_AFF_FLEXNUM
|| pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;
@@ -117398,7 +119753,8 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){
/* Recompute the colNotIdxed field of the Index.
**
** colNotIdxed is a bitmask that has a 0 bit representing each indexed
-** columns that are within the first 63 columns of the table. The
+** columns that are within the first 63 columns of the table and a 1 for
+** all other bits (all columns that are not in the index). The
** high-order bit of colNotIdxed is always 1. All unindexed columns
** of the table have a 1.
**
@@ -117426,7 +119782,7 @@ static void recomputeColumnsNotIndexed(Index *pIdx){
}
}
pIdx->colNotIdxed = ~m;
- assert( (pIdx->colNotIdxed>>63)==1 );
+ assert( (pIdx->colNotIdxed>>63)==1 ); /* See note-20221022-a */
}
/*
@@ -117695,6 +120051,7 @@ SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
** not pass them into code generator routines by mistake.
*/
static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){
+ (void)pWalker;
ExprSetVVAProperty(pExpr, EP_Immutable);
return WRC_Continue;
}
@@ -118167,7 +120524,7 @@ create_view_fail:
** the columns of the view in the pTable structure. Return the number
** of errors. If an error is seen leave an error message in pParse->zErrMsg.
*/
-SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
+static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
Table *pSelTab; /* A fake table from which we get the result set */
Select *pSel; /* Copy of the SELECT that implements the view */
int nErr = 0; /* Number of errors encountered */
@@ -118192,9 +120549,10 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#ifndef SQLITE_OMIT_VIEW
/* A positive nCol means the columns names for this view are
- ** already known.
+ ** already known. This routine is not called unless either the
+ ** table is virtual or nCol is zero.
*/
- if( pTable->nCol>0 ) return 0;
+ assert( pTable->nCol<=0 );
/* A negative nCol is a special marker meaning that we are currently
** trying to compute the column names. If we enter this routine with
@@ -118260,8 +120618,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
&& pTable->nCol==pSel->pEList->nExpr
){
assert( db->mallocFailed==0 );
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel,
- SQLITE_AFF_NONE);
+ sqlite3SubqueryColumnTypes(pParse, pTable, pSel, SQLITE_AFF_NONE);
}
}else{
/* CREATE VIEW name AS... without an argument list. Construct
@@ -118290,6 +120647,11 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#endif /* SQLITE_OMIT_VIEW */
return nErr;
}
+SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
+ assert( pTable!=0 );
+ if( !IsVirtual(pTable) && pTable->nCol>0 ) return 0;
+ return viewGetColumnNames(pParse, pTable);
+}
#endif /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
#ifndef SQLITE_OMIT_VIEW
@@ -119155,7 +121517,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
}
if( !IN_RENAME_OBJECT ){
if( !db->init.busy ){
- if( sqlite3FindTable(db, zName, 0)!=0 ){
+ if( sqlite3FindTable(db, zName, pDb->zDbSName)!=0 ){
sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
goto exit_create_index;
}
@@ -119308,6 +121670,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
j = XN_EXPR;
pIndex->aiColumn[i] = XN_EXPR;
pIndex->uniqNotNull = 0;
+ pIndex->bHasExpr = 1;
}else{
j = pCExpr->iColumn;
assert( j<=0x7fff );
@@ -119319,6 +121682,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
}
if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){
pIndex->bHasVCol = 1;
+ pIndex->bHasExpr = 1;
}
}
pIndex->aiColumn[i] = (i16)j;
@@ -119808,12 +122172,13 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *
*/
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
int i;
+ assert( db!=0 );
if( pList==0 ) return;
assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */
for(i=0; i<pList->nId; i++){
sqlite3DbFree(db, pList->a[i].zName);
}
- sqlite3DbFreeNN(db, pList);
+ sqlite3DbNNFreeNN(db, pList);
}
/*
@@ -120016,11 +122381,12 @@ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
int i;
SrcItem *pItem;
+ assert( db!=0 );
if( pList==0 ) return;
for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
- if( pItem->zDatabase ) sqlite3DbFreeNN(db, pItem->zDatabase);
- sqlite3DbFree(db, pItem->zName);
- if( pItem->zAlias ) sqlite3DbFreeNN(db, pItem->zAlias);
+ if( pItem->zDatabase ) sqlite3DbNNFreeNN(db, pItem->zDatabase);
+ if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName);
+ if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias);
if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pTab);
@@ -120031,7 +122397,7 @@ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
sqlite3ExprDelete(db, pItem->u3.pOn);
}
}
- sqlite3DbFreeNN(db, pList);
+ sqlite3DbNNFreeNN(db, pList);
}
/*
@@ -121283,19 +123649,21 @@ SQLITE_PRIVATE void sqlite3SchemaClear(void *p){
Hash temp2;
HashElem *pElem;
Schema *pSchema = (Schema *)p;
+ sqlite3 xdb;
+ memset(&xdb, 0, sizeof(xdb));
temp1 = pSchema->tblHash;
temp2 = pSchema->trigHash;
sqlite3HashInit(&pSchema->trigHash);
sqlite3HashClear(&pSchema->idxHash);
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
- sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem));
+ sqlite3DeleteTrigger(&xdb, (Trigger*)sqliteHashData(pElem));
}
sqlite3HashClear(&temp2);
sqlite3HashInit(&pSchema->tblHash);
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
Table *pTab = sqliteHashData(pElem);
- sqlite3DeleteTable(0, pTab);
+ sqlite3DeleteTable(&xdb, pTab);
}
sqlite3HashClear(&temp1);
sqlite3HashClear(&pSchema->fkeyHash);
@@ -121394,18 +123762,42 @@ SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *
** 1) It is a virtual table and no implementation of the xUpdate method
** has been provided
**
-** 2) It is a system table (i.e. sqlite_schema), this call is not
+** 2) A trigger is currently being coded and the table is a virtual table
+** that is SQLITE_VTAB_DIRECTONLY or if PRAGMA trusted_schema=OFF and
+** the table is not SQLITE_VTAB_INNOCUOUS.
+**
+** 3) It is a system table (i.e. sqlite_schema), this call is not
** part of a nested parse and writable_schema pragma has not
** been specified
**
-** 3) The table is a shadow table, the database connection is in
+** 4) The table is a shadow table, the database connection is in
** defensive mode, and the current sqlite3_prepare()
** is for a top-level SQL statement.
*/
+static int vtabIsReadOnly(Parse *pParse, Table *pTab){
+ if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){
+ return 1;
+ }
+
+ /* Within triggers:
+ ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY
+ ** virtual tables
+ ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS
+ ** virtual tables if PRAGMA trusted_schema=ON.
+ */
+ if( pParse->pToplevel!=0
+ && pTab->u.vtab.p->eVtabRisk >
+ ((pParse->db->flags & SQLITE_TrustedSchema)!=0)
+ ){
+ sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"",
+ pTab->zName);
+ }
+ return 0;
+}
static int tabIsReadOnly(Parse *pParse, Table *pTab){
sqlite3 *db;
if( IsVirtual(pTab) ){
- return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0;
+ return vtabIsReadOnly(pParse, pTab);
}
if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0;
db = pParse->db;
@@ -121417,9 +123809,11 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){
}
/*
-** Check to make sure the given table is writable. If it is not
-** writable, generate an error message and return 1. If it is
-** writable return 0;
+** Check to make sure the given table is writable.
+**
+** If pTab is not writable -> generate an error message and return 1.
+** If pTab is writable but other errors have occurred -> return 1.
+** If pTab is writable and no prior errors -> return 0;
*/
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
if( tabIsReadOnly(pParse, pTab) ){
@@ -121780,9 +124174,10 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
- sqlite3VdbeChangeP3(v, -1, memCnt ? memCnt : -1);
+ sqlite3VdbeAddOp3(v, OP_Clear, pIdx->tnum, iDb, memCnt ? memCnt : -1);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
}
}
}else
@@ -121982,7 +124377,7 @@ delete_from_cleanup:
sqlite3ExprListDelete(db, pOrderBy);
sqlite3ExprDelete(db, pLimit);
#endif
- sqlite3DbFree(db, aToOpen);
+ if( aToOpen ) sqlite3DbNNFreeNN(db, aToOpen);
return;
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
@@ -123065,7 +125460,7 @@ static int patternCompare(
** c but in the other case and search the input string for either
** c or cx.
*/
- if( c<=0x80 ){
+ if( c<0x80 ){
char zStop[3];
int bMatch;
if( noCase ){
@@ -123148,7 +125543,13 @@ static int patternCompare(
** non-zero if there is no match.
*/
SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){
- return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[');
+ if( zString==0 ){
+ return zGlobPattern!=0;
+ }else if( zGlobPattern==0 ){
+ return 1;
+ }else {
+ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[');
+ }
}
/*
@@ -123156,7 +125557,13 @@ SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){
** a miss - like strcmp().
*/
SQLITE_API int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){
- return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc);
+ if( zStr==0 ){
+ return zPattern!=0;
+ }else if( zPattern==0 ){
+ return 1;
+ }else{
+ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc);
+ }
}
/*
@@ -123395,7 +125802,7 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
}
case SQLITE_BLOB: {
char const *zBlob = sqlite3_value_blob(pValue);
- int nBlob = sqlite3_value_bytes(pValue);
+ i64 nBlob = sqlite3_value_bytes(pValue);
assert( zBlob==sqlite3_value_blob(pValue) ); /* No encoding change */
sqlite3StrAccumEnlarge(pStr, nBlob*2 + 4);
if( pStr->accError==0 ){
@@ -123537,6 +125944,96 @@ static void hexFunc(
}
/*
+** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr
+** contains character ch, or 0 if it does not.
+*/
+static int strContainsChar(const u8 *zStr, int nStr, u32 ch){
+ const u8 *zEnd = &zStr[nStr];
+ const u8 *z = zStr;
+ while( z<zEnd ){
+ u32 tst = Utf8Read(z);
+ if( tst==ch ) return 1;
+ }
+ return 0;
+}
+
+/*
+** The unhex() function. This function may be invoked with either one or
+** two arguments. In both cases the first argument is interpreted as text
+** a text value containing a set of pairs of hexadecimal digits which are
+** decoded and returned as a blob.
+**
+** If there is only a single argument, then it must consist only of an
+** even number of hexadeximal digits. Otherwise, return NULL.
+**
+** Or, if there is a second argument, then any character that appears in
+** the second argument is also allowed to appear between pairs of hexadecimal
+** digits in the first argument. If any other character appears in the
+** first argument, or if one of the allowed characters appears between
+** two hexadecimal digits that make up a single byte, NULL is returned.
+**
+** The following expressions are all true:
+**
+** unhex('ABCD') IS x'ABCD'
+** unhex('AB CD') IS NULL
+** unhex('AB CD', ' ') IS x'ABCD'
+** unhex('A BCD', ' ') IS NULL
+*/
+static void unhexFunc(
+ sqlite3_context *pCtx,
+ int argc,
+ sqlite3_value **argv
+){
+ const u8 *zPass = (const u8*)"";
+ int nPass = 0;
+ const u8 *zHex = sqlite3_value_text(argv[0]);
+ int nHex = sqlite3_value_bytes(argv[0]);
+#ifdef SQLITE_DEBUG
+ const u8 *zEnd = zHex ? &zHex[nHex] : 0;
+#endif
+ u8 *pBlob = 0;
+ u8 *p = 0;
+
+ assert( argc==1 || argc==2 );
+ if( argc==2 ){
+ zPass = sqlite3_value_text(argv[1]);
+ nPass = sqlite3_value_bytes(argv[1]);
+ }
+ if( !zHex || !zPass ) return;
+
+ p = pBlob = contextMalloc(pCtx, (nHex/2)+1);
+ if( pBlob ){
+ u8 c; /* Most significant digit of next byte */
+ u8 d; /* Least significant digit of next byte */
+
+ while( (c = *zHex)!=0x00 ){
+ while( !sqlite3Isxdigit(c) ){
+ u32 ch = Utf8Read(zHex);
+ assert( zHex<=zEnd );
+ if( !strContainsChar(zPass, nPass, ch) ) goto unhex_null;
+ c = *zHex;
+ if( c==0x00 ) goto unhex_done;
+ }
+ zHex++;
+ assert( *zEnd==0x00 );
+ assert( zHex<=zEnd );
+ d = *(zHex++);
+ if( !sqlite3Isxdigit(d) ) goto unhex_null;
+ *(p++) = (sqlite3HexToInt(c)<<4) | sqlite3HexToInt(d);
+ }
+ }
+
+ unhex_done:
+ sqlite3_result_blob(pCtx, pBlob, (p - pBlob), sqlite3_free);
+ return;
+
+ unhex_null:
+ sqlite3_free(pBlob);
+ return;
+}
+
+
+/*
** The zeroblob(N) function returns a zero-filled blob of size N bytes.
*/
static void zeroblobFunc(
@@ -123753,6 +126250,9 @@ static void unknownFunc(
sqlite3_value **argv
){
/* no-op */
+ (void)context;
+ (void)argc;
+ (void)argv;
}
#endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
@@ -124419,17 +126919,15 @@ static void logFunc(
}
ans = log(x)/b;
}else{
- ans = log(x);
switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
case 1:
- /* Convert from natural logarithm to log base 10 */
- ans /= M_LN10;
+ ans = log10(x);
break;
case 2:
- /* Convert from natural logarithm to log base 2 */
- ans /= M_LN2;
+ ans = log2(x);
break;
default:
+ ans = log(x);
break;
}
}
@@ -124498,6 +126996,7 @@ static void piFunc(
sqlite3_value **argv
){
assert( argc==0 );
+ (void)argv;
sqlite3_result_double(context, M_PI);
}
@@ -124598,6 +127097,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(hex, 1, 0, 0, hexFunc ),
+ FUNCTION(unhex, 1, 0, 0, unhexFunc ),
+ FUNCTION(unhex, 2, 0, 0, unhexFunc ),
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
@@ -126150,11 +128651,12 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
FKey *pNext; /* Copy of pFKey->pNextFrom */
assert( IsOrdinaryTable(pTab) );
+ assert( db!=0 );
for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
/* Remove the FK from the fkeyHash hash table. */
- if( !db || db->pnBytesFreed==0 ){
+ if( db->pnBytesFreed==0 ){
if( pFKey->pPrevTo ){
pFKey->pPrevTo->pNextTo = pFKey->pNextTo;
}else{
@@ -126284,6 +128786,7 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
aff = SQLITE_AFF_INTEGER;
}else{
assert( x==XN_EXPR );
+ assert( pIdx->bHasExpr );
assert( pIdx->aColExpr!=0 );
aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
}
@@ -126298,6 +128801,28 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
}
/*
+** Compute an affinity string for a table. Space is obtained
+** from sqlite3DbMalloc(). The caller is responsible for freeing
+** the space when done.
+*/
+SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3 *db, const Table *pTab){
+ char *zColAff;
+ zColAff = (char *)sqlite3DbMallocRaw(db, pTab->nCol+1);
+ if( zColAff ){
+ int i, j;
+ for(i=j=0; i<pTab->nCol; i++){
+ if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
+ zColAff[j++] = pTab->aCol[i].affinity;
+ }
+ }
+ do{
+ zColAff[j--] = 0;
+ }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB );
+ }
+ return zColAff;
+}
+
+/*
** Make changes to the evolving bytecode to do affinity transformations
** of values that are about to be gathered into a row for table pTab.
**
@@ -126338,7 +128863,7 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
** Apply the type checking to that array of registers.
*/
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
- int i, j;
+ int i;
char *zColAff;
if( pTab->tabFlags & TF_Strict ){
if( iReg==0 ){
@@ -126347,7 +128872,7 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
** OP_MakeRecord is found */
VdbeOp *pPrev;
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
- pPrev = sqlite3VdbeGetOp(v, -1);
+ pPrev = sqlite3VdbeGetLastOp(v);
assert( pPrev!=0 );
assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed );
pPrev->opcode = OP_TypeCheck;
@@ -126361,22 +128886,11 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
}
zColAff = pTab->zColAff;
if( zColAff==0 ){
- sqlite3 *db = sqlite3VdbeDb(v);
- zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
+ zColAff = sqlite3TableAffinityStr(0, pTab);
if( !zColAff ){
- sqlite3OomFault(db);
+ sqlite3OomFault(sqlite3VdbeDb(v));
return;
}
-
- for(i=j=0; i<pTab->nCol; i++){
- assert( pTab->aCol[i].affinity!=0 || sqlite3VdbeParser(v)->nErr>0 );
- if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
- zColAff[j++] = pTab->aCol[i].affinity;
- }
- }
- do{
- zColAff[j--] = 0;
- }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
assert( zColAff!=0 );
@@ -126385,7 +128899,7 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
if( iReg ){
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
}else{
- assert( sqlite3VdbeGetOp(v, -1)->opcode==OP_MakeRecord
+ assert( sqlite3VdbeGetLastOp(v)->opcode==OP_MakeRecord
|| sqlite3VdbeDb(v)->mallocFailed );
sqlite3VdbeChangeP4(v, -1, zColAff, i);
}
@@ -126471,7 +128985,7 @@ SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns(
*/
sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore);
if( (pTab->tabFlags & TF_HasStored)!=0 ){
- pOp = sqlite3VdbeGetOp(pParse->pVdbe,-1);
+ pOp = sqlite3VdbeGetLastOp(pParse->pVdbe);
if( pOp->opcode==OP_Affinity ){
/* Change the OP_Affinity argument to '@' (NONE) for all stored
** columns. '@' is the no-op affinity and those columns have not
@@ -127377,7 +129891,12 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+k, iRegStore);
}
}else{
- sqlite3ExprCode(pParse, pList->a[k].pExpr, iRegStore);
+ Expr *pX = pList->a[k].pExpr;
+ int y = sqlite3ExprCodeTarget(pParse, pX, iRegStore);
+ if( y!=iRegStore ){
+ sqlite3VdbeAddOp2(v,
+ ExprHasProperty(pX, EP_Subquery) ? OP_Copy : OP_SCopy, y, iRegStore);
+ }
}
}
@@ -127514,7 +130033,9 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert
);
- sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
+ if( db->flags & SQLITE_ForeignKeys ){
+ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
+ }
/* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE
** constraints or (b) there are no triggers and this table is not a
@@ -127598,7 +130119,7 @@ insert_cleanup:
sqlite3UpsertDelete(db, pUpsert);
sqlite3SelectDelete(db, pSelect);
sqlite3IdListDelete(db, pColumn);
- sqlite3DbFree(db, aRegIdx);
+ if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx);
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
@@ -127962,6 +130483,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
case OE_Fail: {
char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
pCol->zCnName);
+ testcase( zMsg==0 && db->mallocFailed==0 );
sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL,
onError, iReg);
sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
@@ -129825,9 +132347,9 @@ struct sqlite3_api_routines {
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
- char *(*create_filename)(const char*,const char*,const char*,
+ const char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
- void (*free_filename)(char*);
+ void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
@@ -129851,6 +132373,10 @@ struct sqlite3_api_routines {
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
+ /* Version 3.40.0 and later */
+ int (*value_encoding)(sqlite3_value*);
+ /* Version 3.41.0 and later */
+ int (*is_interrupted)(sqlite3*);
};
/*
@@ -130175,6 +132701,10 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
+/* Version 3.40.0 and later */
+#define sqlite3_value_encoding sqlite3_api->value_encoding
+/* Version 3.41.0 and later */
+#define sqlite3_is_interrupted sqlite3_api->is_interrupted
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -130687,7 +133217,11 @@ static const sqlite3_api_routines sqlite3Apis = {
0,
0,
#endif
- sqlite3_db_name
+ sqlite3_db_name,
+ /* Version 3.40.0 and later */
+ sqlite3_value_encoding,
+ /* Version 3.41.0 and later */
+ sqlite3_is_interrupted
};
/* True if x is the directory separator character
@@ -132704,6 +135238,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
*/
case PragTyp_TEMP_STORE_DIRECTORY: {
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
if( !zRight ){
returnSingleText(v, sqlite3_temp_directory);
}else{
@@ -132713,6 +135248,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
if( rc!=SQLITE_OK || res==0 ){
sqlite3ErrorMsg(pParse, "not a writable directory");
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
goto pragma_out;
}
}
@@ -132730,6 +135266,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
break;
}
@@ -132748,6 +135285,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
*/
case PragTyp_DATA_STORE_DIRECTORY: {
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
if( !zRight ){
returnSingleText(v, sqlite3_data_directory);
}else{
@@ -132757,6 +135295,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
if( rc!=SQLITE_OK || res==0 ){
sqlite3ErrorMsg(pParse, "not a writable directory");
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
goto pragma_out;
}
}
@@ -132768,6 +135307,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
break;
}
#endif
@@ -133481,15 +136021,24 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx, *pPk;
- Index *pPrior = 0;
+ Index *pPrior = 0; /* Previous index */
int loopTop;
int iDataCur, iIdxCur;
int r1 = -1;
- int bStrict;
+ int bStrict; /* True for a STRICT table */
+ int r2; /* Previous key for WITHOUT ROWID tables */
+ int mxCol; /* Maximum non-virtual column number */
if( !IsOrdinaryTable(pTab) ) continue;
if( pObjTab && pObjTab!=pTab ) continue;
- pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ if( isQuick || HasRowid(pTab) ){
+ pPk = 0;
+ r2 = 0;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ r2 = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ sqlite3VdbeAddOp3(v, OP_Null, 1, r2, r2+pPk->nKeyCol-1);
+ }
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
1, 0, &iDataCur, &iIdxCur);
/* reg[7] counts the number of entries in the table.
@@ -133503,52 +136052,166 @@ SQLITE_PRIVATE void sqlite3Pragma(
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
+
+ /* Fetch the right-most column from the table. This will cause
+ ** the entire record header to be parsed and sanity checked. It
+ ** will also prepopulate the cursor column cache that is used
+ ** by the OP_IsType code, so it is a required step.
+ */
+ assert( !IsVirtual(pTab) );
+ if( HasRowid(pTab) ){
+ mxCol = -1;
+ for(j=0; j<pTab->nCol; j++){
+ if( (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)==0 ) mxCol++;
+ }
+ if( mxCol==pTab->iPKey ) mxCol--;
+ }else{
+ /* COLFLAG_VIRTUAL columns are not included in the WITHOUT ROWID
+ ** PK index column-count, so there is no need to account for them
+ ** in this case. */
+ mxCol = sqlite3PrimaryKeyIndex(pTab)->nColumn-1;
+ }
+ if( mxCol>=0 ){
+ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, mxCol, 3);
+ sqlite3VdbeTypeofColumn(v, 3);
+ }
+
if( !isQuick ){
- /* Sanity check on record header decoding */
- sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nNVCol-1,3);
- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
- VdbeComment((v, "(right-most column)"));
+ if( pPk ){
+ /* Verify WITHOUT ROWID keys are in ascending order */
+ int a1;
+ char *zErr;
+ a1 = sqlite3VdbeAddOp4Int(v, OP_IdxGT, iDataCur, 0,r2,pPk->nKeyCol);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp1(v, OP_IsNull, r2); VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db,
+ "row not in PRIMARY KEY order for %s",
+ pTab->zName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+ sqlite3VdbeJumpHere(v, a1+1);
+ for(j=0; j<pPk->nKeyCol; j++){
+ sqlite3ExprCodeLoadIndexColumn(pParse, pPk, iDataCur, j, r2+j);
+ }
+ }
}
- /* Verify that all NOT NULL columns really are NOT NULL. At the
- ** same time verify the type of the content of STRICT tables */
+ /* Verify datatypes for all columns:
+ **
+ ** (1) NOT NULL columns may not contain a NULL
+ ** (2) Datatype must be exact for non-ANY columns in STRICT tables
+ ** (3) Datatype for TEXT columns in non-STRICT tables must be
+ ** NULL, TEXT, or BLOB.
+ ** (4) Datatype for numeric columns in non-STRICT tables must not
+ ** be a TEXT value that can be losslessly converted to numeric.
+ */
bStrict = (pTab->tabFlags & TF_Strict)!=0;
for(j=0; j<pTab->nCol; j++){
char *zErr;
- Column *pCol = pTab->aCol + j;
- int doError, jmp2;
+ Column *pCol = pTab->aCol + j; /* The column to be checked */
+ int labelError; /* Jump here to report an error */
+ int labelOk; /* Jump here if all looks ok */
+ int p1, p3, p4; /* Operands to the OP_IsType opcode */
+ int doTypeCheck; /* Check datatypes (besides NOT NULL) */
+
if( j==pTab->iPKey ) continue;
- if( pCol->notNull==0 && !bStrict ) continue;
- doError = bStrict ? sqlite3VdbeMakeLabel(pParse) : 0;
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
- if( sqlite3VdbeGetOp(v,-1)->opcode==OP_Column ){
- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ if( bStrict ){
+ doTypeCheck = pCol->eCType>COLTYPE_ANY;
+ }else{
+ doTypeCheck = pCol->affinity>SQLITE_AFF_BLOB;
+ }
+ if( pCol->notNull==0 && !doTypeCheck ) continue;
+
+ /* Compute the operands that will be needed for OP_IsType */
+ p4 = SQLITE_NULL;
+ if( pCol->colFlags & COLFLAG_VIRTUAL ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
+ p1 = -1;
+ p3 = 3;
+ }else{
+ if( pCol->iDflt ){
+ sqlite3_value *pDfltValue = 0;
+ sqlite3ValueFromExpr(db, sqlite3ColumnExpr(pTab,pCol), ENC(db),
+ pCol->affinity, &pDfltValue);
+ if( pDfltValue ){
+ p4 = sqlite3_value_type(pDfltValue);
+ sqlite3ValueFree(pDfltValue);
+ }
+ }
+ p1 = iDataCur;
+ if( !HasRowid(pTab) ){
+ testcase( j!=sqlite3TableColumnToStorage(pTab, j) );
+ p3 = sqlite3TableColumnToIndex(sqlite3PrimaryKeyIndex(pTab), j);
+ }else{
+ p3 = sqlite3TableColumnToStorage(pTab,j);
+ testcase( p3!=j);
+ }
}
+
+ labelError = sqlite3VdbeMakeLabel(pParse);
+ labelOk = sqlite3VdbeMakeLabel(pParse);
if( pCol->notNull ){
- jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v);
+ /* (1) NOT NULL columns may not contain a NULL */
+ int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ sqlite3VdbeChangeP5(v, 0x0f);
+ VdbeCoverage(v);
zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
pCol->zCnName);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
- if( bStrict && pCol->eCType!=COLTYPE_ANY ){
- sqlite3VdbeGoto(v, doError);
+ if( doTypeCheck ){
+ sqlite3VdbeGoto(v, labelError);
+ sqlite3VdbeJumpHere(v, jmp2);
}else{
- integrityCheckResultRow(v);
+ /* VDBE byte code will fall thru */
}
- sqlite3VdbeJumpHere(v, jmp2);
}
- if( (pTab->tabFlags & TF_Strict)!=0
- && pCol->eCType!=COLTYPE_ANY
- ){
- jmp2 = sqlite3VdbeAddOp3(v, OP_IsNullOrType, 3, 0,
- sqlite3StdTypeMap[pCol->eCType-1]);
+ if( bStrict && doTypeCheck ){
+ /* (2) Datatype must be exact for non-ANY columns in STRICT tables*/
+ static unsigned char aStdTypeMask[] = {
+ 0x1f, /* ANY */
+ 0x18, /* BLOB */
+ 0x11, /* INT */
+ 0x11, /* INTEGER */
+ 0x13, /* REAL */
+ 0x14 /* TEXT */
+ };
+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ assert( pCol->eCType>=1 && pCol->eCType<=sizeof(aStdTypeMask) );
+ sqlite3VdbeChangeP5(v, aStdTypeMask[pCol->eCType-1]);
VdbeCoverage(v);
zErr = sqlite3MPrintf(db, "non-%s value in %s.%s",
sqlite3StdType[pCol->eCType-1],
pTab->zName, pTab->aCol[j].zCnName);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
- sqlite3VdbeResolveLabel(v, doError);
- integrityCheckResultRow(v);
- sqlite3VdbeJumpHere(v, jmp2);
+ }else if( !bStrict && pCol->affinity==SQLITE_AFF_TEXT ){
+ /* (3) Datatype for TEXT columns in non-STRICT tables must be
+ ** NULL, TEXT, or BLOB. */
+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */
+ VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db, "NUMERIC value in %s.%s",
+ pTab->zName, pTab->aCol[j].zCnName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ }else if( !bStrict && pCol->affinity>=SQLITE_AFF_NUMERIC ){
+ /* (4) Datatype for numeric columns in non-STRICT tables must not
+ ** be a TEXT value that can be converted to numeric. */
+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ sqlite3VdbeChangeP5(v, 0x1b); /* NULL, INT, FLOAT, or BLOB */
+ VdbeCoverage(v);
+ if( p1>=0 ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
+ }
+ sqlite3VdbeAddOp4(v, OP_Affinity, 3, 1, 0, "C", P4_STATIC);
+ sqlite3VdbeAddOp4Int(v, OP_IsType, -1, labelOk, 3, p4);
+ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */
+ VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db, "TEXT value in %s.%s",
+ pTab->zName, pTab->aCol[j].zCnName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
}
+ sqlite3VdbeResolveLabel(v, labelError);
+ integrityCheckResultRow(v);
+ sqlite3VdbeResolveLabel(v, labelOk);
}
/* Verify CHECK constraints */
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
@@ -133577,7 +136240,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( !isQuick ){ /* Omit the remaining tests for quick_check */
/* Validate index entries for the current row */
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- int jmp2, jmp3, jmp4, jmp5;
+ int jmp2, jmp3, jmp4, jmp5, label6;
+ int kk;
int ckUniq = sqlite3VdbeMakeLabel(pParse);
if( pPk==pIdx ) continue;
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
@@ -133595,13 +136259,32 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
jmp4 = integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, jmp2);
+
+ /* Any indexed columns with non-BINARY collations must still hold
+ ** the exact same text value as the table. */
+ label6 = 0;
+ for(kk=0; kk<pIdx->nKeyCol; kk++){
+ if( pIdx->azColl[kk]==sqlite3StrBINARY ) continue;
+ if( label6==0 ) label6 = sqlite3VdbeMakeLabel(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+j, kk, 3);
+ sqlite3VdbeAddOp3(v, OP_Ne, 3, label6, r1+kk); VdbeCoverage(v);
+ }
+ if( label6 ){
+ int jmp6 = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeResolveLabel(v, label6);
+ sqlite3VdbeLoadString(v, 3, "row ");
+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+ sqlite3VdbeLoadString(v, 4, " values differ from index ");
+ sqlite3VdbeGoto(v, jmp5-1);
+ sqlite3VdbeJumpHere(v, jmp6);
+ }
+
/* For UNIQUE indexes, verify that only one entry exists with the
** current key. The entry is unique if (1) any column is NULL
** or (2) the next entry has a different key */
if( IsUniqueIndex(pIdx) ){
int uniqOk = sqlite3VdbeMakeLabel(pParse);
int jmp6;
- int kk;
for(kk=0; kk<pIdx->nKeyCol; kk++){
int iCol = pIdx->aiColumn[kk];
assert( iCol!=XN_ROWID && iCol<pTab->nCol );
@@ -133636,6 +136319,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, addr);
}
+ if( pPk ){
+ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
+ }
}
}
}
@@ -133786,6 +136472,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
aOp[1].p2 = iCookie;
aOp[1].p3 = sqlite3Atoi(zRight);
aOp[1].p5 = 1;
+ if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){
+ /* Do not allow the use of PRAGMA schema_version=VALUE in defensive
+ ** mode. Change the OP_SetCookie opcode into a no-op. */
+ aOp[1].opcode = OP_Noop;
+ }
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
@@ -134766,7 +137457,12 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
#else
encoding = SQLITE_UTF8;
#endif
- sqlite3SetTextEncoding(db, encoding);
+ if( db->nVdbeActive>0 && encoding!=ENC(db) ){
+ rc = SQLITE_LOCKED;
+ goto initone_error_out;
+ }else{
+ sqlite3SetTextEncoding(db, encoding);
+ }
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){
@@ -134980,8 +137676,8 @@ static void schemaIsValid(Parse *pParse){
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
+ if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA;
sqlite3ResetOneSchema(db, iDb);
- pParse->rc = SQLITE_SCHEMA;
}
/* Close the transaction, if one was opened. */
@@ -135034,15 +137730,15 @@ SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){
assert( db->pParse==pParse );
assert( pParse->nested==0 );
#ifndef SQLITE_OMIT_SHARED_CACHE
- sqlite3DbFree(db, pParse->aTableLock);
+ if( pParse->aTableLock ) sqlite3DbNNFreeNN(db, pParse->aTableLock);
#endif
while( pParse->pCleanup ){
ParseCleanup *pCleanup = pParse->pCleanup;
pParse->pCleanup = pCleanup->pNext;
pCleanup->xCleanup(db, pCleanup->pPtr);
- sqlite3DbFreeNN(db, pCleanup);
+ sqlite3DbNNFreeNN(db, pCleanup);
}
- sqlite3DbFree(db, pParse->aLabel);
+ if( pParse->aLabel ) sqlite3DbNNFreeNN(db, pParse->aLabel);
if( pParse->pConstExpr ){
sqlite3ExprListDelete(db, pParse->pConstExpr);
}
@@ -135165,7 +137861,7 @@ static int sqlite3Prepare(
sParse.disableLookaside++;
DisableLookaside;
}
- sParse.disableVtab = (prepFlags & SQLITE_PREPARE_NO_VTAB)!=0;
+ sParse.prepFlags = prepFlags & 0xff;
/* Check to verify that it is possible to get a read lock on all
** database schemas. The inability to get a read lock indicates that
@@ -135206,7 +137902,9 @@ static int sqlite3Prepare(
}
}
- sqlite3VtabUnlockList(db);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( db->pDisconnect ) sqlite3VtabUnlockList(db);
+#endif
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
@@ -135590,6 +138288,10 @@ struct SortCtx {
} aDefer[4];
#endif
struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrPush; /* First instruction to push data into sorter */
+ int addrPushEnd; /* Last instruction that pushes data into sorter */
+#endif
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
@@ -135601,6 +138303,7 @@ struct SortCtx {
** If bFree==0, Leave the first Select object unfreed
*/
static void clearSelect(sqlite3 *db, Select *p, int bFree){
+ assert( db!=0 );
while( p ){
Select *pPrior = p->pPrior;
sqlite3ExprListDelete(db, p->pEList);
@@ -135620,7 +138323,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
sqlite3WindowUnlinkFromSelect(p->pWin);
}
#endif
- if( bFree ) sqlite3DbFreeNN(db, p);
+ if( bFree ) sqlite3DbNNFreeNN(db, p);
p = pPrior;
bFree = 1;
}
@@ -136245,6 +138948,10 @@ static void pushOntoSorter(
*/
assert( nData==1 || regData==regOrigData || regOrigData==0 );
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pSort->addrPush = sqlite3VdbeCurrentAddr(v);
+#endif
+
if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq );
regBase = regData - nPrefixReg;
@@ -136345,6 +139052,9 @@ static void pushOntoSorter(
sqlite3VdbeChangeP2(v, iSkip,
pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v));
}
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pSort->addrPushEnd = sqlite3VdbeCurrentAddr(v)-1;
+#endif
}
/*
@@ -137026,9 +139736,10 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
*/
SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){
if( p ){
+ assert( p->db!=0 );
assert( p->nRef>0 );
p->nRef--;
- if( p->nRef==0 ) sqlite3DbFreeNN(p->db, p);
+ if( p->nRef==0 ) sqlite3DbNNFreeNN(p->db, p);
}
}
@@ -137167,6 +139878,16 @@ static void generateSortTail(
int bSeq; /* True if sorter record includes seq. no. */
int nRefKey = 0;
struct ExprList_item *aOutEx = p->pEList->a;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ 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 ":"")
+ );
+ sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd);
+ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush);
+
assert( addrBreak<0 );
if( pSort->labelBkOut ){
@@ -137213,7 +139934,7 @@ static void generateSortTail(
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
- codeOffset(v, p->iOffset, addrContinue);
+ assert( p->iLimit==0 && p->iOffset==0 );
sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
bSeq = 0;
}else{
@@ -137221,6 +139942,9 @@ static void generateSortTail(
codeOffset(v, p->iOffset, addrContinue);
iSortTab = iTab;
bSeq = 1;
+ if( p->iOffset>0 ){
+ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1);
+ }
}
for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
@@ -137276,6 +140000,7 @@ static void generateSortTail(
VdbeComment((v, "%s", aOutEx[i].zEName));
}
}
+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
switch( eDest ){
case SRT_Table:
case SRT_EphemTab: {
@@ -137337,6 +140062,7 @@ static void generateSortTail(
}else{
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v);
}
+ sqlite3VdbeScanStatusRange(v, addrExplain, sqlite3VdbeCurrentAddr(v)-1, -1);
if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
sqlite3VdbeResolveLabel(v, addrBreak);
}
@@ -137345,9 +140071,6 @@ static void generateSortTail(
** Return a pointer to a string containing the 'declaration type' of the
** expression pExpr. The string may be treated as static by the caller.
**
-** Also try to estimate the size of the returned value and return that
-** result in *pEstWidth.
-**
** The declaration type is the exact datatype definition extracted from the
** original CREATE TABLE statement if the expression is a column. The
** declaration type for a ROWID field is INTEGER. Exactly when an expression
@@ -137611,7 +140334,7 @@ SQLITE_PRIVATE void sqlite3GenerateColumnNames(
if( pParse->colNamesSet ) return;
/* Column names are determined by the left-most term of a compound select */
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
- SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
+ TREETRACE(0x80,pParse,pSelect,("generating column names\n"));
pTabList = pSelect->pSrc;
pEList = pSelect->pEList;
assert( v!=0 );
@@ -137711,7 +140434,7 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
*pnCol = nCol;
*paCol = aCol;
- for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){
+ for(i=0, pCol=aCol; i<nCol && !pParse->nErr; i++, pCol++){
struct ExprList_item *pX = &pEList->a[i];
struct ExprList_item *pCollide;
/* Get an appropriate name for the column
@@ -137761,7 +140484,10 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
if( zName[j]==':' ) nName = j;
}
zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
- if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
+ sqlite3ProgressCheck(pParse);
+ if( cnt>3 ){
+ sqlite3_randomness(sizeof(cnt), &cnt);
+ }
}
pCol->zCnName = zName;
pCol->hName = sqlite3StrIHash(zName);
@@ -137774,71 +140500,105 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
}
}
sqlite3HashClear(&ht);
- if( db->mallocFailed ){
+ if( pParse->nErr ){
for(j=0; j<i; j++){
sqlite3DbFree(db, aCol[j].zCnName);
}
sqlite3DbFree(db, aCol);
*paCol = 0;
*pnCol = 0;
- return SQLITE_NOMEM_BKPT;
+ return pParse->rc;
}
return SQLITE_OK;
}
/*
-** Add type and collation information to a column list based on
-** a SELECT statement.
-**
-** The column list presumably came from selectColumnNamesFromExprList().
-** The column list has only names, not types or collations. This
-** routine goes through and adds the types and collations.
+** pTab is a transient Table object that represents a subquery of some
+** kind (maybe a parenthesized subquery in the FROM clause of a larger
+** query, or a VIEW, or a CTE). This routine computes type information
+** for that Table object based on the Select object that implements the
+** subquery. For the purposes of this routine, "type infomation" means:
**
-** This routine requires that all identifiers in the SELECT
-** statement be resolved.
+** * The datatype name, as it might appear in a CREATE TABLE statement
+** * Which collating sequence to use for the column
+** * The affinity of the column
*/
-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
- Parse *pParse, /* Parsing contexts */
- Table *pTab, /* Add column type information to this table */
- Select *pSelect, /* SELECT used to determine types and collations */
- char aff /* Default affinity for columns */
+SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
+ Parse *pParse, /* Parsing contexts */
+ Table *pTab, /* Add column type information to this table */
+ Select *pSelect, /* SELECT used to determine types and collations */
+ char aff /* Default affinity. */
){
sqlite3 *db = pParse->db;
- NameContext sNC;
Column *pCol;
CollSeq *pColl;
- int i;
+ int i,j;
Expr *p;
struct ExprList_item *a;
+ NameContext sNC;
assert( pSelect!=0 );
assert( (pSelect->selFlags & SF_Resolved)!=0 );
- assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
+ assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
+ assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
if( db->mallocFailed ) return;
+ while( pSelect->pPrior ) pSelect = pSelect->pPrior;
+ a = pSelect->pEList->a;
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
- a = pSelect->pEList->a;
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const char *zType;
- i64 n, m;
+ i64 n;
pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
- zType = columnType(&sNC, p, 0, 0, 0);
/* pCol->szEst = ... // Column size est for SELECT tables never used */
pCol->affinity = sqlite3ExprAffinity(p);
- if( zType ){
- m = sqlite3Strlen30(zType);
- n = sqlite3Strlen30(pCol->zCnName);
- pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2);
- if( pCol->zCnName ){
- memcpy(&pCol->zCnName[n+1], zType, m+1);
- pCol->colFlags |= COLFLAG_HASTYPE;
+ if( pCol->affinity<=SQLITE_AFF_NONE ){
+ pCol->affinity = aff;
+ }else if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){
+ pCol->affinity = SQLITE_AFF_FLEXNUM;
+ }
+ if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
+ int m = 0;
+ Select *pS2;
+ for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){
+ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
+ }
+ if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){
+ pCol->affinity = SQLITE_AFF_BLOB;
+ }else
+ if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){
+ pCol->affinity = SQLITE_AFF_BLOB;
+ }
+ }
+ zType = columnType(&sNC, p, 0, 0, 0);
+ if( zType==0 || pCol->affinity!=sqlite3AffinityType(zType, 0) ){
+ if( pCol->affinity==SQLITE_AFF_NUMERIC
+ || pCol->affinity==SQLITE_AFF_FLEXNUM
+ ){
+ zType = "NUM";
}else{
- testcase( pCol->colFlags & COLFLAG_HASTYPE );
+ zType = 0;
+ for(j=1; j<SQLITE_N_STDTYPE; j++){
+ if( sqlite3StdTypeAffinity[j]==pCol->affinity ){
+ zType = sqlite3StdType[j];
+ break;
+ }
+ }
+ }
+ }
+ if( zType ){
+ i64 m = sqlite3Strlen30(zType);
+ n = sqlite3Strlen30(pCol->zCnName);
+ pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2);
+ if( pCol->zCnName ){
+ memcpy(&pCol->zCnName[n+1], zType, m+1);
+ pCol->colFlags |= COLFLAG_HASTYPE;
+ }else{
+ testcase( pCol->colFlags & COLFLAG_HASTYPE );
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
}
}
- if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl ){
assert( pTab->pIndex==0 );
@@ -137872,7 +140632,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, c
pTab->zName = 0;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff);
+ sqlite3SubqueryColumnTypes(pParse, pTab, pSelect, aff);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -138397,7 +141157,7 @@ static int multiSelect(
pPrior->iLimit = p->iLimit;
pPrior->iOffset = p->iOffset;
pPrior->pLimit = p->pLimit;
- SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL left...\n"));
+ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n"));
rc = sqlite3Select(pParse, pPrior, &dest);
pPrior->pLimit = 0;
if( rc ){
@@ -138415,7 +141175,7 @@ static int multiSelect(
}
}
ExplainQueryPlan((pParse, 1, "UNION ALL"));
- SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL right...\n"));
+ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n"));
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@@ -138468,7 +141228,7 @@ static int multiSelect(
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
- SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
+ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
@@ -138488,7 +141248,7 @@ static int multiSelect(
uniondest.eDest = op;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
sqlite3SelectOpName(p->op)));
- SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
+ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
rc = sqlite3Select(pParse, p, &uniondest);
testcase( rc!=SQLITE_OK );
assert( p->pOrderBy==0 );
@@ -138549,7 +141309,7 @@ static int multiSelect(
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
- SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT left...\n"));
+ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n"));
rc = sqlite3Select(pParse, pPrior, &intersectdest);
if( rc ){
goto multi_select_end;
@@ -138566,7 +141326,7 @@ static int multiSelect(
intersectdest.iSDParm = tab2;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
sqlite3SelectOpName(p->op)));
- SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT right...\n"));
+ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n"));
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@@ -139213,10 +141973,11 @@ static int multiSelectOrderBy(
*/
sqlite3VdbeResolveLabel(v, labelEnd);
- /* Reassembly the compound query so that it will be freed correctly
- ** by the calling function */
+ /* Make arrangements to free the 2nd and subsequent arms of the compound
+ ** after the parse has finished */
if( pSplit->pPrior ){
- sqlite3SelectDelete(db, pSplit->pPrior);
+ sqlite3ParserAddCleanup(pParse,
+ (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
}
pSplit->pPrior = pPrior;
pPrior->pNext = pSplit;
@@ -139246,7 +142007,7 @@ static int multiSelectOrderBy(
** the left operands of a RIGHT JOIN. In either case, we need to potentially
** bypass the substituted expression with OP_IfNullRow.
**
-** Suppose the original expression integer constant. Even though the table
+** Suppose the original expression is an integer constant. Even though the table
** has the nullRow flag set, because the expression is an integer constant,
** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode
** that checks to see if the nullRow flag is set on the table. If the nullRow
@@ -139272,6 +142033,7 @@ typedef struct SubstContext {
int iNewTable; /* New table number */
int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
ExprList *pEList; /* Replacement expressions */
+ ExprList *pCList; /* Collation sequences for replacement expr */
} SubstContext;
/* Forward Declarations */
@@ -139313,9 +142075,10 @@ static Expr *substExpr(
#endif
{
Expr *pNew;
- Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
+ int iColumn = pExpr->iColumn;
+ Expr *pCopy = pSubst->pEList->a[iColumn].pExpr;
Expr ifNullRow;
- assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr );
+ assert( pSubst->pEList!=0 && iColumn<pSubst->pEList->nExpr );
assert( pExpr->pRight==0 );
if( sqlite3ExprIsVector(pCopy) ){
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
@@ -139326,6 +142089,7 @@ static Expr *substExpr(
ifNullRow.op = TK_IF_NULL_ROW;
ifNullRow.pLeft = pCopy;
ifNullRow.iTable = pSubst->iNewTable;
+ ifNullRow.iColumn = -99;
ifNullRow.flags = EP_IfNullRow;
pCopy = &ifNullRow;
}
@@ -139352,11 +142116,16 @@ static Expr *substExpr(
/* Ensure that the expression now has an implicit collation sequence,
** just as it did when it was a column of a view or sub-query. */
- if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){
- CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
- pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
- (pColl ? pColl->zName : "BINARY")
+ {
+ CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
+ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
+ pSubst->pCList->a[iColumn].pExpr
);
+ if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
+ pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
+ (pColl ? pColl->zName : "BINARY")
+ );
+ }
}
ExprClearProperty(pExpr, EP_Collate);
}
@@ -139549,6 +142318,46 @@ static void renumberCursors(
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+/*
+** If pSel is not part of a compound SELECT, return a pointer to its
+** expression list. Otherwise, return a pointer to the expression list
+** of the leftmost SELECT in the compound.
+*/
+static ExprList *findLeftmostExprlist(Select *pSel){
+ while( pSel->pPrior ){
+ pSel = pSel->pPrior;
+ }
+ return pSel->pEList;
+}
+
+/*
+** Return true if any of the result-set columns in the compound query
+** have incompatible affinities on one or more arms of the compound.
+*/
+static int compoundHasDifferentAffinities(Select *p){
+ int ii;
+ ExprList *pList;
+ assert( p!=0 );
+ assert( p->pEList!=0 );
+ assert( p->pPrior!=0 );
+ pList = p->pEList;
+ for(ii=0; ii<pList->nExpr; ii++){
+ char aff;
+ Select *pSub1;
+ assert( pList->a[ii].pExpr!=0 );
+ aff = sqlite3ExprAffinity(pList->a[ii].pExpr);
+ for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){
+ assert( pSub1->pEList!=0 );
+ assert( pSub1->pEList->nExpr>ii );
+ assert( pSub1->pEList->a[ii].pExpr!=0 );
+ if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
@@ -139593,7 +142402,8 @@ static void renumberCursors(
** (3a) the subquery may not be a join and
** (3b) the FROM clause of the subquery may not contain a virtual
** table and
-** (3c) the outer query may not be an aggregate.
+** (**) Was: "The outer query may not have a GROUP BY." This case
+** is now managed correctly
** (3d) the outer query may not be DISTINCT.
** See also (26) for restrictions on RIGHT JOIN.
**
@@ -139650,6 +142460,9 @@ static void renumberCursors(
** (17g) either the subquery is the first element of the outer
** query or there are no RIGHT or FULL JOINs in any arm
** of the subquery. (This is a duplicate of condition (27b).)
+** (17h) The corresponding result set expressions in all arms of the
+** compound must have the same affinity. (See restriction (9)
+** on the push-down optimization.)
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
@@ -139701,19 +142514,13 @@ static void renumberCursors(
** See also (3) for restrictions on LEFT JOIN.
**
** (27) The subquery may not contain a FULL or RIGHT JOIN unless it
-** is the first element of the parent query. This must be the
-** the case if:
-** (27a) the subquery is not compound query, and
+** is the first element of the parent query. Two subcases:
+** (27a) the subquery is not a compound query.
** (27b) the subquery is a compound query and the RIGHT JOIN occurs
** in any arm of the compound query. (See also (17g).)
**
** (28) The subquery is not a MATERIALIZED CTE.
**
-** (29) Either the subquery is not the right-hand operand of a join with an
-** ON or USING clause nor the right-hand operand of a NATURAL JOIN, or
-** the right-most table within the FROM clause of the subquery
-** is not part of an outer join.
-**
**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
@@ -139805,16 +142612,10 @@ static int flattenSubquery(
**
** which is not at all the same thing.
**
- ** If the subquery is the right operand of a LEFT JOIN, then the outer
- ** query cannot be an aggregate. (3c) This is an artifact of the way
- ** aggregates are processed - there is no mechanism to determine if
- ** the LEFT JOIN table should be all-NULL.
- **
** See also tickets #306, #350, and #3300.
*/
if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
if( pSubSrc->nSrc>1 /* (3a) */
- || isAgg /* (3c) */
|| IsVirtual(pSubSrc->a[0].pTab) /* (3b) */
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
|| (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */
@@ -139823,15 +142624,6 @@ static int flattenSubquery(
}
isOuterJoin = 1;
}
-#ifdef SQLITE_EXTRA_IFNULLROW
- else if( iFrom>0 && !isAgg ){
- /* Setting isOuterJoin to -1 causes OP_IfNullRow opcodes to be generated for
- ** every reference to any result column from subquery in a join, even
- ** though they are not necessary. This will stress-test the OP_IfNullRow
- ** opcode. */
- isOuterJoin = -1;
- }
-#endif
assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */
if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
@@ -139841,41 +142633,13 @@ static int flattenSubquery(
return 0; /* (28) */
}
- /* Restriction (29):
- **
- ** We do not want two constraints on the same term of the flattened
- ** query where one constraint has EP_InnerON and the other is EP_OuterON.
- ** To prevent this, one or the other of the following conditions must be
- ** false:
- **
- ** (29a) The right-most entry in the FROM clause of the subquery
- ** must not be part of an outer join.
- **
- ** (29b) The subquery itself must not be the right operand of a
- ** NATURAL join or a join that as an ON or USING clause.
- **
- ** These conditions are sufficient to keep an EP_OuterON from being
- ** flattened into an EP_InnerON. Restrictions (3a) and (27a) prevent
- ** an EP_InnerON from being flattened into an EP_OuterON.
- */
- if( pSubSrc->nSrc>=2
- && (pSubSrc->a[pSubSrc->nSrc-1].fg.jointype & JT_OUTER)!=0
- ){
- if( (pSubitem->fg.jointype & JT_NATURAL)!=0
- || pSubitem->fg.isUsing
- || NEVER(pSubitem->u3.pOn!=0) /* ON clause already shifted into WHERE */
- || pSubitem->fg.isOn
- ){
- return 0;
- }
- }
-
/* Restriction (17): If the sub-query is a compound SELECT, then it must
** use only the UNION ALL operator. And none of the simple select queries
** that make up the compound SELECT are allowed to be aggregate or distinct
** queries.
*/
if( pSub->pPrior ){
+ int ii;
if( pSub->pOrderBy ){
return 0; /* Restriction (20) */
}
@@ -139908,7 +142672,6 @@ static int flattenSubquery(
/* Restriction (18). */
if( p->pOrderBy ){
- int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
}
@@ -139917,6 +142680,9 @@ static int flattenSubquery(
/* Restriction (23) */
if( (p->selFlags & SF_Recursive) ) return 0;
+ /* Restriction (17h) */
+ if( compoundHasDifferentAffinities(pSub) ) return 0;
+
if( pSrc->nSrc>1 ){
if( pParse->nSelect>500 ) return 0;
if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0;
@@ -139926,7 +142692,7 @@ static int flattenSubquery(
}
/***** If we reach this point, flattening is permitted. *****/
- SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
+ TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n",
pSub->selId, pSub, iFrom));
/* Authorize the subquery */
@@ -140005,7 +142771,7 @@ static int flattenSubquery(
if( pPrior ) pPrior->pNext = pNew;
pNew->pNext = p;
p->pPrior = pNew;
- SELECTTRACE(2,pParse,p,("compound-subquery flattener"
+ TREETRACE(0x4,pParse,p,("compound-subquery flattener"
" creates %u as peer\n",pNew->selId));
}
assert( pSubitem->pSelect==0 );
@@ -140150,6 +142916,7 @@ static int flattenSubquery(
x.iNewTable = iNewParent;
x.isOuterJoin = isOuterJoin;
x.pEList = pSub->pEList;
+ x.pCList = findLeftmostExprlist(pSub);
substSelect(&x, pParent, 0);
}
@@ -140169,7 +142936,7 @@ static int flattenSubquery(
pSub->pLimit = 0;
}
- /* Recompute the SrcList_item.colUsed masks for the flattened
+ /* Recompute the SrcItem.colUsed masks for the flattened
** tables. */
for(i=0; i<nSubSrc; i++){
recomputeColumnsUsed(pParent, &pSrc->a[i+iFrom]);
@@ -140184,8 +142951,8 @@ static int flattenSubquery(
sqlite3SelectDelete(db, pSub1);
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
+ if( sqlite3TreeTrace & 0x4 ){
+ TREETRACE(0x4,pParse,p,("After flattening:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -140559,6 +143326,15 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
** be materialized. (This restriction is implemented in the calling
** routine.)
**
+** (8) If the subquery is a compound that uses UNION, INTERSECT,
+** or EXCEPT, then all of the result set columns for all arms of
+** the compound must use the BINARY collating sequence.
+**
+** (9) If the subquery is a compound, then all arms of the compound must
+** have the same affinity. (This is the same as restriction (17h)
+** for query flattening.)
+**
+**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
@@ -140574,16 +143350,44 @@ static int pushDownWhereTerms(
if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0;
-#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSubq->pPrior ){
Select *pSel;
+ int notUnionAll = 0;
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
+ u8 op = pSel->op;
+ assert( op==TK_ALL || op==TK_SELECT
+ || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
+ if( op!=TK_ALL && op!=TK_SELECT ){
+ notUnionAll = 1;
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSel->pWin ) return 0; /* restriction (6b) */
+#endif
+ }
+ if( compoundHasDifferentAffinities(pSubq) ){
+ return 0; /* restriction (9) */
+ }
+ if( notUnionAll ){
+ /* If any of the compound arms are connected using UNION, INTERSECT,
+ ** or EXCEPT, then we must ensure that none of the columns use a
+ ** non-BINARY collating sequence. */
+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){
+ int ii;
+ const ExprList *pList = pSel->pEList;
+ assert( pList!=0 );
+ for(ii=0; ii<pList->nExpr; ii++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr);
+ if( !sqlite3IsBinary(pColl) ){
+ return 0; /* Restriction (8) */
+ }
+ }
+ }
}
}else{
+#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
- }
#endif
+ }
#ifdef SQLITE_DEBUG
/* Only the first term of a compound can have a WITH clause. But make
@@ -140632,6 +143436,7 @@ static int pushDownWhereTerms(
x.iNewTable = pSrc->iCursor;
x.isOuterJoin = 0;
x.pEList = pSubq->pEList;
+ x.pCList = findLeftmostExprlist(pSubq);
pNew = substExpr(&x, pNew);
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
@@ -140735,6 +143540,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|| p->pSrc->nSrc!=1
|| p->pSrc->a[0].pSelect
|| pAggInfo->nFunc!=1
+ || p->pHaving
){
return 0;
}
@@ -141043,9 +143849,6 @@ static int resolveFromTermToCte(
pFrom->fg.isCte = 1;
pFrom->u2.pCteUse = pCteUse;
pCteUse->nUse++;
- if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){
- pCteUse->eM10d = M10d_Yes;
- }
/* Check if this is a recursive CTE. */
pRecTerm = pSel = pFrom->pSelect;
@@ -141155,9 +143958,9 @@ SQLITE_PRIVATE void sqlite3SelectPopWith(Walker *pWalker, Select *p){
#endif
/*
-** The SrcList_item structure passed as the second argument represents a
+** The SrcItem structure passed as the second argument represents a
** sub-query in the FROM clause of a SELECT statement. This function
-** allocates and populates the SrcList_item.pTab object. If successful,
+** allocates and populates the SrcItem.pTab object. If successful,
** SQLITE_OK is returned. Otherwise, if an OOM error is encountered,
** SQLITE_NOMEM.
*/
@@ -141585,8 +144388,8 @@ static int selectExpander(Walker *pWalker, Select *p){
}
}
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,("After result-set wildcard expansion:\n"));
+ if( sqlite3TreeTrace & 0x8 ){
+ TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -141637,14 +144440,14 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo()
** interface.
**
-** For each FROM-clause subquery, add Column.zType and Column.zColl
-** information to the Table structure that represents the result set
-** of that subquery.
+** For each FROM-clause subquery, add Column.zType, Column.zColl, and
+** Column.affinity information to the Table structure that represents
+** the result set of that subquery.
**
** The Table structure that represents the result set was constructed
-** by selectExpander() but the type and collation information was omitted
-** at that point because identifiers had not yet been resolved. This
-** routine is called after identifier resolution.
+** by selectExpander() but the type and collation and affinity information
+** was omitted at that point because identifiers had not yet been resolved.
+** This routine is called after identifier resolution.
*/
static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Parse *pParse;
@@ -141664,9 +144467,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
/* A sub-query in the FROM clause of a SELECT */
Select *pSel = pFrom->pSelect;
if( pSel ){
- while( pSel->pPrior ) pSel = pSel->pPrior;
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
- SQLITE_AFF_NONE);
+ sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
}
}
}
@@ -141721,6 +144522,173 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
sqlite3SelectAddTypeInfo(pParse, p);
}
+#if TREETRACE_ENABLED
+/*
+** Display all information about an AggInfo object
+*/
+static void printAggInfo(AggInfo *pAggInfo){
+ int ii;
+ for(ii=0; ii<pAggInfo->nColumn; ii++){
+ struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
+ sqlite3DebugPrintf(
+ "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d"
+ " iSorterColumn=%d %s\n",
+ ii, pCol->pTab ? pCol->pTab->zName : "NULL",
+ pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii,
+ pCol->iSorterColumn,
+ ii>=pAggInfo->nAccumulator ? "" : " Accumulator");
+ sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
+ }
+ for(ii=0; ii<pAggInfo->nFunc; ii++){
+ sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
+ ii, pAggInfo->iFirstReg+pAggInfo->nColumn+ii);
+ sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
+ }
+}
+#endif /* TREETRACE_ENABLED */
+
+/*
+** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[]
+** entries for columns that are arguments to aggregate functions but which
+** are not otherwise used.
+**
+** The aCol[] entries in AggInfo prior to nAccumulator are columns that
+** are referenced outside of aggregate functions. These might be columns
+** that are part of the GROUP by clause, for example. Other database engines
+** would throw an error if there is a column reference that is not in the
+** GROUP BY clause and that is not part of an aggregate function argument.
+** But SQLite allows this.
+**
+** The aCol[] entries beginning with the aCol[nAccumulator] and following
+** are column references that are used exclusively as arguments to
+** aggregate functions. This routine is responsible for computing
+** (or recomputing) those aCol[] entries.
+*/
+static void analyzeAggFuncArgs(
+ AggInfo *pAggInfo,
+ NameContext *pNC
+){
+ int i;
+ assert( pAggInfo!=0 );
+ assert( pAggInfo->iFirstReg==0 );
+ pNC->ncFlags |= NC_InAggFunc;
+ for(i=0; i<pAggInfo->nFunc; i++){
+ Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
+ assert( ExprUseXList(pExpr) );
+ sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ assert( !IsWindowFunc(pExpr) );
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter);
+ }
+#endif
+ }
+ pNC->ncFlags &= ~NC_InAggFunc;
+}
+
+/*
+** An index on expressions is being used in the inner loop of an
+** aggregate query with a GROUP BY clause. This routine attempts
+** to adjust the AggInfo object to take advantage of index and to
+** perhaps use the index as a covering index.
+**
+*/
+static void optimizeAggregateUseOfIndexedExpr(
+ Parse *pParse, /* Parsing context */
+ Select *pSelect, /* The SELECT statement being processed */
+ AggInfo *pAggInfo, /* The aggregate info */
+ NameContext *pNC /* Name context used to resolve agg-func args */
+){
+ assert( pAggInfo->iFirstReg==0 );
+ pAggInfo->nColumn = pAggInfo->nAccumulator;
+ if( ALWAYS(pAggInfo->nSortingColumn>0) ){
+ if( pAggInfo->nColumn==0 ){
+ pAggInfo->nSortingColumn = 0;
+ }else{
+ pAggInfo->nSortingColumn =
+ pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1;
+ }
+ }
+ analyzeAggFuncArgs(pAggInfo, pNC);
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ IndexedExpr *pIEpr;
+ TREETRACE(0x20, pParse, pSelect,
+ ("AggInfo (possibly) adjusted for Indexed Exprs\n"));
+ sqlite3TreeViewSelect(0, pSelect, 0);
+ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
+ printf("data-cursor=%d index={%d,%d}\n",
+ pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol);
+ sqlite3TreeViewExpr(0, pIEpr->pExpr, 0);
+ }
+ printAggInfo(pAggInfo);
+ }
+#else
+ UNUSED_PARAMETER(pSelect);
+ UNUSED_PARAMETER(pParse);
+#endif
+}
+
+/*
+** Walker callback for aggregateConvertIndexedExprRefToColumn().
+*/
+static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){
+ AggInfo *pAggInfo;
+ struct AggInfo_col *pCol;
+ UNUSED_PARAMETER(pWalker);
+ if( pExpr->pAggInfo==0 ) return WRC_Continue;
+ if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue;
+ if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue;
+ if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue;
+ pAggInfo = pExpr->pAggInfo;
+ assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
+ pCol = &pAggInfo->aCol[pExpr->iAgg];
+ pExpr->op = TK_AGG_COLUMN;
+ pExpr->iTable = pCol->iTable;
+ pExpr->iColumn = pCol->iColumn;
+ return WRC_Prune;
+}
+
+/*
+** Convert every pAggInfo->aFunc[].pExpr such that any node within
+** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN
+** opcode.
+*/
+static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){
+ int i;
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = aggregateIdxEprRefToColCallback;
+ for(i=0; i<pAggInfo->nFunc; i++){
+ sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr);
+ }
+}
+
+
+/*
+** Allocate a block of registers so that there is one register for each
+** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first
+** register in this block is stored in pAggInfo->iFirstReg.
+**
+** This routine may only be called once for each AggInfo object. Prior
+** to calling this routine:
+**
+** * The aCol[] and aFunc[] arrays may be modified
+** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used
+**
+** After clling this routine:
+**
+** * The aCol[] and aFunc[] arrays are fixed
+** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used
+**
+*/
+static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){
+ assert( pAggInfo!=0 );
+ assert( pAggInfo->iFirstReg==0 );
+ pAggInfo->iFirstReg = pParse->nMem + 1;
+ pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc;
+}
+
/*
** Reset the aggregate accumulator.
**
@@ -141734,24 +144702,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
int i;
struct AggInfo_func *pFunc;
int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
+ assert( pAggInfo->iFirstReg>0 );
assert( pParse->db->pParse==pParse );
assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
if( nReg==0 ) return;
if( pParse->nErr ) return;
-#ifdef SQLITE_DEBUG
- /* Verify that all AggInfo registers are within the range specified by
- ** AggInfo.mnReg..AggInfo.mxReg */
- assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
- for(i=0; i<pAggInfo->nColumn; i++){
- assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg
- && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg );
- }
- for(i=0; i<pAggInfo->nFunc; i++){
- assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg
- && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg );
- }
-#endif
- sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg,
+ pAggInfo->iFirstReg+nReg-1);
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pFExpr;
@@ -141783,15 +144740,16 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
- sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0);
+ sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
+ pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
}
}
/*
-** Update the accumulator memory cells for an aggregate based on
-** the current cursor position.
+** Generate code that will update the accumulator memory cells for an
+** aggregate based on the current cursor position.
**
** If regAcc is non-zero and there are no min() or max() aggregates
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
@@ -141811,6 +144769,8 @@ static void updateAccumulator(
struct AggInfo_func *pF;
struct AggInfo_col *pC;
+ assert( pAggInfo->iFirstReg>0 );
+ if( pParse->nErr ) return;
pAggInfo->directMode = 1;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
int nArg;
@@ -141871,7 +144831,7 @@ static void updateAccumulator(
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
}
- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem);
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
@@ -141886,7 +144846,7 @@ static void updateAccumulator(
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
}
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
- sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem);
+ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i));
}
pAggInfo->directMode = 0;
@@ -141982,26 +144942,31 @@ static void havingToWhere(Parse *pParse, Select *p){
sqlite3WalkExpr(&sWalker, p->pHaving);
#if TREETRACE_ENABLED
if( sWalker.eCode && (sqlite3TreeTrace & 0x100)!=0 ){
- SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
+ TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}
/*
-** Check to see if the pThis entry of pTabList is a self-join of a prior view.
-** If it is, then return the SrcList_item for the prior view. If it is not,
-** then return 0.
+** Check to see if the pThis entry of pTabList is a self-join of another view.
+** Search FROM-clause entries in the range of iFirst..iEnd, including iFirst
+** but stopping before iEnd.
+**
+** If pThis is a self-join, then return the SrcItem for the first other
+** instance of that view found. If pThis is not a self-join then return 0.
*/
static SrcItem *isSelfJoinView(
SrcList *pTabList, /* Search for self-joins in this FROM clause */
- SrcItem *pThis /* Search for prior reference to this subquery */
+ SrcItem *pThis, /* Search for prior reference to this subquery */
+ int iFirst, int iEnd /* Range of FROM-clause entries to search. */
){
SrcItem *pItem;
assert( pThis->pSelect!=0 );
if( pThis->pSelect->selFlags & SF_PushDown ) return 0;
- for(pItem = pTabList->a; pItem<pThis; pItem++){
+ while( iFirst<iEnd ){
Select *pS1;
+ pItem = &pTabList->a[iFirst++];
if( pItem->pSelect==0 ) continue;
if( pItem->fg.viaCoroutine ) continue;
if( pItem->zName==0 ) continue;
@@ -142070,6 +145035,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
assert( ExprUseXList(pExpr) );
if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */
if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */
+ if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */
pSub = p->pSrc->a[0].pSelect;
if( pSub==0 ) return 0; /* The FROM is a subquery */
if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */
@@ -142114,8 +145080,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
p->selFlags &= ~SF_Aggregate;
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x400 ){
- SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
+ if( sqlite3TreeTrace & 0x200 ){
+ TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -142147,6 +145113,68 @@ static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){
}
/*
+** Return TRUE (non-zero) if the i-th entry in the pTabList SrcList can
+** be implemented as a co-routine. The i-th entry is guaranteed to be
+** a subquery.
+**
+** The subquery is implemented as a co-routine if all of the following are
+** true:
+**
+** (1) The subquery will likely be implemented in the outer loop of
+** the query. This will be the case if any one of the following
+** conditions hold:
+** (a) The subquery is the only term in the FROM clause
+** (b) The subquery is the left-most term and a CROSS JOIN or similar
+** requires it to be the outer loop
+** (c) All of the following are true:
+** (i) The subquery is the left-most subquery in the FROM clause
+** (ii) There is nothing that would prevent the subquery from
+** being used as the outer loop if the sqlite3WhereBegin()
+** routine nominates it to that position.
+** (iii) The query is not a UPDATE ... FROM
+** (2) The subquery is not a CTE that should be materialized because
+** (a) the AS MATERIALIZED keyword is used, or
+** (b) the CTE is used multiple times and does not have the
+** NOT MATERIALIZED keyword
+** (3) The subquery is not part of a left operand for a RIGHT JOIN
+** (4) The SQLITE_Coroutine optimization disable flag is not set
+** (5) The subquery is not self-joined
+*/
+static int fromClauseTermCanBeCoroutine(
+ Parse *pParse, /* Parsing context */
+ SrcList *pTabList, /* FROM clause */
+ int i, /* Which term of the FROM clause holds the subquery */
+ int selFlags /* Flags on the SELECT statement */
+){
+ SrcItem *pItem = &pTabList->a[i];
+ if( pItem->fg.isCte ){
+ const CteUse *pCteUse = pItem->u2.pCteUse;
+ if( pCteUse->eM10d==M10d_Yes ) return 0; /* (2a) */
+ if( pCteUse->nUse>=2 && pCteUse->eM10d!=M10d_No ) return 0; /* (2b) */
+ }
+ if( pTabList->a[0].fg.jointype & JT_LTORJ ) return 0; /* (3) */
+ if( OptimizationDisabled(pParse->db, SQLITE_Coroutines) ) return 0; /* (4) */
+ if( isSelfJoinView(pTabList, pItem, i+1, pTabList->nSrc)!=0 ){
+ return 0; /* (5) */
+ }
+ if( i==0 ){
+ if( pTabList->nSrc==1 ) return 1; /* (1a) */
+ if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */
+ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */
+ return 1;
+ }
+ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */
+ while( 1 /*exit-by-break*/ ){
+ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */
+ if( i==0 ) break;
+ i--;
+ pItem--;
+ if( pItem->pSelect!=0 ) return 0; /* (1c-i) */
+ }
+ return 1;
+}
+
+/*
** Generate code for the SELECT statement given in the p argument.
**
** The results are returned according to the SelectDest structure.
@@ -142191,8 +145219,8 @@ SQLITE_PRIVATE int sqlite3Select(
assert( db->mallocFailed==0 );
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
#if TREETRACE_ENABLED
- SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
- if( sqlite3TreeTrace & 0x10100 ){
+ TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain));
+ if( sqlite3TreeTrace & 0x10000 ){
if( (sqlite3TreeTrace & 0x10001)==0x10000 ){
sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d",
__FILE__, __LINE__);
@@ -142212,8 +145240,8 @@ SQLITE_PRIVATE int sqlite3Select(
/* All of these destinations are also able to ignore the ORDER BY clause */
if( p->pOrderBy ){
#if TREETRACE_ENABLED
- SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
- if( sqlite3TreeTrace & 0x100 ){
+ TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
+ if( sqlite3TreeTrace & 0x800 ){
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
@@ -142233,8 +145261,8 @@ SQLITE_PRIVATE int sqlite3Select(
assert( db->mallocFailed==0 );
assert( p->pEList!=0 );
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x104 ){
- SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
+ if( sqlite3TreeTrace & 0x10 ){
+ TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -142275,8 +145303,8 @@ SQLITE_PRIVATE int sqlite3Select(
goto select_end;
}
#if TREETRACE_ENABLED
- if( p->pWin && (sqlite3TreeTrace & 0x108)!=0 ){
- SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
+ if( p->pWin && (sqlite3TreeTrace & 0x40)!=0 ){
+ TREETRACE(0x40,pParse,p, ("after window rewrite:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -142307,7 +145335,7 @@ SQLITE_PRIVATE int sqlite3Select(
&& sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
){
- SELECTTRACE(0x100,pParse,p,
+ TREETRACE(0x1000,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
assert( pItem->iCursor>=0 );
@@ -142363,7 +145391,7 @@ SQLITE_PRIVATE int sqlite3Select(
&& (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */
&& OptimizationEnabled(db, SQLITE_OmitOrderBy)
){
- SELECTTRACE(0x100,pParse,p,
+ TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
sqlite3ParserAddCleanup(pParse,
(void(*)(sqlite3*,void*))sqlite3ExprListDelete,
@@ -142418,8 +145446,8 @@ SQLITE_PRIVATE int sqlite3Select(
if( p->pPrior ){
rc = multiSelect(pParse, p, pDest);
#if TREETRACE_ENABLED
- SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
- if( (sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+ TREETRACE(0x400,pParse,p,("end compound-select processing\n"));
+ if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -142439,13 +145467,13 @@ SQLITE_PRIVATE int sqlite3Select(
&& propagateConstants(pParse, p)
){
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
+ if( sqlite3TreeTrace & 0x2000 ){
+ TREETRACE(0x2000,pParse,p,("After constant propagation:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}else{
- SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
+ TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n"));
}
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
@@ -142518,36 +145546,23 @@ SQLITE_PRIVATE int sqlite3Select(
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem)
){
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,
+ if( sqlite3TreeTrace & 0x4000 ){
+ TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
- SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
+ TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
}
zSavedAuthContext = pParse->zAuthContext;
pParse->zAuthContext = pItem->zName;
/* Generate code to implement the subquery
- **
- ** The subquery is implemented as a co-routine if all of the following are
- ** true:
- **
- ** (1) the subquery is guaranteed to be the outer loop (so that
- ** it does not need to be computed more than once), and
- ** (2) the subquery is not a CTE that should be materialized
- ** (3) the subquery is not part of a left operand for a RIGHT JOIN
*/
- if( i==0
- && (pTabList->nSrc==1
- || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) /* (1) */
- && (pItem->fg.isCte==0 || pItem->u2.pCteUse->eM10d!=M10d_Yes) /* (2) */
- && (pTabList->a[0].fg.jointype & JT_LTORJ)==0 /* (3) */
- ){
+ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
*/
@@ -142578,7 +145593,7 @@ SQLITE_PRIVATE int sqlite3Select(
VdbeComment((v, "%!S", pItem));
}
pSub->nSelectRow = pCteUse->nRowEst;
- }else if( (pPrior = isSelfJoinView(pTabList, pItem))!=0 ){
+ }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){
/* This view has already been materialized by a prior entry in
** this same FROM clause. Reuse it. */
if( pPrior->addrFillSub ){
@@ -142592,6 +145607,9 @@ SQLITE_PRIVATE int sqlite3Select(
** the same view can reuse the materialization. */
int topAddr;
int onceAddr = 0;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExplain;
+#endif
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp0(v, OP_Goto);
@@ -142607,12 +145625,14 @@ SQLITE_PRIVATE int sqlite3Select(
VdbeNoopComment((v, "materialize %!S", pItem));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- ExplainQueryPlan((pParse, 1, "MATERIALIZE %!S", pItem));
+
+ ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem));
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1);
VdbeComment((v, "end %!S", pItem));
+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
sqlite3VdbeJumpHere(v, topAddr);
sqlite3ClearTempRegCache(pParse);
if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){
@@ -142638,8 +145658,8 @@ SQLITE_PRIVATE int sqlite3Select(
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x400 ){
- SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
+ if( sqlite3TreeTrace & 0x8000 ){
+ TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -142675,8 +145695,8 @@ SQLITE_PRIVATE int sqlite3Select(
sDistinct.isTnct = 2;
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x400 ){
- SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
+ if( sqlite3TreeTrace & 0x20000 ){
+ TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -142728,7 +145748,7 @@ SQLITE_PRIVATE int sqlite3Select(
if( (p->selFlags & SF_FixedLimit)==0 ){
p->nSelectRow = 320; /* 4 billion rows */
}
- computeLimitRegisters(pParse, p, iEnd);
+ if( p->pLimit ) computeLimitRegisters(pParse, p, iEnd);
if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
sSort.sortFlags |= SORTFLAG_UseSorter;
@@ -142762,7 +145782,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Begin the database scan. */
- SELECTTRACE(1,pParse,p,("WhereBegin\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
p->pEList, p, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
@@ -142779,7 +145799,7 @@ SQLITE_PRIVATE int sqlite3Select(
sSort.pOrderBy = 0;
}
}
- SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
@@ -142818,7 +145838,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* End the database scan loop.
*/
- SELECTTRACE(1,pParse,p,("WhereEnd\n"));
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
}
}else{
@@ -142899,12 +145919,14 @@ SQLITE_PRIVATE int sqlite3Select(
goto select_end;
}
pAggInfo->selId = p->selId;
+#ifdef SQLITE_DEBUG
+ pAggInfo->pSelect = p;
+#endif
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
sNC.uNC.pAggInfo = pAggInfo;
VVA_ONLY( sNC.ncFlags = NC_UAggInfo; )
- pAggInfo->mnReg = pParse->nMem+1;
pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
pAggInfo->pGroupBy = pGroupBy;
sqlite3ExprAnalyzeAggList(&sNC, pEList);
@@ -142925,40 +145947,17 @@ SQLITE_PRIVATE int sqlite3Select(
}else{
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
- for(i=0; i<pAggInfo->nFunc; i++){
- Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
- assert( ExprUseXList(pExpr) );
- sNC.ncFlags |= NC_InAggFunc;
- sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
-#ifndef SQLITE_OMIT_WINDOWFUNC
- assert( !IsWindowFunc(pExpr) );
- if( ExprHasProperty(pExpr, EP_WinFunc) ){
- sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter);
- }
-#endif
- sNC.ncFlags &= ~NC_InAggFunc;
- }
- pAggInfo->mxReg = pParse->nMem;
+ analyzeAggFuncArgs(pAggInfo, &sNC);
if( db->mallocFailed ) goto select_end;
#if TREETRACE_ENABLED
- if( sqlite3TreeTrace & 0x400 ){
- int ii;
- SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
sqlite3TreeViewSelect(0, p, 0);
if( minMaxFlag ){
sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
}
- for(ii=0; ii<pAggInfo->nColumn; ii++){
- sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
- ii, pAggInfo->aCol[ii].iMem);
- sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
- }
- for(ii=0; ii<pAggInfo->nFunc; ii++){
- sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
- ii, pAggInfo->aFunc[ii].iMem);
- sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
- }
+ printAggInfo(pAggInfo);
}
#endif
@@ -143027,17 +146026,21 @@ SQLITE_PRIVATE int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- SELECTTRACE(1,pParse,p,("WhereBegin\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct,
- 0, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY)
+ p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY)
| (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0
);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDistinct);
goto select_end;
}
+ if( pParse->pIdxEpr ){
+ optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC);
+ }
+ assignAggregateRegisters(pParse, pAggInfo);
eDist = sqlite3WhereIsDistinct(pWInfo);
- SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
@@ -143072,21 +146075,21 @@ SQLITE_PRIVATE int sqlite3Select(
regBase = sqlite3GetTempRange(pParse, nCol);
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
j = nGroupBy;
+ pAggInfo->directMode = 1;
for(i=0; i<pAggInfo->nColumn; i++){
struct AggInfo_col *pCol = &pAggInfo->aCol[i];
if( pCol->iSorterColumn>=j ){
- int r1 = j + regBase;
- sqlite3ExprCodeGetColumnOfTable(v,
- pCol->pTab, pCol->iTable, pCol->iColumn, r1);
+ sqlite3ExprCode(pParse, pCol->pCExpr, j + regBase);
j++;
}
}
+ pAggInfo->directMode = 0;
regRecord = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
- SELECTTRACE(1,pParse,p,("WhereEnd\n"));
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
sortOut = sqlite3GetTempReg(pParse);
@@ -143096,6 +146099,23 @@ SQLITE_PRIVATE int sqlite3Select(
pAggInfo->useSortingIdx = 1;
}
+ /* If there are entries in pAgggInfo->aFunc[] that contain subexpressions
+ ** that are indexed (and that were previously identified and tagged
+ ** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions
+ ** must now be converted into a TK_AGG_COLUMN node so that the value
+ ** is correctly pulled from the index rather than being recomputed. */
+ if( pParse->pIdxEpr ){
+ aggregateConvertIndexedExprRefToColumn(pAggInfo);
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20, pParse, p,
+ ("AggInfo function expressions converted to reference index\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ printAggInfo(pAggInfo);
+ }
+#endif
+ }
+
/* If the index or temporary table used by the GROUP BY sort
** will naturally deliver rows in the order required by the ORDER BY
** clause, cancel the ephemeral table open coded earlier.
@@ -143164,7 +146184,7 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
VdbeCoverage(v);
}else{
- SELECTTRACE(1,pParse,p,("WhereEnd\n"));
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
}
@@ -143274,7 +146294,8 @@ SQLITE_PRIVATE int sqlite3Select(
if( pKeyInfo ){
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
}
- sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem);
+ assignAggregateRegisters(pParse, pAggInfo);
+ sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0));
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
explainSimpleCount(pParse, pTab, pBest);
}else{
@@ -143310,6 +146331,7 @@ SQLITE_PRIVATE int sqlite3Select(
pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList;
distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0;
}
+ assignAggregateRegisters(pParse, pAggInfo);
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
@@ -143326,13 +146348,13 @@ SQLITE_PRIVATE int sqlite3Select(
assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
- SELECTTRACE(1,pParse,p,("WhereBegin\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
- pDistinct, 0, minMaxFlag|distFlag, 0);
+ pDistinct, p, minMaxFlag|distFlag, 0);
if( pWInfo==0 ){
goto select_end;
}
- SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
eDist = sqlite3WhereIsDistinct(pWInfo);
updateAccumulator(pParse, regAcc, pAggInfo, eDist);
if( eDist!=WHERE_DISTINCT_NOOP ){
@@ -143346,7 +146368,7 @@ SQLITE_PRIVATE int sqlite3Select(
if( minMaxFlag ){
sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
}
- SELECTTRACE(1,pParse,p,("WhereEnd\n"));
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, pAggInfo);
}
@@ -143368,8 +146390,6 @@ SQLITE_PRIVATE int sqlite3Select(
** and send them to the callback one by one.
*/
if( sSort.pOrderBy ){
- explainTempTable(pParse,
- sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
assert( p->pEList==pEList );
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
@@ -143393,7 +146413,7 @@ select_end:
if( pAggInfo && !db->mallocFailed ){
for(i=0; i<pAggInfo->nColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
- assert( pExpr!=0 );
+ if( pExpr==0 ) continue;
assert( pExpr->pAggInfo==pAggInfo );
assert( pExpr->iAgg==i );
}
@@ -143407,8 +146427,8 @@ select_end:
#endif
#if TREETRACE_ENABLED
- SELECTTRACE(0x1,pParse,p,("end processing\n"));
- if( (sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+ TREETRACE(0x1,pParse,p,("end processing\n"));
+ if( (sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -143682,7 +146702,7 @@ SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
if( pTrig->pTabSchema==pTab->pSchema
&& pTrig->table
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
- && pTrig->pTabSchema!=pTmpSchema
+ && (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning)
){
pTrig->pNext = pList;
pList = pTrig;
@@ -143972,6 +146992,23 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
Vdbe *v;
char *z;
+ /* If this is a new CREATE TABLE statement, and if shadow tables
+ ** are read-only, and the trigger makes a change to a shadow table,
+ ** then raise an error - do not allow the trigger to be created. */
+ if( sqlite3ReadOnlyShadowTables(db) ){
+ TriggerStep *pStep;
+ for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){
+ if( pStep->zTarget!=0
+ && sqlite3ShadowTableName(db, pStep->zTarget)
+ ){
+ sqlite3ErrorMsg(pParse,
+ "trigger \"%s\" may not write to shadow table \"%s\"",
+ pTrig->zName, pStep->zTarget);
+ goto triggerfinish_cleanup;
+ }
+ }
+ }
+
/* Make an entry in the sqlite_schema table */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto triggerfinish_cleanup;
@@ -144795,7 +147832,7 @@ static TriggerPrg *codeRowTrigger(
sSubParse.zAuthContext = pTrigger->zName;
sSubParse.eTriggerOp = pTrigger->op;
sSubParse.nQueryLoop = pParse->nQueryLoop;
- sSubParse.disableVtab = pParse->disableVtab;
+ sSubParse.prepFlags = pParse->prepFlags;
v = sqlite3GetVdbe(&sSubParse);
if( v ){
@@ -145141,11 +148178,14 @@ static void updateVirtualTable(
** it has been converted into REAL.
*/
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
+ Column *pCol;
assert( pTab!=0 );
- if( !IsView(pTab) ){
+ assert( pTab->nCol>i );
+ pCol = &pTab->aCol[i];
+ if( pCol->iDflt ){
sqlite3_value *pValue = 0;
u8 enc = ENC(sqlite3VdbeDb(v));
- Column *pCol = &pTab->aCol[i];
+ assert( !IsView(pTab) );
VdbeComment((v, "%s.%s", pTab->zName, pCol->zCnName));
assert( i<pTab->nCol );
sqlite3ValueFromExpr(sqlite3VdbeDb(v),
@@ -145156,7 +148196,7 @@ SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
}
}
#ifndef SQLITE_OMIT_FLOATING_POINT
- if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){
+ if( pCol->affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
}
#endif
@@ -145342,7 +148382,8 @@ static void updateFromSelect(
}
}
pSelect = sqlite3SelectNew(pParse, pList,
- pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UFSrcCheck|SF_IncludeHidden, pLimit2
+ pSrc, pWhere2, pGrp, 0, pOrderBy2,
+ SF_UFSrcCheck|SF_IncludeHidden|SF_UpdateFrom, pLimit2
);
if( pSelect ) pSelect->selFlags |= SF_OrderByReqd;
sqlite3SelectDestInit(&dest, eDest, iEph);
@@ -146596,6 +149637,7 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
if( pIdx->aiColumn[ii]==XN_EXPR ){
assert( pIdx->aColExpr!=0 );
assert( pIdx->aColExpr->nExpr>ii );
+ assert( pIdx->bHasExpr );
pExpr = pIdx->aColExpr->a[ii].pExpr;
if( pExpr->op!=TK_COLLATE ){
sCol[0].pLeft = pExpr;
@@ -146909,6 +149951,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum(
int nDb; /* Number of attached databases */
const char *zDbMain; /* Schema name of database to vacuum */
const char *zOut; /* Name of output file */
+ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */
if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
@@ -146980,12 +150023,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum(
goto end_of_vacuum;
}
db->mDbFlags |= DBFLAG_VacuumInto;
+
+ /* For a VACUUM INTO, the pager-flags are set to the same values as
+ ** they are for the database being vacuumed, except that PAGER_CACHESPILL
+ ** is always set. */
+ pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK);
}
nRes = sqlite3BtreeGetRequestedReserve(pMain);
sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
- sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
+ sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL);
/* Begin a transaction and take an exclusive lock on the main database
** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
@@ -147369,10 +150417,10 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
pVTab->nRef--;
if( pVTab->nRef==0 ){
sqlite3_vtab *p = pVTab->pVtab;
- sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
if( p ){
p->pModule->xDisconnect(p);
}
+ sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
sqlite3DbFree(db, pVTab);
}
}
@@ -147498,7 +150546,8 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){
*/
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){
assert( IsVirtual(p) );
- if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p);
+ assert( db!=0 );
+ if( db->pnBytesFreed==0 ) vtabDisconnectAll(0, p);
if( p->u.vtab.azArg ){
int i;
for(i=0; i<p->u.vtab.nArg; i++){
@@ -148298,7 +151347,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
if( pExpr->op!=TK_COLUMN ) return pDef;
assert( ExprUseYTab(pExpr) );
pTab = pExpr->y.pTab;
- if( pTab==0 ) return pDef;
+ if( NEVER(pTab==0) ) return pDef;
if( !IsVirtual(pTab) ) return pDef;
pVtab = sqlite3GetVTable(db, pTab)->pVtab;
assert( pVtab!=0 );
@@ -148905,7 +151954,7 @@ struct WhereAndInfo {
** between VDBE cursor numbers and bits of the bitmasks in WhereTerm.
**
** The VDBE cursor numbers are small integers contained in
-** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE
+** SrcItem.iCursor and Expr.iTable fields. For any given WHERE
** clause, the cursor numbers might not begin with 0 and they might
** contain gaps in the numbering sequence. But we want to make maximum
** use of the bits in our bitmasks. This structure provides a mapping
@@ -148977,20 +152026,6 @@ struct WhereLoopBuilder {
#endif
/*
-** Each instance of this object records a change to a single node
-** in an expression tree to cause that node to point to a column
-** of an index rather than an expression or a virtual column. All
-** such transformations need to be undone at the end of WHERE clause
-** processing.
-*/
-typedef struct WhereExprMod WhereExprMod;
-struct WhereExprMod {
- WhereExprMod *pNext; /* Next translation on a list of them all */
- Expr *pExpr; /* The Expr node that was transformed */
- Expr orig; /* Original value of the Expr node */
-};
-
-/*
** The WHERE clause processing routine has two halves. The
** first part does the start of the WHERE loop and the second
** half does the tail of the WHERE loop. An instance of
@@ -149005,10 +152040,10 @@ struct WhereInfo {
SrcList *pTabList; /* List of tables in the join */
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
ExprList *pResultSet; /* Result set of the query */
+#if WHERETRACE_ENABLED
Expr *pWhere; /* The complete WHERE clause */
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- Select *pLimit; /* Used to access LIMIT expr/registers for vtabs */
#endif
+ Select *pSelect; /* The entire SELECT statement containing WHERE */
int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
@@ -149027,7 +152062,6 @@ struct WhereInfo {
int iTop; /* The very beginning of the WHERE loop */
int iEndWhere; /* End of the WHERE clause itself */
WhereLoop *pLoops; /* List of all WhereLoop objects */
- WhereExprMod *pExprMods; /* Expression modifications */
WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
WhereClause sWC; /* Decomposition of the WHERE clause */
@@ -149175,6 +152209,8 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */
#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */
#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */
+#define WHERE_VIEWSCAN 0x02000000 /* A full-scan of a VIEW or subquery */
+#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */
#endif /* !defined(SQLITE_WHEREINT_H) */
@@ -149431,6 +152467,8 @@ SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter(
zMsg = sqlite3StrAccumFinish(&str);
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
+
+ sqlite3VdbeScanStatus(v, sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0);
return ret;
}
#endif /* SQLITE_OMIT_EXPLAIN */
@@ -149453,14 +152491,27 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
){
const char *zObj = 0;
WhereLoop *pLoop = pLvl->pWLoop;
- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
+ int wsFlags = pLoop->wsFlags;
+ int viaCoroutine = 0;
+
+ if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
zObj = pLoop->u.btree.pIndex->zName;
}else{
zObj = pSrclist->a[pLvl->iFrom].zName;
+ viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine;
}
sqlite3VdbeScanStatus(
v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
);
+
+ if( viaCoroutine==0 ){
+ if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){
+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
+ }
+ if( wsFlags & WHERE_INDEXED ){
+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
+ }
+ }
}
#endif
@@ -149520,7 +152571,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
pTerm->wtFlags |= TERM_CODED;
}
#ifdef WHERETRACE_ENABLED
- if( sqlite3WhereTrace & 0x20000 ){
+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){
sqlite3DebugPrintf("DISABLE-");
sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a)));
}
@@ -149635,68 +152686,75 @@ static Expr *removeUnindexableInClauseTerms(
Expr *pX /* The IN expression to be reduced */
){
sqlite3 *db = pParse->db;
+ Select *pSelect; /* Pointer to the SELECT on the RHS */
Expr *pNew;
pNew = sqlite3ExprDup(db, pX, 0);
if( db->mallocFailed==0 ){
- ExprList *pOrigRhs; /* Original unmodified RHS */
- ExprList *pOrigLhs; /* Original unmodified LHS */
- ExprList *pRhs = 0; /* New RHS after modifications */
- ExprList *pLhs = 0; /* New LHS after mods */
- int i; /* Loop counter */
- Select *pSelect; /* Pointer to the SELECT on the RHS */
-
- assert( ExprUseXSelect(pNew) );
- pOrigRhs = pNew->x.pSelect->pEList;
- assert( pNew->pLeft!=0 );
- assert( ExprUseXList(pNew->pLeft) );
- pOrigLhs = pNew->pLeft->x.pList;
- for(i=iEq; i<pLoop->nLTerm; i++){
- if( pLoop->aLTerm[i]->pExpr==pX ){
- int iField;
- assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
- iField = pLoop->aLTerm[i]->u.x.iField - 1;
- if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
- pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
- pOrigRhs->a[iField].pExpr = 0;
- assert( pOrigLhs->a[iField].pExpr!=0 );
- pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr);
- pOrigLhs->a[iField].pExpr = 0;
- }
- }
- sqlite3ExprListDelete(db, pOrigRhs);
- sqlite3ExprListDelete(db, pOrigLhs);
- pNew->pLeft->x.pList = pLhs;
- pNew->x.pSelect->pEList = pRhs;
- if( pLhs && pLhs->nExpr==1 ){
- /* Take care here not to generate a TK_VECTOR containing only a
- ** single value. Since the parser never creates such a vector, some
- ** of the subroutines do not handle this case. */
- Expr *p = pLhs->a[0].pExpr;
- pLhs->a[0].pExpr = 0;
- sqlite3ExprDelete(db, pNew->pLeft);
- pNew->pLeft = p;
- }
- pSelect = pNew->x.pSelect;
- if( pSelect->pOrderBy ){
- /* If the SELECT statement has an ORDER BY clause, zero the
- ** iOrderByCol variables. These are set to non-zero when an
- ** ORDER BY term exactly matches one of the terms of the
- ** result-set. Since the result-set of the SELECT statement may
- ** have been modified or reordered, these variables are no longer
- ** set correctly. Since setting them is just an optimization,
- ** it's easiest just to zero them here. */
- ExprList *pOrderBy = pSelect->pOrderBy;
- for(i=0; i<pOrderBy->nExpr; i++){
- pOrderBy->a[i].u.x.iOrderByCol = 0;
+ for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){
+ ExprList *pOrigRhs; /* Original unmodified RHS */
+ ExprList *pOrigLhs = 0; /* Original unmodified LHS */
+ ExprList *pRhs = 0; /* New RHS after modifications */
+ ExprList *pLhs = 0; /* New LHS after mods */
+ int i; /* Loop counter */
+
+ assert( ExprUseXSelect(pNew) );
+ pOrigRhs = pSelect->pEList;
+ assert( pNew->pLeft!=0 );
+ assert( ExprUseXList(pNew->pLeft) );
+ if( pSelect==pNew->x.pSelect ){
+ pOrigLhs = pNew->pLeft->x.pList;
+ }
+ for(i=iEq; i<pLoop->nLTerm; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ int iField;
+ assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
+ iField = pLoop->aLTerm[i]->u.x.iField - 1;
+ if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
+ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
+ pOrigRhs->a[iField].pExpr = 0;
+ if( pOrigLhs ){
+ assert( pOrigLhs->a[iField].pExpr!=0 );
+ pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr);
+ pOrigLhs->a[iField].pExpr = 0;
+ }
+ }
+ }
+ sqlite3ExprListDelete(db, pOrigRhs);
+ if( pOrigLhs ){
+ sqlite3ExprListDelete(db, pOrigLhs);
+ pNew->pLeft->x.pList = pLhs;
+ }
+ pSelect->pEList = pRhs;
+ if( pLhs && pLhs->nExpr==1 ){
+ /* Take care here not to generate a TK_VECTOR containing only a
+ ** single value. Since the parser never creates such a vector, some
+ ** of the subroutines do not handle this case. */
+ Expr *p = pLhs->a[0].pExpr;
+ pLhs->a[0].pExpr = 0;
+ sqlite3ExprDelete(db, pNew->pLeft);
+ pNew->pLeft = p;
+ }
+ if( pSelect->pOrderBy ){
+ /* If the SELECT statement has an ORDER BY clause, zero the
+ ** iOrderByCol variables. These are set to non-zero when an
+ ** ORDER BY term exactly matches one of the terms of the
+ ** result-set. Since the result-set of the SELECT statement may
+ ** have been modified or reordered, these variables are no longer
+ ** set correctly. Since setting them is just an optimization,
+ ** it's easiest just to zero them here. */
+ ExprList *pOrderBy = pSelect->pOrderBy;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].u.x.iOrderByCol = 0;
+ }
}
- }
#if 0
- printf("For indexing, change the IN expr:\n");
- sqlite3TreeViewExpr(0, pX, 0);
- printf("Into:\n");
- sqlite3TreeViewExpr(0, pNew, 0);
+ printf("For indexing, change the IN expr:\n");
+ sqlite3TreeViewExpr(0, pX, 0);
+ printf("Into:\n");
+ sqlite3TreeViewExpr(0, pNew, 0);
#endif
+ }
}
return pNew;
}
@@ -149783,7 +152841,8 @@ static int codeEqualityTerm(
}
sqlite3ExprDelete(db, pX);
}else{
- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
+ int n = sqlite3ExprVectorSize(pX->pLeft);
+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n));
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab);
}
pX = pExpr;
@@ -150053,7 +153112,7 @@ static void whereLikeOptimizationStringFixup(
if( pTerm->wtFlags & TERM_LIKEOPT ){
VdbeOp *pOp;
assert( pLevel->iLikeRepCntr>0 );
- pOp = sqlite3VdbeGetOp(v, -1);
+ pOp = sqlite3VdbeGetLastOp(v);
assert( pOp!=0 );
assert( pOp->opcode==OP_String8
|| pTerm->pWC->pWInfo->pParse->db->mallocFailed );
@@ -150377,143 +153436,6 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
}
}
-/* An instance of the IdxExprTrans object carries information about a
-** mapping from an expression on table columns into a column in an index
-** down through the Walker.
-*/
-typedef struct IdxExprTrans {
- Expr *pIdxExpr; /* The index expression */
- int iTabCur; /* The cursor of the corresponding table */
- int iIdxCur; /* The cursor for the index */
- int iIdxCol; /* The column for the index */
- int iTabCol; /* The column for the table */
- WhereInfo *pWInfo; /* Complete WHERE clause information */
- sqlite3 *db; /* Database connection (for malloc()) */
-} IdxExprTrans;
-
-/*
-** Preserve pExpr on the WhereETrans list of the WhereInfo.
-*/
-static void preserveExpr(IdxExprTrans *pTrans, Expr *pExpr){
- WhereExprMod *pNew;
- pNew = sqlite3DbMallocRaw(pTrans->db, sizeof(*pNew));
- if( pNew==0 ) return;
- pNew->pNext = pTrans->pWInfo->pExprMods;
- pTrans->pWInfo->pExprMods = pNew;
- pNew->pExpr = pExpr;
- memcpy(&pNew->orig, pExpr, sizeof(*pExpr));
-}
-
-/* The walker node callback used to transform matching expressions into
-** a reference to an index column for an index on an expression.
-**
-** If pExpr matches, then transform it into a reference to the index column
-** that contains the value of pExpr.
-*/
-static int whereIndexExprTransNode(Walker *p, Expr *pExpr){
- IdxExprTrans *pX = p->u.pIdxTrans;
- if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){
- pExpr = sqlite3ExprSkipCollate(pExpr);
- preserveExpr(pX, pExpr);
- pExpr->affExpr = sqlite3ExprAffinity(pExpr);
- pExpr->op = TK_COLUMN;
- pExpr->iTable = pX->iIdxCur;
- pExpr->iColumn = pX->iIdxCol;
- testcase( ExprHasProperty(pExpr, EP_Unlikely) );
- ExprClearProperty(pExpr, EP_Skip|EP_Unlikely|EP_WinFunc|EP_Subrtn);
- pExpr->y.pTab = 0;
- return WRC_Prune;
- }else{
- return WRC_Continue;
- }
-}
-
-#ifndef SQLITE_OMIT_GENERATED_COLUMNS
-/* A walker node callback that translates a column reference to a table
-** into a corresponding column reference of an index.
-*/
-static int whereIndexExprTransColumn(Walker *p, Expr *pExpr){
- if( pExpr->op==TK_COLUMN ){
- IdxExprTrans *pX = p->u.pIdxTrans;
- if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){
- assert( ExprUseYTab(pExpr) && pExpr->y.pTab!=0 );
- preserveExpr(pX, pExpr);
- pExpr->affExpr = sqlite3TableColumnAffinity(pExpr->y.pTab,pExpr->iColumn);
- pExpr->iTable = pX->iIdxCur;
- pExpr->iColumn = pX->iIdxCol;
- pExpr->y.pTab = 0;
- }
- }
- return WRC_Continue;
-}
-#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
-
-/*
-** For an indexes on expression X, locate every instance of expression X
-** in pExpr and change that subexpression into a reference to the appropriate
-** column of the index.
-**
-** 2019-10-24: Updated to also translate references to a VIRTUAL column in
-** the table into references to the corresponding (stored) column of the
-** index.
-*/
-static void whereIndexExprTrans(
- Index *pIdx, /* The Index */
- int iTabCur, /* Cursor of the table that is being indexed */
- int iIdxCur, /* Cursor of the index itself */
- WhereInfo *pWInfo /* Transform expressions in this WHERE clause */
-){
- int iIdxCol; /* Column number of the index */
- ExprList *aColExpr; /* Expressions that are indexed */
- Table *pTab;
- Walker w;
- IdxExprTrans x;
- aColExpr = pIdx->aColExpr;
- if( aColExpr==0 && !pIdx->bHasVCol ){
- /* The index does not reference any expressions or virtual columns
- ** so no translations are needed. */
- return;
- }
- pTab = pIdx->pTable;
- memset(&w, 0, sizeof(w));
- w.u.pIdxTrans = &x;
- x.iTabCur = iTabCur;
- x.iIdxCur = iIdxCur;
- x.pWInfo = pWInfo;
- x.db = pWInfo->pParse->db;
- for(iIdxCol=0; iIdxCol<pIdx->nColumn; iIdxCol++){
- i16 iRef = pIdx->aiColumn[iIdxCol];
- if( iRef==XN_EXPR ){
- assert( aColExpr!=0 && aColExpr->a[iIdxCol].pExpr!=0 );
- x.pIdxExpr = aColExpr->a[iIdxCol].pExpr;
- if( sqlite3ExprIsConstant(x.pIdxExpr) ) continue;
- w.xExprCallback = whereIndexExprTransNode;
-#ifndef SQLITE_OMIT_GENERATED_COLUMNS
- }else if( iRef>=0
- && (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0
- && ((pTab->aCol[iRef].colFlags & COLFLAG_HASCOLL)==0
- || sqlite3StrICmp(sqlite3ColumnColl(&pTab->aCol[iRef]),
- sqlite3StrBINARY)==0)
- ){
- /* Check to see if there are direct references to generated columns
- ** that are contained in the index. Pulling the generated column
- ** out of the index is an optimization only - the main table is always
- ** available if the index cannot be used. To avoid unnecessary
- ** complication, omit this optimization if the collating sequence for
- ** the column is non-standard */
- x.iTabCol = iRef;
- w.xExprCallback = whereIndexExprTransColumn;
-#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
- }else{
- continue;
- }
- x.iIdxCol = iIdxCol;
- sqlite3WalkExpr(&w, pWInfo->pWhere);
- sqlite3WalkExprList(&w, pWInfo->pOrderBy);
- sqlite3WalkExprList(&w, pWInfo->pResultSet);
- }
-}
-
/*
** The pTruth expression is always true because it is the WHERE clause
** a partial index that is driving a query loop. Look through all of the
@@ -150582,6 +153504,8 @@ static SQLITE_NOINLINE void filterPullDown(
testcase( pTerm->wtFlags & TERM_VIRTUAL );
regRowid = sqlite3GetTempReg(pParse);
regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid);
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_MustBeInt, regRowid, addrNxt);
+ VdbeCoverage(pParse->pVdbe);
sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter,
addrNxt, regRowid, 1);
VdbeCoverage(pParse->pVdbe);
@@ -150641,13 +153565,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
bRev = (pWInfo->revMask>>iLevel)&1;
VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
-#if WHERETRACE_ENABLED /* 0x20800 */
- if( sqlite3WhereTrace & 0x800 ){
+#if WHERETRACE_ENABLED /* 0x4001 */
+ if( sqlite3WhereTrace & 0x1 ){
sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n",
iLevel, pWInfo->nLevel, (u64)notReady, pLevel->iFrom);
- sqlite3WhereLoopPrint(pLoop, pWC);
+ if( sqlite3WhereTrace & 0x1000 ){
+ sqlite3WhereLoopPrint(pLoop, pWC);
+ }
}
- if( sqlite3WhereTrace & 0x20000 ){
+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){
if( iLevel==0 ){
sqlite3DebugPrintf("WHERE clause being coded:\n");
sqlite3TreeViewExpr(0, pWInfo->pWhere, 0);
@@ -150733,9 +153659,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
&& pLoop->u.vtab.bOmitOffset
){
assert( pTerm->eOperator==WO_AUX );
- assert( pWInfo->pLimit!=0 );
- assert( pWInfo->pLimit->iOffset>0 );
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pLimit->iOffset);
+ assert( pWInfo->pSelect!=0 );
+ assert( pWInfo->pSelect->iOffset>0 );
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pSelect->iOffset);
VdbeComment((v,"Zero OFFSET counter"));
}
}
@@ -150843,6 +153769,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
addrNxt = pLevel->addrNxt;
if( pLevel->regFilter ){
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
+ VdbeCoverage(v);
sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
iRowidReg, 1);
VdbeCoverage(v);
@@ -151194,6 +154122,11 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** guess. */
addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan,
(pIdx->aiRowLogEst[0]+9)/10);
+ if( pRangeStart ){
+ sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeChangeP2(v, addrSeekScan, sqlite3VdbeCurrentAddr(v)+1);
+ addrSeekScan = 0;
+ }
VdbeCoverage(v);
}
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
@@ -151269,8 +154202,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
nConstraint++;
}
- sqlite3DbFree(db, zStartAff);
- sqlite3DbFree(db, zEndAff);
+ if( zStartAff ) sqlite3DbNNFreeNN(db, zStartAff);
+ if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff);
/* Top of the loop body */
if( pLevel->p2==0 ) pLevel->p2 = sqlite3VdbeCurrentAddr(v);
@@ -151332,27 +154265,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
if( pLevel->iLeftJoin==0 ){
- /* If pIdx is an index on one or more expressions, then look through
- ** all the expressions in pWInfo and try to transform matching expressions
- ** into reference to index columns. Also attempt to translate references
- ** to virtual columns in the table into references to (stored) columns
- ** of the index.
- **
- ** Do not do this for the RHS of a LEFT JOIN. This is because the
- ** expression may be evaluated after OP_NullRow has been executed on
- ** the cursor. In this case it is important to do the full evaluation,
- ** as the result of the expression may not be NULL, even if all table
- ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a
- **
- ** Also, do not do this when processing one index an a multi-index
- ** OR clause, since the transformation will become invalid once we
- ** move forward to the next index.
- ** https://sqlite.org/src/info/4e8e4857d32d401f
- */
- if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 ){
- whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
- }
-
/* If a partial index is driving the loop, try to eliminate WHERE clause
** terms from the query that must be true due to the WHERE clause of
** the partial index.
@@ -151465,7 +154377,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int nNotReady; /* The number of notReady tables */
SrcItem *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3StackAllocRaw(db,
+ pOrTab = sqlite3DbMallocRawNN(db,
sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
if( pOrTab==0 ) return notReady;
pOrTab->nAlloc = (u8)(nNotReady + 1);
@@ -151585,7 +154497,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
/* Loop through table entries that match term pOrTerm. */
ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1));
- WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
+ WHERETRACE(0xffffffff, ("Subplan for OR-clause:\n"));
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0,
WHERE_OR_SUBCLAUSE, iCovCur);
assert( pSubWInfo || pParse->nErr );
@@ -151718,7 +154630,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
assert( pLevel->op==OP_Return );
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
- if( pWInfo->nLevel>1 ){ sqlite3StackFree(db, pOrTab); }
+ if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); }
if( !untestedTerms ) disableTerm(pLevel, pTerm);
}else
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
@@ -151822,12 +154734,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
#endif
}
-#ifdef WHERETRACE_ENABLED /* 0xffff */
+#ifdef WHERETRACE_ENABLED /* 0xffffffff */
if( sqlite3WhereTrace ){
VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d",
pWC->nTerm-j, pTerm, iLoop));
}
- if( sqlite3WhereTrace & 0x800 ){
+ if( sqlite3WhereTrace & 0x4000 ){
sqlite3DebugPrintf("Coding auxiliary constraint:\n");
sqlite3WhereTermPrint(pTerm, pWC->nTerm-j);
}
@@ -151856,8 +154768,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pTerm->leftCursor!=iCur ) continue;
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue;
pE = pTerm->pExpr;
-#ifdef WHERETRACE_ENABLED /* 0x800 */
- if( sqlite3WhereTrace & 0x800 ){
+#ifdef WHERETRACE_ENABLED /* 0x4001 */
+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){
sqlite3DebugPrintf("Coding transitive constraint:\n");
sqlite3WhereTermPrint(pTerm, pWC->nTerm-j);
}
@@ -151972,13 +154884,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
}
-#if WHERETRACE_ENABLED /* 0x20800 */
- if( sqlite3WhereTrace & 0x20000 ){
+#if WHERETRACE_ENABLED /* 0x4001 */
+ if( sqlite3WhereTrace & 0x4000 ){
sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n",
iLevel);
sqlite3WhereClausePrint(pWC);
}
- if( sqlite3WhereTrace & 0x800 ){
+ if( sqlite3WhereTrace & 0x1 ){
sqlite3DebugPrintf("End Coding level %d: notReady=%llx\n",
iLevel, (u64)pLevel->notReady);
}
@@ -152346,7 +155258,7 @@ static int isLikeOrGlob(
if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|| (ALWAYS( ExprUseYTab(pLeft) )
- && pLeft->y.pTab
+ && ALWAYS(pLeft->y.pTab)
&& IsVirtual(pLeft->y.pTab)) /* Might be numeric */
){
int isNum;
@@ -152463,8 +155375,7 @@ static int isAuxiliaryVtabOperator(
** MATCH(expression,vtab_column)
*/
pCol = pList->a[1].pExpr;
- assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) );
- testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 );
+ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) );
if( ExprIsVtab(pCol) ){
for(i=0; i<ArraySize(aOp); i++){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
@@ -152489,7 +155400,7 @@ static int isAuxiliaryVtabOperator(
*/
pCol = pList->a[0].pExpr;
assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) );
- testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 );
+ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) );
if( ExprIsVtab(pCol) ){
sqlite3_vtab *pVtab;
sqlite3_module *pMod;
@@ -152514,13 +155425,12 @@ static int isAuxiliaryVtabOperator(
int res = 0;
Expr *pLeft = pExpr->pLeft;
Expr *pRight = pExpr->pRight;
- assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) );
- testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 );
+ assert( pLeft->op!=TK_COLUMN || (ExprUseYTab(pLeft) && pLeft->y.pTab!=0) );
if( ExprIsVtab(pLeft) ){
res++;
}
- assert( pRight==0 || pRight->op!=TK_COLUMN || ExprUseYTab(pRight) );
- testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 );
+ assert( pRight==0 || pRight->op!=TK_COLUMN
+ || (ExprUseYTab(pRight) && pRight->y.pTab!=0) );
if( pRight && ExprIsVtab(pRight) ){
res++;
SWAP(Expr*, pLeft, pRight);
@@ -153056,35 +155966,40 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
*/
static SQLITE_NOINLINE int exprMightBeIndexed2(
SrcList *pFrom, /* The FROM clause */
- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
int *aiCurCol, /* Write the referenced table cursor and column here */
- Expr *pExpr /* An operand of a comparison operator */
+ Expr *pExpr, /* An operand of a comparison operator */
+ int j /* Start looking with the j-th pFrom entry */
){
Index *pIdx;
int i;
int iCur;
- for(i=0; mPrereq>1; i++, mPrereq>>=1){}
- iCur = pFrom->a[i].iCursor;
- for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->aColExpr==0 ) continue;
- for(i=0; i<pIdx->nKeyCol; i++){
- if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
- if( sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
- aiCurCol[0] = iCur;
- aiCurCol[1] = XN_EXPR;
- return 1;
+ do{
+ iCur = pFrom->a[j].iCursor;
+ for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->aColExpr==0 ) continue;
+ for(i=0; i<pIdx->nKeyCol; i++){
+ if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
+ assert( pIdx->bHasExpr );
+ if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0
+ && pExpr->op!=TK_STRING
+ ){
+ aiCurCol[0] = iCur;
+ aiCurCol[1] = XN_EXPR;
+ return 1;
+ }
}
}
- }
+ }while( ++j < pFrom->nSrc );
return 0;
}
static int exprMightBeIndexed(
SrcList *pFrom, /* The FROM clause */
- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
int *aiCurCol, /* Write the referenced table cursor & column here */
Expr *pExpr, /* An operand of a comparison operator */
int op /* The specific comparison operator */
){
+ int i;
+
/* If this expression is a vector to the left or right of a
** inequality constraint (>, <, >= or <=), perform the processing
** on the first element of the vector. */
@@ -153094,7 +156009,6 @@ static int exprMightBeIndexed(
if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
assert( ExprUseXList(pExpr) );
pExpr = pExpr->x.pList->a[0].pExpr;
-
}
if( pExpr->op==TK_COLUMN ){
@@ -153102,9 +156016,16 @@ static int exprMightBeIndexed(
aiCurCol[1] = pExpr->iColumn;
return 1;
}
- if( mPrereq==0 ) return 0; /* No table references */
- if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */
- return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
+
+ for(i=0; i<pFrom->nSrc; i++){
+ Index *pIdx;
+ for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->aColExpr ){
+ return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i);
+ }
+ }
+ }
+ return 0;
}
@@ -153230,7 +156151,7 @@ static void exprAnalyze(
pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr;
}
- if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){
+ if( exprMightBeIndexed(pSrc, aiCurCol, pLeft, op) ){
pTerm->leftCursor = aiCurCol[0];
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
pTerm->u.x.leftColumn = aiCurCol[1];
@@ -153238,7 +156159,7 @@ static void exprAnalyze(
}
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
if( pRight
- && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op)
+ && exprMightBeIndexed(pSrc, aiCurCol, pRight, op)
&& !ExprHasProperty(pRight, EP_FixedCol)
){
WhereTerm *pNew;
@@ -153449,7 +156370,6 @@ static void exprAnalyze(
transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags);
testcase( idxNew1==0 );
- exprAnalyze(pSrc, pWC, idxNew1);
pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
@@ -153457,6 +156377,7 @@ static void exprAnalyze(
transferJoinMarkings(pNewExpr2, pExpr);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags);
testcase( idxNew2==0 );
+ exprAnalyze(pSrc, pWC, idxNew1);
exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm];
if( isComplete ){
@@ -153513,7 +156434,7 @@ static void exprAnalyze(
&& pTerm->u.x.iField==0
&& pExpr->pLeft->op==TK_VECTOR
&& ALWAYS( ExprUseXSelect(pExpr) )
- && pExpr->x.pSelect->pPrior==0
+ && (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values))
#ifndef SQLITE_OMIT_WINDOWFUNC
&& pExpr->x.pSelect->pWin==0
#endif
@@ -153682,9 +156603,9 @@ static void whereAddLimitExpr(
** exist only so that they may be passed to the xBestIndex method of the
** single virtual table in the FROM clause of the SELECT.
*/
-SQLITE_PRIVATE void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
- assert( p==0 || (p->pGroupBy==0 && (p->selFlags & SF_Aggregate)==0) );
- if( (p && p->pLimit) /* 1 */
+SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
+ assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */
+ if( p->pGroupBy==0
&& (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */
&& (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */
){
@@ -153701,6 +156622,13 @@ SQLITE_PRIVATE void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
assert( pWC->a[ii].eOperator==WO_ROWVAL );
continue;
}
+ if( pWC->a[ii].nChild ){
+ /* If this term has child terms, then they are also part of the
+ ** pWC->a[] array. So this term can be ignored, as a LIMIT clause
+ ** will only be added if each of the child terms passes the
+ ** (leftCursor==iCsr) test below. */
+ continue;
+ }
if( pWC->a[ii].leftCursor!=iCsr ) return;
}
@@ -154001,7 +156929,7 @@ SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
** block sorting is required.
*/
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
- return pWInfo->nOBSat;
+ return pWInfo->nOBSat<0 ? 0 : pWInfo->nOBSat;
}
/*
@@ -154639,7 +157567,7 @@ static void translateColumnToCopy(
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
int i;
- if( !sqlite3WhereTrace ) return;
+ if( (sqlite3WhereTrace & 0x10)==0 ) return;
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
@@ -154659,7 +157587,7 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
}
static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
int i;
- if( !sqlite3WhereTrace ) return;
+ if( (sqlite3WhereTrace & 0x10)==0 ) return;
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n",
i,
@@ -154677,6 +157605,43 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
#define whereTraceIndexInfoOutputs(A)
#endif
+/*
+** We know that pSrc is an operand of an outer join. Return true if
+** pTerm is a constraint that is compatible with that join.
+**
+** pTerm must be EP_OuterON if pSrc is the right operand of an
+** outer join. pTerm can be either EP_OuterON or EP_InnerON if pSrc
+** is the left operand of a RIGHT join.
+**
+** See https://sqlite.org/forum/forumpost/206d99a16dd9212f
+** for an example of a WHERE clause constraints that may not be used on
+** the right table of a RIGHT JOIN because the constraint implies a
+** not-NULL condition on the left table of the RIGHT JOIN.
+*/
+static int constraintCompatibleWithOuterJoin(
+ const WhereTerm *pTerm, /* WHERE clause term to check */
+ const SrcItem *pSrc /* Table we are trying to access */
+){
+ assert( (pSrc->fg.jointype&(JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ); /* By caller */
+ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
+ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
+ testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
+ testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
+ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
+ || pTerm->pExpr->w.iJoin != pSrc->iCursor
+ ){
+ return 0;
+ }
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
+ && ExprHasProperty(pTerm->pExpr, EP_InnerON)
+ ){
+ return 0;
+ }
+ return 1;
+}
+
+
+
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
** Return TRUE if the WHERE clause term pTerm is of a form where it
@@ -154692,16 +157657,10 @@ static int termCanDriveIndex(
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
assert( (pSrc->fg.jointype & JT_RIGHT)==0 );
- if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
- testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
- testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
- if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
- || pTerm->pExpr->w.iJoin != pSrc->iCursor
- ){
- return 0; /* See tag-20191211-001 */
- }
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc)
+ ){
+ return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */
}
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
@@ -154715,6 +157674,57 @@ static int termCanDriveIndex(
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+/*
+** Argument pIdx represents an automatic index that the current statement
+** will create and populate. Add an OP_Explain with text of the form:
+**
+** CREATE AUTOMATIC INDEX ON <table>(<cols>) [WHERE <expr>]
+**
+** This is only required if sqlite3_stmt_scanstatus() is enabled, to
+** associate an SQLITE_SCANSTAT_NCYCLE and SQLITE_SCANSTAT_NLOOP
+** values with. In order to avoid breaking legacy code and test cases,
+** the OP_Explain is not added if this is an EXPLAIN QUERY PLAN command.
+*/
+static void explainAutomaticIndex(
+ Parse *pParse,
+ Index *pIdx, /* Automatic index to explain */
+ int bPartial, /* True if pIdx is a partial index */
+ int *pAddrExplain /* OUT: Address of OP_Explain */
+){
+ if( pParse->explain!=2 ){
+ Table *pTab = pIdx->pTable;
+ const char *zSep = "";
+ char *zText = 0;
+ int ii = 0;
+ sqlite3_str *pStr = sqlite3_str_new(pParse->db);
+ sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName);
+ assert( pIdx->nColumn>1 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID );
+ for(ii=0; ii<(pIdx->nColumn-1); ii++){
+ const char *zName = 0;
+ int iCol = pIdx->aiColumn[ii];
+
+ zName = pTab->aCol[iCol].zCnName;
+ sqlite3_str_appendf(pStr, "%s%s", zSep, zName);
+ zSep = ", ";
+ }
+ zText = sqlite3_str_finish(pStr);
+ if( zText==0 ){
+ sqlite3OomFault(pParse->db);
+ }else{
+ *pAddrExplain = sqlite3VdbeExplain(
+ pParse, 0, "%s)%s", zText, (bPartial ? " WHERE <expr>" : "")
+ );
+ sqlite3_free(zText);
+ }
+ }
+}
+#else
+# define explainAutomaticIndex(a,b,c,d)
+#endif
+
/*
** Generate code to construct the Index object for an automatic index
** and to set up the WhereLevel object pLevel so that the code generator
@@ -154750,6 +157760,9 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
SrcItem *pTabItem; /* FROM clause term being indexed */
int addrCounter = 0; /* Address where integer counter is initialized */
int regBase; /* Array of registers where record is assembled */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExp = 0; /* Address of OP_Explain */
+#endif
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -154873,6 +157886,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
pIdx->azColl[n] = sqlite3StrBINARY;
/* Create the automatic index */
+ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp);
assert( pLevel->iIdxCur>=0 );
pLevel->iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
@@ -154908,6 +157922,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0,
regBase, pLoop->u.btree.nEq);
}
+ sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
@@ -154928,6 +157943,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
/* Jump here when skipping the initialization */
sqlite3VdbeJumpHere(v, addrInit);
+ sqlite3VdbeScanStatusRange(v, addrExp, addrExp, -1);
end_auto_index_create:
sqlite3ExprDelete(pParse->db, pPartial);
@@ -155113,22 +158129,10 @@ static sqlite3_index_info *allocateIndexInfo(
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
assert( pTerm->u.x.leftColumn>=XN_ROWID );
assert( pTerm->u.x.leftColumn<pTab->nCol );
-
- /* tag-20191211-002: WHERE-clause constraints are not useful to the
- ** right-hand table of a LEFT JOIN nor to the either table of a
- ** RIGHT JOIN. See tag-20191211-001 for the
- ** equivalent restriction for ordinary tables. */
- if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT );
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
- testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) );
- testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
- if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
- || pTerm->pExpr->w.iJoin != pSrc->iCursor
- ){
- continue;
- }
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc)
+ ){
+ continue;
}
nTerm++;
pTerm->wtFlags |= TERM_OK;
@@ -155479,12 +158483,12 @@ static int whereKeyStats(
if( iCol>0 ){
pRec->nField = iCol;
assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0
- || pParse->db->mallocFailed );
+ || pParse->db->mallocFailed || CORRUPT_DB );
}
if( i>0 ){
pRec->nField = nField;
assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0
- || pParse->db->mallocFailed );
+ || pParse->db->mallocFailed || CORRUPT_DB );
}
}
}
@@ -155501,7 +158505,7 @@ static int whereKeyStats(
** is larger than all samples in the array. */
tRowcnt iUpper, iGap;
if( i>=pIdx->nSample ){
- iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]);
+ iUpper = pIdx->nRowEst0;
}else{
iUpper = aSample[i].anLt[iCol];
}
@@ -155657,7 +158661,7 @@ static int whereRangeSkipScanEst(
int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff));
pLoop->nOut -= nAdjust;
*pbDone = 1;
- WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
+ WHERETRACE(0x20, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
nLower, nUpper, nAdjust*-1, pLoop->nOut));
}
@@ -155835,7 +158839,7 @@ static int whereRangeScanEst(
if( nNew<nOut ){
nOut = nNew;
}
- WHERETRACE(0x10, ("STAT4 range scan: %u..%u est=%d\n",
+ WHERETRACE(0x20, ("STAT4 range scan: %u..%u est=%d\n",
(u32)iLower, (u32)iUpper, nOut));
}
}else{
@@ -155868,7 +158872,7 @@ static int whereRangeScanEst(
if( nNew<nOut ) nOut = nNew;
#if defined(WHERETRACE_ENABLED)
if( pLoop->nOut>nOut ){
- WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n",
+ WHERETRACE(0x20,("Range scan lowers nOut from %d to %d\n",
pLoop->nOut, nOut));
}
#endif
@@ -155933,7 +158937,7 @@ static int whereEqualScanEst(
pBuilder->nRecValid = nEq;
whereKeyStats(pParse, p, pRec, 0, a);
- WHERETRACE(0x10,("equality scan regions %s(%d): %d\n",
+ WHERETRACE(0x20,("equality scan regions %s(%d): %d\n",
p->zName, nEq-1, (int)a[1]));
*pnRow = a[1];
@@ -155981,9 +158985,9 @@ static int whereInScanEst(
}
if( rc==SQLITE_OK ){
- if( nRowEst > nRow0 ) nRowEst = nRow0;
+ if( nRowEst > (tRowcnt)nRow0 ) nRowEst = nRow0;
*pnRow = nRowEst;
- WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst));
+ WHERETRACE(0x20,("IN row estimate: est=%d\n", nRowEst));
}
assert( pBuilder->nRecValid==nRecValid );
return rc;
@@ -156092,7 +159096,7 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm);
}
sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
- if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){
+ if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
int i;
for(i=0; i<p->nLTerm; i++){
sqlite3WhereTermPrint(p->aLTerm[i], i);
@@ -156130,12 +159134,18 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
}
/*
-** Deallocate internal memory used by a WhereLoop object
+** Deallocate internal memory used by a WhereLoop object. Leave the
+** object in an initialized state, as if it had been newly allocated.
*/
static void whereLoopClear(sqlite3 *db, WhereLoop *p){
- if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFreeNN(db, p->aLTerm);
+ if( p->aLTerm!=p->aLTermSpace ){
+ sqlite3DbFreeNN(db, p->aLTerm);
+ p->aLTerm = p->aLTermSpace;
+ p->nLSlot = ArraySize(p->aLTermSpace);
+ }
whereLoopClearUnion(db, p);
- whereLoopInit(p);
+ p->nLTerm = 0;
+ p->wsFlags = 0;
}
/*
@@ -156159,7 +159169,9 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
*/
static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
whereLoopClearUnion(db, pTo);
- if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
+ if( pFrom->nLTerm > pTo->nLSlot
+ && whereLoopResize(db, pTo, pFrom->nLTerm)
+ ){
memset(pTo, 0, WHERE_LOOP_XFER_SZ);
return SQLITE_NOMEM_BKPT;
}
@@ -156177,8 +159189,9 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
** Delete a WhereLoop object
*/
static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
+ assert( db!=0 );
whereLoopClear(db, p);
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
/*
@@ -156186,30 +159199,19 @@ static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
*/
static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
assert( pWInfo!=0 );
+ assert( db!=0 );
sqlite3WhereClauseClear(&pWInfo->sWC);
while( pWInfo->pLoops ){
WhereLoop *p = pWInfo->pLoops;
pWInfo->pLoops = p->pNextLoop;
whereLoopDelete(db, p);
}
- assert( pWInfo->pExprMods==0 );
while( pWInfo->pMemToFree ){
WhereMemBlock *pNext = pWInfo->pMemToFree->pNext;
- sqlite3DbFreeNN(db, pWInfo->pMemToFree);
+ sqlite3DbNNFreeNN(db, pWInfo->pMemToFree);
pWInfo->pMemToFree = pNext;
}
- sqlite3DbFreeNN(db, pWInfo);
-}
-
-/* Undo all Expr node modifications
-*/
-static void whereUndoExprMods(WhereInfo *pWInfo){
- while( pWInfo->pExprMods ){
- WhereExprMod *p = pWInfo->pExprMods;
- pWInfo->pExprMods = p->pNext;
- memcpy(p->pExpr, &p->orig, sizeof(p->orig));
- sqlite3DbFree(pWInfo->pParse->db, p);
- }
+ sqlite3DbNNFreeNN(db, pWInfo);
}
/*
@@ -156558,6 +159560,7 @@ static void whereLoopOutputAdjust(
if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
}
if( j<0 ){
+ sqlite3ProgressCheck(pWC->pWInfo->pParse);
if( pLoop->maskSelf==pTerm->prereqAll ){
/* If there are extra terms in the WHERE clause not used by an index
** that depend only on the table being scanned, and that will tend to
@@ -156725,7 +159728,10 @@ static int whereLoopAddBtreeIndex(
WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
pNew = pBuilder->pNew;
- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ assert( db->mallocFailed==0 || pParse->nErr>0 );
+ if( pParse->nErr ){
+ return pParse->rc;
+ }
WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n",
pProbe->pTable->zName,pProbe->zName,
pNew->u.btree.nEq, pNew->nSkip, pNew->rRun));
@@ -156776,32 +159782,11 @@ static int whereLoopAddBtreeIndex(
** to mix with a lower range bound from some other source */
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
- /* tag-20191211-001: Do not allow constraints from the WHERE clause to
- ** be used by the right table of a LEFT JOIN nor by the left table of a
- ** RIGHT JOIN. Only constraints in the ON clause are allowed.
- ** See tag-20191211-002 for the vtab equivalent.
- **
- ** 2022-06-06: See https://sqlite.org/forum/forumpost/206d99a16dd9212f
- ** for an example of a WHERE clause constraints that may not be used on
- ** the right table of a RIGHT JOIN because the constraint implies a
- ** not-NULL condition on the left table of the RIGHT JOIN.
- **
- ** 2022-06-10: The same condition applies to termCanDriveIndex() above.
- ** https://sqlite.org/forum/forumpost/51e6959f61
- */
- if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT );
- testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
- testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
- testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
- if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
- || pTerm->pExpr->w.iJoin != pSrc->iCursor
- ){
- continue;
- }
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc)
+ ){
+ continue;
}
-
if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){
pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE;
}else{
@@ -156812,7 +159797,11 @@ static int whereLoopAddBtreeIndex(
pNew->u.btree.nBtm = saved_nBtm;
pNew->u.btree.nTop = saved_nTop;
pNew->nLTerm = saved_nLTerm;
- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ if( pNew->nLTerm>=pNew->nLSlot
+ && whereLoopResize(db, pNew, pNew->nLTerm+1)
+ ){
+ break; /* OOM while trying to enlarge the pNew->aLTerm array */
+ }
pNew->aLTerm[pNew->nLTerm++] = pTerm;
pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
@@ -156905,38 +159894,39 @@ static int whereLoopAddBtreeIndex(
if( scan.iEquiv>1 ) pNew->wsFlags |= WHERE_TRANSCONS;
}else if( eOp & WO_ISNULL ){
pNew->wsFlags |= WHERE_COLUMN_NULL;
- }else if( eOp & (WO_GT|WO_GE) ){
- testcase( eOp & WO_GT );
- testcase( eOp & WO_GE );
- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
- pNew->u.btree.nBtm = whereRangeVectorLen(
- pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
- );
- pBtm = pTerm;
- pTop = 0;
- if( pTerm->wtFlags & TERM_LIKEOPT ){
- /* Range constraints that come from the LIKE optimization are
- ** always used in pairs. */
- pTop = &pTerm[1];
- assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
- assert( pTop->wtFlags & TERM_LIKEOPT );
- assert( pTop->eOperator==WO_LT );
- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
- pNew->aLTerm[pNew->nLTerm++] = pTop;
- pNew->wsFlags |= WHERE_TOP_LIMIT;
- pNew->u.btree.nTop = 1;
- }
- }else{
- assert( eOp & (WO_LT|WO_LE) );
- testcase( eOp & WO_LT );
- testcase( eOp & WO_LE );
- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
- pNew->u.btree.nTop = whereRangeVectorLen(
+ }else{
+ int nVecLen = whereRangeVectorLen(
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
);
- pTop = pTerm;
- pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
- pNew->aLTerm[pNew->nLTerm-2] : 0;
+ if( eOp & (WO_GT|WO_GE) ){
+ testcase( eOp & WO_GT );
+ testcase( eOp & WO_GE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pNew->u.btree.nBtm = nVecLen;
+ pBtm = pTerm;
+ pTop = 0;
+ if( pTerm->wtFlags & TERM_LIKEOPT ){
+ /* Range constraints that come from the LIKE optimization are
+ ** always used in pairs. */
+ pTop = &pTerm[1];
+ assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
+ assert( pTop->wtFlags & TERM_LIKEOPT );
+ assert( pTop->eOperator==WO_LT );
+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ pNew->aLTerm[pNew->nLTerm++] = pTop;
+ pNew->wsFlags |= WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = 1;
+ }
+ }else{
+ assert( eOp & (WO_LT|WO_LE) );
+ testcase( eOp & WO_LT );
+ testcase( eOp & WO_LE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = nVecLen;
+ pTop = pTerm;
+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
+ pNew->aLTerm[pNew->nLTerm-2] : 0;
+ }
}
/* At this point pNew->nOut is set to the number of rows expected to
@@ -156988,7 +159978,7 @@ static int whereLoopAddBtreeIndex(
&& pNew->nOut+10 > pProbe->aiRowLogEst[0]
){
#if WHERETRACE_ENABLED /* 0x01 */
- if( sqlite3WhereTrace & 0x01 ){
+ if( sqlite3WhereTrace & 0x20 ){
sqlite3DebugPrintf(
"STAT4 determines term has low selectivity:\n");
sqlite3WhereTermPrint(pTerm, 999);
@@ -157025,9 +160015,17 @@ static int whereLoopAddBtreeIndex(
** seek only. Then, if this is a non-covering index, add the cost of
** visiting the rows in the main table. */
assert( pSrc->pTab->szTabRow>0 );
- rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
+ if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){
+ /* The pProbe->szIdxRow is low for an IPK table since the interior
+ ** pages are small. Thuse szIdxRow gives a good estimate of seek cost.
+ ** But the leaf pages are full-size, so pProbe->szIdxRow would badly
+ ** under-estimate the scanning cost. */
+ rCostIdx = pNew->nOut + 16;
+ }else{
+ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
+ }
pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
- if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
+ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
}
ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult);
@@ -157049,6 +160047,9 @@ static int whereLoopAddBtreeIndex(
&& (pNew->u.btree.nEq<pProbe->nKeyCol ||
pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY)
){
+ if( pNew->u.btree.nEq>3 ){
+ sqlite3ProgressCheck(pParse);
+ }
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
}
pNew->nOut = saved_nOut;
@@ -157181,6 +160182,149 @@ static int whereUsablePartialIndex(
}
/*
+** pIdx is an index containing expressions. Check it see if any of the
+** expressions in the index match the pExpr expression.
+*/
+static int exprIsCoveredByIndex(
+ const Expr *pExpr,
+ const Index *pIdx,
+ int iTabCur
+){
+ int i;
+ for(i=0; i<pIdx->nColumn; i++){
+ if( pIdx->aiColumn[i]==XN_EXPR
+ && sqlite3ExprCompare(0, pExpr, pIdx->aColExpr->a[i].pExpr, iTabCur)==0
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Structure passed to the whereIsCoveringIndex Walker callback.
+*/
+typedef struct CoveringIndexCheck CoveringIndexCheck;
+struct CoveringIndexCheck {
+ Index *pIdx; /* The index */
+ int iTabCur; /* Cursor number for the corresponding table */
+ u8 bExpr; /* Uses an indexed expression */
+ u8 bUnidx; /* Uses an unindexed column not within an indexed expr */
+};
+
+/*
+** Information passed in is pWalk->u.pCovIdxCk. Call it pCk.
+**
+** If the Expr node references the table with cursor pCk->iTabCur, then
+** make sure that column is covered by the index pCk->pIdx. We know that
+** all columns less than 63 (really BMS-1) are covered, so we don't need
+** to check them. But we do need to check any column at 63 or greater.
+**
+** If the index does not cover the column, then set pWalk->eCode to
+** non-zero and return WRC_Abort to stop the search.
+**
+** If this node does not disprove that the index can be a covering index,
+** then just return WRC_Continue, to continue the search.
+**
+** If pCk->pIdx contains indexed expressions and one of those expressions
+** matches pExpr, then prune the search.
+*/
+static int whereIsCoveringIndexWalkCallback(Walker *pWalk, Expr *pExpr){
+ int i; /* Loop counter */
+ const Index *pIdx; /* The index of interest */
+ const i16 *aiColumn; /* Columns contained in the index */
+ u16 nColumn; /* Number of columns in the index */
+ CoveringIndexCheck *pCk; /* Info about this search */
+
+ pCk = pWalk->u.pCovIdxCk;
+ pIdx = pCk->pIdx;
+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) ){
+ /* if( pExpr->iColumn<(BMS-1) && pIdx->bHasExpr==0 ) return WRC_Continue;*/
+ if( pExpr->iTable!=pCk->iTabCur ) return WRC_Continue;
+ pIdx = pWalk->u.pCovIdxCk->pIdx;
+ aiColumn = pIdx->aiColumn;
+ nColumn = pIdx->nColumn;
+ for(i=0; i<nColumn; i++){
+ if( aiColumn[i]==pExpr->iColumn ) return WRC_Continue;
+ }
+ pCk->bUnidx = 1;
+ return WRC_Abort;
+ }else if( pIdx->bHasExpr
+ && exprIsCoveredByIndex(pExpr, pIdx, pWalk->u.pCovIdxCk->iTabCur) ){
+ pCk->bExpr = 1;
+ return WRC_Prune;
+ }
+ return WRC_Continue;
+}
+
+
+/*
+** pIdx is an index that covers all of the low-number columns used by
+** pWInfo->pSelect (columns from 0 through 62) or an index that has
+** expressions terms. Hence, we cannot determine whether or not it is
+** a covering index by using the colUsed bitmasks. We have to do a search
+** to see if the index is covering. This routine does that search.
+**
+** The return value is one of these:
+**
+** 0 The index is definitely not a covering index
+**
+** WHERE_IDX_ONLY The index is definitely a covering index
+**
+** WHERE_EXPRIDX The index is likely a covering index, but it is
+** difficult to determine precisely because of the
+** expressions that are indexed. Score it as a
+** covering index, but still keep the main table open
+** just in case we need it.
+**
+** This routine is an optimization. It is always safe to return zero.
+** But returning one of the other two values when zero should have been
+** returned can lead to incorrect bytecode and assertion faults.
+*/
+static SQLITE_NOINLINE u32 whereIsCoveringIndex(
+ WhereInfo *pWInfo, /* The WHERE clause context */
+ Index *pIdx, /* Index that is being tested */
+ int iTabCur /* Cursor for the table being indexed */
+){
+ int i, rc;
+ struct CoveringIndexCheck ck;
+ Walker w;
+ if( pWInfo->pSelect==0 ){
+ /* We don't have access to the full query, so we cannot check to see
+ ** if pIdx is covering. Assume it is not. */
+ return 0;
+ }
+ if( pIdx->bHasExpr==0 ){
+ for(i=0; i<pIdx->nColumn; i++){
+ if( pIdx->aiColumn[i]>=BMS-1 ) break;
+ }
+ if( i>=pIdx->nColumn ){
+ /* pIdx does not index any columns greater than 62, but we know from
+ ** colMask that columns greater than 62 are used, so this is not a
+ ** covering index */
+ return 0;
+ }
+ }
+ ck.pIdx = pIdx;
+ ck.iTabCur = iTabCur;
+ ck.bExpr = 0;
+ ck.bUnidx = 0;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = whereIsCoveringIndexWalkCallback;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ w.u.pCovIdxCk = &ck;
+ sqlite3WalkSelect(&w, pWInfo->pSelect);
+ if( ck.bUnidx ){
+ rc = 0;
+ }else if( ck.bExpr ){
+ rc = WHERE_EXPRIDX;
+ }else{
+ rc = WHERE_IDX_ONLY;
+ }
+ return rc;
+}
+
+/*
** Add all WhereLoop objects for a single table of the join where the table
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
@@ -157262,7 +160406,7 @@ static int whereLoopAddBtree(
sPk.aiRowLogEst = aiRowEstPk;
sPk.onError = OE_Replace;
sPk.pTable = pTab;
- sPk.szIdxRow = pTab->szTabRow;
+ sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */
sPk.idxType = SQLITE_IDXTYPE_IPK;
aiRowEstPk[0] = pTab->nRowLogEst;
aiRowEstPk[1] = 0;
@@ -157313,7 +160457,8 @@ static int whereLoopAddBtree(
if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){
pNew->rSetup += 28;
}else{
- pNew->rSetup -= 10;
+ pNew->rSetup -= 25; /* Greatly reduced setup cost for auto indexes
+ ** on ephemeral materializations of views */
}
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
if( pNew->rSetup<0 ) pNew->rSetup = 0;
@@ -157382,6 +160527,9 @@ static int whereLoopAddBtree(
#else
pNew->rRun = rSize + 16;
#endif
+ if( IsView(pTab) || (pTab->tabFlags & TF_Ephemeral)!=0 ){
+ pNew->wsFlags |= WHERE_VIEWSCAN;
+ }
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
rc = whereLoopInsert(pBuilder, pNew);
@@ -157390,11 +160538,38 @@ static int whereLoopAddBtree(
}else{
Bitmask m;
if( pProbe->isCovering ){
- pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
m = 0;
+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
}else{
m = pSrc->colUsed & pProbe->colNotIdxed;
- pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
+ pNew->wsFlags = WHERE_INDEXED;
+ if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){
+ u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor);
+ if( isCov==0 ){
+ WHERETRACE(0x200,
+ ("-> %s is not a covering index"
+ " according to whereIsCoveringIndex()\n", pProbe->zName));
+ assert( m!=0 );
+ }else{
+ m = 0;
+ pNew->wsFlags |= isCov;
+ if( isCov & WHERE_IDX_ONLY ){
+ WHERETRACE(0x200,
+ ("-> %s is a covering expression index"
+ " according to whereIsCoveringIndex()\n", pProbe->zName));
+ }else{
+ assert( isCov==WHERE_EXPRIDX );
+ WHERETRACE(0x200,
+ ("-> %s might be a covering expression index"
+ " according to whereIsCoveringIndex()\n", pProbe->zName));
+ }
+ }
+ }else if( m==0 ){
+ WHERETRACE(0x200,
+ ("-> %s a covering index according to bitmasks\n",
+ pProbe->zName, m==0 ? "is" : "is not"));
+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
+ }
}
/* Full scan via index */
@@ -157567,7 +160742,7 @@ static int whereLoopAddVirtualOne(
** that the particular combination of parameters provided is unusable.
** Make no entries in the loop table.
*/
- WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n"));
+ WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n"));
return SQLITE_OK;
}
return rc;
@@ -157678,7 +160853,7 @@ static int whereLoopAddVirtualOne(
sqlite3_free(pNew->u.vtab.idxStr);
pNew->u.vtab.needFree = 0;
}
- WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
+ WHERETRACE(0xffffffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
*pbIn, (sqlite3_uint64)mPrereq,
(sqlite3_uint64)(pNew->prereq & ~mPrereq)));
@@ -157783,7 +160958,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
** Cause the prepared statement that is associated with a call to
-** xBestIndex to potentiall use all schemas. If the statement being
+** xBestIndex to potentially use all schemas. If the statement being
** prepared is read-only, then just start read transactions on all
** schemas. But if this is a write operation, start writes on all
** schemas.
@@ -157798,7 +160973,7 @@ SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(sqlite3_index_info *pIdxInfo){
for(i=0; i<nDb; i++){
sqlite3CodeVerifySchema(pParse, i);
}
- if( pParse->writeMask ){
+ if( DbMaskNonZero(pParse->writeMask) ){
for(i=0; i<nDb; i++){
sqlite3BeginWriteOperation(pParse, 0, i);
}
@@ -157870,7 +161045,7 @@ static int whereLoopAddVirtual(
/* First call xBestIndex() with all constraints usable. */
WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName));
- WHERETRACE(0x40, (" VirtualOne: all usable\n"));
+ WHERETRACE(0x800, (" VirtualOne: all usable\n"));
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry
);
@@ -157895,7 +161070,7 @@ static int whereLoopAddVirtual(
/* If the plan produced by the earlier call uses an IN(...) term, call
** xBestIndex again, this time with IN(...) terms disabled. */
if( bIn ){
- WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
+ WHERETRACE(0x800, (" VirtualOne: all usable w/o IN\n"));
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0);
assert( bIn==0 );
@@ -157921,7 +161096,7 @@ static int whereLoopAddVirtual(
mPrev = mNext;
if( mNext==ALLBITS ) break;
if( mNext==mBest || mNext==mBestNoIn ) continue;
- WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
+ WHERETRACE(0x800, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
(sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0);
@@ -157935,7 +161110,7 @@ static int whereLoopAddVirtual(
** that requires no source tables at all (i.e. one guaranteed to be
** usable), make a call here with all source tables disabled */
if( rc==SQLITE_OK && seenZero==0 ){
- WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
+ WHERETRACE(0x800, (" VirtualOne: all disabled\n"));
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0);
if( bIn==0 ) seenZeroNoIN = 1;
@@ -157945,7 +161120,7 @@ static int whereLoopAddVirtual(
** that requires no source tables at all and does not use an IN(...)
** operator, make a final call to obtain one here. */
if( rc==SQLITE_OK && seenZeroNoIN==0 ){
- WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
+ WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n"));
rc = whereLoopAddVirtualOne(
pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0);
}
@@ -158001,7 +161176,7 @@ static int whereLoopAddOr(
sSubBuild = *pBuilder;
sSubBuild.pOrSet = &sCur;
- WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm));
+ WHERETRACE(0x400, ("Begin processing OR-clause %p\n", pTerm));
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
if( (pOrTerm->eOperator & WO_AND)!=0 ){
sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc;
@@ -158018,9 +161193,9 @@ static int whereLoopAddOr(
}
sCur.n = 0;
#ifdef WHERETRACE_ENABLED
- WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n",
+ WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n",
(int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm));
- if( sqlite3WhereTrace & 0x400 ){
+ if( sqlite3WhereTrace & 0x20000 ){
sqlite3WhereClausePrint(sSubBuild.pWC);
}
#endif
@@ -158035,8 +161210,6 @@ static int whereLoopAddOr(
if( rc==SQLITE_OK ){
rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable);
}
- assert( rc==SQLITE_OK || rc==SQLITE_DONE || sCur.n==0
- || rc==SQLITE_NOMEM );
testcase( rc==SQLITE_NOMEM && sCur.n>0 );
testcase( rc==SQLITE_DONE );
if( sCur.n==0 ){
@@ -158082,7 +161255,7 @@ static int whereLoopAddOr(
pNew->prereq = sSum.a[i].prereq;
rc = whereLoopInsert(pBuilder, pNew);
}
- WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm));
+ WHERETRACE(0x400, ("End processing OR-clause %p\n", pTerm));
}
}
return rc;
@@ -158108,7 +161281,13 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
/* Loop over the tables in the join, from left to right */
pNew = pBuilder->pNew;
- whereLoopInit(pNew);
+
+ /* Verify that pNew has already been initialized */
+ assert( pNew->nLTerm==0 );
+ assert( pNew->wsFlags==0 );
+ assert( pNew->nLSlot>=ArraySize(pNew->aLTermSpace) );
+ assert( pNew->aLTerm!=0 );
+
pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT;
for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
Bitmask mUnusable = 0;
@@ -158424,8 +161603,8 @@ static i8 wherePathSatisfiesOrderBy(
if( pOBExpr->iTable!=iCur ) continue;
if( pOBExpr->iColumn!=iColumn ) continue;
}else{
- Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr;
- if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){
+ Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr;
+ if( sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){
continue;
}
}
@@ -158557,37 +161736,56 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){
** order.
*/
static LogEst whereSortingCost(
- WhereInfo *pWInfo,
- LogEst nRow,
- int nOrderBy,
- int nSorted
+ WhereInfo *pWInfo, /* Query planning context */
+ LogEst nRow, /* Estimated number of rows to sort */
+ int nOrderBy, /* Number of ORDER BY clause terms */
+ int nSorted /* Number of initial ORDER BY terms naturally in order */
){
- /* TUNING: Estimated cost of a full external sort, where N is
+ /* Estimated cost of a full external sort, where N is
** the number of rows to sort is:
**
- ** cost = (3.0 * N * log(N)).
+ ** cost = (K * N * log(N)).
**
** Or, if the order-by clause has X terms but only the last Y
** terms are out of order, then block-sorting will reduce the
** sorting cost to:
**
- ** cost = (3.0 * N * log(N)) * (Y/X)
+ ** cost = (K * N * log(N)) * (Y/X)
+ **
+ ** The constant K is at least 2.0 but will be larger if there are a
+ ** large number of columns to be sorted, as the sorting time is
+ ** proportional to the amount of content to be sorted. The algorithm
+ ** does not currently distinguish between fat columns (BLOBs and TEXTs)
+ ** and skinny columns (INTs). It just uses the number of columns as
+ ** an approximation for the row width.
**
- ** The (Y/X) term is implemented using stack variable rScale
- ** below.
+ ** And extra factor of 2.0 or 3.0 is added to the sorting cost if the sort
+ ** is built using OP_IdxInsert and OP_Sort rather than with OP_SorterInsert.
*/
- LogEst rScale, rSortCost;
- assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
- rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
- rSortCost = nRow + rScale + 16;
+ LogEst rSortCost, nCol;
+ assert( pWInfo->pSelect!=0 );
+ assert( pWInfo->pSelect->pEList!=0 );
+ /* TUNING: sorting cost proportional to the number of output columns: */
+ nCol = sqlite3LogEst((pWInfo->pSelect->pEList->nExpr+59)/30);
+ rSortCost = nRow + nCol;
+ if( nSorted>0 ){
+ /* Scale the result by (Y/X) */
+ rSortCost += sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
+ }
/* Multiple by log(M) where M is the number of output rows.
** Use the LIMIT for M if it is smaller. Or if this sort is for
** a DISTINCT operator, M will be the number of distinct output
** rows, so fudge it downwards a bit.
*/
- if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
- nRow = pWInfo->iLimit;
+ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 ){
+ rSortCost += 10; /* TUNING: Extra 2.0x if using LIMIT */
+ if( nSorted!=0 ){
+ rSortCost += 6; /* TUNING: Extra 1.5x if also using partial sort */
+ }
+ if( pWInfo->iLimit<nRow ){
+ nRow = pWInfo->iLimit;
+ }
}else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){
/* TUNING: In the sort for a DISTINCT operator, assume that the DISTINCT
** reduces the number of output rows by a factor of 2 */
@@ -158613,7 +161811,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
int mxChoice; /* Maximum number of simultaneous paths tracked */
int nLoop; /* Number of terms in the join */
Parse *pParse; /* Parsing context */
- sqlite3 *db; /* The database connection */
int iLoop; /* Loop counter over the terms of the join */
int ii, jj; /* Loop counters */
int mxI = 0; /* Index of next entry to replace */
@@ -158632,7 +161829,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
int nSpace; /* Bytes of space allocated at pSpace */
pParse = pWInfo->pParse;
- db = pParse->db;
nLoop = pWInfo->nLevel;
/* TUNING: For simple queries, only the best path is tracked.
** For 2-way joins, the 5 best paths are followed.
@@ -158655,7 +161851,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
/* Allocate and initialize space for aTo, aFrom and aSortCost[] */
nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
nSpace += sizeof(LogEst) * nOrderBy;
- pSpace = sqlite3DbMallocRawNN(db, nSpace);
+ pSpace = sqlite3StackAllocRawNN(pParse->db, nSpace);
if( pSpace==0 ) return SQLITE_NOMEM_BKPT;
aTo = (WherePath*)pSpace;
aFrom = aTo+mxChoice;
@@ -158705,9 +161901,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
LogEst nOut; /* Rows visited by (pFrom+pWLoop) */
LogEst rCost; /* Cost of path (pFrom+pWLoop) */
LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */
- i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */
+ i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */
Bitmask maskNew; /* Mask of src visited by (..) */
- Bitmask revMask = 0; /* Mask of rev-order loops for (..) */
+ Bitmask revMask; /* Mask of rev-order loops for (..) */
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
@@ -158726,7 +161922,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted);
nOut = pFrom->nRow + pWLoop->nOut;
maskNew = pFrom->maskLoop | pWLoop->maskSelf;
+ isOrdered = pFrom->isOrdered;
if( isOrdered<0 ){
+ revMask = 0;
isOrdered = wherePathSatisfiesOrderBy(pWInfo,
pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
iLoop, pWLoop, &revMask);
@@ -158739,11 +161937,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo, nRowEst, nOrderBy, isOrdered
);
}
- /* TUNING: Add a small extra penalty (5) to sorting as an
+ /* TUNING: Add a small extra penalty (3) to sorting as an
** extra encouragment to the query planner to select a plan
** where the rows emerge in the correct order without any sorting
** required. */
- rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 5;
+ rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3;
WHERETRACE(0x002,
("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
@@ -158754,6 +161952,13 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
+ /* TUNING: A full-scan of a VIEW or subquery in the outer loop
+ ** is not so bad. */
+ if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 ){
+ rCost += -10;
+ nOut += -30;
+ }
+
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
@@ -158904,7 +162109,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( nFrom==0 ){
sqlite3ErrorMsg(pParse, "no query solution");
- sqlite3DbFreeNN(db, pSpace);
+ sqlite3StackFreeNN(pParse->db, pSpace);
return SQLITE_ERROR;
}
@@ -158986,7 +162191,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo->nRowOut = pFrom->nRow;
/* Free temporary memory and return success */
- sqlite3DbFreeNN(db, pSpace);
+ sqlite3StackFreeNN(pParse->db, pSpace);
return SQLITE_OK;
}
@@ -159084,7 +162289,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
pLoop->cId = '0';
#endif
#ifdef WHERETRACE_ENABLED
- if( sqlite3WhereTrace ){
+ if( sqlite3WhereTrace & 0x02 ){
sqlite3DebugPrintf("whereShortCut() used to compute solution\n");
}
#endif
@@ -159214,7 +162419,7 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
}
}
if( pTerm<pEnd ) continue;
- WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
+ WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId));
notReady &= ~pLoop->maskSelf;
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
@@ -159253,28 +162458,27 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
const WhereInfo *pWInfo
){
int i;
- LogEst nSearch;
+ LogEst nSearch = 0;
assert( pWInfo->nLevel>=2 );
assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) );
- nSearch = pWInfo->a[0].pWLoop->nOut;
- for(i=1; i<pWInfo->nLevel; i++){
+ for(i=0; i<pWInfo->nLevel; i++){
WhereLoop *pLoop = pWInfo->a[i].pWLoop;
const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
- if( (pLoop->wsFlags & reqFlags)==reqFlags
+ SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
+ Table *pTab = pItem->pTab;
+ if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
+ pTab->tabFlags |= TF_StatsUsed;
+ if( i>=1
+ && (pLoop->wsFlags & reqFlags)==reqFlags
/* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
&& ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0)
){
- SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
- Table *pTab = pItem->pTab;
- pTab->tabFlags |= TF_StatsUsed;
- if( nSearch > pTab->nRowLogEst
- && (pTab->tabFlags & TF_HasStat1)!=0
- ){
+ if( nSearch > pTab->nRowLogEst ){
testcase( pItem->fg.jointype & JT_LEFT );
pLoop->wsFlags |= WHERE_BLOOMFILTER;
pLoop->wsFlags &= ~WHERE_IDX_ONLY;
- WHERETRACE(0xffff, (
+ WHERETRACE(0xffffffff, (
"-> use Bloom-filter on loop %c because there are ~%.1e "
"lookups into %s which has only ~%.1e rows\n",
pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName,
@@ -159286,6 +162490,83 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
}
/*
+** This is an sqlite3ParserAddCleanup() callback that is invoked to
+** free the Parse->pIdxEpr list when the Parse object is destroyed.
+*/
+static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
+ Parse *pParse = (Parse*)pObject;
+ while( pParse->pIdxEpr!=0 ){
+ IndexedExpr *p = pParse->pIdxEpr;
+ pParse->pIdxEpr = p->pIENext;
+ sqlite3ExprDelete(db, p->pExpr);
+ sqlite3DbFreeNN(db, p);
+ }
+}
+
+/*
+** The index pIdx is used by a query and contains one or more expressions.
+** In other words pIdx is an index on an expression. iIdxCur is the cursor
+** number for the index and iDataCur is the cursor number for the corresponding
+** table.
+**
+** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for
+** each of the expressions in the index so that the expression code generator
+** will know to replace occurrences of the indexed expression with
+** references to the corresponding column of the index.
+*/
+static SQLITE_NOINLINE void whereAddIndexedExpr(
+ Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */
+ Index *pIdx, /* The index-on-expression that contains the expressions */
+ int iIdxCur, /* Cursor number for pIdx */
+ SrcItem *pTabItem /* The FROM clause entry for the table */
+){
+ int i;
+ IndexedExpr *p;
+ Table *pTab;
+ assert( pIdx->bHasExpr );
+ pTab = pIdx->pTable;
+ for(i=0; i<pIdx->nColumn; i++){
+ Expr *pExpr;
+ int j = pIdx->aiColumn[i];
+ int bMaybeNullRow;
+ if( j==XN_EXPR ){
+ pExpr = pIdx->aColExpr->a[i].pExpr;
+ testcase( pTabItem->fg.jointype & JT_LEFT );
+ testcase( pTabItem->fg.jointype & JT_RIGHT );
+ testcase( pTabItem->fg.jointype & JT_LTORJ );
+ bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
+ }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
+ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
+ bMaybeNullRow = 0;
+ }else{
+ continue;
+ }
+ if( sqlite3ExprIsConstant(pExpr) ) continue;
+ p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
+ if( p==0 ) break;
+ p->pIENext = pParse->pIdxEpr;
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x200 ){
+ sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i);
+ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(pExpr);
+ }
+#endif
+ p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
+ p->iDataCur = pTabItem->iCursor;
+ p->iIdxCur = iIdxCur;
+ p->iIdxCol = i;
+ p->bMaybeNullRow = bMaybeNullRow;
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ p->zIdxName = pIdx->zName;
+#endif
+ pParse->pIdxEpr = p;
+ if( p->pIENext==0 ){
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
+ }
+ }
+}
+
+/*
** Generate the beginning of the loop used for WHERE clause processing.
** The return value is a pointer to an opaque structure that contains
** information needed to terminate the loop. Later, the calling routine
@@ -159379,7 +162660,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
Expr *pWhere, /* The WHERE clause */
ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */
- Select *pLimit, /* Use this LIMIT/OFFSET clause, if any */
+ Select *pSelect, /* The entire SELECT statement */
u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number
** If WHERE_USE_LIMIT, then the limit amount */
@@ -159448,7 +162729,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->pOrderBy = pOrderBy;
+#if WHERETRACE_ENABLED
pWInfo->pWhere = pWhere;
+#endif
pWInfo->pResultSet = pResultSet;
pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1;
pWInfo->nLevel = nTabList;
@@ -159456,9 +162739,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pWInfo->wctrlFlags = wctrlFlags;
pWInfo->iLimit = iAuxArg;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- pWInfo->pLimit = pLimit;
-#endif
+ pWInfo->pSelect = pSelect;
memset(&pWInfo->nOBSat, 0,
offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat));
memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel));
@@ -159527,7 +162808,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Analyze all of the subexpressions. */
sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
- sqlite3WhereAddLimit(&pWInfo->sWC, pLimit);
+ if( pSelect && pSelect->pLimit ){
+ sqlite3WhereAddLimit(&pWInfo->sWC, pSelect);
+ }
if( pParse->nErr ) goto whereBeginError;
/* Special case: WHERE terms that do not refer to any tables in the join
@@ -159568,13 +162851,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Construct the WhereLoop objects */
#if defined(WHERETRACE_ENABLED)
- if( sqlite3WhereTrace & 0xffff ){
+ if( sqlite3WhereTrace & 0xffffffff ){
sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags);
if( wctrlFlags & WHERE_USE_LIMIT ){
sqlite3DebugPrintf(", limit: %d", iAuxArg);
}
sqlite3DebugPrintf(")\n");
- if( sqlite3WhereTrace & 0x100 ){
+ if( sqlite3WhereTrace & 0x8000 ){
Select sSelect;
memset(&sSelect, 0, sizeof(sSelect));
sSelect.selFlags = SF_WhereBegin;
@@ -159584,10 +162867,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sSelect.pEList = pResultSet;
sqlite3TreeViewSelect(0, &sSelect, 0);
}
- }
- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
- sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n");
- sqlite3WhereClausePrint(sWLB.pWC);
+ if( sqlite3WhereTrace & 0x4000 ){ /* Display all WHERE clause terms */
+ sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n");
+ sqlite3WhereClausePrint(sWLB.pWC);
+ }
}
#endif
@@ -159603,7 +162886,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** loops will be built using the revised truthProb values. */
if( sWLB.bldFlags2 & SQLITE_BLDF2_2NDPASS ){
WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC);
- WHERETRACE(0xffff,
+ WHERETRACE(0xffffffff,
("**** Redo all loop computations due to"
" TERM_HIGHTRUTH changes ****\n"));
while( pWInfo->pLoops ){
@@ -159689,11 +162972,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
#if defined(WHERETRACE_ENABLED)
- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
+ if( sqlite3WhereTrace & 0x4000 ){ /* Display all terms of the WHERE clause */
sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n");
sqlite3WhereClausePrint(sWLB.pWC);
}
- WHERETRACE(0xffff,("*** Optimizer Finished ***\n"));
+ WHERETRACE(0xffffffff,("*** Optimizer Finished ***\n"));
#endif
pWInfo->pParse->nQueryLoop += pWInfo->nRowOut;
@@ -159830,6 +163113,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
op = OP_ReopenIdx;
}else{
iIndexCur = pParse->nTab++;
+ if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){
+ whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem);
+ }
}
pLevel->iIdxCur = iIndexCur;
assert( pIx!=0 );
@@ -159952,8 +163238,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Jump here if malloc fails */
whereBeginError:
if( pWInfo ){
- testcase( pWInfo->pExprMods!=0 );
- whereUndoExprMods(pWInfo);
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
@@ -160172,7 +163456,6 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}
assert( pWInfo->nLevel<=pTabList->nSrc );
- if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo);
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
int k, last;
VdbeOp *pOp, *pLastOp;
@@ -160226,6 +163509,23 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}else{
last = pWInfo->iEndWhere;
}
+ if( pIdx->bHasExpr ){
+ IndexedExpr *p = pParse->pIdxEpr;
+ while( p ){
+ if( p->iIdxCur==pLevel->iIdxCur ){
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x200 ){
+ sqlite3DebugPrintf("Disable pParse->pIdxEpr term {%d,%d}\n",
+ p->iIdxCur, p->iIdxCol);
+ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(p->pExpr);
+ }
+#endif
+ p->iDataCur = -1;
+ p->iIdxCur = -1;
+ }
+ p = p->pIENext;
+ }
+ }
k = pLevel->addrBody + 1;
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeAddopTrace ){
@@ -161219,7 +164519,6 @@ static ExprList *exprListAppendList(
for(i=0; i<pAppend->nExpr; i++){
sqlite3 *db = pParse->db;
Expr *pDup = sqlite3ExprDup(db, pAppend->a[i].pExpr, 0);
- assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) );
if( db->mallocFailed ){
sqlite3ExprDelete(db, pDup);
break;
@@ -161389,7 +164688,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
pSub = sqlite3SelectNew(
pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
);
- SELECTTRACE(1,pParse,pSub,
+ TREETRACE(0x40,pParse,pSub,
("New window-function subquery in FROM clause of (%u/%p)\n",
p->selId, p));
p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
@@ -161399,6 +164698,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
if( p->pSrc ){
Table *pTab2;
p->pSrc->a[0].pSelect = pSub;
+ p->pSrc->a[0].fg.isCorrelated = 1;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
@@ -162490,10 +165790,9 @@ static void windowCodeRangeTest(
/* This block runs if reg1 is not NULL, but reg2 is. */
sqlite3VdbeJumpHere(v, addr);
- sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v);
- if( op==OP_Gt || op==OP_Ge ){
- sqlite3VdbeChangeP2(v, -1, addrDone);
- }
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2,
+ (op==OP_Gt || op==OP_Ge) ? addrDone : lbl);
+ VdbeCoverage(v);
}
/* Register reg1 currently contains csr1.peerVal (the peer-value from csr1).
@@ -163265,8 +166564,7 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound <expr> */
VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
windowAggFinal(&s, 0);
- sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
- VdbeCoverageNeverTaken(v);
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr);
windowReturnOneRow(&s);
sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
@@ -163278,13 +166576,10 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
}
if( pMWin->eStart!=TK_UNBOUNDED ){
- sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1);
- VdbeCoverageNeverTaken(v);
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.start.csr);
}
- sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1);
- VdbeCoverageNeverTaken(v);
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr);
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.end.csr);
if( regPeer && pOrderBy ){
sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1);
sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1);
@@ -167966,6 +171261,11 @@ static YYACTIONTYPE yy_reduce(
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
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);
+ pRHS->x.pSelect = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
}else{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
if( yymsp[-4].minor.yy528==0 ){
@@ -170070,7 +173370,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
if( pParse->pNewTrigger && !IN_RENAME_OBJECT ){
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
}
- if( pParse->pVList ) sqlite3DbFreeNN(db, pParse->pVList);
+ if( pParse->pVList ) sqlite3DbNNFreeNN(db, pParse->pVList);
db->pParse = pParentParse;
assert( nErr==0 || pParse->rc!=SQLITE_OK );
return nErr;
@@ -171426,18 +174726,19 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
db->lookaside.bMalloced = pBuf==0 ?1:0;
db->lookaside.nSlot = nBig+nSm;
}else{
- db->lookaside.pStart = db;
+ db->lookaside.pStart = 0;
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
db->lookaside.pSmallInit = 0;
db->lookaside.pSmallFree = 0;
- db->lookaside.pMiddle = db;
+ db->lookaside.pMiddle = 0;
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
- db->lookaside.pEnd = db;
+ db->lookaside.pEnd = 0;
db->lookaside.bDisable = 1;
db->lookaside.sz = 0;
db->lookaside.bMalloced = 0;
db->lookaside.nSlot = 0;
}
+ db->lookaside.pTrueEnd = db->lookaside.pEnd;
assert( sqlite3LookasideUsed(db,0)==0 );
#endif /* SQLITE_OMIT_LOOKASIDE */
return SQLITE_OK;
@@ -171516,6 +174817,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){
SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
va_list ap;
int rc;
+ sqlite3_mutex_enter(db->mutex);
va_start(ap, op);
switch( op ){
case SQLITE_DBCONFIG_MAINDBNAME: {
@@ -171581,6 +174883,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
}
}
va_end(ap);
+ sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -172165,6 +175468,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break;
case SQLITE_NOTICE_RECOVER_ROLLBACK:
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
+ case SQLITE_NOTICE_RBU: zName = "SQLITE_NOTICE_RBU"; break;
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
@@ -172394,7 +175698,9 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !sqlite3SafetyCheckOk(db) && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE) ){
+ if( !sqlite3SafetyCheckOk(db)
+ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE)
+ ){
(void)SQLITE_MISUSE_BKPT;
return;
}
@@ -172402,6 +175708,21 @@ SQLITE_API void sqlite3_interrupt(sqlite3 *db){
AtomicStore(&db->u1.isInterrupted, 1);
}
+/*
+** Return true or false depending on whether or not an interrupt is
+** pending on connection db.
+*/
+SQLITE_API int sqlite3_is_interrupted(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db)
+ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE)
+ ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+ return AtomicLoad(&db->u1.isInterrupted)!=0;
+}
/*
** This function is exactly the same as sqlite3_create_function(), except
@@ -172446,7 +175767,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
** the meaning is inverted. So flip the bit. */
assert( SQLITE_FUNC_UNSAFE==SQLITE_INNOCUOUS );
- extraFlags ^= SQLITE_FUNC_UNSAFE;
+ extraFlags ^= SQLITE_FUNC_UNSAFE; /* tag-20230109-1 */
#ifndef SQLITE_OMIT_UTF16
@@ -172464,11 +175785,11 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
case SQLITE_ANY: {
int rc;
rc = sqlite3CreateFunc(db, zFunctionName, nArg,
- (SQLITE_UTF8|extraFlags)^SQLITE_FUNC_UNSAFE,
+ (SQLITE_UTF8|extraFlags)^SQLITE_FUNC_UNSAFE, /* tag-20230109-1 */
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
if( rc==SQLITE_OK ){
rc = sqlite3CreateFunc(db, zFunctionName, nArg,
- (SQLITE_UTF16LE|extraFlags)^SQLITE_FUNC_UNSAFE,
+ (SQLITE_UTF16LE|extraFlags)^SQLITE_FUNC_UNSAFE, /* tag-20230109-1*/
pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
}
if( rc!=SQLITE_OK ){
@@ -172717,7 +176038,7 @@ SQLITE_API int sqlite3_overload_function(
rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0;
sqlite3_mutex_leave(db->mutex);
if( rc ) return SQLITE_OK;
- zCopy = sqlite3_mprintf(zName);
+ zCopy = sqlite3_mprintf("%s", zName);
if( zCopy==0 ) return SQLITE_NOMEM;
return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8,
zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free);
@@ -173951,6 +177272,19 @@ static int openDatabase(
goto opendb_out;
}
+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
+ /* Process magic filenames ":localStorage:" and ":sessionStorage:" */
+ if( zFilename && zFilename[0]==':' ){
+ if( strcmp(zFilename, ":localStorage:")==0 ){
+ zFilename = "file:local?vfs=kvvfs";
+ flags |= SQLITE_OPEN_URI;
+ }else if( strcmp(zFilename, ":sessionStorage:")==0 ){
+ zFilename = "file:session?vfs=kvvfs";
+ flags |= SQLITE_OPEN_URI;
+ }
+ }
+#endif /* SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) */
+
/* Parse the filename/URI argument
**
** Only allow sensible combinations of bits in the flags argument.
@@ -173981,6 +177315,12 @@ static int openDatabase(
sqlite3_free(zErrMsg);
goto opendb_out;
}
+ assert( db->pVfs!=0 );
+#if SQLITE_OS_KV || defined(SQLITE_OS_KV_OPTIONAL)
+ if( sqlite3_stricmp(db->pVfs->zName, "kvvfs")==0 ){
+ db->temp_store = 2;
+ }
+#endif
/* Open the backend database driver */
rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
@@ -174530,6 +177870,9 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo
sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
}
rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_RESET_CACHE ){
+ sqlite3BtreeClearCache(pBtree);
+ rc = SQLITE_OK;
}else{
int nSave = db->busyHandler.nBusy;
rc = sqlite3OsFileControl(fd, op, pArg);
@@ -175090,7 +178433,7 @@ static char *appendText(char *p, const char *z){
** Memory layout must be compatible with that generated by the pager
** and expected by sqlite3_uri_parameter() and databaseName().
*/
-SQLITE_API char *sqlite3_create_filename(
+SQLITE_API const char *sqlite3_create_filename(
const char *zDatabase,
const char *zJournal,
const char *zWal,
@@ -175126,10 +178469,10 @@ SQLITE_API char *sqlite3_create_filename(
** error to call this routine with any parameter other than a pointer
** previously obtained from sqlite3_create_filename() or a NULL pointer.
*/
-SQLITE_API void sqlite3_free_filename(char *p){
+SQLITE_API void sqlite3_free_filename(const char *p){
if( p==0 ) return;
- p = (char*)databaseName(p);
- sqlite3_free(p - 4);
+ p = databaseName(p);
+ sqlite3_free((char*)p - 4);
}
@@ -175380,8 +178723,8 @@ SQLITE_API int sqlite3_snapshot_open(
*/
SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
int rc = SQLITE_ERROR;
- int iDb;
#ifndef SQLITE_OMIT_WAL
+ int iDb;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ){
@@ -176936,7 +180279,7 @@ struct Fts3MultiSegReader {
int nAdvance; /* How many seg-readers to advance */
Fts3SegFilter *pFilter; /* Pointer to filter object */
char *aBuffer; /* Buffer to merge doclists in */
- int nBuffer; /* Allocated size of aBuffer[] in bytes */
+ i64 nBuffer; /* Allocated size of aBuffer[] in bytes */
int iColFilter; /* If >=0, filter for this column */
int bRestart;
@@ -177028,6 +180371,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
#endif
+SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);
+
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
@@ -179632,7 +182977,7 @@ static int fts3TermSelectMerge(
**
** Similar padding is added in the fts3DoclistOrMerge() function.
*/
- pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1);
+ pTS->aaOutput[0] = sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1);
pTS->anOutput[0] = nDoclist;
if( pTS->aaOutput[0] ){
memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
@@ -181120,7 +184465,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
nDistance = iPrev - nMaxUndeferred;
}
- aOut = (char *)sqlite3_malloc(nPoslist+8);
+ aOut = (char *)sqlite3Fts3MallocZero(nPoslist+FTS3_BUFFER_PADDING);
if( !aOut ){
sqlite3_free(aPoslist);
return SQLITE_NOMEM;
@@ -181489,7 +184834,7 @@ static int fts3EvalIncrPhraseNext(
if( bEof==0 ){
int nList = 0;
int nByte = a[p->nToken-1].nList;
- char *aDoclist = sqlite3_malloc(nByte+FTS3_BUFFER_PADDING);
+ char *aDoclist = sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING);
if( !aDoclist ) return SQLITE_NOMEM;
memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING);
@@ -182031,9 +185376,8 @@ static void fts3EvalNextRow(
Fts3Expr *pExpr, /* Expr. to advance to next matching row */
int *pRc /* IN/OUT: Error code */
){
- if( *pRc==SQLITE_OK ){
+ if( *pRc==SQLITE_OK && pExpr->bEof==0 ){
int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */
- assert( pExpr->bEof==0 );
pExpr->bStart = 1;
switch( pExpr->eType ){
@@ -182510,6 +185854,22 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){
}
/*
+** This is an sqlite3Fts3ExprIterate() callback. If the Fts3Expr.aMI[] array
+** has not yet been allocated, allocate and zero it. Otherwise, just zero
+** it.
+*/
+static int fts3AllocateMSI(Fts3Expr *pExpr, int iPhrase, void *pCtx){
+ Fts3Table *pTab = (Fts3Table*)pCtx;
+ UNUSED_PARAMETER(iPhrase);
+ if( pExpr->aMI==0 ){
+ pExpr->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32));
+ if( pExpr->aMI==0 ) return SQLITE_NOMEM;
+ }
+ memset(pExpr->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
+ return SQLITE_OK;
+}
+
+/*
** Expression pExpr must be of type FTSQUERY_PHRASE.
**
** If it is not already allocated and populated, this function allocates and
@@ -182530,7 +185890,6 @@ static int fts3EvalGatherStats(
if( pExpr->aMI==0 ){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
Fts3Expr *pRoot; /* Root of NEAR expression */
- Fts3Expr *p; /* Iterator used for several purposes */
sqlite3_int64 iPrevId = pCsr->iPrevId;
sqlite3_int64 iDocid;
@@ -182538,7 +185897,9 @@ static int fts3EvalGatherStats(
/* Find the root of the NEAR expression */
pRoot = pExpr;
- while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){
+ while( pRoot->pParent
+ && (pRoot->pParent->eType==FTSQUERY_NEAR || pRoot->bDeferred)
+ ){
pRoot = pRoot->pParent;
}
iDocid = pRoot->iDocid;
@@ -182546,14 +185907,8 @@ static int fts3EvalGatherStats(
assert( pRoot->bStart );
/* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */
- for(p=pRoot; p; p=p->pLeft){
- Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight);
- assert( pE->aMI==0 );
- pE->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32));
- if( !pE->aMI ) return SQLITE_NOMEM;
- memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
- }
-
+ rc = sqlite3Fts3ExprIterate(pRoot, fts3AllocateMSI, (void*)pTab);
+ if( rc!=SQLITE_OK ) return rc;
fts3EvalRestart(pCsr, pRoot, &rc);
while( pCsr->isEof==0 && rc==SQLITE_OK ){
@@ -182709,6 +186064,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
u8 bTreeEof = 0;
Fts3Expr *p; /* Used to iterate from pExpr to root */
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
+ Fts3Expr *pRun; /* Closest non-deferred ancestor of pNear */
int bMatch;
/* Check if this phrase descends from an OR expression node. If not,
@@ -182723,25 +186079,30 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
+ pRun = pNear;
+ while( pRun->bDeferred ){
+ assert( pRun->pParent );
+ pRun = pRun->pParent;
+ }
/* This is the descendent of an OR node. In this case we cannot use
** an incremental phrase. Load the entire doclist for the phrase
** into memory in this case. */
if( pPhrase->bIncr ){
- int bEofSave = pNear->bEof;
- fts3EvalRestart(pCsr, pNear, &rc);
- while( rc==SQLITE_OK && !pNear->bEof ){
- fts3EvalNextRow(pCsr, pNear, &rc);
- if( bEofSave==0 && pNear->iDocid==iDocid ) break;
+ int bEofSave = pRun->bEof;
+ fts3EvalRestart(pCsr, pRun, &rc);
+ while( rc==SQLITE_OK && !pRun->bEof ){
+ fts3EvalNextRow(pCsr, pRun, &rc);
+ if( bEofSave==0 && pRun->iDocid==iDocid ) break;
}
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
- if( rc==SQLITE_OK && pNear->bEof!=bEofSave ){
+ if( rc==SQLITE_OK && pRun->bEof!=bEofSave ){
rc = FTS_CORRUPT_VTAB;
}
}
if( bTreeEof ){
- while( rc==SQLITE_OK && !pNear->bEof ){
- fts3EvalNextRow(pCsr, pNear, &rc);
+ while( rc==SQLITE_OK && !pRun->bEof ){
+ fts3EvalNextRow(pCsr, pRun, &rc);
}
}
if( rc!=SQLITE_OK ) return rc;
@@ -185725,7 +189086,7 @@ static int porterNext(
if( n>c->nAllocated ){
char *pNew;
c->nAllocated = n+20;
- pNew = sqlite3_realloc(c->zToken, c->nAllocated);
+ pNew = sqlite3_realloc64(c->zToken, c->nAllocated);
if( !pNew ) return SQLITE_NOMEM;
c->zToken = pNew;
}
@@ -186477,7 +189838,7 @@ static int simpleNext(
if( n>c->nTokenAllocated ){
char *pNew;
c->nTokenAllocated = n+20;
- pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated);
+ pNew = sqlite3_realloc64(c->pToken, c->nTokenAllocated);
if( !pNew ) return SQLITE_NOMEM;
c->pToken = pNew;
}
@@ -187639,7 +191000,7 @@ static int fts3PendingListAppendVarint(
/* Allocate or grow the PendingList as required. */
if( !p ){
- p = sqlite3_malloc(sizeof(*p) + 100);
+ p = sqlite3_malloc64(sizeof(*p) + 100);
if( !p ){
return SQLITE_NOMEM;
}
@@ -187648,14 +191009,14 @@ static int fts3PendingListAppendVarint(
p->nData = 0;
}
else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){
- int nNew = p->nSpace * 2;
- p = sqlite3_realloc(p, sizeof(*p) + nNew);
+ i64 nNew = p->nSpace * 2;
+ p = sqlite3_realloc64(p, sizeof(*p) + nNew);
if( !p ){
sqlite3_free(*pp);
*pp = 0;
return SQLITE_NOMEM;
}
- p->nSpace = nNew;
+ p->nSpace = (int)nNew;
p->aData = (char *)&p[1];
}
@@ -188212,7 +191573,7 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock(
int nByte = sqlite3_blob_bytes(p->pSegments);
*pnBlob = nByte;
if( paBlob ){
- char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING);
+ char *aByte = sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING);
if( !aByte ){
rc = SQLITE_NOMEM;
}else{
@@ -188329,7 +191690,7 @@ static int fts3SegReaderNext(
int nTerm = fts3HashKeysize(pElem);
if( (nTerm+1)>pReader->nTermAlloc ){
sqlite3_free(pReader->zTerm);
- pReader->zTerm = (char*)sqlite3_malloc((nTerm+1)*2);
+ pReader->zTerm = (char*)sqlite3_malloc64(((i64)nTerm+1)*2);
if( !pReader->zTerm ) return SQLITE_NOMEM;
pReader->nTermAlloc = (nTerm+1)*2;
}
@@ -188337,7 +191698,7 @@ static int fts3SegReaderNext(
pReader->zTerm[nTerm] = '\0';
pReader->nTerm = nTerm;
- aCopy = (char*)sqlite3_malloc(nCopy);
+ aCopy = (char*)sqlite3_malloc64(nCopy);
if( !aCopy ) return SQLITE_NOMEM;
memcpy(aCopy, pList->aData, nCopy);
pReader->nNode = pReader->nDoclist = nCopy;
@@ -188624,7 +191985,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
nExtra = nRoot + FTS3_NODE_PADDING;
}
- pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra);
+ pReader = (Fts3SegReader *)sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra);
if( !pReader ){
return SQLITE_NOMEM;
}
@@ -188716,7 +192077,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
if( nElem==nAlloc ){
Fts3HashElem **aElem2;
nAlloc += 16;
- aElem2 = (Fts3HashElem **)sqlite3_realloc(
+ aElem2 = (Fts3HashElem **)sqlite3_realloc64(
aElem, nAlloc*sizeof(Fts3HashElem *)
);
if( !aElem2 ){
@@ -189050,7 +192411,7 @@ static int fts3NodeAddTerm(
** this is not expected to be a serious problem.
*/
assert( pTree->aData==(char *)&pTree[1] );
- pTree->aData = (char *)sqlite3_malloc(nReq);
+ pTree->aData = (char *)sqlite3_malloc64(nReq);
if( !pTree->aData ){
return SQLITE_NOMEM;
}
@@ -189068,7 +192429,7 @@ static int fts3NodeAddTerm(
if( isCopyTerm ){
if( pTree->nMalloc<nTerm ){
- char *zNew = sqlite3_realloc(pTree->zMalloc, nTerm*2);
+ char *zNew = sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2);
if( !zNew ){
return SQLITE_NOMEM;
}
@@ -189094,7 +192455,7 @@ static int fts3NodeAddTerm(
** now. Instead, the term is inserted into the parent of pTree. If pTree
** has no parent, one is created here.
*/
- pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize);
+ pNew = (SegmentNode *)sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize);
if( !pNew ){
return SQLITE_NOMEM;
}
@@ -189232,7 +192593,7 @@ static int fts3SegWriterAdd(
){
int nPrefix; /* Size of term prefix in bytes */
int nSuffix; /* Size of term suffix in bytes */
- int nReq; /* Number of bytes required on leaf page */
+ i64 nReq; /* Number of bytes required on leaf page */
int nData;
SegmentWriter *pWriter = *ppWriter;
@@ -189241,13 +192602,13 @@ static int fts3SegWriterAdd(
sqlite3_stmt *pStmt;
/* Allocate the SegmentWriter structure */
- pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter));
+ pWriter = (SegmentWriter *)sqlite3_malloc64(sizeof(SegmentWriter));
if( !pWriter ) return SQLITE_NOMEM;
memset(pWriter, 0, sizeof(SegmentWriter));
*ppWriter = pWriter;
/* Allocate a buffer in which to accumulate data */
- pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize);
+ pWriter->aData = (char *)sqlite3_malloc64(p->nNodeSize);
if( !pWriter->aData ) return SQLITE_NOMEM;
pWriter->nSize = p->nNodeSize;
@@ -189322,7 +192683,7 @@ static int fts3SegWriterAdd(
** the buffer to make it large enough.
*/
if( nReq>pWriter->nSize ){
- char *aNew = sqlite3_realloc(pWriter->aData, nReq);
+ char *aNew = sqlite3_realloc64(pWriter->aData, nReq);
if( !aNew ) return SQLITE_NOMEM;
pWriter->aData = aNew;
pWriter->nSize = nReq;
@@ -189347,7 +192708,7 @@ static int fts3SegWriterAdd(
*/
if( isCopyTerm ){
if( nTerm>pWriter->nMalloc ){
- char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2);
+ char *zNew = sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2);
if( !zNew ){
return SQLITE_NOMEM;
}
@@ -189655,12 +193016,12 @@ static void fts3ColumnFilter(
static int fts3MsrBufferData(
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
char *pList,
- int nList
+ i64 nList
){
if( nList>pMsr->nBuffer ){
char *pNew;
pMsr->nBuffer = nList*2;
- pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer);
+ pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, pMsr->nBuffer);
if( !pNew ) return SQLITE_NOMEM;
pMsr->aBuffer = pNew;
}
@@ -189716,7 +193077,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){
- rc = fts3MsrBufferData(pMsr, pList, nList+1);
+ rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1);
if( rc!=SQLITE_OK ) return rc;
assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
pList = pMsr->aBuffer;
@@ -189853,11 +193214,11 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
return SQLITE_OK;
}
-static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){
+static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){
if( nReq>pCsr->nBuffer ){
char *aNew;
pCsr->nBuffer = nReq*2;
- aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
+ aNew = sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer);
if( !aNew ){
return SQLITE_NOMEM;
}
@@ -189948,7 +193309,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
){
pCsr->nDoclist = apSegment[0]->nDoclist;
if( fts3SegReaderIsPending(apSegment[0]) ){
- rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
+ rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist,
+ (i64)pCsr->nDoclist);
pCsr->aDoclist = pCsr->aBuffer;
}else{
pCsr->aDoclist = apSegment[0]->aDoclist;
@@ -190001,7 +193363,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
- rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist+FTS3_NODE_PADDING);
+ rc = fts3GrowSegReaderBuffer(pCsr,
+ (i64)nByte+nDoclist+FTS3_NODE_PADDING);
if( rc ) return rc;
if( isFirst ){
@@ -190027,7 +193390,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
}
if( nDoclist>0 ){
- rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING);
+ rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING);
if( rc ) return rc;
memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING);
pCsr->aDoclist = pCsr->aBuffer;
@@ -190740,7 +194103,7 @@ struct NodeReader {
static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){
if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){
int nAlloc = nMin;
- char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc);
+ char *a = (char *)sqlite3_realloc64(pBlob->a, nAlloc);
if( a ){
pBlob->nAlloc = nAlloc;
pBlob->a = a;
@@ -191537,7 +194900,7 @@ static int fts3RepackSegdirLevel(
if( nIdx>=nAlloc ){
int *aNew;
nAlloc += 16;
- aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int));
+ aNew = sqlite3_realloc64(aIdx, nAlloc*sizeof(int));
if( !aNew ){
rc = SQLITE_NOMEM;
break;
@@ -191911,7 +195274,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
/* Allocate space for the cursor, filter and writer objects */
const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter);
- pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc);
+ pWriter = (IncrmergeWriter *)sqlite3_malloc64(nAlloc);
if( !pWriter ) return SQLITE_NOMEM;
pFilter = (Fts3SegFilter *)&pWriter[1];
pCsr = (Fts3MultiSegReader *)&pFilter[1];
@@ -192547,7 +195910,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(
return SQLITE_OK;
}
- pRet = (char *)sqlite3_malloc(p->pList->nData);
+ pRet = (char *)sqlite3_malloc64(p->pList->nData);
if( !pRet ) return SQLITE_NOMEM;
nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy);
@@ -192567,7 +195930,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
int iCol /* Column that token must appear in (or -1) */
){
Fts3DeferredToken *pDeferred;
- pDeferred = sqlite3_malloc(sizeof(*pDeferred));
+ pDeferred = sqlite3_malloc64(sizeof(*pDeferred));
if( !pDeferred ){
return SQLITE_NOMEM;
}
@@ -192846,7 +196209,7 @@ typedef sqlite3_int64 i64;
/*
-** Used as an fts3ExprIterate() context when loading phrase doclists to
+** Used as an sqlite3Fts3ExprIterate() context when loading phrase doclists to
** Fts3Expr.aDoclist[]/nDoclist.
*/
typedef struct LoadDoclistCtx LoadDoclistCtx;
@@ -192890,7 +196253,7 @@ struct SnippetFragment {
};
/*
-** This type is used as an fts3ExprIterate() context object while
+** This type is used as an sqlite3Fts3ExprIterate() context object while
** accumulating the data returned by the matchinfo() function.
*/
typedef struct MatchInfo MatchInfo;
@@ -193049,7 +196412,7 @@ static void fts3GetDeltaPosition(char **pp, i64 *piPos){
}
/*
-** Helper function for fts3ExprIterate() (see below).
+** Helper function for sqlite3Fts3ExprIterate() (see below).
*/
static int fts3ExprIterate2(
Fts3Expr *pExpr, /* Expression to iterate phrases of */
@@ -193083,7 +196446,7 @@ static int fts3ExprIterate2(
** Otherwise, SQLITE_OK is returned after a callback has been made for
** all eligible phrase nodes.
*/
-static int fts3ExprIterate(
+SQLITE_PRIVATE int sqlite3Fts3ExprIterate(
Fts3Expr *pExpr, /* Expression to iterate phrases of */
int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */
void *pCtx /* Second argument to pass to callback */
@@ -193092,10 +196455,9 @@ static int fts3ExprIterate(
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
-
/*
-** This is an fts3ExprIterate() callback used while loading the doclists
-** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
+** This is an sqlite3Fts3ExprIterate() callback used while loading the
+** doclists for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
@@ -193127,9 +196489,9 @@ static int fts3ExprLoadDoclists(
int *pnToken /* OUT: Number of tokens in query */
){
int rc; /* Return Code */
- LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */
+ LoadDoclistCtx sCtx = {0,0,0}; /* Context for sqlite3Fts3ExprIterate() */
sCtx.pCsr = pCsr;
- rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx);
+ rc = sqlite3Fts3ExprIterate(pCsr->pExpr,fts3ExprLoadDoclistsCb,(void*)&sCtx);
if( pnPhrase ) *pnPhrase = sCtx.nPhrase;
if( pnToken ) *pnToken = sCtx.nToken;
return rc;
@@ -193142,7 +196504,7 @@ static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
}
static int fts3ExprPhraseCount(Fts3Expr *pExpr){
int nPhrase = 0;
- (void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase);
+ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase);
return nPhrase;
}
@@ -193270,8 +196632,9 @@ static void fts3SnippetDetails(
}
/*
-** This function is an fts3ExprIterate() callback used by fts3BestSnippet().
-** Each invocation populates an element of the SnippetIter.aPhrase[] array.
+** This function is an sqlite3Fts3ExprIterate() callback used by
+** fts3BestSnippet(). Each invocation populates an element of the
+** SnippetIter.aPhrase[] array.
*/
static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx;
@@ -193361,7 +196724,9 @@ static int fts3BestSnippet(
sIter.nSnippet = nSnippet;
sIter.nPhrase = nList;
sIter.iCurrent = -1;
- rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter);
+ rc = sqlite3Fts3ExprIterate(
+ pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter
+ );
if( rc==SQLITE_OK ){
/* Set the *pmSeen output variable. */
@@ -193722,10 +197087,10 @@ static int fts3ExprLHitGather(
}
/*
-** fts3ExprIterate() callback used to collect the "global" matchinfo stats
-** for a single query.
+** sqlite3Fts3ExprIterate() callback used to collect the "global" matchinfo
+** stats for a single query.
**
-** fts3ExprIterate() callback to load the 'global' elements of a
+** sqlite3Fts3ExprIterate() callback to load the 'global' elements of a
** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements
** of the matchinfo array that are constant for all rows returned by the
** current query.
@@ -193760,7 +197125,7 @@ static int fts3ExprGlobalHitsCb(
}
/*
-** fts3ExprIterate() callback used to collect the "local" part of the
+** sqlite3Fts3ExprIterate() callback used to collect the "local" part of the
** FTS3_MATCHINFO_HITS array. The local stats are those elements of the
** array that are different for each row returned by the query.
*/
@@ -193956,7 +197321,7 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
**/
aIter = sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase);
if( !aIter ) return SQLITE_NOMEM;
- (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
+ (void)sqlite3Fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
@@ -194133,11 +197498,11 @@ static int fts3MatchinfoValues(
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
if( rc!=SQLITE_OK ) break;
}
- rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
+ rc = sqlite3Fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
sqlite3Fts3EvalTestDeferred(pCsr, &rc);
if( rc!=SQLITE_OK ) break;
}
- (void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
+ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
break;
}
}
@@ -194360,7 +197725,7 @@ struct TermOffsetCtx {
};
/*
-** This function is an fts3ExprIterate() callback used by sqlite3Fts3Offsets().
+** This function is an sqlite3Fts3ExprIterate() callback used by sqlite3Fts3Offsets().
*/
static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
TermOffsetCtx *p = (TermOffsetCtx *)ctx;
@@ -194442,7 +197807,9 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
*/
sCtx.iCol = iCol;
sCtx.iTerm = 0;
- rc = fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx);
+ rc = sqlite3Fts3ExprIterate(
+ pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx
+ );
if( rc!=SQLITE_OK ) goto offsets_out;
/* Retreive the text stored in column iCol. If an SQL NULL is stored
@@ -197818,6 +201185,13 @@ static int jsonEachBestIndex(
idxMask |= iMask;
}
}
+ if( pIdxInfo->nOrderBy>0
+ && pIdxInfo->aOrderBy[0].iColumn<0
+ && pIdxInfo->aOrderBy[0].desc==0
+ ){
+ pIdxInfo->orderByConsumed = 1;
+ }
+
if( (unusableMask & ~idxMask)!=0 ){
/* If there are any unusable constraints on JSON or ROOT, then reject
** this entire plan */
@@ -198013,10 +201387,10 @@ SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS),
+ SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
+ SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
@@ -198548,7 +201922,7 @@ static int readInt16(u8 *p){
return (p[0]<<8) + p[1];
}
static void readCoord(u8 *p, RtreeCoord *pCoord){
- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
+ assert( (((sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */
#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
pCoord->u = _byteswap_ulong(*(u32*)p);
#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000
@@ -198602,7 +201976,7 @@ static void writeInt16(u8 *p, int i){
}
static int writeCoord(u8 *p, RtreeCoord *pCoord){
u32 i;
- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
+ assert( (((sqlite3_uint64)p)&3)==0 ); /* p is always 4-byte aligned */
assert( sizeof(RtreeCoord)==4 );
assert( sizeof(u32)==4 );
#if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000
@@ -199330,7 +202704,7 @@ static void rtreeNonleafConstraint(
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
|| p->op==RTREE_FALSE );
- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
+ assert( (((sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */
switch( p->op ){
case RTREE_TRUE: return; /* Always satisfied */
case RTREE_FALSE: break; /* Never satisfied */
@@ -199383,7 +202757,7 @@ static void rtreeLeafConstraint(
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
|| p->op==RTREE_FALSE );
pCellData += 8 + p->iCoord*4;
- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
+ assert( (((sqlite3_uint64)pCellData)&3)==0 ); /* 4-byte aligned */
RTREE_DECODE_COORD(eInt, pCellData, xN);
switch( p->op ){
case RTREE_TRUE: return; /* Always satisfied */
@@ -201282,7 +204656,7 @@ static int rtreeUpdate(
rtreeReference(pRtree);
assert(nData>=1);
- cell.iRowid = 0; /* Used only to suppress a compiler warning */
+ memset(&cell, 0, sizeof(cell));
/* Constraint handling. A write operation on an r-tree table may return
** SQLITE_CONSTRAINT for two reasons:
@@ -202755,7 +206129,7 @@ static GeoPoly *geopolyFuncParam(
int nByte;
testcase( pCtx==0 );
if( sqlite3_value_type(pVal)==SQLITE_BLOB
- && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
+ && (nByte = sqlite3_value_bytes(pVal))>=(int)(4+6*sizeof(GeoCoord))
){
const unsigned char *a = sqlite3_value_blob(pVal);
int nVertex;
@@ -202813,6 +206187,7 @@ static void geopolyBlobFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
@@ -202832,6 +206207,7 @@ static void geopolyJsonFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_str *x = sqlite3_str_new(db);
@@ -202913,6 +206289,7 @@ static void geopolyXformFunc(
double F = sqlite3_value_double(argv[6]);
GeoCoord x1, y1, x0, y0;
int ii;
+ (void)argc;
if( p ){
for(ii=0; ii<p->nVertex; ii++){
x0 = GeoX(p,ii);
@@ -202963,6 +206340,7 @@ static void geopolyAreaFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
sqlite3_result_double(context, geopolyArea(p));
sqlite3_free(p);
@@ -202988,6 +206366,7 @@ static void geopolyCcwFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
if( geopolyArea(p)<0.0 ){
int ii, jj;
@@ -203042,6 +206421,7 @@ static void geopolyRegularFunc(
int n = sqlite3_value_int(argv[3]);
int i;
GeoPoly *p;
+ (void)argc;
if( n<3 || r<=0.0 ) return;
if( n>1000 ) n = 1000;
@@ -203151,6 +206531,7 @@ static void geopolyBBoxFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyBBox(context, argv[0], 0, 0);
+ (void)argc;
if( p ){
sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
@@ -203178,6 +206559,7 @@ static void geopolyBBoxStep(
){
RtreeCoord a[4];
int rc = SQLITE_OK;
+ (void)argc;
(void)geopolyBBox(context, argv[0], a, &rc);
if( rc==SQLITE_OK ){
GeoBBox *pBBox;
@@ -203266,6 +206648,8 @@ static void geopolyContainsPointFunc(
int v = 0;
int cnt = 0;
int ii;
+ (void)argc;
+
if( p1==0 ) return;
for(ii=0; ii<p1->nVertex-1; ii++){
v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii),
@@ -203305,6 +206689,7 @@ static void geopolyWithinFunc(
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
+ (void)argc;
if( p1 && p2 ){
int x = geopolyOverlap(p1, p2);
if( x<0 ){
@@ -203635,6 +207020,7 @@ static void geopolyOverlapFunc(
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
+ (void)argc;
if( p1 && p2 ){
int x = geopolyOverlap(p1, p2);
if( x<0 ){
@@ -203655,8 +207041,12 @@ static void geopolyDebugFunc(
int argc,
sqlite3_value **argv
){
+ (void)context;
+ (void)argc;
#ifdef GEOPOLY_ENABLE_DEBUG
geo_debug = sqlite3_value_int(argv[0]);
+#else
+ (void)argv;
#endif
}
@@ -203684,6 +207074,7 @@ static int geopolyInit(
sqlite3_str *pSql;
char *zSql;
int ii;
+ (void)pAux;
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
@@ -203800,6 +207191,7 @@ static int geopolyFilter(
RtreeNode *pRoot = 0;
int rc = SQLITE_OK;
int iCell = 0;
+ (void)idxStr;
rtreeReference(pRtree);
@@ -203926,6 +207318,7 @@ static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iRowidTerm = -1;
int iFuncTerm = -1;
int idxNum = 0;
+ (void)tab;
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
@@ -204146,7 +207539,7 @@ static int geopolyUpdate(
sqlite3_free(p);
nChange = 1;
}
- for(jj=1; jj<pRtree->nAux; jj++){
+ for(jj=1; jj<nData-2; jj++){
nChange++;
sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
}
@@ -204172,6 +207565,8 @@ static int geopolyFindFunction(
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg
){
+ (void)pVtab;
+ (void)nArg;
if( sqlite3_stricmp(zName, "geopoly_overlap")==0 ){
*pxFunc = geopolyOverlapFunc;
*ppArg = 0;
@@ -204241,7 +207636,7 @@ static int sqlite3_geopoly_init(sqlite3 *db){
} aAgg[] = {
{ geopolyBBoxStep, geopolyBBoxFinal, "geopoly_group_bbox" },
};
- int i;
+ unsigned int i;
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
int enc;
if( aFunc[i].bPure ){
@@ -204749,8 +208144,9 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
if( U_SUCCESS(status) ){
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
- }else{
- assert(!pExpr);
+ pExpr = sqlite3_get_auxdata(p, 0);
+ }
+ if( !pExpr ){
icuFunctionError(p, "uregex_open", status);
return;
}
@@ -205461,7 +208857,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** The order of the columns in the data_% table does not matter.
**
** Instead of a regular table, the RBU database may also contain virtual
-** tables or view named using the data_<target> naming scheme.
+** tables or views named using the data_<target> naming scheme.
**
** Instead of the plain data_<target> naming scheme, RBU database tables
** may also be named data<integer>_<target>, where <integer> is any sequence
@@ -205474,7 +208870,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
**
** If the target database table is a virtual table or a table that has no
** PRIMARY KEY declaration, the data_% table must also contain a column
-** named "rbu_rowid". This column is mapped to the tables implicit primary
+** named "rbu_rowid". This column is mapped to the table's implicit primary
** key column - "rowid". Virtual tables for which the "rowid" column does
** not function like a primary key value cannot be updated using RBU. For
** example, if the target db contains either of the following:
@@ -205908,6 +209304,34 @@ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
/*
+** As part of applying an RBU update or performing an RBU vacuum operation,
+** the system must at one point move the *-oal file to the equivalent *-wal
+** path. Normally, it does this by invoking POSIX function rename(2) directly.
+** Except on WINCE platforms, where it uses win32 API MoveFileW(). This
+** function may be used to register a callback that the RBU module will invoke
+** instead of one of these APIs.
+**
+** If a callback is registered with an RBU handle, it invokes it instead
+** of rename(2) when it needs to move a file within the file-system. The
+** first argument passed to the xRename() callback is a copy of the second
+** argument (pArg) passed to this function. The second is the full path
+** to the file to move and the third the full path to which it should be
+** moved. The callback function should return SQLITE_OK to indicate
+** success. If an error occurs, it should return an SQLite error code.
+** In this case the RBU operation will be abandoned and the error returned
+** to the RBU user.
+**
+** Passing a NULL pointer in place of the xRename argument to this function
+** restores the default behaviour.
+*/
+SQLITE_API void sqlite3rbu_rename_handler(
+ sqlite3rbu *pRbu,
+ void *pArg,
+ int (*xRename)(void *pArg, const char *zOld, const char *zNew)
+);
+
+
+/*
** Create an RBU VFS named zName that accesses the underlying file-system
** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
** then the new RBU VFS uses the default system VFS to access the file-system.
@@ -206274,6 +209698,8 @@ struct sqlite3rbu {
int nPagePerSector; /* Pages per sector for pTargetFd */
i64 iOalSz;
i64 nPhaseOneStep;
+ void *pRenameArg;
+ int (*xRename)(void*, const char*, const char*);
/* The following state variables are used as part of the incremental
** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
@@ -208662,7 +212088,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
if( p->zState==0 ){
const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
- p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
+ p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile);
}
}
@@ -208910,11 +212336,11 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
** no-ops. These locks will not be released until the connection
** is closed.
**
- ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL
+ ** * Attempting to xSync() the database file causes an SQLITE_NOTICE
** error.
**
** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
- ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[]
+ ** checkpoint below fails with SQLITE_NOTICE, and leaves the aFrame[]
** array populated with a set of (frame -> page) mappings. Because the
** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
** data from the wal file into the database file according to the
@@ -208924,7 +212350,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
int rc2;
p->eStage = RBU_STAGE_CAPTURE;
rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
- if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
+ if( rc2!=SQLITE_NOTICE ) p->rc = rc2;
}
if( p->rc==SQLITE_OK && p->nFrame>0 ){
@@ -208970,7 +212396,7 @@ static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
if( pRbu->mLock!=mReq ){
pRbu->rc = SQLITE_BUSY;
- return SQLITE_INTERNAL;
+ return SQLITE_NOTICE_RBU;
}
pRbu->pgsz = iAmt;
@@ -209122,32 +212548,7 @@ static void rbuMoveOalFile(sqlite3rbu *p){
}
if( p->rc==SQLITE_OK ){
-#if defined(_WIN32_WCE)
- {
- LPWSTR zWideOal;
- LPWSTR zWideWal;
-
- zWideOal = rbuWinUtf8ToUnicode(zOal);
- if( zWideOal ){
- zWideWal = rbuWinUtf8ToUnicode(zWal);
- if( zWideWal ){
- if( MoveFileW(zWideOal, zWideWal) ){
- p->rc = SQLITE_OK;
- }else{
- p->rc = SQLITE_IOERR;
- }
- sqlite3_free(zWideWal);
- }else{
- p->rc = SQLITE_IOERR_NOMEM;
- }
- sqlite3_free(zWideOal);
- }else{
- p->rc = SQLITE_IOERR_NOMEM;
- }
- }
-#else
- p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
-#endif
+ p->rc = p->xRename(p->pRenameArg, zOal, zWal);
}
if( p->rc!=SQLITE_OK
@@ -209734,7 +213135,8 @@ static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
static void rbuDeleteOalFile(sqlite3rbu *p){
char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget);
if( zOal ){
- sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ sqlite3_vfs *pVfs = 0;
+ sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_VFS_POINTER, &pVfs);
assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 );
pVfs->xDelete(pVfs, zOal, 0);
sqlite3_free(zOal);
@@ -209886,6 +213288,7 @@ static sqlite3rbu *openRbuHandle(
/* Create the custom VFS. */
memset(p, 0, sizeof(sqlite3rbu));
+ sqlite3rbu_rename_handler(p, 0, 0);
rbuCreateVfs(p);
/* Open the target, RBU and state databases */
@@ -210277,6 +213680,54 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
return rc;
}
+/*
+** Default xRename callback for RBU.
+*/
+static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
+ int rc = SQLITE_OK;
+#if defined(_WIN32_WCE)
+ {
+ LPWSTR zWideOld;
+ LPWSTR zWideNew;
+
+ zWideOld = rbuWinUtf8ToUnicode(zOld);
+ if( zWideOld ){
+ zWideNew = rbuWinUtf8ToUnicode(zNew);
+ if( zWideNew ){
+ if( MoveFileW(zWideOld, zWideNew) ){
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_IOERR;
+ }
+ sqlite3_free(zWideNew);
+ }else{
+ rc = SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_free(zWideOld);
+ }else{
+ rc = SQLITE_IOERR_NOMEM;
+ }
+ }
+#else
+ rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK;
+#endif
+ return rc;
+}
+
+SQLITE_API void sqlite3rbu_rename_handler(
+ sqlite3rbu *pRbu,
+ void *pArg,
+ int (*xRename)(void *pArg, const char *zOld, const char *zNew)
+){
+ if( xRename ){
+ pRbu->xRename = xRename;
+ pRbu->pRenameArg = pArg;
+ }else{
+ pRbu->xRename = xDefaultRename;
+ pRbu->pRenameArg = 0;
+ }
+}
+
/**************************************************************************
** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
** of a standard VFS in the following ways:
@@ -210333,7 +213784,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
** database file are recorded. xShmLock() calls to unlock the same
** locks are no-ops (so that once obtained, these locks are never
** relinquished). Finally, calls to xSync() on the target database
-** file fail with SQLITE_INTERNAL errors.
+** file fail with SQLITE_NOTICE errors.
*/
static void rbuUnlockShm(rbu_file *p){
@@ -210442,9 +213893,12 @@ static int rbuVfsClose(sqlite3_file *pFile){
sqlite3_free(p->zDel);
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ const sqlite3_io_methods *pMeth = p->pReal->pMethods;
rbuMainlistRemove(p);
rbuUnlockShm(p);
- p->pReal->pMethods->xShmUnmap(p->pReal, 0);
+ if( pMeth->iVersion>1 && pMeth->xShmUnmap ){
+ pMeth->xShmUnmap(p->pReal, 0);
+ }
}
else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
rbuUpdateTempSize(p, 0);
@@ -210612,7 +214066,7 @@ static int rbuVfsSync(sqlite3_file *pFile, int flags){
rbu_file *p = (rbu_file *)pFile;
if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
- return SQLITE_INTERNAL;
+ return SQLITE_NOTICE_RBU;
}
return SQLITE_OK;
}
@@ -210903,6 +214357,25 @@ static int rbuVfsOpen(
rbuVfsShmUnmap, /* xShmUnmap */
0, 0 /* xFetch, xUnfetch */
};
+ static sqlite3_io_methods rbuvfs_io_methods1 = {
+ 1, /* iVersion */
+ rbuVfsClose, /* xClose */
+ rbuVfsRead, /* xRead */
+ rbuVfsWrite, /* xWrite */
+ rbuVfsTruncate, /* xTruncate */
+ rbuVfsSync, /* xSync */
+ rbuVfsFileSize, /* xFileSize */
+ rbuVfsLock, /* xLock */
+ rbuVfsUnlock, /* xUnlock */
+ rbuVfsCheckReservedLock, /* xCheckReservedLock */
+ rbuVfsFileControl, /* xFileControl */
+ rbuVfsSectorSize, /* xSectorSize */
+ rbuVfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, 0, 0, 0, 0, 0
+ };
+
+
+
rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
rbu_file *pFd = (rbu_file *)pFile;
@@ -210957,10 +214430,15 @@ static int rbuVfsOpen(
rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags);
}
if( pFd->pReal->pMethods ){
+ const sqlite3_io_methods *pMeth = pFd->pReal->pMethods;
/* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods
** pointer and, if the file is a main database file, link it into the
** mutex protected linked list of all such files. */
- pFile->pMethods = &rbuvfs_io_methods;
+ if( pMeth->iVersion<2 || pMeth->xShmLock==0 ){
+ pFile->pMethods = &rbuvfs_io_methods1;
+ }else{
+ pFile->pMethods = &rbuvfs_io_methods;
+ }
if( flags & SQLITE_OPEN_MAIN_DB ){
rbuMainlistAdd(pFd);
}
@@ -211393,6 +214871,7 @@ static int statConnect(
StatTable *pTab = 0;
int rc = SQLITE_OK;
int iDb;
+ (void)pAux;
if( argc>=4 ){
Token nm;
@@ -211446,6 +214925,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iSchema = -1;
int iName = -1;
int iAgg = -1;
+ (void)tab;
/* Look for a valid schema=? constraint. If found, change the idxNum to
** 1 and request the value of that constraint be sent to xFilter. And
@@ -211971,6 +215451,8 @@ static int statFilter(
int iArg = 0; /* Count of argv[] parameters used so far */
int rc = SQLITE_OK; /* Result of this operation */
const char *zName = 0; /* Only provide analysis of this table */
+ (void)argc;
+ (void)idxStr;
statResetCsr(pCsr);
sqlite3_finalize(pCsr->pStmt);
@@ -212054,16 +215536,16 @@ static int statColumn(
}
break;
case 4: /* ncell */
- sqlite3_result_int(ctx, pCsr->nCell);
+ sqlite3_result_int64(ctx, pCsr->nCell);
break;
case 5: /* payload */
- sqlite3_result_int(ctx, pCsr->nPayload);
+ sqlite3_result_int64(ctx, pCsr->nPayload);
break;
case 6: /* unused */
- sqlite3_result_int(ctx, pCsr->nUnused);
+ sqlite3_result_int64(ctx, pCsr->nUnused);
break;
case 7: /* mx_payload */
- sqlite3_result_int(ctx, pCsr->nMxPayload);
+ sqlite3_result_int64(ctx, pCsr->nMxPayload);
break;
case 8: /* pgoffset */
if( !pCsr->isAgg ){
@@ -212071,7 +215553,7 @@ static int statColumn(
}
break;
case 9: /* pgsize */
- sqlite3_result_int(ctx, pCsr->szPage);
+ sqlite3_result_int64(ctx, pCsr->szPage);
break;
case 10: { /* schema */
sqlite3 *db = sqlite3_context_db_handle(ctx);
@@ -212205,6 +215687,10 @@ static int dbpageConnect(
){
DbpageTable *pTab = 0;
int rc = SQLITE_OK;
+ (void)pAux;
+ (void)argc;
+ (void)argv;
+ (void)pzErr;
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
rc = sqlite3_declare_vtab(db,
@@ -212243,6 +215729,7 @@ static int dbpageDisconnect(sqlite3_vtab *pVtab){
static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
int iPlan = 0;
+ (void)tab;
/* If there is a schema= constraint, it must be honored. Report a
** ridiculously large estimated cost if the schema= constraint is
@@ -212358,6 +215845,8 @@ static int dbpageFilter(
sqlite3 *db = pTab->db;
Btree *pBt;
+ (void)idxStr;
+
/* Default setting is no rows of result */
pCsr->pgno = 1;
pCsr->mxPgno = 0;
@@ -212372,7 +215861,7 @@ static int dbpageFilter(
pCsr->iDb = 0;
}
pBt = db->aDb[pCsr->iDb].pBt;
- if( pBt==0 ) return SQLITE_OK;
+ if( NEVER(pBt==0) ) return SQLITE_OK;
pCsr->pPager = sqlite3BtreePager(pBt);
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
@@ -212407,12 +215896,18 @@ static int dbpageColumn(
}
case 1: { /* data */
DbPage *pDbPage = 0;
- rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
- if( rc==SQLITE_OK ){
- sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
- SQLITE_TRANSIENT);
+ if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
+ /* The pending byte page. Assume it is zeroed out. Attempting to
+ ** request this page from the page is an SQLITE_CORRUPT error. */
+ sqlite3_result_zeroblob(ctx, pCsr->szPage);
+ }else{
+ rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
+ SQLITE_TRANSIENT);
+ }
+ sqlite3PagerUnref(pDbPage);
}
- sqlite3PagerUnref(pDbPage);
break;
}
default: { /* schema */
@@ -212421,7 +215916,7 @@ static int dbpageColumn(
break;
}
}
- return SQLITE_OK;
+ return rc;
}
static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
@@ -212447,6 +215942,7 @@ static int dbpageUpdate(
Pager *pPager;
int szPage;
+ (void)pRowid;
if( pTab->db->flags & SQLITE_Defensive ){
zErr = "read-only";
goto update_fail;
@@ -212456,18 +215952,20 @@ static int dbpageUpdate(
goto update_fail;
}
pgno = sqlite3_value_int(argv[0]);
- if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL
+ || (Pgno)sqlite3_value_int(argv[1])!=pgno
+ ){
zErr = "cannot insert";
goto update_fail;
}
zSchema = (const char*)sqlite3_value_text(argv[4]);
- iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
- if( iDb<0 ){
+ iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1;
+ if( NEVER(iDb<0) ){
zErr = "no such schema";
goto update_fail;
}
pBt = pTab->db->aDb[iDb].pBt;
- if( pgno<1 || pBt==0 || pgno>sqlite3BtreeLastPage(pBt) ){
+ if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){
zErr = "bad page number";
goto update_fail;
}
@@ -212481,11 +215979,12 @@ static int dbpageUpdate(
pPager = sqlite3BtreePager(pBt);
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
if( rc==SQLITE_OK ){
- rc = sqlite3PagerWrite(pDbPage);
- if( rc==SQLITE_OK ){
- memcpy(sqlite3PagerGetData(pDbPage),
- sqlite3_value_blob(argv[3]),
- szPage);
+ const void *pData = sqlite3_value_blob(argv[3]);
+ assert( pData!=0 || pTab->db->mallocFailed );
+ if( pData
+ && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK
+ ){
+ memcpy(sqlite3PagerGetData(pDbPage), pData, szPage);
}
}
sqlite3PagerUnref(pDbPage);
@@ -212507,7 +216006,7 @@ static int dbpageBegin(sqlite3_vtab *pVtab){
int i;
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0);
+ if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
}
return SQLITE_OK;
}
@@ -214052,6 +217551,8 @@ static void xPreUpdate(
int nDb = sqlite3Strlen30(zDb);
assert( sqlite3_mutex_held(db->mutex) );
+ (void)iKey1;
+ (void)iKey2;
for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
SessionTable *pTab;
@@ -214128,6 +217629,7 @@ static int sessionDiffCount(void *pCtx){
return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt);
}
static int sessionDiffDepth(void *pCtx){
+ (void)pCtx;
return 0;
}
@@ -214201,7 +217703,6 @@ static char *sessionExprCompareOther(
}
static char *sessionSelectFindNew(
- int nCol,
const char *zDb1, /* Pick rows in this db only */
const char *zDb2, /* But not in this one */
const char *zTbl, /* Table name */
@@ -214225,7 +217726,7 @@ static int sessionDiffFindNew(
char *zExpr
){
int rc = SQLITE_OK;
- char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr);
+ char *zStmt = sessionSelectFindNew(zDb1, zDb2, pTab->zName,zExpr);
if( zStmt==0 ){
rc = SQLITE_NOMEM;
@@ -215880,6 +219381,22 @@ static int sessionChangesetNextOne(
if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE;
else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT;
}
+
+ /* If this is an UPDATE that is part of a changeset, then check that
+ ** there are no fields in the old.* record that are not (a) PK fields,
+ ** or (b) also present in the new.* record.
+ **
+ ** Such records are technically corrupt, but the rebaser was at one
+ ** point generating them. Under most circumstances this is benign, but
+ ** can cause spurious SQLITE_RANGE errors when applying the changeset. */
+ if( p->bPatchset==0 && p->op==SQLITE_UPDATE){
+ for(i=0; i<p->nCol; i++){
+ if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){
+ sqlite3ValueFree(p->apValue[i]);
+ p->apValue[i] = 0;
+ }
+ }
+ }
}
return SQLITE_ROW;
@@ -216726,7 +220243,6 @@ static int sessionBindRow(
** UPDATE, bind values from the old.* record.
*/
static int sessionSeekToRow(
- sqlite3 *db, /* Database handle */
sqlite3_changeset_iter *pIter, /* Changeset iterator */
u8 *abPK, /* Primary key flags array */
sqlite3_stmt *pSelect /* SELECT statement from sessionSelectRow() */
@@ -216856,7 +220372,7 @@ static int sessionConflictHandler(
/* Bind the new.* PRIMARY KEY values to the SELECT statement. */
if( pbReplace ){
- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
+ rc = sessionSeekToRow(pIter, p->abPK, p->pSelect);
}else{
rc = SQLITE_OK;
}
@@ -217030,7 +220546,7 @@ static int sessionApplyOneOp(
/* Check if there is a conflicting row. For sqlite_stat1, this needs
** to be done using a SELECT, as there is no PRIMARY KEY in the
** database schema to throw an exception if a duplicate is inserted. */
- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
+ rc = sessionSeekToRow(pIter, p->abPK, p->pSelect);
if( rc==SQLITE_ROW ){
rc = SQLITE_CONSTRAINT;
sqlite3_reset(p->pSelect);
@@ -218076,7 +221592,7 @@ static void sessionAppendPartialUpdate(
if( !pIter->abPK[i] && a1[0] ) bData = 1;
memcpy(pOut, a1, n1);
pOut += n1;
- }else if( a2[0]!=0xFF ){
+ }else if( a2[0]!=0xFF && a1[0] ){
bData = 1;
memcpy(pOut, a2, n2);
pOut += n2;
@@ -219233,7 +222749,7 @@ static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...);
static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
#define fts5BufferZero(x) sqlite3Fts5BufferZero(x)
-#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c)
+#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,(i64)c)
#define fts5BufferFree(a) sqlite3Fts5BufferFree(a)
#define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d)
#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d)
@@ -223680,6 +227196,19 @@ static int sqlite3Fts5ExprNew(
}
/*
+** Assuming that buffer z is at least nByte bytes in size and contains a
+** valid utf-8 string, return the number of characters in the string.
+*/
+static int fts5ExprCountChar(const char *z, int nByte){
+ int nRet = 0;
+ int ii;
+ for(ii=0; ii<nByte; ii++){
+ if( (z[ii] & 0xC0)!=0x80 ) nRet++;
+ }
+ return nRet;
+}
+
+/*
** This function is only called when using the special 'trigram' tokenizer.
** Argument zText contains the text of a LIKE or GLOB pattern matched
** against column iCol. This function creates and compiles an FTS5 MATCH
@@ -223716,7 +227245,8 @@ static int sqlite3Fts5ExprPattern(
if( i==nText
|| zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2]
){
- if( i-iFirst>=3 ){
+
+ if( fts5ExprCountChar(&zText[iFirst], i-iFirst)>=3 ){
int jj;
zExpr[iOut++] = '"';
for(jj=iFirst; jj<i; jj++){
@@ -227077,6 +230607,8 @@ static void sqlite3Fts5HashScanEntry(
# error "FTS5_MAX_PREFIX_INDEXES is too large"
#endif
+#define FTS5_MAX_LEVEL 64
+
/*
** Details:
**
@@ -231110,7 +234642,9 @@ static void fts5WriteAppendRowid(
fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
}else{
assert_nc( p->rc || iRowid>pWriter->iPrevRowid );
- fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid);
+ fts5BufferAppendVarint(&p->rc, &pPage->buf,
+ (u64)iRowid - (u64)pWriter->iPrevRowid
+ );
}
pWriter->iPrevRowid = iRowid;
pWriter->bFirstRowidInDoclist = 0;
@@ -231789,10 +235323,10 @@ static Fts5Structure *fts5IndexOptimizeStruct(
if( pNew ){
Fts5StructureLevel *pLvl;
nByte = nSeg * sizeof(Fts5StructureSegment);
- pNew->nLevel = pStruct->nLevel+1;
+ pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL);
pNew->nRef = 1;
pNew->nWriteCounter = pStruct->nWriteCounter;
- pLvl = &pNew->aLevel[pStruct->nLevel];
+ pLvl = &pNew->aLevel[pNew->nLevel-1];
pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pLvl->aSeg ){
int iLvl, iSeg;
@@ -231874,7 +235408,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
static void fts5AppendRowid(
Fts5Index *p,
- i64 iDelta,
+ u64 iDelta,
Fts5Iter *pUnused,
Fts5Buffer *pBuf
){
@@ -231884,7 +235418,7 @@ static void fts5AppendRowid(
static void fts5AppendPoslist(
Fts5Index *p,
- i64 iDelta,
+ u64 iDelta,
Fts5Iter *pMulti,
Fts5Buffer *pBuf
){
@@ -231959,10 +235493,10 @@ static void fts5MergeAppendDocid(
}
#endif
-#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
- assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
- fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \
- (iLastRowid) = (iRowid); \
+#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
+ assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
+ fts5BufferSafeAppendVarint((pBuf), (u64)(iRowid) - (u64)(iLastRowid)); \
+ (iLastRowid) = (iRowid); \
}
/*
@@ -232094,7 +235628,7 @@ static void fts5MergePrefixLists(
/* Initialize a doclist-iterator for each input buffer. Arrange them in
** a linked-list starting at pHead in ascending order of rowid. Avoid
** linking any iterators already at EOF into the linked list at all. */
- assert( nBuf+1<=sizeof(aMerger)/sizeof(aMerger[0]) );
+ assert( nBuf+1<=(int)(sizeof(aMerger)/sizeof(aMerger[0])) );
memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1));
pHead = &aMerger[nBuf];
fts5DoclistIterInit(p1, &pHead->iter);
@@ -232233,7 +235767,7 @@ static void fts5SetupPrefixIter(
int nMerge = 1;
void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
- void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
+ void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
xMerge = fts5MergeRowidLists;
xAppend = fts5AppendRowid;
@@ -232272,7 +235806,7 @@ static void fts5SetupPrefixIter(
Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
p1->xSetOutputs(p1, pSeg);
if( p1->base.nData ){
- xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
+ xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
iLastRowid = p1->base.iRowid;
}
}
@@ -232320,7 +235854,7 @@ static void fts5SetupPrefixIter(
iLastRowid = 0;
}
- xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
+ xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
iLastRowid = p1->base.iRowid;
}
@@ -233299,6 +236833,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum
/* If this is a new term, query for it. Update cksum3 with the results. */
fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
+ if( p->rc ) break;
if( eDetail==FTS5_DETAIL_NONE ){
if( 0==fts5MultiIterIsEmpty(p, pIter) ){
@@ -234103,7 +237638,7 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
break;
case FTS5_SYNC:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState==1 || p->ts.eState==2 );
p->ts.eState = 2;
break;
@@ -234118,21 +237653,21 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
break;
case FTS5_SAVEPOINT:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState>=1 );
assert( iSavepoint>=0 );
assert( iSavepoint>=p->ts.iSavepoint );
p->ts.iSavepoint = iSavepoint;
break;
case FTS5_RELEASE:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState>=1 );
assert( iSavepoint>=0 );
assert( iSavepoint<=p->ts.iSavepoint );
p->ts.iSavepoint = iSavepoint-1;
break;
case FTS5_ROLLBACKTO:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState>=1 );
assert( iSavepoint>=-1 );
/* The following assert() can fail if another vtab strikes an error
** within an xSavepoint() call then SQLite calls xRollbackTo() - without
@@ -235468,7 +239003,7 @@ static int fts5UpdateMethod(
int rc = SQLITE_OK; /* Return code */
/* A transaction must be open when this is called. */
- assert( pTab->ts.eState==1 );
+ assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
assert( pVtab->zErrMsg==0 );
assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
@@ -236636,7 +240171,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668e6603", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2023-02-21 18:09:37 05941c2a04037fc3ed2ffae11f5d2260706f89431f463518740f72ada350866d", -1, SQLITE_TRANSIENT);
}
/*
@@ -236709,7 +240244,9 @@ static int fts5Init(sqlite3 *db){
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
- db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0
+ db, "fts5_source_id", 0,
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
+ p, fts5SourceIdFunc, 0, 0
);
}
}
@@ -241374,6 +244911,10 @@ static int stmtConnect(
#define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */
+ (void)pAux;
+ (void)argc;
+ (void)argv;
+ (void)pzErr;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep,"
"reprep,run,mem)");
@@ -241493,6 +245034,10 @@ static int stmtFilter(
sqlite3_int64 iRowid = 1;
StmtRow **ppRow = 0;
+ (void)idxNum;
+ (void)idxStr;
+ (void)argc;
+ (void)argv;
stmtCsrReset(pCur);
ppRow = &pCur->pRow;
for(p=sqlite3_next_stmt(pCur->db, 0); p; p=sqlite3_next_stmt(pCur->db, p)){
@@ -241548,6 +245093,7 @@ static int stmtBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
+ (void)tab;
pIdxInfo->estimatedCost = (double)500;
pIdxInfo->estimatedRows = 500;
return SQLITE_OK;
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index f0df724d7b..4c6addac26 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.39.2"
-#define SQLITE_VERSION_NUMBER 3039002
-#define SQLITE_SOURCE_ID "2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668e6603"
+#define SQLITE_VERSION "3.41.0"
+#define SQLITE_VERSION_NUMBER 3041000
+#define SQLITE_SOURCE_ID "2023-02-21 18:09:37 05941c2a04037fc3ed2ffae11f5d2260706f89431f463518740f72ada350866d"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -563,6 +563,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
@@ -670,13 +671,17 @@ SQLITE_API int sqlite3_exec(
**
** SQLite uses one of these integer values as the second
** argument to calls it makes to the xLock() and xUnlock() methods
-** of an [sqlite3_io_methods] object.
+** of an [sqlite3_io_methods] object. These values are ordered from
+** lest restrictive to most restrictive.
+**
+** The argument to xLock() is always SHARED or higher. The argument to
+** xUnlock is either SHARED or NONE.
*/
-#define SQLITE_LOCK_NONE 0
-#define SQLITE_LOCK_SHARED 1
-#define SQLITE_LOCK_RESERVED 2
-#define SQLITE_LOCK_PENDING 3
-#define SQLITE_LOCK_EXCLUSIVE 4
+#define SQLITE_LOCK_NONE 0 /* xUnlock() only */
+#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
+#define SQLITE_LOCK_RESERVED 2 /* xLock() only */
+#define SQLITE_LOCK_PENDING 3 /* xLock() only */
+#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
/*
** CAPI3REF: Synchronization Type Flags
@@ -754,7 +759,14 @@ struct sqlite3_file {
** <li> [SQLITE_LOCK_PENDING], or
** <li> [SQLITE_LOCK_EXCLUSIVE].
** </ul>
-** xLock() increases the lock. xUnlock() decreases the lock.
+** 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
+** 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
+** 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,
** PENDING, or EXCLUSIVE lock on the file. It returns true
@@ -859,9 +871,8 @@ struct sqlite3_io_methods {
** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
-** into an integer that the pArg argument points to. This capability
-** is used during testing and is only available when the SQLITE_TEST
-** compile-time option is used.
+** into an integer that the pArg argument points to.
+** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
**
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
@@ -1165,7 +1176,6 @@ struct sqlite3_io_methods {
** in wal mode after the client has finished copying pages from the wal
** file to the database file, but before the *-shm file is updated to
** record the fact that the pages have been checkpointed.
-** </ul>
**
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
@@ -1178,10 +1188,16 @@ struct sqlite3_io_methods {
** the database is not a wal-mode db, or if there is no such connection in any
** other process. This opcode cannot be used to detect transactions opened
** by clients within the current process, only within other processes.
-** </ul>
**
** <li>[[SQLITE_FCNTL_CKSM_FILE]]
-** Used by the cksmvfs VFS module only.
+** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the
+** [checksum VFS shim] only.
+**
+** <li>[[SQLITE_FCNTL_RESET_CACHE]]
+** If there is currently no transaction open on the database, and the
+** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
+** purges the contents of the in-memory page cache. If there is an open
+** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1224,6 +1240,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_CKPT_START 39
#define SQLITE_FCNTL_EXTERNAL_READER 40
#define SQLITE_FCNTL_CKSM_FILE 41
+#define SQLITE_FCNTL_RESET_CACHE 42
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1254,6 +1271,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
+** CAPI3REF: File Name
+**
+** Type [sqlite3_filename] is used by SQLite to pass filenames to the
+** xOpen method of a [VFS]. It may be cast to (const char*) and treated
+** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
+** may also be passed to special APIs such as:
+**
+** <ul>
+** <li> sqlite3_filename_database()
+** <li> sqlite3_filename_journal()
+** <li> sqlite3_filename_wal()
+** <li> sqlite3_uri_parameter()
+** <li> sqlite3_uri_boolean()
+** <li> sqlite3_uri_int64()
+** <li> sqlite3_uri_key()
+** </ul>
+*/
+typedef const char *sqlite3_filename;
+
+/*
** CAPI3REF: OS Interface Object
**
** An instance of the sqlite3_vfs object defines the interface between
@@ -1431,7 +1468,7 @@ struct sqlite3_vfs {
sqlite3_vfs *pNext; /* Next registered VFS */
const char *zName; /* Name of this virtual file system */
void *pAppData; /* Pointer to application-specific data */
- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
+ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
int flags, int *pOutFlags);
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
@@ -2147,7 +2184,7 @@ struct sqlite3_mem_methods {
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
** when the "current value" returned by
-** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
+** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
** Any attempt to change the lookaside memory configuration when lookaside
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
@@ -2297,8 +2334,12 @@ struct sqlite3_mem_methods {
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
** </ol>
** Because resetting a database is destructive and irreversible, the
-** process requires the use of this obscure API and multiple steps to help
-** ensure that it does not happen by accident.
+** process requires the use of this obscure API and multiple steps to
+** help ensure that it does not happen by accident. Because this
+** feature must be capable of resetting corrupt databases, and
+** shutting down virtual tables may require access to that corrupt
+** storage, the library must abandon any installed virtual tables
+** without calling their xDestroy() methods.
**
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
@@ -2309,6 +2350,7 @@ struct sqlite3_mem_methods {
** <ul>
** <li> The [PRAGMA writable_schema=ON] statement.
** <li> The [PRAGMA journal_mode=OFF] statement.
+** <li> The [PRAGMA schema_version=N] statement.
** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables].
** </ul>
@@ -2636,8 +2678,12 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
** ^A call to sqlite3_interrupt(D) that occurs when there are no running
** SQL statements is a no-op and has no effect on SQL statements
** that are started after the sqlite3_interrupt() call returns.
+**
+** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether
+** or not an interrupt is currently in effect for [database connection] D.
*/
SQLITE_API void sqlite3_interrupt(sqlite3*);
+SQLITE_API int sqlite3_is_interrupted(sqlite3*);
/*
** CAPI3REF: Determine If An SQL Statement Is Complete
@@ -3255,8 +3301,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
** information as is provided by the [sqlite3_profile()] callback.
** ^The P argument is a pointer to the [prepared statement] and the
-** X argument points to a 64-bit integer which is the estimated of
-** the number of nanosecond that the prepared statement took to run.
+** X argument points to a 64-bit integer which is approximately
+** the number of nanoseconds that the prepared statement took to run.
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
**
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
@@ -3319,7 +3365,7 @@ SQLITE_API int sqlite3_trace_v2(
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
-** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for
+** [sqlite3_step()] and [sqlite3_prepare()] and similar for
** database connection D. An example use for this
** interface is to keep a GUI updated during a large query.
**
@@ -3344,6 +3390,13 @@ SQLITE_API int sqlite3_trace_v2(
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
** database connections for the meaning of "modify" in this paragraph.
**
+** The progress handler callback would originally only be invoked from the
+** bytecode engine. It still might be invoked during [sqlite3_prepare()]
+** and similar because those routines might force a reparse of the schema
+** which involves running the bytecode engine. However, beginning with
+** SQLite version 3.41.0, the progress handler callback might also be
+** invoked directly from [sqlite3_prepare()] while analyzing and generating
+** code for complex queries.
*/
SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
@@ -3380,13 +3433,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
**
** <dl>
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
-** <dd>The database is opened in read-only mode. If the database does not
-** already exist, an error is returned.</dd>)^
+** <dd>The database is opened in read-only mode. If the database does
+** not already exist, an error is returned.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
-** <dd>The database is opened for reading and writing if possible, or reading
-** only if the file is write protected by the operating system. In either
-** case the database must already exist, otherwise an error is returned.</dd>)^
+** <dd>The database is opened for reading and writing if possible, or
+** reading only if the file is write protected by the operating
+** system. In either case the database must already exist, otherwise
+** an error is returned. For historical reasons, if opening in
+** read-write mode fails due to OS-level permissions, an attempt is
+** made to open it in read-only mode. [sqlite3_db_readonly()] can be
+** used to determine whether the database is actually
+** read-write.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
** <dd>The database is opened for reading and writing, and is created if
@@ -3424,6 +3482,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** <dd>The database is opened [shared cache] enabled, overriding
** the default shared cache setting provided by
** [sqlite3_enable_shared_cache()].)^
+** The [use of shared cache mode is discouraged] and hence shared cache
+** capabilities may be omitted from many builds of SQLite. In such cases,
+** this option is a no-op.
**
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
** <dd>The database is opened [shared cache] disabled, overriding
@@ -3439,7 +3500,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** to return an extended result code.</dd>
**
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
-** <dd>The database filename is not allowed to be a symbolic link</dd>
+** <dd>The database filename is not allowed to contain a symbolic link</dd>
** </dl>)^
**
** If the 3rd parameter to sqlite3_open_v2() is not one of the
@@ -3698,10 +3759,10 @@ SQLITE_API int sqlite3_open_v2(
**
** See the [URI filename] documentation for additional information.
*/
-SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
-SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
-SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
-SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
+SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
+SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
+SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
/*
** CAPI3REF: Translate filenames
@@ -3730,9 +3791,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
** return value from [sqlite3_db_filename()], then the result is
** undefined and is likely a memory access violation.
*/
-SQLITE_API const char *sqlite3_filename_database(const char*);
-SQLITE_API const char *sqlite3_filename_journal(const char*);
-SQLITE_API const char *sqlite3_filename_wal(const char*);
+SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
/*
** CAPI3REF: Database File Corresponding To A Journal
@@ -3798,14 +3859,14 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y).
*/
-SQLITE_API char *sqlite3_create_filename(
+SQLITE_API sqlite3_filename sqlite3_create_filename(
const char *zDatabase,
const char *zJournal,
const char *zWal,
int nParam,
const char **azParam
);
-SQLITE_API void sqlite3_free_filename(char*);
+SQLITE_API void sqlite3_free_filename(sqlite3_filename);
/*
** CAPI3REF: Error Codes And Messages
@@ -5364,10 +5425,21 @@ SQLITE_API int sqlite3_create_window_function(
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in
** schema structures such as [CHECK constraints], [DEFAULT clauses],
** [expression indexes], [partial indexes], or [generated columns].
-** The SQLITE_DIRECTONLY flags is a security feature which is recommended
-** for all [application-defined SQL functions], and especially for functions
-** that have side-effects or that could potentially leak sensitive
-** information.
+** <p>
+** The SQLITE_DIRECTONLY flag is recommended for any
+** [application-defined SQL function]
+** that has side-effects or that could potentially leak sensitive information.
+** This will prevent attacks in which an application is tricked
+** into using a database file that has had its schema surreptiously
+** modified to invoke the application-defined function in ways that are
+** harmful.
+** <p>
+** Some people say it is good practice to set SQLITE_DIRECTONLY on all
+** [application-defined SQL functions], regardless of whether or not they
+** are security sensitive, as doing so prevents those functions from being used
+** inside of the database schema, and thus ensures that the database
+** can be inspected and modified using generic tools (such as the [CLI])
+** that do not have access to the application-defined functions.
** </dd>
**
** [[SQLITE_INNOCUOUS]] <dt>SQLITE_INNOCUOUS</dt><dd>
@@ -5574,6 +5646,28 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
/*
+** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
+** METHOD: sqlite3_value
+**
+** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
+** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
+** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
+** returns something other than SQLITE_TEXT, then the return value from
+** sqlite3_value_encoding(X) is meaningless. ^Calls to
+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
+** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
+** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
+** thus change the return from subsequent calls to sqlite3_value_encoding(X).
+**
+** This routine is intended for used by applications that test and validate
+** the SQLite implementation. This routine is inquiring about the opaque
+** internal state of an [sqlite3_value] object. Ordinary applications should
+** not need to know what the internal state of an sqlite3_value object is and
+** hence should not need to use this interface.
+*/
+SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
+
+/*
** CAPI3REF: Finding The Subtype Of SQL Values
** METHOD: sqlite3_value
**
@@ -5625,7 +5719,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
**
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
** when first called if N is less than or equal to zero or if a memory
-** allocate error occurs.
+** allocation error occurs.
**
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the
@@ -5830,9 +5924,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
-** ^If the 3rd parameter to the sqlite3_result_text* interfaces
-** is negative, then SQLite takes result text from the 2nd parameter
-** through the first zero character.
+** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
+** other than sqlite3_result_text64() is negative, then SQLite computes
+** the string length itself by searching the 2nd parameter for the first
+** zero character.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
@@ -6328,7 +6423,7 @@ SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
** <li> [sqlite3_filename_wal()]
** </ul>
*/
-SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
+SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Determine if a database is read-only
@@ -6465,7 +6560,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** function C that is invoked prior to each autovacuum of the database
** file. ^The callback is passed a copy of the generic data pointer (P),
** the schema-name of the attached database that is being autovacuumed,
-** the the size of the database file in pages, the number of free pages,
+** the size of the database file in pages, the number of free pages,
** and the number of bytes per page, respectively. The callback should
** return the number of free pages that should be removed by the
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
@@ -6586,6 +6681,11 @@ SQLITE_API void *sqlite3_update_hook(
** to the same database. Sharing is enabled if the argument is true
** and disabled if the argument is false.)^
**
+** This interface is omitted if SQLite is compiled with
+** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
+** compile-time option is recommended because the
+** [use of shared cache mode is discouraged].
+**
** ^Cache sharing is enabled and disabled for an entire process.
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
** In prior versions of SQLite,
@@ -6684,7 +6784,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** ^The soft heap limit may not be greater than the hard heap limit.
** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
** is invoked with a value of N that is greater than the hard heap limit,
-** the the soft heap limit is set to the value of the hard heap limit.
+** the soft heap limit is set to the value of the hard heap limit.
** ^The soft heap limit is automatically enabled whenever the hard heap
** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
** the soft heap limit is outside the range of 1..N, then the soft heap
@@ -6946,15 +7046,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
SQLITE_API void sqlite3_reset_auto_extension(void);
/*
-** The interface to the virtual-table mechanism is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** Structures used by the virtual table interface
*/
typedef struct sqlite3_vtab sqlite3_vtab;
@@ -7072,10 +7163,10 @@ struct sqlite3_module {
** when the omit flag is true there is no guarantee that the constraint will
** not be checked again using byte code.)^
**
-** ^The idxNum and idxPtr values are recorded and passed into the
+** ^The idxNum and idxStr values are recorded and passed into the
** [xFilter] method.
-** ^[sqlite3_free()] is used to free idxPtr if and only if
-** needToFreeIdxPtr is true.
+** ^[sqlite3_free()] is used to free idxStr if and only if
+** needToFreeIdxStr is true.
**
** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
** the correct order to satisfy the ORDER BY clause so that no separate
@@ -7195,7 +7286,7 @@ struct sqlite3_index_info {
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
** tables, the collating sequence of constraints does not matter (for example
** because the constraints are numeric) and so the sqlite3_vtab_collation()
-** interface is no commonly needed.
+** interface is not commonly needed.
*/
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
@@ -7355,16 +7446,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
/*
-** The interface to the virtual-table mechanism defined above (back up
-** to a comment remarkably similar to this one) is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** CAPI3REF: A Handle To An Open BLOB
** KEYWORDS: {BLOB handle} {BLOB handles}
**
@@ -8979,7 +9060,7 @@ typedef struct sqlite3_backup sqlite3_backup;
** if the application incorrectly accesses the destination [database connection]
** and so no error code is reported, but the operations may malfunction
** nevertheless. Use of the destination database connection while a
-** backup is in progress might also also cause a mutex deadlock.
+** backup is in progress might also cause a mutex deadlock.
**
** If running in [shared cache mode], the application must
** guarantee that the shared cache used by the destination database
@@ -9407,7 +9488,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
*/
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
-#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
@@ -9567,7 +9648,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
** <li><p> Otherwise, "BINARY" is returned.
** </ol>
*/
-SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
/*
** CAPI3REF: Determine if a virtual table query is DISTINCT
@@ -9724,21 +9805,20 @@ SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
** is undefined and probably harmful.
**
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
-** sqlite3_vtab_in_next(X,P) must be one of the parameters to the
+** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
** xFilter method which invokes these routines, and specifically
** a parameter that was previously selected for all-at-once IN constraint
** processing use the [sqlite3_vtab_in()] interface in the
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
** an xFilter argument that was selected for all-at-once IN constraint
-** processing, then these routines return [SQLITE_MISUSE])^ or perhaps
-** exhibit some other undefined or harmful behavior.
+** processing, then these routines return [SQLITE_ERROR].)^
**
** ^(Use these routines to access all values on the right-hand side
** of the IN constraint using code like the following:
**
** <blockquote><pre>
** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
-** &nbsp; rc==SQLITE_OK && pVal
+** &nbsp; rc==SQLITE_OK && pVal;
** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
** &nbsp; ){
** &nbsp; // do something with pVal
@@ -9836,6 +9916,10 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
** managed by the prepared statement S and will be automatically freed when
** S is finalized.
**
+** Not all values are available for all query elements. When a value is
+** not available, the output variable is set to -1 if the value is numeric,
+** or to NULL if it is a string (SQLITE_SCANSTAT_NAME).
+**
** <dl>
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
@@ -9863,12 +9947,24 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
** description for the X-th loop.
**
-** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
+** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt>
** <dd>^The "int" variable pointed to by the V parameter will be set to the
-** "select-id" for the X-th loop. The select-id identifies which query or
-** subquery the loop is part of. The main query has a select-id of zero.
-** The select-id is the same value as is output in the first column
-** of an [EXPLAIN QUERY PLAN] query.
+** id for the X-th query plan element. The id value is unique within the
+** statement. The select-id is the same value as is output in the first
+** column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt>
+** <dd>The "int" variable pointed to by the V parameter will be set to the
+** the id of the parent of the current query element, if applicable, or
+** to zero if the query element has no parent. This is the same value as
+** returned in the second column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt>
+** <dd>The sqlite3_int64 output value is set to the number of cycles,
+** according to the processor time-stamp counter, that elapsed while the
+** query element was being processed. This value is not available for
+** all query elements - if it is unavailable the output variable is
+** set to -1.
** </dl>
*/
#define SQLITE_SCANSTAT_NLOOP 0
@@ -9877,12 +9973,14 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
#define SQLITE_SCANSTAT_NAME 3
#define SQLITE_SCANSTAT_EXPLAIN 4
#define SQLITE_SCANSTAT_SELECTID 5
+#define SQLITE_SCANSTAT_PARENTID 6
+#define SQLITE_SCANSTAT_NCYCLE 7
/*
** CAPI3REF: Prepared Statement Scan Status
** METHOD: sqlite3_stmt
**
-** This interface returns information about the predicted and measured
+** These interfaces return information about the predicted and measured
** performance for pStmt. Advanced applications can use this
** interface to compare the predicted and the measured performance and
** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
@@ -9893,19 +9991,25 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
**
** The "iScanStatusOp" parameter determines which status information to return.
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
-** of this interface is undefined.
-** ^The requested measurement is written into a variable pointed to by
-** the "pOut" parameter.
-** Parameter "idx" identifies the specific loop to retrieve statistics for.
-** Loops are numbered starting from zero. ^If idx is out of range - less than
-** zero or greater than or equal to the total number of loops used to implement
-** the statement - a non-zero value is returned and the variable that pOut
-** points to is unchanged.
-**
-** ^Statistics might not be available for all loops in all statements. ^In cases
-** where there exist loops with no available statistics, this function behaves
-** as if the loop did not exist - it returns non-zero and leave the variable
-** that pOut points to unchanged.
+** of this interface is undefined. ^The requested measurement is written into
+** a variable pointed to by the "pOut" parameter.
+**
+** The "flags" parameter must be passed a mask of flags. At present only
+** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
+** is specified, then status information is available for all elements
+** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
+** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
+** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
+** the EXPLAIN QUERY PLAN output) are available. Invoking API
+** sqlite3_stmt_scanstatus() is equivalent to calling
+** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter.
+**
+** Parameter "idx" identifies the specific query element to retrieve statistics
+** for. Query elements are numbered starting from zero. A value of -1 may be
+** to query for statistics regarding the entire query. ^If idx is out of range
+** - less than -1 or greater than or equal to the total number of query
+** elements used to implement the statement - a non-zero value is returned and
+** the variable that pOut points to is unchanged.
**
** See also: [sqlite3_stmt_scanstatus_reset()]
*/
@@ -9915,6 +10019,19 @@ SQLITE_API int sqlite3_stmt_scanstatus(
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
void *pOut /* Result written here */
);
+SQLITE_API int sqlite3_stmt_scanstatus_v2(
+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
+ int flags, /* Mask of flags defined below */
+ void *pOut /* Result written here */
+);
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** KEYWORDS: {scan status flags}
+*/
+#define SQLITE_SCANSTAT_COMPLEX 0x0001
/*
** CAPI3REF: Zero Scan-Status Counters
@@ -10005,6 +10122,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** function is not defined for operations on WITHOUT ROWID tables, or for
** DELETE operations on rowid tables.
**
+** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from
+** the previous call on the same [database connection] D, or NULL for
+** the first call on D.
+**
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
** provide additional information about a preupdate event. These routines
@@ -10410,6 +10531,19 @@ SQLITE_API int sqlite3_deserialize(
# undef double
#endif
+#if defined(__wasi__)
+# undef SQLITE_WASI
+# define SQLITE_WASI 1
+# undef SQLITE_OMIT_WAL
+# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */
+# ifndef SQLITE_OMIT_LOAD_EXTENSION
+# define SQLITE_OMIT_LOAD_EXTENSION
+# endif
+# ifndef SQLITE_THREADSAFE
+# define SQLITE_THREADSAFE 0
+# endif
+#endif
+
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
diff --git a/src/libs/3rdparty/sqlite/sqlite3ext.h b/src/libs/3rdparty/sqlite/sqlite3ext.h
index 2cdd0e429b..19e030028a 100644
--- a/src/libs/3rdparty/sqlite/sqlite3ext.h
+++ b/src/libs/3rdparty/sqlite/sqlite3ext.h
@@ -331,9 +331,9 @@ struct sqlite3_api_routines {
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
- char *(*create_filename)(const char*,const char*,const char*,
+ const char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
- void (*free_filename)(char*);
+ void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
@@ -357,6 +357,10 @@ struct sqlite3_api_routines {
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
+ /* Version 3.40.0 and later */
+ int (*value_encoding)(sqlite3_value*);
+ /* Version 3.41.0 and later */
+ int (*is_interrupted)(sqlite3*);
};
/*
@@ -681,6 +685,10 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
+/* Version 3.40.0 and later */
+#define sqlite3_value_encoding sqlite3_api->value_encoding
+/* Version 3.41.0 and later */
+#define sqlite3_is_interrupted sqlite3_api->is_interrupted
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp
index 41551e96da..4754da22c6 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp
@@ -179,7 +179,7 @@ void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion regio
{
Q_UNUSED(offset);
Q_UNUSED(length);
- Q_D(SyntaxHighlighter);
+ [[maybe_unused]] Q_D(SyntaxHighlighter);
if (region.type() == FoldingRegion::Begin) {
d->foldingRegions.push_back(region);
diff --git a/src/libs/3rdparty/winpty/.gitattributes b/src/libs/3rdparty/winpty/.gitattributes
new file mode 100644
index 0000000000..36d4c60f1a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/.gitattributes
@@ -0,0 +1,19 @@
+* text=auto
+*.bat text eol=crlf
+*.c text
+*.cc text
+*.gyp text
+*.gypi text
+*.h text
+*.ps1 text eol=crlf
+*.rst text
+*.sh text
+*.txt text
+.gitignore text
+.gitattributes text
+Makefile text
+configure text
+
+*.sh eol=lf
+configure eol=lf
+VERSION.txt eol=lf
diff --git a/src/libs/3rdparty/winpty/.gitignore b/src/libs/3rdparty/winpty/.gitignore
new file mode 100644
index 0000000000..68c6b47fb3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/.gitignore
@@ -0,0 +1,16 @@
+*.sln
+*.suo
+*.vcxproj
+*.vcxproj.filters
+*.pyc
+winpty.sdf
+winpty.opensdf
+/config.mk
+/build
+/build-gyp
+/build-libpty
+/ship/packages
+/ship/tmp
+/src/Default
+/src/Release
+/src/gen
diff --git a/src/libs/3rdparty/winpty/CMakeLists.txt b/src/libs/3rdparty/winpty/CMakeLists.txt
new file mode 100644
index 0000000000..febd4f0ab6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(src)
diff --git a/src/libs/3rdparty/winpty/LICENSE b/src/libs/3rdparty/winpty/LICENSE
new file mode 100644
index 0000000000..246fbe0113
--- /dev/null
+++ b/src/libs/3rdparty/winpty/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2011-2016 Ryan Prichard
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/src/libs/3rdparty/winpty/README.md b/src/libs/3rdparty/winpty/README.md
new file mode 100644
index 0000000000..bc8e7d6e5f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/README.md
@@ -0,0 +1,151 @@
+# winpty
+
+[![Build Status](https://ci.appveyor.com/api/projects/status/69tb9gylsph1ee1x/branch/master?svg=true)](https://ci.appveyor.com/project/rprichard/winpty/branch/master)
+
+winpty is a Windows software package providing an interface similar to a Unix
+pty-master for communicating with Windows console programs. The package
+consists of a library (libwinpty) and a tool for Cygwin and MSYS for running
+Windows console programs in a Cygwin/MSYS pty.
+
+The software works by starting the `winpty-agent.exe` process with a new,
+hidden console window, which bridges between the console API and terminal
+input/output escape codes. It polls the hidden console's screen buffer for
+changes and generates a corresponding stream of output.
+
+The Unix adapter allows running Windows console programs (e.g. CMD, PowerShell,
+IronPython, etc.) under `mintty` or Cygwin's `sshd` with
+properly-functioning input (e.g. arrow and function keys) and output (e.g. line
+buffering). The library could be also useful for writing a non-Cygwin SSH
+server.
+
+## Supported Windows versions
+
+winpty runs on Windows XP through Windows 10, including server versions. It
+can be compiled into either 32-bit or 64-bit binaries.
+
+## Cygwin/MSYS adapter (`winpty.exe`)
+
+### Prerequisites
+
+You need the following to build winpty:
+
+* A Cygwin or MSYS installation
+* GNU make
+* A MinGW g++ toolchain capable of compiling C++11 code to build `winpty.dll`
+ and `winpty-agent.exe`
+* A g++ toolchain targeting Cygwin or MSYS to build `winpty.exe`
+
+Winpty requires two g++ toolchains as it is split into two parts. The
+`winpty.dll` and `winpty-agent.exe` binaries interface with the native
+Windows command prompt window so they are compiled with the native MinGW
+toolchain. The `winpty.exe` binary interfaces with the MSYS/Cygwin terminal so
+it is compiled with the MSYS/Cygwin toolchain.
+
+MinGW appears to be split into two distributions -- MinGW (creates 32-bit
+binaries) and MinGW-w64 (creates both 32-bit and 64-bit binaries). Either
+one is generally acceptable.
+
+#### Cygwin packages
+
+The default g++ compiler for Cygwin targets Cygwin itself, but Cygwin also
+packages MinGW-w64 compilers. As of this writing, the necessary packages are:
+
+* Either `mingw64-i686-gcc-g++` or `mingw64-x86_64-gcc-g++`. Select the
+ appropriate compiler for your CPU architecture.
+* `gcc-g++`
+* `make`
+
+As of this writing (2016-01-23), only the MinGW-w64 compiler is acceptable.
+The MinGW compiler (e.g. from the `mingw-gcc-g++` package) is no longer
+maintained and is too buggy.
+
+#### MSYS packages
+
+For the original MSYS, use the `mingw-get` tool (MinGW Installation Manager),
+and select at least these components:
+
+* `mingw-developer-toolkit`
+* `mingw32-base`
+* `mingw32-gcc-g++`
+* `msys-base`
+* `msys-system-builder`
+
+When running `./configure`, make sure that `mingw32-g++` is in your
+`PATH`. It will be in the `C:\MinGW\bin` directory.
+
+#### MSYS2 packages
+
+For MSYS2, use `pacman` and install at least these packages:
+
+* `msys/gcc`
+* `mingw32/mingw-w64-i686-gcc` or `mingw64/mingw-w64-x86_64-gcc`. Select
+ the appropriate compiler for your CPU architecture.
+* `make`
+
+MSYS2 provides three start menu shortcuts for starting MSYS2:
+
+* MinGW-w64 Win32 Shell
+* MinGW-w64 Win64 Shell
+* MSYS2 Shell
+
+To build winpty, use the MinGW-w64 {Win32,Win64} shortcut of the architecture
+matching MSYS2. These shortcuts will put the g++ compiler from the
+`{mingw32,mingw64}/mingw-w64-{i686,x86_64}-gcc` packages into the `PATH`.
+
+Alternatively, instead of installing `mingw32/mingw-w64-i686-gcc` or
+`mingw64/mingw-w64-x86_64-gcc`, install the `mingw-w64-cross-gcc` and
+`mingw-w64-cross-crt-git` packages. These packages install cross-compilers
+into `/opt/bin`, and then any of the three shortcuts will work.
+
+### Building the Unix adapter
+
+In the project directory, run `./configure`, then `make`, then `make install`.
+By default, winpty is installed into `/usr/local`. Pass `PREFIX=<path>` to
+`make install` to override this default.
+
+### Using the Unix adapter
+
+To run a Windows console program in `mintty` or Cygwin `sshd`, prepend
+`winpty` to the command-line:
+
+ $ winpty powershell
+ Windows PowerShell
+ Copyright (C) 2009 Microsoft Corporation. All rights reserved.
+
+ PS C:\rprichard\proj\winpty> 10 + 20
+ 30
+ PS C:\rprichard\proj\winpty> exit
+
+## Embedding winpty / MSVC compilation
+
+See `src/include/winpty.h` for the prototypes of functions exported by
+`winpty.dll`.
+
+Only the `winpty.exe` binary uses Cygwin; all the other binaries work without
+it and can be compiled with either MinGW or MSVC. To compile using MSVC,
+download gyp and run `gyp -I configurations.gypi` in the `src` subdirectory.
+This will generate a `winpty.sln` and associated project files. See the
+`src/winpty.gyp` and `src/configurations.gypi` files for notes on dealing with
+MSVC versions and different architectures.
+
+Compiling winpty with MSVC currently requires MSVC 2013 or newer.
+
+## Debugging winpty
+
+winpty comes with a tool for collecting timestamped debugging output. To use
+it:
+
+1. Run `winpty-debugserver.exe` on the same computer as winpty.
+2. Set the `WINPTY_DEBUG` environment variable to `trace` for the
+ `winpty.exe` process and/or the process using `libwinpty.dll`.
+
+winpty also recognizes a `WINPTY_SHOW_CONSOLE` environment variable. Set it
+to 1 to prevent winpty from hiding the console window.
+
+## Copyright
+
+This project is distributed under the MIT license (see the `LICENSE` file in
+the project root).
+
+By submitting a pull request for this project, you agree to license your
+contribution under the MIT license to this project.
diff --git a/src/libs/3rdparty/winpty/RELEASES.md b/src/libs/3rdparty/winpty/RELEASES.md
new file mode 100644
index 0000000000..768cdf90e3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/RELEASES.md
@@ -0,0 +1,280 @@
+# Next Version
+
+Input handling changes:
+
+ * Improve Ctrl-C handling with programs that use unprocessed input. (e.g.
+ Ctrl-C now cancels input with PowerShell on Windows 10.)
+ [#116](https://github.com/rprichard/winpty/issues/116)
+ * Fix a theoretical issue with input event ordering.
+ [#117](https://github.com/rprichard/winpty/issues/117)
+ * Ctrl/Shift+{Arrow,Home,End} keys now work with IntelliJ.
+ [#118](https://github.com/rprichard/winpty/issues/118)
+
+# Version 0.4.3 (2017-05-17)
+
+Input handling changes:
+
+ * winpty sets `ENHANCED_KEY` for arrow and navigation keys. This fixes an
+ issue with the Ruby REPL.
+ [#99](https://github.com/rprichard/winpty/issues/99)
+ * AltGr keys are handled better now.
+ [#109](https://github.com/rprichard/winpty/issues/109)
+ * In `ENABLE_VIRTUAL_TERMINAL_INPUT` mode, when typing Home/End with a
+ modifier (e.g. Ctrl), winpty now generates an H/F escape sequence like
+ `^[[1;5F` rather than a 1/4 escape like `^[[4;5~`.
+ [#114](https://github.com/rprichard/winpty/issues/114)
+
+Resizing and scraping fixes:
+
+ * winpty now synthesizes a `WINDOW_BUFFER_SIZE_EVENT` event after resizing
+ the console to better propagate window size changes to console programs.
+ In particular, this affects WSL and Cygwin.
+ [#110](https://github.com/rprichard/winpty/issues/110)
+ * Better handling of resizing for certain full-screen programs, like
+ WSL less.
+ [#112](https://github.com/rprichard/winpty/issues/112)
+ * Hide the cursor if it's currently outside the console window. This change
+ fixes an issue with Far Manager.
+ [#113](https://github.com/rprichard/winpty/issues/113)
+ * winpty now avoids using console fonts smaller than 5px high to improve
+ half-vs-full-width character handling. See
+ https://github.com/Microsoft/vscode/issues/19665.
+ [b4db322010](https://github.com/rprichard/winpty/commit/b4db322010d2d897e6c496fefc4f0ecc9b84c2f3)
+
+Cygwin/MSYS adapter fix:
+
+ * The way the `winpty` Cygwin/MSYS2 adapter searches for the program to
+ launch changed. It now resolves symlinks and searches the PATH explicitly.
+ [#81](https://github.com/rprichard/winpty/issues/81)
+ [#98](https://github.com/rprichard/winpty/issues/98)
+
+This release does not include binaries for the old MSYS1 project anymore.
+MSYS2 will continue to be supported. See
+https://github.com/rprichard/winpty/issues/97.
+
+# Version 0.4.2 (2017-01-18)
+
+This release improves WSL support (i.e. Bash-on-Windows):
+
+ * winpty generates more correct input escape sequences for WSL programs that
+ enable an alternate input mode using DECCKM. This bug affected arrow keys
+ and Home/End in WSL programs such as `vim`, `mc`, and `less`.
+ [#90](https://github.com/rprichard/winpty/issues/90)
+ * winpty now recognizes the `COMMON_LVB_REVERSE_VIDEO` and
+ `COMMON_LVB_UNDERSCORE` text attributes. The Windows console uses these
+ attributes to implement the SGR.4(Underline) and SGR.7(Negative) modes in
+ its VT handling. This change affects WSL pager status bars, man pages, etc.
+
+The build system no longer has a "version suffix" mechanism, so passing
+`VERSION_SUFFIX=<suffix>` to make or `-D VERSION_SUFFIX=<suffix>` to gyp now
+has no effect. AFAIK, the mechanism was never used publicly.
+[67a34b6c03](https://github.com/rprichard/winpty/commit/67a34b6c03557a5c2e0a2bdd502c2210921d8f3e)
+
+# Version 0.4.1 (2017-01-03)
+
+Bug fixes:
+
+ * This version fixes a bug where the `winpty-agent.exe` process could read
+ past the end of a buffer.
+ [#94](https://github.com/rprichard/winpty/issues/94)
+
+# Version 0.4.0 (2016-06-28)
+
+The winpty library has a new API that should be easier for embedding.
+[880c00c69e](https://github.com/rprichard/winpty/commit/880c00c69eeca73643ddb576f02c5badbec81f56)
+
+User-visible changes:
+
+ * winpty now automatically puts the terminal into mouse mode when it detects
+ that the console has left QuickEdit mode. The `--mouse` option still forces
+ the terminal into mouse mode. In principle, an option could be added to
+ suppress terminal mode, but hopefully it won't be necessary. There is a
+ script in the `misc` subdirectory, `misc/ConinMode.ps1`, that can change
+ the QuickEdit mode from the command-line.
+ * winpty now passes keyboard escapes to `bash.exe` in the Windows Subsystem
+ for Linux.
+ [#82](https://github.com/rprichard/winpty/issues/82)
+
+Bug fixes:
+
+ * By default, `winpty.dll` avoids calling `SetProcessWindowStation` within
+ the calling process.
+ [#58](https://github.com/rprichard/winpty/issues/58)
+ * Fixed an uninitialized memory bug that could have crashed winpty.
+ [#80](https://github.com/rprichard/winpty/issues/80)
+ * winpty now works better with very large and very small terminal windows.
+ It resizes the console font according to the number of columns.
+ [#61](https://github.com/rprichard/winpty/issues/61)
+ * winpty no longer uses Mark to freeze the console on Windows 10. The Mark
+ command could interfere with the cursor position, corrupting the data in
+ the screen buffer.
+ [#79](https://github.com/rprichard/winpty/issues/79)
+
+# Version 0.3.0 (2016-05-20)
+
+User-visible changes:
+
+ * The UNIX adapter is renamed from `console.exe` to `winpty.exe` to be
+ consistent with MSYS2. The name `winpty.exe` is less likely to conflict
+ with another program and is easier to search for online (e.g. for someone
+ unfamiliar with winpty).
+ * The UNIX adapter now clears the `TERM` variable.
+ [#43](https://github.com/rprichard/winpty/issues/43)
+ * An escape character appearing in a console screen buffer cell is converted
+ to a '?'.
+ [#47](https://github.com/rprichard/winpty/issues/47)
+
+Bug fixes:
+
+ * A major bug affecting XP users was fixed.
+ [#67](https://github.com/rprichard/winpty/issues/67)
+ * Fixed an incompatibility with ConEmu where winpty hung if ConEmu's
+ "Process 'start'" feature was enabled.
+ [#70](https://github.com/rprichard/winpty/issues/70)
+ * Fixed a bug where `cmd.exe` sometimes printed the message,
+ `Not enough storage is available to process this command.`.
+ [#74](https://github.com/rprichard/winpty/issues/74)
+
+Many changes internally:
+
+ * The codebase is switched from C++03 to C++11 and uses exceptions internally.
+ No exceptions are thrown across the C APIs defined in `winpty.h`.
+ * This version drops support for the original MinGW compiler packaged with
+ Cygwin (`i686-pc-mingw32-g++`). The MinGW-w64 compiler is still supported,
+ as is the MinGW distributed at mingw.org. Compiling with MSVC now requires
+ MSVC 2013 or newer. Windows XP is still supported.
+ [ec3eae8df5](https://github.com/rprichard/winpty/commit/ec3eae8df5bbbb36d7628d168b0815638d122f37)
+ * Pipe security is improved. winpty works harder to produce unique pipe names
+ and includes a random component in the name. winpty secures pipes with a
+ DACL that prevents arbitrary users from connecting to its pipes. winpty now
+ passes `PIPE_REJECT_REMOTE_CLIENTS` on Vista and up, and it verifies that
+ the pipe client PID is correct, again on Vista and up. When connecting to a
+ named pipe, winpty uses the `SECURITY_IDENTIFICATION` flag to restrict
+ impersonation. Previous versions *should* still be secure.
+ * `winpty-debugserver.exe` now has an `--everyone` flag that allows capturing
+ debug output from other users.
+ * The code now compiles cleanly with MSVC's "Security Development Lifecycle"
+ (`/SDL`) checks enabled.
+
+# Version 0.2.2 (2016-02-25)
+
+Minor bug fixes and enhancements:
+
+ * Fix a bug that generated spurious mouse input records when an incomplete
+ mouse escape sequence was seen.
+ * Fix a buffer overflow bug in `winpty-debugserver.exe` affecting messages of
+ exactly 4096 bytes.
+ * For MSVC builds, add a `src/configurations.gypi` file that can be included
+ on the gyp command-line to enable 32-bit and 64-bit builds.
+ * `winpty-agent --show-input` mode: Flush stdout after each line.
+ * Makefile builds: generate a `build/winpty.lib` import library to accompany
+ `build/winpty.dll`.
+
+# Version 0.2.1 (2015-12-19)
+
+ * The main project source was moved into a `src` directory for better code
+ organization and to fix
+ [#51](https://github.com/rprichard/winpty/issues/51).
+ * winpty recognizes many more escape sequences, including:
+ * putty/rxvt's F1-F4 keys
+ [#40](https://github.com/rprichard/winpty/issues/40)
+ * the Linux virtual console's F1-F5 keys
+ * the "application numpad" keys (e.g. enabled with DECPAM)
+ * Fixed handling of Shift-Alt-O and Alt-[.
+ * Added support for mouse input. The UNIX adapter has a `--mouse` argument
+ that puts the terminal into mouse mode, but the agent recognizes mouse
+ input even without the argument. The agent recognizes double-clicks using
+ Windows' double-click interval setting (i.e. GetDoubleClickTime).
+ [#57](https://github.com/rprichard/winpty/issues/57)
+
+Changes to debugging interfaces:
+
+ * The `WINPTY_DEBUG` variable is now a comma-separated list. The old
+ behavior (i.e. tracing) is enabled with `WINPTY_DEBUG=trace`.
+ * The UNIX adapter program now has a `--showkey` argument that dumps input
+ bytes.
+ * The `winpty-agent.exe` program has a `--show-input` argument that dumps
+ `INPUT_RECORD` records. (It omits mouse events unless `--with-mouse` is
+ also specified.) The agent also responds to `WINPTY_DEBUG=trace,input`,
+ which logs input bytes and synthesized console events, and it responds to
+ `WINPTY_DEBUG=trace,dump_input_map`, which dumps the internal table of
+ escape sequences.
+
+# Version 0.2.0 (2015-11-13)
+
+No changes to the API, but many small changes to the implementation. The big
+changes include:
+
+ * Support for 64-bit Cygwin and MSYS2
+ * Support for Windows 10
+ * Better Unicode support (especially East Asian languages)
+
+Details:
+
+ * The `configure` script recognizes 64-bit Cygwin and MSYS2 environments and
+ selects the appropriate compiler.
+ * winpty works much better with the upgraded console in Windows 10. The
+ `conhost.exe` hang can still occur, but only with certain programs, and
+ is much less likely to occur. With the new console, use Mark instead of
+ SelectAll, for better performance.
+ [#31](https://github.com/rprichard/winpty/issues/31)
+ [#30](https://github.com/rprichard/winpty/issues/30)
+ [#53](https://github.com/rprichard/winpty/issues/53)
+ * The UNIX adapter now calls `setlocale(LC_ALL, "")` to set the locale.
+ * Improved Unicode support. When a console is started with an East Asian code
+ page, winpty now chooses an East Asian font rather than Consolas / Lucida
+ Console. Selecting the right font helps synchronize character widths
+ between the console and terminal. (It's not perfect, though.)
+ [#41](https://github.com/rprichard/winpty/issues/41)
+ * winpty now more-or-less works with programs that change the screen buffer
+ or resize the original screen buffer. If the screen buffer height changes,
+ winpty switches to a "direct mode", where it makes no effort to track
+ scrolling. In direct mode, it merely syncs snapshots of the console to the
+ terminal. Caveats:
+ * Changing the screen buffer (i.e. `SetConsoleActiveScreenBuffer`)
+ breaks winpty on Windows 7. This problem can eventually be mitigated,
+ but never completely fixed, due to Windows 7 bugginess.
+ * Resizing the original screen buffer can hang `conhost.exe` on Windows 10.
+ Enabling the legacy console is a workaround.
+ * If a program changes the screen buffer and then exits, relying on the OS
+ to restore the original screen buffer, that restoration probably will not
+ happen with winpty. winpty's behavior can probably be improved here.
+ * Improved color handling:
+ * DkGray-on-Black text was previously hiddenly completely. Now it is
+ output as DkGray, with a fallback to LtGray on terminals that don't
+ recognize the intense colors.
+ [#39](https://github.com/rprichard/winpty/issues/39).
+ * The console is always initialized to LtGray-on-Black, regardless of the
+ user setting, which matches the console color heuristic, which translates
+ LtGray-on-Black to "reset SGR parameters."
+ * Shift-Tab is recognized correctly now.
+ [#19](https://github.com/rprichard/winpty/issues/19)
+ * Add a `--version` argument to `winpty-agent.exe` and the UNIX adapter. The
+ argument reports the nominal version (i.e. the `VERSION.txt`) file, with a
+ "VERSION_SUFFIX" appended (defaulted to `-dev`), and a git commit hash, if
+ the `git` command successfully reports a hash during the build. The `git`
+ command is invoked by either `make` or `gyp`.
+ * The agent now combines `ReadConsoleOutputW` calls when it polls the console
+ buffer for changes, which may slightly reduce its CPU overhead.
+ [#44](https://github.com/rprichard/winpty/issues/44).
+ * A `gyp` file is added to help compile with MSVC.
+ * The code can now be compiled as C++11 code, though it isn't by default.
+ [bde8922e08](https://github.com/rprichard/winpty/commit/bde8922e08c3638e01ecc7b581b676c314163e3c)
+ * If winpty can't create a new window station, it charges ahead rather than
+ aborting. This situation might happen if winpty were started from an SSH
+ session.
+ * Debugging improvements:
+ * `WINPTYDBG` is renamed to `WINPTY_DEBUG`, and a new `WINPTY_SHOW_CONSOLE`
+ variable keeps the underlying console visible.
+ * A `winpty-debugserver.exe` program is built and shipped by default. It
+ collects the trace output enabled with `WINPTY_DEBUG`.
+ * The `Makefile` build of winpty now compiles `winpty-agent.exe` and
+ `winpty.dll` with -O2.
+
+# Version 0.1.1 (2012-07-28)
+
+Minor bugfix release.
+
+# Version 0.1 (2012-04-17)
+
+Initial release.
diff --git a/src/libs/3rdparty/winpty/VERSION.txt b/src/libs/3rdparty/winpty/VERSION.txt
new file mode 100644
index 0000000000..5d47ff8c45
--- /dev/null
+++ b/src/libs/3rdparty/winpty/VERSION.txt
@@ -0,0 +1 @@
+0.4.4-dev
diff --git a/src/libs/3rdparty/winpty/appveyor.yml b/src/libs/3rdparty/winpty/appveyor.yml
new file mode 100644
index 0000000000..a9e8726fc1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/appveyor.yml
@@ -0,0 +1,16 @@
+image: Visual Studio 2015
+
+init:
+ - C:\msys64\usr\bin\bash --login -c "pacman -S --needed --noconfirm --noprogressbar msys/make msys/tar msys/gcc mingw-w64-cross-toolchain"
+ - C:\cygwin\setup-x86 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make
+ - C:\cygwin64\setup-x86_64 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make
+
+build_script:
+ - C:\Python27-x64\python.exe ship\ship.py --kind msys2 --arch x64 --syspath C:\msys64
+ - C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch ia32 --syspath C:\cygwin
+ - C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch x64 --syspath C:\cygwin64
+ - C:\Python27-x64\python.exe ship\make_msvc_package.py
+
+artifacts:
+ - path: ship\packages\*.tar.gz
+ - path: ship\packages\*.zip
diff --git a/src/libs/3rdparty/winpty/configure b/src/libs/3rdparty/winpty/configure
new file mode 100644
index 0000000000..6d37d65b09
--- /dev/null
+++ b/src/libs/3rdparty/winpty/configure
@@ -0,0 +1,167 @@
+#!/bin/bash
+#
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+#
+# findTool(desc, commandList)
+#
+# Searches commandLine for the first command in the PATH and returns it.
+# Prints an error and aborts the script if no match is found.
+#
+FINDTOOL_OUT=""
+function findTool {
+ DESC=$1
+ OPTIONS=$2
+ for CMD in ${OPTIONS}; do
+ if (which $CMD &>/dev/null) then
+ echo "Found $DESC: $CMD"
+ FINDTOOL_OUT="$CMD"
+ return
+ fi
+ done
+ echo "Error: could not find $DESC. One of these should be in your PATH:"
+ for CMD in ${OPTIONS}; do
+ echo " * $CMD"
+ done
+ exit 1
+}
+
+IS_CYGWIN=0
+IS_MSYS1=0
+IS_MSYS2=0
+
+# Link parts of the Cygwin binary statically to aid in redistribution? The
+# binary still links dynamically against the main DLL. The MinGW binaries are
+# also statically linked and therefore depend only on Windows DLLs. I started
+# linking the Cygwin/MSYS binary statically, because G++ 4.7 changed the
+# Windows C++ ABI.
+UNIX_LDFLAGS_STATIC='-static -static-libgcc -static-libstdc++'
+
+# Detect the environment -- Cygwin or MSYS.
+case $(uname -s) in
+ CYGWIN*)
+ echo 'uname -s identifies a Cygwin environment.'
+ IS_CYGWIN=1
+ case $(uname -m) in
+ i686)
+ echo 'uname -m identifies an i686 environment.'
+ UNIX_CXX=i686-pc-cygwin-g++
+ MINGW_CXX=i686-w64-mingw32-g++
+ ;;
+ x86_64)
+ echo 'uname -m identifies an x86_64 environment.'
+ UNIX_CXX=x86_64-pc-cygwin-g++
+ MINGW_CXX=x86_64-w64-mingw32-g++
+ ;;
+ *)
+ echo 'Error: uname -m did not match either i686 or x86_64.'
+ exit 1
+ ;;
+ esac
+ ;;
+ MSYS*|MINGW*)
+ # MSYS2 notes:
+ # - MSYS2 offers two shortcuts to open an environment:
+ # - MinGW-w64 Win32 Shell. This env reports a `uname -s` of
+ # MINGW32_NT-6.1 on 32-bit Win7. The MinGW-w64 compiler
+ # (i686-w64-mingw32-g++.exe) is in the PATH.
+ # - MSYS2 Shell. `uname -s` instead reports MSYS_NT-6.1.
+ # The i686-w64-mingw32-g++ compiler is not in the PATH.
+ # - MSYS2 appears to use MinGW-w64, not the older mingw.org.
+ # MSYS notes:
+ # - `uname -s` is always MINGW32_NT-6.1 on Win7.
+ echo 'uname -s identifies an MSYS/MSYS2 environment.'
+ case $(uname -m) in
+ i686)
+ echo 'uname -m identifies an i686 environment.'
+ UNIX_CXX=i686-pc-msys-g++
+ if echo "$(uname -r)" | grep '^1[.]' > /dev/null; then
+ # The MSYS-targeting compiler for the original 32-bit-only
+ # MSYS does not recognize the -static-libstdc++ flag, and
+ # it does not work with -static, because it tries to link
+ # statically with the core MSYS library and fails.
+ #
+ # Distinguish between the two using the major version
+ # number of `uname -r`:
+ #
+ # MSYS uname -r: 1.0.18(0.48/3/2)
+ # MSYS2 uname -r: 2.0.0(0.284/5/3)
+ #
+ # This is suboptimal because MSYS2 is not actually the
+ # second version of MSYS--it's a brand-new fork of Cygwin.
+ #
+ IS_MSYS1=1
+ UNIX_LDFLAGS_STATIC=
+ MINGW_CXX=mingw32-g++
+ else
+ IS_MSYS2=1
+ MINGW_CXX=i686-w64-mingw32-g++.exe
+ fi
+ ;;
+ x86_64)
+ echo 'uname -m identifies an x86_64 environment.'
+ IS_MSYS2=1
+ UNIX_CXX=x86_64-pc-msys-g++
+ MINGW_CXX=x86_64-w64-mingw32-g++
+ ;;
+ *)
+ echo 'Error: uname -m did not match either i686 or x86_64.'
+ exit 1
+ ;;
+ esac
+ ;;
+ *)
+ echo 'Error: uname -s did not match either CYGWIN* or MINGW*.'
+ exit 1
+ ;;
+esac
+
+# Search the PATH and pick the first match.
+findTool "Cygwin/MSYS G++ compiler" "$UNIX_CXX"
+UNIX_CXX=$FINDTOOL_OUT
+findTool "MinGW G++ compiler" "$MINGW_CXX"
+MINGW_CXX=$FINDTOOL_OUT
+
+# Write config files.
+echo Writing config.mk
+echo UNIX_CXX=$UNIX_CXX > config.mk
+echo UNIX_LDFLAGS_STATIC=$UNIX_LDFLAGS_STATIC >> config.mk
+echo MINGW_CXX=$MINGW_CXX >> config.mk
+
+if test $IS_MSYS1 = 1; then
+ echo UNIX_CXXFLAGS += -DWINPTY_TARGET_MSYS1 >> config.mk
+ # The MSYS1 MinGW compiler has a bug that prevents inclusion of algorithm
+ # and math.h in normal C++11 mode. The workaround is to enable the gnu++11
+ # mode instead. The bug was fixed on 2015-07-31, but as of 2016-02-26, the
+ # fix apparently hasn't been released. See
+ # http://ehc.ac/p/mingw/bugs/2250/.
+ echo MINGW_ENABLE_CXX11_FLAG := -std=gnu++11 >> config.mk
+fi
+
+if test -d .git -a -f .git/HEAD -a -f .git/index && git rev-parse HEAD >&/dev/null; then
+ echo "Commit info: git"
+ echo 'COMMIT_HASH = $(shell git rev-parse HEAD)' >> config.mk
+ echo 'COMMIT_HASH_DEP := config.mk .git/HEAD .git/index' >> config.mk
+else
+ echo "Commit info: none"
+ echo 'COMMIT_HASH := none' >> config.mk
+ echo 'COMMIT_HASH_DEP := config.mk' >> config.mk
+fi
diff --git a/src/libs/3rdparty/winpty/misc/.gitignore b/src/libs/3rdparty/winpty/misc/.gitignore
new file mode 100644
index 0000000000..23751645fa
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/.gitignore
@@ -0,0 +1,2 @@
+*.exe
+UnixEcho \ No newline at end of file
diff --git a/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc b/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc
new file mode 100644
index 0000000000..a5bb074826
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc
@@ -0,0 +1,90 @@
+#include <windows.h>
+#include <cassert>
+
+#include "TestUtil.cc"
+
+void dumpInfoToTrace() {
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
+ trace("win=(%d,%d,%d,%d)",
+ (int)info.srWindow.Left,
+ (int)info.srWindow.Top,
+ (int)info.srWindow.Right,
+ (int)info.srWindow.Bottom);
+ trace("buf=(%d,%d)",
+ (int)info.dwSize.X,
+ (int)info.dwSize.Y);
+ trace("cur=(%d,%d)",
+ (int)info.dwCursorPosition.X,
+ (int)info.dwCursorPosition.Y);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+
+ if (false) {
+ // Reducing the buffer height can move the window up.
+ setBufferSize(80, 25);
+ setWindowPos(0, 20, 80, 5);
+ Sleep(2000);
+ setBufferSize(80, 10);
+ }
+
+ if (false) {
+ // Reducing the buffer height moves the window up and the buffer
+ // contents up too.
+ setBufferSize(80, 25);
+ setWindowPos(0, 20, 80, 5);
+ setCursorPos(0, 20);
+ printf("TEST1\nTEST2\nTEST3\nTEST4\n");
+ fflush(stdout);
+ Sleep(2000);
+ setBufferSize(80, 10);
+ }
+
+ if (false) {
+ // Reducing the buffer width can move the window left.
+ setBufferSize(80, 25);
+ setWindowPos(40, 0, 40, 25);
+ Sleep(2000);
+ setBufferSize(60, 25);
+ }
+
+ if (false) {
+ // Sometimes the buffer contents are shifted up; sometimes they're
+ // shifted down. It seems to depend on the cursor position?
+
+ // setBufferSize(80, 25);
+ // setWindowPos(0, 20, 80, 5);
+ // setCursorPos(0, 20);
+ // printf("TESTa\nTESTb\nTESTc\nTESTd\nTESTe");
+ // fflush(stdout);
+ // setCursorPos(0, 0);
+ // printf("TEST1\nTEST2\nTEST3\nTEST4\nTEST5");
+ // fflush(stdout);
+ // setCursorPos(0, 24);
+ // Sleep(5000);
+ // setBufferSize(80, 24);
+
+ setBufferSize(80, 20);
+ setWindowPos(0, 10, 80, 10);
+ setCursorPos(0, 18);
+
+ printf("TEST1\nTEST2");
+ fflush(stdout);
+ setCursorPos(0, 18);
+
+ Sleep(2000);
+ setBufferSize(80, 18);
+ }
+
+ dumpInfoToTrace();
+ Sleep(30000);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc b/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc
new file mode 100644
index 0000000000..701a2cb4a3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc
@@ -0,0 +1,53 @@
+// A test program for CreateConsoleScreenBuffer / SetConsoleActiveScreenBuffer
+//
+
+#include <windows.h>
+#include <stdio.h>
+#include <conio.h>
+#include <io.h>
+#include <cassert>
+
+#include "TestUtil.cc"
+
+int main()
+{
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE childBuffer = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
+
+ SetConsoleActiveScreenBuffer(childBuffer);
+
+ while (true) {
+ char buf[1024];
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ assert(GetConsoleScreenBufferInfo(origBuffer, &info));
+ trace("child.size=(%d,%d)", (int)info.dwSize.X, (int)info.dwSize.Y);
+ trace("child.cursor=(%d,%d)", (int)info.dwCursorPosition.X, (int)info.dwCursorPosition.Y);
+ trace("child.window=(%d,%d,%d,%d)",
+ (int)info.srWindow.Left, (int)info.srWindow.Top,
+ (int)info.srWindow.Right, (int)info.srWindow.Bottom);
+ trace("child.maxSize=(%d,%d)", (int)info.dwMaximumWindowSize.X, (int)info.dwMaximumWindowSize.Y);
+
+ int ch = getch();
+ sprintf(buf, "%02x\n", ch);
+ DWORD actual = 0;
+ WriteFile(childBuffer, buf, strlen(buf), &actual, NULL);
+ if (ch == 0x1b/*ESC*/ || ch == 0x03/*CTRL-C*/)
+ break;
+
+ if (ch == 'b') {
+ setBufferSize(origBuffer, 40, 25);
+ } else if (ch == 'w') {
+ setWindowPos(origBuffer, 1, 1, 38, 23);
+ } else if (ch == 'c') {
+ setCursorPos(origBuffer, 10, 10);
+ }
+ }
+
+ SetConsoleActiveScreenBuffer(origBuffer);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ClearConsole.cc b/src/libs/3rdparty/winpty/misc/ClearConsole.cc
new file mode 100644
index 0000000000..f95f8c84ca
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ClearConsole.cc
@@ -0,0 +1,72 @@
+/*
+ * Demonstrates that console clearing sets each cell's character to SP, not
+ * NUL, and it sets the attribute of each cell to the current text attribute.
+ *
+ * This confirms the MSDN instruction in the "Clearing the Screen" article.
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022(v=vs.85).aspx
+ * It advises using GetConsoleScreenBufferInfo to get the current text
+ * attribute, then FillConsoleOutputCharacter and FillConsoleOutputAttribute to
+ * write to the console buffer.
+ */
+
+#include <windows.h>
+
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ SetConsoleTextAttribute(conout, 0x24);
+ system("cls");
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 25);
+ setWindowPos(0, 0, 80, 25);
+
+ CHAR_INFO buf;
+ COORD bufSize = { 1, 1 };
+ COORD bufCoord = { 0, 0 };
+ SMALL_RECT rect = { 5, 5, 5, 5 };
+ BOOL ret;
+ DWORD actual;
+ COORD writeCoord = { 5, 5 };
+
+ // After cls, each cell's character is a space, and its attributes are the
+ // default text attributes.
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L' ' && buf.Attributes == 0x24);
+
+ // Nevertheless, it is possible to change a cell to NUL.
+ ret = FillConsoleOutputCharacterW(conout, L'\0', 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L'\0' && buf.Attributes == 0x24);
+
+ // As well as a 0 attribute. (As one would expect, the cell is
+ // black-on-black.)
+ ret = FillConsoleOutputAttribute(conout, 0, 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L'\0' && buf.Attributes == 0);
+ ret = FillConsoleOutputCharacterW(conout, L'X', 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L'X' && buf.Attributes == 0);
+
+ // The 'X' is invisible.
+ countDown(3);
+
+ ret = FillConsoleOutputAttribute(conout, 0x42, 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+
+ countDown(5);
+}
diff --git a/src/libs/3rdparty/winpty/misc/ConinMode.cc b/src/libs/3rdparty/winpty/misc/ConinMode.cc
new file mode 100644
index 0000000000..1e1428d8b0
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ConinMode.cc
@@ -0,0 +1,117 @@
+#include <windows.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+static HANDLE getConin() {
+ HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
+ if (conin == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "error: cannot get stdin\n");
+ exit(1);
+ }
+ return conin;
+}
+
+static DWORD getConsoleMode() {
+ DWORD mode = 0;
+ if (!GetConsoleMode(getConin(), &mode)) {
+ fprintf(stderr, "error: GetConsoleMode failed (is stdin a console?)\n");
+ exit(1);
+ }
+ return mode;
+}
+
+static void setConsoleMode(DWORD mode) {
+ if (!SetConsoleMode(getConin(), mode)) {
+ fprintf(stderr, "error: SetConsoleMode failed (is stdin a console?)\n");
+ exit(1);
+ }
+}
+
+static long parseInt(const std::string &s) {
+ errno = 0;
+ char *endptr = nullptr;
+ long result = strtol(s.c_str(), &endptr, 0);
+ if (errno != 0 || !endptr || *endptr != '\0') {
+ fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str());
+ exit(1);
+ }
+ return result;
+}
+
+static void usage() {
+ printf("Usage: ConinMode [verb] [options]\n");
+ printf("Verbs:\n");
+ printf(" [info] Dumps info about mode flags.\n");
+ printf(" get Prints the mode DWORD.\n");
+ printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n");
+ printf(" set VALUE MASK\n");
+ printf(" Same as `set VALUE`, but only alters the bits in MASK.\n");
+ exit(1);
+}
+
+struct {
+ const char *name;
+ DWORD value;
+} kInputFlags[] = {
+ "ENABLE_PROCESSED_INPUT", ENABLE_PROCESSED_INPUT, // 0x0001
+ "ENABLE_LINE_INPUT", ENABLE_LINE_INPUT, // 0x0002
+ "ENABLE_ECHO_INPUT", ENABLE_ECHO_INPUT, // 0x0004
+ "ENABLE_WINDOW_INPUT", ENABLE_WINDOW_INPUT, // 0x0008
+ "ENABLE_MOUSE_INPUT", ENABLE_MOUSE_INPUT, // 0x0010
+ "ENABLE_INSERT_MODE", ENABLE_INSERT_MODE, // 0x0020
+ "ENABLE_QUICK_EDIT_MODE", ENABLE_QUICK_EDIT_MODE, // 0x0040
+ "ENABLE_EXTENDED_FLAGS", ENABLE_EXTENDED_FLAGS, // 0x0080
+ "ENABLE_VIRTUAL_TERMINAL_INPUT", 0x0200/*ENABLE_VIRTUAL_TERMINAL_INPUT*/, // 0x0200
+};
+
+int main(int argc, char *argv[]) {
+ std::vector<std::string> args;
+ for (size_t i = 1; i < argc; ++i) {
+ args.push_back(argv[i]);
+ }
+
+ if (args.empty() || args.size() == 1 && args[0] == "info") {
+ DWORD mode = getConsoleMode();
+ printf("mode: 0x%lx\n", mode);
+ for (const auto &flag : kInputFlags) {
+ printf("%-29s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off");
+ mode &= ~flag.value;
+ }
+ for (int i = 0; i < 32; ++i) {
+ if (mode & (1u << i)) {
+ printf("Unrecognized flag: %04x\n", (1u << i));
+ }
+ }
+ return 0;
+ }
+
+ const auto verb = args[0];
+
+ if (verb == "set") {
+ if (args.size() == 2) {
+ const DWORD newMode = parseInt(args[1]);
+ setConsoleMode(newMode);
+ } else if (args.size() == 3) {
+ const DWORD mode = parseInt(args[1]);
+ const DWORD mask = parseInt(args[2]);
+ const int newMode = (getConsoleMode() & ~mask) | (mode & mask);
+ setConsoleMode(newMode);
+ } else {
+ usage();
+ }
+ } else if (verb == "get") {
+ if (args.size() != 1) {
+ usage();
+ }
+ printf("0x%lx\n", getConsoleMode());
+ } else {
+ usage();
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ConinMode.ps1 b/src/libs/3rdparty/winpty/misc/ConinMode.ps1
new file mode 100644
index 0000000000..ecfe8f039e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ConinMode.ps1
@@ -0,0 +1,116 @@
+#
+# PowerShell script for controlling the console QuickEdit and InsertMode flags.
+#
+# Turn QuickEdit off to interact with mouse-driven console programs.
+#
+# Usage:
+#
+# powershell .\ConinMode.ps1 [Options]
+#
+# Options:
+# -QuickEdit [on/off]
+# -InsertMode [on/off]
+# -Mode [integer]
+#
+
+param (
+ [ValidateSet("on", "off")][string] $QuickEdit,
+ [ValidateSet("on", "off")][string] $InsertMode,
+ [int] $Mode
+)
+
+$signature = @'
+[DllImport("kernel32.dll", SetLastError = true)]
+public static extern IntPtr GetStdHandle(int nStdHandle);
+
+[DllImport("kernel32.dll", SetLastError = true)]
+public static extern uint GetConsoleMode(
+ IntPtr hConsoleHandle,
+ out uint lpMode);
+
+[DllImport("kernel32.dll", SetLastError = true)]
+public static extern uint SetConsoleMode(
+ IntPtr hConsoleHandle,
+ uint dwMode);
+
+public const int STD_INPUT_HANDLE = -10;
+public const int ENABLE_INSERT_MODE = 0x0020;
+public const int ENABLE_QUICK_EDIT_MODE = 0x0040;
+public const int ENABLE_EXTENDED_FLAGS = 0x0080;
+'@
+
+$WinAPI = Add-Type -MemberDefinition $signature `
+ -Name WinAPI -Namespace ConinModeScript `
+ -PassThru
+
+function GetConIn {
+ $ret = $WinAPI::GetStdHandle($WinAPI::STD_INPUT_HANDLE)
+ if ($ret -eq -1) {
+ throw "error: cannot get stdin"
+ }
+ return $ret
+}
+
+function GetConsoleMode {
+ $conin = GetConIn
+ $mode = 0
+ $ret = $WinAPI::GetConsoleMode($conin, [ref]$mode)
+ if ($ret -eq 0) {
+ throw "GetConsoleMode failed (is stdin a console?)"
+ }
+ return $mode
+}
+
+function SetConsoleMode($mode) {
+ $conin = GetConIn
+ $ret = $WinAPI::SetConsoleMode($conin, $mode)
+ if ($ret -eq 0) {
+ throw "SetConsoleMode failed (is stdin a console?)"
+ }
+}
+
+$oldMode = GetConsoleMode
+$newMode = $oldMode
+$doingSomething = $false
+
+if ($PSBoundParameters.ContainsKey("Mode")) {
+ $newMode = $Mode
+ $doingSomething = $true
+}
+
+if ($QuickEdit + $InsertMode -ne "") {
+ if (!($newMode -band $WinAPI::ENABLE_EXTENDED_FLAGS)) {
+ # We can't enable an extended flag without overwriting the existing
+ # QuickEdit/InsertMode flags. AFAICT, there is no way to query their
+ # existing values, so at least we can choose sensible defaults.
+ $newMode = $newMode -bor $WinAPI::ENABLE_EXTENDED_FLAGS
+ $newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE
+ $newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE
+ $doingSomething = $true
+ }
+}
+
+if ($QuickEdit -eq "on") {
+ $newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE
+ $doingSomething = $true
+} elseif ($QuickEdit -eq "off") {
+ $newMode = $newMode -band (-bnot $WinAPI::ENABLE_QUICK_EDIT_MODE)
+ $doingSomething = $true
+}
+
+if ($InsertMode -eq "on") {
+ $newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE
+ $doingSomething = $true
+} elseif ($InsertMode -eq "off") {
+ $newMode = $newMode -band (-bnot $WinAPI::ENABLE_INSERT_MODE)
+ $doingSomething = $true
+}
+
+if ($doingSomething) {
+ echo "old mode: $oldMode"
+ SetConsoleMode $newMode
+ $newMode = GetConsoleMode
+ echo "new mode: $newMode"
+} else {
+ echo "mode: $oldMode"
+}
diff --git a/src/libs/3rdparty/winpty/misc/ConoutMode.cc b/src/libs/3rdparty/winpty/misc/ConoutMode.cc
new file mode 100644
index 0000000000..100e0c7bea
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ConoutMode.cc
@@ -0,0 +1,113 @@
+#include <windows.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+static HANDLE getConout() {
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (conout == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "error: cannot get stdout\n");
+ exit(1);
+ }
+ return conout;
+}
+
+static DWORD getConsoleMode() {
+ DWORD mode = 0;
+ if (!GetConsoleMode(getConout(), &mode)) {
+ fprintf(stderr, "error: GetConsoleMode failed (is stdout a console?)\n");
+ exit(1);
+ }
+ return mode;
+}
+
+static void setConsoleMode(DWORD mode) {
+ if (!SetConsoleMode(getConout(), mode)) {
+ fprintf(stderr, "error: SetConsoleMode failed (is stdout a console?)\n");
+ exit(1);
+ }
+}
+
+static long parseInt(const std::string &s) {
+ errno = 0;
+ char *endptr = nullptr;
+ long result = strtol(s.c_str(), &endptr, 0);
+ if (errno != 0 || !endptr || *endptr != '\0') {
+ fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str());
+ exit(1);
+ }
+ return result;
+}
+
+static void usage() {
+ printf("Usage: ConoutMode [verb] [options]\n");
+ printf("Verbs:\n");
+ printf(" [info] Dumps info about mode flags.\n");
+ printf(" get Prints the mode DWORD.\n");
+ printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n");
+ printf(" set VALUE MASK\n");
+ printf(" Same as `set VALUE`, but only alters the bits in MASK.\n");
+ exit(1);
+}
+
+struct {
+ const char *name;
+ DWORD value;
+} kOutputFlags[] = {
+ "ENABLE_PROCESSED_OUTPUT", ENABLE_PROCESSED_OUTPUT, // 0x0001
+ "ENABLE_WRAP_AT_EOL_OUTPUT", ENABLE_WRAP_AT_EOL_OUTPUT, // 0x0002
+ "ENABLE_VIRTUAL_TERMINAL_PROCESSING", 0x0004/*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/, // 0x0004
+ "DISABLE_NEWLINE_AUTO_RETURN", 0x0008/*DISABLE_NEWLINE_AUTO_RETURN*/, // 0x0008
+ "ENABLE_LVB_GRID_WORLDWIDE", 0x0010/*ENABLE_LVB_GRID_WORLDWIDE*/, //0x0010
+};
+
+int main(int argc, char *argv[]) {
+ std::vector<std::string> args;
+ for (size_t i = 1; i < argc; ++i) {
+ args.push_back(argv[i]);
+ }
+
+ if (args.empty() || args.size() == 1 && args[0] == "info") {
+ DWORD mode = getConsoleMode();
+ printf("mode: 0x%lx\n", mode);
+ for (const auto &flag : kOutputFlags) {
+ printf("%-34s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off");
+ mode &= ~flag.value;
+ }
+ for (int i = 0; i < 32; ++i) {
+ if (mode & (1u << i)) {
+ printf("Unrecognized flag: %04x\n", (1u << i));
+ }
+ }
+ return 0;
+ }
+
+ const auto verb = args[0];
+
+ if (verb == "set") {
+ if (args.size() == 2) {
+ const DWORD newMode = parseInt(args[1]);
+ setConsoleMode(newMode);
+ } else if (args.size() == 3) {
+ const DWORD mode = parseInt(args[1]);
+ const DWORD mask = parseInt(args[2]);
+ const int newMode = (getConsoleMode() & ~mask) | (mode & mask);
+ setConsoleMode(newMode);
+ } else {
+ usage();
+ }
+ } else if (verb == "get") {
+ if (args.size() != 1) {
+ usage();
+ }
+ printf("0x%lx\n", getConsoleMode());
+ } else {
+ usage();
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/DebugClient.py b/src/libs/3rdparty/winpty/misc/DebugClient.py
new file mode 100644
index 0000000000..cd12df8924
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/DebugClient.py
@@ -0,0 +1,42 @@
+#!python
+# Run with native CPython. Needs pywin32 extensions.
+
+# Copyright (c) 2011-2012 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import winerror
+import win32pipe
+import win32file
+import win32api
+import sys
+import pywintypes
+import time
+
+if len(sys.argv) != 2:
+ print("Usage: %s message" % sys.argv[0])
+ sys.exit(1)
+
+message = "[%05.3f %s]: %s" % (time.time() % 100000, sys.argv[0], sys.argv[1])
+
+win32pipe.CallNamedPipe(
+ "\\\\.\\pipe\\DebugServer",
+ message.encode(),
+ 16,
+ win32pipe.NMPWAIT_WAIT_FOREVER)
diff --git a/src/libs/3rdparty/winpty/misc/DebugServer.py b/src/libs/3rdparty/winpty/misc/DebugServer.py
new file mode 100644
index 0000000000..3fc068bae7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/DebugServer.py
@@ -0,0 +1,63 @@
+#!python
+#
+# Run with native CPython. Needs pywin32 extensions.
+
+# Copyright (c) 2011-2012 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import win32pipe
+import win32api
+import win32file
+import time
+import threading
+import sys
+
+# A message may not be larger than this size.
+MSG_SIZE=4096
+
+serverPipe = win32pipe.CreateNamedPipe(
+ "\\\\.\\pipe\\DebugServer",
+ win32pipe.PIPE_ACCESS_DUPLEX,
+ win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE,
+ win32pipe.PIPE_UNLIMITED_INSTANCES,
+ MSG_SIZE,
+ MSG_SIZE,
+ 10 * 1000,
+ None)
+while True:
+ win32pipe.ConnectNamedPipe(serverPipe, None)
+ (ret, data) = win32file.ReadFile(serverPipe, MSG_SIZE)
+ print(data.decode())
+ sys.stdout.flush()
+
+ # The client uses CallNamedPipe to send its message. CallNamedPipe waits
+ # for a reply message. If I send a reply, however, using WriteFile, then
+ # sometimes WriteFile fails with:
+ # pywintypes.error: (232, 'WriteFile', 'The pipe is being closed.')
+ # I can't figure out how to write a strictly correct pipe server, but if
+ # I comment out the WriteFile line, then everything seems to work. I
+ # think the DisconnectNamedPipe call aborts the client's CallNamedPipe
+ # call normally.
+
+ try:
+ win32file.WriteFile(serverPipe, b'OK')
+ except:
+ pass
+ win32pipe.DisconnectNamedPipe(serverPipe)
diff --git a/src/libs/3rdparty/winpty/misc/DumpLines.py b/src/libs/3rdparty/winpty/misc/DumpLines.py
new file mode 100644
index 0000000000..40049961b5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/DumpLines.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+import sys
+
+for i in range(1, int(sys.argv[1]) + 1):
+ print i, "X" * 78
diff --git a/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt b/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt
new file mode 100644
index 0000000000..37914dac26
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt
@@ -0,0 +1,46 @@
+Note regarding ENABLE_EXTENDED_FLAGS (2016-05-30)
+
+There is a complicated interaction between the ENABLE_EXTENDED_FLAGS flag
+and the ENABLE_QUICK_EDIT_MODE and ENABLE_INSERT_MODE flags (presumably for
+backwards compatibility?). I studied the behavior on Windows 7 and Windows
+10, with both the old and new consoles, and I didn't see any differences
+between versions. Here's what I seemed to observe:
+
+ - The console has three flags internally:
+ - QuickEdit
+ - InsertMode
+ - ExtendedFlags
+
+ - SetConsoleMode psuedocode:
+ void SetConsoleMode(..., DWORD mode) {
+ ExtendedFlags = (mode & (ENABLE_EXTENDED_FLAGS
+ | ENABLE_QUICK_EDIT_MODE
+ | ENABLE_INSERT_MODE )) != 0;
+ if (ExtendedFlags) {
+ QuickEdit = (mode & ENABLE_QUICK_EDIT_MODE) != 0;
+ InsertMode = (mode & ENABLE_INSERT_MODE) != 0;
+ }
+ }
+
+ - Setting QuickEdit or InsertMode from the properties dialog GUI does not
+ affect the ExtendedFlags setting -- it simply toggles the one flag.
+
+ - GetConsoleMode psuedocode:
+ GetConsoleMode(..., DWORD *result) {
+ if (ExtendedFlags) {
+ *result |= ENABLE_EXTENDED_FLAGS;
+ if (QuickEdit) { *result |= ENABLE_QUICK_EDIT_MODE; }
+ if (InsertMode) { *result |= ENABLE_INSERT_MODE; }
+ }
+ }
+
+Effectively, the ExtendedFlags flags controls whether the other two flags
+are visible/controlled by the user application. If they aren't visible,
+though, there is no way for the user application to make them visible,
+except by overwriting their values! Calling SetConsoleMode with just
+ENABLE_EXTENDED_FLAGS would clear the extended flags we want to read.
+
+Consequently, if a program temporarily alters the QuickEdit flag (e.g. to
+enable mouse input), it cannot restore the original values of the QuickEdit
+and InsertMode flags, UNLESS every other console program cooperates by
+keeping the ExtendedFlags flag set.
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt
new file mode 100644
index 0000000000..067bd3824a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt
@@ -0,0 +1,528 @@
+==================================
+Code Page 437, Consolas font
+==================================
+
+Options: -face "Consolas" -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+FontSurvey "-face \"Consolas\" -family 0x36"
+
+Windows 7
+---------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 8
+---------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 8.1
+-----------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,7 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,21 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,35 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,63 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt
new file mode 100644
index 0000000000..0eed93ad98
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt
@@ -0,0 +1,633 @@
+==================================
+Code Page 437, Lucida Console font
+==================================
+
+Options: -face "Lucida Console" -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+FontSurvey "-face \"Lucida Console\" -family 0x36"
+
+Vista
+-----
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+
+Windows 7
+---------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 8
+---------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,64 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt
new file mode 100644
index 0000000000..ed3637eac1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt
@@ -0,0 +1,630 @@
+=======================================
+Code Page 932, Japanese, MS Gothic font
+=======================================
+
+Options: -face-gothic -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 BAD (HHHFHH)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 BAD (HHHFHH)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 BAD (HHHFHH)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 BAD (HHHFHH)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 BAD (HHHFHH)
+Size 23: 12,23 BAD (HHHFHH)
+Size 24: 12,24 BAD (HHHFHH)
+Size 25: 13,25 BAD (HHHFHH)
+Size 26: 13,26 BAD (HHHFHH)
+Size 27: 14,27 BAD (HHHFHH)
+Size 28: 14,28 BAD (HHHFHH)
+Size 29: 15,29 BAD (HHHFHH)
+Size 30: 15,30 BAD (HHHFHH)
+Size 31: 16,31 BAD (HHHFHH)
+Size 32: 16,33 BAD (HHHFHH)
+Size 33: 17,33 BAD (HHHFHH)
+Size 34: 17,34 BAD (HHHFHH)
+Size 35: 18,35 BAD (HHHFHH)
+Size 36: 18,36 BAD (HHHFHH)
+Size 37: 19,37 BAD (HHHFHH)
+Size 38: 19,38 BAD (HHHFHH)
+Size 39: 20,39 BAD (HHHFHH)
+Size 40: 20,40 BAD (HHHFHH)
+Size 41: 21,41 BAD (HHHFHH)
+Size 42: 21,42 BAD (HHHFHH)
+Size 43: 22,43 BAD (HHHFHH)
+Size 44: 22,44 BAD (HHHFHH)
+Size 45: 23,45 BAD (HHHFHH)
+Size 46: 23,46 BAD (HHHFHH)
+Size 47: 24,47 BAD (HHHFHH)
+Size 48: 24,48 BAD (HHHFHH)
+Size 49: 25,49 BAD (HHHFHH)
+Size 50: 25,50 BAD (HHHFHH)
+Size 51: 26,51 BAD (HHHFHH)
+Size 52: 26,52 BAD (HHHFHH)
+Size 53: 27,53 BAD (HHHFHH)
+Size 54: 27,54 BAD (HHHFHH)
+Size 55: 28,55 BAD (HHHFHH)
+Size 56: 28,56 BAD (HHHFHH)
+Size 57: 29,57 BAD (HHHFHH)
+Size 58: 29,58 BAD (HHHFHH)
+Size 59: 30,59 BAD (HHHFHH)
+Size 60: 30,60 BAD (HHHFHH)
+Size 61: 31,61 BAD (HHHFHH)
+Size 62: 31,62 BAD (HHHFHH)
+Size 63: 32,63 BAD (HHHFHH)
+Size 64: 32,64 BAD (HHHFHH)
+Size 65: 33,65 BAD (HHHFHH)
+Size 66: 33,66 BAD (HHHFHH)
+Size 67: 34,67 BAD (HHHFHH)
+Size 68: 34,68 BAD (HHHFHH)
+Size 69: 35,69 BAD (HHHFHH)
+Size 70: 35,70 BAD (HHHFHH)
+Size 71: 36,71 BAD (HHHFHH)
+Size 72: 36,72 BAD (HHHFHH)
+Size 73: 37,73 BAD (HHHFHH)
+Size 74: 37,74 BAD (HHHFHH)
+Size 75: 38,75 BAD (HHHFHH)
+Size 76: 38,76 BAD (HHHFHH)
+Size 77: 39,77 BAD (HHHFHH)
+Size 78: 39,78 BAD (HHHFHH)
+Size 79: 40,79 BAD (HHHFHH)
+Size 80: 40,80 BAD (HHHFHH)
+Size 81: 41,81 BAD (HHHFHH)
+Size 82: 41,82 BAD (HHHFHH)
+Size 83: 42,83 BAD (HHHFHH)
+Size 84: 42,84 BAD (HHHFHH)
+Size 85: 43,85 BAD (HHHFHH)
+Size 86: 43,86 BAD (HHHFHH)
+Size 87: 44,87 BAD (HHHFHH)
+Size 88: 44,88 BAD (HHHFHH)
+Size 89: 45,89 BAD (HHHFHH)
+Size 90: 45,90 BAD (HHHFHH)
+Size 91: 46,91 BAD (HHHFHH)
+Size 92: 46,92 BAD (HHHFHH)
+Size 93: 47,93 BAD (HHHFHH)
+Size 94: 47,94 BAD (HHHFHH)
+Size 95: 48,95 BAD (HHHFHH)
+Size 96: 48,97 BAD (HHHFHH)
+Size 97: 49,97 BAD (HHHFHH)
+Size 98: 49,98 BAD (HHHFHH)
+Size 99: 50,99 BAD (HHHFHH)
+Size 100: 50,100 BAD (HHHFHH)
+
+Windows 7
+---------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 BAD (FFFFFF)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 BAD (FFFFFF)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 BAD (FFFFFF)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 BAD (FFFFHH)
+Size 2: 1,2 BAD (FFFFHH)
+Size 3: 2,3 BAD (FFFFFF)
+Size 4: 2,4 BAD (FFFFHH)
+Size 5: 3,5 BAD (FFFFFF)
+Size 6: 3,6 BAD (FFFFHH)
+Size 7: 4,7 BAD (FFFFFF)
+Size 8: 4,8 BAD (FFFFHH)
+Size 9: 5,9 BAD (FFFFFF)
+Size 10: 5,10 BAD (FFFFHH)
+Size 11: 6,11 BAD (FFFFFF)
+Size 12: 6,12 BAD (FFFFHH)
+Size 13: 7,13 BAD (FFFFFF)
+Size 14: 7,14 BAD (FFFFHH)
+Size 15: 8,15 BAD (FFFFFF)
+Size 16: 8,16 BAD (FFFFHH)
+Size 17: 9,17 BAD (FFFFFF)
+Size 18: 9,18 BAD (FFFFHH)
+Size 19: 10,19 BAD (FFFFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 BAD (FFFFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 BAD (FFFFHH)
+Size 2: 1,2 BAD (FFFFHH)
+Size 3: 2,3 BAD (FFFFFF)
+Size 4: 2,4 BAD (FFFFHH)
+Size 5: 3,5 BAD (FFFFFF)
+Size 6: 3,6 BAD (FFFFHH)
+Size 7: 4,7 BAD (FFFFFF)
+Size 8: 4,8 BAD (FFFFHH)
+Size 9: 5,9 BAD (FFFFFF)
+Size 10: 5,10 BAD (FFFFHH)
+Size 11: 6,11 BAD (FFFFFF)
+Size 12: 6,12 BAD (FFFFHH)
+Size 13: 7,13 BAD (FFFFFF)
+Size 14: 7,14 BAD (FFFFHH)
+Size 15: 8,15 BAD (FFFFFF)
+Size 16: 8,16 BAD (FFFFHH)
+Size 17: 9,17 BAD (FFFFFF)
+Size 18: 9,18 BAD (FFFFHH)
+Size 19: 10,19 BAD (FFFFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 BAD (FFFFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 BAD (FFFFHH)
+Size 2: 1,2 BAD (FFFFHH)
+Size 3: 2,3 BAD (FFFFFF)
+Size 4: 2,4 BAD (FFFFHH)
+Size 5: 3,5 BAD (FFFFFF)
+Size 6: 3,6 BAD (FFFFHH)
+Size 7: 4,7 BAD (FFFFFF)
+Size 8: 4,8 BAD (FFFFHH)
+Size 9: 5,9 BAD (FFFFFF)
+Size 10: 5,10 BAD (FFFFHH)
+Size 11: 6,11 BAD (FFFFFF)
+Size 12: 6,12 BAD (FFFFHH)
+Size 13: 7,13 BAD (FFFFFF)
+Size 14: 7,14 BAD (FFFFHH)
+Size 15: 8,15 BAD (FFFFFF)
+Size 16: 8,16 BAD (FFFFHH)
+Size 17: 9,17 BAD (FFFFFF)
+Size 18: 9,18 BAD (FFFFHH)
+Size 19: 10,19 BAD (FFFFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 BAD (FFFFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 OK (HHHFFF)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 OK (HHHFFF)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 OK (HHHFFF)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 OK (HHHFFF)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 OK (HHHFFF)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 OK (HHHFFF)
+Size 23: 12,23 OK (HHHFFF)
+Size 24: 12,24 OK (HHHFFF)
+Size 25: 13,25 OK (HHHFFF)
+Size 26: 13,26 OK (HHHFFF)
+Size 27: 14,27 OK (HHHFFF)
+Size 28: 14,28 OK (HHHFFF)
+Size 29: 15,29 OK (HHHFFF)
+Size 30: 15,30 OK (HHHFFF)
+Size 31: 16,31 OK (HHHFFF)
+Size 32: 16,32 OK (HHHFFF)
+Size 33: 17,33 OK (HHHFFF)
+Size 34: 17,34 OK (HHHFFF)
+Size 35: 18,35 OK (HHHFFF)
+Size 36: 18,36 OK (HHHFFF)
+Size 37: 19,37 OK (HHHFFF)
+Size 38: 19,38 OK (HHHFFF)
+Size 39: 20,39 OK (HHHFFF)
+Size 40: 20,40 OK (HHHFFF)
+Size 41: 21,41 OK (HHHFFF)
+Size 42: 21,42 OK (HHHFFF)
+Size 43: 22,43 OK (HHHFFF)
+Size 44: 22,44 OK (HHHFFF)
+Size 45: 23,45 OK (HHHFFF)
+Size 46: 23,46 OK (HHHFFF)
+Size 47: 24,47 OK (HHHFFF)
+Size 48: 24,48 OK (HHHFFF)
+Size 49: 25,49 OK (HHHFFF)
+Size 50: 25,50 OK (HHHFFF)
+Size 51: 26,51 OK (HHHFFF)
+Size 52: 26,52 OK (HHHFFF)
+Size 53: 27,53 OK (HHHFFF)
+Size 54: 27,54 OK (HHHFFF)
+Size 55: 28,55 OK (HHHFFF)
+Size 56: 28,56 OK (HHHFFF)
+Size 57: 29,57 OK (HHHFFF)
+Size 58: 29,58 OK (HHHFFF)
+Size 59: 30,59 OK (HHHFFF)
+Size 60: 30,60 OK (HHHFFF)
+Size 61: 31,61 OK (HHHFFF)
+Size 62: 31,62 OK (HHHFFF)
+Size 63: 32,63 OK (HHHFFF)
+Size 64: 32,64 OK (HHHFFF)
+Size 65: 33,65 OK (HHHFFF)
+Size 66: 33,66 OK (HHHFFF)
+Size 67: 34,67 OK (HHHFFF)
+Size 68: 34,68 OK (HHHFFF)
+Size 69: 35,69 OK (HHHFFF)
+Size 70: 35,70 OK (HHHFFF)
+Size 71: 36,71 OK (HHHFFF)
+Size 72: 36,72 OK (HHHFFF)
+Size 73: 37,73 OK (HHHFFF)
+Size 74: 37,74 OK (HHHFFF)
+Size 75: 38,75 OK (HHHFFF)
+Size 76: 38,76 OK (HHHFFF)
+Size 77: 39,77 OK (HHHFFF)
+Size 78: 39,78 OK (HHHFFF)
+Size 79: 40,79 OK (HHHFFF)
+Size 80: 40,80 OK (HHHFFF)
+Size 81: 41,81 OK (HHHFFF)
+Size 82: 41,82 OK (HHHFFF)
+Size 83: 42,83 OK (HHHFFF)
+Size 84: 42,84 OK (HHHFFF)
+Size 85: 43,85 OK (HHHFFF)
+Size 86: 43,86 OK (HHHFFF)
+Size 87: 44,87 OK (HHHFFF)
+Size 88: 44,88 OK (HHHFFF)
+Size 89: 45,89 OK (HHHFFF)
+Size 90: 45,90 OK (HHHFFF)
+Size 91: 46,91 OK (HHHFFF)
+Size 92: 46,92 OK (HHHFFF)
+Size 93: 47,93 OK (HHHFFF)
+Size 94: 47,94 OK (HHHFFF)
+Size 95: 48,95 OK (HHHFFF)
+Size 96: 48,96 OK (HHHFFF)
+Size 97: 49,97 OK (HHHFFF)
+Size 98: 49,98 OK (HHHFFF)
+Size 99: 50,99 OK (HHHFFF)
+Size 100: 50,100 OK (HHHFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt
new file mode 100644
index 0000000000..43210dac51
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt
@@ -0,0 +1,630 @@
+==========================================================
+Code Page 936, Chinese Simplified (China/PRC), SimSun font
+==========================================================
+
+Options: -face-simsun -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (HHHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (HHHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (HHHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (HHHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (HHHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (HHHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (HHHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (HHHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (HHHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (HHHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (HHHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (HHHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (HHHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (HHHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (HHHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (HHHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (HHHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (HHHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (HHHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (HHHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (HHHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (HHHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (HHHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (HHHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (HHHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (HHHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (HHHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (HHHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (HHHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (HHHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 7
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 GOOD (HHFFFF)
+Size 4: 2,4 GOOD (HHFFFF)
+Size 5: 3,5 GOOD (HHFFFF)
+Size 6: 3,6 GOOD (HHFFFF)
+Size 7: 4,7 GOOD (HHFFFF)
+Size 8: 4,8 GOOD (HHFFFF)
+Size 9: 5,9 GOOD (HHFFFF)
+Size 10: 5,10 GOOD (HHFFFF)
+Size 11: 6,11 GOOD (HHFFFF)
+Size 12: 6,12 GOOD (HHFFFF)
+Size 13: 7,13 GOOD (HHFFFF)
+Size 14: 7,14 GOOD (HHFFFF)
+Size 15: 8,15 GOOD (HHFFFF)
+Size 16: 8,16 GOOD (HHFFFF)
+Size 17: 9,17 GOOD (HHFFFF)
+Size 18: 9,18 GOOD (HHFFFF)
+Size 19: 10,19 GOOD (HHFFFF)
+Size 20: 10,20 GOOD (HHFFFF)
+Size 21: 11,21 GOOD (HHFFFF)
+Size 22: 11,22 GOOD (HHFFFF)
+Size 23: 12,23 GOOD (HHFFFF)
+Size 24: 12,24 GOOD (HHFFFF)
+Size 25: 13,25 GOOD (HHFFFF)
+Size 26: 13,26 GOOD (HHFFFF)
+Size 27: 14,27 GOOD (HHFFFF)
+Size 28: 14,28 GOOD (HHFFFF)
+Size 29: 15,29 GOOD (HHFFFF)
+Size 30: 15,30 GOOD (HHFFFF)
+Size 31: 16,31 GOOD (HHFFFF)
+Size 32: 16,32 GOOD (HHFFFF)
+Size 33: 17,33 GOOD (HHFFFF)
+Size 34: 17,34 GOOD (HHFFFF)
+Size 35: 18,35 GOOD (HHFFFF)
+Size 36: 18,36 GOOD (HHFFFF)
+Size 37: 19,37 GOOD (HHFFFF)
+Size 38: 19,38 GOOD (HHFFFF)
+Size 39: 20,39 GOOD (HHFFFF)
+Size 40: 20,40 GOOD (HHFFFF)
+Size 41: 21,41 GOOD (HHFFFF)
+Size 42: 21,42 GOOD (HHFFFF)
+Size 43: 22,43 GOOD (HHFFFF)
+Size 44: 22,44 GOOD (HHFFFF)
+Size 45: 23,45 GOOD (HHFFFF)
+Size 46: 23,46 GOOD (HHFFFF)
+Size 47: 24,47 GOOD (HHFFFF)
+Size 48: 24,48 GOOD (HHFFFF)
+Size 49: 25,49 GOOD (HHFFFF)
+Size 50: 25,50 GOOD (HHFFFF)
+Size 51: 26,51 GOOD (HHFFFF)
+Size 52: 26,52 GOOD (HHFFFF)
+Size 53: 27,53 GOOD (HHFFFF)
+Size 54: 27,54 GOOD (HHFFFF)
+Size 55: 28,55 GOOD (HHFFFF)
+Size 56: 28,56 GOOD (HHFFFF)
+Size 57: 29,57 GOOD (HHFFFF)
+Size 58: 29,58 GOOD (HHFFFF)
+Size 59: 30,59 GOOD (HHFFFF)
+Size 60: 30,60 GOOD (HHFFFF)
+Size 61: 31,61 GOOD (HHFFFF)
+Size 62: 31,62 GOOD (HHFFFF)
+Size 63: 32,63 GOOD (HHFFFF)
+Size 64: 32,64 GOOD (HHFFFF)
+Size 65: 33,65 GOOD (HHFFFF)
+Size 66: 33,66 GOOD (HHFFFF)
+Size 67: 34,67 GOOD (HHFFFF)
+Size 68: 34,68 GOOD (HHFFFF)
+Size 69: 35,69 GOOD (HHFFFF)
+Size 70: 35,70 GOOD (HHFFFF)
+Size 71: 36,71 GOOD (HHFFFF)
+Size 72: 36,72 GOOD (HHFFFF)
+Size 73: 37,73 GOOD (HHFFFF)
+Size 74: 37,74 GOOD (HHFFFF)
+Size 75: 38,75 GOOD (HHFFFF)
+Size 76: 38,76 GOOD (HHFFFF)
+Size 77: 39,77 GOOD (HHFFFF)
+Size 78: 39,78 GOOD (HHFFFF)
+Size 79: 40,79 GOOD (HHFFFF)
+Size 80: 40,80 GOOD (HHFFFF)
+Size 81: 41,81 GOOD (HHFFFF)
+Size 82: 41,82 GOOD (HHFFFF)
+Size 83: 42,83 GOOD (HHFFFF)
+Size 84: 42,84 GOOD (HHFFFF)
+Size 85: 43,85 GOOD (HHFFFF)
+Size 86: 43,86 GOOD (HHFFFF)
+Size 87: 44,87 GOOD (HHFFFF)
+Size 88: 44,88 GOOD (HHFFFF)
+Size 89: 45,89 GOOD (HHFFFF)
+Size 90: 45,90 GOOD (HHFFFF)
+Size 91: 46,91 GOOD (HHFFFF)
+Size 92: 46,92 GOOD (HHFFFF)
+Size 93: 47,93 GOOD (HHFFFF)
+Size 94: 47,94 GOOD (HHFFFF)
+Size 95: 48,95 GOOD (HHFFFF)
+Size 96: 48,96 GOOD (HHFFFF)
+Size 97: 49,97 GOOD (HHFFFF)
+Size 98: 49,98 GOOD (HHFFFF)
+Size 99: 50,99 GOOD (HHFFFF)
+Size 100: 50,100 GOOD (HHFFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt
new file mode 100644
index 0000000000..2f0ea1e7c2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt
@@ -0,0 +1,630 @@
+=====================================
+Code Page 949, Korean, GulimChe font
+=====================================
+
+Options: -face-gulimche -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (HHHFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (HHHFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (HHHFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (HHHFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (HHHFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (HHHFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (HHHFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (HHHFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (HHHFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (HHHFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (HHHFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (HHHFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (HHHFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (HHHFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (HHHFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (HHHFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (HHHFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (HHHFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (HHHFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (HHHFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (HHHFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (HHHFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (HHHFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (HHHFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (HHHFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (HHHFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (HHHFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (HHHFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (HHHFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (HHHFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (HHHFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (HHHFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (HHHFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (HHHFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (HHHFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (HHHFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (HHHFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (HHHFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (HHHFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (HHHFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (HHHFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (HHHFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (HHHFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (HHHFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (HHHFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (HHHFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (HHHFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (HHHFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 7
+---------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 OK (HHHFFF)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 OK (HHHFFF)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 OK (HHHFFF)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 OK (HHHFFF)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 OK (HHHFFF)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 OK (HHHFFF)
+Size 23: 12,23 OK (HHHFFF)
+Size 24: 12,24 OK (HHHFFF)
+Size 25: 13,25 OK (HHHFFF)
+Size 26: 13,26 OK (HHHFFF)
+Size 27: 14,27 OK (HHHFFF)
+Size 28: 14,28 OK (HHHFFF)
+Size 29: 15,29 OK (HHHFFF)
+Size 30: 15,30 OK (HHHFFF)
+Size 31: 16,31 OK (HHHFFF)
+Size 32: 16,32 OK (HHHFFF)
+Size 33: 17,33 OK (HHHFFF)
+Size 34: 17,34 OK (HHHFFF)
+Size 35: 18,35 OK (HHHFFF)
+Size 36: 18,36 OK (HHHFFF)
+Size 37: 19,37 OK (HHHFFF)
+Size 38: 19,38 OK (HHHFFF)
+Size 39: 20,39 OK (HHHFFF)
+Size 40: 20,40 OK (HHHFFF)
+Size 41: 21,41 OK (HHHFFF)
+Size 42: 21,42 OK (HHHFFF)
+Size 43: 22,43 OK (HHHFFF)
+Size 44: 22,44 OK (HHHFFF)
+Size 45: 23,45 OK (HHHFFF)
+Size 46: 23,46 OK (HHHFFF)
+Size 47: 24,47 OK (HHHFFF)
+Size 48: 24,48 OK (HHHFFF)
+Size 49: 25,49 OK (HHHFFF)
+Size 50: 25,50 OK (HHHFFF)
+Size 51: 26,51 OK (HHHFFF)
+Size 52: 26,52 OK (HHHFFF)
+Size 53: 27,53 OK (HHHFFF)
+Size 54: 27,54 OK (HHHFFF)
+Size 55: 28,55 OK (HHHFFF)
+Size 56: 28,56 OK (HHHFFF)
+Size 57: 29,57 OK (HHHFFF)
+Size 58: 29,58 OK (HHHFFF)
+Size 59: 30,59 OK (HHHFFF)
+Size 60: 30,60 OK (HHHFFF)
+Size 61: 31,61 OK (HHHFFF)
+Size 62: 31,62 OK (HHHFFF)
+Size 63: 32,63 OK (HHHFFF)
+Size 64: 32,64 OK (HHHFFF)
+Size 65: 33,65 OK (HHHFFF)
+Size 66: 33,66 OK (HHHFFF)
+Size 67: 34,67 OK (HHHFFF)
+Size 68: 34,68 OK (HHHFFF)
+Size 69: 35,69 OK (HHHFFF)
+Size 70: 35,70 OK (HHHFFF)
+Size 71: 36,71 OK (HHHFFF)
+Size 72: 36,72 OK (HHHFFF)
+Size 73: 37,73 OK (HHHFFF)
+Size 74: 37,74 OK (HHHFFF)
+Size 75: 38,75 OK (HHHFFF)
+Size 76: 38,76 OK (HHHFFF)
+Size 77: 39,77 OK (HHHFFF)
+Size 78: 39,78 OK (HHHFFF)
+Size 79: 40,79 OK (HHHFFF)
+Size 80: 40,80 OK (HHHFFF)
+Size 81: 41,81 OK (HHHFFF)
+Size 82: 41,82 OK (HHHFFF)
+Size 83: 42,83 OK (HHHFFF)
+Size 84: 42,84 OK (HHHFFF)
+Size 85: 43,85 OK (HHHFFF)
+Size 86: 43,86 OK (HHHFFF)
+Size 87: 44,87 OK (HHHFFF)
+Size 88: 44,88 OK (HHHFFF)
+Size 89: 45,89 OK (HHHFFF)
+Size 90: 45,90 OK (HHHFFF)
+Size 91: 46,91 OK (HHHFFF)
+Size 92: 46,92 OK (HHHFFF)
+Size 93: 47,93 OK (HHHFFF)
+Size 94: 47,94 OK (HHHFFF)
+Size 95: 48,95 OK (HHHFFF)
+Size 96: 48,96 OK (HHHFFF)
+Size 97: 49,97 OK (HHHFFF)
+Size 98: 49,98 OK (HHHFFF)
+Size 99: 50,99 OK (HHHFFF)
+Size 100: 50,100 OK (HHHFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt
new file mode 100644
index 0000000000..0dbade504d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt
@@ -0,0 +1,630 @@
+===========================================================
+Code Page 950, Chinese Traditional (Taiwan), MingLight font
+===========================================================
+
+Options: -face-minglight -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (HHHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (HHHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (HHHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (HHHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (HHHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (HHHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (HHHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (HHHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (HHHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (HHHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (HHHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (HHHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (HHHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (HHHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (HHHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (HHHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (HHHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (HHHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (HHHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (HHHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (HHHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (HHHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (HHHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (HHHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (HHHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (HHHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (HHHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (HHHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (HHHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (HHHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (HHHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (HHHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (HHHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (HHHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (HHHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (HHHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (HHHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (HHHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (HHHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (HHHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (HHHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (HHHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (HHHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (HHHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (HHHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (HHHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (HHHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (HHHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 7
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 GOOD (HHFFFF)
+Size 4: 2,4 GOOD (HHFFFF)
+Size 5: 3,5 GOOD (HHFFFF)
+Size 6: 3,6 GOOD (HHFFFF)
+Size 7: 4,7 GOOD (HHFFFF)
+Size 8: 4,8 GOOD (HHFFFF)
+Size 9: 5,9 GOOD (HHFFFF)
+Size 10: 5,10 GOOD (HHFFFF)
+Size 11: 6,11 GOOD (HHFFFF)
+Size 12: 6,12 GOOD (HHFFFF)
+Size 13: 7,13 GOOD (HHFFFF)
+Size 14: 7,14 GOOD (HHFFFF)
+Size 15: 8,15 GOOD (HHFFFF)
+Size 16: 8,16 GOOD (HHFFFF)
+Size 17: 9,17 GOOD (HHFFFF)
+Size 18: 9,18 GOOD (HHFFFF)
+Size 19: 10,19 GOOD (HHFFFF)
+Size 20: 10,20 GOOD (HHFFFF)
+Size 21: 11,21 GOOD (HHFFFF)
+Size 22: 11,22 GOOD (HHFFFF)
+Size 23: 12,23 GOOD (HHFFFF)
+Size 24: 12,24 GOOD (HHFFFF)
+Size 25: 13,25 GOOD (HHFFFF)
+Size 26: 13,26 GOOD (HHFFFF)
+Size 27: 14,27 GOOD (HHFFFF)
+Size 28: 14,28 GOOD (HHFFFF)
+Size 29: 15,29 GOOD (HHFFFF)
+Size 30: 15,30 GOOD (HHFFFF)
+Size 31: 16,31 GOOD (HHFFFF)
+Size 32: 16,32 GOOD (HHFFFF)
+Size 33: 17,33 GOOD (HHFFFF)
+Size 34: 17,34 GOOD (HHFFFF)
+Size 35: 18,35 GOOD (HHFFFF)
+Size 36: 18,36 GOOD (HHFFFF)
+Size 37: 19,37 GOOD (HHFFFF)
+Size 38: 19,38 GOOD (HHFFFF)
+Size 39: 20,39 GOOD (HHFFFF)
+Size 40: 20,40 GOOD (HHFFFF)
+Size 41: 21,41 GOOD (HHFFFF)
+Size 42: 21,42 GOOD (HHFFFF)
+Size 43: 22,43 GOOD (HHFFFF)
+Size 44: 22,44 GOOD (HHFFFF)
+Size 45: 23,45 GOOD (HHFFFF)
+Size 46: 23,46 GOOD (HHFFFF)
+Size 47: 24,47 GOOD (HHFFFF)
+Size 48: 24,48 GOOD (HHFFFF)
+Size 49: 25,49 GOOD (HHFFFF)
+Size 50: 25,50 GOOD (HHFFFF)
+Size 51: 26,51 GOOD (HHFFFF)
+Size 52: 26,52 GOOD (HHFFFF)
+Size 53: 27,53 GOOD (HHFFFF)
+Size 54: 27,54 GOOD (HHFFFF)
+Size 55: 28,55 GOOD (HHFFFF)
+Size 56: 28,56 GOOD (HHFFFF)
+Size 57: 29,57 GOOD (HHFFFF)
+Size 58: 29,58 GOOD (HHFFFF)
+Size 59: 30,59 GOOD (HHFFFF)
+Size 60: 30,60 GOOD (HHFFFF)
+Size 61: 31,61 GOOD (HHFFFF)
+Size 62: 31,62 GOOD (HHFFFF)
+Size 63: 32,63 GOOD (HHFFFF)
+Size 64: 32,64 GOOD (HHFFFF)
+Size 65: 33,65 GOOD (HHFFFF)
+Size 66: 33,66 GOOD (HHFFFF)
+Size 67: 34,67 GOOD (HHFFFF)
+Size 68: 34,68 GOOD (HHFFFF)
+Size 69: 35,69 GOOD (HHFFFF)
+Size 70: 35,70 GOOD (HHFFFF)
+Size 71: 36,71 GOOD (HHFFFF)
+Size 72: 36,72 GOOD (HHFFFF)
+Size 73: 37,73 GOOD (HHFFFF)
+Size 74: 37,74 GOOD (HHFFFF)
+Size 75: 38,75 GOOD (HHFFFF)
+Size 76: 38,76 GOOD (HHFFFF)
+Size 77: 39,77 GOOD (HHFFFF)
+Size 78: 39,78 GOOD (HHFFFF)
+Size 79: 40,79 GOOD (HHFFFF)
+Size 80: 40,80 GOOD (HHFFFF)
+Size 81: 41,81 GOOD (HHFFFF)
+Size 82: 41,82 GOOD (HHFFFF)
+Size 83: 42,83 GOOD (HHFFFF)
+Size 84: 42,84 GOOD (HHFFFF)
+Size 85: 43,85 GOOD (HHFFFF)
+Size 86: 43,86 GOOD (HHFFFF)
+Size 87: 44,87 GOOD (HHFFFF)
+Size 88: 44,88 GOOD (HHFFFF)
+Size 89: 45,89 GOOD (HHFFFF)
+Size 90: 45,90 GOOD (HHFFFF)
+Size 91: 46,91 GOOD (HHFFFF)
+Size 92: 46,92 GOOD (HHFFFF)
+Size 93: 47,93 GOOD (HHFFFF)
+Size 94: 47,94 GOOD (HHFFFF)
+Size 95: 48,95 GOOD (HHFFFF)
+Size 96: 48,96 GOOD (HHFFFF)
+Size 97: 49,97 GOOD (HHFFFF)
+Size 98: 49,98 GOOD (HHFFFF)
+Size 99: 50,99 GOOD (HHFFFF)
+Size 100: 50,100 GOOD (HHFFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt
new file mode 100644
index 0000000000..d5261d8db3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt
@@ -0,0 +1,16 @@
+The narrowest allowed console window, in pixels, on a conventional (~96dpi)
+monitor:
+
+(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 1 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12
+
+(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 16 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12
+
+ sz1:px sz1:col sz16:px sz16:col
+Vista: 124 104 137 10
+Windows 7: 132 112 147 11
+Windows 8: 140 120 147 11
+Windows 8.1: 140 120 147 11
+Windows 10 OLD: 136 116 147 11
+Windows 10 NEW: 136 103 136 10
+
+I used build 14342 to test Windows 10.
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt
new file mode 100644
index 0000000000..15a825cb51
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt
@@ -0,0 +1,4 @@
+As before, avoid odd sizes in favor of even sizes.
+
+It's curious that the Japanese font is handled so poorly, especially with
+Windows 8 and later.
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt
new file mode 100644
index 0000000000..fef397a1e3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt
@@ -0,0 +1,144 @@
+Issues:
+
+ - Starting with the 14342 build, changing the font using
+ SetCurrentConsoleFontEx does not affect the window size. e.g. The content
+ itself will resize/redraw, but the window neither shrinks nor expands.
+ Presumably this is an oversight? It's almost a convenience; if a program
+ is going to resize the window anyway, then it's nice that the window size
+ contraints don't get in the way. Ordinarily, changing the font doesn't just
+ change the window size in pixels--it can also change the size as measured in
+ rows and columns.
+
+ - (Aside: in the 14342 build, there is also a bug with wmic.exe. Open a console
+ with more than 300 lines of screen buffer, then fill those lines with, e.g.,
+ dir /s. Then run wmic.exe. You won't be able to see the wmic.exe prompt.
+ If you query the screen buffer info somehow, you'll notice that the srWindow
+ is not contained within the dwSize. This breaks winpty's scraping, because
+ it's invalid.)
+
+ - In build 14316, with the Japanese locale, with the 437 code page, attempting
+ to set the Consolas font instead sets the Terminal (raster) font. It seems
+ to pick an appropriate vertical size.
+
+ - It seems necessary to specify "-family 0x36" for maximum reliability.
+ Setting the family to 0 almost always works, and specifying just -tt rarely
+ works.
+
+Win7
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt unreliable
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+
+Win10 Build 10586
+ New console
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+
+Win10 Build 14316
+ Old console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selected very small Consolas font
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ New console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt works
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 selects gothic instead
+ SetFont.exe -face Consolas -h 16 -tt selects gothic instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36(*) selects Terminal font instead
+
+Win10 Build 14342
+ Old Console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ New console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt works
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 selects gothic instead
+ SetFont.exe -face Consolas -h 16 -tt selects gothic instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -tt works
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+
+(*) I was trying to figure out whether the inconsistency was at when I stumbled
+onto this completely unexpected bug. Here's more detail:
+
+ F:\>SetFont.exe -face Consolas -h 16 -family 0x36 -weight normal -w 8
+ Setting to: nFont=0 dwFontSize=(8,16) FontFamily=0x36 FontWeight=400 FaceName="Consolas"
+ SetCurrentConsoleFontEx returned 1
+
+ F:\>GetFont.exe
+ largestConsoleWindowSize=(96,50)
+ maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ 00-00: 12x16
+ GetNumberOfConsoleFonts returned 0
+ CP=437 OutputCP=437
+
+ F:\>SetFont.exe -face "Lucida Console" -h 16 -family 0x36 -weight normal
+ Setting to: nFont=0 dwFontSize=(0,16) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console"
+ SetCurrentConsoleFontEx returned 1
+
+ F:\>GetFont.exe
+ largestConsoleWindowSize=(96,50)
+ maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ 00-00: 12x16
+ GetNumberOfConsoleFonts returned 0
+ CP=437 OutputCP=437
+
+ F:\>SetFont.exe -face "Lucida Console" -h 12 -family 0x36 -weight normal
+ Setting to: nFont=0 dwFontSize=(0,12) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console"
+ SetCurrentConsoleFontEx returned 1
+
+ F:\>GetFont.exe
+ largestConsoleWindowSize=(230,66)
+ maxWnd=0: nFont=0 dwFontSize=(5,12) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ maxWnd=1: nFont=0 dwFontSize=(116,36) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ 00-00: 5x12
+ GetNumberOfConsoleFonts returned 0
+ CP=437 OutputCP=437
+
+Even attempting to set to a Lucida Console / Consolas font from the Console
+properties dialog fails.
diff --git a/src/libs/3rdparty/winpty/misc/FontSurvey.cc b/src/libs/3rdparty/winpty/misc/FontSurvey.cc
new file mode 100644
index 0000000000..254bcc81a6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/FontSurvey.cc
@@ -0,0 +1,100 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vector>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
+
+std::vector<bool> condense(const std::vector<CHAR_INFO> &buf) {
+ std::vector<bool> ret;
+ size_t i = 0;
+ while (i < buf.size()) {
+ if (buf[i].Char.UnicodeChar == L' ' &&
+ ((buf[i].Attributes & 0x300) == 0)) {
+ // end of line
+ break;
+ } else if (i + 1 < buf.size() &&
+ ((buf[i].Attributes & 0x300) == 0x100) &&
+ ((buf[i + 1].Attributes & 0x300) == 0x200) &&
+ buf[i].Char.UnicodeChar != L' ' &&
+ buf[i].Char.UnicodeChar == buf[i + 1].Char.UnicodeChar) {
+ // double-width
+ ret.push_back(true);
+ i += 2;
+ } else if ((buf[i].Attributes & 0x300) == 0) {
+ // single-width
+ ret.push_back(false);
+ i++;
+ } else {
+ ASSERT(false && "unexpected output");
+ }
+ }
+ return ret;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ printf("Usage: %s \"arguments for SetFont.exe\"\n", argv[0]);
+ return 1;
+ }
+
+ const char *setFontArgs = argv[1];
+
+ const wchar_t testLine[] = { 0xA2, 0xA3, 0x2014, 0x3044, 0x30FC, 0x4000, 0 };
+ const HANDLE conout = openConout();
+
+ char setFontCmd[1024];
+ for (int h = 1; h <= 100; ++h) {
+ sprintf(setFontCmd, ".\\SetFont.exe %s -h %d && cls", setFontArgs, h);
+ system(setFontCmd);
+
+ CONSOLE_FONT_INFOEX infoex = {};
+ infoex.cbSize = sizeof(infoex);
+ BOOL success = GetCurrentConsoleFontEx(conout, FALSE, &infoex);
+ ASSERT(success && "GetCurrentConsoleFontEx failed");
+
+ DWORD actual = 0;
+ success = WriteConsoleW(conout, testLine, wcslen(testLine), &actual, nullptr);
+ ASSERT(success && actual == wcslen(testLine));
+
+ std::vector<CHAR_INFO> readBuf(14);
+ const SMALL_RECT readRegion = {0, 0, static_cast<short>(readBuf.size() - 1), 0};
+ SMALL_RECT readRegion2 = readRegion;
+ success = ReadConsoleOutputW(
+ conout, readBuf.data(),
+ {static_cast<short>(readBuf.size()), 1},
+ {0, 0},
+ &readRegion2);
+ ASSERT(success && !memcmp(&readRegion, &readRegion2, sizeof(readRegion)));
+
+ const auto widths = condense(readBuf);
+ std::string widthsStr;
+ for (bool width : widths) {
+ widthsStr.append(width ? "F" : "H");
+ }
+ char size[16];
+ sprintf(size, "%d,%d", infoex.dwFontSize.X, infoex.dwFontSize.Y);
+ const char *status = "";
+ if (widthsStr == "HHFFFF") {
+ status = "GOOD";
+ } else if (widthsStr == "HHHFFF") {
+ status = "OK";
+ } else {
+ status = "BAD";
+ }
+ trace("Size %3d: %-7s %-4s (%s)", h, size, status, widthsStr.c_str());
+ }
+ sprintf(setFontCmd, ".\\SetFont.exe %s -h 14", setFontArgs);
+ system(setFontCmd);
+}
diff --git a/src/libs/3rdparty/winpty/misc/FormatChar.h b/src/libs/3rdparty/winpty/misc/FormatChar.h
new file mode 100644
index 0000000000..aade488f9e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/FormatChar.h
@@ -0,0 +1,21 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+static inline void formatChar(char *str, char ch)
+{
+ // Print some common control codes.
+ switch (ch) {
+ case '\r': strcpy(str, "CR "); break;
+ case '\n': strcpy(str, "LF "); break;
+ case ' ': strcpy(str, "SP "); break;
+ case 27: strcpy(str, "^[ "); break;
+ case 3: strcpy(str, "^C "); break;
+ default:
+ if (isgraph(ch))
+ sprintf(str, "%c ", ch);
+ else
+ sprintf(str, "%02x ", ch);
+ break;
+ }
+}
diff --git a/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc b/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc
new file mode 100644
index 0000000000..2c0b0086a1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc
@@ -0,0 +1,62 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+int main(int argc, char *argv[0]) {
+
+ if (argc != 2) {
+ printf("Usage: %s (mark|selectall|read)\n", argv[0]);
+ return 1;
+ }
+
+ enum class Test { Mark, SelectAll, Read } test;
+ if (!strcmp(argv[1], "mark")) {
+ test = Test::Mark;
+ } else if (!strcmp(argv[1], "selectall")) {
+ test = Test::SelectAll;
+ } else if (!strcmp(argv[1], "read")) {
+ test = Test::Read;
+ } else {
+ printf("Invalid test: %s\n", argv[1]);
+ return 1;
+ }
+
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ TimeMeasurement tm;
+ HWND hwnd = GetConsoleWindow();
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(100, 3000);
+ system("cls");
+ setWindowPos(0, 2975, 100, 25);
+ setCursorPos(0, 2999);
+
+ ShowWindow(hwnd, SW_HIDE);
+
+ for (int i = 0; i < 1000; ++i) {
+ // CONSOLE_SCREEN_BUFFER_INFO info = {};
+ // GetConsoleScreenBufferInfo(conout, &info);
+
+ if (test == Test::Mark) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ } else if (test == Test::SelectAll) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ } else if (test == Test::Read) {
+ static CHAR_INFO buffer[100 * 3000];
+ const SMALL_RECT readRegion = {0, 0, 99, 2999};
+ SMALL_RECT tmp = readRegion;
+ BOOL ret = ReadConsoleOutput(conout, buffer, {100, 3000}, {0, 0}, &tmp);
+ ASSERT(ret && !memcmp(&tmp, &readRegion, sizeof(tmp)));
+ }
+ }
+
+ ShowWindow(hwnd, SW_SHOW);
+
+ printf("elapsed: %f\n", tm.elapsed());
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/GetCh.cc b/src/libs/3rdparty/winpty/misc/GetCh.cc
new file mode 100644
index 0000000000..cd6ed1943a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/GetCh.cc
@@ -0,0 +1,20 @@
+#include <conio.h>
+#include <ctype.h>
+#include <stdio.h>
+
+int main() {
+ printf("\nPress any keys -- Ctrl-D exits\n\n");
+
+ while (true) {
+ const int ch = getch();
+ printf("0x%x", ch);
+ if (isgraph(ch)) {
+ printf(" '%c'", ch);
+ }
+ printf("\n");
+ if (ch == 0x4) { // Ctrl-D
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/GetConsolePos.cc b/src/libs/3rdparty/winpty/misc/GetConsolePos.cc
new file mode 100644
index 0000000000..1f3cc5316f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/GetConsolePos.cc
@@ -0,0 +1,41 @@
+#include <windows.h>
+
+#include <stdio.h>
+
+#include "TestUtil.cc"
+
+int main() {
+ const HANDLE conout = openConout();
+
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+
+ trace("cursor=%d,%d", info.dwCursorPosition.X, info.dwCursorPosition.Y);
+ printf("cursor=%d,%d\n", info.dwCursorPosition.X, info.dwCursorPosition.Y);
+
+ trace("srWindow={L=%d,T=%d,R=%d,B=%d}", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom);
+ printf("srWindow={L=%d,T=%d,R=%d,B=%d}\n", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom);
+
+ trace("dwSize=%d,%d", info.dwSize.X, info.dwSize.Y);
+ printf("dwSize=%d,%d\n", info.dwSize.X, info.dwSize.Y);
+
+ const HWND hwnd = GetConsoleWindow();
+ if (hwnd != NULL) {
+ RECT r = {};
+ if (GetWindowRect(hwnd, &r)) {
+ const int w = r.right - r.left;
+ const int h = r.bottom - r.top;
+ trace("hwnd: pos=(%d,%d) size=(%d,%d)", r.left, r.top, w, h);
+ printf("hwnd: pos=(%d,%d) size=(%d,%d)\n", r.left, r.top, w, h);
+ } else {
+ trace("GetWindowRect failed");
+ printf("GetWindowRect failed\n");
+ }
+ } else {
+ trace("GetConsoleWindow returned NULL");
+ printf("GetConsoleWindow returned NULL\n");
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/GetFont.cc b/src/libs/3rdparty/winpty/misc/GetFont.cc
new file mode 100644
index 0000000000..38625317ab
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/GetFont.cc
@@ -0,0 +1,261 @@
+#include <windows.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+
+#include "../src/shared/OsModule.h"
+#include "../src/shared/StringUtil.h"
+
+#include "TestUtil.cc"
+#include "../src/shared/StringUtil.cc"
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+// Some of these types and functions are missing from the MinGW headers.
+// Others are undocumented.
+
+struct AGENT_CONSOLE_FONT_INFO {
+ DWORD nFont;
+ COORD dwFontSize;
+};
+
+struct AGENT_CONSOLE_FONT_INFOEX {
+ ULONG cbSize;
+ DWORD nFont;
+ COORD dwFontSize;
+ UINT FontFamily;
+ UINT FontWeight;
+ WCHAR FaceName[LF_FACESIZE];
+};
+
+// undocumented XP API
+typedef BOOL WINAPI SetConsoleFont_t(
+ HANDLE hOutput,
+ DWORD dwFontIndex);
+
+// undocumented XP API
+typedef DWORD WINAPI GetNumberOfConsoleFonts_t();
+
+// XP and up
+typedef BOOL WINAPI GetCurrentConsoleFont_t(
+ HANDLE hOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont);
+
+// XP and up
+typedef COORD WINAPI GetConsoleFontSize_t(
+ HANDLE hConsoleOutput,
+ DWORD nFont);
+
+// Vista and up
+typedef BOOL WINAPI GetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+// Vista and up
+typedef BOOL WINAPI SetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+#define GET_MODULE_PROC(mod, funcName) \
+ m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \
+
+#define DEFINE_ACCESSOR(funcName) \
+ funcName##_t &funcName() const { \
+ ASSERT(valid()); \
+ return *m_##funcName; \
+ }
+
+class XPFontAPI {
+public:
+ XPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetConsoleFontSize);
+ }
+
+ bool valid() const {
+ return m_GetCurrentConsoleFont != NULL &&
+ m_GetConsoleFontSize != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFont)
+ DEFINE_ACCESSOR(GetConsoleFontSize)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFont_t *m_GetCurrentConsoleFont;
+ GetConsoleFontSize_t *m_GetConsoleFontSize;
+};
+
+class UndocumentedXPFontAPI : public XPFontAPI {
+public:
+ UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, SetConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_SetConsoleFont != NULL &&
+ m_GetNumberOfConsoleFonts != NULL;
+ }
+
+ DEFINE_ACCESSOR(SetConsoleFont)
+ DEFINE_ACCESSOR(GetNumberOfConsoleFonts)
+
+private:
+ OsModule m_kernel32;
+ SetConsoleFont_t *m_SetConsoleFont;
+ GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts;
+};
+
+class VistaFontAPI : public XPFontAPI {
+public:
+ VistaFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx);
+ GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_GetCurrentConsoleFontEx != NULL &&
+ m_SetCurrentConsoleFontEx != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFontEx)
+ DEFINE_ACCESSOR(SetCurrentConsoleFontEx)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx;
+ SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx;
+};
+
+static std::vector<std::pair<DWORD, COORD> > readFontTable(
+ XPFontAPI &api, HANDLE conout, DWORD maxCount) {
+ std::vector<std::pair<DWORD, COORD> > ret;
+ for (DWORD i = 0; i < maxCount; ++i) {
+ COORD size = api.GetConsoleFontSize()(conout, i);
+ if (size.X == 0 && size.Y == 0) {
+ break;
+ }
+ ret.push_back(std::make_pair(i, size));
+ }
+ return ret;
+}
+
+static void dumpFontTable(HANDLE conout) {
+ const int kMaxCount = 1000;
+ XPFontAPI api;
+ if (!api.valid()) {
+ printf("dumpFontTable: cannot dump font table -- missing APIs\n");
+ return;
+ }
+ std::vector<std::pair<DWORD, COORD> > table =
+ readFontTable(api, conout, kMaxCount);
+ std::string line;
+ char tmp[128];
+ size_t first = 0;
+ while (first < table.size()) {
+ size_t last = std::min(table.size() - 1, first + 10 - 1);
+ winpty_snprintf(tmp, "%02u-%02u:",
+ static_cast<unsigned>(first), static_cast<unsigned>(last));
+ line = tmp;
+ for (size_t i = first; i <= last; ++i) {
+ if (i % 10 == 5) {
+ line += " - ";
+ }
+ winpty_snprintf(tmp, " %2dx%-2d",
+ table[i].second.X, table[i].second.Y);
+ line += tmp;
+ }
+ printf("%s\n", line.c_str());
+ first = last + 1;
+ }
+ if (table.size() == kMaxCount) {
+ printf("... stopped reading at %d fonts ...\n", kMaxCount);
+ }
+}
+
+static std::string stringToCodePoints(const std::wstring &str) {
+ std::string ret = "(";
+ for (size_t i = 0; i < str.size(); ++i) {
+ char tmp[32];
+ winpty_snprintf(tmp, "%X", str[i]);
+ if (ret.size() > 1) {
+ ret.push_back(' ');
+ }
+ ret += tmp;
+ }
+ ret.push_back(')');
+ return ret;
+}
+
+static void dumpFontInfoEx(
+ const AGENT_CONSOLE_FONT_INFOEX &infoex) {
+ std::wstring faceName(infoex.FaceName,
+ winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName)));
+ cprintf(L"nFont=%u dwFontSize=(%d,%d) "
+ "FontFamily=0x%x FontWeight=%u FaceName=%ls %hs\n",
+ static_cast<unsigned>(infoex.nFont),
+ infoex.dwFontSize.X, infoex.dwFontSize.Y,
+ infoex.FontFamily, infoex.FontWeight, faceName.c_str(),
+ stringToCodePoints(faceName).c_str());
+}
+
+static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, BOOL maxWindow) {
+ AGENT_CONSOLE_FONT_INFOEX infoex = {0};
+ infoex.cbSize = sizeof(infoex);
+ if (!api.GetCurrentConsoleFontEx()(conout, maxWindow, &infoex)) {
+ printf("GetCurrentConsoleFontEx call failed\n");
+ return;
+ }
+ dumpFontInfoEx(infoex);
+}
+
+static void dumpXPFont(XPFontAPI &api, HANDLE conout, BOOL maxWindow) {
+ AGENT_CONSOLE_FONT_INFO info = {0};
+ if (!api.GetCurrentConsoleFont()(conout, maxWindow, &info)) {
+ printf("GetCurrentConsoleFont call failed\n");
+ return;
+ }
+ printf("nFont=%u dwFontSize=(%d,%d)\n",
+ static_cast<unsigned>(info.nFont),
+ info.dwFontSize.X, info.dwFontSize.Y);
+}
+
+static void dumpFontAndTable(HANDLE conout) {
+ VistaFontAPI vista;
+ if (vista.valid()) {
+ printf("maxWnd=0: "); dumpVistaFont(vista, conout, FALSE);
+ printf("maxWnd=1: "); dumpVistaFont(vista, conout, TRUE);
+ dumpFontTable(conout);
+ return;
+ }
+ UndocumentedXPFontAPI xp;
+ if (xp.valid()) {
+ printf("maxWnd=0: "); dumpXPFont(xp, conout, FALSE);
+ printf("maxWnd=1: "); dumpXPFont(xp, conout, TRUE);
+ dumpFontTable(conout);
+ return;
+ }
+ printf("setSmallFont: neither Vista nor XP APIs detected -- giving up\n");
+ dumpFontTable(conout);
+}
+
+int main() {
+ const HANDLE conout = openConout();
+ const COORD largest = GetLargestConsoleWindowSize(conout);
+ printf("largestConsoleWindowSize=(%d,%d)\n", largest.X, largest.Y);
+ dumpFontAndTable(conout);
+ UndocumentedXPFontAPI xp;
+ if (xp.valid()) {
+ printf("GetNumberOfConsoleFonts returned %u\n", xp.GetNumberOfConsoleFonts()());
+ } else {
+ printf("The GetNumberOfConsoleFonts API was missing\n");
+ }
+ printf("CP=%u OutputCP=%u\n", GetConsoleCP(), GetConsoleOutputCP());
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1 b/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1
new file mode 100644
index 0000000000..0c488597bd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1
@@ -0,0 +1,51 @@
+#
+# Usage: powershell <path>\IdentifyConsoleWindow.ps1
+#
+# This script determines whether the process has a console attached, whether
+# that console has a non-NULL window (e.g. HWND), and whether the window is on
+# the current window station.
+#
+
+$signature = @'
+[DllImport("kernel32.dll", SetLastError=true)]
+public static extern IntPtr GetConsoleWindow();
+
+[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
+public static extern bool SetConsoleTitle(String title);
+
+[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
+public static extern int GetWindowText(IntPtr hWnd,
+ System.Text.StringBuilder lpString,
+ int nMaxCount);
+'@
+
+$WinAPI = Add-Type -MemberDefinition $signature `
+ -Name WinAPI -Namespace IdentifyConsoleWindow -PassThru
+
+if (!$WinAPI::SetConsoleTitle("ConsoleWindowScript")) {
+ echo "error: could not change console title -- is a console attached?"
+ exit 1
+} else {
+ echo "note: successfully set console title to ""ConsoleWindowScript""."
+}
+
+$hwnd = $WinAPI::GetConsoleWindow()
+if ($hwnd -eq 0) {
+ echo "note: GetConsoleWindow returned NULL."
+} else {
+ echo "note: GetConsoleWindow returned 0x$($hwnd.ToString("X"))."
+ $sb = New-Object System.Text.StringBuilder -ArgumentList 4096
+ if ($WinAPI::GetWindowText($hwnd, $sb, $sb.Capacity)) {
+ $title = $sb.ToString()
+ echo "note: GetWindowText returned ""${title}""."
+ if ($title -eq "ConsoleWindowScript") {
+ echo "success!"
+ } else {
+ echo "error: expected to see ""ConsoleWindowScript""."
+ echo " (Perhaps the console window is on a different window station?)"
+ }
+ } else {
+ echo "error: GetWindowText could not read the window title."
+ echo " (Perhaps the console window is on a different window station?)"
+ }
+}
diff --git a/src/libs/3rdparty/winpty/misc/IsNewConsole.cc b/src/libs/3rdparty/winpty/misc/IsNewConsole.cc
new file mode 100644
index 0000000000..2b554c72c9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/IsNewConsole.cc
@@ -0,0 +1,87 @@
+// Determines whether this is a new console by testing whether MARK moves the
+// cursor.
+//
+// WARNING: This test program may behave erratically if run under winpty.
+//
+
+#include <windows.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+static COORD getWindowPos(HANDLE conout) {
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+ return { info.srWindow.Left, info.srWindow.Top };
+}
+
+static COORD getWindowSize(HANDLE conout) {
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+ return {
+ static_cast<short>(info.srWindow.Right - info.srWindow.Left + 1),
+ static_cast<short>(info.srWindow.Bottom - info.srWindow.Top + 1)
+ };
+}
+
+static COORD getCursorPos(HANDLE conout) {
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+ return info.dwCursorPosition;
+}
+
+static void setCursorPos(HANDLE conout, COORD pos) {
+ BOOL ret = SetConsoleCursorPosition(conout, pos);
+ ASSERT(ret && "SetConsoleCursorPosition failed");
+}
+
+int main() {
+ const HANDLE conout = openConout();
+ const HWND hwnd = GetConsoleWindow();
+ ASSERT(hwnd != NULL && "GetConsoleWindow() returned NULL");
+
+ // With the legacy console, the Mark command moves the the cursor to the
+ // top-left cell of the visible console window. Determine whether this
+ // is the new console by seeing if the cursor moves.
+
+ const auto windowSize = getWindowSize(conout);
+ if (windowSize.X <= 1) {
+ printf("Error: console window must be at least 2 columns wide\n");
+ trace("Error: console window must be at least 2 columns wide");
+ return 1;
+ }
+
+ bool cursorMoved = false;
+ const auto initialPos = getCursorPos(conout);
+
+ const auto windowPos = getWindowPos(conout);
+ setCursorPos(conout, { static_cast<short>(windowPos.X + 1), windowPos.Y });
+
+ {
+ const auto posA = getCursorPos(conout);
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+ const auto posB = getCursorPos(conout);
+ cursorMoved = memcmp(&posA, &posB, sizeof(posA)) != 0;
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001); // Send ESCAPE
+ }
+
+ setCursorPos(conout, initialPos);
+
+ if (cursorMoved) {
+ printf("Legacy console (i.e. MARK moved cursor)\n");
+ trace("Legacy console (i.e. MARK moved cursor)");
+ } else {
+ printf("Windows 10 new console (i.e MARK did not move cursor)\n");
+ trace("Windows 10 new console (i.e MARK did not move cursor)");
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt b/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt
new file mode 100644
index 0000000000..18460c6861
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt
@@ -0,0 +1,90 @@
+Introduction
+============
+
+The only specification I could find describing mouse input escape sequences
+was the /usr/share/doc/xterm/ctlseqs.txt.gz file installed on my Ubuntu
+machine.
+
+Here are the relevant escape sequences:
+
+ * [ON] CSI '?' M 'h' Enable mouse input mode M
+ * [OFF] CSI '?' M 'l' Disable mouse input mode M
+ * [EVT] CSI 'M' F X Y Mouse event (default or mode 1005)
+ * [EVT6] CSI '<' F ';' X ';' Y 'M' Mouse event with mode 1006
+ * [EVT6] CSI '<' F ';' X ';' Y 'm' Mouse event with mode 1006 (up)
+ * [EVT15] CSI F ';' X ';' Y 'M' Mouse event with mode 1015
+
+The first batch of modes affect what events are reported:
+
+ * 9: Presses only (not as well-supported as the other modes)
+ * 1000: Presses and releases
+ * 1002: Presses, releases, and moves-while-pressed
+ * 1003: Presses, releases, and all moves
+
+The next batch of modes affect the encoding of the mouse events:
+
+ * 1005: The X and Y coordinates are UTF-8 codepoints rather than bytes.
+ * 1006: Use the EVT6 sequences instead of EVT
+ * 1015: Use the EVT15 sequence instead of EVT (aka URVXT-mode)
+
+Support for modes in existing terminals
+=======================================
+
+ | 9 1000 1002 1003 | 1004 | overflow | defhi | 1005 1006 1015
+---------------------------------+---------------------+------+--------------+-------+----------------
+Eclipse TM Terminal (Neon) | _ _ _ _ | _ | n/a | n/a | _ _ _
+gnome-terminal 3.6.2 | X X X X | _ | suppressed*b | 0x07 | _ X X
+iTerm2 2.1.4 | _ X X X | OI | wrap*z | n/a | X X X
+jediterm/IntelliJ | _ X X X | _ | ch='?' | 0xff | X X X
+Konsole 2.13.2 | _ X X *a | _ | suppressed | 0xff | X X X
+mintty 2.2.2 | X X X X | OI | ch='\0' | 0xff | X X X
+putty 0.66 | _ X X _ | _ | suppressed | 0xff | _ X X
+rxvt 2.7.10 | X X _ _ | _ | wrap*z | n/a | _ _ _
+screen(under xterm) | X X X X | _ | suppressed | 0xff | _ _ _
+urxvt 9.21 | X X X X | _ | wrap*z | n/a | X _ X
+xfce4-terminal 0.6.3 (GTK2 VTE) | X X X X | _ | wrap | n/a | _ _ _
+xterm | X X X X | OI | ch='\0' | 0xff | X X X
+
+*a: Mode 1003 is handled the same way as 1002.
+*b: The coordinate wraps from 0xff to 0x00, then maxs out at 0x07. I'm
+ guessing this behavior is a bug? I'm using the Xubuntu 14.04
+ gnome-terminal.
+*z: These terminals have a bug where column 224 (and row 224, presumably)
+ yields a truncated escape sequence. 224 + 32 is 0, so it would normally
+ yield `CSI 'M' F '\0' Y`, but the '\0' is interpreted as a NUL-terminator.
+
+Problem 1: How do these flags work?
+===================================
+
+Terminals accept the OFF sequence with any of the input modes. This makes
+little sense--there are two multi-value settings, not seven independent flags!
+
+All the terminals handle Granularity the same way. ON-Granularity sets
+Granularity to the specified value, and OFF-Granularity sets Granularity to
+OFF.
+
+Terminals vary in how they handle the Encoding modes. For example:
+
+ * xterm. ON-Encoding sets Encoding. OFF-Encoding with a non-active Encoding
+ has no effect. OFF-Encoding otherwise resets Encoding to Default.
+
+ * mintty (tested 2.2.2), iTerm2 2.1.4, and jediterm. ON-Encoding sets
+ Encoding. OFF-Encoding resets Encoding to Default.
+
+ * Konsole (tested 2.13.2) seems to configure each encoding method
+ independently. The effective Encoding is the first enabled encoding in this
+ list:
+ - Mode 1006
+ - Mode 1015
+ - Mode 1005
+ - Default
+
+ * gnome-terminal (tested 3.6.2) also configures each encoding method
+ independently. The effective Encoding is the first enabled encoding in
+ this list:
+ - Mode 1006
+ - Mode 1015
+ - Default
+ Mode 1005 is not supported.
+
+ * xfce4 terminal 0.6.3 (GTK2 VTE) always outputs the default encoding method.
diff --git a/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc b/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc
new file mode 100644
index 0000000000..7d9684fe94
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc
@@ -0,0 +1,34 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc != 3 && argc != 5) {
+ printf("Usage: %s x y\n", argv[0]);
+ printf("Usage: %s x y width height\n", argv[0]);
+ return 1;
+ }
+
+ HWND hwnd = GetConsoleWindow();
+
+ const int x = atoi(argv[1]);
+ const int y = atoi(argv[2]);
+
+ int w = 0, h = 0;
+ if (argc == 3) {
+ RECT r = {};
+ BOOL ret = GetWindowRect(hwnd, &r);
+ ASSERT(ret && "GetWindowRect failed on console window");
+ w = r.right - r.left;
+ h = r.bottom - r.top;
+ } else {
+ w = atoi(argv[3]);
+ h = atoi(argv[4]);
+ }
+
+ BOOL ret = MoveWindow(hwnd, x, y, w, h, TRUE);
+ trace("MoveWindow: ret=%d", ret);
+ printf("MoveWindow: ret=%d\n", ret);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Notes.txt b/src/libs/3rdparty/winpty/misc/Notes.txt
new file mode 100644
index 0000000000..410e184198
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Notes.txt
@@ -0,0 +1,219 @@
+Test programs
+-------------
+
+Cygwin
+ emacs
+ vim
+ mc (Midnight Commander)
+ lynx
+ links
+ less
+ more
+ wget
+
+Capturing the console output
+----------------------------
+
+Initial idea:
+
+In the agent, keep track of the remote terminal state for N lines of
+(window+history). Also keep track of the terminal size. Regularly poll for
+changes to the console screen buffer, then use some number of edits to bring
+the remote terminal into sync with the console.
+
+This idea seems to have trouble when a Unix terminal is resized. When the
+server receives a resize notification, it can have a hard time figuring out
+what the terminal did. Race conditions might also be a problem.
+
+The behavior of the terminal can be tricky:
+
+ - When the window is expanded by one line, does the terminal add a blank line
+ to the bottom or move a line from the history into the top?
+
+ - When the window is shrunk by one line, does the terminal delete the topmost
+ or the bottommost line? Can it delete the line with the cursor?
+
+Some popular behaviors for expanding:
+ - [all] If there are no history lines, then add a line at the bottom.
+ - [konsole] Always add a line at the bottom.
+ - [putty,xterm,rxvt] Pull in a history line from the top.
+ - [g-t] I can't tell. It seems to add a blank line, until the program writes
+ to stdout or until I click the scroll bar, then the output "snaps" back down,
+ pulling lines out of the history. I thought I saw different behavior
+ between Ubuntu 10.10 and 11.10, so maybe GNOME 3 changed something. Avoid
+ using "bash" to test this behavior because "bash" apparently always writes
+ the prompt after terminal resize.
+
+Some popular behaviors for shrinking:
+ - [konsole,putty,xterm,rxvt] If the line at the bottom is blank, then delete
+ it. Otherwise, move the topmost line into history.
+ - [g-t] If the line at the bottom has not been touched, then delete it.
+ Otherwise, move the topmost line into history.
+
+(TODO: I need to test my theories about the terminal behavior better still.
+It's interesting to see how g-t handles clear differently than every other
+terminal.)
+
+There is an ANSI escape sequence (DSR) that sends the current cursor location
+to the terminal's input. One idea I had was to use this code to figure out how
+the terminal had handled a resize. I currently think this idea won't work due
+to race conditions.
+
+Newer idea:
+
+Keep track of the last N lines that have been sent to the remote terminal.
+Poll for changes to console output. When the output changes, send just the
+changed content to the terminal. In particular:
+ - Don't send a cursor position (CUP) code. Instead, if the line that's 3
+ steps up from the latest line changes, send a relative cursor up (CUU)
+ code. It's OK to send an absolute column number code (CHA).
+ - At least in general, don't try to send complete screenshots of the current
+ console window.
+
+The idea is that sending just the changes should have good behavior for streams
+of output, even when those streams modify the output (e.g. an archiver, or
+maybe a downloader/packager/wget). I need to think about whether this works
+for full-screen programs (e.g. emacs, less, lynx, the above list of programs).
+
+I noticed that console programs don't typically modify the window or buffer
+coordinates. edit.com is an exception.
+
+I tested the pager in native Python (more?), and I verified that ENTER and SPACE
+both paid no attention to the location of the console window within the screen
+buffer. This makes sense -- why would they care? The Cygwin less, on the other
+hand, does care. If I scroll the window up, then Cygwin less will write to a
+position within the window. I didn't really expect this behavior, but it
+doesn't seem to be a problem.
+
+Setting up a TestNetServer service
+----------------------------------
+
+First run the deploy.sh script to copy files into deploy. Make sure
+TestNetServer.exe will run in a bare environment (no MinGW or Qt in the path).
+
+Install the Windows Server 2003 Resource Kit. It will have two programs in it,
+instsrv and srvany.
+
+Run:
+
+ InstSrv TestNetServer <path-to-srvany>\srvany.exe
+
+This creates a service named "TestNetServer" that uses the Microsoft service
+wrapper. To configure the new service to run TestNetServer, set a registry
+value:
+
+ [HKLM\SYSTEM\CurrentControlSet\Services\TestNetServer\Parameters]
+ Application=<full-path>\TestNetServer.exe
+
+Also see http://www.iopus.com/guides/srvany.htm.
+
+To remove the service, run:
+
+ InstSrv TestNetServer REMOVE
+
+TODO
+----
+
+Agent: When resizing the console, consider whether to add lines to the top
+or bottom. I remember thinking the current behavior was wrong for some
+application, but I forgot which one.
+
+Make the font as small as possible. The console window dimensions are limited by
+the screen size, so making the font small reduces an unnecessary limitation on the
+PseudoConsole size. There's a documented Vista/Win7 API for this
+(SetCurrentConsoleFontEx), and apparently WinXP has an undocumented API
+(SetConsoleFont):
+ http://blogs.microsoft.co.il/blogs/pavely/archive/2009/07/23/changing-console-fonts.aspx
+
+Make the agent work with DOS programs like edit and qbasic.
+ - Detect that the terminal program has resized the window/buffer and enter a
+ simple just-scrape-and-dont-resize mode. Track the client window size and
+ send the intersection of the console and the agent's client.
+ - I also need to generate keyboard scan codes.
+ - Solve the NTVDM.EXE console shutdown problem, probably by ignoring NTVDM.EXE
+ when it appears on the GetConsoleProcessList list.
+
+Rename the agent? Is the term "proxy" more accurate?
+
+Optimize the polling. e.g. Use a longer poll interval when the console is idle.
+Do a minimal poll that checks whether the sync marker or window has moved.
+
+Increase the console buffer size to ~9000 lines. Beware making it so big that
+reading the sync column exhausts the 32KB conhost<->agent heap.
+
+Reduce the memory overhead of the agent. The agent's m_bufferData array can
+be small (a few hundred lines?) relative to the console buffer size.
+
+Try to handle console background color better.
+ Unix terminal emulators have a user-configurable foreground and background
+color, and for best results, the agent really needs to avoid changing the colors,
+especially the background color. It's undesirable/ugly to SSH into a machine
+and see the command prompt change the colors. It's especially ugly that the
+terminal retains its original colors and only drawn cells get the new colors.
+(e.g. Resizing the window to the right uses the local terminal colors rather
+than the remote colors.) It's especially ugly in gnome-terminal, which draws
+user-configurable black as black, but VT100 black as dark-gray.
+ If there were a way to query the terminal emulator's colors, then I could
+match the console's colors to the terminal and everything would just work. As
+far as I know, that's not possible.
+ I thought of a kludge that might work. Instead of translating console white
+and black to VT/100 white and black, I would translate them to "reset" and
+"invert". I'd translate other colors normally. This approach should produce
+ideal results for command-line work and tolerable results for full-screen
+programs without configuration. Configuring the agent for black-on-white or
+white-on-black would produce ideal results in all situations.
+ This kludge only really applies to the SSH application. For a Win32 Konsole
+application, it should be easy to get the colors right all the time.
+
+Try using the screen reader API:
+ - To eliminate polling.
+ - To detect when a line wraps. When a line wraps, it'd be nice not to send a
+ CRLF to the terminal emulator so copy-and-paste works better.
+ - To detect hard tabs with Cygwin.
+
+Implement VT100/ANSI escape sequence recognition for input. Decide where this
+functionality belongs. PseudoConsole.dll? Disambiguating ESC from an escape
+sequence might be tricky. For the SSH server, I was thinking that when a small
+SSH payload ended with an ESC character, I could assume the character was really
+an ESC keypress, on the assumption that if it were an escape sequence, the
+payload would probably contain the whole sequence. I'm not sure this works,
+especially if there's a lot of other traffic multiplexed on the SSH socket.
+
+Support Unicode.
+ - Some DOS programs draw using line/box characters. Can these characters be
+ translated to the Unicode equivalents?
+
+Create automated tests.
+
+Experiment with the Terminator emulator, an emulator that doesn't wrap lines.
+How many columns does it report having? What column does it report the cursor
+in as it's writing past the right end of the window? Will Terminator be a
+problem if I implement line wrapping detection in the agent?
+
+BUG: After the unix-adapter/pconsole.exe program exits, the blinking cursor is
+replaced with a hidden cursor.
+
+Fix assert() in the agent. If it fails, the failure message needs to be
+reported somewhere. Pop up a dialog box? Maybe switch the active desktop,
+then show a dialog box?
+
+TODO: There's already a pconsole project on GitHub. Maybe rename this project
+to something else? winpty?
+
+TODO: Can the DebugServer system be replaced with OutputDebugString? How
+do we decide whose processes' output to collect?
+
+TODO: Three executables:
+ build/winpty-agent.exe
+ build/winpty.dll
+ build/console.exe
+
+BUG: Run the pconsole.exe inside another console. As I type dir, I see this:
+ D:\rprichard\pconsole>
+ D:\rprichard\pconsole>d
+ D:\rprichard\pconsole>di
+ D:\rprichard\pconsole>dir
+ In the output of "dir", every other line is blank.
+ There was a bug in Terminal::sendLine that was causing this to happen
+ frequently. Now that I fixed it, this bug should only manifest on lines
+ whose last column is not a space (i.e. a full line).
diff --git a/src/libs/3rdparty/winpty/misc/OSVersion.cc b/src/libs/3rdparty/winpty/misc/OSVersion.cc
new file mode 100644
index 0000000000..456708f05b
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/OSVersion.cc
@@ -0,0 +1,27 @@
+#include <windows.h>
+
+#include <assert.h>
+#include <locale.h>
+#include <stdio.h>
+
+#include <iostream>
+
+int main() {
+ setlocale(LC_ALL, "");
+
+ OSVERSIONINFOEXW info = {0};
+ info.dwOSVersionInfoSize = sizeof(info);
+ assert(GetVersionExW((OSVERSIONINFOW*)&info));
+
+ printf("dwMajorVersion = %d\n", (int)info.dwMajorVersion);
+ printf("dwMinorVersion = %d\n", (int)info.dwMinorVersion);
+ printf("dwBuildNumber = %d\n", (int)info.dwBuildNumber);
+ printf("dwPlatformId = %d\n", (int)info.dwPlatformId);
+ printf("szCSDVersion = %ls\n", info.szCSDVersion);
+ printf("wServicePackMajor = %d\n", info.wServicePackMajor);
+ printf("wServicePackMinor = %d\n", info.wServicePackMinor);
+ printf("wSuiteMask = 0x%x\n", (unsigned int)info.wSuiteMask);
+ printf("wProductType = 0x%x\n", (unsigned int)info.wProductType);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc
new file mode 100644
index 0000000000..656d4f126d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc
@@ -0,0 +1,101 @@
+//
+// Verify that console selection blocks writes to an inactive console screen
+// buffer. Writes TEST PASSED or TEST FAILED to the popup console window.
+//
+
+#include <windows.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+bool g_useMark = false;
+
+CALLBACK DWORD pausingThread(LPVOID dummy)
+{
+ HWND hwnd = GetConsoleWindow();
+ trace("Sending selection to freeze");
+ SendMessage(hwnd, WM_SYSCOMMAND,
+ g_useMark ? SC_CONSOLE_MARK :
+ SC_CONSOLE_SELECT_ALL,
+ 0);
+ Sleep(1000);
+ trace("Sending escape WM_CHAR to unfreeze");
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ Sleep(1000);
+}
+
+static HANDLE createBuffer() {
+ HANDLE buf = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CONSOLE_TEXTMODE_BUFFER,
+ NULL);
+ ASSERT(buf != INVALID_HANDLE_VALUE);
+ return buf;
+}
+
+static void runTest(bool useMark, bool createEarly) {
+ trace("=======================================");
+ trace("useMark=%d createEarly=%d", useMark, createEarly);
+ g_useMark = useMark;
+ HANDLE buf = INVALID_HANDLE_VALUE;
+
+ if (createEarly) {
+ buf = createBuffer();
+ }
+
+ CreateThread(NULL, 0,
+ pausingThread, NULL,
+ 0, NULL);
+ Sleep(500);
+
+ if (!createEarly) {
+ trace("Creating buffer");
+ TimeMeasurement tm1;
+ buf = createBuffer();
+ const double elapsed1 = tm1.elapsed();
+ if (elapsed1 >= 0.250) {
+ printf("!!! TEST FAILED !!!\n");
+ Sleep(2000);
+ return;
+ }
+ }
+
+ trace("Writing to aux buffer");
+ TimeMeasurement tm2;
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(buf, L"HI", 2, &actual, NULL);
+ const double elapsed2 = tm2.elapsed();
+ trace("Writing to aux buffer: finished: ret=%d actual=%d (elapsed=%1.3f)", ret, actual, elapsed2);
+ if (elapsed2 < 0.250) {
+ printf("!!! TEST FAILED !!!\n");
+ } else {
+ printf("TEST PASSED\n");
+ }
+ Sleep(2000);
+}
+
+int main(int argc, char **argv) {
+ if (argc == 1) {
+ startChildProcess(L"child");
+ return 0;
+ }
+
+ std::string arg = argv[1];
+ if (arg == "child") {
+ for (int useMark = 0; useMark <= 1; useMark++) {
+ for (int createEarly = 0; createEarly <= 1; createEarly++) {
+ runTest(useMark, createEarly);
+ }
+ }
+ printf("done...\n");
+ Sleep(1000);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc
new file mode 100644
index 0000000000..fa584b9fae
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc
@@ -0,0 +1,671 @@
+//
+// Windows versions tested
+//
+// Vista Enterprise SP2 32-bit
+// - ver reports [Version 6.0.6002]
+// - kernel32.dll product/file versions are 6.0.6002.19381
+//
+// Windows 7 Ultimate SP1 32-bit
+// - ver reports [Version 6.1.7601]
+// - conhost.exe product/file versions are 6.1.7601.18847
+// - kernel32.dll product/file versions are 6.1.7601.18847
+//
+// Windows Server 2008 R2 Datacenter SP1 64-bit
+// - ver reports [Version 6.1.7601]
+// - conhost.exe product/file versions are 6.1.7601.23153
+// - kernel32.dll product/file versions are 6.1.7601.23153
+//
+// Windows 8 Enterprise 32-bit
+// - ver reports [Version 6.2.9200]
+// - conhost.exe product/file versions are 6.2.9200.16578
+// - kernel32.dll product/file versions are 6.2.9200.16859
+//
+
+//
+// Specific version details on working Server 2008 R2:
+//
+// dwMajorVersion = 6
+// dwMinorVersion = 1
+// dwBuildNumber = 7601
+// dwPlatformId = 2
+// szCSDVersion = Service Pack 1
+// wServicePackMajor = 1
+// wServicePackMinor = 0
+// wSuiteMask = 0x190
+// wProductType = 0x3
+//
+// Specific version details on broken Win7:
+//
+// dwMajorVersion = 6
+// dwMinorVersion = 1
+// dwBuildNumber = 7601
+// dwPlatformId = 2
+// szCSDVersion = Service Pack 1
+// wServicePackMajor = 1
+// wServicePackMinor = 0
+// wSuiteMask = 0x100
+// wProductType = 0x1
+//
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "TestUtil.cc"
+
+const char *g_prefix = "";
+
+static void dumpHandles() {
+ trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x",
+ g_prefix,
+ (long long)GetStdHandle(STD_INPUT_HANDLE),
+ (long long)GetStdHandle(STD_OUTPUT_HANDLE),
+ (long long)GetStdHandle(STD_ERROR_HANDLE));
+}
+
+static const char *successOrFail(BOOL ret) {
+ return ret ? "ok" : "FAILED";
+}
+
+static void startChildInSameConsole(const wchar_t *args, BOOL
+ bInheritHandles=FALSE) {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(NULL, program, 1024);
+ swprintf(cmdline, L"\"%ls\" %ls", program, args);
+
+ STARTUPINFOW sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(sui);
+
+ CreateProcessW(program, cmdline,
+ NULL, NULL,
+ /*bInheritHandles=*/bInheritHandles,
+ /*dwCreationFlags=*/0,
+ NULL, NULL,
+ &sui, &pi);
+}
+
+static void closeHandle(HANDLE h) {
+ trace("%sClosing handle 0x%I64x...", g_prefix, (long long)h);
+ trace("%sClosing handle 0x%I64x... %s", g_prefix, (long long)h, successOrFail(CloseHandle(h)));
+}
+
+static HANDLE createBuffer() {
+
+ // If sa isn't provided, the handle defaults to not-inheritable.
+ SECURITY_ATTRIBUTES sa = {0};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+
+ trace("%sCreating a new buffer...", g_prefix);
+ HANDLE conout = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ CONSOLE_TEXTMODE_BUFFER, NULL);
+
+ trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout);
+ return conout;
+}
+
+static HANDLE openConout() {
+
+ // If sa isn't provided, the handle defaults to not-inheritable.
+ SECURITY_ATTRIBUTES sa = {0};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+
+ trace("%sOpening CONOUT...", g_prefix);
+ HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ OPEN_EXISTING, 0, NULL);
+ trace("%sOpening CONOUT... 0x%I64x", g_prefix, (long long)conout);
+ return conout;
+}
+
+static void setConsoleActiveScreenBuffer(HANDLE conout) {
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...",
+ g_prefix, (long long)conout);
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s",
+ g_prefix, (long long)conout,
+ successOrFail(SetConsoleActiveScreenBuffer(conout)));
+}
+
+static void writeTest(HANDLE conout, const char *msg) {
+ char writeData[256];
+ sprintf(writeData, "%s%s\n", g_prefix, msg);
+
+ trace("%sWriting to 0x%I64x: '%s'...",
+ g_prefix, (long long)conout, msg);
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL);
+ trace("%sWriting to 0x%I64x: '%s'... %s",
+ g_prefix, (long long)conout, msg,
+ successOrFail(ret && actual == strlen(writeData)));
+}
+
+static void writeTest(const char *msg) {
+ writeTest(GetStdHandle(STD_OUTPUT_HANDLE), msg);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST 1 -- create new buffer, activate it, and close the handle. The console
+// automatically switches the screen buffer back to the original.
+//
+// This test passes everywhere.
+//
+
+static void test1(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "1")) {
+ startChildProcess(L"1:child");
+ return;
+ }
+
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ writeTest(origBuffer, "<-- origBuffer -->");
+
+ HANDLE newBuffer = createBuffer();
+ writeTest(newBuffer, "<-- newBuffer -->");
+ setConsoleActiveScreenBuffer(newBuffer);
+ Sleep(2000);
+
+ writeTest(origBuffer, "TEST PASSED!");
+
+ // Closing the handle w/o switching the active screen buffer automatically
+ // switches the console back to the original buffer.
+ closeHandle(newBuffer);
+
+ while (true) {
+ Sleep(1000);
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST 2 -- Test program that creates and activates newBuffer, starts a child
+// process, then closes its newBuffer handle. newBuffer remains activated,
+// because the child keeps it active. (Also see TEST D.)
+//
+
+static void test2(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "2")) {
+ startChildProcess(L"2:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "2:parent")) {
+ g_prefix = "parent: ";
+ dumpHandles();
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ writeTest(origBuffer, "<-- origBuffer -->");
+
+ HANDLE newBuffer = createBuffer();
+ writeTest(newBuffer, "<-- newBuffer -->");
+ setConsoleActiveScreenBuffer(newBuffer);
+
+ Sleep(1000);
+ writeTest(newBuffer, "bInheritHandles=FALSE:");
+ startChildInSameConsole(L"2:child", FALSE);
+ Sleep(1000);
+ writeTest(newBuffer, "bInheritHandles=TRUE:");
+ startChildInSameConsole(L"2:child", TRUE);
+
+ Sleep(1000);
+ trace("parent:----");
+
+ // Close the new buffer. The active screen buffer doesn't automatically
+ // switch back to origBuffer, because the child process has a handle open
+ // to the original buffer.
+ closeHandle(newBuffer);
+
+ Sleep(600 * 1000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "2:child")) {
+ g_prefix = "child: ";
+ dumpHandles();
+ // The child's output isn't visible, because it's still writing to
+ // origBuffer.
+ trace("child:----");
+ writeTest("writing to STDOUT");
+
+ // Handle inheritability is curious. The console handles this program
+ // creates are inheritable, but CreateProcess is called with both
+ // bInheritHandles=TRUE and bInheritHandles=FALSE.
+ //
+ // Vista and Windows 7: bInheritHandles has no effect. The child and
+ // parent processes have the same STDIN/STDOUT/STDERR handles:
+ // 0x3, 0x7, and 0xB. The parent has a 0xF handle for newBuffer.
+ // The child can only write to 0x7, 0xB, and 0xF. Only the writes to
+ // 0xF are visible (i.e. they touch newBuffer).
+ //
+ // Windows 8 or Windows 10 (legacy or non-legacy): the lowest 2 bits of
+ // the HANDLE to WriteConsole seem to be ignored. The new process'
+ // console handles always refer to the buffer that was active when they
+ // started, but the values of the handles depend upon bInheritHandles.
+ // With bInheritHandles=TRUE, the child has the same
+ // STDIN/STDOUT/STDERR/newBuffer handles as the parent, and the three
+ // output handles all work, though their output is all visible. With
+ // bInheritHandles=FALSE, the child has different STDIN/STDOUT/STDERR
+ // handles, and only the new STDOUT/STDERR handles work.
+ //
+ for (unsigned int i = 0x1; i <= 0xB0; ++i) {
+ char msg[256];
+ sprintf(msg, "Write to handle 0x%x", i);
+ HANDLE h = reinterpret_cast<HANDLE>(i);
+ writeTest(h, msg);
+ }
+
+ Sleep(600 * 1000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST A -- demonstrate an apparent Windows bug with screen buffers
+//
+// Steps:
+// - The parent starts a child process.
+// - The child process creates and activates newBuffer
+// - The parent opens CONOUT$ and writes to it.
+// - The parent closes CONOUT$.
+// - At this point, broken Windows reactivates origBuffer.
+// - The child writes to newBuffer again.
+// - The child activates origBuffer again, then closes newBuffer.
+//
+// Test passes if the message "TEST PASSED!" is visible.
+// Test commonly fails if conhost.exe crashes.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
+// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testA_parentWork() {
+ // Open an extra CONOUT$ handle so that the HANDLE values in parent and
+ // child don't collide. I think it's OK if they collide, but since we're
+ // trying to track down a Windows bug, it's best to avoid unnecessary
+ // complication.
+ HANDLE dummy = openConout();
+
+ Sleep(3000);
+
+ // Step 2: Open CONOUT$ in the parent. This opens the active buffer, which
+ // was just created in the child. It's handle 0x13. Write to it.
+
+ HANDLE newBuffer = openConout();
+ writeTest(newBuffer, "step2: writing to newBuffer");
+
+ Sleep(3000);
+
+ // Step 3: Close handle 0x13. With Windows 7, the console switches back to
+ // origBuffer, and (unless I'm missing something) it shouldn't.
+
+ closeHandle(newBuffer);
+}
+
+static void testA_childWork() {
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ //
+ // Step 1: Create the new screen buffer in the child process and make it
+ // active. (Typically, it's handle 0x0F.)
+ //
+
+ HANDLE newBuffer = createBuffer();
+
+ setConsoleActiveScreenBuffer(newBuffer);
+ writeTest(newBuffer, "<-- newBuffer -->");
+
+ Sleep(9000);
+ trace("child:----");
+
+ // Step 4: write to the newBuffer again.
+ writeTest(newBuffer, "TEST PASSED!");
+
+ //
+ // Step 5: Switch back to the original screen buffer and close the new
+ // buffer. The switch call succeeds, but the CloseHandle call freezes for
+ // several seconds, because conhost.exe crashes.
+ //
+ Sleep(3000);
+
+ setConsoleActiveScreenBuffer(origBuffer);
+ writeTest(origBuffer, "writing to origBuffer");
+
+ closeHandle(newBuffer);
+
+ // The console HWND is NULL.
+ trace("child: console HWND=0x%I64x", (long long)GetConsoleWindow());
+
+ // At this point, the console window has closed, but the parent/child
+ // processes are still running. Calling AllocConsole would fail, but
+ // calling FreeConsole followed by AllocConsole would both succeed, and a
+ // new console would appear.
+}
+
+static void testA(int argc, char *argv[]) {
+
+ if (!strcmp(argv[1], "A")) {
+ startChildProcess(L"A:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "A:parent")) {
+ g_prefix = "parent: ";
+ trace("parent:----");
+ dumpHandles();
+ writeTest("<-- origBuffer -->");
+ startChildInSameConsole(L"A:child");
+ testA_parentWork();
+ Sleep(120000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "A:child")) {
+ g_prefix = "child: ";
+ dumpHandles();
+ testA_childWork();
+ Sleep(120000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST B -- invert TEST A -- also crashes conhost on Windows 7
+//
+// Test passes if the message "TEST PASSED!" is visible.
+// Test commonly fails if conhost.exe crashes.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
+// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testB(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "B")) {
+ startChildProcess(L"B:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "B:parent")) {
+ g_prefix = "parent: ";
+ startChildInSameConsole(L"B:child");
+ writeTest("<-- origBuffer -->");
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ //
+ // Step 1: Create the new buffer and make it active.
+ //
+ trace("%s----", g_prefix);
+ HANDLE newBuffer = createBuffer();
+ setConsoleActiveScreenBuffer(newBuffer);
+ writeTest(newBuffer, "<-- newBuffer -->");
+
+ //
+ // Step 4: Attempt to write again to the new buffer.
+ //
+ Sleep(9000);
+ trace("%s----", g_prefix);
+ writeTest(newBuffer, "TEST PASSED!");
+
+ //
+ // Step 5: Switch back to the original buffer.
+ //
+ Sleep(3000);
+ trace("%s----", g_prefix);
+ setConsoleActiveScreenBuffer(origBuffer);
+ closeHandle(newBuffer);
+ writeTest(origBuffer, "writing to the initial buffer");
+
+ Sleep(60000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "B:child")) {
+ g_prefix = "child: ";
+ Sleep(3000);
+ trace("%s----", g_prefix);
+
+ //
+ // Step 2: Open the newly active buffer and write to it.
+ //
+ HANDLE newBuffer = openConout();
+ writeTest(newBuffer, "writing to newBuffer");
+
+ //
+ // Step 3: Close the newly active buffer.
+ //
+ Sleep(3000);
+ closeHandle(newBuffer);
+
+ Sleep(60000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST C -- Interleaving open/close of console handles also seems to break on
+// Windows 7.
+//
+// Test:
+// - child creates and activates newBuf1
+// - parent opens newBuf1
+// - child creates and activates newBuf2
+// - parent opens newBuf2, then closes newBuf1
+// - child switches back to newBuf1
+// * At this point, the console starts malfunctioning.
+// - parent and child close newBuf2
+// - child closes newBuf1
+//
+// Test passes if the message "TEST PASSED!" is visible.
+// Test commonly fails if conhost.exe crashes.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
+// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testC(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "C")) {
+ startChildProcess(L"C:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "C:parent")) {
+ startChildInSameConsole(L"C:child");
+ writeTest("<-- origBuffer -->");
+ g_prefix = "parent: ";
+
+ // At time=4, open newBuffer1.
+ Sleep(4000);
+ trace("%s---- t=4", g_prefix);
+ const HANDLE newBuffer1 = openConout();
+
+ // At time=8, open newBuffer2, and close newBuffer1.
+ Sleep(4000);
+ trace("%s---- t=8", g_prefix);
+ const HANDLE newBuffer2 = openConout();
+ closeHandle(newBuffer1);
+
+ // At time=25, cleanup of newBuffer2.
+ Sleep(17000);
+ trace("%s---- t=25", g_prefix);
+ closeHandle(newBuffer2);
+
+ Sleep(240000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "C:child")) {
+ g_prefix = "child: ";
+
+ // At time=2, create newBuffer1 and activate it.
+ Sleep(2000);
+ trace("%s---- t=2", g_prefix);
+ const HANDLE newBuffer1 = createBuffer();
+ setConsoleActiveScreenBuffer(newBuffer1);
+ writeTest(newBuffer1, "<-- newBuffer1 -->");
+
+ // At time=6, create newBuffer2 and activate it.
+ Sleep(4000);
+ trace("%s---- t=6", g_prefix);
+ const HANDLE newBuffer2 = createBuffer();
+ setConsoleActiveScreenBuffer(newBuffer2);
+ writeTest(newBuffer2, "<-- newBuffer2 -->");
+
+ // At time=10, attempt to switch back to newBuffer1. The parent process
+ // has opened and closed its handle to newBuffer1, so does it still exist?
+ Sleep(4000);
+ trace("%s---- t=10", g_prefix);
+ setConsoleActiveScreenBuffer(newBuffer1);
+ writeTest(newBuffer1, "write to newBuffer1: TEST PASSED!");
+
+ // At time=25, cleanup of newBuffer2.
+ Sleep(15000);
+ trace("%s---- t=25", g_prefix);
+ closeHandle(newBuffer2);
+
+ // At time=35, cleanup of newBuffer1. The console should switch to the
+ // initial buffer again.
+ Sleep(10000);
+ trace("%s---- t=35", g_prefix);
+ closeHandle(newBuffer1);
+
+ Sleep(240000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST D -- parent creates a new buffer, child launches, writes,
+// closes it output handle, then parent writes again. (Also see TEST 2.)
+//
+// On success, this will appear:
+//
+// parent: <-- newBuffer -->
+// child: writing to newBuffer
+// parent: TEST PASSED!
+//
+// If this appears, it indicates that the child's closing its output handle did
+// not destroy newBuffer.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testD(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "D")) {
+ startChildProcess(L"D:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "D:parent")) {
+ g_prefix = "parent: ";
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ writeTest(origBuffer, "<-- origBuffer -->");
+
+ HANDLE newBuffer = createBuffer();
+ writeTest(newBuffer, "<-- newBuffer -->");
+ setConsoleActiveScreenBuffer(newBuffer);
+
+ // At t=2, start a child process, explicitly forcing it to use
+ // newBuffer for its standard handles. These calls are apparently
+ // redundant on Windows 8 and up.
+ Sleep(2000);
+ trace("parent:----");
+ trace("parent: starting child process");
+ SetStdHandle(STD_OUTPUT_HANDLE, newBuffer);
+ SetStdHandle(STD_ERROR_HANDLE, newBuffer);
+ startChildInSameConsole(L"D:child");
+ SetStdHandle(STD_OUTPUT_HANDLE, origBuffer);
+ SetStdHandle(STD_ERROR_HANDLE, origBuffer);
+
+ // At t=6, write again to newBuffer.
+ Sleep(4000);
+ trace("parent:----");
+ writeTest(newBuffer, "TEST PASSED!");
+
+ // At t=8, close the newBuffer. In earlier versions of windows
+ // (including Server 2008 R2), the console then switches back to
+ // origBuffer. As of Windows 8, it doesn't, because somehow the child
+ // process is keeping the console on newBuffer, even though the child
+ // process closed its STDIN/STDOUT/STDERR handles. Killing the child
+ // process by hand after the test finishes *does* force the console
+ // back to origBuffer.
+ Sleep(2000);
+ closeHandle(newBuffer);
+
+ Sleep(120000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "D:child")) {
+ g_prefix = "child: ";
+ // At t=2, the child starts.
+ trace("child:----");
+ dumpHandles();
+ writeTest("writing to newBuffer");
+
+ // At t=4, the child explicitly closes its handle.
+ Sleep(2000);
+ trace("child:----");
+ if (GetStdHandle(STD_ERROR_HANDLE) != GetStdHandle(STD_OUTPUT_HANDLE)) {
+ closeHandle(GetStdHandle(STD_ERROR_HANDLE));
+ }
+ closeHandle(GetStdHandle(STD_OUTPUT_HANDLE));
+ closeHandle(GetStdHandle(STD_INPUT_HANDLE));
+
+ Sleep(120000);
+ return;
+ }
+}
+
+
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ printf("USAGE: %s testnum\n", argv[0]);
+ return 0;
+ }
+
+ if (argv[1][0] == '1') {
+ test1(argc, argv);
+ } else if (argv[1][0] == '2') {
+ test2(argc, argv);
+ } else if (argv[1][0] == 'A') {
+ testA(argc, argv);
+ } else if (argv[1][0] == 'B') {
+ testB(argc, argv);
+ } else if (argv[1][0] == 'C') {
+ testC(argc, argv);
+ } else if (argv[1][0] == 'D') {
+ testD(argc, argv);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc
new file mode 100644
index 0000000000..2b648c9409
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc
@@ -0,0 +1,151 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+const char *g_prefix = "";
+
+static void dumpHandles() {
+ trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x",
+ g_prefix,
+ (long long)GetStdHandle(STD_INPUT_HANDLE),
+ (long long)GetStdHandle(STD_OUTPUT_HANDLE),
+ (long long)GetStdHandle(STD_ERROR_HANDLE));
+}
+
+static HANDLE createBuffer() {
+
+ // If sa isn't provided, the handle defaults to not-inheritable.
+ SECURITY_ATTRIBUTES sa = {0};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+
+ trace("%sCreating a new buffer...", g_prefix);
+ HANDLE conout = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ CONSOLE_TEXTMODE_BUFFER, NULL);
+
+ trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout);
+ return conout;
+}
+
+static const char *successOrFail(BOOL ret) {
+ return ret ? "ok" : "FAILED";
+}
+
+static void setConsoleActiveScreenBuffer(HANDLE conout) {
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...",
+ g_prefix, (long long)conout);
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s",
+ g_prefix, (long long)conout,
+ successOrFail(SetConsoleActiveScreenBuffer(conout)));
+}
+
+static void writeTest(HANDLE conout, const char *msg) {
+ char writeData[256];
+ sprintf(writeData, "%s%s\n", g_prefix, msg);
+
+ trace("%sWriting to 0x%I64x: '%s'...",
+ g_prefix, (long long)conout, msg);
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL);
+ trace("%sWriting to 0x%I64x: '%s'... %s",
+ g_prefix, (long long)conout, msg,
+ successOrFail(ret && actual == strlen(writeData)));
+}
+
+static HANDLE startChildInSameConsole(const wchar_t *args, BOOL
+ bInheritHandles=FALSE) {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(NULL, program, 1024);
+ swprintf(cmdline, L"\"%ls\" %ls", program, args);
+
+ STARTUPINFOW sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(sui);
+
+ CreateProcessW(program, cmdline,
+ NULL, NULL,
+ /*bInheritHandles=*/bInheritHandles,
+ /*dwCreationFlags=*/0,
+ NULL, NULL,
+ &sui, &pi);
+
+ return pi.hProcess;
+}
+
+static HANDLE dup(HANDLE h, HANDLE targetProcess) {
+ HANDLE h2 = INVALID_HANDLE_VALUE;
+ BOOL ret = DuplicateHandle(
+ GetCurrentProcess(), h,
+ targetProcess, &h2,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ trace("dup(0x%I64x) to process 0x%I64x... %s, 0x%I64x",
+ (long long)h,
+ (long long)targetProcess,
+ successOrFail(ret),
+ (long long)h2);
+ return h2;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"parent");
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "parent")) {
+ g_prefix = "parent: ";
+ dumpHandles();
+ HANDLE hChild = startChildInSameConsole(L"child");
+
+ // Windows 10.
+ HANDLE orig1 = GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE new1 = createBuffer();
+
+ Sleep(2000);
+ setConsoleActiveScreenBuffer(new1);
+
+ // Handle duplication results to child process in same console:
+ // - Windows XP: fails
+ // - Windows 7 Ultimate SP1 32-bit: fails
+ // - Windows Server 2008 R2 Datacenter SP1 64-bit: fails
+ // - Windows 8 Enterprise 32-bit: succeeds
+ // - Windows 10: succeeds
+ HANDLE orig2 = dup(orig1, GetCurrentProcess());
+ HANDLE new2 = dup(new1, GetCurrentProcess());
+
+ dup(orig1, hChild);
+ dup(new1, hChild);
+
+ // The writes to orig1/orig2 are invisible. The writes to new1/new2
+ // are visible.
+ writeTest(orig1, "write to orig1");
+ writeTest(orig2, "write to orig2");
+ writeTest(new1, "write to new1");
+ writeTest(new2, "write to new2");
+
+ Sleep(120000);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "child")) {
+ g_prefix = "child: ";
+ dumpHandles();
+ Sleep(4000);
+ for (unsigned int i = 0x1; i <= 0xB0; ++i) {
+ char msg[256];
+ sprintf(msg, "Write to handle 0x%x", i);
+ HANDLE h = reinterpret_cast<HANDLE>(i);
+ writeTest(h, msg);
+ }
+ Sleep(120000);
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SelectAllTest.cc b/src/libs/3rdparty/winpty/misc/SelectAllTest.cc
new file mode 100644
index 0000000000..a6c27739d8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SelectAllTest.cc
@@ -0,0 +1,45 @@
+#define _WIN32_WINNT 0x0501
+#include <stdio.h>
+#include <windows.h>
+
+#include "../src/shared/DebugClient.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+CALLBACK DWORD pausingThread(LPVOID dummy)
+{
+ HWND hwnd = GetConsoleWindow();
+ while (true) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
+ Sleep(1000);
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ Sleep(1000);
+ }
+}
+
+int main()
+{
+ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ GetConsoleScreenBufferInfo(out, &info);
+ COORD initial = info.dwCursorPosition;
+
+ CreateThread(NULL, 0,
+ pausingThread, NULL,
+ 0, NULL);
+
+ for (int i = 0; i < 30; ++i) {
+ Sleep(100);
+ GetConsoleScreenBufferInfo(out, &info);
+ if (memcmp(&info.dwCursorPosition, &initial, sizeof(COORD)) != 0) {
+ trace("cursor moved to [%d,%d]",
+ info.dwCursorPosition.X,
+ info.dwCursorPosition.Y);
+ } else {
+ trace("cursor in expected position");
+ }
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetBufInfo.cc b/src/libs/3rdparty/winpty/misc/SetBufInfo.cc
new file mode 100644
index 0000000000..f37c31bdf7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetBufInfo.cc
@@ -0,0 +1,90 @@
+#include <windows.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+static void usage() {
+ printf("usage: SetBufInfo [-set] [-buf W H] [-win W H] [-pos X Y]\n");
+}
+
+int main(int argc, char *argv[]) {
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+
+ bool change = false;
+ BOOL success;
+ CONSOLE_SCREEN_BUFFER_INFOEX info = {};
+ info.cbSize = sizeof(info);
+
+ success = GetConsoleScreenBufferInfoEx(conout, &info);
+ ASSERT(success && "GetConsoleScreenBufferInfoEx failed");
+
+ for (int i = 1; i < argc; ) {
+ std::string arg = argv[i];
+ if (arg == "-buf" && (i + 2) < argc) {
+ info.dwSize.X = atoi(argv[i + 1]);
+ info.dwSize.Y = atoi(argv[i + 2]);
+ i += 3;
+ change = true;
+ } else if (arg == "-pos" && (i + 2) < argc) {
+ int dx = info.srWindow.Right - info.srWindow.Left;
+ int dy = info.srWindow.Bottom - info.srWindow.Top;
+ info.srWindow.Left = atoi(argv[i + 1]);
+ info.srWindow.Top = atoi(argv[i + 2]);
+ i += 3;
+ info.srWindow.Right = info.srWindow.Left + dx;
+ info.srWindow.Bottom = info.srWindow.Top + dy;
+ change = true;
+ } else if (arg == "-win" && (i + 2) < argc) {
+ info.srWindow.Right = info.srWindow.Left + atoi(argv[i + 1]) - 1;
+ info.srWindow.Bottom = info.srWindow.Top + atoi(argv[i + 2]) - 1;
+ i += 3;
+ change = true;
+ } else if (arg == "-set") {
+ change = true;
+ ++i;
+ } else if (arg == "--help" || arg == "-help") {
+ usage();
+ exit(0);
+ } else {
+ fprintf(stderr, "error: unrecognized argument: %s\n", arg.c_str());
+ usage();
+ exit(1);
+ }
+ }
+
+ if (change) {
+ success = SetConsoleScreenBufferInfoEx(conout, &info);
+ if (success) {
+ printf("success\n");
+ } else {
+ printf("SetConsoleScreenBufferInfoEx call failed\n");
+ }
+ success = GetConsoleScreenBufferInfoEx(conout, &info);
+ ASSERT(success && "GetConsoleScreenBufferInfoEx failed");
+ }
+
+ auto dump = [](const char *fmt, ...) {
+ char msg[256];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(msg, fmt, ap);
+ va_end(ap);
+ trace("%s", msg);
+ printf("%s\n", msg);
+ };
+
+ dump("buffer-size: %d x %d", info.dwSize.X, info.dwSize.Y);
+ dump("window-size: %d x %d",
+ info.srWindow.Right - info.srWindow.Left + 1,
+ info.srWindow.Bottom - info.srWindow.Top + 1);
+ dump("window-pos: %d, %d", info.srWindow.Left, info.srWindow.Top);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetBufferSize.cc b/src/libs/3rdparty/winpty/misc/SetBufferSize.cc
new file mode 100644
index 0000000000..b50a1f8dc3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetBufferSize.cc
@@ -0,0 +1,32 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc != 3) {
+ printf("Usage: %s x y width height\n", argv[0]);
+ return 1;
+ }
+
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+
+ COORD size = {
+ (short)atoi(argv[1]),
+ (short)atoi(argv[2]),
+ };
+
+ BOOL ret = SetConsoleScreenBufferSize(conout, size);
+ const unsigned lastError = GetLastError();
+ const char *const retStr = ret ? "OK" : "failed";
+ trace("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)", retStr, lastError);
+ printf("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)\n", retStr, lastError);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetCursorPos.cc b/src/libs/3rdparty/winpty/misc/SetCursorPos.cc
new file mode 100644
index 0000000000..d20fdbdfc0
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetCursorPos.cc
@@ -0,0 +1,10 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ int col = atoi(argv[1]);
+ int row = atoi(argv[2]);
+ setCursorPos(col, row);
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetFont.cc b/src/libs/3rdparty/winpty/misc/SetFont.cc
new file mode 100644
index 0000000000..9bcd4b4cc9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetFont.cc
@@ -0,0 +1,145 @@
+#include <windows.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
+
+int main() {
+ setlocale(LC_ALL, "");
+ wchar_t *cmdline = GetCommandLineW();
+ int argc = 0;
+ wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
+ const HANDLE conout = openConout();
+
+ if (argc == 1) {
+ cprintf(L"Usage:\n");
+ cprintf(L" SetFont <index>\n");
+ cprintf(L" SetFont options\n");
+ cprintf(L"\n");
+ cprintf(L"Options for SetCurrentConsoleFontEx:\n");
+ cprintf(L" -idx INDEX\n");
+ cprintf(L" -w WIDTH\n");
+ cprintf(L" -h HEIGHT\n");
+ cprintf(L" -family (0xNN|NN)\n");
+ cprintf(L" -weight (normal|bold|NNN)\n");
+ cprintf(L" -face FACENAME\n");
+ cprintf(L" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
+ cprintf(L" -tt\n");
+ cprintf(L" -vec\n");
+ cprintf(L" -vp\n");
+ cprintf(L" -dev\n");
+ cprintf(L" -roman\n");
+ cprintf(L" -swiss\n");
+ cprintf(L" -modern\n");
+ cprintf(L" -script\n");
+ cprintf(L" -decorative\n");
+ return 0;
+ }
+
+ if (isdigit(argv[1][0])) {
+ int index = _wtoi(argv[1]);
+ HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
+ FARPROC proc = GetProcAddress(kernel32, "SetConsoleFont");
+ if (proc == NULL) {
+ cprintf(L"Couldn't get address of SetConsoleFont\n");
+ } else {
+ BOOL ret = reinterpret_cast<BOOL WINAPI(*)(HANDLE, DWORD)>(proc)(
+ conout, index);
+ cprintf(L"SetFont returned %d\n", ret);
+ }
+ return 0;
+ }
+
+ CONSOLE_FONT_INFOEX fontex = {0};
+ fontex.cbSize = sizeof(fontex);
+
+ for (int i = 1; i < argc; ++i) {
+ std::wstring arg = argv[i];
+ if (i + 1 < argc) {
+ std::wstring next = argv[i + 1];
+ if (arg == L"-idx") {
+ fontex.nFont = _wtoi(next.c_str());
+ ++i; continue;
+ } else if (arg == L"-w") {
+ fontex.dwFontSize.X = _wtoi(next.c_str());
+ ++i; continue;
+ } else if (arg == L"-h") {
+ fontex.dwFontSize.Y = _wtoi(next.c_str());
+ ++i; continue;
+ } else if (arg == L"-weight") {
+ if (next == L"normal") {
+ fontex.FontWeight = 400;
+ } else if (next == L"bold") {
+ fontex.FontWeight = 700;
+ } else {
+ fontex.FontWeight = _wtoi(next.c_str());
+ }
+ ++i; continue;
+ } else if (arg == L"-face") {
+ wcsncpy(fontex.FaceName, next.c_str(), COUNT_OF(fontex.FaceName));
+ ++i; continue;
+ } else if (arg == L"-family") {
+ fontex.FontFamily = strtol(narrowString(next).c_str(), nullptr, 0);
+ ++i; continue;
+ }
+ }
+ if (arg == L"-tt") {
+ fontex.FontFamily |= TMPF_TRUETYPE;
+ } else if (arg == L"-vec") {
+ fontex.FontFamily |= TMPF_VECTOR;
+ } else if (arg == L"-vp") {
+ // Setting the TMPF_FIXED_PITCH bit actually indicates variable
+ // pitch.
+ fontex.FontFamily |= TMPF_FIXED_PITCH;
+ } else if (arg == L"-dev") {
+ fontex.FontFamily |= TMPF_DEVICE;
+ } else if (arg == L"-roman") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_ROMAN;
+ } else if (arg == L"-swiss") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_SWISS;
+ } else if (arg == L"-modern") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_MODERN;
+ } else if (arg == L"-script") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_SCRIPT;
+ } else if (arg == L"-decorative") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_DECORATIVE;
+ } else if (arg == L"-face-gothic") {
+ wcsncpy(fontex.FaceName, kMSGothic, COUNT_OF(fontex.FaceName));
+ } else if (arg == L"-face-simsun") {
+ wcsncpy(fontex.FaceName, kNSimSun, COUNT_OF(fontex.FaceName));
+ } else if (arg == L"-face-minglight") {
+ wcsncpy(fontex.FaceName, kMingLight, COUNT_OF(fontex.FaceName));
+ } else if (arg == L"-face-gulimche") {
+ wcsncpy(fontex.FaceName, kGulimChe, COUNT_OF(fontex.FaceName));
+ } else {
+ cprintf(L"Unrecognized argument: %ls\n", arg.c_str());
+ exit(1);
+ }
+ }
+
+ cprintf(L"Setting to: nFont=%u dwFontSize=(%d,%d) "
+ L"FontFamily=0x%x FontWeight=%u "
+ L"FaceName=\"%ls\"\n",
+ static_cast<unsigned>(fontex.nFont),
+ fontex.dwFontSize.X, fontex.dwFontSize.Y,
+ fontex.FontFamily, fontex.FontWeight,
+ fontex.FaceName);
+
+ BOOL ret = SetCurrentConsoleFontEx(
+ conout,
+ FALSE,
+ &fontex);
+ cprintf(L"SetCurrentConsoleFontEx returned %d\n", ret);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetWindowRect.cc b/src/libs/3rdparty/winpty/misc/SetWindowRect.cc
new file mode 100644
index 0000000000..6291dd6745
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetWindowRect.cc
@@ -0,0 +1,36 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc != 5) {
+ printf("Usage: %s x y width height\n", argv[0]);
+ return 1;
+ }
+
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+
+ SMALL_RECT sr = {
+ (short)atoi(argv[1]),
+ (short)atoi(argv[2]),
+ (short)(atoi(argv[1]) + atoi(argv[3]) - 1),
+ (short)(atoi(argv[2]) + atoi(argv[4]) - 1),
+ };
+
+ trace("Calling SetConsoleWindowInfo with {L=%d,T=%d,R=%d,B=%d}",
+ sr.Left, sr.Top, sr.Right, sr.Bottom);
+ BOOL ret = SetConsoleWindowInfo(conout, TRUE, &sr);
+ const unsigned lastError = GetLastError();
+ const char *const retStr = ret ? "OK" : "failed";
+ trace("SetConsoleWindowInfo ret: %s (LastError=0x%x)", retStr, lastError);
+ printf("SetConsoleWindowInfo ret: %s (LastError=0x%x)\n", retStr, lastError);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ShowArgv.cc b/src/libs/3rdparty/winpty/misc/ShowArgv.cc
new file mode 100644
index 0000000000..29a0f09131
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ShowArgv.cc
@@ -0,0 +1,12 @@
+// This test program is useful for studying commandline<->argv conversion.
+
+#include <stdio.h>
+#include <windows.h>
+
+int main(int argc, char **argv)
+{
+ printf("cmdline = [%s]\n", GetCommandLine());
+ for (int i = 0; i < argc; ++i)
+ printf("[%s]\n", argv[i]);
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc b/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc
new file mode 100644
index 0000000000..75fbfb81f1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc
@@ -0,0 +1,40 @@
+#include <windows.h>
+#include <stdio.h>
+#include <ctype.h>
+
+int main(int argc, char *argv[])
+{
+ static int escCount = 0;
+
+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ while (true) {
+ DWORD count;
+ INPUT_RECORD ir;
+ if (!ReadConsoleInput(hStdin, &ir, 1, &count)) {
+ printf("ReadConsoleInput failed\n");
+ return 1;
+ }
+
+ if (true) {
+ DWORD mode;
+ GetConsoleMode(hStdin, &mode);
+ SetConsoleMode(hStdin, mode & ~ENABLE_PROCESSED_INPUT);
+ }
+
+ if (ir.EventType == KEY_EVENT) {
+ const KEY_EVENT_RECORD &ker = ir.Event.KeyEvent;
+ printf("%s", ker.bKeyDown ? "dn" : "up");
+ printf(" ch=");
+ if (isprint(ker.uChar.AsciiChar))
+ printf("'%c'", ker.uChar.AsciiChar);
+ printf("%d", ker.uChar.AsciiChar);
+ printf(" vk=%#x", ker.wVirtualKeyCode);
+ printf(" scan=%#x", ker.wVirtualScanCode);
+ printf(" state=%#x", (int)ker.dwControlKeyState);
+ printf(" repeat=%d", ker.wRepeatCount);
+ printf("\n");
+ if (ker.uChar.AsciiChar == 27 && ++escCount == 6)
+ break;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/misc/Spew.py b/src/libs/3rdparty/winpty/misc/Spew.py
new file mode 100644
index 0000000000..9d1796af37
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Spew.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+i = 0;
+while True:
+ i += 1
+ print(i)
diff --git a/src/libs/3rdparty/winpty/misc/TestUtil.cc b/src/libs/3rdparty/winpty/misc/TestUtil.cc
new file mode 100644
index 0000000000..c832a12b85
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/TestUtil.cc
@@ -0,0 +1,172 @@
+// This file is included into test programs using #include
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <vector>
+#include <string>
+
+#include "../src/shared/DebugClient.h"
+#include "../src/shared/TimeMeasurement.h"
+
+#include "../src/shared/DebugClient.cc"
+#include "../src/shared/WinptyAssert.cc"
+#include "../src/shared/WinptyException.cc"
+
+// Launch this test program again, in a new console that we will destroy.
+static void startChildProcess(const wchar_t *args) {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(NULL, program, 1024);
+ swprintf(cmdline, L"\"%ls\" %ls", program, args);
+
+ STARTUPINFOW sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(sui);
+
+ CreateProcessW(program, cmdline,
+ NULL, NULL,
+ /*bInheritHandles=*/FALSE,
+ /*dwCreationFlags=*/CREATE_NEW_CONSOLE,
+ NULL, NULL,
+ &sui, &pi);
+}
+
+static void setBufferSize(HANDLE conout, int x, int y) {
+ COORD size = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
+ BOOL success = SetConsoleScreenBufferSize(conout, size);
+ trace("setBufferSize: (%d,%d), result=%d", x, y, success);
+}
+
+static void setWindowPos(HANDLE conout, int x, int y, int w, int h) {
+ SMALL_RECT r = {
+ static_cast<SHORT>(x), static_cast<SHORT>(y),
+ static_cast<SHORT>(x + w - 1),
+ static_cast<SHORT>(y + h - 1)
+ };
+ BOOL success = SetConsoleWindowInfo(conout, /*bAbsolute=*/TRUE, &r);
+ trace("setWindowPos: (%d,%d,%d,%d), result=%d", x, y, w, h, success);
+}
+
+static void setCursorPos(HANDLE conout, int x, int y) {
+ COORD coord = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
+ SetConsoleCursorPosition(conout, coord);
+}
+
+static void setBufferSize(int x, int y) {
+ setBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), x, y);
+}
+
+static void setWindowPos(int x, int y, int w, int h) {
+ setWindowPos(GetStdHandle(STD_OUTPUT_HANDLE), x, y, w, h);
+}
+
+static void setCursorPos(int x, int y) {
+ setCursorPos(GetStdHandle(STD_OUTPUT_HANDLE), x, y);
+}
+
+static void countDown(int sec) {
+ for (int i = sec; i > 0; --i) {
+ printf("%d.. ", i);
+ fflush(stdout);
+ Sleep(1000);
+ }
+ printf("\n");
+}
+
+static void writeBox(int x, int y, int w, int h, char ch, int attributes=7) {
+ CHAR_INFO info = { 0 };
+ info.Char.AsciiChar = ch;
+ info.Attributes = attributes;
+ std::vector<CHAR_INFO> buf(w * h, info);
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ COORD bufSize = { static_cast<SHORT>(w), static_cast<SHORT>(h) };
+ COORD bufCoord = { 0, 0 };
+ SMALL_RECT writeRegion = {
+ static_cast<SHORT>(x),
+ static_cast<SHORT>(y),
+ static_cast<SHORT>(x + w - 1),
+ static_cast<SHORT>(y + h - 1)
+ };
+ WriteConsoleOutputA(conout, buf.data(), bufSize, bufCoord, &writeRegion);
+}
+
+static void setChar(int x, int y, char ch, int attributes=7) {
+ writeBox(x, y, 1, 1, ch, attributes);
+}
+
+static void fillChar(int x, int y, int repeat, char ch) {
+ COORD coord = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
+ DWORD actual = 0;
+ FillConsoleOutputCharacterA(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ ch, repeat, coord, &actual);
+}
+
+static void repeatChar(int count, char ch) {
+ for (int i = 0; i < count; ++i) {
+ putchar(ch);
+ }
+ fflush(stdout);
+}
+
+// I don't know why, but wprintf fails to print this face name,
+// "MS ゴシック" (aka MS Gothic). It helps to use wprintf instead of printf, and
+// it helps to call `setlocale(LC_ALL, "")`, but the Japanese symbols are
+// ultimately converted to `?` symbols, even though MS Gothic is able to
+// display its own name, and the current code page is 932 (Shift-JIS).
+static void cvfprintf(HANDLE conout, const wchar_t *fmt, va_list ap) {
+ wchar_t buffer[256];
+ vswprintf(buffer, 256 - 1, fmt, ap);
+ buffer[255] = L'\0';
+ DWORD actual = 0;
+ if (!WriteConsoleW(conout, buffer, wcslen(buffer), &actual, NULL)) {
+ wprintf(L"WriteConsoleW call failed!\n");
+ }
+}
+
+static void cfprintf(HANDLE conout, const wchar_t *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ cvfprintf(conout, fmt, ap);
+ va_end(ap);
+}
+
+static void cprintf(const wchar_t *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ cvfprintf(GetStdHandle(STD_OUTPUT_HANDLE), fmt, ap);
+ va_end(ap);
+}
+
+static std::string narrowString(const std::wstring &input)
+{
+ int mblen = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ NULL, 0, NULL, NULL);
+ if (mblen <= 0) {
+ return std::string();
+ }
+ std::vector<char> tmp(mblen);
+ int mblen2 = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ tmp.data(), tmp.size(),
+ NULL, NULL);
+ assert(mblen2 == mblen);
+ return std::string(tmp.data(), tmp.size());
+}
+
+HANDLE openConout() {
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+ return conout;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc b/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc
new file mode 100644
index 0000000000..7210d41032
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc
@@ -0,0 +1,102 @@
+// Demonstrates how U+30FC is sometimes handled as a single-width character
+// when it should be handled as a double-width character.
+//
+// It only runs on computers where 932 is a valid code page. Set the system
+// local to "Japanese (Japan)" to ensure this.
+//
+// The problem seems to happen when U+30FC is printed in a console using the
+// Lucida Console font, and only when that font is at certain sizes.
+//
+
+#include <windows.h>
+#include <assert.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+static void setFont(const wchar_t *faceName, int pxSize) {
+ CONSOLE_FONT_INFOEX infoex = {0};
+ infoex.cbSize = sizeof(infoex);
+ infoex.dwFontSize.Y = pxSize;
+ wcsncpy(infoex.FaceName, faceName, COUNT_OF(infoex.FaceName));
+ BOOL ret = SetCurrentConsoleFontEx(
+ GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &infoex);
+ assert(ret);
+}
+
+static bool performTest(const wchar_t testChar) {
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ SetConsoleTextAttribute(conout, 7);
+
+ system("cls");
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(conout, &testChar, 1, &actual, NULL);
+ assert(ret && actual == 1);
+
+ CHAR_INFO verify[2];
+ COORD bufSize = {2, 1};
+ COORD bufCoord = {0, 0};
+ const SMALL_RECT readRegion = {0, 0, 1, 0};
+ SMALL_RECT actualRegion = readRegion;
+ ret = ReadConsoleOutputW(conout, verify, bufSize, bufCoord, &actualRegion);
+ assert(ret && !memcmp(&readRegion, &actualRegion, sizeof(readRegion)));
+ assert(verify[0].Char.UnicodeChar == testChar);
+
+ if (verify[1].Char.UnicodeChar == testChar) {
+ // Typical double-width behavior with a TrueType font. Pass.
+ assert(verify[0].Attributes == 0x107);
+ assert(verify[1].Attributes == 0x207);
+ return true;
+ } else if (verify[1].Char.UnicodeChar == 0) {
+ // Typical double-width behavior with a Raster Font. Pass.
+ assert(verify[0].Attributes == 7);
+ assert(verify[1].Attributes == 0);
+ return true;
+ } else if (verify[1].Char.UnicodeChar == L' ') {
+ // Single-width behavior. Fail.
+ assert(verify[0].Attributes == 7);
+ assert(verify[1].Attributes == 7);
+ return false;
+ } else {
+ // Unexpected output.
+ assert(false);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ setlocale(LC_ALL, "");
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ assert(SetConsoleCP(932));
+ assert(SetConsoleOutputCP(932));
+
+ const wchar_t testChar = 0x30FC;
+ const wchar_t *const faceNames[] = {
+ L"Lucida Console",
+ L"Consolas",
+ L"MS ゴシック",
+ };
+
+ trace("Test started");
+
+ for (auto faceName : faceNames) {
+ for (int px = 1; px <= 50; ++px) {
+ setFont(faceName, px);
+ if (!performTest(testChar)) {
+ trace("FAILURE: %s %dpx", narrowString(faceName).c_str(), px);
+ }
+ }
+ }
+
+ trace("Test complete");
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc b/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc
new file mode 100644
index 0000000000..a8d798e70d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc
@@ -0,0 +1,246 @@
+#include <windows.h>
+
+#include <assert.h>
+#include <vector>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+
+CHAR_INFO ci(wchar_t ch, WORD attributes) {
+ CHAR_INFO ret;
+ ret.Char.UnicodeChar = ch;
+ ret.Attributes = attributes;
+ return ret;
+}
+
+CHAR_INFO ci(wchar_t ch) {
+ return ci(ch, 7);
+}
+
+CHAR_INFO ci() {
+ return ci(L' ');
+}
+
+bool operator==(SMALL_RECT x, SMALL_RECT y) {
+ return !memcmp(&x, &y, sizeof(x));
+}
+
+SMALL_RECT sr(COORD pt, COORD size) {
+ return {
+ pt.X, pt.Y,
+ static_cast<SHORT>(pt.X + size.X - 1),
+ static_cast<SHORT>(pt.Y + size.Y - 1)
+ };
+}
+
+static void set(
+ const COORD pt,
+ const COORD size,
+ const std::vector<CHAR_INFO> &data) {
+ assert(data.size() == size.X * size.Y);
+ SMALL_RECT writeRegion = sr(pt, size);
+ BOOL ret = WriteConsoleOutputW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), size, {0, 0}, &writeRegion);
+ assert(ret && writeRegion == sr(pt, size));
+}
+
+static void set(
+ const COORD pt,
+ const std::vector<CHAR_INFO> &data) {
+ set(pt, {static_cast<SHORT>(data.size()), 1}, data);
+}
+
+static void writeAttrsAt(
+ const COORD pt,
+ const std::vector<WORD> &data) {
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleOutputAttribute(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), pt, &actual);
+ assert(ret && actual == data.size());
+}
+
+static void writeCharsAt(
+ const COORD pt,
+ const std::vector<wchar_t> &data) {
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleOutputCharacterW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), pt, &actual);
+ assert(ret && actual == data.size());
+}
+
+static void writeChars(
+ const std::vector<wchar_t> &data) {
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), &actual, NULL);
+ assert(ret && actual == data.size());
+}
+
+std::vector<CHAR_INFO> get(
+ const COORD pt,
+ const COORD size) {
+ std::vector<CHAR_INFO> data(size.X * size.Y);
+ SMALL_RECT readRegion = sr(pt, size);
+ BOOL ret = ReadConsoleOutputW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), size, {0, 0}, &readRegion);
+ assert(ret && readRegion == sr(pt, size));
+ return data;
+}
+
+std::vector<wchar_t> readCharsAt(
+ const COORD pt,
+ int size) {
+ std::vector<wchar_t> data(size);
+ DWORD actual = 0;
+ BOOL ret = ReadConsoleOutputCharacterW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), pt, &actual);
+ assert(ret);
+ data.resize(actual); // With double-width chars, we can read fewer than `size`.
+ return data;
+}
+
+static void dump(const COORD pt, const COORD size) {
+ for (CHAR_INFO ci : get(pt, size)) {
+ printf("%04X %04X\n", ci.Char.UnicodeChar, ci.Attributes);
+ }
+}
+
+static void dumpCharsAt(const COORD pt, int size) {
+ for (wchar_t ch : readCharsAt(pt, size)) {
+ printf("%04X\n", ch);
+ }
+}
+
+static COORD getCursorPos() {
+ CONSOLE_SCREEN_BUFFER_INFO info = { sizeof(info) };
+ assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
+ return info.dwCursorPosition;
+}
+
+static void test1() {
+ // We write "䀀䀀", then write "䀁" in the middle of the two. The second
+ // write turns the first and last cells into spaces. The LEADING/TRAILING
+ // flags retain consistency.
+ printf("test1 - overlap full-width char with full-width char\n");
+ writeCharsAt({1,0}, {0x4000, 0x4000});
+ dump({0,0}, {6,1});
+ printf("\n");
+ writeCharsAt({2,0}, {0x4001});
+ dump({0,0}, {6,1});
+ printf("\n");
+}
+
+static void test2() {
+ // Like `test1`, but use a lower-level API to do the write. Consistency is
+ // preserved here too -- the first and last cells are replaced with spaces.
+ printf("test2 - overlap full-width char with full-width char (lowlevel)\n");
+ writeCharsAt({1,0}, {0x4000, 0x4000});
+ dump({0,0}, {6,1});
+ printf("\n");
+ set({2,0}, {ci(0x4001,0x107), ci(0x4001,0x207)});
+ dump({0,0}, {6,1});
+ printf("\n");
+}
+
+static void test3() {
+ // However, the lower-level API can break the LEADING/TRAILING invariant
+ // explicitly:
+ printf("test3 - explicitly violate LEADING/TRAILING using lowlevel API\n");
+ set({1,0}, {
+ ci(0x4000, 0x207),
+ ci(0x4001, 0x107),
+ ci(0x3044, 7),
+ ci(L'X', 0x107),
+ ci(L'X', 0x207),
+ });
+ dump({0,0}, {7,1});
+}
+
+static void test4() {
+ // It is possible for the two cells of a double-width character to have two
+ // colors.
+ printf("test4 - use lowlevel to assign two colors to one full-width char\n");
+ set({0,0}, {
+ ci(0x4000, 0x142),
+ ci(0x4000, 0x224),
+ });
+ dump({0,0}, {2,1});
+}
+
+static void test5() {
+ // WriteConsoleOutputAttribute doesn't seem to affect the LEADING/TRAILING
+ // flags.
+ printf("test5 - WriteConsoleOutputAttribute cannot affect LEADING/TRAILING\n");
+
+ // Trying to clear the flags doesn't work...
+ writeCharsAt({0,0}, {0x4000});
+ dump({0,0}, {2,1});
+ writeAttrsAt({0,0}, {0x42, 0x24});
+ printf("\n");
+ dump({0,0}, {2,1});
+
+ // ... and trying to add them also doesn't work.
+ writeCharsAt({0,1}, {'A', ' '});
+ writeAttrsAt({0,1}, {0x107, 0x207});
+ printf("\n");
+ dump({0,1}, {2,1});
+}
+
+static void test6() {
+ // The cursor position may be on either cell of a double-width character.
+ // Visually, the cursor appears under both cells, regardless of which
+ // specific one has the cursor.
+ printf("test6 - cursor can be either left or right cell of full-width char\n");
+
+ writeCharsAt({2,1}, {0x4000});
+
+ setCursorPos(2, 1);
+ auto pos1 = getCursorPos();
+ Sleep(1000);
+
+ setCursorPos(3, 1);
+ auto pos2 = getCursorPos();
+ Sleep(1000);
+
+ setCursorPos(0, 15);
+ printf("%d,%d\n", pos1.X, pos1.Y);
+ printf("%d,%d\n", pos2.X, pos2.Y);
+}
+
+static void runTest(void (&test)()) {
+ system("cls");
+ setCursorPos(0, 14);
+ test();
+ system("pause");
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 40);
+ setWindowPos(0, 0, 80, 40);
+
+ auto cp = GetConsoleOutputCP();
+ assert(cp == 932 || cp == 936 || cp == 949 || cp == 950);
+
+ runTest(test1);
+ runTest(test2);
+ runTest(test3);
+ runTest(test4);
+ runTest(test5);
+ runTest(test6);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc b/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc
new file mode 100644
index 0000000000..05f80f70bd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc
@@ -0,0 +1,130 @@
+//
+// Test half-width vs full-width characters.
+//
+
+#include <windows.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "TestUtil.cc"
+
+static void writeChars(const wchar_t *text) {
+ wcslen(text);
+ const int len = wcslen(text);
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ text, len, &actual, NULL);
+ trace("writeChars: ret=%d, actual=%lld", ret, (long long)actual);
+}
+
+static void dumpChars(int x, int y, int w, int h) {
+ BOOL ret;
+ const COORD bufSize = {w, h};
+ const COORD bufCoord = {0, 0};
+ const SMALL_RECT topLeft = {x, y, x + w - 1, y + h - 1};
+ CHAR_INFO mbcsData[w * h];
+ CHAR_INFO unicodeData[w * h];
+ SMALL_RECT readRegion;
+ readRegion = topLeft;
+ ret = ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), unicodeData,
+ bufSize, bufCoord, &readRegion);
+ assert(ret);
+ readRegion = topLeft;
+ ret = ReadConsoleOutputA(GetStdHandle(STD_OUTPUT_HANDLE), mbcsData,
+ bufSize, bufCoord, &readRegion);
+ assert(ret);
+
+ printf("\n");
+ for (int i = 0; i < w * h; ++i) {
+ printf("(%02d,%02d) CHAR: %04x %4x -- %02x %4x\n",
+ x + i % w, y + i / w,
+ (unsigned short)unicodeData[i].Char.UnicodeChar,
+ (unsigned short)unicodeData[i].Attributes,
+ (unsigned char)mbcsData[i].Char.AsciiChar,
+ (unsigned short)mbcsData[i].Attributes);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ system("cls");
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 38);
+ setWindowPos(0, 0, 80, 38);
+
+ // Write text.
+ const wchar_t text1[] = {
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0x2014, // U+2014 (EM DASH)
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0xFF2D, // U+FF2D (FULLWIDTH LATIN CAPITAL LETTER M)
+ 0x30FC, // U+30FC (KATAKANA-HIRAGANA PROLONGED SOUND MARK)
+ 0x0031, // U+3031 (DIGIT ONE)
+ 0x2014, // U+2014 (EM DASH)
+ 0x0032, // U+0032 (DIGIT TWO)
+ 0x005C, // U+005C (REVERSE SOLIDUS)
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0
+ };
+ setCursorPos(0, 0);
+ writeChars(text1);
+
+ setCursorPos(78, 1);
+ writeChars(L"<>");
+
+ const wchar_t text2[] = {
+ 0x0032, // U+3032 (DIGIT TWO)
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0,
+ };
+ setCursorPos(78, 1);
+ writeChars(text2);
+
+ system("pause");
+
+ dumpChars(0, 0, 17, 1);
+ dumpChars(2, 0, 2, 1);
+ dumpChars(2, 0, 1, 1);
+ dumpChars(3, 0, 1, 1);
+ dumpChars(78, 1, 2, 1);
+ dumpChars(0, 2, 2, 1);
+
+ system("pause");
+ system("cls");
+
+ const wchar_t text3[] = {
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 1
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 2
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 3
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 4
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 5
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 6
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 7
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 8
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 9
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 10
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 11
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 12
+ L'\r', '\n',
+ L'\r', '\n',
+ 0
+ };
+ writeChars(text3);
+ system("pause");
+ {
+ const COORD bufSize = {80, 2};
+ const COORD bufCoord = {0, 0};
+ SMALL_RECT readRegion = {0, 0, 79, 1};
+ CHAR_INFO unicodeData[160];
+ BOOL ret = ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), unicodeData,
+ bufSize, bufCoord, &readRegion);
+ assert(ret);
+ for (int i = 0; i < 96; ++i) {
+ printf("%04x ", unicodeData[i].Char.UnicodeChar);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnixEcho.cc b/src/libs/3rdparty/winpty/misc/UnixEcho.cc
new file mode 100644
index 0000000000..372e045157
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnixEcho.cc
@@ -0,0 +1,89 @@
+/*
+ * Unix test code that puts the terminal into raw mode, then echos typed
+ * characters to stdout. Derived from sample code in the Stevens book, posted
+ * online at http://www.lafn.org/~dave/linux/terminalIO.html.
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "FormatChar.h"
+
+static struct termios save_termios;
+static int term_saved;
+
+/* RAW! mode */
+int tty_raw(int fd)
+{
+ struct termios buf;
+
+ if (tcgetattr(fd, &save_termios) < 0) /* get the original state */
+ return -1;
+
+ buf = save_termios;
+
+ /* echo off, canonical mode off, extended input
+ processing off, signal chars off */
+ buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+
+ /* no SIGINT on BREAK, CR-to-NL off, input parity
+ check off, don't strip the 8th bit on input,
+ ouput flow control off */
+ buf.c_iflag &= ~(BRKINT | ICRNL | ISTRIP | IXON);
+
+ /* clear size bits, parity checking off */
+ buf.c_cflag &= ~(CSIZE | PARENB);
+
+ /* set 8 bits/char */
+ buf.c_cflag |= CS8;
+
+ /* output processing off */
+ buf.c_oflag &= ~(OPOST);
+
+ buf.c_cc[VMIN] = 1; /* 1 byte at a time */
+ buf.c_cc[VTIME] = 0; /* no timer on input */
+
+ if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
+ return -1;
+
+ term_saved = 1;
+
+ return 0;
+}
+
+
+/* set it to normal! */
+int tty_reset(int fd)
+{
+ if (term_saved)
+ if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+int main()
+{
+ tty_raw(0);
+
+ int count = 0;
+ while (true) {
+ char ch;
+ char buf[16];
+ int actual = read(0, &ch, 1);
+ if (actual != 1) {
+ perror("read error");
+ break;
+ }
+ formatChar(buf, ch);
+ fputs(buf, stdout);
+ fflush(stdout);
+ if (ch == 3) // Ctrl-C
+ break;
+ }
+
+ tty_reset(0);
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Utf16Echo.cc b/src/libs/3rdparty/winpty/misc/Utf16Echo.cc
new file mode 100644
index 0000000000..ef5f302de4
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Utf16Echo.cc
@@ -0,0 +1,46 @@
+#include <windows.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <vector>
+#include <string>
+
+int main(int argc, char *argv[]) {
+ system("cls");
+
+ if (argc == 1) {
+ printf("Usage: %s hhhh\n", argv[0]);
+ return 0;
+ }
+
+ std::wstring dataToWrite;
+ for (int i = 1; i < argc; ++i) {
+ wchar_t ch = strtol(argv[i], NULL, 16);
+ dataToWrite.push_back(ch);
+ }
+
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ dataToWrite.data(), dataToWrite.size(), &actual, NULL);
+ assert(ret && actual == dataToWrite.size());
+
+ // Read it back.
+ std::vector<CHAR_INFO> readBuffer(dataToWrite.size() * 2);
+ COORD bufSize = {static_cast<short>(readBuffer.size()), 1};
+ COORD bufCoord = {0, 0};
+ SMALL_RECT topLeft = {0, 0, static_cast<short>(readBuffer.size() - 1), 0};
+ ret = ReadConsoleOutputW(
+ GetStdHandle(STD_OUTPUT_HANDLE), readBuffer.data(),
+ bufSize, bufCoord, &topLeft);
+ assert(ret);
+
+ printf("\n");
+ for (int i = 0; i < readBuffer.size(); ++i) {
+ printf("CHAR: %04x %04x\n",
+ readBuffer[i].Char.UnicodeChar,
+ readBuffer[i].Attributes);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc b/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc
new file mode 100644
index 0000000000..58f0897022
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc
@@ -0,0 +1,122 @@
+//
+// 2015-09-25
+// I measured these limits on the size of a single ReadConsoleOutputW call.
+// The limit seems to more-or-less disppear with Windows 8, which is the first
+// OS to stop using ALPCs for console I/O. My guess is that the new I/O
+// method does not use the 64KiB shared memory buffer that the ALPC method
+// uses.
+//
+// I'm guessing the remaining difference between Windows 8/8.1 and Windows 10
+// might be related to the 32-vs-64-bitness.
+//
+// Client OSs
+//
+// Windows XP 32-bit VM ==> up to 13304 characters
+// - 13304x1 works, but 13305x1 fails instantly
+// Windows 7 32-bit VM ==> between 16-17 thousand characters
+// - 16000x1 works, 17000x1 fails instantly
+// - 163x100 *crashes* conhost.exe but leaves VeryLargeRead.exe running
+// Windows 8 32-bit VM ==> between 240-250 million characters
+// - 10000x24000 works, but 10000x25000 does not
+// Windows 8.1 32-bit VM ==> between 240-250 million characters
+// - 10000x24000 works, but 10000x25000 does not
+// Windows 10 64-bit VM ==> no limit (tested to 576 million characters)
+// - 24000x24000 works
+// - `ver` reports [Version 10.0.10240], conhost.exe and ConhostV1.dll are
+// 10.0.10240.16384 for file and product version. ConhostV2.dll is
+// 10.0.10240.16391 for file and product version.
+//
+// Server OSs
+//
+// Windows Server 2008 64-bit VM ==> 14300-14400 characters
+// - 14300x1 works, 14400x1 fails instantly
+// - This OS does not have conhost.exe.
+// - `ver` reports [Version 6.0.6002]
+// Windows Server 2008 R2 64-bit VM ==> 15600-15700 characters
+// - 15600x1 works, 15700x1 fails instantly
+// - This OS has conhost.exe, and procexp.exe reveals console ALPC ports in
+// use in conhost.exe.
+// - `ver` reports [Version 6.1.7601], conhost.exe is 6.1.7601.23153 for file
+// and product version.
+// Windows Server 2012 64-bit VM ==> at least 100 million characters
+// - 10000x10000 works (VM had only 1GiB of RAM, so I skipped larger tests)
+// - This OS has Windows 8's task manager and procexp.exe reveals the same
+// lack of ALPC ports and the same \Device\ConDrv\* files as Windows 8.
+// - `ver` reports [Version 6.2.9200], conhost.exe is 6.2.9200.16579 for file
+// and product version.
+//
+// To summarize:
+//
+// client-OS server-OS notes
+// ---------------------------------------------------------------------------
+// XP Server 2008 CSRSS, small reads
+// 7 Server 2008 R2 ALPC-to-conhost, small reads
+// 8, 8.1 Server 2012 new I/O interface, large reads allowed
+// 10 <no server OS yet> enhanced console w/rewrapping
+//
+// (Presumably, Win2K, Vista, and Win2K3 behave the same as XP. conhost.exe
+// was announced as a Win7 feature.)
+//
+
+#include <windows.h>
+#include <assert.h>
+#include <vector>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ long long width = 9000;
+ long long height = 9000;
+
+ assert(argc >= 1);
+ if (argc == 4) {
+ width = atoi(argv[2]);
+ height = atoi(argv[3]);
+ } else {
+ if (argc == 3) {
+ width = atoi(argv[1]);
+ height = atoi(argv[2]);
+ }
+ wchar_t args[1024];
+ swprintf(args, 1024, L"CHILD %lld %lld", width, height);
+ startChildProcess(args);
+ return 0;
+ }
+
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(width, height);
+ setWindowPos(0, 0, std::min(80LL, width), std::min(50LL, height));
+
+ setCursorPos(0, 0);
+ printf("A");
+ fflush(stdout);
+ setCursorPos(width - 2, height - 1);
+ printf("B");
+ fflush(stdout);
+
+ trace("sizeof(CHAR_INFO) = %d", (int)sizeof(CHAR_INFO));
+
+ trace("Allocating buffer...");
+ CHAR_INFO *buffer = new CHAR_INFO[width * height];
+ assert(buffer != NULL);
+ memset(&buffer[0], 0, sizeof(CHAR_INFO));
+ memset(&buffer[width * height - 2], 0, sizeof(CHAR_INFO));
+
+ COORD bufSize = { width, height };
+ COORD bufCoord = { 0, 0 };
+ SMALL_RECT readRegion = { 0, 0, width - 1, height - 1 };
+ trace("ReadConsoleOutputW: calling...");
+ BOOL success = ReadConsoleOutputW(conout, buffer, bufSize, bufCoord, &readRegion);
+ trace("ReadConsoleOutputW: success=%d", success);
+
+ assert(buffer[0].Char.UnicodeChar == L'A');
+ assert(buffer[width * height - 2].Char.UnicodeChar == L'B');
+ trace("Top-left and bottom-right characters read successfully!");
+
+ Sleep(30000);
+
+ delete [] buffer;
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc b/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc
new file mode 100644
index 0000000000..97bf59f998
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc
@@ -0,0 +1,56 @@
+/*
+ * Sending VK_PAUSE to the console window almost works as a mechanism for
+ * pausing it, but it doesn't because the console could turn off the
+ * ENABLE_LINE_INPUT console mode flag.
+ */
+
+#define _WIN32_WINNT 0x0501
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+
+CALLBACK DWORD pausingThread(LPVOID dummy)
+{
+ if (1) {
+ Sleep(1000);
+ HWND hwnd = GetConsoleWindow();
+ SendMessage(hwnd, WM_KEYDOWN, VK_PAUSE, 1);
+ Sleep(1000);
+ SendMessage(hwnd, WM_KEYDOWN, VK_ESCAPE, 1);
+ }
+
+ if (0) {
+ INPUT_RECORD ir;
+ memset(&ir, 0, sizeof(ir));
+ ir.EventType = KEY_EVENT;
+ ir.Event.KeyEvent.bKeyDown = TRUE;
+ ir.Event.KeyEvent.wVirtualKeyCode = VK_PAUSE;
+ ir.Event.KeyEvent.wRepeatCount = 1;
+ }
+
+ return 0;
+}
+
+int main()
+{
+ HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
+ COORD c = { 0, 0 };
+
+ DWORD mode;
+ GetConsoleMode(hin, &mode);
+ SetConsoleMode(hin, mode &
+ ~(ENABLE_LINE_INPUT));
+
+ CreateThread(NULL, 0,
+ pausingThread, NULL,
+ 0, NULL);
+
+ int i = 0;
+ while (true) {
+ Sleep(100);
+ printf("%d\n", ++i);
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc b/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc
new file mode 100644
index 0000000000..82feaf3c50
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc
@@ -0,0 +1,52 @@
+/*
+ * Demonstrates a conhost hang that occurs when widening the console buffer
+ * while selection is in progress. The problem affects the new Windows 10
+ * console, not the "legacy" console mode that Windows 10 also includes.
+ *
+ * First tested with:
+ * - Windows 10.0.10240
+ * - conhost.exe version 10.0.10240.16384
+ * - ConhostV1.dll version 10.0.10240.16384
+ * - ConhostV2.dll version 10.0.10240.16391
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 25);
+ setWindowPos(0, 0, 80, 25);
+
+ countDown(5);
+
+ SendMessage(GetConsoleWindow(), WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
+ Sleep(2000);
+
+ // This API call does not return. In the console window, the "Select All"
+ // operation appears to end. The console window becomes non-responsive,
+ // and the conhost.exe process must be killed from the Task Manager.
+ // (Killing this test program or closing the console window is not
+ // sufficient.)
+ //
+ // The same hang occurs whether line resizing is off or on. It happens
+ // with both "Mark" and "Select All". Calling setBufferSize with the
+ // existing buffer size does not hang, but calling it with only a changed
+ // buffer height *does* hang. Calling setWindowPos does not hang.
+ setBufferSize(120, 25);
+
+ printf("Done...\n");
+ Sleep(2000);
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc b/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc
new file mode 100644
index 0000000000..645fa95d54
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc
@@ -0,0 +1,57 @@
+/*
+ * Demonstrates some wrapping behaviors of the new Windows 10 console.
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(40, 20);
+ setWindowPos(0, 0, 40, 20);
+
+ system("cls");
+
+ repeatChar(39, 'A'); repeatChar(1, ' ');
+ repeatChar(39, 'B'); repeatChar(1, ' ');
+ printf("\n");
+
+ repeatChar(39, 'C'); repeatChar(1, ' ');
+ repeatChar(39, 'D'); repeatChar(1, ' ');
+ printf("\n");
+
+ repeatChar(40, 'E');
+ repeatChar(40, 'F');
+ printf("\n");
+
+ repeatChar(39, 'G'); repeatChar(1, ' ');
+ repeatChar(39, 'H'); repeatChar(1, ' ');
+ printf("\n");
+
+ Sleep(2000);
+
+ setChar(39, 0, '*', 0x24);
+ setChar(39, 1, '*', 0x24);
+
+ setChar(39, 3, ' ', 0x24);
+ setChar(39, 4, ' ', 0x24);
+
+ setChar(38, 6, ' ', 0x24);
+ setChar(38, 7, ' ', 0x24);
+
+ Sleep(2000);
+ setWindowPos(0, 0, 35, 20);
+ setBufferSize(35, 20);
+ trace("DONE");
+
+ printf("Sleeping forever...\n");
+ while(true) { Sleep(1000); }
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc b/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc
new file mode 100644
index 0000000000..50615fc8c7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc
@@ -0,0 +1,30 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ const int WIDTH = 25;
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(WIDTH, 40);
+ setWindowPos(0, 0, WIDTH, 20);
+
+ system("cls");
+
+ for (int i = 0; i < 100; ++i) {
+ printf("FOO(%d)\n", i);
+ }
+
+ repeatChar(5, '\n');
+ repeatChar(WIDTH * 5, '.');
+ repeatChar(10, '\n');
+ setWindowPos(0, 20, WIDTH, 20);
+ writeBox(0, 5, 1, 10, '|');
+
+ Sleep(120000);
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Echo1.cc b/src/libs/3rdparty/winpty/misc/Win32Echo1.cc
new file mode 100644
index 0000000000..06fc79f794
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Echo1.cc
@@ -0,0 +1,26 @@
+/*
+ * A Win32 program that reads raw console input with ReadFile and echos
+ * it to stdout.
+ */
+
+#include <stdio.h>
+#include <conio.h>
+#include <windows.h>
+
+int main()
+{
+ int count = 0;
+ HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleMode(hStdIn, 0);
+
+ while (true) {
+ DWORD actual;
+ char ch;
+ ReadFile(hStdIn, &ch, 1, &actual, NULL);
+ printf("%02x ", ch);
+ if (++count == 50)
+ break;
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Echo2.cc b/src/libs/3rdparty/winpty/misc/Win32Echo2.cc
new file mode 100644
index 0000000000..b2ea2ad1c5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Echo2.cc
@@ -0,0 +1,19 @@
+/*
+ * A Win32 program that reads raw console input with getch and echos
+ * it to stdout.
+ */
+
+#include <stdio.h>
+#include <conio.h>
+
+int main()
+{
+ int count = 0;
+ while (true) {
+ int ch = getch();
+ printf("%02x ", ch);
+ if (++count == 50)
+ break;
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Test1.cc b/src/libs/3rdparty/winpty/misc/Win32Test1.cc
new file mode 100644
index 0000000000..a40d318a98
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Test1.cc
@@ -0,0 +1,46 @@
+#define _WIN32_WINNT 0x0501
+#include "../src/shared/DebugClient.cc"
+#include <windows.h>
+#include <stdio.h>
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+
+CALLBACK DWORD writerThread(void*)
+{
+ while (true) {
+ Sleep(1000);
+ trace("writing");
+ printf("X\n");
+ trace("written");
+ }
+}
+
+int main()
+{
+ CreateThread(NULL, 0, writerThread, NULL, 0, NULL);
+ trace("marking console");
+ HWND hwnd = GetConsoleWindow();
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+
+ Sleep(2000);
+
+ trace("reading output");
+ CHAR_INFO buf[1];
+ COORD bufSize = { 1, 1 };
+ COORD zeroCoord = { 0, 0 };
+ SMALL_RECT readRect = { 0, 0, 0, 0 };
+ ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
+ buf,
+ bufSize,
+ zeroCoord,
+ &readRect);
+ trace("done reading output");
+
+ Sleep(2000);
+
+ PostMessage(hwnd, WM_CHAR, 27, 0x00010001);
+
+ Sleep(1100);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Test2.cc b/src/libs/3rdparty/winpty/misc/Win32Test2.cc
new file mode 100644
index 0000000000..2777bad456
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Test2.cc
@@ -0,0 +1,70 @@
+/*
+ * This test demonstrates that putting a console into selection mode does not
+ * block the low-level console APIs, even though it blocks WriteFile.
+ */
+
+#define _WIN32_WINNT 0x0501
+#include "../src/shared/DebugClient.cc"
+#include <windows.h>
+#include <stdio.h>
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+
+CALLBACK DWORD writerThread(void*)
+{
+ CHAR_INFO xChar, fillChar;
+ memset(&xChar, 0, sizeof(xChar));
+ xChar.Char.AsciiChar = 'X';
+ xChar.Attributes = 7;
+ memset(&fillChar, 0, sizeof(fillChar));
+ fillChar.Char.AsciiChar = ' ';
+ fillChar.Attributes = 7;
+ COORD oneCoord = { 1, 1 };
+ COORD zeroCoord = { 0, 0 };
+
+ while (true) {
+ SMALL_RECT writeRegion = { 5, 5, 5, 5 };
+ WriteConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
+ &xChar, oneCoord,
+ zeroCoord,
+ &writeRegion);
+ Sleep(500);
+ SMALL_RECT scrollRect = { 1, 1, 20, 20 };
+ COORD destCoord = { 0, 0 };
+ ScrollConsoleScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE),
+ &scrollRect,
+ NULL,
+ destCoord,
+ &fillChar);
+ }
+}
+
+int main()
+{
+ CreateThread(NULL, 0, writerThread, NULL, 0, NULL);
+ trace("marking console");
+ HWND hwnd = GetConsoleWindow();
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+
+ Sleep(2000);
+
+ trace("reading output");
+ CHAR_INFO buf[1];
+ COORD bufSize = { 1, 1 };
+ COORD zeroCoord = { 0, 0 };
+ SMALL_RECT readRect = { 0, 0, 0, 0 };
+ ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
+ buf,
+ bufSize,
+ zeroCoord,
+ &readRect);
+ trace("done reading output");
+
+ Sleep(2000);
+
+ PostMessage(hwnd, WM_CHAR, 27, 0x00010001);
+
+ Sleep(1100);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Test3.cc b/src/libs/3rdparty/winpty/misc/Win32Test3.cc
new file mode 100644
index 0000000000..1fb92aff3d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Test3.cc
@@ -0,0 +1,78 @@
+/*
+ * Creates a window station and starts a process under it. The new process
+ * also gets a new console.
+ */
+
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+
+int main()
+{
+ BOOL success;
+
+ SECURITY_ATTRIBUTES sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.bInheritHandle = TRUE;
+
+ HWINSTA originalStation = GetProcessWindowStation();
+ printf("originalStation == 0x%x\n", originalStation);
+ HWINSTA station = CreateWindowStation(NULL,
+ 0,
+ WINSTA_ALL_ACCESS,
+ &sa);
+ printf("station == 0x%x\n", station);
+ if (!SetProcessWindowStation(station))
+ printf("SetWindowStation failed!\n");
+ HDESK desktop = CreateDesktop("Default", NULL, NULL,
+ /*dwFlags=*/0, GENERIC_ALL,
+ &sa);
+ printf("desktop = 0x%x\n", desktop);
+
+ char stationName[256];
+ stationName[0] = '\0';
+ success = GetUserObjectInformation(station, UOI_NAME,
+ stationName, sizeof(stationName),
+ NULL);
+ printf("stationName = [%s]\n", stationName);
+
+ char startupDesktop[256];
+ sprintf(startupDesktop, "%s\\Default", stationName);
+
+ STARTUPINFO sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(STARTUPINFO);
+ sui.lpDesktop = startupDesktop;
+
+ // Start a cmd subprocess, and have it start its own cmd subprocess.
+ // Both subprocesses will connect to the same non-interactive window
+ // station.
+
+ const char program[] = "c:\\windows\\system32\\cmd.exe";
+ char cmdline[256];
+ sprintf(cmdline, "%s /c cmd", program);
+ success = CreateProcess(program,
+ cmdline,
+ NULL,
+ NULL,
+ /*bInheritHandles=*/FALSE,
+ /*dwCreationFlags=*/CREATE_NEW_CONSOLE,
+ NULL, NULL,
+ &sui,
+ &pi);
+
+ printf("pid == %d\n", pi.dwProcessId);
+
+ // This sleep is necessary. We must give the child enough time to
+ // connect to the specified window station.
+ Sleep(5000);
+
+ SetProcessWindowStation(originalStation);
+ CloseWindowStation(station);
+ CloseDesktop(desktop);
+ Sleep(5000);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Write1.cc b/src/libs/3rdparty/winpty/misc/Win32Write1.cc
new file mode 100644
index 0000000000..6e5bf96682
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Write1.cc
@@ -0,0 +1,44 @@
+/*
+ * A Win32 program that scrolls and writes to the console using the ioctl-like
+ * interface.
+ */
+
+#include <stdio.h>
+#include <windows.h>
+
+int main()
+{
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ for (int i = 0; i < 80; ++i) {
+
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo(conout, &info);
+
+ SMALL_RECT src = { 0, 1, info.dwSize.X - 1, info.dwSize.Y - 1 };
+ COORD destOrigin = { 0, 0 };
+ CHAR_INFO fillCharInfo = { 0 };
+ fillCharInfo.Char.AsciiChar = ' ';
+ fillCharInfo.Attributes = 7;
+ ScrollConsoleScreenBuffer(conout,
+ &src,
+ NULL,
+ destOrigin,
+ &fillCharInfo);
+
+ CHAR_INFO buffer = { 0 };
+ buffer.Char.AsciiChar = 'X';
+ buffer.Attributes = 7;
+ COORD bufferSize = { 1, 1 };
+ COORD bufferCoord = { 0, 0 };
+ SMALL_RECT writeRegion = { 0, 0, 0, 0 };
+ writeRegion.Left = writeRegion.Right = i;
+ writeRegion.Top = writeRegion.Bottom = 5;
+ WriteConsoleOutput(conout,
+ &buffer, bufferSize, bufferCoord,
+ &writeRegion);
+
+ Sleep(250);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc b/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc
new file mode 100644
index 0000000000..e6d9558df6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc
@@ -0,0 +1,27 @@
+// I noticed this on the ConEmu web site:
+//
+// https://social.msdn.microsoft.com/Forums/en-US/40c8e395-cca9-45c8-b9b8-2fbe6782ac2b/readconsoleoutput-cause-access-violation-writing-location-exception
+// https://conemu.github.io/en/MicrosoftBugs.html
+//
+// In Windows 7, 8, and 8.1, a ReadConsoleOutputW with an out-of-bounds read
+// region crashes the application. I have reproduced the problem on Windows 8
+// and 8.1, but not on Windows 7.
+//
+
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main() {
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 25);
+ setWindowPos(0, 0, 80, 25);
+
+ const HANDLE conout = openConout();
+ static CHAR_INFO lineBuf[80];
+ SMALL_RECT readRegion = { 0, 999, 79, 999 };
+ const BOOL ret = ReadConsoleOutputW(conout, lineBuf, {80, 1}, {0, 0}, &readRegion);
+ ASSERT(!ret && "ReadConsoleOutputW should have failed");
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/WriteConsole.cc b/src/libs/3rdparty/winpty/misc/WriteConsole.cc
new file mode 100644
index 0000000000..a03670ca92
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/WriteConsole.cc
@@ -0,0 +1,106 @@
+#include <windows.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+static std::wstring mbsToWcs(const std::string &s) {
+ const size_t len = mbstowcs(nullptr, s.c_str(), 0);
+ if (len == static_cast<size_t>(-1)) {
+ assert(false && "mbsToWcs: invalid string");
+ }
+ std::wstring ret;
+ ret.resize(len);
+ const size_t len2 = mbstowcs(&ret[0], s.c_str(), len);
+ assert(len == len2);
+ return ret;
+}
+
+uint32_t parseHex(wchar_t ch, bool &invalid) {
+ if (ch >= L'0' && ch <= L'9') {
+ return ch - L'0';
+ } else if (ch >= L'a' && ch <= L'f') {
+ return ch - L'a' + 10;
+ } else if (ch >= L'A' && ch <= L'F') {
+ return ch - L'A' + 10;
+ } else {
+ invalid = true;
+ return 0;
+ }
+}
+
+int main(int argc, char *argv[]) {
+ std::vector<std::wstring> args;
+ for (int i = 1; i < argc; ++i) {
+ args.push_back(mbsToWcs(argv[i]));
+ }
+
+ std::wstring out;
+ for (const auto &arg : args) {
+ if (!out.empty()) {
+ out.push_back(L' ');
+ }
+ for (size_t i = 0; i < arg.size(); ++i) {
+ wchar_t ch = arg[i];
+ wchar_t nch = i + 1 < arg.size() ? arg[i + 1] : L'\0';
+ if (ch == L'\\') {
+ switch (nch) {
+ case L'a': ch = L'\a'; ++i; break;
+ case L'b': ch = L'\b'; ++i; break;
+ case L'e': ch = L'\x1b'; ++i; break;
+ case L'f': ch = L'\f'; ++i; break;
+ case L'n': ch = L'\n'; ++i; break;
+ case L'r': ch = L'\r'; ++i; break;
+ case L't': ch = L'\t'; ++i; break;
+ case L'v': ch = L'\v'; ++i; break;
+ case L'\\': ch = L'\\'; ++i; break;
+ case L'\'': ch = L'\''; ++i; break;
+ case L'\"': ch = L'\"'; ++i; break;
+ case L'\?': ch = L'\?'; ++i; break;
+ case L'x':
+ if (i + 3 < arg.size()) {
+ bool invalid = false;
+ uint32_t d1 = parseHex(arg[i + 2], invalid);
+ uint32_t d2 = parseHex(arg[i + 3], invalid);
+ if (!invalid) {
+ i += 3;
+ ch = (d1 << 4) | d2;
+ }
+ }
+ break;
+ case L'u':
+ if (i + 5 < arg.size()) {
+ bool invalid = false;
+ uint32_t d1 = parseHex(arg[i + 2], invalid);
+ uint32_t d2 = parseHex(arg[i + 3], invalid);
+ uint32_t d3 = parseHex(arg[i + 4], invalid);
+ uint32_t d4 = parseHex(arg[i + 5], invalid);
+ if (!invalid) {
+ i += 5;
+ ch = (d1 << 24) | (d2 << 16) | (d3 << 8) | d4;
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ out.push_back(ch);
+ }
+ }
+
+ DWORD actual = 0;
+ if (!WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ out.c_str(),
+ out.size(),
+ &actual,
+ nullptr)) {
+ fprintf(stderr, "WriteConsole failed (is stdout a console?)\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/build32.sh b/src/libs/3rdparty/winpty/misc/build32.sh
new file mode 100644
index 0000000000..162993ce33
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/build32.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -e
+name=$1
+name=${name%.}
+name=${name%.cc}
+name=${name%.exe}
+echo Compiling $name.cc to $name.exe
+i686-w64-mingw32-g++.exe -static -std=c++11 $name.cc -o $name.exe
+i686-w64-mingw32-strip $name.exe
diff --git a/src/libs/3rdparty/winpty/misc/build64.sh b/src/libs/3rdparty/winpty/misc/build64.sh
new file mode 100644
index 0000000000..6757967684
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/build64.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -e
+name=$1
+name=${name%.}
+name=${name%.cc}
+name=${name%.exe}
+echo Compiling $name.cc to $name.exe
+x86_64-w64-mingw32-g++.exe -static -std=c++11 $name.cc -o $name.exe
+x86_64-w64-mingw32-strip $name.exe
diff --git a/src/libs/3rdparty/winpty/misc/color-test.sh b/src/libs/3rdparty/winpty/misc/color-test.sh
new file mode 100644
index 0000000000..065c8094e2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/color-test.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+
+FORE=$1
+BACK=$2
+FILL=$3
+
+if [ "$FORE" = "" ]; then
+ FORE=DefaultFore
+fi
+if [ "$BACK" = "" ]; then
+ BACK=DefaultBack
+fi
+
+# To detect color changes, we want a character that fills the whole cell
+# if possible. U+2588 is perfect, except that it becomes invisible in the
+# original xterm, when bolded. For that terminal, use something else, like
+# "#" or "@".
+if [ "$FILL" = "" ]; then
+ FILL="█"
+fi
+
+# SGR (Select Graphic Rendition)
+s() {
+ printf '\033[0m'
+ while [ "$1" != "" ]; do
+ printf '\033['"$1"'m'
+ shift
+ done
+}
+
+# Print
+p() {
+ echo -n "$@"
+}
+
+# Print with newline
+pn() {
+ echo "$@"
+}
+
+# For practical reasons, sandwich black and white in-between the other colors.
+FORE_COLORS="31 30 37 32 33 34 35 36"
+BACK_COLORS="41 40 47 42 43 44 45 46"
+
+
+
+### Test order of Invert(7) -- it does not matter what order it appears in.
+
+# The Red color setting here (31) is shadowed by the green setting (32). The
+# Reverse flag does not cause (32) to alter the background color immediately;
+# instead, the Reverse flag is applied once to determine the final effective
+# Fore/Back colors.
+s 7 31 32; p " -- Should be: $BACK-on-green -- "; s; pn
+s 31 7 32; p " -- Should be: $BACK-on-green -- "; s; pn
+s 31 32 7; p " -- Should be: $BACK-on-green -- "; s; pn
+
+# As above, but for the background color.
+s 7 41 42; p " -- Should be: green-on-$FORE -- "; s; pn
+s 41 7 42; p " -- Should be: green-on-$FORE -- "; s; pn
+s 41 42 7; p " -- Should be: green-on-$FORE -- "; s; pn
+
+# One last, related test
+s 7; p "Invert text"; s 7 1; p " with some words bold"; s; pn;
+s 0; p "Normal text"; s 0 1; p " with some words bold"; s; pn;
+
+pn
+
+
+
+### Test effect of Bold(1) on color, with and without Invert(7).
+
+# The Bold flag does not affect the background color when Reverse is missing.
+# There should always be 8 colored boxes.
+p " "
+for x in $BACK_COLORS; do
+ s $x; p "-"; s $x 1; p "-"
+done
+s; pn " Bold should not affect background"
+
+# On some terminals, Bold affects color, and on some it doesn't. If there
+# are only 8 colored boxes, then the next two tests will also show 8 colored
+# boxes. If there are 16 boxes, then exactly one of the next two tests will
+# also have 16 boxes.
+p " "
+for x in $FORE_COLORS; do
+ s $x; p "$FILL"; s $x 1; p "$FILL"
+done
+s; pn " Does bold affect foreground color?"
+
+# On some terminals, Bold+Invert highlights the final Background color.
+p " "
+for x in $FORE_COLORS; do
+ s $x 7; p "-"; s $x 7 1; p "-"
+done
+s; pn " Test if Bold+Invert affects background color"
+
+# On some terminals, Bold+Invert highlights the final Foreground color.
+p " "
+for x in $BACK_COLORS; do
+ s $x 7; p "$FILL"; s $x 7 1; p "$FILL"
+done
+s; pn " Test if Bold+Invert affects foreground color"
+
+pn
+
+
+
+### Test for support of ForeHi and BackHi properties.
+
+# ForeHi
+p " "
+for x in $FORE_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "$FILL"; s $hi; p "$FILL"
+done
+s; pn " Test for support of ForeHi colors"
+p " "
+for x in $FORE_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "$FILL"; s $x $hi; p "$FILL"
+done
+s; pn " Test for support of ForeHi colors (w/compat)"
+
+# BackHi
+p " "
+for x in $BACK_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "-"; s $hi; p "-"
+done
+s; pn " Test for support of BackHi colors"
+p " "
+for x in $BACK_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "-"; s $x $hi; p "-"
+done
+s; pn " Test for support of BackHi colors (w/compat)"
+
+pn
+
+
+
+### Identify the default fore and back colors.
+
+pn "Match default fore and back colors against 16-color palette"
+pn " ==fore== ==back=="
+for fore in $FORE_COLORS; do
+ forehi=$(( $fore + 60 ))
+ back=$(( $fore + 10 ))
+ backhi=$(( $back + 60 ))
+ p " "
+ s $fore; p "$FILL"; s; p "$FILL"; s $fore; p "$FILL"; s; p " "
+ s $forehi; p "$FILL"; s; p "$FILL"; s $forehi; p "$FILL"; s; p " "
+ s $back; p "-"; s; p "-"; s $back; p "-"; s; p " "
+ s $backhi; p "-"; s; p "-"; s $backhi; p "-"; s; p " "
+ pn " $fore $forehi $back $backhi"
+done
+
+pn
+
+
+
+### Test coloring of rest-of-line.
+
+#
+# When a new line is scrolled in, every cell in the line receives the
+# current background color, which can be the default/transparent color.
+#
+
+p "Newline with red background: usually no red -->"; s 41; pn
+s; pn "This text is plain, but rest is red if scrolled -->"
+s; p " "; s 41; printf '\033[1K'; s; printf '\033[1C'; pn "<-- red Erase-in-Line to beginning"
+s; p "red Erase-in-Line to end -->"; s 41; printf '\033[0K'; s; pn
+pn
+
+
+
+### Moving the cursor around does not change colors of anything.
+
+pn "Test modifying uncolored lines with a colored SGR:"
+pn "aaaa"
+pn
+pn "____e"
+s 31 42; printf '\033[4C\033[3A'; pn "bb"
+pn "cccc"
+pn "dddd"
+s; pn
+
+pn "Test modifying colored+inverted+bold line with plain text:"
+s 42 31 7 1; printf 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r';
+s; pn "This text is plain and followed by green-on-red -->"
+pn
+
+
+
+### Full-width character overwriting
+
+pn 'Overwrite part of a full-width char with a half-width char'
+p 'initial U+4000 ideographs -->'; s 31 42; p '䀀䀀'; s; pn
+p 'write X to index #1 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[24G'; p X; s; pn
+p 'write X to index #2 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[25G'; p X; s; pn
+p 'write X to index #3 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[26G'; p X; s; pn
+p 'write X to index #4 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[27G'; p X; s; pn
+pn
+
+pn 'Verify that Erase-in-Line can "fix" last char in line'
+p 'original -->'; s 31 42; p '䀀䀀'; s; pn
+p 'overwrite -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[30G'; p 'XXX'; s; pn
+p 'overwrite + Erase-in-Line -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[30G'; p 'XXX'; s; printf '\033[0K'; pn
+p 'original -->'; s 31 42; p 'X䀀䀀'; s; pn
+p 'overwrite -->'; s 31 42; p 'X䀀䀀'; s 35 44; printf '\033[30G'; p 'ーー'; s; pn
+p 'overwrite + Erase-in-Line -->'; s 31 42; p 'X䀀䀀'; s 35 44; printf '\033[30G'; p 'ーー'; s; printf '\033[0K'; pn
+pn
diff --git a/src/libs/3rdparty/winpty/misc/font-notes.txt b/src/libs/3rdparty/winpty/misc/font-notes.txt
new file mode 100644
index 0000000000..d4e36d8e25
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/font-notes.txt
@@ -0,0 +1,300 @@
+==================================================================
+Notes regarding fonts, code pages, and East Asian character widths
+==================================================================
+
+
+Registry settings
+=================
+
+ * There are console registry settings in `HKCU\Console`. That key has many
+ default settings (e.g. the default font settings) and also per-app subkeys
+ for app-specific overrides.
+
+ * It is possible to override the code page with an app-specific setting.
+
+ * There are registry settings in
+ `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console`. In particular,
+ the `TrueTypeFont` subkey has a list of suitable font names associated with
+ various CJK code pages, as well as default font names.
+
+ * There are two values in `HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage`
+ that specify the current code pages -- `OEMCP` and `ACP`. Setting the
+ system locale via the Control Panel's "Region" or "Language" dialogs seems
+ to change these code page values.
+
+
+Console fonts
+=============
+
+ * The `FontFamily` field of `CONSOLE_FONT_INFOEX` has two parts:
+ - The high four bits can be exactly one of the `FF_xxxx` font families:
+ FF_DONTCARE(0x00)
+ FF_ROMAN(0x10)
+ FF_SWISS(0x20)
+ FF_MODERN(0x30)
+ FF_SCRIPT(0x40)
+ FF_DECORATIVE(0x50)
+ - The low four bits are a bitmask:
+ TMPF_FIXED_PITCH(1) -- actually means variable pitch
+ TMPF_VECTOR(2)
+ TMPF_TRUETYPE(4)
+ TMPF_DEVICE(8)
+
+ * Each console has its own independent console font table. The current font
+ is identified with an index into this table. The size of the table is
+ returned by the undocumented `GetNumberOfConsoleFonts` API. It is apparently
+ possible to get the table size without this API, by instead calling
+ `GetConsoleFontSize` on each nonnegative index starting with 0 until the API
+ fails by returning (0, 0).
+
+ * The font table grows dynamically. Each time the console is configured with
+ a previously-unused (FaceName, Size) combination, two entries are added to
+ the font table -- one with normal weight and one with bold weight. Fonts
+ added this way are always TrueType fonts.
+
+ * Initially, the font table appears to contain only raster fonts. For
+ example, on an English Windows 8 installation, here is the initial font
+ table:
+ font 0: 4x6
+ font 1: 6x8
+ font 2: 8x8
+ font 3: 16x8
+ font 4: 5x12
+ font 5: 7x12
+ font 6: 8x12 -- the current font
+ font 7: 16x12
+ font 8: 12x16
+ font 9: 10x18
+ `GetNumberOfConsoleFonts` returns 10, and this table matches the raster font
+ sizes according to the console properties dialog.
+
+ * With a Japanese or Chinese locale, the initial font table appears to contain
+ the sizes applicable to both the East Asian raster font, as well as the
+ sizes for the CP437/CP1252 raster font.
+
+ * The index passed to `SetCurrentConsoleFontEx` apparently has no effect.
+ The undocumented `SetConsoleFont` API, however, accepts *only* a font index,
+ and on Windows 8 English, it switches between all 10 fonts, even font index
+ #0.
+
+ * If the index passed to `SetConsoleFont` identifies a Raster Font
+ incompatible with the current code page, then another Raster Font is
+ activated.
+
+ * Passing "Terminal" to `SetCurrentConsoleFontEx` seems to have no effect.
+ Perhaps relatedly, `SetCurrentConsoleFontEx` does not fail if it is given a
+ bogus `FaceName`. Some font is still chosen and activated. Passing a face
+ name and height seems to work reliably, modulo the CP936 issue described
+ below.
+
+
+Console fonts and code pages
+============================
+
+ * On an English Windows installation, the default code page is 437, and it
+ cannot be set to 932 (Shift-JIS). (The API call fails.) Changing the
+ system locale to "Japanese (Japan)" using the Region/Language dialog
+ changes the default CP to 932 and permits changing the console CP between
+ 437 and 932.
+
+ * A console has both an input code page and an output code page
+ (`{Get,Set}ConsoleCP` and `{Get,Set}ConsoleOutputCP`). I'm not going to
+ distinguish between the two for this document; presumably only the output
+ CP matters. The code page can change while the console is open, e.g.
+ by running `mode con: cp select={932,437,1252}` or by calling
+ `SetConsoleOutputCP`.
+
+ * The current code page restricts which TrueType fonts and which Raster Font
+ sizes are available in the console properties dialog. This can change
+ while the console is open.
+
+ * Changing the code page almost(?) always changes the current console font.
+ So far, I don't know how the new font is chosen.
+
+ * With a CP of 932, the only TrueType font available in the console properties
+ dialog is "MS Gothic", displayed as "MS ゴシック". It is still possible to
+ use the English-default TrueType console fonts, Lucida Console and Consolas,
+ via `SetCurrentConsoleFontEx`.
+
+ * When using a Raster Font and CP437 or CP1252, writing a UTF-16 codepoint not
+ representable in the code page instead writes a question mark ('?') to the
+ console. This conversion does not apply with a TrueType font, nor with the
+ Raster Font for CP932 or CP936.
+
+
+ReadConsoleOutput and double-width characters
+==============================================
+
+ * With a Raster Font active, when `ReadConsoleOutputW` reads two cells of a
+ double-width character, it fills only a single `CHAR_INFO` structure. The
+ unused trailing `CHAR_INFO` structures are zero-filled. With a TrueType
+ font active, `ReadConsoleOutputW` instead fills two `CHAR_INFO` structures,
+ the first marked with `COMMON_LVB_LEADING_BYTE` and the second marked with
+ `COMMON_LVB_TRAILING_BYTE`. The flag is a misnomer--there aren't two
+ *bytes*, but two cells, and they have equal `CHAR_INFO.Char.UnicodeChar`
+ values.
+
+ * `ReadConsoleOutputA`, on the other hand, reads two `CHAR_INFO` cells, and
+ if the UTF-16 value can be represented as two bytes in the ANSI/OEM CP, then
+ the two bytes are placed in the two `CHAR_INFO.Char.AsciiChar` values, and
+ the `COMMON_LVB_{LEADING,TRAILING}_BYTE` values are also used. If the
+ codepoint isn't representable, I don't remember what happens -- I think the
+ `AsciiChar` values take on an invalid marker.
+
+ * Reading only one cell of a double-width character reads a space (U+0020)
+ instead. Raster-vs-TrueType and wide-vs-ANSI do not matter.
+ - XXX: what about attributes? Can a double-width character have mismatched
+ color attributes?
+ - XXX: what happens when writing to just one cell of a double-width
+ character?
+
+
+Default Windows fonts for East Asian languages
+==============================================
+CP932 / Japanese: "MS ゴシック" (MS Gothic)
+CP936 / Chinese Simplified: "新宋体" (SimSun)
+
+
+Unreliable character width (half-width vs full-width)
+=====================================================
+
+The half-width vs full-width status of a codepoint depends on at least these variables:
+ * OS version (Win10 legacy and new modes are different versions)
+ * system locale (English vs Japanese vs Chinese Simplified vs Chinese Traditional, etc)
+ * code page (437 vs 932 vs 936, etc)
+ * raster vs TrueType (Terminal vs MS Gothic vs SimSun, etc)
+ * font size
+ * rendered-vs-model (rendered width can be larger or smaller than model width)
+
+Example 1: U+2014 (EM DASH): East_Asian_Width: Ambiguous
+--------------------------------------------------------
+ rendered modeled
+CP932: Win7/8 Raster Fonts half half
+CP932: Win7/8 Gothic 14/15px half full
+CP932: Win7/8 Consolas 14/15px half full
+CP932: Win7/8 Lucida Console 14px half full
+CP932: Win7/8 Lucida Console 15px half half
+CP932: Win10New Raster Fonts half half
+CP932: Win10New Gothic 14/15px half half
+CP932: Win10New Consolas 14/15px half half
+CP932: Win10New Lucida Console 14/15px half half
+
+CP936: Win7/8 Raster Fonts full full
+CP936: Win7/8 SimSun 14px full full
+CP936: Win7/8 SimSun 15px full half
+CP936: Win7/8 Consolas 14/15px half full
+CP936: Win10New Raster Fonts full full
+CP936: Win10New SimSum 14/15px full full
+CP936: Win10New Consolas 14/15px half half
+
+Example 2: U+3044 (HIRAGANA LETTER I): East_Asian_Width: Wide
+-------------------------------------------------------------
+ rendered modeled
+CP932: Win7/8/10N Raster Fonts full full
+CP932: Win7/8/10N Gothic 14/15px full full
+CP932: Win7/8/10N Consolas 14/15px half(*2) full
+CP932: Win7/8/10N Lucida Console 14/15px half(*3) full
+
+CP936: Win7/8/10N Raster Fonts full full
+CP936: Win7/8/10N SimSun 14/15px full full
+CP936: Win7/8/10N Consolas 14/15px full full
+
+Example 3: U+30FC (KATAKANA-HIRAGANA PROLONGED SOUND MARK): East_Asian_Width: Wide
+----------------------------------------------------------------------------------
+ rendered modeled
+CP932: Win7 Raster Fonts full full
+CP932: Win7 Gothic 14/15px full full
+CP932: Win7 Consolas 14/15px half(*2) full
+CP932: Win7 Lucida Console 14px half(*3) full
+CP932: Win7 Lucida Console 15px half(*3) half
+CP932: Win8 Raster Fonts full full
+CP932: Win8 Gothic 14px full half
+CP932: Win8 Gothic 15px full full
+CP932: Win8 Consolas 14/15px half(*2) full
+CP932: Win8 Lucida Console 14px half(*3) full
+CP932: Win8 Lucida Console 15px half(*3) half
+CP932: Win10New Raster Fonts full full
+CP932: Win10New Gothic 14/15px full full
+CP932: Win10New Consolas 14/15px half(*2) half
+CP932: Win10New Lucida Console 14/15px half(*2) half
+
+CP936: Win7/8 Raster Fonts full full
+CP936: Win7/8 SimSun 14px full full
+CP936: Win7/8 SimSun 15px full half
+CP936: Win7/8 Consolas 14px full full
+CP936: Win7/8 Consolas 15px full half
+CP936: Win10New Raster Fonts full full
+CP936: Win10New SimSum 14/15px full full
+CP936: Win10New Consolas 14/15px full full
+
+Example 4: U+4000 (CJK UNIFIED IDEOGRAPH-4000): East_Asian_Width: Wide
+----------------------------------------------------------------------
+ rendered modeled
+CP932: Win7 Raster Fonts half(*1) half
+CP932: Win7 Gothic 14/15px full full
+CP932: Win7 Consolas 14/15px half(*2) full
+CP932: Win7 Lucida Console 14px half(*3) full
+CP932: Win7 Lucida Console 15px half(*3) half
+CP932: Win8 Raster Fonts half(*1) half
+CP932: Win8 Gothic 14px full half
+CP932: Win8 Gothic 15px full full
+CP932: Win8 Consolas 14/15px half(*2) full
+CP932: Win8 Lucida Console 14px half(*3) full
+CP932: Win8 Lucida Console 15px half(*3) half
+CP932: Win10New Raster Fonts half(*1) half
+CP932: Win10New Gothic 14/15px full full
+CP932: Win10New Consolas 14/15px half(*2) half
+CP932: Win10New Lucida Console 14/15px half(*2) half
+
+CP936: Win7/8 Raster Fonts full full
+CP936: Win7/8 SimSun 14px full full
+CP936: Win7/8 SimSun 15px full half
+CP936: Win7/8 Consolas 14px full full
+CP936: Win7/8 Consolas 15px full half
+CP936: Win10New Raster Fonts full full
+CP936: Win10New SimSum 14/15px full full
+CP936: Win10New Consolas 14/15px full full
+
+(*1) Rendered as a half-width filled white box
+(*2) Rendered as a half-width box with a question mark inside
+(*3) Rendered as a half-width empty box
+(!!) One of the only places in Win10New where rendered and modeled width disagree
+
+
+Windows quirk: unreliable font heights with CP936 / Chinese Simplified
+======================================================================
+
+When I set the font to 新宋体 17px, using either the properties dialog or
+`SetCurrentConsoleFontEx`, the height reported by `GetCurrentConsoleFontEx` is
+not 17, but is instead 19. The same problem does not affect Raster Fonts,
+nor have I seen the problem in the English or Japanese locales. I observed
+this with Windows 7 and Windows 10 new mode.
+
+If I set the font using the facename, width, *and* height, then the
+`SetCurrentConsoleFontEx` and `GetCurrentConsoleFontEx` values agree. If I
+set the font using *only* the facename and height, then the two values
+disagree.
+
+
+Windows bug: GetCurrentConsoleFontEx is initially invalid
+=========================================================
+
+ - Assume there is no configured console font name in the registry. In this
+ case, the console defaults to a raster font.
+ - Open a new console and call the `GetCurrentConsoleFontEx` API.
+ - The `FaceName` field of the returned `CONSOLE_FONT_INFOEX` data
+ structure is incorrect. On Windows 7, 8, and 10, I observed that the
+ field was blank. On Windows 8, occasionally, it instead contained:
+ U+AE72 U+75BE U+0001
+ The other fields of the structure all appeared correct:
+ nFont=6 dwFontSize=(8,12) FontFamily=0x30 FontWeight=400
+ - The `FaceName` field becomes initialized easily:
+ - Open the console properties dialog and click OK. (Cancel is not
+ sufficient.)
+ - Call the undocumented `SetConsoleFont` with the current font table
+ index, which is 6 in the example above.
+ - It seems that the console uncritically accepts whatever string is
+ stored in the registry, including a blank string, and passes it on the
+ the `GetCurrentConsoleFontEx` caller. It is possible to get the console
+ to *write* a blank setting into the registry -- simply open the console
+ (default or app-specific) properties and click OK.
diff --git a/src/libs/3rdparty/winpty/misc/winbug-15048.cc b/src/libs/3rdparty/winpty/misc/winbug-15048.cc
new file mode 100644
index 0000000000..0e98d648c5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/winbug-15048.cc
@@ -0,0 +1,201 @@
+/*
+
+Test program demonstrating a problem in Windows 15048's ReadConsoleOutput API.
+
+To compile:
+
+ cl /nologo /EHsc winbug-15048.cc shell32.lib
+
+Example of regressed input:
+
+Case 1:
+
+ > chcp 932
+ > winbug-15048 -face-gothic 3044
+
+ Correct output:
+
+ 1**34 (nb: U+3044 replaced with '**' to avoid MSVC encoding warning)
+ 5678
+
+ ReadConsoleOutputW (both rows, 3 cols)
+ row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007)
+
+ ReadConsoleOutputW (both rows, 4 cols)
+ row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007) U+0034(0007)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
+
+ ReadConsoleOutputW (second row)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
+
+ ...
+
+ Win10 15048 bad output:
+
+ 1**34
+ 5678
+
+ ReadConsoleOutputW (both rows, 3 cols)
+ row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0035(0007)
+ row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0000(0000)
+
+ ReadConsoleOutputW (both rows, 4 cols)
+ row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0034(0007) U+0035(0007)
+ row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) U+0000(0000)
+
+ ReadConsoleOutputW (second row)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
+
+ ...
+
+ The U+3044 character (HIRAGANA LETTER I) occupies two columns, but it only
+ fills one record in the ReadConsoleOutput output buffer, which has the
+ effect of shifting the first cell of the second row into the last cell of
+ the first row. Ordinarily, the first and second cells would also have the
+ COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE attributes set, which
+ allows winpty to detect the double-column character.
+
+Case 2:
+
+ > chcp 437
+ > winbug-15048 -face "Lucida Console" -h 4 221A
+
+ The same issue happens with U+221A (SQUARE ROOT), but only in certain
+ fonts. The console seems to think this character occupies two columns
+ if the font is sufficiently small. The Windows console properties dialog
+ doesn't allow fonts below 5 pt, but winpty tries to use 2pt and 4pt Lucida
+ Console to allow very large console windows.
+
+Case 3:
+
+ > chcp 437
+ > winbug-15048 -face "Lucida Console" -h 12 FF12
+
+ The console selection system thinks U+FF12 (FULLWIDTH DIGIT TWO) occupies
+ two columns, which happens to be correct, but it's displayed as a single
+ column unrecognized character. It otherwise behaves the same as the other
+ cases.
+
+*/
+
+#include <windows.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#include <string>
+
+#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
+
+static void set_font(const wchar_t *name, int size) {
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_FONT_INFOEX fontex {};
+ fontex.cbSize = sizeof(fontex);
+ fontex.dwFontSize.Y = size;
+ fontex.FontWeight = 400;
+ fontex.FontFamily = 0x36;
+ wcsncpy(fontex.FaceName, name, COUNT_OF(fontex.FaceName));
+ assert(SetCurrentConsoleFontEx(conout, FALSE, &fontex));
+}
+
+static void usage(const wchar_t *prog) {
+ printf("Usage: %ls [options]\n", prog);
+ printf(" -h HEIGHT\n");
+ printf(" -face FACENAME\n");
+ printf(" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
+ printf(" hhhh -- print U+hhhh\n");
+ exit(1);
+}
+
+static void dump_region(SMALL_RECT region, const char *name) {
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ CHAR_INFO buf[1000];
+ memset(buf, 0xcc, sizeof(buf));
+
+ const int w = region.Right - region.Left + 1;
+ const int h = region.Bottom - region.Top + 1;
+
+ assert(ReadConsoleOutputW(
+ conout, buf, { (short)w, (short)h }, { 0, 0 },
+ &region));
+
+ printf("\n");
+ printf("ReadConsoleOutputW (%s)\n", name);
+ for (int y = 0; y < h; ++y) {
+ printf("row %d: ", region.Top + y);
+ for (int i = 0; i < region.Left * 13; ++i) {
+ printf(" ");
+ }
+ for (int x = 0; x < w; ++x) {
+ const int i = y * w + x;
+ printf("U+%04x(%04x) ", buf[i].Char.UnicodeChar, buf[i].Attributes);
+ }
+ printf("\n");
+ }
+}
+
+int main() {
+ wchar_t *cmdline = GetCommandLineW();
+ int argc = 0;
+ wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
+ const wchar_t *font_name = L"Lucida Console";
+ int font_height = 8;
+ int test_ch = 0xff12; // U+FF12 FULLWIDTH DIGIT TWO
+
+ for (int i = 1; i < argc; ++i) {
+ const std::wstring arg = argv[i];
+ const std::wstring next = i + 1 < argc ? argv[i + 1] : L"";
+ if (arg == L"-face" && i + 1 < argc) {
+ font_name = argv[i + 1];
+ i++;
+ } else if (arg == L"-face-gothic") {
+ font_name = kMSGothic;
+ } else if (arg == L"-face-simsun") {
+ font_name = kNSimSun;
+ } else if (arg == L"-face-minglight") {
+ font_name = kMingLight;
+ } else if (arg == L"-face-gulimche") {
+ font_name = kGulimChe;
+ } else if (arg == L"-h" && i + 1 < argc) {
+ font_height = _wtoi(next.c_str());
+ i++;
+ } else if (arg.c_str()[0] != '-') {
+ test_ch = wcstol(arg.c_str(), NULL, 16);
+ } else {
+ printf("Unrecognized argument: %ls\n", arg.c_str());
+ usage(argv[0]);
+ }
+ }
+
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ set_font(font_name, font_height);
+
+ system("cls");
+ DWORD actual = 0;
+ wchar_t output[] = L"1234\n5678\n";
+ output[1] = test_ch;
+ WriteConsoleW(conout, output, 10, &actual, nullptr);
+
+ dump_region({ 0, 0, 3, 1 }, "both rows, 3 cols");
+ dump_region({ 0, 0, 4, 1 }, "both rows, 4 cols");
+ dump_region({ 0, 1, 4, 1 }, "second row");
+ dump_region({ 0, 0, 4, 0 }, "first row");
+ dump_region({ 1, 0, 4, 0 }, "first row, skip 1");
+ dump_region({ 2, 0, 4, 0 }, "first row, skip 2");
+ dump_region({ 3, 0, 4, 0 }, "first row, skip 3");
+
+ set_font(font_name, 14);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat b/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat
new file mode 100644
index 0000000000..b6bca7b079
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat
@@ -0,0 +1,36 @@
+@echo off
+
+setlocal
+cd %~dp0..
+set Path=C:\Python27;C:\Program Files\Git\cmd;%Path%
+
+call "%VS140COMNTOOLS%\VsDevCmd.bat" || goto :fail
+
+rmdir /s/q build-libpty 2>NUL
+mkdir build-libpty\win
+mkdir build-libpty\win\x86
+mkdir build-libpty\win\x86_64
+mkdir build-libpty\win\xp
+
+rmdir /s/q src\Release 2>NUL
+rmdir /s/q src\.vs 2>NUL
+del src\*.vcxproj src\*.vcxproj.filters src\*.sln src\*.sdf 2>NUL
+
+call vcbuild.bat --msvc-platform Win32 --gyp-msvs-version 2015 --toolset v140_xp || goto :fail
+copy src\Release\Win32\winpty.dll build-libpty\win\xp || goto :fail
+copy src\Release\Win32\winpty-agent.exe build-libpty\win\xp || goto :fail
+
+call vcbuild.bat --msvc-platform Win32 --gyp-msvs-version 2015 || goto :fail
+copy src\Release\Win32\winpty.dll build-libpty\win\x86 || goto :fail
+copy src\Release\Win32\winpty-agent.exe build-libpty\win\x86 || goto :fail
+
+call vcbuild.bat --msvc-platform x64 --gyp-msvs-version 2015 || goto :fail
+copy src\Release\x64\winpty.dll build-libpty\win\x86_64 || goto :fail
+copy src\Release\x64\winpty-agent.exe build-libpty\win\x86_64 || goto :fail
+
+echo success
+goto :EOF
+
+:fail
+echo error: build failed
+exit /b 1
diff --git a/src/libs/3rdparty/winpty/ship/common_ship.py b/src/libs/3rdparty/winpty/ship/common_ship.py
new file mode 100644
index 0000000000..b587ac7ce1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/common_ship.py
@@ -0,0 +1,89 @@
+import os
+import sys
+
+# These scripts need to continue using Python 2 rather than 3, because
+# make_msvc_package.py puts the current Python interpreter on the PATH for the
+# sake of gyp, and gyp doesn't work with Python 3 yet.
+# https://bugs.chromium.org/p/gyp/issues/detail?id=36
+if os.name != "nt":
+ sys.exit("Error: ship scripts require native Python 2.7. (wrong os.name)")
+if sys.version_info[0:2] != (2,7):
+ sys.exit("Error: ship scripts require native Python 2.7. (wrong version)")
+
+import glob
+import hashlib
+import shutil
+import subprocess
+from distutils.spawn import find_executable
+
+topDir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+
+with open(topDir + "/VERSION.txt", "rt") as f:
+ winptyVersion = f.read().strip()
+
+def rmrf(patterns):
+ for pattern in patterns:
+ for path in glob.glob(pattern):
+ if os.path.isdir(path) and not os.path.islink(path):
+ print("+ rm -r " + path)
+ sys.stdout.flush()
+ shutil.rmtree(path)
+ elif os.path.isfile(path):
+ print("+ rm " + path)
+ sys.stdout.flush()
+ os.remove(path)
+
+def mkdir(path):
+ if not os.path.isdir(path):
+ os.makedirs(path)
+
+def requireExe(name, guesses):
+ if find_executable(name) is None:
+ for guess in guesses:
+ if os.path.exists(guess):
+ newDir = os.path.dirname(guess)
+ print("Adding " + newDir + " to Path to provide " + name)
+ os.environ["Path"] = newDir + ";" + os.environ["Path"]
+ ret = find_executable(name)
+ if ret is None:
+ sys.exit("Error: required EXE is missing from Path: " + name)
+ return ret
+
+class ModifyEnv:
+ def __init__(self, **kwargs):
+ self._changes = dict(kwargs)
+ self._original = dict()
+
+ def __enter__(self):
+ for var, val in self._changes.items():
+ self._original[var] = os.environ[var]
+ os.environ[var] = val
+
+ def __exit__(self, type, value, traceback):
+ for var, val in self._original.items():
+ os.environ[var] = val
+
+def sha256(path):
+ with open(path, "rb") as fp:
+ return hashlib.sha256(fp.read()).hexdigest()
+
+def checkSha256(path, expected):
+ actual = sha256(path)
+ if actual != expected:
+ sys.exit("error: sha256 hash mismatch on {}: expected {}, found {}".format(
+ path, expected, actual))
+
+requireExe("git.exe", [
+ "C:\\Program Files\\Git\\cmd\\git.exe",
+ "C:\\Program Files (x86)\\Git\\cmd\\git.exe"
+])
+
+commitHash = subprocess.check_output(["git.exe", "rev-parse", "HEAD"]).strip()
+defaultPathEnviron = "C:\\Windows\\System32;C:\\Windows"
+
+ZIP_TOOL = requireExe("7z.exe", [
+ "C:\\Program Files\\7-Zip\\7z.exe",
+ "C:\\Program Files (x86)\\7-Zip\\7z.exe",
+])
+
+requireExe("curl.exe", [])
diff --git a/src/libs/3rdparty/winpty/ship/make_msvc_package.py b/src/libs/3rdparty/winpty/ship/make_msvc_package.py
new file mode 100644
index 0000000000..2d10aac787
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/make_msvc_package.py
@@ -0,0 +1,163 @@
+#!python
+
+# Copyright (c) 2016 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+#
+# Run with native CPython 2.7.
+#
+# This script looks for MSVC using a version-specific environment variable,
+# such as VS140COMNTOOLS for MSVC 2015.
+#
+
+import common_ship
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+
+os.chdir(common_ship.topDir)
+
+MSVC_VERSION_TABLE = {
+ "2015" : {
+ "package_name" : "msvc2015",
+ "gyp_version" : "2015",
+ "common_tools_env" : "VS140COMNTOOLS",
+ "xp_toolset" : "v140_xp",
+ },
+ "2013" : {
+ "package_name" : "msvc2013",
+ "gyp_version" : "2013",
+ "common_tools_env" : "VS120COMNTOOLS",
+ "xp_toolset" : "v120_xp",
+ },
+}
+
+ARCH_TABLE = {
+ "x64" : {
+ "msvc_platform" : "x64",
+ },
+ "ia32" : {
+ "msvc_platform" : "Win32",
+ },
+}
+
+def readArguments():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--msvc-version", default="2015")
+ ret = parser.parse_args()
+ if ret.msvc_version not in MSVC_VERSION_TABLE:
+ sys.exit("Error: unrecognized version: " + ret.msvc_version + ". " +
+ "Versions: " + " ".join(sorted(MSVC_VERSION_TABLE.keys())))
+ return ret
+
+ARGS = readArguments()
+
+def checkoutGyp():
+ if os.path.isdir("build-gyp"):
+ return
+ subprocess.check_call([
+ "git.exe",
+ "clone",
+ "https://chromium.googlesource.com/external/gyp",
+ "build-gyp"
+ ])
+
+def cleanMsvc():
+ common_ship.rmrf("""
+ src/Release src/.vs src/gen
+ src/*.vcxproj src/*.vcxproj.filters src/*.sln src/*.sdf
+ """.split())
+
+def build(arch, packageDir, xp=False):
+ archInfo = ARCH_TABLE[arch]
+ versionInfo = MSVC_VERSION_TABLE[ARGS.msvc_version]
+
+ devCmdPath = os.path.join(os.environ[versionInfo["common_tools_env"]], "VsDevCmd.bat")
+ if not os.path.isfile(devCmdPath):
+ sys.exit("Error: MSVC environment script missing: " + devCmdPath)
+
+ toolsetArgument = " --toolset {}".format(versionInfo["xp_toolset"]) if xp else ""
+ newEnv = os.environ.copy()
+ newEnv["PATH"] = os.path.dirname(sys.executable) + ";" + common_ship.defaultPathEnviron
+ commandLine = (
+ '"' + devCmdPath + '" && ' +
+ " vcbuild.bat" +
+ " --gyp-msvs-version " + versionInfo["gyp_version"] +
+ " --msvc-platform " + archInfo["msvc_platform"] +
+ " --commit-hash " + common_ship.commitHash +
+ toolsetArgument
+ )
+
+ subprocess.check_call(commandLine, shell=True, env=newEnv)
+
+ archPackageDir = os.path.join(packageDir, arch)
+ if xp:
+ archPackageDir += "_xp"
+
+ common_ship.mkdir(archPackageDir + "/bin")
+ common_ship.mkdir(archPackageDir + "/lib")
+
+ binSrc = os.path.join(common_ship.topDir, "src/Release", archInfo["msvc_platform"])
+
+ shutil.copy(binSrc + "/winpty.dll", archPackageDir + "/bin")
+ shutil.copy(binSrc + "/winpty-agent.exe", archPackageDir + "/bin")
+ shutil.copy(binSrc + "/winpty-debugserver.exe", archPackageDir + "/bin")
+ shutil.copy(binSrc + "/winpty.lib", archPackageDir + "/lib")
+
+def buildPackage():
+ versionInfo = MSVC_VERSION_TABLE[ARGS.msvc_version]
+
+ packageName = "winpty-%s-%s" % (
+ common_ship.winptyVersion,
+ versionInfo["package_name"],
+ )
+
+ packageRoot = os.path.join(common_ship.topDir, "ship/packages")
+ packageDir = os.path.join(packageRoot, packageName)
+ packageFile = packageDir + ".zip"
+
+ common_ship.rmrf([packageDir])
+ common_ship.rmrf([packageFile])
+ common_ship.mkdir(packageDir)
+
+ checkoutGyp()
+ cleanMsvc()
+ build("ia32", packageDir, True)
+ build("x64", packageDir, True)
+ cleanMsvc()
+ build("ia32", packageDir)
+ build("x64", packageDir)
+
+ topDir = common_ship.topDir
+
+ common_ship.mkdir(packageDir + "/include")
+ shutil.copy(topDir + "/src/include/winpty.h", packageDir + "/include")
+ shutil.copy(topDir + "/src/include/winpty_constants.h", packageDir + "/include")
+ shutil.copy(topDir + "/LICENSE", packageDir)
+ shutil.copy(topDir + "/README.md", packageDir)
+ shutil.copy(topDir + "/RELEASES.md", packageDir)
+
+ subprocess.check_call([common_ship.ZIP_TOOL, "a", packageFile, "."], cwd=packageDir)
+
+if __name__ == "__main__":
+ buildPackage()
diff --git a/src/libs/3rdparty/winpty/ship/ship.py b/src/libs/3rdparty/winpty/ship/ship.py
new file mode 100644
index 0000000000..44f5862e3e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/ship.py
@@ -0,0 +1,104 @@
+#!python
+
+# Copyright (c) 2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+#
+# Run with native CPython 2.7 on a 64-bit computer.
+#
+
+import common_ship
+
+from optparse import OptionParser
+import multiprocessing
+import os
+import shutil
+import subprocess
+import sys
+
+
+def dllVersion(path):
+ version = subprocess.check_output(
+ ["powershell.exe",
+ "[System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"" + path + "\").FileVersion"])
+ return version.strip()
+
+
+os.chdir(common_ship.topDir)
+
+
+# Determine other build parameters.
+BUILD_KINDS = {
+ "cygwin": {
+ "path": ["bin"],
+ "dll": "bin\\cygwin1.dll",
+ },
+ "msys2": {
+ "path": ["opt\\bin", "usr\\bin"],
+ "dll": "usr\\bin\\msys-2.0.dll",
+ },
+}
+
+
+def buildTarget(kind, syspath, arch):
+
+ binPaths = [os.path.join(syspath, p) for p in BUILD_KINDS[kind]["path"]]
+ binPaths += common_ship.defaultPathEnviron.split(";")
+ newPath = ";".join(binPaths)
+
+ dllver = dllVersion(os.path.join(syspath, BUILD_KINDS[kind]["dll"]))
+ packageName = "winpty-{}-{}-{}-{}".format(common_ship.winptyVersion, kind, dllver, arch)
+ if os.path.exists("ship\\packages\\" + packageName):
+ shutil.rmtree("ship\\packages\\" + packageName)
+
+ print("+ Setting PATH to: {}".format(newPath))
+ with common_ship.ModifyEnv(PATH=newPath):
+ subprocess.check_call(["sh.exe", "configure"])
+ subprocess.check_call(["make.exe", "clean"])
+ makeBaseCmd = [
+ "make.exe",
+ "COMMIT_HASH=" + common_ship.commitHash,
+ "PREFIX=ship/packages/" + packageName
+ ]
+ subprocess.check_call(makeBaseCmd + ["all", "tests", "-j%d" % multiprocessing.cpu_count()])
+ subprocess.check_call(["build\\trivial_test.exe"])
+ subprocess.check_call(makeBaseCmd + ["install"])
+ subprocess.check_call(["tar.exe", "cvfz",
+ packageName + ".tar.gz",
+ packageName], cwd=os.path.join(os.getcwd(), "ship", "packages"))
+
+
+def main():
+ parser = OptionParser()
+ parser.add_option("--kind", type="choice", choices=["cygwin", "msys2"])
+ parser.add_option("--syspath")
+ parser.add_option("--arch", type="choice", choices=["ia32", "x64"])
+ (args, extra) = parser.parse_args()
+
+ args.kind or parser.error("--kind must be specified")
+ args.arch or parser.error("--arch must be specified")
+ args.syspath or parser.error("--syspath must be specified")
+ extra and parser.error("unexpected positional argument(s)")
+
+ buildTarget(args.kind, args.syspath, args.arch)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/libs/3rdparty/winpty/src/CMakeLists.txt b/src/libs/3rdparty/winpty/src/CMakeLists.txt
new file mode 100644
index 0000000000..22b15111d4
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/CMakeLists.txt
@@ -0,0 +1,111 @@
+if (MSVC)
+ add_compile_definitions(NOMINMAX UNICODE _UNICODE)
+endif()
+
+file(WRITE ${CMAKE_BINARY_DIR}/GenVersion.h.in [=[
+const char GenVersion_Version[] = "@VERSION@";
+const char GenVersion_Commit[] = "@COMMIT_HASH@";
+]=])
+
+file(READ ../VERSION.txt VERSION)
+string(REPLACE "\n" "" VERSION "${VERSION}")
+configure_file(${CMAKE_BINARY_DIR}/GenVersion.h.in ${CMAKE_BINARY_DIR}/GenVersion.h @ONLY)
+
+set(shared_sources
+ shared/AgentMsg.h
+ shared/BackgroundDesktop.h
+ shared/BackgroundDesktop.cc
+ shared/Buffer.h
+ shared/Buffer.cc
+ shared/DebugClient.h
+ shared/DebugClient.cc
+ shared/GenRandom.h
+ shared/GenRandom.cc
+ shared/OsModule.h
+ shared/OwnedHandle.h
+ shared/OwnedHandle.cc
+ shared/StringBuilder.h
+ shared/StringUtil.cc
+ shared/StringUtil.h
+ shared/UnixCtrlChars.h
+ shared/WindowsSecurity.cc
+ shared/WindowsSecurity.h
+ shared/WindowsVersion.h
+ shared/WindowsVersion.cc
+ shared/WinptyAssert.h
+ shared/WinptyAssert.cc
+ shared/WinptyException.h
+ shared/WinptyException.cc
+ shared/WinptyVersion.h
+ shared/WinptyVersion.cc
+ shared/winpty_snprintf.h
+)
+
+#
+# winpty-agent
+#
+
+add_qtc_executable(winpty-agent
+ INCLUDES
+ include ${CMAKE_BINARY_DIR}
+ DEFINES WINPTY_AGENT_ASSERT
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ SOURCES
+ agent/Agent.h
+ agent/Agent.cc
+ agent/AgentCreateDesktop.h
+ agent/AgentCreateDesktop.cc
+ agent/ConsoleFont.cc
+ agent/ConsoleFont.h
+ agent/ConsoleInput.cc
+ agent/ConsoleInput.h
+ agent/ConsoleInputReencoding.cc
+ agent/ConsoleInputReencoding.h
+ agent/ConsoleLine.cc
+ agent/ConsoleLine.h
+ agent/Coord.h
+ agent/DebugShowInput.h
+ agent/DebugShowInput.cc
+ agent/DefaultInputMap.h
+ agent/DefaultInputMap.cc
+ agent/DsrSender.h
+ agent/EventLoop.h
+ agent/EventLoop.cc
+ agent/InputMap.h
+ agent/InputMap.cc
+ agent/LargeConsoleRead.h
+ agent/LargeConsoleRead.cc
+ agent/NamedPipe.h
+ agent/NamedPipe.cc
+ agent/Scraper.h
+ agent/Scraper.cc
+ agent/SimplePool.h
+ agent/SmallRect.h
+ agent/Terminal.h
+ agent/Terminal.cc
+ agent/UnicodeEncoding.h
+ agent/Win32Console.cc
+ agent/Win32Console.h
+ agent/Win32ConsoleBuffer.cc
+ agent/Win32ConsoleBuffer.h
+ agent/main.cc
+ ${shared_sources}
+)
+
+#
+# libwinpty
+#
+
+add_qtc_library(winpty STATIC
+ INCLUDES ${CMAKE_BINARY_DIR}
+ PUBLIC_DEFINES COMPILING_WINPTY_DLL
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ SOURCES
+ libwinpty/AgentLocation.cc
+ libwinpty/AgentLocation.h
+ libwinpty/winpty.cc
+ ${shared_sources}
+)
+
+target_include_directories(winpty
+ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
diff --git a/src/libs/3rdparty/winpty/src/agent/Agent.cc b/src/libs/3rdparty/winpty/src/agent/Agent.cc
new file mode 100644
index 0000000000..986edead13
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Agent.cc
@@ -0,0 +1,612 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Agent.h"
+
+#include <windows.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "../include/winpty_constants.h"
+
+#include "../shared/AgentMsg.h"
+#include "../shared/Buffer.h"
+#include "../shared/DebugClient.h"
+#include "../shared/GenRandom.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+
+#include "ConsoleFont.h"
+#include "ConsoleInput.h"
+#include "NamedPipe.h"
+#include "Scraper.h"
+#include "Terminal.h"
+#include "Win32ConsoleBuffer.h"
+
+namespace {
+
+static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
+{
+ if (dwCtrlType == CTRL_C_EVENT) {
+ // Do nothing and claim to have handled the event.
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// We can detect the new Windows 10 console by observing the effect of the
+// Mark command. In older consoles, Mark temporarily moves the cursor to the
+// top-left of the console window. In the new console, the cursor isn't
+// initially moved.
+//
+// We might like to use Mark to freeze the console, but we can't, because when
+// the Mark command ends, the console moves the cursor back to its starting
+// point, even if the console application has moved it in the meantime.
+static void detectNewWindows10Console(
+ Win32Console &console, Win32ConsoleBuffer &buffer)
+{
+ if (!isAtLeastWindows8()) {
+ return;
+ }
+
+ ConsoleScreenBufferInfo info = buffer.bufferInfo();
+
+ // Make sure the window isn't 1x1. AFAIK, this should never happen
+ // accidentally. It is difficult to make it happen deliberately.
+ if (info.srWindow.Left == info.srWindow.Right &&
+ info.srWindow.Top == info.srWindow.Bottom) {
+ trace("detectNewWindows10Console: Initial console window was 1x1 -- "
+ "expanding for test");
+ setSmallFont(buffer.conout(), 400, false);
+ buffer.moveWindow(SmallRect(0, 0, 1, 1));
+ buffer.resizeBuffer(Coord(400, 1));
+ buffer.moveWindow(SmallRect(0, 0, 2, 1));
+ // This use of GetLargestConsoleWindowSize ought to be unnecessary
+ // given the behavior I've seen from moveWindow(0, 0, 1, 1), but
+ // I'd like to be especially sure, considering that this code will
+ // rarely be tested.
+ const auto largest = GetLargestConsoleWindowSize(buffer.conout());
+ buffer.moveWindow(
+ SmallRect(0, 0, std::min(largest.X, buffer.bufferSize().X), 1));
+ info = buffer.bufferInfo();
+ ASSERT(info.srWindow.Right > info.srWindow.Left &&
+ "Could not expand console window from 1x1");
+ }
+
+ // Test whether MARK moves the cursor.
+ const Coord initialPosition(info.srWindow.Right, info.srWindow.Bottom);
+ buffer.setCursorPosition(initialPosition);
+ ASSERT(!console.frozen());
+ console.setFreezeUsesMark(true);
+ console.setFrozen(true);
+ const bool isNewW10 = (buffer.cursorPosition() == initialPosition);
+ console.setFrozen(false);
+ buffer.setCursorPosition(Coord(0, 0));
+
+ trace("Attempting to detect new Windows 10 console using MARK: %s",
+ isNewW10 ? "detected" : "not detected");
+ console.setFreezeUsesMark(false);
+ console.setNewW10(isNewW10);
+}
+
+static inline WriteBuffer newPacket() {
+ WriteBuffer packet;
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
+ return packet;
+}
+
+static HANDLE duplicateHandle(HANDLE h) {
+ HANDLE ret = nullptr;
+ if (!DuplicateHandle(
+ GetCurrentProcess(), h,
+ GetCurrentProcess(), &ret,
+ 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ ASSERT(false && "DuplicateHandle failed!");
+ }
+ return ret;
+}
+
+// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
+// back to 64-bits. See the MSDN article, "Interprocess Communication Between
+// 32-bit and 64-bit Applications".
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
+static int64_t int64FromHandle(HANDLE h) {
+ return static_cast<int64_t>(reinterpret_cast<intptr_t>(h));
+}
+
+} // anonymous namespace
+
+Agent::Agent(LPCWSTR controlPipeName,
+ uint64_t agentFlags,
+ int mouseMode,
+ int initialCols,
+ int initialRows) :
+ m_useConerr((agentFlags & WINPTY_FLAG_CONERR) != 0),
+ m_plainMode((agentFlags & WINPTY_FLAG_PLAIN_OUTPUT) != 0),
+ m_mouseMode(mouseMode)
+{
+ trace("Agent::Agent entered");
+
+ ASSERT(initialCols >= 1 && initialRows >= 1);
+ initialCols = std::min(initialCols, MAX_CONSOLE_WIDTH);
+ initialRows = std::min(initialRows, MAX_CONSOLE_HEIGHT);
+
+ const bool outputColor =
+ !m_plainMode || (agentFlags & WINPTY_FLAG_COLOR_ESCAPES);
+ const Coord initialSize(initialCols, initialRows);
+
+ auto primaryBuffer = openPrimaryBuffer();
+ if (m_useConerr) {
+ m_errorBuffer = Win32ConsoleBuffer::createErrorBuffer();
+ }
+
+ detectNewWindows10Console(m_console, *primaryBuffer);
+
+ m_controlPipe = &connectToControlPipe(controlPipeName);
+ m_coninPipe = &createDataServerPipe(false, L"conin");
+ m_conoutPipe = &createDataServerPipe(true, L"conout");
+ if (m_useConerr) {
+ m_conerrPipe = &createDataServerPipe(true, L"conerr");
+ }
+
+ // Send an initial response packet to winpty.dll containing pipe names.
+ {
+ auto setupPacket = newPacket();
+ setupPacket.putWString(m_coninPipe->name());
+ setupPacket.putWString(m_conoutPipe->name());
+ if (m_useConerr) {
+ setupPacket.putWString(m_conerrPipe->name());
+ }
+ writePacket(setupPacket);
+ }
+
+ std::unique_ptr<Terminal> primaryTerminal;
+ primaryTerminal.reset(new Terminal(*m_conoutPipe,
+ m_plainMode,
+ outputColor));
+ m_primaryScraper.reset(new Scraper(m_console,
+ *primaryBuffer,
+ std::move(primaryTerminal),
+ initialSize));
+ if (m_useConerr) {
+ std::unique_ptr<Terminal> errorTerminal;
+ errorTerminal.reset(new Terminal(*m_conerrPipe,
+ m_plainMode,
+ outputColor));
+ m_errorScraper.reset(new Scraper(m_console,
+ *m_errorBuffer,
+ std::move(errorTerminal),
+ initialSize));
+ }
+
+ m_console.setTitle(m_currentTitle);
+
+ const HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
+ m_consoleInput.reset(
+ new ConsoleInput(conin, m_mouseMode, *this, m_console));
+
+ // Setup Ctrl-C handling. First restore default handling of Ctrl-C. This
+ // attribute is inherited by child processes. Then register a custom
+ // Ctrl-C handler that does nothing. The handler will be called when the
+ // agent calls GenerateConsoleCtrlEvent.
+ SetConsoleCtrlHandler(NULL, FALSE);
+ SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
+
+ setPollInterval(25);
+}
+
+Agent::~Agent()
+{
+ trace("Agent::~Agent entered");
+ agentShutdown();
+ if (m_childProcess != NULL) {
+ CloseHandle(m_childProcess);
+ }
+}
+
+// Write a "Device Status Report" command to the terminal. The terminal will
+// reply with a row+col escape sequence. Presumably, the DSR reply will not
+// split a keypress escape sequence, so it should be safe to assume that the
+// bytes before it are complete keypresses.
+void Agent::sendDsr()
+{
+ if (!m_plainMode && !m_conoutPipe->isClosed()) {
+ m_conoutPipe->write("\x1B[6n");
+ }
+}
+
+NamedPipe &Agent::connectToControlPipe(LPCWSTR pipeName)
+{
+ NamedPipe &pipe = createNamedPipe();
+ pipe.connectToServer(pipeName, NamedPipe::OpenMode::Duplex);
+ pipe.setReadBufferSize(64 * 1024);
+ return pipe;
+}
+
+// Returns a new server named pipe. It has not yet been connected.
+NamedPipe &Agent::createDataServerPipe(bool write, const wchar_t *kind)
+{
+ const auto name =
+ (WStringBuilder(128)
+ << L"\\\\.\\pipe\\winpty-"
+ << kind << L'-'
+ << GenRandom().uniqueName()).str_moved();
+ NamedPipe &pipe = createNamedPipe();
+ pipe.openServerPipe(
+ name.c_str(),
+ write ? NamedPipe::OpenMode::Writing
+ : NamedPipe::OpenMode::Reading,
+ write ? 8192 : 0,
+ write ? 0 : 256);
+ if (!write) {
+ pipe.setReadBufferSize(64 * 1024);
+ }
+ return pipe;
+}
+
+void Agent::onPipeIo(NamedPipe &namedPipe)
+{
+ if (&namedPipe == m_conoutPipe || &namedPipe == m_conerrPipe) {
+ autoClosePipesForShutdown();
+ } else if (&namedPipe == m_coninPipe) {
+ pollConinPipe();
+ } else if (&namedPipe == m_controlPipe) {
+ pollControlPipe();
+ }
+}
+
+void Agent::pollControlPipe()
+{
+ if (m_controlPipe->isClosed()) {
+ trace("Agent exiting (control pipe is closed)");
+ shutdown();
+ return;
+ }
+
+ while (true) {
+ uint64_t packetSize = 0;
+ const auto amt1 =
+ m_controlPipe->peek(&packetSize, sizeof(packetSize));
+ if (amt1 < sizeof(packetSize)) {
+ break;
+ }
+ ASSERT(packetSize >= sizeof(packetSize) && packetSize <= SIZE_MAX);
+ if (m_controlPipe->bytesAvailable() < packetSize) {
+ if (m_controlPipe->readBufferSize() < packetSize) {
+ m_controlPipe->setReadBufferSize(packetSize);
+ }
+ break;
+ }
+ std::vector<char> packetData;
+ packetData.resize(packetSize);
+ const auto amt2 = m_controlPipe->read(packetData.data(), packetSize);
+ ASSERT(amt2 == packetSize);
+ try {
+ ReadBuffer buffer(std::move(packetData));
+ buffer.getRawValue<uint64_t>(); // Discard the size.
+ handlePacket(buffer);
+ } catch (const ReadBuffer::DecodeError&) {
+ ASSERT(false && "Decode error");
+ }
+ }
+}
+
+void Agent::handlePacket(ReadBuffer &packet)
+{
+ const int type = packet.getInt32();
+ switch (type) {
+ case AgentMsg::StartProcess:
+ handleStartProcessPacket(packet);
+ break;
+ case AgentMsg::SetSize:
+ // TODO: I think it might make sense to collapse consecutive SetSize
+ // messages. i.e. The terminal process can probably generate SetSize
+ // messages faster than they can be processed, and some GUIs might
+ // generate a flood of them, so if we can read multiple SetSize packets
+ // at once, we can ignore the early ones.
+ handleSetSizePacket(packet);
+ break;
+ case AgentMsg::GetConsoleProcessList:
+ handleGetConsoleProcessListPacket(packet);
+ break;
+ default:
+ trace("Unrecognized message, id:%d", type);
+ }
+}
+
+void Agent::writePacket(WriteBuffer &packet)
+{
+ const auto &bytes = packet.buf();
+ packet.replaceRawValue<uint64_t>(0, bytes.size());
+ m_controlPipe->write(bytes.data(), bytes.size());
+}
+
+void Agent::handleStartProcessPacket(ReadBuffer &packet)
+{
+ ASSERT(m_childProcess == nullptr);
+ ASSERT(!m_closingOutputPipes);
+
+ const uint64_t spawnFlags = packet.getInt64();
+ const bool wantProcessHandle = packet.getInt32() != 0;
+ const bool wantThreadHandle = packet.getInt32() != 0;
+ const auto program = packet.getWString();
+ const auto cmdline = packet.getWString();
+ const auto cwd = packet.getWString();
+ const auto env = packet.getWString();
+ const auto desktop = packet.getWString();
+ packet.assertEof();
+
+ auto cmdlineV = vectorWithNulFromString(cmdline);
+ auto desktopV = vectorWithNulFromString(desktop);
+ auto envV = vectorFromString(env);
+
+ LPCWSTR programArg = program.empty() ? nullptr : program.c_str();
+ LPWSTR cmdlineArg = cmdline.empty() ? nullptr : cmdlineV.data();
+ LPCWSTR cwdArg = cwd.empty() ? nullptr : cwd.c_str();
+ LPWSTR envArg = env.empty() ? nullptr : envV.data();
+
+ STARTUPINFOW sui = {};
+ PROCESS_INFORMATION pi = {};
+ sui.cb = sizeof(sui);
+ sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
+ BOOL inheritHandles = FALSE;
+ if (m_useConerr) {
+ inheritHandles = TRUE;
+ sui.dwFlags |= STARTF_USESTDHANDLES;
+ sui.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ sui.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ sui.hStdError = m_errorBuffer->conout();
+ }
+
+ const BOOL success =
+ CreateProcessW(programArg, cmdlineArg, nullptr, nullptr,
+ /*bInheritHandles=*/inheritHandles,
+ /*dwCreationFlags=*/CREATE_UNICODE_ENVIRONMENT,
+ envArg, cwdArg, &sui, &pi);
+ const int lastError = success ? 0 : GetLastError();
+
+ trace("CreateProcess: %s %u",
+ (success ? "success" : "fail"),
+ static_cast<unsigned int>(pi.dwProcessId));
+
+ auto reply = newPacket();
+ if (success) {
+ int64_t replyProcess = 0;
+ int64_t replyThread = 0;
+ if (wantProcessHandle) {
+ replyProcess = int64FromHandle(duplicateHandle(pi.hProcess));
+ }
+ if (wantThreadHandle) {
+ replyThread = int64FromHandle(duplicateHandle(pi.hThread));
+ }
+ CloseHandle(pi.hThread);
+ m_childProcess = pi.hProcess;
+ m_autoShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN) != 0;
+ m_exitAfterShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN) != 0;
+ reply.putInt32(static_cast<int32_t>(StartProcessResult::ProcessCreated));
+ reply.putInt64(replyProcess);
+ reply.putInt64(replyThread);
+ } else {
+ reply.putInt32(static_cast<int32_t>(StartProcessResult::CreateProcessFailed));
+ reply.putInt32(lastError);
+ }
+ writePacket(reply);
+}
+
+void Agent::handleSetSizePacket(ReadBuffer &packet)
+{
+ const int cols = packet.getInt32();
+ const int rows = packet.getInt32();
+ packet.assertEof();
+ resizeWindow(cols, rows);
+ auto reply = newPacket();
+ writePacket(reply);
+}
+
+void Agent::handleGetConsoleProcessListPacket(ReadBuffer &packet)
+{
+ packet.assertEof();
+
+ auto processList = std::vector<DWORD>(64);
+ auto processCount = GetConsoleProcessList(&processList[0], processList.size());
+
+ // The process list can change while we're trying to read it
+ while (processList.size() < processCount) {
+ // Multiplying by two caps the number of iterations
+ const auto newSize = std::max<DWORD>(processList.size() * 2, processCount);
+ processList.resize(newSize);
+ processCount = GetConsoleProcessList(&processList[0], processList.size());
+ }
+
+ if (processCount == 0) {
+ trace("GetConsoleProcessList failed");
+ }
+
+ auto reply = newPacket();
+ reply.putInt32(processCount);
+ for (DWORD i = 0; i < processCount; i++) {
+ reply.putInt32(processList[i]);
+ }
+ writePacket(reply);
+}
+
+void Agent::pollConinPipe()
+{
+ const std::string newData = m_coninPipe->readAllToString();
+ if (hasDebugFlag("input_separated_bytes")) {
+ // This debug flag is intended to help with testing incomplete escape
+ // sequences and multibyte UTF-8 encodings. (I wonder if the normal
+ // code path ought to advance a state machine one byte at a time.)
+ for (size_t i = 0; i < newData.size(); ++i) {
+ m_consoleInput->writeInput(newData.substr(i, 1));
+ }
+ } else {
+ m_consoleInput->writeInput(newData);
+ }
+}
+
+void Agent::onPollTimeout()
+{
+ m_consoleInput->updateInputFlags();
+ const bool enableMouseMode = m_consoleInput->shouldActivateTerminalMouse();
+
+ // Give the ConsoleInput object a chance to flush input from an incomplete
+ // escape sequence (e.g. pressing ESC).
+ m_consoleInput->flushIncompleteEscapeCode();
+
+ const bool shouldScrapeContent = !m_closingOutputPipes;
+
+ // Check if the child process has exited.
+ if (m_autoShutdown &&
+ m_childProcess != nullptr &&
+ WaitForSingleObject(m_childProcess, 0) == WAIT_OBJECT_0) {
+ CloseHandle(m_childProcess);
+ m_childProcess = nullptr;
+
+ // Close the data socket to signal to the client that the child
+ // process has exited. If there's any data left to send, send it
+ // before closing the socket.
+ m_closingOutputPipes = true;
+ }
+
+ // Scrape for output *after* the above exit-check to ensure that we collect
+ // the child process's final output.
+ if (shouldScrapeContent) {
+ syncConsoleTitle();
+ scrapeBuffers();
+ }
+
+ // We must ensure that we disable mouse mode before closing the CONOUT
+ // pipe, so update the mouse mode here.
+ m_primaryScraper->terminal().enableMouseMode(
+ enableMouseMode && !m_closingOutputPipes);
+
+ autoClosePipesForShutdown();
+}
+
+void Agent::autoClosePipesForShutdown()
+{
+ if (m_closingOutputPipes) {
+ // We don't want to close a pipe before it's connected! If we do, the
+ // libwinpty client may try to connect to a non-existent pipe. This
+ // case is important for short-lived programs.
+ if (m_conoutPipe->isConnected() &&
+ m_conoutPipe->bytesToSend() == 0) {
+ trace("Closing CONOUT pipe (auto-shutdown)");
+ m_conoutPipe->closePipe();
+ }
+ if (m_conerrPipe != nullptr &&
+ m_conerrPipe->isConnected() &&
+ m_conerrPipe->bytesToSend() == 0) {
+ trace("Closing CONERR pipe (auto-shutdown)");
+ m_conerrPipe->closePipe();
+ }
+ if (m_exitAfterShutdown &&
+ m_conoutPipe->isClosed() &&
+ (m_conerrPipe == nullptr || m_conerrPipe->isClosed())) {
+ trace("Agent exiting (exit-after-shutdown)");
+ shutdown();
+ }
+ }
+}
+
+std::unique_ptr<Win32ConsoleBuffer> Agent::openPrimaryBuffer()
+{
+ // If we're using a separate buffer for stderr, and a program were to
+ // activate the stderr buffer, then we could accidentally scrape the same
+ // buffer twice. That probably shouldn't happen in ordinary use, but it
+ // can be avoided anyway by using the original console screen buffer in
+ // that mode.
+ if (!m_useConerr) {
+ return Win32ConsoleBuffer::openConout();
+ } else {
+ return Win32ConsoleBuffer::openStdout();
+ }
+}
+
+void Agent::resizeWindow(int cols, int rows)
+{
+ ASSERT(cols >= 1 && rows >= 1);
+ cols = std::min(cols, MAX_CONSOLE_WIDTH);
+ rows = std::min(rows, MAX_CONSOLE_HEIGHT);
+
+ Win32Console::FreezeGuard guard(m_console, m_console.frozen());
+ const Coord newSize(cols, rows);
+ ConsoleScreenBufferInfo info;
+ auto primaryBuffer = openPrimaryBuffer();
+ m_primaryScraper->resizeWindow(*primaryBuffer, newSize, info);
+ m_consoleInput->setMouseWindowRect(info.windowRect());
+ if (m_errorScraper) {
+ m_errorScraper->resizeWindow(*m_errorBuffer, newSize, info);
+ }
+
+ // Synthesize a WINDOW_BUFFER_SIZE_EVENT event. Normally, Windows
+ // generates this event only when the buffer size changes, not when the
+ // window size changes. This behavior is undesirable in two ways:
+ // - When winpty expands the window horizontally, it must expand the
+ // buffer first, then the window. At least some programs (e.g. the WSL
+ // bash.exe wrapper) use the window width rather than the buffer width,
+ // so there is a short timespan during which they can read the wrong
+ // value.
+ // - If the window's vertical size is changed, no event is generated,
+ // even though a typical well-behaved console program cares about the
+ // *window* height, not the *buffer* height.
+ // This synthesization works around a design flaw in the console. It's probably
+ // harmless. See https://github.com/rprichard/winpty/issues/110.
+ INPUT_RECORD sizeEvent {};
+ sizeEvent.EventType = WINDOW_BUFFER_SIZE_EVENT;
+ sizeEvent.Event.WindowBufferSizeEvent.dwSize = primaryBuffer->bufferSize();
+ DWORD actual {};
+ WriteConsoleInputW(GetStdHandle(STD_INPUT_HANDLE), &sizeEvent, 1, &actual);
+}
+
+void Agent::scrapeBuffers()
+{
+ Win32Console::FreezeGuard guard(m_console, m_console.frozen());
+ ConsoleScreenBufferInfo info;
+ m_primaryScraper->scrapeBuffer(*openPrimaryBuffer(), info);
+ m_consoleInput->setMouseWindowRect(info.windowRect());
+ if (m_errorScraper) {
+ m_errorScraper->scrapeBuffer(*m_errorBuffer, info);
+ }
+}
+
+void Agent::syncConsoleTitle()
+{
+ std::wstring newTitle = m_console.title();
+ if (newTitle != m_currentTitle) {
+ if (!m_plainMode && !m_conoutPipe->isClosed()) {
+ std::string command = std::string("\x1b]0;") +
+ utf8FromWide(newTitle) + "\x07";
+ m_conoutPipe->write(command.c_str());
+ }
+ m_currentTitle = newTitle;
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Agent.h b/src/libs/3rdparty/winpty/src/agent/Agent.h
new file mode 100644
index 0000000000..1dde48fe4a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Agent.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_H
+#define AGENT_H
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "DsrSender.h"
+#include "EventLoop.h"
+#include "Win32Console.h"
+
+class ConsoleInput;
+class NamedPipe;
+class ReadBuffer;
+class Scraper;
+class WriteBuffer;
+class Win32ConsoleBuffer;
+
+class Agent : public EventLoop, public DsrSender
+{
+public:
+ Agent(LPCWSTR controlPipeName,
+ uint64_t agentFlags,
+ int mouseMode,
+ int initialCols,
+ int initialRows);
+ virtual ~Agent();
+ void sendDsr() override;
+
+private:
+ NamedPipe &connectToControlPipe(LPCWSTR pipeName);
+ NamedPipe &createDataServerPipe(bool write, const wchar_t *kind);
+
+private:
+ void pollControlPipe();
+ void handlePacket(ReadBuffer &packet);
+ void writePacket(WriteBuffer &packet);
+ void handleStartProcessPacket(ReadBuffer &packet);
+ void handleSetSizePacket(ReadBuffer &packet);
+ void handleGetConsoleProcessListPacket(ReadBuffer &packet);
+ void pollConinPipe();
+
+protected:
+ virtual void onPollTimeout() override;
+ virtual void onPipeIo(NamedPipe &namedPipe) override;
+
+private:
+ void autoClosePipesForShutdown();
+ std::unique_ptr<Win32ConsoleBuffer> openPrimaryBuffer();
+ void resizeWindow(int cols, int rows);
+ void scrapeBuffers();
+ void syncConsoleTitle();
+
+private:
+ const bool m_useConerr;
+ const bool m_plainMode;
+ const int m_mouseMode;
+ Win32Console m_console;
+ std::unique_ptr<Scraper> m_primaryScraper;
+ std::unique_ptr<Scraper> m_errorScraper;
+ std::unique_ptr<Win32ConsoleBuffer> m_errorBuffer;
+ NamedPipe *m_controlPipe = nullptr;
+ NamedPipe *m_coninPipe = nullptr;
+ NamedPipe *m_conoutPipe = nullptr;
+ NamedPipe *m_conerrPipe = nullptr;
+ bool m_autoShutdown = false;
+ bool m_exitAfterShutdown = false;
+ bool m_closingOutputPipes = false;
+ std::unique_ptr<ConsoleInput> m_consoleInput;
+ HANDLE m_childProcess = nullptr;
+
+ // If the title is initialized to the empty string, then cmd.exe will
+ // sometimes print this error:
+ // Not enough storage is available to process this command.
+ // It happens on Windows 7 when logged into a Cygwin SSH session, for
+ // example. Using a title of a single space character avoids the problem.
+ // See https://github.com/rprichard/winpty/issues/74.
+ std::wstring m_currentTitle = L" ";
+};
+
+#endif // AGENT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc
new file mode 100644
index 0000000000..9ad6503b1c
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "AgentCreateDesktop.h"
+
+#include "../shared/BackgroundDesktop.h"
+#include "../shared/Buffer.h"
+#include "../shared/DebugClient.h"
+#include "../shared/StringUtil.h"
+
+#include "EventLoop.h"
+#include "NamedPipe.h"
+
+namespace {
+
+static inline WriteBuffer newPacket() {
+ WriteBuffer packet;
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
+ return packet;
+}
+
+class CreateDesktopLoop : public EventLoop {
+public:
+ CreateDesktopLoop(LPCWSTR controlPipeName);
+
+protected:
+ virtual void onPipeIo(NamedPipe &namedPipe) override;
+
+private:
+ void writePacket(WriteBuffer &packet);
+
+ BackgroundDesktop m_desktop;
+ NamedPipe &m_pipe;
+};
+
+CreateDesktopLoop::CreateDesktopLoop(LPCWSTR controlPipeName) :
+ m_pipe(createNamedPipe()) {
+ m_pipe.connectToServer(controlPipeName, NamedPipe::OpenMode::Duplex);
+ auto packet = newPacket();
+ packet.putWString(m_desktop.desktopName());
+ writePacket(packet);
+}
+
+void CreateDesktopLoop::writePacket(WriteBuffer &packet) {
+ const auto &bytes = packet.buf();
+ packet.replaceRawValue<uint64_t>(0, bytes.size());
+ m_pipe.write(bytes.data(), bytes.size());
+}
+
+void CreateDesktopLoop::onPipeIo(NamedPipe &namedPipe) {
+ if (m_pipe.isClosed()) {
+ shutdown();
+ }
+}
+
+} // anonymous namespace
+
+void handleCreateDesktop(LPCWSTR controlPipeName) {
+ try {
+ CreateDesktopLoop loop(controlPipeName);
+ loop.run();
+ trace("Agent exiting...");
+ } catch (const WinptyException &e) {
+ trace("handleCreateDesktop: internal error: %s",
+ utf8FromWide(e.what()).c_str());
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h
new file mode 100644
index 0000000000..2ae539c7fa
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_CREATE_DESKTOP_H
+#define AGENT_CREATE_DESKTOP_H
+
+#include <windows.h>
+
+void handleCreateDesktop(LPCWSTR controlPipeName);
+
+#endif // AGENT_CREATE_DESKTOP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc
new file mode 100644
index 0000000000..aa1f7876d3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc
@@ -0,0 +1,698 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "ConsoleFont.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <algorithm>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "../shared/DebugClient.h"
+#include "../shared/OsModule.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+namespace {
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kLucidaConsole[] = L"Lucida Console";
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // 932, Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // 936, Chinese Simplified
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // 949, Korean
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // 950, Chinese Traditional
+
+struct FontSize {
+ short size;
+ int width;
+};
+
+struct Font {
+ const wchar_t *faceName;
+ unsigned int family;
+ short size;
+};
+
+// Ideographs in East Asian languages take two columns rather than one.
+// In the console screen buffer, a "full-width" character will occupy two
+// cells of the buffer, the first with attribute 0x100 and the second with
+// attribute 0x200.
+//
+// Windows does not correctly identify code points as double-width in all
+// configurations. It depends heavily on the code page, the font facename,
+// and (somehow) even the font size. In the 437 code page (MS-DOS), for
+// example, no codepoints are interpreted as double-width. When the console
+// is in an East Asian code page (932, 936, 949, or 950), then sometimes
+// selecting a "Western" facename like "Lucida Console" or "Consolas" doesn't
+// register, or if the font *can* be chosen, then the console doesn't handle
+// double-width correctly. I tested the double-width handling by writing
+// several code points with WriteConsole and checking whether one or two cells
+// were filled.
+//
+// In the Japanese code page (932), Microsoft's default font is MS Gothic.
+// MS Gothic double-width handling seems to be broken with console versions
+// prior to Windows 10 (including Windows 10's legacy mode), and it's
+// especially broken in Windows 8 and 8.1.
+//
+// Test by running: misc/Utf16Echo A2 A3 2014 3044 30FC 4000
+//
+// The first three codepoints are always rendered as half-width with the
+// Windows Japanese fonts. (Of these, the first two must be half-width,
+// but U+2014 could be either.) The last three are rendered as full-width,
+// and they are East_Asian_Width=Wide.
+//
+// Windows 7 fails by modeling all codepoints as full-width with font
+// sizes 22 and above.
+//
+// Windows 8 gets U+00A2, U+00A3, U+2014, U+30FC, and U+4000 wrong, but
+// using a point size not listed in the console properties dialog
+// (e.g. "9") is less wrong:
+//
+// | code point |
+// font | 00A2 00A3 2014 3044 30FC 4000 | cell size
+// ------------+---------------------------------+----------
+// 8 | F F F F H H | 4x8
+// 9 | F F F F F F | 5x9
+// 16 | F F F F H H | 8x16
+// raster 6x13 | H H H F F H(*) | 6x13
+//
+// (*) The Raster Font renders U+4000 as a white box (i.e. an unsupported
+// character).
+//
+
+// See:
+// - misc/Font-Report-June2016 directory for per-size details
+// - misc/font-notes.txt
+// - misc/Utf16Echo.cc, misc/FontSurvey.cc, misc/SetFont.cc, misc/GetFont.cc
+
+const FontSize kLucidaFontSizes[] = {
+ { 5, 3 },
+ { 6, 4 },
+ { 8, 5 },
+ { 10, 6 },
+ { 12, 7 },
+ { 14, 8 },
+ { 16, 10 },
+ { 18, 11 },
+ { 20, 12 },
+ { 36, 22 },
+ { 48, 29 },
+ { 60, 36 },
+ { 72, 43 },
+};
+
+// Japanese. Used on Vista and Windows 7.
+const FontSize k932GothicVista[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 13, 7 },
+ { 15, 8 },
+ { 17, 9 },
+ { 19, 10 },
+ { 21, 11 },
+ // All larger fonts are more broken w.r.t. full-size East Asian characters.
+};
+
+// Japanese. Used on Windows 8, 8.1, and the legacy 10 console.
+const FontSize k932GothicWin8[] = {
+ // All of these characters are broken w.r.t. full-size East Asian
+ // characters, but they're equally broken.
+ { 5, 3 },
+ { 7, 4 },
+ { 9, 5 },
+ { 11, 6 },
+ { 13, 7 },
+ { 15, 8 },
+ { 17, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Japanese. Used on the new Windows 10 console.
+const FontSize k932GothicWin10[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Chinese Simplified.
+const FontSize k936SimSun[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Korean.
+const FontSize k949GulimChe[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Chinese Traditional.
+const FontSize k950MingLight[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Some of these types and functions are missing from the MinGW headers.
+// Others are undocumented.
+
+struct AGENT_CONSOLE_FONT_INFO {
+ DWORD nFont;
+ COORD dwFontSize;
+};
+
+struct AGENT_CONSOLE_FONT_INFOEX {
+ ULONG cbSize;
+ DWORD nFont;
+ COORD dwFontSize;
+ UINT FontFamily;
+ UINT FontWeight;
+ WCHAR FaceName[LF_FACESIZE];
+};
+
+// undocumented XP API
+typedef BOOL WINAPI SetConsoleFont_t(
+ HANDLE hOutput,
+ DWORD dwFontIndex);
+
+// undocumented XP API
+typedef DWORD WINAPI GetNumberOfConsoleFonts_t();
+
+// XP and up
+typedef BOOL WINAPI GetCurrentConsoleFont_t(
+ HANDLE hOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont);
+
+// XP and up
+typedef COORD WINAPI GetConsoleFontSize_t(
+ HANDLE hConsoleOutput,
+ DWORD nFont);
+
+// Vista and up
+typedef BOOL WINAPI GetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+// Vista and up
+typedef BOOL WINAPI SetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+#define GET_MODULE_PROC(mod, funcName) \
+ m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \
+
+#define DEFINE_ACCESSOR(funcName) \
+ funcName##_t &funcName() const { \
+ ASSERT(valid()); \
+ return *m_##funcName; \
+ }
+
+class XPFontAPI {
+public:
+ XPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetConsoleFontSize);
+ }
+
+ bool valid() const {
+ return m_GetCurrentConsoleFont != NULL &&
+ m_GetConsoleFontSize != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFont)
+ DEFINE_ACCESSOR(GetConsoleFontSize)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFont_t *m_GetCurrentConsoleFont;
+ GetConsoleFontSize_t *m_GetConsoleFontSize;
+};
+
+class UndocumentedXPFontAPI : public XPFontAPI {
+public:
+ UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, SetConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_SetConsoleFont != NULL &&
+ m_GetNumberOfConsoleFonts != NULL;
+ }
+
+ DEFINE_ACCESSOR(SetConsoleFont)
+ DEFINE_ACCESSOR(GetNumberOfConsoleFonts)
+
+private:
+ OsModule m_kernel32;
+ SetConsoleFont_t *m_SetConsoleFont;
+ GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts;
+};
+
+class VistaFontAPI : public XPFontAPI {
+public:
+ VistaFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx);
+ GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_GetCurrentConsoleFontEx != NULL &&
+ m_SetCurrentConsoleFontEx != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFontEx)
+ DEFINE_ACCESSOR(SetCurrentConsoleFontEx)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx;
+ SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx;
+};
+
+static std::vector<std::pair<DWORD, COORD> > readFontTable(
+ XPFontAPI &api, HANDLE conout, DWORD maxCount) {
+ std::vector<std::pair<DWORD, COORD> > ret;
+ for (DWORD i = 0; i < maxCount; ++i) {
+ COORD size = api.GetConsoleFontSize()(conout, i);
+ if (size.X == 0 && size.Y == 0) {
+ break;
+ }
+ ret.push_back(std::make_pair(i, size));
+ }
+ return ret;
+}
+
+static void dumpFontTable(HANDLE conout, const char *prefix) {
+ const int kMaxCount = 1000;
+ if (!isTracingEnabled()) {
+ return;
+ }
+ XPFontAPI api;
+ if (!api.valid()) {
+ trace("dumpFontTable: cannot dump font table -- missing APIs");
+ return;
+ }
+ std::vector<std::pair<DWORD, COORD> > table =
+ readFontTable(api, conout, kMaxCount);
+ std::string line;
+ char tmp[128];
+ size_t first = 0;
+ while (first < table.size()) {
+ size_t last = std::min(table.size() - 1, first + 10 - 1);
+ winpty_snprintf(tmp, "%sfonts %02u-%02u:",
+ prefix, static_cast<unsigned>(first), static_cast<unsigned>(last));
+ line = tmp;
+ for (size_t i = first; i <= last; ++i) {
+ if (i % 10 == 5) {
+ line += " - ";
+ }
+ winpty_snprintf(tmp, " %2dx%-2d",
+ table[i].second.X, table[i].second.Y);
+ line += tmp;
+ }
+ trace("%s", line.c_str());
+ first = last + 1;
+ }
+ if (table.size() == kMaxCount) {
+ trace("%sfonts: ... stopped reading at %d fonts ...",
+ prefix, kMaxCount);
+ }
+}
+
+static std::string stringToCodePoints(const std::wstring &str) {
+ std::string ret = "(";
+ for (size_t i = 0; i < str.size(); ++i) {
+ char tmp[32];
+ winpty_snprintf(tmp, "%X", str[i]);
+ if (ret.size() > 1) {
+ ret.push_back(' ');
+ }
+ ret += tmp;
+ }
+ ret.push_back(')');
+ return ret;
+}
+
+static void dumpFontInfoEx(
+ const AGENT_CONSOLE_FONT_INFOEX &infoex,
+ const char *prefix) {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ std::wstring faceName(infoex.FaceName,
+ winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName)));
+ trace("%snFont=%u dwFontSize=(%d,%d) "
+ "FontFamily=0x%x FontWeight=%u FaceName=%s %s",
+ prefix,
+ static_cast<unsigned>(infoex.nFont),
+ infoex.dwFontSize.X, infoex.dwFontSize.Y,
+ infoex.FontFamily, infoex.FontWeight, utf8FromWide(faceName).c_str(),
+ stringToCodePoints(faceName).c_str());
+}
+
+static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, const char *prefix) {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ AGENT_CONSOLE_FONT_INFOEX infoex = {0};
+ infoex.cbSize = sizeof(infoex);
+ if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
+ trace("GetCurrentConsoleFontEx call failed");
+ return;
+ }
+ dumpFontInfoEx(infoex, prefix);
+}
+
+static void dumpXPFont(XPFontAPI &api, HANDLE conout, const char *prefix) {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ AGENT_CONSOLE_FONT_INFO info = {0};
+ if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) {
+ trace("GetCurrentConsoleFont call failed");
+ return;
+ }
+ trace("%snFont=%u dwFontSize=(%d,%d)",
+ prefix,
+ static_cast<unsigned>(info.nFont),
+ info.dwFontSize.X, info.dwFontSize.Y);
+}
+
+static bool setFontVista(
+ VistaFontAPI &api,
+ HANDLE conout,
+ const Font &font) {
+ AGENT_CONSOLE_FONT_INFOEX infoex = {};
+ infoex.cbSize = sizeof(AGENT_CONSOLE_FONT_INFOEX);
+ infoex.dwFontSize.Y = font.size;
+ infoex.FontFamily = font.family;
+ infoex.FontWeight = 400;
+ winpty_wcsncpy_nul(infoex.FaceName, font.faceName);
+ dumpFontInfoEx(infoex, "setFontVista: setting font to: ");
+ if (!api.SetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
+ trace("setFontVista: SetCurrentConsoleFontEx call failed");
+ return false;
+ }
+ memset(&infoex, 0, sizeof(infoex));
+ infoex.cbSize = sizeof(infoex);
+ if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
+ trace("setFontVista: GetCurrentConsoleFontEx call failed");
+ return false;
+ }
+ if (wcsncmp(infoex.FaceName, font.faceName,
+ COUNT_OF(infoex.FaceName)) != 0) {
+ trace("setFontVista: face name was not set");
+ dumpFontInfoEx(infoex, "setFontVista: post-call font: ");
+ return false;
+ }
+ // We'd like to verify that the new font size is correct, but we can't
+ // predict what it will be, even though we just set it to `pxSize` through
+ // an apprently symmetric interface. For the Chinese and Korean fonts, the
+ // new `infoex.dwFontSize.Y` value can be slightly larger than the height
+ // we specified.
+ return true;
+}
+
+static Font selectSmallFont(int codePage, int columns, bool isNewW10) {
+ // Iterate over a set of font sizes according to the code page, and select
+ // one.
+
+ const wchar_t *faceName = nullptr;
+ unsigned int fontFamily = 0;
+ const FontSize *table = nullptr;
+ size_t tableSize = 0;
+
+ switch (codePage) {
+ case 932: // Japanese
+ faceName = kMSGothic;
+ fontFamily = 0x36;
+ if (isNewW10) {
+ table = k932GothicWin10;
+ tableSize = COUNT_OF(k932GothicWin10);
+ } else if (isAtLeastWindows8()) {
+ table = k932GothicWin8;
+ tableSize = COUNT_OF(k932GothicWin8);
+ } else {
+ table = k932GothicVista;
+ tableSize = COUNT_OF(k932GothicVista);
+ }
+ break;
+ case 936: // Chinese Simplified
+ faceName = kNSimSun;
+ fontFamily = 0x36;
+ table = k936SimSun;
+ tableSize = COUNT_OF(k936SimSun);
+ break;
+ case 949: // Korean
+ faceName = kGulimChe;
+ fontFamily = 0x36;
+ table = k949GulimChe;
+ tableSize = COUNT_OF(k949GulimChe);
+ break;
+ case 950: // Chinese Traditional
+ faceName = kMingLight;
+ fontFamily = 0x36;
+ table = k950MingLight;
+ tableSize = COUNT_OF(k950MingLight);
+ break;
+ default:
+ faceName = kLucidaConsole;
+ fontFamily = 0x36;
+ table = kLucidaFontSizes;
+ tableSize = COUNT_OF(kLucidaFontSizes);
+ break;
+ }
+
+ size_t bestIndex = static_cast<size_t>(-1);
+ std::tuple<int, int> bestScore = std::make_tuple(-1, -1);
+
+ // We might want to pick the smallest possible font, because we don't know
+ // how large the monitor is (and the monitor size can change). We might
+ // want to pick a larger font to accommodate console programs that resize
+ // the console on their own, like DOS edit.com, which tends to resize the
+ // console to 80 columns.
+
+ for (size_t i = 0; i < tableSize; ++i) {
+ const int width = table[i].width * columns;
+
+ // In general, we'd like to pick a font size where cutting the number
+ // of columns in half doesn't immediately violate the minimum width
+ // constraint. (e.g. To run DOS edit.com, a user might resize their
+ // terminal to ~100 columns so it's big enough to show the 80 columns
+ // post-resize.) To achieve this, give priority to fonts that allow
+ // this halving. We don't want to encourage *very* large fonts,
+ // though, so disable the effect as the number of columns scales from
+ // 80 to 40.
+ const int halfColumns = std::min(columns, std::max(40, columns / 2));
+ const int halfWidth = table[i].width * halfColumns;
+
+ std::tuple<int, int> thisScore = std::make_tuple(-1, -1);
+ if (width >= 160 && halfWidth >= 160) {
+ // Both sizes are good. Prefer the smaller fonts.
+ thisScore = std::make_tuple(2, -width);
+ } else if (width >= 160) {
+ // Prefer the smaller fonts.
+ thisScore = std::make_tuple(1, -width);
+ } else {
+ // Otherwise, prefer the largest font in our table.
+ thisScore = std::make_tuple(0, width);
+ }
+ if (thisScore > bestScore) {
+ bestIndex = i;
+ bestScore = thisScore;
+ }
+ }
+
+ ASSERT(bestIndex != static_cast<size_t>(-1));
+ return Font { faceName, fontFamily, table[bestIndex].size };
+}
+
+static void setSmallFontVista(VistaFontAPI &api, HANDLE conout,
+ int columns, bool isNewW10) {
+ int codePage = GetConsoleOutputCP();
+ const auto font = selectSmallFont(codePage, columns, isNewW10);
+ if (setFontVista(api, conout, font)) {
+ trace("setSmallFontVista: success");
+ return;
+ }
+ if (codePage == 932 || codePage == 936 ||
+ codePage == 949 || codePage == 950) {
+ trace("setSmallFontVista: falling back to default codepage font instead");
+ const auto fontFB = selectSmallFont(0, columns, isNewW10);
+ if (setFontVista(api, conout, fontFB)) {
+ trace("setSmallFontVista: fallback was successful");
+ return;
+ }
+ }
+ trace("setSmallFontVista: failure");
+}
+
+struct FontSizeComparator {
+ bool operator()(const std::pair<DWORD, COORD> &obj1,
+ const std::pair<DWORD, COORD> &obj2) const {
+ int score1 = obj1.second.X + obj1.second.Y;
+ int score2 = obj2.second.X + obj2.second.Y;
+ return score1 < score2;
+ }
+};
+
+static void setSmallFontXP(UndocumentedXPFontAPI &api, HANDLE conout) {
+ // Read the console font table and sort it from smallest to largest.
+ const DWORD fontCount = api.GetNumberOfConsoleFonts()();
+ trace("setSmallFontXP: number of console fonts: %u",
+ static_cast<unsigned>(fontCount));
+ std::vector<std::pair<DWORD, COORD> > table =
+ readFontTable(api, conout, fontCount);
+ std::sort(table.begin(), table.end(), FontSizeComparator());
+ for (size_t i = 0; i < table.size(); ++i) {
+ // Skip especially narrow fonts to permit narrower terminals.
+ if (table[i].second.X < 4) {
+ continue;
+ }
+ trace("setSmallFontXP: setting font to %u",
+ static_cast<unsigned>(table[i].first));
+ if (!api.SetConsoleFont()(conout, table[i].first)) {
+ trace("setSmallFontXP: SetConsoleFont call failed");
+ continue;
+ }
+ AGENT_CONSOLE_FONT_INFO info;
+ if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) {
+ trace("setSmallFontXP: GetCurrentConsoleFont call failed");
+ return;
+ }
+ if (info.nFont != table[i].first) {
+ trace("setSmallFontXP: font was not set");
+ dumpXPFont(api, conout, "setSmallFontXP: post-call font: ");
+ continue;
+ }
+ trace("setSmallFontXP: success");
+ return;
+ }
+ trace("setSmallFontXP: failure");
+}
+
+} // anonymous namespace
+
+// A Windows console window can never be larger than the desktop window. To
+// maximize the possible size of the console in rows*cols, try to configure
+// the console with a small font. Unfortunately, we cannot make the font *too*
+// small, because there is also a minimum window size in pixels.
+void setSmallFont(HANDLE conout, int columns, bool isNewW10) {
+ trace("setSmallFont: attempting to set a small font for %d columns "
+ "(CP=%u OutputCP=%u)",
+ columns,
+ static_cast<unsigned>(GetConsoleCP()),
+ static_cast<unsigned>(GetConsoleOutputCP()));
+ VistaFontAPI vista;
+ if (vista.valid()) {
+ dumpVistaFont(vista, conout, "previous font: ");
+ dumpFontTable(conout, "previous font table: ");
+ setSmallFontVista(vista, conout, columns, isNewW10);
+ dumpVistaFont(vista, conout, "new font: ");
+ dumpFontTable(conout, "new font table: ");
+ return;
+ }
+ UndocumentedXPFontAPI xp;
+ if (xp.valid()) {
+ dumpXPFont(xp, conout, "previous font: ");
+ dumpFontTable(conout, "previous font table: ");
+ setSmallFontXP(xp, conout);
+ dumpXPFont(xp, conout, "new font: ");
+ dumpFontTable(conout, "new font table: ");
+ return;
+ }
+ trace("setSmallFont: neither Vista nor XP APIs detected -- giving up");
+ dumpFontTable(conout, "font table: ");
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h
new file mode 100644
index 0000000000..99cb10698d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef CONSOLEFONT_H
+#define CONSOLEFONT_H
+
+#include <windows.h>
+
+void setSmallFont(HANDLE conout, int columns, bool isNewW10);
+
+#endif // CONSOLEFONT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc
new file mode 100644
index 0000000000..192cac2a29
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc
@@ -0,0 +1,852 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "ConsoleInput.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+
+#include "../include/winpty_constants.h"
+
+#include "../shared/DebugClient.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/UnixCtrlChars.h"
+
+#include "ConsoleInputReencoding.h"
+#include "DebugShowInput.h"
+#include "DefaultInputMap.h"
+#include "DsrSender.h"
+#include "UnicodeEncoding.h"
+#include "Win32Console.h"
+
+// MAPVK_VK_TO_VSC isn't defined by the old MinGW.
+#ifndef MAPVK_VK_TO_VSC
+#define MAPVK_VK_TO_VSC 0
+#endif
+
+namespace {
+
+struct MouseRecord {
+ bool release;
+ int flags;
+ COORD coord;
+
+ std::string toString() const;
+};
+
+std::string MouseRecord::toString() const {
+ StringBuilder sb(40);
+ sb << "pos=" << coord.X << ',' << coord.Y
+ << " flags=0x" << hexOfInt(flags);
+ if (release) {
+ sb << " release";
+ }
+ return sb.str_moved();
+}
+
+const unsigned int kIncompleteEscapeTimeoutMs = 1000u;
+
+#define CHECK(cond) \
+ do { \
+ if (!(cond)) { return 0; } \
+ } while(0)
+
+#define ADVANCE() \
+ do { \
+ pch++; \
+ if (pch == stop) { return -1; } \
+ } while(0)
+
+#define SCAN_INT(out, maxLen) \
+ do { \
+ (out) = 0; \
+ CHECK(isdigit(*pch)); \
+ const char *begin = pch; \
+ do { \
+ CHECK(pch - begin + 1 < maxLen); \
+ (out) = (out) * 10 + *pch - '0'; \
+ ADVANCE(); \
+ } while (isdigit(*pch)); \
+ } while(0)
+
+#define SCAN_SIGNED_INT(out, maxLen) \
+ do { \
+ bool negative = false; \
+ if (*pch == '-') { \
+ negative = true; \
+ ADVANCE(); \
+ } \
+ SCAN_INT(out, maxLen); \
+ if (negative) { \
+ (out) = -(out); \
+ } \
+ } while(0)
+
+// Match the Device Status Report console input: ESC [ nn ; mm R
+// Returns:
+// 0 no match
+// >0 match, returns length of match
+// -1 incomplete match
+static int matchDsr(const char *input, int inputSize)
+{
+ int32_t dummy = 0;
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ SCAN_INT(dummy, 8);
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_INT(dummy, 8);
+ CHECK(*pch == 'R');
+ return pch - input + 1;
+}
+
+static int matchMouseDefault(const char *input, int inputSize,
+ MouseRecord &out)
+{
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ CHECK(*pch == 'M'); ADVANCE();
+ out.flags = (*pch - 32) & 0xFF; ADVANCE();
+ out.coord.X = (*pch - '!') & 0xFF;
+ ADVANCE();
+ out.coord.Y = (*pch - '!') & 0xFF;
+ out.release = false;
+ return pch - input + 1;
+}
+
+static int matchMouse1006(const char *input, int inputSize, MouseRecord &out)
+{
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ int32_t temp;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ CHECK(*pch == '<'); ADVANCE();
+ SCAN_INT(out.flags, 8);
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1;
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1;
+ CHECK(*pch == 'M' || *pch == 'm');
+ out.release = (*pch == 'm');
+ return pch - input + 1;
+}
+
+static int matchMouse1015(const char *input, int inputSize, MouseRecord &out)
+{
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ int32_t temp;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ SCAN_INT(out.flags, 8); out.flags -= 32;
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1;
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1;
+ CHECK(*pch == 'M');
+ out.release = false;
+ return pch - input + 1;
+}
+
+// Match a mouse input escape sequence of any kind.
+// 0 no match
+// >0 match, returns length of match
+// -1 incomplete match
+static int matchMouseRecord(const char *input, int inputSize, MouseRecord &out)
+{
+ memset(&out, 0, sizeof(out));
+ int ret;
+ if ((ret = matchMouse1006(input, inputSize, out)) != 0) { return ret; }
+ if ((ret = matchMouse1015(input, inputSize, out)) != 0) { return ret; }
+ if ((ret = matchMouseDefault(input, inputSize, out)) != 0) { return ret; }
+ return 0;
+}
+
+#undef CHECK
+#undef ADVANCE
+#undef SCAN_INT
+
+} // anonymous namespace
+
+ConsoleInput::ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender,
+ Win32Console &console) :
+ m_console(console),
+ m_conin(conin),
+ m_mouseMode(mouseMode),
+ m_dsrSender(dsrSender)
+{
+ addDefaultEntriesToInputMap(m_inputMap);
+ if (hasDebugFlag("dump_input_map")) {
+ m_inputMap.dumpInputMap();
+ }
+
+ // Configure Quick Edit mode according to the mouse mode. Enable
+ // InsertMode for two reasons:
+ // - If it's OFF, it's difficult for the user to turn it ON. The
+ // properties dialog is inaccesible. winpty still faithfully handles
+ // the Insert key, which toggles between the insertion and overwrite
+ // modes.
+ // - When we modify the QuickEdit setting, if ExtendedFlags is OFF,
+ // then we must choose the InsertMode setting. I don't *think* this
+ // case happens, though, because a new console always has ExtendedFlags
+ // ON.
+ // See misc/EnableExtendedFlags.txt.
+ DWORD mode = 0;
+ if (!GetConsoleMode(conin, &mode)) {
+ trace("Agent startup: GetConsoleMode failed");
+ } else {
+ mode |= ENABLE_EXTENDED_FLAGS;
+ mode |= ENABLE_INSERT_MODE;
+ if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) {
+ mode |= ENABLE_QUICK_EDIT_MODE;
+ } else {
+ mode &= ~ENABLE_QUICK_EDIT_MODE;
+ }
+ if (!SetConsoleMode(conin, mode)) {
+ trace("Agent startup: SetConsoleMode failed");
+ }
+ }
+
+ updateInputFlags(true);
+}
+
+void ConsoleInput::writeInput(const std::string &input)
+{
+ if (input.size() == 0) {
+ return;
+ }
+
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ std::string dumpString;
+ for (size_t i = 0; i < input.size(); ++i) {
+ const char ch = input[i];
+ const char ctrl = decodeUnixCtrlChar(ch);
+ if (ctrl != '\0') {
+ dumpString += '^';
+ dumpString += ctrl;
+ } else {
+ dumpString += ch;
+ }
+ }
+ dumpString += " (";
+ for (size_t i = 0; i < input.size(); ++i) {
+ if (i > 0) {
+ dumpString += ' ';
+ }
+ const unsigned char uch = input[i];
+ char buf[32];
+ winpty_snprintf(buf, "%02X", uch);
+ dumpString += buf;
+ }
+ dumpString += ')';
+ trace("input chars: %s", dumpString.c_str());
+ }
+ }
+
+ m_byteQueue.append(input);
+ doWrite(false);
+ if (!m_byteQueue.empty() && !m_dsrSent) {
+ trace("send DSR");
+ m_dsrSender.sendDsr();
+ m_dsrSent = true;
+ }
+ m_lastWriteTick = GetTickCount();
+}
+
+void ConsoleInput::flushIncompleteEscapeCode()
+{
+ if (!m_byteQueue.empty() &&
+ (GetTickCount() - m_lastWriteTick) > kIncompleteEscapeTimeoutMs) {
+ doWrite(true);
+ m_byteQueue.clear();
+ }
+}
+
+void ConsoleInput::updateInputFlags(bool forceTrace)
+{
+ const DWORD mode = inputConsoleMode();
+ const bool newFlagEE = (mode & ENABLE_EXTENDED_FLAGS) != 0;
+ const bool newFlagMI = (mode & ENABLE_MOUSE_INPUT) != 0;
+ const bool newFlagQE = (mode & ENABLE_QUICK_EDIT_MODE) != 0;
+ const bool newFlagEI = (mode & 0x200) != 0;
+ if (forceTrace ||
+ newFlagEE != m_enableExtendedEnabled ||
+ newFlagMI != m_mouseInputEnabled ||
+ newFlagQE != m_quickEditEnabled ||
+ newFlagEI != m_escapeInputEnabled) {
+ trace("CONIN modes: Extended=%s, MouseInput=%s QuickEdit=%s EscapeInput=%s",
+ newFlagEE ? "on" : "off",
+ newFlagMI ? "on" : "off",
+ newFlagQE ? "on" : "off",
+ newFlagEI ? "on" : "off");
+ }
+ m_enableExtendedEnabled = newFlagEE;
+ m_mouseInputEnabled = newFlagMI;
+ m_quickEditEnabled = newFlagQE;
+ m_escapeInputEnabled = newFlagEI;
+}
+
+bool ConsoleInput::shouldActivateTerminalMouse()
+{
+ // Return whether the agent should activate the terminal's mouse mode.
+ if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) {
+ // Some programs (e.g. Cygwin command-line programs like bash.exe and
+ // python2.7.exe) turn off ENABLE_EXTENDED_FLAGS and turn on
+ // ENABLE_MOUSE_INPUT, but do not turn off QuickEdit mode and do not
+ // actually care about mouse input. Only enable the terminal mouse
+ // mode if ENABLE_EXTENDED_FLAGS is on. See
+ // misc/EnableExtendedFlags.txt.
+ return m_mouseInputEnabled && !m_quickEditEnabled &&
+ m_enableExtendedEnabled;
+ } else if (m_mouseMode == WINPTY_MOUSE_MODE_FORCE) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ConsoleInput::doWrite(bool isEof)
+{
+ const char *data = m_byteQueue.c_str();
+ std::vector<INPUT_RECORD> records;
+ size_t idx = 0;
+ while (idx < m_byteQueue.size()) {
+ int charSize = scanInput(records, &data[idx], m_byteQueue.size() - idx, isEof);
+ if (charSize == -1)
+ break;
+ idx += charSize;
+ }
+ m_byteQueue.erase(0, idx);
+ flushInputRecords(records);
+}
+
+void ConsoleInput::flushInputRecords(std::vector<INPUT_RECORD> &records)
+{
+ if (records.size() == 0) {
+ return;
+ }
+ DWORD actual = 0;
+ if (!WriteConsoleInputW(m_conin, records.data(), records.size(), &actual)) {
+ trace("WriteConsoleInputW failed");
+ }
+ records.clear();
+}
+
+// This behavior isn't strictly correct, because the keypresses (probably?)
+// adopt the keyboard state (e.g. Ctrl/Alt/Shift modifiers) of the current
+// window station's keyboard, which has no necessary relationship to the winpty
+// instance. It's unlikely to be an issue in practice, but it's conceivable.
+// (Imagine a foreground SSH server, where the local user holds down Ctrl,
+// while the remote user tries to use WSL navigation keys.) I suspect using
+// the BackgroundDesktop mechanism in winpty would fix the problem.
+//
+// https://github.com/rprichard/winpty/issues/116
+static void sendKeyMessage(HWND hwnd, bool isKeyDown, uint16_t virtualKey)
+{
+ uint32_t scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);
+ if (scanCode > 255) {
+ scanCode = 0;
+ }
+ SendMessage(hwnd, isKeyDown ? WM_KEYDOWN : WM_KEYUP, virtualKey,
+ (scanCode << 16) | 1u | (isKeyDown ? 0u : 0xc0000000u));
+}
+
+int ConsoleInput::scanInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize,
+ bool isEof)
+{
+ ASSERT(inputSize >= 1);
+
+ // Ctrl-C.
+ //
+ // In processed mode, use GenerateConsoleCtrlEvent so that Ctrl-C handlers
+ // are called. GenerateConsoleCtrlEvent unfortunately doesn't interrupt
+ // ReadConsole calls[1]. Using WM_KEYDOWN/UP fixes the ReadConsole
+ // problem, but breaks in background window stations/desktops.
+ //
+ // In unprocessed mode, there's an entry for Ctrl-C in the SimpleEncoding
+ // table in DefaultInputMap.
+ //
+ // [1] https://github.com/rprichard/winpty/issues/116
+ if (input[0] == '\x03' && (inputConsoleMode() & ENABLE_PROCESSED_INPUT)) {
+ flushInputRecords(records);
+ trace("Ctrl-C");
+ const BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
+ trace("GenerateConsoleCtrlEvent: %d", ret);
+ return 1;
+ }
+
+ if (input[0] == '\x1B') {
+ // Attempt to match the Device Status Report (DSR) reply.
+ int dsrLen = matchDsr(input, inputSize);
+ if (dsrLen > 0) {
+ trace("Received a DSR reply");
+ m_dsrSent = false;
+ return dsrLen;
+ } else if (!isEof && dsrLen == -1) {
+ // Incomplete DSR match.
+ trace("Incomplete DSR match");
+ return -1;
+ }
+
+ int mouseLen = scanMouseInput(records, input, inputSize);
+ if (mouseLen > 0 || (!isEof && mouseLen == -1)) {
+ return mouseLen;
+ }
+ }
+
+ // Search the input map.
+ InputMap::Key match;
+ bool incomplete;
+ int matchLen = m_inputMap.lookupKey(input, inputSize, match, incomplete);
+ if (!isEof && incomplete) {
+ // Incomplete match -- need more characters (or wait for a
+ // timeout to signify flushed input).
+ trace("Incomplete escape sequence");
+ return -1;
+ } else if (matchLen > 0) {
+ uint32_t winCodePointDn = match.unicodeChar;
+ if ((match.keyState & LEFT_CTRL_PRESSED) && (match.keyState & LEFT_ALT_PRESSED)) {
+ winCodePointDn = '\0';
+ }
+ uint32_t winCodePointUp = winCodePointDn;
+ if (match.keyState & LEFT_ALT_PRESSED) {
+ winCodePointUp = '\0';
+ }
+ appendKeyPress(records, match.virtualKey,
+ winCodePointDn, winCodePointUp, match.keyState,
+ match.unicodeChar, match.keyState);
+ return matchLen;
+ }
+
+ // Recognize Alt-<character>.
+ //
+ // This code doesn't match Alt-ESC, which is encoded as `ESC ESC`, but
+ // maybe it should. I was concerned that pressing ESC rapidly enough could
+ // accidentally trigger Alt-ESC. (e.g. The user would have to be faster
+ // than the DSR flushing mechanism or use a decrepit terminal. The user
+ // might be on a slow network connection.)
+ if (input[0] == '\x1B' && inputSize >= 2 && input[1] != '\x1B') {
+ const int len = utf8CharLength(input[1]);
+ if (len > 0) {
+ if (1 + len > inputSize) {
+ // Incomplete character.
+ trace("Incomplete UTF-8 character in Alt-<Char>");
+ return -1;
+ }
+ appendUtf8Char(records, &input[1], len, true);
+ return 1 + len;
+ }
+ }
+
+ // A UTF-8 character.
+ const int len = utf8CharLength(input[0]);
+ if (len == 0) {
+ static bool debugInput = isTracingEnabled() && hasDebugFlag("input");
+ if (debugInput) {
+ trace("Discarding invalid input byte: %02X",
+ static_cast<unsigned char>(input[0]));
+ }
+ return 1;
+ }
+ if (len > inputSize) {
+ // Incomplete character.
+ trace("Incomplete UTF-8 character");
+ return -1;
+ }
+ appendUtf8Char(records, &input[0], len, false);
+ return len;
+}
+
+int ConsoleInput::scanMouseInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize)
+{
+ MouseRecord record;
+ const int len = matchMouseRecord(input, inputSize, record);
+ if (len <= 0) {
+ return len;
+ }
+
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ trace("mouse input: %s", record.toString().c_str());
+ }
+ }
+
+ const int button = record.flags & 0x03;
+ INPUT_RECORD newRecord = {0};
+ newRecord.EventType = MOUSE_EVENT;
+ MOUSE_EVENT_RECORD &mer = newRecord.Event.MouseEvent;
+
+ mer.dwMousePosition.X =
+ m_mouseWindowRect.Left +
+ std::max(0, std::min<int>(record.coord.X,
+ m_mouseWindowRect.width() - 1));
+
+ mer.dwMousePosition.Y =
+ m_mouseWindowRect.Top +
+ std::max(0, std::min<int>(record.coord.Y,
+ m_mouseWindowRect.height() - 1));
+
+ // The modifier state is neatly independent of everything else.
+ if (record.flags & 0x04) { mer.dwControlKeyState |= SHIFT_PRESSED; }
+ if (record.flags & 0x08) { mer.dwControlKeyState |= LEFT_ALT_PRESSED; }
+ if (record.flags & 0x10) { mer.dwControlKeyState |= LEFT_CTRL_PRESSED; }
+
+ if (record.flags & 0x40) {
+ // Mouse wheel
+ mer.dwEventFlags |= MOUSE_WHEELED;
+ if (button == 0) {
+ // up
+ mer.dwButtonState |= 0x00780000;
+ } else if (button == 1) {
+ // down
+ mer.dwButtonState |= 0xff880000;
+ } else {
+ // Invalid -- do nothing
+ return len;
+ }
+ } else {
+ // Ordinary mouse event
+ if (record.flags & 0x20) { mer.dwEventFlags |= MOUSE_MOVED; }
+ if (button == 3) {
+ m_mouseButtonState = 0;
+ // Potentially advance double-click detection.
+ m_doubleClick.released = true;
+ } else {
+ const DWORD relevantFlag =
+ (button == 0) ? FROM_LEFT_1ST_BUTTON_PRESSED :
+ (button == 1) ? FROM_LEFT_2ND_BUTTON_PRESSED :
+ (button == 2) ? RIGHTMOST_BUTTON_PRESSED :
+ 0;
+ ASSERT(relevantFlag != 0);
+ if (record.release) {
+ m_mouseButtonState &= ~relevantFlag;
+ if (relevantFlag == m_doubleClick.button) {
+ // Potentially advance double-click detection.
+ m_doubleClick.released = true;
+ } else {
+ // End double-click detection.
+ m_doubleClick = DoubleClickDetection();
+ }
+ } else if ((m_mouseButtonState & relevantFlag) == 0) {
+ // The button has been newly pressed.
+ m_mouseButtonState |= relevantFlag;
+ // Detect a double-click. This code looks for an exact
+ // coordinate match, which is stricter than what Windows does,
+ // but Windows has pixel coordinates, and we only have terminal
+ // coordinates.
+ if (m_doubleClick.button == relevantFlag &&
+ m_doubleClick.pos == record.coord &&
+ (GetTickCount() - m_doubleClick.tick <
+ GetDoubleClickTime())) {
+ // Record a double-click and end double-click detection.
+ mer.dwEventFlags |= DOUBLE_CLICK;
+ m_doubleClick = DoubleClickDetection();
+ } else {
+ // Begin double-click detection.
+ m_doubleClick.button = relevantFlag;
+ m_doubleClick.pos = record.coord;
+ m_doubleClick.tick = GetTickCount();
+ }
+ }
+ }
+ }
+
+ mer.dwButtonState |= m_mouseButtonState;
+
+ if (m_mouseInputEnabled && !m_quickEditEnabled) {
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ trace("mouse event: %s", mouseEventToString(mer).c_str());
+ }
+ }
+
+ records.push_back(newRecord);
+ }
+
+ return len;
+}
+
+void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
+ const char *charBuffer,
+ const int charLen,
+ const bool terminalAltEscape)
+{
+ const uint32_t codePoint = decodeUtf8(charBuffer);
+ if (codePoint == static_cast<uint32_t>(-1)) {
+ static bool debugInput = isTracingEnabled() && hasDebugFlag("input");
+ if (debugInput) {
+ StringBuilder error(64);
+ error << "Discarding invalid UTF-8 sequence:";
+ for (int i = 0; i < charLen; ++i) {
+ error << ' ';
+ error << hexOfInt<true, uint8_t>(charBuffer[i]);
+ }
+ trace("%s", error.c_str());
+ }
+ return;
+ }
+
+ const short charScan = codePoint > 0xFFFF ? -1 : VkKeyScan(codePoint);
+ uint16_t virtualKey = 0;
+ uint16_t winKeyState = 0;
+ uint32_t winCodePointDn = codePoint;
+ uint32_t winCodePointUp = codePoint;
+ uint16_t vtKeyState = 0;
+
+ if (charScan != -1) {
+ virtualKey = charScan & 0xFF;
+ if (charScan & 0x100) {
+ winKeyState |= SHIFT_PRESSED;
+ }
+ if (charScan & 0x200) {
+ winKeyState |= LEFT_CTRL_PRESSED;
+ }
+ if (charScan & 0x400) {
+ winKeyState |= RIGHT_ALT_PRESSED;
+ }
+ if (terminalAltEscape && (winKeyState & LEFT_CTRL_PRESSED)) {
+ // If the terminal escapes a Ctrl-<Key> with Alt, then set the
+ // codepoint to 0. On the other hand, if a character requires
+ // AltGr (like U+00B2 on a German layout), then VkKeyScan will
+ // report both Ctrl and Alt pressed, and we should keep the
+ // codepoint. See https://github.com/rprichard/winpty/issues/109.
+ winCodePointDn = 0;
+ winCodePointUp = 0;
+ }
+ }
+ if (terminalAltEscape) {
+ winCodePointUp = 0;
+ winKeyState |= LEFT_ALT_PRESSED;
+ vtKeyState |= LEFT_ALT_PRESSED;
+ }
+
+ appendKeyPress(records, virtualKey,
+ winCodePointDn, winCodePointUp, winKeyState,
+ codePoint, vtKeyState);
+}
+
+void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
+ const uint16_t virtualKey,
+ const uint32_t winCodePointDn,
+ const uint32_t winCodePointUp,
+ const uint16_t winKeyState,
+ const uint32_t vtCodePoint,
+ const uint16_t vtKeyState)
+{
+ const bool ctrl = (winKeyState & LEFT_CTRL_PRESSED) != 0;
+ const bool leftAlt = (winKeyState & LEFT_ALT_PRESSED) != 0;
+ const bool rightAlt = (winKeyState & RIGHT_ALT_PRESSED) != 0;
+ const bool shift = (winKeyState & SHIFT_PRESSED) != 0;
+ const bool enhanced = (winKeyState & ENHANCED_KEY) != 0;
+ bool hasDebugInput = false;
+
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ hasDebugInput = true;
+ InputMap::Key key = { virtualKey, winCodePointDn, winKeyState };
+ trace("keypress: %s", key.toString().c_str());
+ }
+ }
+
+ if (m_escapeInputEnabled &&
+ (virtualKey == VK_UP ||
+ virtualKey == VK_DOWN ||
+ virtualKey == VK_LEFT ||
+ virtualKey == VK_RIGHT ||
+ virtualKey == VK_HOME ||
+ virtualKey == VK_END) &&
+ !ctrl && !leftAlt && !rightAlt && !shift) {
+ flushInputRecords(records);
+ if (hasDebugInput) {
+ trace("sending keypress to console HWND");
+ }
+ sendKeyMessage(m_console.hwnd(), true, virtualKey);
+ sendKeyMessage(m_console.hwnd(), false, virtualKey);
+ return;
+ }
+
+ uint16_t stepKeyState = 0;
+ if (ctrl) {
+ stepKeyState |= LEFT_CTRL_PRESSED;
+ appendInputRecord(records, TRUE, VK_CONTROL, 0, stepKeyState);
+ }
+ if (leftAlt) {
+ stepKeyState |= LEFT_ALT_PRESSED;
+ appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState);
+ }
+ if (rightAlt) {
+ stepKeyState |= RIGHT_ALT_PRESSED;
+ appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState | ENHANCED_KEY);
+ }
+ if (shift) {
+ stepKeyState |= SHIFT_PRESSED;
+ appendInputRecord(records, TRUE, VK_SHIFT, 0, stepKeyState);
+ }
+ if (enhanced) {
+ stepKeyState |= ENHANCED_KEY;
+ }
+ if (m_escapeInputEnabled) {
+ reencodeEscapedKeyPress(records, virtualKey, vtCodePoint, vtKeyState);
+ } else {
+ appendCPInputRecords(records, TRUE, virtualKey, winCodePointDn, stepKeyState);
+ }
+ appendCPInputRecords(records, FALSE, virtualKey, winCodePointUp, stepKeyState);
+ if (enhanced) {
+ stepKeyState &= ~ENHANCED_KEY;
+ }
+ if (shift) {
+ stepKeyState &= ~SHIFT_PRESSED;
+ appendInputRecord(records, FALSE, VK_SHIFT, 0, stepKeyState);
+ }
+ if (rightAlt) {
+ stepKeyState &= ~RIGHT_ALT_PRESSED;
+ appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState | ENHANCED_KEY);
+ }
+ if (leftAlt) {
+ stepKeyState &= ~LEFT_ALT_PRESSED;
+ appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState);
+ }
+ if (ctrl) {
+ stepKeyState &= ~LEFT_CTRL_PRESSED;
+ appendInputRecord(records, FALSE, VK_CONTROL, 0, stepKeyState);
+ }
+}
+
+void ConsoleInput::appendCPInputRecords(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState)
+{
+ // This behavior really doesn't match that of the Windows console (in
+ // normal, non-escape-mode). Judging by the copy-and-paste behavior,
+ // Windows apparently handles everything outside of the keyboard layout by
+ // first sending a sequence of Alt+KeyPad events, then finally a key-up
+ // event whose UnicodeChar has the appropriate value. For U+00A2 (CENT
+ // SIGN):
+ //
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=79 LAlt-NUMPAD1 ch=0
+ // key: up rpt=1 scn=79 LAlt-NUMPAD1 ch=0
+ // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xa2
+ //
+ // The Alt+155 value matches the encoding of U+00A2 in CP-437. Curiously,
+ // if I use "chcp 1252" to change the encoding, then copy-and-pasting
+ // produces Alt+162 instead. (U+00A2 is 162 in CP-1252.) However, typing
+ // Alt+155 or Alt+162 produce the same characters regardless of console
+ // code page. (That is, they use CP-437 and yield U+00A2 and U+00F3.)
+ //
+ // For characters outside the BMP, Windows repeats the process for both
+ // UTF-16 code units, e.g, for U+1F300 (CYCLONE):
+ //
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xd83c
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xdf00
+ //
+ // In this case, it sends Alt+63 twice, which signifies '?'. Apparently
+ // CMD and Cygwin bash are both able to decode this.
+ //
+ // Also note that typing Alt+NNN still works if NumLock is off, e.g.:
+ //
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=79 LAlt-END ch=0
+ // key: up rpt=1 scn=79 LAlt-END ch=0
+ // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: up rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: up rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xa2
+ //
+ // Evidently, the Alt+NNN key events are not intended to be decoded to a
+ // character. Maybe programs are looking for a key-up ALT/MENU event with
+ // a non-zero character?
+
+ wchar_t ws[2];
+ const int wslen = encodeUtf16(ws, codePoint);
+
+ if (wslen == 1) {
+ appendInputRecord(records, keyDown, virtualKey, ws[0], keyState);
+ } else if (wslen == 2) {
+ appendInputRecord(records, keyDown, virtualKey, ws[0], keyState);
+ appendInputRecord(records, keyDown, virtualKey, ws[1], keyState);
+ } else {
+ // This situation isn't that bad, but it should never happen,
+ // because invalid codepoints shouldn't reach this point.
+ trace("INTERNAL ERROR: appendInputRecordCP: invalid codePoint: "
+ "U+%04X", codePoint);
+ }
+}
+
+void ConsoleInput::appendInputRecord(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ wchar_t utf16Char,
+ uint16_t keyState)
+{
+ INPUT_RECORD ir = {};
+ ir.EventType = KEY_EVENT;
+ ir.Event.KeyEvent.bKeyDown = keyDown;
+ ir.Event.KeyEvent.wRepeatCount = 1;
+ ir.Event.KeyEvent.wVirtualKeyCode = virtualKey;
+ ir.Event.KeyEvent.wVirtualScanCode =
+ MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);
+ ir.Event.KeyEvent.uChar.UnicodeChar = utf16Char;
+ ir.Event.KeyEvent.dwControlKeyState = keyState;
+ records.push_back(ir);
+}
+
+DWORD ConsoleInput::inputConsoleMode()
+{
+ DWORD mode = 0;
+ if (!GetConsoleMode(m_conin, &mode)) {
+ trace("GetConsoleMode failed");
+ return 0;
+ }
+ return mode;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h
new file mode 100644
index 0000000000..e807d973ba
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef CONSOLEINPUT_H
+#define CONSOLEINPUT_H
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Coord.h"
+#include "InputMap.h"
+#include "SmallRect.h"
+
+class Win32Console;
+class DsrSender;
+
+class ConsoleInput
+{
+public:
+ ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender,
+ Win32Console &console);
+ void writeInput(const std::string &input);
+ void flushIncompleteEscapeCode();
+ void setMouseWindowRect(SmallRect val) { m_mouseWindowRect = val; }
+ void updateInputFlags(bool forceTrace=false);
+ bool shouldActivateTerminalMouse();
+
+private:
+ void doWrite(bool isEof);
+ void flushInputRecords(std::vector<INPUT_RECORD> &records);
+ int scanInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize,
+ bool isEof);
+ int scanMouseInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize);
+ void appendUtf8Char(std::vector<INPUT_RECORD> &records,
+ const char *charBuffer,
+ int charLen,
+ bool terminalAltEscape);
+ void appendKeyPress(std::vector<INPUT_RECORD> &records,
+ uint16_t virtualKey,
+ uint32_t winCodePointDn,
+ uint32_t winCodePointUp,
+ uint16_t winKeyState,
+ uint32_t vtCodePoint,
+ uint16_t vtKeyState);
+
+public:
+ static void appendCPInputRecords(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState);
+ static void appendInputRecord(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ wchar_t utf16Char,
+ uint16_t keyState);
+
+private:
+ DWORD inputConsoleMode();
+
+private:
+ Win32Console &m_console;
+ HANDLE m_conin = nullptr;
+ int m_mouseMode = 0;
+ DsrSender &m_dsrSender;
+ bool m_dsrSent = false;
+ std::string m_byteQueue;
+ InputMap m_inputMap;
+ DWORD m_lastWriteTick = 0;
+ DWORD m_mouseButtonState = 0;
+ struct DoubleClickDetection {
+ DWORD button = 0;
+ Coord pos;
+ DWORD tick = 0;
+ bool released = false;
+ } m_doubleClick;
+ bool m_enableExtendedEnabled = false;
+ bool m_mouseInputEnabled = false;
+ bool m_quickEditEnabled = false;
+ bool m_escapeInputEnabled = false;
+ SmallRect m_mouseWindowRect;
+};
+
+#endif // CONSOLEINPUT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc
new file mode 100644
index 0000000000..b79545eea9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "ConsoleInputReencoding.h"
+
+#include "ConsoleInput.h"
+
+namespace {
+
+static void outch(std::vector<INPUT_RECORD> &out, wchar_t ch) {
+ ConsoleInput::appendInputRecord(out, TRUE, 0, ch, 0);
+}
+
+} // anonymous namespace
+
+void reencodeEscapedKeyPress(
+ std::vector<INPUT_RECORD> &out,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState) {
+
+ struct EscapedKey {
+ enum { None, Numeric, Letter } kind;
+ wchar_t content[2];
+ };
+
+ EscapedKey escapeCode = {};
+ switch (virtualKey) {
+ case VK_UP: escapeCode = { EscapedKey::Letter, {'A'} }; break;
+ case VK_DOWN: escapeCode = { EscapedKey::Letter, {'B'} }; break;
+ case VK_RIGHT: escapeCode = { EscapedKey::Letter, {'C'} }; break;
+ case VK_LEFT: escapeCode = { EscapedKey::Letter, {'D'} }; break;
+ case VK_CLEAR: escapeCode = { EscapedKey::Letter, {'E'} }; break;
+ case VK_F1: escapeCode = { EscapedKey::Numeric, {'1', '1'} }; break;
+ case VK_F2: escapeCode = { EscapedKey::Numeric, {'1', '2'} }; break;
+ case VK_F3: escapeCode = { EscapedKey::Numeric, {'1', '3'} }; break;
+ case VK_F4: escapeCode = { EscapedKey::Numeric, {'1', '4'} }; break;
+ case VK_F5: escapeCode = { EscapedKey::Numeric, {'1', '5'} }; break;
+ case VK_F6: escapeCode = { EscapedKey::Numeric, {'1', '7'} }; break;
+ case VK_F7: escapeCode = { EscapedKey::Numeric, {'1', '8'} }; break;
+ case VK_F8: escapeCode = { EscapedKey::Numeric, {'1', '9'} }; break;
+ case VK_F9: escapeCode = { EscapedKey::Numeric, {'2', '0'} }; break;
+ case VK_F10: escapeCode = { EscapedKey::Numeric, {'2', '1'} }; break;
+ case VK_F11: escapeCode = { EscapedKey::Numeric, {'2', '3'} }; break;
+ case VK_F12: escapeCode = { EscapedKey::Numeric, {'2', '4'} }; break;
+ case VK_HOME: escapeCode = { EscapedKey::Letter, {'H'} }; break;
+ case VK_INSERT: escapeCode = { EscapedKey::Numeric, {'2'} }; break;
+ case VK_DELETE: escapeCode = { EscapedKey::Numeric, {'3'} }; break;
+ case VK_END: escapeCode = { EscapedKey::Letter, {'F'} }; break;
+ case VK_PRIOR: escapeCode = { EscapedKey::Numeric, {'5'} }; break;
+ case VK_NEXT: escapeCode = { EscapedKey::Numeric, {'6'} }; break;
+ }
+ if (escapeCode.kind != EscapedKey::None) {
+ int flags = 0;
+ if (keyState & SHIFT_PRESSED) { flags |= 0x1; }
+ if (keyState & LEFT_ALT_PRESSED) { flags |= 0x2; }
+ if (keyState & LEFT_CTRL_PRESSED) { flags |= 0x4; }
+ outch(out, L'\x1b');
+ outch(out, L'[');
+ if (escapeCode.kind == EscapedKey::Numeric) {
+ for (wchar_t ch : escapeCode.content) {
+ if (ch != L'\0') {
+ outch(out, ch);
+ }
+ }
+ } else if (flags != 0) {
+ outch(out, L'1');
+ }
+ if (flags != 0) {
+ outch(out, L';');
+ outch(out, L'1' + flags);
+ }
+ if (escapeCode.kind == EscapedKey::Numeric) {
+ outch(out, L'~');
+ } else {
+ outch(out, escapeCode.content[0]);
+ }
+ return;
+ }
+
+ switch (virtualKey) {
+ case VK_BACK:
+ if (keyState & LEFT_ALT_PRESSED) {
+ outch(out, L'\x1b');
+ }
+ outch(out, L'\x7f');
+ return;
+ case VK_TAB:
+ if (keyState & SHIFT_PRESSED) {
+ outch(out, L'\x1b');
+ outch(out, L'[');
+ outch(out, L'Z');
+ return;
+ }
+ break;
+ }
+
+ if (codePoint != 0) {
+ if (keyState & LEFT_ALT_PRESSED) {
+ outch(out, L'\x1b');
+ }
+ ConsoleInput::appendCPInputRecords(out, TRUE, 0, codePoint, 0);
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h
new file mode 100644
index 0000000000..63bc006b5a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_CONSOLE_INPUT_REENCODING_H
+#define AGENT_CONSOLE_INPUT_REENCODING_H
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <vector>
+
+void reencodeEscapedKeyPress(
+ std::vector<INPUT_RECORD> &records,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState);
+
+#endif // AGENT_CONSOLE_INPUT_REENCODING_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc
new file mode 100644
index 0000000000..1d2bcb7685
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+//
+// ConsoleLine
+//
+// This data structure keep tracks of the previous CHAR_INFO content of an
+// output line and determines when a line has changed. Detecting line changes
+// is made complicated by terminal resizing.
+//
+
+#include "ConsoleLine.h"
+
+#include <algorithm>
+
+#include "../shared/WinptyAssert.h"
+
+static CHAR_INFO blankChar(WORD attributes)
+{
+ // N.B.: As long as we write to UnicodeChar rather than AsciiChar, there
+ // are no padding bytes that could contain uninitialized bytes. This fact
+ // is important for efficient comparison.
+ CHAR_INFO ret;
+ ret.Attributes = attributes;
+ ret.Char.UnicodeChar = L' ';
+ return ret;
+}
+
+static bool isLineBlank(const CHAR_INFO *line, int length, WORD attributes)
+{
+ for (int col = 0; col < length; ++col) {
+ if (line[col].Attributes != attributes ||
+ line[col].Char.UnicodeChar != L' ') {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool areLinesEqual(
+ const CHAR_INFO *line1,
+ const CHAR_INFO *line2,
+ int length)
+{
+ return memcmp(line1, line2, sizeof(CHAR_INFO) * length) == 0;
+}
+
+ConsoleLine::ConsoleLine() : m_prevLength(0)
+{
+}
+
+void ConsoleLine::reset()
+{
+ m_prevLength = 0;
+ m_prevData.clear();
+}
+
+// Determines whether the given line is sufficiently different from the
+// previously seen line as to justify reoutputting the line. The function
+// also sets the `ConsoleLine` to the given line, exactly as if `setLine` had
+// been called.
+bool ConsoleLine::detectChangeAndSetLine(const CHAR_INFO *const line, const int newLength)
+{
+ ASSERT(newLength >= 1);
+ ASSERT(m_prevLength <= static_cast<int>(m_prevData.size()));
+
+ if (newLength == m_prevLength) {
+ bool equalLines = areLinesEqual(m_prevData.data(), line, newLength);
+ if (!equalLines) {
+ setLine(line, newLength);
+ }
+ return !equalLines;
+ } else {
+ if (m_prevLength == 0) {
+ setLine(line, newLength);
+ return true;
+ }
+
+ ASSERT(m_prevLength >= 1);
+ const WORD prevBlank = m_prevData[m_prevLength - 1].Attributes;
+ const WORD newBlank = line[newLength - 1].Attributes;
+
+ bool equalLines = false;
+ if (newLength < m_prevLength) {
+ // The line has become shorter. The lines are equal if the common
+ // part is equal, and if the newly truncated characters were blank.
+ equalLines =
+ areLinesEqual(m_prevData.data(), line, newLength) &&
+ isLineBlank(m_prevData.data() + newLength,
+ m_prevLength - newLength,
+ newBlank);
+ } else {
+ //
+ // The line has become longer. The lines are equal if the common
+ // part is equal, and if both the extra characters and any
+ // potentially reexposed characters are blank.
+ //
+ // Two of the most relevant terminals for winpty--mintty and
+ // jediterm--don't (currently) erase the obscured content when a
+ // line is cleared, so we should anticipate its existence when
+ // making a terminal wider and reoutput the line. See:
+ //
+ // * https://github.com/mintty/mintty/issues/480
+ // * https://github.com/JetBrains/jediterm/issues/118
+ //
+ ASSERT(newLength > m_prevLength);
+ equalLines =
+ areLinesEqual(m_prevData.data(), line, m_prevLength) &&
+ isLineBlank(m_prevData.data() + m_prevLength,
+ std::min<int>(m_prevData.size(), newLength) - m_prevLength,
+ prevBlank) &&
+ isLineBlank(line + m_prevLength,
+ newLength - m_prevLength,
+ prevBlank);
+ }
+ setLine(line, newLength);
+ return !equalLines;
+ }
+}
+
+void ConsoleLine::setLine(const CHAR_INFO *const line, const int newLength)
+{
+ if (static_cast<int>(m_prevData.size()) < newLength) {
+ m_prevData.resize(newLength);
+ }
+ memcpy(m_prevData.data(), line, sizeof(CHAR_INFO) * newLength);
+ m_prevLength = newLength;
+}
+
+void ConsoleLine::blank(WORD attributes)
+{
+ m_prevData.resize(1);
+ m_prevData[0] = blankChar(attributes);
+ m_prevLength = 1;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h
new file mode 100644
index 0000000000..802c189c75
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef CONSOLE_LINE_H
+#define CONSOLE_LINE_H
+
+#include <windows.h>
+
+#include <vector>
+
+class ConsoleLine
+{
+public:
+ ConsoleLine();
+ void reset();
+ bool detectChangeAndSetLine(const CHAR_INFO *line, int newLength);
+ void setLine(const CHAR_INFO *line, int newLength);
+ void blank(WORD attributes);
+private:
+ int m_prevLength;
+ std::vector<CHAR_INFO> m_prevData;
+};
+
+#endif // CONSOLE_LINE_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Coord.h b/src/libs/3rdparty/winpty/src/agent/Coord.h
new file mode 100644
index 0000000000..74c98addac
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Coord.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef COORD_H
+#define COORD_H
+
+#include <windows.h>
+
+#include <string>
+
+#include "../shared/winpty_snprintf.h"
+
+struct Coord : COORD {
+ Coord()
+ {
+ X = 0;
+ Y = 0;
+ }
+
+ Coord(SHORT x, SHORT y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ Coord(COORD other)
+ {
+ *(COORD*)this = other;
+ }
+
+ Coord(const Coord &other)
+ {
+ *(COORD*)this = *(const COORD*)&other;
+ }
+
+ Coord &operator=(const Coord &other)
+ {
+ *(COORD*)this = *(const COORD*)&other;
+ return *this;
+ }
+
+ bool operator==(const Coord &other) const
+ {
+ return X == other.X && Y == other.Y;
+ }
+
+ bool operator!=(const Coord &other) const
+ {
+ return !(*this == other);
+ }
+
+ Coord operator+(const Coord &other) const
+ {
+ return Coord(X + other.X, Y + other.Y);
+ }
+
+ bool isEmpty() const
+ {
+ return X <= 0 || Y <= 0;
+ }
+
+ std::string toString() const
+ {
+ char ret[32];
+ winpty_snprintf(ret, "(%d,%d)", X, Y);
+ return std::string(ret);
+ }
+};
+
+#endif // COORD_H
diff --git a/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc
new file mode 100644
index 0000000000..191b2e1466
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "DebugShowInput.h"
+
+#include <windows.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "../shared/StringBuilder.h"
+#include "InputMap.h"
+
+namespace {
+
+struct Flag {
+ DWORD value;
+ const char *text;
+};
+
+static const Flag kButtonStates[] = {
+ { FROM_LEFT_1ST_BUTTON_PRESSED, "1" },
+ { FROM_LEFT_2ND_BUTTON_PRESSED, "2" },
+ { FROM_LEFT_3RD_BUTTON_PRESSED, "3" },
+ { FROM_LEFT_4TH_BUTTON_PRESSED, "4" },
+ { RIGHTMOST_BUTTON_PRESSED, "R" },
+};
+
+static const Flag kControlKeyStates[] = {
+ { CAPSLOCK_ON, "CapsLock" },
+ { ENHANCED_KEY, "Enhanced" },
+ { LEFT_ALT_PRESSED, "LAlt" },
+ { LEFT_CTRL_PRESSED, "LCtrl" },
+ { NUMLOCK_ON, "NumLock" },
+ { RIGHT_ALT_PRESSED, "RAlt" },
+ { RIGHT_CTRL_PRESSED, "RCtrl" },
+ { SCROLLLOCK_ON, "ScrollLock" },
+ { SHIFT_PRESSED, "Shift" },
+};
+
+static const Flag kMouseEventFlags[] = {
+ { DOUBLE_CLICK, "Double" },
+ { 8/*MOUSE_HWHEELED*/, "HWheel" },
+ { MOUSE_MOVED, "Move" },
+ { MOUSE_WHEELED, "Wheel" },
+};
+
+static void writeFlags(StringBuilder &out, DWORD flags,
+ const char *remainderName,
+ const Flag *table, size_t tableSize,
+ char pre, char sep, char post) {
+ DWORD remaining = flags;
+ bool wroteSomething = false;
+ for (size_t i = 0; i < tableSize; ++i) {
+ const Flag &f = table[i];
+ if ((f.value & flags) == f.value) {
+ if (!wroteSomething && pre != '\0') {
+ out << pre;
+ } else if (wroteSomething && sep != '\0') {
+ out << sep;
+ }
+ out << f.text;
+ wroteSomething = true;
+ remaining &= ~f.value;
+ }
+ }
+ if (remaining != 0) {
+ if (!wroteSomething && pre != '\0') {
+ out << pre;
+ } else if (wroteSomething && sep != '\0') {
+ out << sep;
+ }
+ out << remainderName << "(0x" << hexOfInt(remaining) << ')';
+ wroteSomething = true;
+ }
+ if (wroteSomething && post != '\0') {
+ out << post;
+ }
+}
+
+template <size_t n>
+static void writeFlags(StringBuilder &out, DWORD flags,
+ const char *remainderName,
+ const Flag (&table)[n],
+ char pre, char sep, char post) {
+ writeFlags(out, flags, remainderName, table, n, pre, sep, post);
+}
+
+} // anonymous namespace
+
+std::string controlKeyStatePrefix(DWORD controlKeyState) {
+ StringBuilder sb;
+ writeFlags(sb, controlKeyState,
+ "keyState", kControlKeyStates, '\0', '-', '-');
+ return sb.str_moved();
+}
+
+std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer) {
+ const uint16_t buttons = mer.dwButtonState & 0xFFFF;
+ const int16_t wheel = mer.dwButtonState >> 16;
+ StringBuilder sb;
+ sb << "pos=" << mer.dwMousePosition.X << ','
+ << mer.dwMousePosition.Y;
+ writeFlags(sb, mer.dwControlKeyState, "keyState", kControlKeyStates, ' ', ' ', '\0');
+ writeFlags(sb, mer.dwEventFlags, "flags", kMouseEventFlags, ' ', ' ', '\0');
+ writeFlags(sb, buttons, "buttons", kButtonStates, ' ', ' ', '\0');
+ if (wheel != 0) {
+ sb << " wheel=" << wheel;
+ }
+ return sb.str_moved();
+}
+
+void debugShowInput(bool enableMouse, bool escapeInput) {
+ HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD origConsoleMode = 0;
+ if (!GetConsoleMode(conin, &origConsoleMode)) {
+ fprintf(stderr, "Error: could not read console mode -- "
+ "is STDIN a console handle?\n");
+ exit(1);
+ }
+ DWORD restoreConsoleMode = origConsoleMode;
+ if (enableMouse && !(restoreConsoleMode & ENABLE_EXTENDED_FLAGS)) {
+ // We need to disable QuickEdit mode, because it blocks mouse events.
+ // If ENABLE_EXTENDED_FLAGS wasn't originally in the console mode, then
+ // we have no way of knowning whether QuickEdit or InsertMode are
+ // currently enabled. Enable them both (eventually), because they're
+ // sensible defaults. This case shouldn't happen typically. See
+ // misc/EnableExtendedFlags.txt.
+ restoreConsoleMode |= ENABLE_EXTENDED_FLAGS;
+ restoreConsoleMode |= ENABLE_QUICK_EDIT_MODE;
+ restoreConsoleMode |= ENABLE_INSERT_MODE;
+ }
+ DWORD newConsoleMode = restoreConsoleMode;
+ newConsoleMode &= ~ENABLE_PROCESSED_INPUT;
+ newConsoleMode &= ~ENABLE_LINE_INPUT;
+ newConsoleMode &= ~ENABLE_ECHO_INPUT;
+ newConsoleMode |= ENABLE_WINDOW_INPUT;
+ if (enableMouse) {
+ newConsoleMode |= ENABLE_MOUSE_INPUT;
+ newConsoleMode &= ~ENABLE_QUICK_EDIT_MODE;
+ } else {
+ newConsoleMode &= ~ENABLE_MOUSE_INPUT;
+ }
+ if (escapeInput) {
+ // As of this writing (2016-06-05), Microsoft has shipped two preview
+ // builds of Windows 10 (14316 and 14342) that include a new "Windows
+ // Subsystem for Linux" that runs Ubuntu in a new subsystem. Running
+ // bash in this subsystem requires the non-legacy console mode, and the
+ // console input buffer is put into a special mode where escape
+ // sequences are written into the console input buffer. This mode is
+ // enabled with the 0x200 flag, which is as-yet undocumented.
+ // See https://github.com/rprichard/winpty/issues/82.
+ newConsoleMode |= 0x200;
+ }
+ if (!SetConsoleMode(conin, newConsoleMode)) {
+ fprintf(stderr, "Error: could not set console mode "
+ "(0x%x -> 0x%x -> 0x%x)\n",
+ static_cast<unsigned int>(origConsoleMode),
+ static_cast<unsigned int>(newConsoleMode),
+ static_cast<unsigned int>(restoreConsoleMode));
+ exit(1);
+ }
+ printf("\nPress any keys -- Ctrl-D exits\n\n");
+ INPUT_RECORD records[32];
+ DWORD actual = 0;
+ bool finished = false;
+ while (!finished &&
+ ReadConsoleInputW(conin, records, 32, &actual) && actual >= 1) {
+ StringBuilder sb;
+ for (DWORD i = 0; i < actual; ++i) {
+ const INPUT_RECORD &record = records[i];
+ if (record.EventType == KEY_EVENT) {
+ const KEY_EVENT_RECORD &ker = record.Event.KeyEvent;
+ InputMap::Key key = {
+ ker.wVirtualKeyCode,
+ ker.uChar.UnicodeChar,
+ static_cast<uint16_t>(ker.dwControlKeyState),
+ };
+ sb << "key: " << (ker.bKeyDown ? "dn" : "up")
+ << " rpt=" << ker.wRepeatCount
+ << " scn=" << (ker.wVirtualScanCode ? "0x" : "") << hexOfInt(ker.wVirtualScanCode)
+ << ' ' << key.toString() << '\n';
+ if ((ker.dwControlKeyState &
+ (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
+ ker.wVirtualKeyCode == 'D') {
+ finished = true;
+ break;
+ } else if (ker.wVirtualKeyCode == 0 &&
+ ker.wVirtualScanCode == 0 &&
+ ker.uChar.UnicodeChar == 4) {
+ // Also look for a zeroed-out Ctrl-D record generated for
+ // ENABLE_VIRTUAL_TERMINAL_INPUT.
+ finished = true;
+ break;
+ }
+ } else if (record.EventType == MOUSE_EVENT) {
+ const MOUSE_EVENT_RECORD &mer = record.Event.MouseEvent;
+ sb << "mouse: " << mouseEventToString(mer) << '\n';
+ } else if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
+ const WINDOW_BUFFER_SIZE_RECORD &wbsr =
+ record.Event.WindowBufferSizeEvent;
+ sb << "buffer-resized: dwSize=("
+ << wbsr.dwSize.X << ','
+ << wbsr.dwSize.Y << ")\n";
+ } else if (record.EventType == MENU_EVENT) {
+ const MENU_EVENT_RECORD &mer = record.Event.MenuEvent;
+ sb << "menu-event: commandId=0x"
+ << hexOfInt(mer.dwCommandId) << '\n';
+ } else if (record.EventType == FOCUS_EVENT) {
+ const FOCUS_EVENT_RECORD &fer = record.Event.FocusEvent;
+ sb << "focus: " << (fer.bSetFocus ? "gained" : "lost") << '\n';
+ }
+ }
+
+ const auto str = sb.str_moved();
+ fwrite(str.data(), 1, str.size(), stdout);
+ fflush(stdout);
+ }
+ SetConsoleMode(conin, restoreConsoleMode);
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h
new file mode 100644
index 0000000000..4fa13604bd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_DEBUG_SHOW_INPUT_H
+#define AGENT_DEBUG_SHOW_INPUT_H
+
+#include <windows.h>
+
+#include <string>
+
+std::string controlKeyStatePrefix(DWORD controlKeyState);
+std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer);
+void debugShowInput(bool enableMouse, bool escapeInput);
+
+#endif // AGENT_DEBUG_SHOW_INPUT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc
new file mode 100644
index 0000000000..5e29d98e4e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "DefaultInputMap.h"
+
+#include <windows.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "../shared/StringBuilder.h"
+#include "../shared/WinptyAssert.h"
+#include "InputMap.h"
+
+#define ESC "\x1B"
+#define DIM(x) (sizeof(x) / sizeof((x)[0]))
+
+namespace {
+
+struct EscapeEncoding {
+ bool alt_prefix_allowed;
+ char prefix;
+ char id;
+ int modifiers;
+ InputMap::Key key;
+};
+
+// Modifiers. A "modifier" is an integer from 2 to 8 that conveys the status
+// of Shift(1), Alt(2), and Ctrl(4). The value is constructed by OR'ing the
+// appropriate value for each active modifier, then adding 1.
+//
+// Details:
+// - kBare: expands to: ESC <prefix> <suffix>
+// - kSemiMod: expands to: ESC <prefix> <numid> ; <mod> <suffix>
+// - kBareMod: expands to: ESC <prefix> <mod> <suffix>
+const int kBare = 0x01;
+const int kSemiMod = 0x02;
+const int kBareMod = 0x04;
+
+// Numeric escape sequences suffixes:
+// - with no flag: accept: ~
+// - kSuffixCtrl: accept: ~ ^
+// - kSuffixShift: accept: ~ $
+// - kSuffixBoth: accept: ~ ^ $ @
+const int kSuffixCtrl = 0x08;
+const int kSuffixShift = 0x10;
+const int kSuffixBoth = kSuffixCtrl | kSuffixShift;
+
+static const EscapeEncoding escapeLetterEncodings[] = {
+ // Conventional arrow keys
+ // kBareMod: Ubuntu /etc/inputrc and IntelliJ/JediTerm use escapes like: ESC [ n ABCD
+ { true, '[', 'A', kBare | kBareMod | kSemiMod, { VK_UP, '\0', 0 } },
+ { true, '[', 'B', kBare | kBareMod | kSemiMod, { VK_DOWN, '\0', 0 } },
+ { true, '[', 'C', kBare | kBareMod | kSemiMod, { VK_RIGHT, '\0', 0 } },
+ { true, '[', 'D', kBare | kBareMod | kSemiMod, { VK_LEFT, '\0', 0 } },
+
+ // putty. putty uses this sequence for Ctrl-Arrow, Shift-Arrow, and
+ // Ctrl-Shift-Arrow, but I can only decode to one choice, so I'm just
+ // leaving the modifier off altogether.
+ { true, 'O', 'A', kBare, { VK_UP, '\0', 0 } },
+ { true, 'O', 'B', kBare, { VK_DOWN, '\0', 0 } },
+ { true, 'O', 'C', kBare, { VK_RIGHT, '\0', 0 } },
+ { true, 'O', 'D', kBare, { VK_LEFT, '\0', 0 } },
+
+ // rxvt, rxvt-unicode
+ // Shift-Ctrl-Arrow can't be identified. It's the same as Shift-Arrow.
+ { true, '[', 'a', kBare, { VK_UP, '\0', SHIFT_PRESSED } },
+ { true, '[', 'b', kBare, { VK_DOWN, '\0', SHIFT_PRESSED } },
+ { true, '[', 'c', kBare, { VK_RIGHT, '\0', SHIFT_PRESSED } },
+ { true, '[', 'd', kBare, { VK_LEFT, '\0', SHIFT_PRESSED } },
+ { true, 'O', 'a', kBare, { VK_UP, '\0', LEFT_CTRL_PRESSED } },
+ { true, 'O', 'b', kBare, { VK_DOWN, '\0', LEFT_CTRL_PRESSED } },
+ { true, 'O', 'c', kBare, { VK_RIGHT, '\0', LEFT_CTRL_PRESSED } },
+ { true, 'O', 'd', kBare, { VK_LEFT, '\0', LEFT_CTRL_PRESSED } },
+
+ // Numpad 5 with NumLock off
+ // * xterm, mintty, and gnome-terminal use `ESC [ E`.
+ // * putty, TERM=cygwin, TERM=linux all use `ESC [ G` for 5
+ // * putty uses `ESC O G` for Ctrl-5 and Shift-5. Omit the modifier
+ // as with putty's arrow keys.
+ // * I never saw modifiers inserted into these escapes, but I think
+ // it should be completely OK with the CSI escapes.
+ { true, '[', 'E', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } },
+ { true, '[', 'G', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } },
+ { true, 'O', 'G', kBare, { VK_CLEAR, '\0', 0 } },
+
+ // Home/End, letter version
+ // * gnome-terminal uses `ESC O [HF]`. I never saw it modified.
+ // kBareMod: IntelliJ/JediTerm uses escapes like: ESC [ n HF
+ { true, '[', 'H', kBare | kBareMod | kSemiMod, { VK_HOME, '\0', 0 } },
+ { true, '[', 'F', kBare | kBareMod | kSemiMod, { VK_END, '\0', 0 } },
+ { true, 'O', 'H', kBare, { VK_HOME, '\0', 0 } },
+ { true, 'O', 'F', kBare, { VK_END, '\0', 0 } },
+
+ // F1-F4, letter version (xterm, VTE, konsole)
+ { true, '[', 'P', kSemiMod, { VK_F1, '\0', 0 } },
+ { true, '[', 'Q', kSemiMod, { VK_F2, '\0', 0 } },
+ { true, '[', 'R', kSemiMod, { VK_F3, '\0', 0 } },
+ { true, '[', 'S', kSemiMod, { VK_F4, '\0', 0 } },
+
+ // GNOME VTE and Konsole have special encodings for modified F1-F4:
+ // * [VTE] ESC O 1 ; n [PQRS]
+ // * [Konsole] ESC O n [PQRS]
+ { false, 'O', 'P', kBare | kBareMod | kSemiMod, { VK_F1, '\0', 0 } },
+ { false, 'O', 'Q', kBare | kBareMod | kSemiMod, { VK_F2, '\0', 0 } },
+ { false, 'O', 'R', kBare | kBareMod | kSemiMod, { VK_F3, '\0', 0 } },
+ { false, 'O', 'S', kBare | kBareMod | kSemiMod, { VK_F4, '\0', 0 } },
+
+ // Handle the "application numpad" escape sequences.
+ //
+ // Terminals output these codes under various circumstances:
+ // * rxvt-unicode: numpad, hold down SHIFT
+ // * rxvt: numpad, by default
+ // * xterm: numpad, after enabling app-mode using DECPAM (`ESC =`). xterm
+ // generates `ESC O <mod> <letter>` for modified numpad presses,
+ // necessitating kBareMod.
+ // * mintty: by combining Ctrl with various keys such as '1' or ','.
+ // Handling those keys is difficult, because mintty is generating the
+ // same sequence for Ctrl-1 and Ctrl-NumPadEnd -- should the virtualKey
+ // be '1' or VK_HOME?
+
+ { true, 'O', 'M', kBare | kBareMod, { VK_RETURN, '\r', 0 } },
+ { true, 'O', 'j', kBare | kBareMod, { VK_MULTIPLY, '*', 0 } },
+ { true, 'O', 'k', kBare | kBareMod, { VK_ADD, '+', 0 } },
+ { true, 'O', 'm', kBare | kBareMod, { VK_SUBTRACT, '-', 0 } },
+ { true, 'O', 'n', kBare | kBareMod, { VK_DELETE, '\0', 0 } },
+ { true, 'O', 'o', kBare | kBareMod, { VK_DIVIDE, '/', 0 } },
+ { true, 'O', 'p', kBare | kBareMod, { VK_INSERT, '\0', 0 } },
+ { true, 'O', 'q', kBare | kBareMod, { VK_END, '\0', 0 } },
+ { true, 'O', 'r', kBare | kBareMod, { VK_DOWN, '\0', 0 } },
+ { true, 'O', 's', kBare | kBareMod, { VK_NEXT, '\0', 0 } },
+ { true, 'O', 't', kBare | kBareMod, { VK_LEFT, '\0', 0 } },
+ { true, 'O', 'u', kBare | kBareMod, { VK_CLEAR, '\0', 0 } },
+ { true, 'O', 'v', kBare | kBareMod, { VK_RIGHT, '\0', 0 } },
+ { true, 'O', 'w', kBare | kBareMod, { VK_HOME, '\0', 0 } },
+ { true, 'O', 'x', kBare | kBareMod, { VK_UP, '\0', 0 } },
+ { true, 'O', 'y', kBare | kBareMod, { VK_PRIOR, '\0', 0 } },
+
+ { true, '[', 'M', kBare | kSemiMod, { VK_RETURN, '\r', 0 } },
+ { true, '[', 'j', kBare | kSemiMod, { VK_MULTIPLY, '*', 0 } },
+ { true, '[', 'k', kBare | kSemiMod, { VK_ADD, '+', 0 } },
+ { true, '[', 'm', kBare | kSemiMod, { VK_SUBTRACT, '-', 0 } },
+ { true, '[', 'n', kBare | kSemiMod, { VK_DELETE, '\0', 0 } },
+ { true, '[', 'o', kBare | kSemiMod, { VK_DIVIDE, '/', 0 } },
+ { true, '[', 'p', kBare | kSemiMod, { VK_INSERT, '\0', 0 } },
+ { true, '[', 'q', kBare | kSemiMod, { VK_END, '\0', 0 } },
+ { true, '[', 'r', kBare | kSemiMod, { VK_DOWN, '\0', 0 } },
+ { true, '[', 's', kBare | kSemiMod, { VK_NEXT, '\0', 0 } },
+ { true, '[', 't', kBare | kSemiMod, { VK_LEFT, '\0', 0 } },
+ { true, '[', 'u', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } },
+ { true, '[', 'v', kBare | kSemiMod, { VK_RIGHT, '\0', 0 } },
+ { true, '[', 'w', kBare | kSemiMod, { VK_HOME, '\0', 0 } },
+ { true, '[', 'x', kBare | kSemiMod, { VK_UP, '\0', 0 } },
+ { true, '[', 'y', kBare | kSemiMod, { VK_PRIOR, '\0', 0 } },
+
+ { false, '[', 'Z', kBare, { VK_TAB, '\t', SHIFT_PRESSED } },
+};
+
+static const EscapeEncoding escapeNumericEncodings[] = {
+ { true, '[', 1, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } },
+ { true, '[', 2, kBare | kSemiMod | kSuffixBoth, { VK_INSERT, '\0', 0 } },
+ { true, '[', 3, kBare | kSemiMod | kSuffixBoth, { VK_DELETE, '\0', 0 } },
+ { true, '[', 4, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } },
+ { true, '[', 5, kBare | kSemiMod | kSuffixBoth, { VK_PRIOR, '\0', 0 } },
+ { true, '[', 6, kBare | kSemiMod | kSuffixBoth, { VK_NEXT, '\0', 0 } },
+ { true, '[', 7, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } },
+ { true, '[', 8, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } },
+ { true, '[', 11, kBare | kSemiMod | kSuffixBoth, { VK_F1, '\0', 0 } },
+ { true, '[', 12, kBare | kSemiMod | kSuffixBoth, { VK_F2, '\0', 0 } },
+ { true, '[', 13, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', 0 } },
+ { true, '[', 14, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', 0 } },
+ { true, '[', 15, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', 0 } },
+ { true, '[', 17, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', 0 } },
+ { true, '[', 18, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', 0 } },
+ { true, '[', 19, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', 0 } },
+ { true, '[', 20, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', 0 } },
+ { true, '[', 21, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', 0 } },
+ { true, '[', 23, kBare | kSemiMod | kSuffixBoth, { VK_F11, '\0', 0 } },
+ { true, '[', 24, kBare | kSemiMod | kSuffixBoth, { VK_F12, '\0', 0 } },
+ { true, '[', 25, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', SHIFT_PRESSED } },
+ { true, '[', 26, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', SHIFT_PRESSED } },
+ { true, '[', 28, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', SHIFT_PRESSED } },
+ { true, '[', 29, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', SHIFT_PRESSED } },
+ { true, '[', 31, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', SHIFT_PRESSED } },
+ { true, '[', 32, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', SHIFT_PRESSED } },
+ { true, '[', 33, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', SHIFT_PRESSED } },
+ { true, '[', 34, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', SHIFT_PRESSED } },
+};
+
+const int kCsiShiftModifier = 1;
+const int kCsiAltModifier = 2;
+const int kCsiCtrlModifier = 4;
+
+static inline bool useEnhancedForVirtualKey(uint16_t vk) {
+ switch (vk) {
+ case VK_UP:
+ case VK_DOWN:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_INSERT:
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ case VK_PRIOR:
+ case VK_NEXT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void addSimpleEntries(InputMap &inputMap) {
+ struct SimpleEncoding {
+ const char *encoding;
+ InputMap::Key key;
+ };
+
+ static const SimpleEncoding simpleEncodings[] = {
+ // Ctrl-<letter/digit> seems to be handled OK by the default code path.
+
+ { "\x7F", { VK_BACK, '\x08', 0, } },
+ { ESC "\x7F", { VK_BACK, '\x08', LEFT_ALT_PRESSED, } },
+ { "\x03", { 'C', '\x03', LEFT_CTRL_PRESSED, } },
+
+ // Handle special F1-F5 for TERM=linux and TERM=cygwin.
+ { ESC "[[A", { VK_F1, '\0', 0 } },
+ { ESC "[[B", { VK_F2, '\0', 0 } },
+ { ESC "[[C", { VK_F3, '\0', 0 } },
+ { ESC "[[D", { VK_F4, '\0', 0 } },
+ { ESC "[[E", { VK_F5, '\0', 0 } },
+
+ { ESC ESC "[[A", { VK_F1, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[B", { VK_F2, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[C", { VK_F3, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[D", { VK_F4, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[E", { VK_F5, '\0', LEFT_ALT_PRESSED } },
+ };
+
+ for (size_t i = 0; i < DIM(simpleEncodings); ++i) {
+ auto k = simpleEncodings[i].key;
+ if (useEnhancedForVirtualKey(k.virtualKey)) {
+ k.keyState |= ENHANCED_KEY;
+ }
+ inputMap.set(simpleEncodings[i].encoding,
+ strlen(simpleEncodings[i].encoding),
+ k);
+ }
+}
+
+struct ExpandContext {
+ InputMap &inputMap;
+ const EscapeEncoding &e;
+ char *buffer;
+ char *bufferEnd;
+};
+
+static inline void setEncoding(const ExpandContext &ctx, char *end,
+ uint16_t extraKeyState) {
+ InputMap::Key k = ctx.e.key;
+ k.keyState |= extraKeyState;
+ if (k.keyState & LEFT_CTRL_PRESSED) {
+ switch (k.virtualKey) {
+ case VK_ADD:
+ case VK_DIVIDE:
+ case VK_MULTIPLY:
+ case VK_SUBTRACT:
+ k.unicodeChar = '\0';
+ break;
+ case VK_RETURN:
+ k.unicodeChar = '\n';
+ break;
+ }
+ }
+ if (useEnhancedForVirtualKey(k.virtualKey)) {
+ k.keyState |= ENHANCED_KEY;
+ }
+ ctx.inputMap.set(ctx.buffer, end - ctx.buffer, k);
+}
+
+static inline uint16_t keyStateForMod(int mod) {
+ int ret = 0;
+ if ((mod - 1) & kCsiShiftModifier) ret |= SHIFT_PRESSED;
+ if ((mod - 1) & kCsiAltModifier) ret |= LEFT_ALT_PRESSED;
+ if ((mod - 1) & kCsiCtrlModifier) ret |= LEFT_CTRL_PRESSED;
+ return ret;
+}
+
+static void expandNumericEncodingSuffix(const ExpandContext &ctx, char *p,
+ uint16_t extraKeyState) {
+ ASSERT(p <= ctx.bufferEnd - 1);
+ {
+ char *q = p;
+ *q++ = '~';
+ setEncoding(ctx, q, extraKeyState);
+ }
+ if (ctx.e.modifiers & kSuffixShift) {
+ char *q = p;
+ *q++ = '$';
+ setEncoding(ctx, q, extraKeyState | SHIFT_PRESSED);
+ }
+ if (ctx.e.modifiers & kSuffixCtrl) {
+ char *q = p;
+ *q++ = '^';
+ setEncoding(ctx, q, extraKeyState | LEFT_CTRL_PRESSED);
+ }
+ if (ctx.e.modifiers & (kSuffixCtrl | kSuffixShift)) {
+ char *q = p;
+ *q++ = '@';
+ setEncoding(ctx, q, extraKeyState | SHIFT_PRESSED | LEFT_CTRL_PRESSED);
+ }
+}
+
+template <bool is_numeric>
+static inline void expandEncodingAfterAltPrefix(
+ const ExpandContext &ctx, char *p, uint16_t extraKeyState) {
+ auto appendId = [&](char *&ptr) {
+ const auto idstr = decOfInt(ctx.e.id);
+ ASSERT(ptr <= ctx.bufferEnd - idstr.size());
+ std::copy(idstr.data(), idstr.data() + idstr.size(), ptr);
+ ptr += idstr.size();
+ };
+ ASSERT(p <= ctx.bufferEnd - 2);
+ *p++ = '\x1b';
+ *p++ = ctx.e.prefix;
+ if (ctx.e.modifiers & kBare) {
+ char *q = p;
+ if (is_numeric) {
+ appendId(q);
+ expandNumericEncodingSuffix(ctx, q, extraKeyState);
+ } else {
+ ASSERT(q <= ctx.bufferEnd - 1);
+ *q++ = ctx.e.id;
+ setEncoding(ctx, q, extraKeyState);
+ }
+ }
+ if (ctx.e.modifiers & kBareMod) {
+ ASSERT(!is_numeric && "kBareMod is invalid with numeric sequences");
+ for (int mod = 2; mod <= 8; ++mod) {
+ char *q = p;
+ ASSERT(q <= ctx.bufferEnd - 2);
+ *q++ = '0' + mod;
+ *q++ = ctx.e.id;
+ setEncoding(ctx, q, extraKeyState | keyStateForMod(mod));
+ }
+ }
+ if (ctx.e.modifiers & kSemiMod) {
+ for (int mod = 2; mod <= 8; ++mod) {
+ char *q = p;
+ if (is_numeric) {
+ appendId(q);
+ ASSERT(q <= ctx.bufferEnd - 2);
+ *q++ = ';';
+ *q++ = '0' + mod;
+ expandNumericEncodingSuffix(
+ ctx, q, extraKeyState | keyStateForMod(mod));
+ } else {
+ ASSERT(q <= ctx.bufferEnd - 4);
+ *q++ = '1';
+ *q++ = ';';
+ *q++ = '0' + mod;
+ *q++ = ctx.e.id;
+ setEncoding(ctx, q, extraKeyState | keyStateForMod(mod));
+ }
+ }
+ }
+}
+
+template <bool is_numeric>
+static inline void expandEncoding(const ExpandContext &ctx) {
+ if (ctx.e.alt_prefix_allowed) {
+ // For better or for worse, this code expands all of:
+ // * ESC [ <key> -- <key>
+ // * ESC ESC [ <key> -- Alt-<key>
+ // * ESC [ 1 ; 3 <key> -- Alt-<key>
+ // * ESC ESC [ 1 ; 3 <key> -- Alt-<key> specified twice
+ // I suspect no terminal actually emits the last one (i.e. specifying
+ // the Alt modifier using both methods), but I have seen a terminal
+ // that emitted a prefix ESC for Alt and a non-Alt modifier.
+ char *p = ctx.buffer;
+ ASSERT(p <= ctx.bufferEnd - 1);
+ *p++ = '\x1b';
+ expandEncodingAfterAltPrefix<is_numeric>(ctx, p, LEFT_ALT_PRESSED);
+ }
+ expandEncodingAfterAltPrefix<is_numeric>(ctx, ctx.buffer, 0);
+}
+
+template <bool is_numeric, size_t N>
+static void addEscapes(InputMap &inputMap, const EscapeEncoding (&encodings)[N]) {
+ char buffer[32];
+ for (size_t i = 0; i < DIM(encodings); ++i) {
+ ExpandContext ctx = {
+ inputMap, encodings[i],
+ buffer, buffer + sizeof(buffer)
+ };
+ expandEncoding<is_numeric>(ctx);
+ }
+}
+
+} // anonymous namespace
+
+void addDefaultEntriesToInputMap(InputMap &inputMap) {
+ addEscapes<false>(inputMap, escapeLetterEncodings);
+ addEscapes<true>(inputMap, escapeNumericEncodings);
+ addSimpleEntries(inputMap);
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h
new file mode 100644
index 0000000000..c4b9083678
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef DEFAULT_INPUT_MAP_H
+#define DEFAULT_INPUT_MAP_H
+
+class InputMap;
+
+void addDefaultEntriesToInputMap(InputMap &inputMap);
+
+#endif // DEFAULT_INPUT_MAP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/DsrSender.h b/src/libs/3rdparty/winpty/src/agent/DsrSender.h
new file mode 100644
index 0000000000..1ec0a97d2e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DsrSender.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef DSRSENDER_H
+#define DSRSENDER_H
+
+class DsrSender
+{
+public:
+ virtual void sendDsr() = 0;
+};
+
+#endif // DSRSENDER_H
diff --git a/src/libs/3rdparty/winpty/src/agent/EventLoop.cc b/src/libs/3rdparty/winpty/src/agent/EventLoop.cc
new file mode 100644
index 0000000000..ba5cf18cc8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/EventLoop.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "EventLoop.h"
+
+#include <algorithm>
+
+#include "NamedPipe.h"
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+
+EventLoop::~EventLoop() {
+ for (NamedPipe *pipe : m_pipes) {
+ delete pipe;
+ }
+ m_pipes.clear();
+}
+
+// Enter the event loop. Runs until the I/O or timeout handler calls exit().
+void EventLoop::run()
+{
+ std::vector<HANDLE> waitHandles;
+ DWORD lastTime = GetTickCount();
+ while (!m_exiting) {
+ bool didSomething = false;
+
+ // Attempt to make progress with the pipes.
+ waitHandles.clear();
+ for (size_t i = 0; i < m_pipes.size(); ++i) {
+ if (m_pipes[i]->serviceIo(&waitHandles)) {
+ onPipeIo(*m_pipes[i]);
+ didSomething = true;
+ }
+ }
+
+ // Call the timeout if enough time has elapsed.
+ if (m_pollInterval > 0) {
+ int elapsed = GetTickCount() - lastTime;
+ if (elapsed >= m_pollInterval) {
+ onPollTimeout();
+ lastTime = GetTickCount();
+ didSomething = true;
+ }
+ }
+
+ if (didSomething)
+ continue;
+
+ // If there's nothing to do, wait.
+ DWORD timeout = INFINITE;
+ if (m_pollInterval > 0)
+ timeout = std::max(0, (int)(lastTime + m_pollInterval - GetTickCount()));
+ if (waitHandles.size() == 0) {
+ ASSERT(timeout != INFINITE);
+ if (timeout > 0)
+ Sleep(timeout);
+ } else {
+ DWORD result = WaitForMultipleObjects(waitHandles.size(),
+ waitHandles.data(),
+ FALSE,
+ timeout);
+ ASSERT(result != WAIT_FAILED);
+ }
+ }
+}
+
+NamedPipe &EventLoop::createNamedPipe()
+{
+ NamedPipe *ret = new NamedPipe();
+ m_pipes.push_back(ret);
+ return *ret;
+}
+
+void EventLoop::setPollInterval(int ms)
+{
+ m_pollInterval = ms;
+}
+
+void EventLoop::shutdown()
+{
+ m_exiting = true;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/EventLoop.h b/src/libs/3rdparty/winpty/src/agent/EventLoop.h
new file mode 100644
index 0000000000..eddb0f6267
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/EventLoop.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef EVENTLOOP_H
+#define EVENTLOOP_H
+
+#include <vector>
+
+class NamedPipe;
+
+class EventLoop
+{
+public:
+ virtual ~EventLoop();
+ void run();
+
+protected:
+ NamedPipe &createNamedPipe();
+ void setPollInterval(int ms);
+ void shutdown();
+ virtual void onPollTimeout() {}
+ virtual void onPipeIo(NamedPipe &namedPipe) {}
+
+private:
+ bool m_exiting = false;
+ std::vector<NamedPipe*> m_pipes;
+ int m_pollInterval = 0;
+};
+
+#endif // EVENTLOOP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/InputMap.cc b/src/libs/3rdparty/winpty/src/agent/InputMap.cc
new file mode 100644
index 0000000000..b1fbfc2e30
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/InputMap.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "InputMap.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "DebugShowInput.h"
+#include "SimplePool.h"
+#include "../shared/DebugClient.h"
+#include "../shared/UnixCtrlChars.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+namespace {
+
+static const char *getVirtualKeyString(int virtualKey)
+{
+ switch (virtualKey) {
+#define WINPTY_GVKS_KEY(x) case VK_##x: return #x;
+ WINPTY_GVKS_KEY(RBUTTON) WINPTY_GVKS_KEY(F9)
+ WINPTY_GVKS_KEY(CANCEL) WINPTY_GVKS_KEY(F10)
+ WINPTY_GVKS_KEY(MBUTTON) WINPTY_GVKS_KEY(F11)
+ WINPTY_GVKS_KEY(XBUTTON1) WINPTY_GVKS_KEY(F12)
+ WINPTY_GVKS_KEY(XBUTTON2) WINPTY_GVKS_KEY(F13)
+ WINPTY_GVKS_KEY(BACK) WINPTY_GVKS_KEY(F14)
+ WINPTY_GVKS_KEY(TAB) WINPTY_GVKS_KEY(F15)
+ WINPTY_GVKS_KEY(CLEAR) WINPTY_GVKS_KEY(F16)
+ WINPTY_GVKS_KEY(RETURN) WINPTY_GVKS_KEY(F17)
+ WINPTY_GVKS_KEY(SHIFT) WINPTY_GVKS_KEY(F18)
+ WINPTY_GVKS_KEY(CONTROL) WINPTY_GVKS_KEY(F19)
+ WINPTY_GVKS_KEY(MENU) WINPTY_GVKS_KEY(F20)
+ WINPTY_GVKS_KEY(PAUSE) WINPTY_GVKS_KEY(F21)
+ WINPTY_GVKS_KEY(CAPITAL) WINPTY_GVKS_KEY(F22)
+ WINPTY_GVKS_KEY(HANGUL) WINPTY_GVKS_KEY(F23)
+ WINPTY_GVKS_KEY(JUNJA) WINPTY_GVKS_KEY(F24)
+ WINPTY_GVKS_KEY(FINAL) WINPTY_GVKS_KEY(NUMLOCK)
+ WINPTY_GVKS_KEY(KANJI) WINPTY_GVKS_KEY(SCROLL)
+ WINPTY_GVKS_KEY(ESCAPE) WINPTY_GVKS_KEY(LSHIFT)
+ WINPTY_GVKS_KEY(CONVERT) WINPTY_GVKS_KEY(RSHIFT)
+ WINPTY_GVKS_KEY(NONCONVERT) WINPTY_GVKS_KEY(LCONTROL)
+ WINPTY_GVKS_KEY(ACCEPT) WINPTY_GVKS_KEY(RCONTROL)
+ WINPTY_GVKS_KEY(MODECHANGE) WINPTY_GVKS_KEY(LMENU)
+ WINPTY_GVKS_KEY(SPACE) WINPTY_GVKS_KEY(RMENU)
+ WINPTY_GVKS_KEY(PRIOR) WINPTY_GVKS_KEY(BROWSER_BACK)
+ WINPTY_GVKS_KEY(NEXT) WINPTY_GVKS_KEY(BROWSER_FORWARD)
+ WINPTY_GVKS_KEY(END) WINPTY_GVKS_KEY(BROWSER_REFRESH)
+ WINPTY_GVKS_KEY(HOME) WINPTY_GVKS_KEY(BROWSER_STOP)
+ WINPTY_GVKS_KEY(LEFT) WINPTY_GVKS_KEY(BROWSER_SEARCH)
+ WINPTY_GVKS_KEY(UP) WINPTY_GVKS_KEY(BROWSER_FAVORITES)
+ WINPTY_GVKS_KEY(RIGHT) WINPTY_GVKS_KEY(BROWSER_HOME)
+ WINPTY_GVKS_KEY(DOWN) WINPTY_GVKS_KEY(VOLUME_MUTE)
+ WINPTY_GVKS_KEY(SELECT) WINPTY_GVKS_KEY(VOLUME_DOWN)
+ WINPTY_GVKS_KEY(PRINT) WINPTY_GVKS_KEY(VOLUME_UP)
+ WINPTY_GVKS_KEY(EXECUTE) WINPTY_GVKS_KEY(MEDIA_NEXT_TRACK)
+ WINPTY_GVKS_KEY(SNAPSHOT) WINPTY_GVKS_KEY(MEDIA_PREV_TRACK)
+ WINPTY_GVKS_KEY(INSERT) WINPTY_GVKS_KEY(MEDIA_STOP)
+ WINPTY_GVKS_KEY(DELETE) WINPTY_GVKS_KEY(MEDIA_PLAY_PAUSE)
+ WINPTY_GVKS_KEY(HELP) WINPTY_GVKS_KEY(LAUNCH_MAIL)
+ WINPTY_GVKS_KEY(LWIN) WINPTY_GVKS_KEY(LAUNCH_MEDIA_SELECT)
+ WINPTY_GVKS_KEY(RWIN) WINPTY_GVKS_KEY(LAUNCH_APP1)
+ WINPTY_GVKS_KEY(APPS) WINPTY_GVKS_KEY(LAUNCH_APP2)
+ WINPTY_GVKS_KEY(SLEEP) WINPTY_GVKS_KEY(OEM_1)
+ WINPTY_GVKS_KEY(NUMPAD0) WINPTY_GVKS_KEY(OEM_PLUS)
+ WINPTY_GVKS_KEY(NUMPAD1) WINPTY_GVKS_KEY(OEM_COMMA)
+ WINPTY_GVKS_KEY(NUMPAD2) WINPTY_GVKS_KEY(OEM_MINUS)
+ WINPTY_GVKS_KEY(NUMPAD3) WINPTY_GVKS_KEY(OEM_PERIOD)
+ WINPTY_GVKS_KEY(NUMPAD4) WINPTY_GVKS_KEY(OEM_2)
+ WINPTY_GVKS_KEY(NUMPAD5) WINPTY_GVKS_KEY(OEM_3)
+ WINPTY_GVKS_KEY(NUMPAD6) WINPTY_GVKS_KEY(OEM_4)
+ WINPTY_GVKS_KEY(NUMPAD7) WINPTY_GVKS_KEY(OEM_5)
+ WINPTY_GVKS_KEY(NUMPAD8) WINPTY_GVKS_KEY(OEM_6)
+ WINPTY_GVKS_KEY(NUMPAD9) WINPTY_GVKS_KEY(OEM_7)
+ WINPTY_GVKS_KEY(MULTIPLY) WINPTY_GVKS_KEY(OEM_8)
+ WINPTY_GVKS_KEY(ADD) WINPTY_GVKS_KEY(OEM_102)
+ WINPTY_GVKS_KEY(SEPARATOR) WINPTY_GVKS_KEY(PROCESSKEY)
+ WINPTY_GVKS_KEY(SUBTRACT) WINPTY_GVKS_KEY(PACKET)
+ WINPTY_GVKS_KEY(DECIMAL) WINPTY_GVKS_KEY(ATTN)
+ WINPTY_GVKS_KEY(DIVIDE) WINPTY_GVKS_KEY(CRSEL)
+ WINPTY_GVKS_KEY(F1) WINPTY_GVKS_KEY(EXSEL)
+ WINPTY_GVKS_KEY(F2) WINPTY_GVKS_KEY(EREOF)
+ WINPTY_GVKS_KEY(F3) WINPTY_GVKS_KEY(PLAY)
+ WINPTY_GVKS_KEY(F4) WINPTY_GVKS_KEY(ZOOM)
+ WINPTY_GVKS_KEY(F5) WINPTY_GVKS_KEY(NONAME)
+ WINPTY_GVKS_KEY(F6) WINPTY_GVKS_KEY(PA1)
+ WINPTY_GVKS_KEY(F7) WINPTY_GVKS_KEY(OEM_CLEAR)
+ WINPTY_GVKS_KEY(F8)
+#undef WINPTY_GVKS_KEY
+ default: return NULL;
+ }
+}
+
+} // anonymous namespace
+
+std::string InputMap::Key::toString() const {
+ std::string ret;
+ ret += controlKeyStatePrefix(keyState);
+ char buf[256];
+ const char *vkString = getVirtualKeyString(virtualKey);
+ if (vkString != NULL) {
+ ret += vkString;
+ } else if ((virtualKey >= 'A' && virtualKey <= 'Z') ||
+ (virtualKey >= '0' && virtualKey <= '9')) {
+ ret += static_cast<char>(virtualKey);
+ } else {
+ winpty_snprintf(buf, "%#x", virtualKey);
+ ret += buf;
+ }
+ if (unicodeChar >= 32 && unicodeChar <= 126) {
+ winpty_snprintf(buf, " ch='%c'",
+ static_cast<char>(unicodeChar));
+ } else {
+ winpty_snprintf(buf, " ch=%#x",
+ static_cast<unsigned int>(unicodeChar));
+ }
+ ret += buf;
+ return ret;
+}
+
+void InputMap::set(const char *encoding, int encodingLen, const Key &key) {
+ ASSERT(encodingLen > 0);
+ setHelper(m_root, encoding, encodingLen, key);
+}
+
+void InputMap::setHelper(Node &node, const char *encoding, int encodingLen, const Key &key) {
+ if (encodingLen == 0) {
+ node.key = key;
+ } else {
+ setHelper(getOrCreateChild(node, encoding[0]), encoding + 1, encodingLen - 1, key);
+ }
+}
+
+InputMap::Node &InputMap::getOrCreateChild(Node &node, unsigned char ch) {
+ Node *ret = getChild(node, ch);
+ if (ret != NULL) {
+ return *ret;
+ }
+ if (node.childCount < Node::kTinyCount) {
+ // Maintain sorted order for the sake of the InputMap dumping.
+ int insertIndex = node.childCount;
+ for (int i = 0; i < node.childCount; ++i) {
+ if (ch < node.u.tiny.values[i]) {
+ insertIndex = i;
+ break;
+ }
+ }
+ for (int j = node.childCount; j > insertIndex; --j) {
+ node.u.tiny.values[j] = node.u.tiny.values[j - 1];
+ node.u.tiny.children[j] = node.u.tiny.children[j - 1];
+ }
+ node.u.tiny.values[insertIndex] = ch;
+ node.u.tiny.children[insertIndex] = ret = m_nodePool.alloc();
+ ++node.childCount;
+ return *ret;
+ }
+ if (node.childCount == Node::kTinyCount) {
+ Branch *branch = m_branchPool.alloc();
+ for (int i = 0; i < node.childCount; ++i) {
+ branch->children[node.u.tiny.values[i]] = node.u.tiny.children[i];
+ }
+ node.u.branch = branch;
+ }
+ node.u.branch->children[ch] = ret = m_nodePool.alloc();
+ ++node.childCount;
+ return *ret;
+}
+
+// Find the longest matching key and node.
+int InputMap::lookupKey(const char *input, int inputSize,
+ Key &keyOut, bool &incompleteOut) const {
+ keyOut = kKeyZero;
+ incompleteOut = false;
+
+ const Node *node = &m_root;
+ InputMap::Key longestMatch = kKeyZero;
+ int longestMatchLen = 0;
+
+ for (int i = 0; i < inputSize; ++i) {
+ unsigned char ch = input[i];
+ node = getChild(*node, ch);
+ if (node == NULL) {
+ keyOut = longestMatch;
+ return longestMatchLen;
+ } else if (node->hasKey()) {
+ longestMatchLen = i + 1;
+ longestMatch = node->key;
+ }
+ }
+ keyOut = longestMatch;
+ incompleteOut = node->childCount > 0;
+ return longestMatchLen;
+}
+
+void InputMap::dumpInputMap() const {
+ std::string encoding;
+ dumpInputMapHelper(m_root, encoding);
+}
+
+void InputMap::dumpInputMapHelper(
+ const Node &node, std::string &encoding) const {
+ if (node.hasKey()) {
+ trace("%s -> %s",
+ encoding.c_str(),
+ node.key.toString().c_str());
+ }
+ for (int i = 0; i < 256; ++i) {
+ const Node *child = getChild(node, i);
+ if (child != NULL) {
+ size_t oldSize = encoding.size();
+ if (!encoding.empty()) {
+ encoding.push_back(' ');
+ }
+ char ctrlChar = decodeUnixCtrlChar(i);
+ if (ctrlChar != '\0') {
+ encoding.push_back('^');
+ encoding.push_back(static_cast<char>(ctrlChar));
+ } else if (i == ' ') {
+ encoding.append("' '");
+ } else {
+ encoding.push_back(static_cast<char>(i));
+ }
+ dumpInputMapHelper(*child, encoding);
+ encoding.resize(oldSize);
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/InputMap.h b/src/libs/3rdparty/winpty/src/agent/InputMap.h
new file mode 100644
index 0000000000..9a666c7976
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/InputMap.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef INPUT_MAP_H
+#define INPUT_MAP_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "SimplePool.h"
+#include "../shared/WinptyAssert.h"
+
+class InputMap {
+public:
+ struct Key {
+ uint16_t virtualKey;
+ uint32_t unicodeChar;
+ uint16_t keyState;
+
+ std::string toString() const;
+ };
+
+private:
+ struct Node;
+
+ struct Branch {
+ Branch() {
+ memset(&children, 0, sizeof(children));
+ }
+
+ Node *children[256];
+ };
+
+ struct Node {
+ Node() : childCount(0) {
+ Key zeroKey = { 0, 0, 0 };
+ key = zeroKey;
+ }
+
+ Key key;
+ int childCount;
+ enum { kTinyCount = 8 };
+ union {
+ Branch *branch;
+ struct {
+ unsigned char values[kTinyCount];
+ Node *children[kTinyCount];
+ } tiny;
+ } u;
+
+ bool hasKey() const {
+ return key.virtualKey != 0 || key.unicodeChar != 0;
+ }
+ };
+
+private:
+ SimplePool<Node, 256> m_nodePool;
+ SimplePool<Branch, 8> m_branchPool;
+ Node m_root;
+
+public:
+ void set(const char *encoding, int encodingLen, const Key &key);
+ int lookupKey(const char *input, int inputSize,
+ Key &keyOut, bool &incompleteOut) const;
+ void dumpInputMap() const;
+
+private:
+ Node *getChild(Node &node, unsigned char ch) {
+ return const_cast<Node*>(getChild(static_cast<const Node&>(node), ch));
+ }
+
+ const Node *getChild(const Node &node, unsigned char ch) const {
+ if (node.childCount <= Node::kTinyCount) {
+ for (int i = 0; i < node.childCount; ++i) {
+ if (node.u.tiny.values[i] == ch) {
+ return node.u.tiny.children[i];
+ }
+ }
+ return NULL;
+ } else {
+ return node.u.branch->children[ch];
+ }
+ }
+
+ void setHelper(Node &node, const char *encoding, int encodingLen, const Key &key);
+ Node &getOrCreateChild(Node &node, unsigned char ch);
+ void dumpInputMapHelper(const Node &node, std::string &encoding) const;
+};
+
+const InputMap::Key kKeyZero = { 0, 0, 0 };
+
+void dumpInputMap(InputMap &inputMap);
+
+#endif // INPUT_MAP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc
new file mode 100644
index 0000000000..80ac640e48
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "LargeConsoleRead.h"
+
+#include <stdlib.h>
+
+#include "../shared/WindowsVersion.h"
+#include "Scraper.h"
+#include "Win32ConsoleBuffer.h"
+
+LargeConsoleReadBuffer::LargeConsoleReadBuffer() :
+ m_rect(0, 0, 0, 0), m_rectWidth(0)
+{
+}
+
+void largeConsoleRead(LargeConsoleReadBuffer &out,
+ Win32ConsoleBuffer &buffer,
+ const SmallRect &readArea,
+ WORD attributesMask) {
+ ASSERT(readArea.Left >= 0 &&
+ readArea.Top >= 0 &&
+ readArea.Right >= readArea.Left &&
+ readArea.Bottom >= readArea.Top &&
+ readArea.width() <= MAX_CONSOLE_WIDTH);
+ const size_t count = readArea.width() * readArea.height();
+ if (out.m_data.size() < count) {
+ out.m_data.resize(count);
+ }
+ out.m_rect = readArea;
+ out.m_rectWidth = readArea.width();
+
+ static const bool useLargeReads = isAtLeastWindows8();
+ if (useLargeReads) {
+ buffer.read(readArea, out.m_data.data());
+ } else {
+ const int maxReadLines = std::max(1, MAX_CONSOLE_WIDTH / readArea.width());
+ int curLine = readArea.Top;
+ while (curLine <= readArea.Bottom) {
+ const SmallRect subReadArea(
+ readArea.Left,
+ curLine,
+ readArea.width(),
+ std::min(maxReadLines, readArea.Bottom + 1 - curLine));
+ buffer.read(subReadArea, out.lineDataMut(curLine));
+ curLine = subReadArea.Bottom + 1;
+ }
+ }
+ if (attributesMask != static_cast<WORD>(~0)) {
+ for (size_t i = 0; i < count; ++i) {
+ out.m_data[i].Attributes &= attributesMask;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h
new file mode 100644
index 0000000000..1bcf2c0232
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LARGE_CONSOLE_READ_H
+#define LARGE_CONSOLE_READ_H
+
+#include <windows.h>
+#include <stdlib.h>
+
+#include <vector>
+
+#include "SmallRect.h"
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+
+class Win32ConsoleBuffer;
+
+class LargeConsoleReadBuffer {
+public:
+ LargeConsoleReadBuffer();
+ const SmallRect &rect() const { return m_rect; }
+ const CHAR_INFO *lineData(int line) const {
+ validateLineNumber(line);
+ return &m_data[(line - m_rect.Top) * m_rectWidth];
+ }
+
+private:
+ CHAR_INFO *lineDataMut(int line) {
+ validateLineNumber(line);
+ return &m_data[(line - m_rect.Top) * m_rectWidth];
+ }
+
+ void validateLineNumber(int line) const {
+ if (line < m_rect.Top || line > m_rect.Bottom) {
+ trace("Fatal error: LargeConsoleReadBuffer: invalid line %d for "
+ "read rect %s", line, m_rect.toString().c_str());
+ abort();
+ }
+ }
+
+ SmallRect m_rect;
+ int m_rectWidth;
+ std::vector<CHAR_INFO> m_data;
+
+ friend void largeConsoleRead(LargeConsoleReadBuffer &out,
+ Win32ConsoleBuffer &buffer,
+ const SmallRect &readArea,
+ WORD attributesMask);
+};
+
+#endif // LARGE_CONSOLE_READ_H
diff --git a/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc b/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc
new file mode 100644
index 0000000000..64044e6e5d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc
@@ -0,0 +1,378 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "EventLoop.h"
+#include "NamedPipe.h"
+#include "../shared/DebugClient.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsSecurity.h"
+#include "../shared/WinptyAssert.h"
+
+// Returns true if anything happens (data received, data sent, pipe error).
+bool NamedPipe::serviceIo(std::vector<HANDLE> *waitHandles)
+{
+ bool justConnected = false;
+ const auto kError = ServiceResult::Error;
+ const auto kProgress = ServiceResult::Progress;
+ const auto kNoProgress = ServiceResult::NoProgress;
+ if (m_handle == NULL) {
+ return false;
+ }
+ if (m_connectEvent.get() != nullptr) {
+ // We're still connecting this server pipe. Check whether the pipe is
+ // now connected. If it isn't, add the pipe to the list of handles to
+ // wait on.
+ DWORD actual = 0;
+ BOOL success =
+ GetOverlappedResult(m_handle, &m_connectOver, &actual, FALSE);
+ if (!success && GetLastError() == ERROR_PIPE_CONNECTED) {
+ // I'm not sure this can happen, but it's easy to handle if it
+ // does.
+ success = TRUE;
+ }
+ if (!success) {
+ ASSERT(GetLastError() == ERROR_IO_INCOMPLETE &&
+ "Pended ConnectNamedPipe call failed");
+ waitHandles->push_back(m_connectEvent.get());
+ } else {
+ TRACE("Server pipe [%s] connected",
+ utf8FromWide(m_name).c_str());
+ m_connectEvent.dispose();
+ startPipeWorkers();
+ justConnected = true;
+ }
+ }
+ const auto readProgress = m_inputWorker ? m_inputWorker->service() : kNoProgress;
+ const auto writeProgress = m_outputWorker ? m_outputWorker->service() : kNoProgress;
+ if (readProgress == kError || writeProgress == kError) {
+ closePipe();
+ return true;
+ }
+ if (m_inputWorker && m_inputWorker->getWaitEvent() != nullptr) {
+ waitHandles->push_back(m_inputWorker->getWaitEvent());
+ }
+ if (m_outputWorker && m_outputWorker->getWaitEvent() != nullptr) {
+ waitHandles->push_back(m_outputWorker->getWaitEvent());
+ }
+ return justConnected
+ || readProgress == kProgress
+ || writeProgress == kProgress;
+}
+
+// manual reset, initially unset
+static OwnedHandle createEvent() {
+ HANDLE ret = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ ASSERT(ret != nullptr && "CreateEventW failed");
+ return OwnedHandle(ret);
+}
+
+NamedPipe::IoWorker::IoWorker(NamedPipe &namedPipe) :
+ m_namedPipe(namedPipe),
+ m_event(createEvent())
+{
+}
+
+NamedPipe::ServiceResult NamedPipe::IoWorker::service()
+{
+ ServiceResult progress = ServiceResult::NoProgress;
+ if (m_pending) {
+ DWORD actual = 0;
+ BOOL ret = GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, FALSE);
+ if (!ret) {
+ if (GetLastError() == ERROR_IO_INCOMPLETE) {
+ // There is a pending I/O.
+ return progress;
+ } else {
+ // Pipe error.
+ return ServiceResult::Error;
+ }
+ }
+ ResetEvent(m_event.get());
+ m_pending = false;
+ completeIo(actual);
+ m_currentIoSize = 0;
+ progress = ServiceResult::Progress;
+ }
+ DWORD nextSize = 0;
+ bool isRead = false;
+ while (shouldIssueIo(&nextSize, &isRead)) {
+ m_currentIoSize = nextSize;
+ DWORD actual = 0;
+ memset(&m_over, 0, sizeof(m_over));
+ m_over.hEvent = m_event.get();
+ BOOL ret = isRead
+ ? ReadFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over)
+ : WriteFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over);
+ if (!ret) {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ // There is a pending I/O.
+ m_pending = true;
+ return progress;
+ } else {
+ // Pipe error.
+ return ServiceResult::Error;
+ }
+ }
+ ResetEvent(m_event.get());
+ completeIo(actual);
+ m_currentIoSize = 0;
+ progress = ServiceResult::Progress;
+ }
+ return progress;
+}
+
+// This function is called after CancelIo has returned. We need to block until
+// the I/O operations have completed, which should happen very quickly.
+// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613
+void NamedPipe::IoWorker::waitForCanceledIo()
+{
+ if (m_pending) {
+ DWORD actual = 0;
+ GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, TRUE);
+ m_pending = false;
+ }
+}
+
+HANDLE NamedPipe::IoWorker::getWaitEvent()
+{
+ return m_pending ? m_event.get() : NULL;
+}
+
+void NamedPipe::InputWorker::completeIo(DWORD size)
+{
+ m_namedPipe.m_inQueue.append(m_buffer, size);
+}
+
+bool NamedPipe::InputWorker::shouldIssueIo(DWORD *size, bool *isRead)
+{
+ *isRead = true;
+ ASSERT(!m_namedPipe.isConnecting());
+ if (m_namedPipe.isClosed()) {
+ return false;
+ } else if (m_namedPipe.m_inQueue.size() < m_namedPipe.readBufferSize()) {
+ *size = kIoSize;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void NamedPipe::OutputWorker::completeIo(DWORD size)
+{
+ ASSERT(size == m_currentIoSize);
+}
+
+bool NamedPipe::OutputWorker::shouldIssueIo(DWORD *size, bool *isRead)
+{
+ *isRead = false;
+ if (!m_namedPipe.m_outQueue.empty()) {
+ auto &out = m_namedPipe.m_outQueue;
+ const DWORD writeSize = std::min<size_t>(out.size(), kIoSize);
+ std::copy(&out[0], &out[writeSize], m_buffer);
+ out.erase(0, writeSize);
+ *size = writeSize;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+DWORD NamedPipe::OutputWorker::getPendingIoSize()
+{
+ return m_pending ? m_currentIoSize : 0;
+}
+
+void NamedPipe::openServerPipe(LPCWSTR pipeName, OpenMode::t openMode,
+ int outBufferSize, int inBufferSize) {
+ ASSERT(isClosed());
+ ASSERT((openMode & OpenMode::Duplex) != 0);
+ const DWORD winOpenMode =
+ ((openMode & OpenMode::Reading) ? PIPE_ACCESS_INBOUND : 0)
+ | ((openMode & OpenMode::Writing) ? PIPE_ACCESS_OUTBOUND : 0)
+ | FILE_FLAG_FIRST_PIPE_INSTANCE
+ | FILE_FLAG_OVERLAPPED;
+ const auto sd = createPipeSecurityDescriptorOwnerFullControl();
+ ASSERT(sd && "error creating data pipe SECURITY_DESCRIPTOR");
+ SECURITY_ATTRIBUTES sa = {};
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd.get();
+ HANDLE handle = CreateNamedPipeW(
+ pipeName,
+ /*dwOpenMode=*/winOpenMode,
+ /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
+ /*nMaxInstances=*/1,
+ /*nOutBufferSize=*/outBufferSize,
+ /*nInBufferSize=*/inBufferSize,
+ /*nDefaultTimeOut=*/30000,
+ &sa);
+ TRACE("opened server pipe [%s], handle == %p",
+ utf8FromWide(pipeName).c_str(), handle);
+ ASSERT(handle != INVALID_HANDLE_VALUE && "Could not open server pipe");
+ m_name = pipeName;
+ m_handle = handle;
+ m_openMode = openMode;
+
+ // Start an asynchronous connection attempt.
+ m_connectEvent = createEvent();
+ memset(&m_connectOver, 0, sizeof(m_connectOver));
+ m_connectOver.hEvent = m_connectEvent.get();
+ BOOL success = ConnectNamedPipe(m_handle, &m_connectOver);
+ const auto err = GetLastError();
+ if (!success && err == ERROR_PIPE_CONNECTED) {
+ success = TRUE;
+ }
+ if (success) {
+ TRACE("Server pipe [%s] connected", utf8FromWide(pipeName).c_str());
+ m_connectEvent.dispose();
+ startPipeWorkers();
+ } else if (err != ERROR_IO_PENDING) {
+ ASSERT(false && "ConnectNamedPipe call failed");
+ }
+}
+
+void NamedPipe::connectToServer(LPCWSTR pipeName, OpenMode::t openMode)
+{
+ ASSERT(isClosed());
+ ASSERT((openMode & OpenMode::Duplex) != 0);
+ HANDLE handle = CreateFileW(
+ pipeName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
+ NULL);
+ TRACE("connected to [%s], handle == %p",
+ utf8FromWide(pipeName).c_str(), handle);
+ ASSERT(handle != INVALID_HANDLE_VALUE && "Could not connect to pipe");
+ m_name = pipeName;
+ m_handle = handle;
+ m_openMode = openMode;
+ startPipeWorkers();
+}
+
+void NamedPipe::startPipeWorkers()
+{
+ if (m_openMode & OpenMode::Reading) {
+ m_inputWorker.reset(new InputWorker(*this));
+ }
+ if (m_openMode & OpenMode::Writing) {
+ m_outputWorker.reset(new OutputWorker(*this));
+ }
+}
+
+size_t NamedPipe::bytesToSend()
+{
+ ASSERT(m_openMode & OpenMode::Writing);
+ auto ret = m_outQueue.size();
+ if (m_outputWorker != NULL) {
+ ret += m_outputWorker->getPendingIoSize();
+ }
+ return ret;
+}
+
+void NamedPipe::write(const void *data, size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Writing);
+ m_outQueue.append(reinterpret_cast<const char*>(data), size);
+}
+
+void NamedPipe::write(const char *text)
+{
+ write(text, strlen(text));
+}
+
+size_t NamedPipe::readBufferSize()
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ return m_readBufferSize;
+}
+
+void NamedPipe::setReadBufferSize(size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ m_readBufferSize = size;
+}
+
+size_t NamedPipe::bytesAvailable()
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ return m_inQueue.size();
+}
+
+size_t NamedPipe::peek(void *data, size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ const auto out = reinterpret_cast<char*>(data);
+ const size_t ret = std::min(size, m_inQueue.size());
+ std::copy(&m_inQueue[0], &m_inQueue[ret], out);
+ return ret;
+}
+
+size_t NamedPipe::read(void *data, size_t size)
+{
+ size_t ret = peek(data, size);
+ m_inQueue.erase(0, ret);
+ return ret;
+}
+
+std::string NamedPipe::readToString(size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ size_t retSize = std::min(size, m_inQueue.size());
+ std::string ret = m_inQueue.substr(0, retSize);
+ m_inQueue.erase(0, retSize);
+ return ret;
+}
+
+std::string NamedPipe::readAllToString()
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ std::string ret = m_inQueue;
+ m_inQueue.clear();
+ return ret;
+}
+
+void NamedPipe::closePipe()
+{
+ if (m_handle == NULL) {
+ return;
+ }
+ CancelIo(m_handle);
+ if (m_connectEvent.get() != nullptr) {
+ DWORD actual = 0;
+ GetOverlappedResult(m_handle, &m_connectOver, &actual, TRUE);
+ m_connectEvent.dispose();
+ }
+ if (m_inputWorker) {
+ m_inputWorker->waitForCanceledIo();
+ m_inputWorker.reset();
+ }
+ if (m_outputWorker) {
+ m_outputWorker->waitForCanceledIo();
+ m_outputWorker.reset();
+ }
+ CloseHandle(m_handle);
+ m_handle = NULL;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/NamedPipe.h b/src/libs/3rdparty/winpty/src/agent/NamedPipe.h
new file mode 100644
index 0000000000..0a4d8b0c75
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/NamedPipe.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef NAMEDPIPE_H
+#define NAMEDPIPE_H
+
+#include <windows.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "../shared/OwnedHandle.h"
+
+class EventLoop;
+
+class NamedPipe
+{
+private:
+ // The EventLoop uses these private members.
+ friend class EventLoop;
+ NamedPipe() {}
+ ~NamedPipe() { closePipe(); }
+ bool serviceIo(std::vector<HANDLE> *waitHandles);
+ void startPipeWorkers();
+
+ enum class ServiceResult { NoProgress, Error, Progress };
+
+private:
+ class IoWorker
+ {
+ public:
+ IoWorker(NamedPipe &namedPipe);
+ virtual ~IoWorker() {}
+ ServiceResult service();
+ void waitForCanceledIo();
+ HANDLE getWaitEvent();
+ protected:
+ NamedPipe &m_namedPipe;
+ bool m_pending = false;
+ DWORD m_currentIoSize = 0;
+ OwnedHandle m_event;
+ OVERLAPPED m_over = {};
+ enum { kIoSize = 64 * 1024 };
+ char m_buffer[kIoSize];
+ virtual void completeIo(DWORD size) = 0;
+ virtual bool shouldIssueIo(DWORD *size, bool *isRead) = 0;
+ };
+
+ class InputWorker : public IoWorker
+ {
+ public:
+ InputWorker(NamedPipe &namedPipe) : IoWorker(namedPipe) {}
+ protected:
+ virtual void completeIo(DWORD size) override;
+ virtual bool shouldIssueIo(DWORD *size, bool *isRead) override;
+ };
+
+ class OutputWorker : public IoWorker
+ {
+ public:
+ OutputWorker(NamedPipe &namedPipe) : IoWorker(namedPipe) {}
+ DWORD getPendingIoSize();
+ protected:
+ virtual void completeIo(DWORD size) override;
+ virtual bool shouldIssueIo(DWORD *size, bool *isRead) override;
+ };
+
+public:
+ struct OpenMode {
+ typedef int t;
+ enum { None = 0, Reading = 1, Writing = 2, Duplex = 3 };
+ };
+
+ std::wstring name() const { return m_name; }
+ void openServerPipe(LPCWSTR pipeName, OpenMode::t openMode,
+ int outBufferSize, int inBufferSize);
+ void connectToServer(LPCWSTR pipeName, OpenMode::t openMode);
+ size_t bytesToSend();
+ void write(const void *data, size_t size);
+ void write(const char *text);
+ size_t readBufferSize();
+ void setReadBufferSize(size_t size);
+ size_t bytesAvailable();
+ size_t peek(void *data, size_t size);
+ size_t read(void *data, size_t size);
+ std::string readToString(size_t size);
+ std::string readAllToString();
+ void closePipe();
+ bool isClosed() { return m_handle == nullptr; }
+ bool isConnected() { return !isClosed() && !isConnecting(); }
+ bool isConnecting() { return m_connectEvent.get() != nullptr; }
+
+private:
+ // Input/output buffers
+ std::wstring m_name;
+ OVERLAPPED m_connectOver = {};
+ OwnedHandle m_connectEvent;
+ OpenMode::t m_openMode = OpenMode::None;
+ size_t m_readBufferSize = 64 * 1024;
+ std::string m_inQueue;
+ std::string m_outQueue;
+ HANDLE m_handle = nullptr;
+ std::unique_ptr<InputWorker> m_inputWorker;
+ std::unique_ptr<OutputWorker> m_outputWorker;
+};
+
+#endif // NAMEDPIPE_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Scraper.cc b/src/libs/3rdparty/winpty/src/agent/Scraper.cc
new file mode 100644
index 0000000000..21f9c67104
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Scraper.cc
@@ -0,0 +1,699 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Scraper.h"
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+#include "ConsoleFont.h"
+#include "Win32Console.h"
+#include "Win32ConsoleBuffer.h"
+
+namespace {
+
+template <typename T>
+T constrained(T min, T val, T max) {
+ ASSERT(min <= max);
+ return std::min(std::max(min, val), max);
+}
+
+} // anonymous namespace
+
+Scraper::Scraper(
+ Win32Console &console,
+ Win32ConsoleBuffer &buffer,
+ std::unique_ptr<Terminal> terminal,
+ Coord initialSize) :
+ m_console(console),
+ m_terminal(std::move(terminal)),
+ m_ptySize(initialSize)
+{
+ m_consoleBuffer = &buffer;
+
+ resetConsoleTracking(Terminal::OmitClear, buffer.windowRect().top());
+
+ m_bufferData.resize(BUFFER_LINE_COUNT);
+
+ // Setup the initial screen buffer and window size.
+ //
+ // Use SetConsoleWindowInfo to shrink the console window as much as
+ // possible -- to a 1x1 cell at the top-left. This call always succeeds.
+ // Prior to the new Windows 10 console, it also actually resizes the GUI
+ // window to 1x1 cell. Nevertheless, even though the GUI window can
+ // therefore be narrower than its minimum, calling
+ // SetConsoleScreenBufferSize with a 1x1 size still fails.
+ //
+ // While the small font intends to support large buffers, a user could
+ // still hit a limit imposed by their monitor width, so cap the new window
+ // size to GetLargestConsoleWindowSize().
+ setSmallFont(buffer.conout(), initialSize.X, m_console.isNewW10());
+ buffer.moveWindow(SmallRect(0, 0, 1, 1));
+ buffer.resizeBufferRange(Coord(initialSize.X, BUFFER_LINE_COUNT));
+ const auto largest = GetLargestConsoleWindowSize(buffer.conout());
+ buffer.moveWindow(SmallRect(
+ 0, 0,
+ std::min(initialSize.X, largest.X),
+ std::min(initialSize.Y, largest.Y)));
+ buffer.setCursorPosition(Coord(0, 0));
+
+ // For the sake of the color translation heuristic, set the console color
+ // to LtGray-on-Black.
+ buffer.setTextAttribute(Win32ConsoleBuffer::kDefaultAttributes);
+ buffer.clearAllLines(m_consoleBuffer->bufferInfo());
+
+ m_consoleBuffer = nullptr;
+}
+
+Scraper::~Scraper()
+{
+}
+
+// Whether or not the agent is frozen on entry, it will be frozen on exit.
+void Scraper::resizeWindow(Win32ConsoleBuffer &buffer,
+ Coord newSize,
+ ConsoleScreenBufferInfo &finalInfoOut)
+{
+ m_consoleBuffer = &buffer;
+ m_ptySize = newSize;
+ syncConsoleContentAndSize(true, finalInfoOut);
+ m_consoleBuffer = nullptr;
+}
+
+// This function may freeze the agent, but it will not unfreeze it.
+void Scraper::scrapeBuffer(Win32ConsoleBuffer &buffer,
+ ConsoleScreenBufferInfo &finalInfoOut)
+{
+ m_consoleBuffer = &buffer;
+ syncConsoleContentAndSize(false, finalInfoOut);
+ m_consoleBuffer = nullptr;
+}
+
+void Scraper::resetConsoleTracking(
+ Terminal::SendClearFlag sendClear, int64_t scrapedLineCount)
+{
+ for (ConsoleLine &line : m_bufferData) {
+ line.reset();
+ }
+ m_syncRow = -1;
+ m_scrapedLineCount = scrapedLineCount;
+ m_scrolledCount = 0;
+ m_maxBufferedLine = -1;
+ m_dirtyWindowTop = -1;
+ m_dirtyLineCount = 0;
+ m_terminal->reset(sendClear, m_scrapedLineCount);
+}
+
+// Detect window movement. If the window moves down (presumably as a
+// result of scrolling), then assume that all screen buffer lines down to
+// the bottom of the window are dirty.
+void Scraper::markEntireWindowDirty(const SmallRect &windowRect)
+{
+ m_dirtyLineCount = std::max(m_dirtyLineCount,
+ windowRect.top() + windowRect.height());
+}
+
+// Scan the screen buffer and advance the dirty line count when we find
+// non-empty lines.
+void Scraper::scanForDirtyLines(const SmallRect &windowRect)
+{
+ const int w = m_readBuffer.rect().width();
+ ASSERT(m_dirtyLineCount >= 1);
+ const CHAR_INFO *const prevLine =
+ m_readBuffer.lineData(m_dirtyLineCount - 1);
+ WORD prevLineAttr = prevLine[w - 1].Attributes;
+ const int stopLine = windowRect.top() + windowRect.height();
+
+ for (int line = m_dirtyLineCount; line < stopLine; ++line) {
+ const CHAR_INFO *lineData = m_readBuffer.lineData(line);
+ for (int col = 0; col < w; ++col) {
+ const WORD colAttr = lineData[col].Attributes;
+ if (lineData[col].Char.UnicodeChar != L' ' ||
+ colAttr != prevLineAttr) {
+ m_dirtyLineCount = line + 1;
+ break;
+ }
+ }
+ prevLineAttr = lineData[w - 1].Attributes;
+ }
+}
+
+// Clear lines in the line buffer. The `firstRow` parameter is in
+// screen-buffer coordinates.
+void Scraper::clearBufferLines(
+ const int firstRow,
+ const int count)
+{
+ ASSERT(!m_directMode);
+ for (int row = firstRow; row < firstRow + count; ++row) {
+ const int64_t bufLine = row + m_scrolledCount;
+ m_maxBufferedLine = std::max(m_maxBufferedLine, bufLine);
+ m_bufferData[bufLine % BUFFER_LINE_COUNT].blank(
+ Win32ConsoleBuffer::kDefaultAttributes);
+ }
+}
+
+static bool cursorInWindow(const ConsoleScreenBufferInfo &info)
+{
+ return info.dwCursorPosition.Y >= info.srWindow.Top &&
+ info.dwCursorPosition.Y <= info.srWindow.Bottom;
+}
+
+void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo)
+{
+ ASSERT(m_console.frozen());
+ const int cols = m_ptySize.X;
+ const int rows = m_ptySize.Y;
+ Coord finalBufferSize;
+
+ {
+ //
+ // To accommodate Windows 10, erase all lines up to the top of the
+ // visible window. It's hard to tell whether this is strictly
+ // necessary. It ensures that the sync marker won't move downward,
+ // and it ensures that we won't repeat lines that have already scrolled
+ // up into the scrollback.
+ //
+ // It *is* possible for these blank lines to reappear in the visible
+ // window (e.g. if the window is made taller), but because we blanked
+ // the lines in the line buffer, we still don't output them again.
+ //
+ const Coord origBufferSize = origInfo.bufferSize();
+ const SmallRect origWindowRect = origInfo.windowRect();
+
+ if (m_directMode) {
+ for (ConsoleLine &line : m_bufferData) {
+ line.reset();
+ }
+ } else {
+ m_consoleBuffer->clearLines(0, origWindowRect.Top, origInfo);
+ clearBufferLines(0, origWindowRect.Top);
+ if (m_syncRow != -1) {
+ createSyncMarker(std::min(
+ m_syncRow,
+ BUFFER_LINE_COUNT - rows
+ - SYNC_MARKER_LEN
+ - SYNC_MARKER_MARGIN));
+ }
+ }
+
+ finalBufferSize = Coord(
+ cols,
+ // If there was previously no scrollback (e.g. a full-screen app
+ // in direct mode) and we're reducing the window height, then
+ // reduce the console buffer's height too.
+ (origWindowRect.height() == origBufferSize.Y)
+ ? rows
+ : std::max<int>(rows, origBufferSize.Y));
+
+ // Reset the console font size. We need to do this before shrinking
+ // the window, because we might need to make the font bigger to permit
+ // a smaller window width. Making the font smaller could expand the
+ // screen buffer, which would hang the conhost process in the
+ // Windows 10 (10240 build) if the console selection is in progress, so
+ // unfreeze it first.
+ m_console.setFrozen(false);
+ setSmallFont(m_consoleBuffer->conout(), cols, m_console.isNewW10());
+ }
+
+ // We try to make the font small enough so that the entire screen buffer
+ // fits on the monitor, but it can't be guaranteed.
+ const auto largest =
+ GetLargestConsoleWindowSize(m_consoleBuffer->conout());
+ const short visibleCols = std::min<short>(cols, largest.X);
+ const short visibleRows = std::min<short>(rows, largest.Y);
+
+ {
+ // Make the window small enough. We want the console frozen during
+ // this step so we don't accidentally move the window above the cursor.
+ m_console.setFrozen(true);
+ const auto info = m_consoleBuffer->bufferInfo();
+ const auto &bufferSize = info.dwSize;
+ const int tmpWindowWidth = std::min(bufferSize.X, visibleCols);
+ const int tmpWindowHeight = std::min(bufferSize.Y, visibleRows);
+ SmallRect tmpWindowRect(
+ 0,
+ std::min<int>(bufferSize.Y - tmpWindowHeight,
+ info.windowRect().Top),
+ tmpWindowWidth,
+ tmpWindowHeight);
+ if (cursorInWindow(info)) {
+ tmpWindowRect = tmpWindowRect.ensureLineIncluded(
+ info.cursorPosition().Y);
+ }
+ m_consoleBuffer->moveWindow(tmpWindowRect);
+ }
+
+ {
+ // Resize the buffer to the final desired size.
+ m_console.setFrozen(false);
+ m_consoleBuffer->resizeBufferRange(finalBufferSize);
+ }
+
+ {
+ // Expand the window to its full size.
+ m_console.setFrozen(true);
+ const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo();
+
+ SmallRect finalWindowRect(
+ 0,
+ std::min<int>(info.bufferSize().Y - visibleRows,
+ info.windowRect().Top),
+ visibleCols,
+ visibleRows);
+
+ //
+ // Once a line in the screen buffer is "dirty", it should stay visible
+ // in the console window, so that we continue to update its content in
+ // the terminal. This code is particularly (only?) necessary on
+ // Windows 10, where making the buffer wider can rewrap lines and move
+ // the console window upward.
+ //
+ if (!m_directMode && m_dirtyLineCount > finalWindowRect.Bottom + 1) {
+ // In theory, we avoid ensureLineIncluded, because, a massive
+ // amount of output could have occurred while the console was
+ // unfrozen, so that the *top* of the window is now below the
+ // dirtiest tracked line.
+ finalWindowRect = SmallRect(
+ 0, m_dirtyLineCount - visibleRows,
+ visibleCols, visibleRows);
+ }
+
+ // Highest priority constraint: ensure that the cursor remains visible.
+ if (cursorInWindow(info)) {
+ finalWindowRect = finalWindowRect.ensureLineIncluded(
+ info.cursorPosition().Y);
+ }
+
+ m_consoleBuffer->moveWindow(finalWindowRect);
+ m_dirtyWindowTop = finalWindowRect.Top;
+ }
+
+ ASSERT(m_console.frozen());
+}
+
+void Scraper::syncConsoleContentAndSize(
+ bool forceResize,
+ ConsoleScreenBufferInfo &finalInfoOut)
+{
+ // We'll try to avoid freezing the console by reading large chunks (or
+ // all!) of the screen buffer without otherwise attempting to synchronize
+ // with the console application. We can only do this on Windows 10 and up
+ // because:
+ // - Prior to Windows 8, the size of a ReadConsoleOutputW call was limited
+ // by the ~32KB RPC buffer.
+ // - Prior to Windows 10, an out-of-range read region crashes the caller.
+ // (See misc/WindowsBugCrashReader.cc.)
+ //
+ if (!m_console.isNewW10() || forceResize) {
+ m_console.setFrozen(true);
+ }
+
+ const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo();
+ bool cursorVisible = true;
+ CONSOLE_CURSOR_INFO cursorInfo = {};
+ if (!GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursorInfo)) {
+ trace("GetConsoleCursorInfo failed");
+ } else {
+ cursorVisible = cursorInfo.bVisible != 0;
+ }
+
+ // If an app resizes the buffer height, then we enter "direct mode", where
+ // we stop trying to track incremental console changes.
+ const bool newDirectMode = (info.bufferSize().Y != BUFFER_LINE_COUNT);
+ if (newDirectMode != m_directMode) {
+ trace("Entering %s mode", newDirectMode ? "direct" : "scrolling");
+ resetConsoleTracking(Terminal::SendClear,
+ newDirectMode ? 0 : info.windowRect().top());
+ m_directMode = newDirectMode;
+
+ // When we switch from direct->scrolling mode, make sure the console is
+ // the right size.
+ if (!m_directMode) {
+ m_console.setFrozen(true);
+ forceResize = true;
+ }
+ }
+
+ if (m_directMode) {
+ // In direct-mode, resizing the console redraws the terminal, so do it
+ // before scraping.
+ if (forceResize) {
+ resizeImpl(info);
+ }
+ directScrapeOutput(info, cursorVisible);
+ } else {
+ if (!m_console.frozen()) {
+ if (!scrollingScrapeOutput(info, cursorVisible, true)) {
+ m_console.setFrozen(true);
+ }
+ }
+ if (m_console.frozen()) {
+ scrollingScrapeOutput(info, cursorVisible, false);
+ }
+ // In scrolling mode, we want to scrape before resizing, because we'll
+ // erase everything in the console buffer up to the top of the console
+ // window.
+ if (forceResize) {
+ resizeImpl(info);
+ }
+ }
+
+ finalInfoOut = forceResize ? m_consoleBuffer->bufferInfo() : info;
+}
+
+// Try to match Windows' behavior w.r.t. to the LVB attribute flags. In some
+// situations, Windows ignores the LVB flags on a character cell because of
+// backwards compatibility -- apparently some programs set the flags without
+// intending to enable reverse-video or underscores.
+//
+// [rprichard 2017-01-15] I haven't actually noticed any old programs that need
+// this treatment -- the motivation for this function comes from the MSDN
+// documentation for SetConsoleMode and ENABLE_LVB_GRID_WORLDWIDE.
+WORD Scraper::attributesMask()
+{
+ const auto WINPTY_ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4u;
+ const auto WINPTY_ENABLE_LVB_GRID_WORLDWIDE = 0x10u;
+ const auto WINPTY_COMMON_LVB_REVERSE_VIDEO = 0x4000u;
+ const auto WINPTY_COMMON_LVB_UNDERSCORE = 0x8000u;
+
+ const auto cp = GetConsoleOutputCP();
+ const auto isCjk = (cp == 932 || cp == 936 || cp == 949 || cp == 950);
+
+ const DWORD outputMode = [this]{
+ ASSERT(this->m_consoleBuffer != nullptr);
+ DWORD mode = 0;
+ if (!GetConsoleMode(this->m_consoleBuffer->conout(), &mode)) {
+ mode = 0;
+ }
+ return mode;
+ }();
+ const bool hasEnableLvbGridWorldwide =
+ (outputMode & WINPTY_ENABLE_LVB_GRID_WORLDWIDE) != 0;
+ const bool hasEnableVtProcessing =
+ (outputMode & WINPTY_ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
+
+ // The new Windows 10 console (as of 14393) seems to respect
+ // COMMON_LVB_REVERSE_VIDEO even in CP437 w/o the other enabling modes, so
+ // try to match that behavior.
+ const auto isReverseSupported =
+ isCjk || hasEnableLvbGridWorldwide || hasEnableVtProcessing || m_console.isNewW10();
+ const auto isUnderscoreSupported =
+ isCjk || hasEnableLvbGridWorldwide || hasEnableVtProcessing;
+
+ WORD mask = ~0;
+ if (!isReverseSupported) { mask &= ~WINPTY_COMMON_LVB_REVERSE_VIDEO; }
+ if (!isUnderscoreSupported) { mask &= ~WINPTY_COMMON_LVB_UNDERSCORE; }
+ return mask;
+}
+
+void Scraper::directScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible)
+{
+ const SmallRect windowRect = info.windowRect();
+
+ const SmallRect scrapeRect(
+ windowRect.left(), windowRect.top(),
+ std::min<SHORT>(std::min(windowRect.width(), m_ptySize.X),
+ MAX_CONSOLE_WIDTH),
+ std::min<SHORT>(std::min(windowRect.height(), m_ptySize.Y),
+ BUFFER_LINE_COUNT));
+ const int w = scrapeRect.width();
+ const int h = scrapeRect.height();
+
+ const Coord cursor = info.cursorPosition();
+ const bool showTerminalCursor =
+ consoleCursorVisible && scrapeRect.contains(cursor);
+ const int cursorColumn = !showTerminalCursor ? -1 : cursor.X - scrapeRect.Left;
+ const int cursorLine = !showTerminalCursor ? -1 : cursor.Y - scrapeRect.Top;
+
+ if (!showTerminalCursor) {
+ m_terminal->hideTerminalCursor();
+ }
+
+ largeConsoleRead(m_readBuffer, *m_consoleBuffer, scrapeRect, attributesMask());
+
+ for (int line = 0; line < h; ++line) {
+ const CHAR_INFO *const curLine =
+ m_readBuffer.lineData(scrapeRect.top() + line);
+ ConsoleLine &bufLine = m_bufferData[line];
+ if (bufLine.detectChangeAndSetLine(curLine, w)) {
+ const int lineCursorColumn =
+ line == cursorLine ? cursorColumn : -1;
+ m_terminal->sendLine(line, curLine, w, lineCursorColumn);
+ }
+ }
+
+ if (showTerminalCursor) {
+ m_terminal->showTerminalCursor(cursorColumn, cursorLine);
+ }
+}
+
+bool Scraper::scrollingScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible,
+ bool tentative)
+{
+ const Coord cursor = info.cursorPosition();
+ const SmallRect windowRect = info.windowRect();
+
+ if (m_syncRow != -1) {
+ // If a synchronizing marker was placed into the history, look for it
+ // and adjust the scroll count.
+ const int markerRow = findSyncMarker();
+ if (markerRow == -1) {
+ if (tentative) {
+ // I *think* it's possible to keep going, but it's simple to
+ // bail out.
+ return false;
+ }
+ // Something has happened. Reset the terminal.
+ trace("Sync marker has disappeared -- resetting the terminal"
+ " (m_syncCounter=%u)",
+ m_syncCounter);
+ resetConsoleTracking(Terminal::SendClear, windowRect.top());
+ } else if (markerRow != m_syncRow) {
+ ASSERT(markerRow < m_syncRow);
+ m_scrolledCount += (m_syncRow - markerRow);
+ m_syncRow = markerRow;
+ // If the buffer has scrolled, then the entire window is dirty.
+ markEntireWindowDirty(windowRect);
+ }
+ }
+
+ // Creating a new sync row requires clearing part of the console buffer, so
+ // avoid doing it if there's already a sync row that's good enough.
+ const int newSyncRow =
+ static_cast<int>(windowRect.top()) - SYNC_MARKER_LEN - SYNC_MARKER_MARGIN;
+ const bool shouldCreateSyncRow =
+ newSyncRow >= m_syncRow + SYNC_MARKER_LEN + SYNC_MARKER_MARGIN;
+ if (tentative && shouldCreateSyncRow) {
+ // It's difficult even in principle to put down a new marker if the
+ // console can scroll an arbitrarily amount while we're writing.
+ return false;
+ }
+
+ // Update the dirty line count:
+ // - If the window has moved, the entire window is dirty.
+ // - Everything up to the cursor is dirty.
+ // - All lines above the window are dirty.
+ // - Any non-blank lines are dirty.
+ if (m_dirtyWindowTop != -1) {
+ if (windowRect.top() > m_dirtyWindowTop) {
+ // The window has moved down, presumably as a result of scrolling.
+ markEntireWindowDirty(windowRect);
+ } else if (windowRect.top() < m_dirtyWindowTop) {
+ if (tentative) {
+ // I *think* it's possible to keep going, but it's simple to
+ // bail out.
+ return false;
+ }
+ // The window has moved upward. This is generally not expected to
+ // happen, but the CMD/PowerShell CLS command will move the window
+ // to the top as part of clearing everything else in the console.
+ trace("Window moved upward -- resetting the terminal"
+ " (m_syncCounter=%u)",
+ m_syncCounter);
+ resetConsoleTracking(Terminal::SendClear, windowRect.top());
+ }
+ }
+ m_dirtyWindowTop = windowRect.top();
+ m_dirtyLineCount = std::max(m_dirtyLineCount, cursor.Y + 1);
+ m_dirtyLineCount = std::max(m_dirtyLineCount, (int)windowRect.top());
+
+ // There will be at least one dirty line, because there is a cursor.
+ ASSERT(m_dirtyLineCount >= 1);
+
+ // The first line to scrape, in virtual line coordinates.
+ const int64_t firstVirtLine = std::min(m_scrapedLineCount,
+ windowRect.top() + m_scrolledCount);
+
+ // Read all the data we will need from the console. Start reading with the
+ // first line to scrape, but adjust the the read area upward to account for
+ // scanForDirtyLines' need to read the previous attribute. Read to the
+ // bottom of the window. (It's not clear to me whether the
+ // m_dirtyLineCount adjustment here is strictly necessary. It isn't
+ // necessary so long as the cursor is inside the current window.)
+ const int firstReadLine = std::min<int>(firstVirtLine - m_scrolledCount,
+ m_dirtyLineCount - 1);
+ const int stopReadLine = std::max(windowRect.top() + windowRect.height(),
+ m_dirtyLineCount);
+ ASSERT(firstReadLine >= 0 && stopReadLine > firstReadLine);
+ largeConsoleRead(m_readBuffer,
+ *m_consoleBuffer,
+ SmallRect(0, firstReadLine,
+ std::min<SHORT>(info.bufferSize().X,
+ MAX_CONSOLE_WIDTH),
+ stopReadLine - firstReadLine),
+ attributesMask());
+
+ // If we're scraping the buffer without freezing it, we have to query the
+ // buffer position data separately from the buffer content, so the two
+ // could easily be out-of-sync. If they *are* out-of-sync, abort the
+ // scrape operation and restart it frozen. (We may have updated the
+ // dirty-line high-water-mark, but that should be OK.)
+ if (tentative) {
+ const auto infoCheck = m_consoleBuffer->bufferInfo();
+ if (info.bufferSize() != infoCheck.bufferSize() ||
+ info.windowRect() != infoCheck.windowRect() ||
+ info.cursorPosition() != infoCheck.cursorPosition()) {
+ return false;
+ }
+ if (m_syncRow != -1 && m_syncRow != findSyncMarker()) {
+ return false;
+ }
+ }
+
+ if (shouldCreateSyncRow) {
+ ASSERT(!tentative);
+ createSyncMarker(newSyncRow);
+ }
+
+ // At this point, we're finished interacting (reading or writing) the
+ // console, and we just need to convert our collected data into terminal
+ // output.
+
+ scanForDirtyLines(windowRect);
+
+ // Note that it's possible for all the lines on the current window to
+ // be non-dirty.
+
+ // The line to stop scraping at, in virtual line coordinates.
+ const int64_t stopVirtLine =
+ std::min(m_dirtyLineCount, windowRect.top() + windowRect.height()) +
+ m_scrolledCount;
+
+ const bool showTerminalCursor =
+ consoleCursorVisible && windowRect.contains(cursor);
+ const int64_t cursorLine = !showTerminalCursor ? -1 : cursor.Y + m_scrolledCount;
+ const int cursorColumn = !showTerminalCursor ? -1 : cursor.X;
+
+ if (!showTerminalCursor) {
+ m_terminal->hideTerminalCursor();
+ }
+
+ bool sawModifiedLine = false;
+
+ const int w = m_readBuffer.rect().width();
+ for (int64_t line = firstVirtLine; line < stopVirtLine; ++line) {
+ const CHAR_INFO *curLine =
+ m_readBuffer.lineData(line - m_scrolledCount);
+ ConsoleLine &bufLine = m_bufferData[line % BUFFER_LINE_COUNT];
+ if (line > m_maxBufferedLine) {
+ m_maxBufferedLine = line;
+ sawModifiedLine = true;
+ }
+ if (sawModifiedLine) {
+ bufLine.setLine(curLine, w);
+ } else {
+ sawModifiedLine = bufLine.detectChangeAndSetLine(curLine, w);
+ }
+ if (sawModifiedLine) {
+ const int lineCursorColumn =
+ line == cursorLine ? cursorColumn : -1;
+ m_terminal->sendLine(line, curLine, w, lineCursorColumn);
+ }
+ }
+
+ m_scrapedLineCount = windowRect.top() + m_scrolledCount;
+
+ if (showTerminalCursor) {
+ m_terminal->showTerminalCursor(cursorColumn, cursorLine);
+ }
+
+ return true;
+}
+
+void Scraper::syncMarkerText(CHAR_INFO (&output)[SYNC_MARKER_LEN])
+{
+ // XXX: The marker text generated here could easily collide with ordinary
+ // console output. Does it make sense to try to avoid the collision?
+ char str[SYNC_MARKER_LEN + 1];
+ winpty_snprintf(str, "S*Y*N*C*%08x", m_syncCounter);
+ for (int i = 0; i < SYNC_MARKER_LEN; ++i) {
+ output[i].Char.UnicodeChar = str[i];
+ output[i].Attributes = 7;
+ }
+}
+
+int Scraper::findSyncMarker()
+{
+ ASSERT(m_syncRow >= 0);
+ CHAR_INFO marker[SYNC_MARKER_LEN];
+ CHAR_INFO column[BUFFER_LINE_COUNT];
+ syncMarkerText(marker);
+ SmallRect rect(0, 0, 1, m_syncRow + SYNC_MARKER_LEN);
+ m_consoleBuffer->read(rect, column);
+ int i;
+ for (i = m_syncRow; i >= 0; --i) {
+ int j;
+ for (j = 0; j < SYNC_MARKER_LEN; ++j) {
+ if (column[i + j].Char.UnicodeChar != marker[j].Char.UnicodeChar)
+ break;
+ }
+ if (j == SYNC_MARKER_LEN)
+ return i;
+ }
+ return -1;
+}
+
+void Scraper::createSyncMarker(int row)
+{
+ ASSERT(row >= 1);
+
+ // Clear the lines around the marker to ensure that Windows 10's rewrapping
+ // does not affect the marker.
+ m_consoleBuffer->clearLines(row - 1, SYNC_MARKER_LEN + 1,
+ m_consoleBuffer->bufferInfo());
+
+ // Write a new marker.
+ m_syncCounter++;
+ CHAR_INFO marker[SYNC_MARKER_LEN];
+ syncMarkerText(marker);
+ m_syncRow = row;
+ SmallRect markerRect(0, m_syncRow, 1, SYNC_MARKER_LEN);
+ m_consoleBuffer->write(markerRect, marker);
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Scraper.h b/src/libs/3rdparty/winpty/src/agent/Scraper.h
new file mode 100644
index 0000000000..9c10d80aed
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Scraper.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_SCRAPER_H
+#define AGENT_SCRAPER_H
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "ConsoleLine.h"
+#include "Coord.h"
+#include "LargeConsoleRead.h"
+#include "SmallRect.h"
+#include "Terminal.h"
+
+class ConsoleScreenBufferInfo;
+class Win32Console;
+class Win32ConsoleBuffer;
+
+// We must be able to issue a single ReadConsoleOutputW call of
+// MAX_CONSOLE_WIDTH characters, and a single read of approximately several
+// hundred fewer characters than BUFFER_LINE_COUNT.
+const int BUFFER_LINE_COUNT = 3000;
+const int MAX_CONSOLE_WIDTH = 2500;
+const int MAX_CONSOLE_HEIGHT = 2000;
+const int SYNC_MARKER_LEN = 16;
+const int SYNC_MARKER_MARGIN = 200;
+
+class Scraper {
+public:
+ Scraper(
+ Win32Console &console,
+ Win32ConsoleBuffer &buffer,
+ std::unique_ptr<Terminal> terminal,
+ Coord initialSize);
+ ~Scraper();
+ void resizeWindow(Win32ConsoleBuffer &buffer,
+ Coord newSize,
+ ConsoleScreenBufferInfo &finalInfoOut);
+ void scrapeBuffer(Win32ConsoleBuffer &buffer,
+ ConsoleScreenBufferInfo &finalInfoOut);
+ Terminal &terminal() { return *m_terminal; }
+
+private:
+ void resetConsoleTracking(
+ Terminal::SendClearFlag sendClear, int64_t scrapedLineCount);
+ void markEntireWindowDirty(const SmallRect &windowRect);
+ void scanForDirtyLines(const SmallRect &windowRect);
+ void clearBufferLines(int firstRow, int count);
+ void resizeImpl(const ConsoleScreenBufferInfo &origInfo);
+ void syncConsoleContentAndSize(bool forceResize,
+ ConsoleScreenBufferInfo &finalInfoOut);
+ WORD attributesMask();
+ void directScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible);
+ bool scrollingScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible,
+ bool tentative);
+ void syncMarkerText(CHAR_INFO (&output)[SYNC_MARKER_LEN]);
+ int findSyncMarker();
+ void createSyncMarker(int row);
+
+private:
+ Win32Console &m_console;
+ Win32ConsoleBuffer *m_consoleBuffer = nullptr;
+ std::unique_ptr<Terminal> m_terminal;
+
+ int m_syncRow = -1;
+ unsigned int m_syncCounter = 0;
+
+ bool m_directMode = false;
+ Coord m_ptySize;
+ int64_t m_scrapedLineCount = 0;
+ int64_t m_scrolledCount = 0;
+ int64_t m_maxBufferedLine = -1;
+ LargeConsoleReadBuffer m_readBuffer;
+ std::vector<ConsoleLine> m_bufferData;
+ int m_dirtyWindowTop = -1;
+ int m_dirtyLineCount = 0;
+};
+
+#endif // AGENT_SCRAPER_H
diff --git a/src/libs/3rdparty/winpty/src/agent/SimplePool.h b/src/libs/3rdparty/winpty/src/agent/SimplePool.h
new file mode 100644
index 0000000000..41ff94a90d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/SimplePool.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef SIMPLE_POOL_H
+#define SIMPLE_POOL_H
+
+#include <stdlib.h>
+
+#include <vector>
+
+#include "../shared/WinptyAssert.h"
+
+template <typename T, size_t chunkSize>
+class SimplePool {
+public:
+ ~SimplePool();
+ T *alloc();
+ void clear();
+private:
+ struct Chunk {
+ size_t count;
+ T *data;
+ };
+ std::vector<Chunk> m_chunks;
+};
+
+template <typename T, size_t chunkSize>
+SimplePool<T, chunkSize>::~SimplePool() {
+ clear();
+}
+
+template <typename T, size_t chunkSize>
+void SimplePool<T, chunkSize>::clear() {
+ for (size_t ci = 0; ci < m_chunks.size(); ++ci) {
+ Chunk &chunk = m_chunks[ci];
+ for (size_t ti = 0; ti < chunk.count; ++ti) {
+ chunk.data[ti].~T();
+ }
+ free(chunk.data);
+ }
+ m_chunks.clear();
+}
+
+template <typename T, size_t chunkSize>
+T *SimplePool<T, chunkSize>::alloc() {
+ if (m_chunks.empty() || m_chunks.back().count == chunkSize) {
+ T *newData = reinterpret_cast<T*>(malloc(sizeof(T) * chunkSize));
+ ASSERT(newData != NULL);
+ Chunk newChunk = { 0, newData };
+ m_chunks.push_back(newChunk);
+ }
+ Chunk &chunk = m_chunks.back();
+ T *ret = &chunk.data[chunk.count++];
+ new (ret) T();
+ return ret;
+}
+
+#endif // SIMPLE_POOL_H
diff --git a/src/libs/3rdparty/winpty/src/agent/SmallRect.h b/src/libs/3rdparty/winpty/src/agent/SmallRect.h
new file mode 100644
index 0000000000..bad0b88683
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/SmallRect.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef SMALLRECT_H
+#define SMALLRECT_H
+
+#include <windows.h>
+
+#include <algorithm>
+#include <string>
+
+#include "../shared/winpty_snprintf.h"
+#include "Coord.h"
+
+struct SmallRect : SMALL_RECT
+{
+ SmallRect()
+ {
+ Left = Right = Top = Bottom = 0;
+ }
+
+ SmallRect(SHORT x, SHORT y, SHORT width, SHORT height)
+ {
+ Left = x;
+ Top = y;
+ Right = x + width - 1;
+ Bottom = y + height - 1;
+ }
+
+ SmallRect(const COORD &topLeft, const COORD &size)
+ {
+ Left = topLeft.X;
+ Top = topLeft.Y;
+ Right = Left + size.X - 1;
+ Bottom = Top + size.Y - 1;
+ }
+
+ SmallRect(const SMALL_RECT &other)
+ {
+ *(SMALL_RECT*)this = other;
+ }
+
+ SmallRect(const SmallRect &other)
+ {
+ *(SMALL_RECT*)this = *(const SMALL_RECT*)&other;
+ }
+
+ SmallRect &operator=(const SmallRect &other)
+ {
+ *(SMALL_RECT*)this = *(const SMALL_RECT*)&other;
+ return *this;
+ }
+
+ bool contains(const SmallRect &other) const
+ {
+ return other.Left >= Left &&
+ other.Right <= Right &&
+ other.Top >= Top &&
+ other.Bottom <= Bottom;
+ }
+
+ bool contains(const Coord &other) const
+ {
+ return other.X >= Left &&
+ other.X <= Right &&
+ other.Y >= Top &&
+ other.Y <= Bottom;
+ }
+
+ SmallRect intersected(const SmallRect &other) const
+ {
+ int x1 = std::max(Left, other.Left);
+ int x2 = std::min(Right, other.Right);
+ int y1 = std::max(Top, other.Top);
+ int y2 = std::min(Bottom, other.Bottom);
+ return SmallRect(x1,
+ y1,
+ std::max(0, x2 - x1 + 1),
+ std::max(0, y2 - y1 + 1));
+ }
+
+ SmallRect ensureLineIncluded(SHORT line) const
+ {
+ const SHORT h = height();
+ if (line < Top) {
+ return SmallRect(Left, line, width(), h);
+ } else if (line > Bottom) {
+ return SmallRect(Left, line - h + 1, width(), h);
+ } else {
+ return *this;
+ }
+ }
+
+ SHORT top() const { return Top; }
+ SHORT left() const { return Left; }
+ SHORT width() const { return Right - Left + 1; }
+ SHORT height() const { return Bottom - Top + 1; }
+ void setTop(SHORT top) { Top = top; }
+ void setLeft(SHORT left) { Left = left; }
+ void setWidth(SHORT width) { Right = Left + width - 1; }
+ void setHeight(SHORT height) { Bottom = Top + height - 1; }
+ Coord size() const { return Coord(width(), height()); }
+
+ bool operator==(const SmallRect &other) const
+ {
+ return Left == other.Left &&
+ Right == other.Right &&
+ Top == other.Top &&
+ Bottom == other.Bottom;
+ }
+
+ bool operator!=(const SmallRect &other) const
+ {
+ return !(*this == other);
+ }
+
+ std::string toString() const
+ {
+ char ret[64];
+ winpty_snprintf(ret, "(x=%d,y=%d,w=%d,h=%d)",
+ Left, Top, width(), height());
+ return std::string(ret);
+ }
+};
+
+#endif // SMALLRECT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Terminal.cc b/src/libs/3rdparty/winpty/src/agent/Terminal.cc
new file mode 100644
index 0000000000..afa0a36260
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Terminal.cc
@@ -0,0 +1,535 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Terminal.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+#include "NamedPipe.h"
+#include "UnicodeEncoding.h"
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+#define CSI "\x1b["
+
+// Work around the old MinGW, which lacks COMMON_LVB_LEADING_BYTE and
+// COMMON_LVB_TRAILING_BYTE.
+const int WINPTY_COMMON_LVB_LEADING_BYTE = 0x100;
+const int WINPTY_COMMON_LVB_TRAILING_BYTE = 0x200;
+const int WINPTY_COMMON_LVB_REVERSE_VIDEO = 0x4000;
+const int WINPTY_COMMON_LVB_UNDERSCORE = 0x8000;
+
+const int COLOR_ATTRIBUTE_MASK =
+ FOREGROUND_BLUE |
+ FOREGROUND_GREEN |
+ FOREGROUND_RED |
+ FOREGROUND_INTENSITY |
+ BACKGROUND_BLUE |
+ BACKGROUND_GREEN |
+ BACKGROUND_RED |
+ BACKGROUND_INTENSITY |
+ WINPTY_COMMON_LVB_REVERSE_VIDEO |
+ WINPTY_COMMON_LVB_UNDERSCORE;
+
+const int FLAG_RED = 1;
+const int FLAG_GREEN = 2;
+const int FLAG_BLUE = 4;
+const int FLAG_BRIGHT = 8;
+
+const int BLACK = 0;
+const int DKGRAY = BLACK | FLAG_BRIGHT;
+const int LTGRAY = FLAG_RED | FLAG_GREEN | FLAG_BLUE;
+const int WHITE = LTGRAY | FLAG_BRIGHT;
+
+// SGR parameters (Select Graphic Rendition)
+const int SGR_FORE = 30;
+const int SGR_FORE_HI = 90;
+const int SGR_BACK = 40;
+const int SGR_BACK_HI = 100;
+
+namespace {
+
+static void outUInt(std::string &out, unsigned int n)
+{
+ char buf[32];
+ char *pbuf = &buf[32];
+ *(--pbuf) = '\0';
+ do {
+ *(--pbuf) = '0' + n % 10;
+ n /= 10;
+ } while (n != 0);
+ out.append(pbuf);
+}
+
+static void outputSetColorSgrParams(std::string &out, bool isFore, int color)
+{
+ out.push_back(';');
+ const int sgrBase = isFore ? SGR_FORE : SGR_BACK;
+ if (color & FLAG_BRIGHT) {
+ // Some terminals don't support the 9X/10X "intensive" color parameters
+ // (e.g. the Eclipse TM terminal as of this writing). Those terminals
+ // will quietly ignore a 9X/10X code, and the other terminals will
+ // ignore a 3X/4X code if it's followed by a 9X/10X code. Therefore,
+ // output a 3X/4X code as a fallback, then override it.
+ const int colorBase = color & ~FLAG_BRIGHT;
+ outUInt(out, sgrBase + colorBase);
+ out.push_back(';');
+ outUInt(out, sgrBase + (SGR_FORE_HI - SGR_FORE) + colorBase);
+ } else {
+ outUInt(out, sgrBase + color);
+ }
+}
+
+static void outputSetColor(std::string &out, int color)
+{
+ int fore = 0;
+ int back = 0;
+ if (color & FOREGROUND_RED) fore |= FLAG_RED;
+ if (color & FOREGROUND_GREEN) fore |= FLAG_GREEN;
+ if (color & FOREGROUND_BLUE) fore |= FLAG_BLUE;
+ if (color & FOREGROUND_INTENSITY) fore |= FLAG_BRIGHT;
+ if (color & BACKGROUND_RED) back |= FLAG_RED;
+ if (color & BACKGROUND_GREEN) back |= FLAG_GREEN;
+ if (color & BACKGROUND_BLUE) back |= FLAG_BLUE;
+ if (color & BACKGROUND_INTENSITY) back |= FLAG_BRIGHT;
+
+ if (color & WINPTY_COMMON_LVB_REVERSE_VIDEO) {
+ // n.b.: The COMMON_LVB_REVERSE_VIDEO flag also swaps
+ // FOREGROUND_INTENSITY and BACKGROUND_INTENSITY. Tested on
+ // Windows 10 v14393.
+ std::swap(fore, back);
+ }
+
+ // Translate the fore/back colors into terminal escape codes using
+ // a heuristic that works OK with common white-on-black or
+ // black-on-white color schemes. We don't know which color scheme
+ // the terminal is using. It is ugly to force white-on-black text
+ // on a black-on-white terminal, and it's even ugly to force the
+ // matching scheme. It's probably relevant that the default
+ // fore/back terminal colors frequently do not match any of the 16
+ // palette colors.
+
+ // Typical default terminal color schemes (according to palette,
+ // when possible):
+ // - mintty: LtGray-on-Black(A)
+ // - putty: LtGray-on-Black(A)
+ // - xterm: LtGray-on-Black(A)
+ // - Konsole: LtGray-on-Black(A)
+ // - JediTerm/JetBrains: Black-on-White(B)
+ // - rxvt: Black-on-White(B)
+
+ // If the background is the default color (black), then it will
+ // map to Black(A) or White(B). If we translate White to White,
+ // then a Black background and a White background in the console
+ // are both White with (B). Therefore, we should translate White
+ // using SGR 7 (Invert). The typical finished mapping table for
+ // background grayscale colors is:
+ //
+ // (A) White => LtGray(fore)
+ // (A) Black => Black(back)
+ // (A) LtGray => LtGray
+ // (A) DkGray => DkGray
+ //
+ // (B) White => Black(fore)
+ // (B) Black => White(back)
+ // (B) LtGray => LtGray
+ // (B) DkGray => DkGray
+ //
+
+ out.append(CSI "0");
+ if (back == BLACK) {
+ if (fore == LTGRAY) {
+ // The "default" foreground color. Use the terminal's
+ // default colors.
+ } else if (fore == WHITE) {
+ // Sending the literal color white would behave poorly if
+ // the terminal were black-on-white. Sending Bold is not
+ // guaranteed to alter the color, but it will make the text
+ // visually distinct, so do that instead.
+ out.append(";1");
+ } else if (fore == DKGRAY) {
+ // Set the foreground color to DkGray(90) with a fallback
+ // of LtGray(37) for terminals that don't handle the 9X SGR
+ // parameters (e.g. Eclipse's TM Terminal as of this
+ // writing).
+ out.append(";37;90");
+ } else {
+ outputSetColorSgrParams(out, true, fore);
+ }
+ } else if (back == WHITE) {
+ // Set the background color using Invert on the default
+ // foreground color, and set the foreground color by setting a
+ // background color.
+
+ // Use the terminal's inverted colors.
+ out.append(";7");
+ if (fore == LTGRAY || fore == BLACK) {
+ // We're likely mapping Console White to terminal LtGray or
+ // Black. If they are the Console foreground color, then
+ // don't set a terminal foreground color to avoid creating
+ // invisible text.
+ } else {
+ outputSetColorSgrParams(out, false, fore);
+ }
+ } else {
+ // Set the foreground and background to match exactly that in
+ // the Windows console.
+ outputSetColorSgrParams(out, true, fore);
+ outputSetColorSgrParams(out, false, back);
+ }
+ if (fore == back) {
+ // The foreground and background colors are exactly equal, so
+ // attempt to hide the text using the Conceal SGR parameter,
+ // which some terminals support.
+ out.append(";8");
+ }
+ if (color & WINPTY_COMMON_LVB_UNDERSCORE) {
+ out.append(";4");
+ }
+ out.push_back('m');
+}
+
+static inline unsigned int fixSpecialCharacters(unsigned int ch)
+{
+ if (ch <= 0x1b) {
+ switch (ch) {
+ // The Windows Console has a popup window (e.g. that appears with
+ // F7) that is sometimes bordered with box-drawing characters.
+ // With the Japanese and Korean system locales (CP932 and CP949),
+ // the UnicodeChar values for the box-drawing characters are 1
+ // through 6. Detect this and map the values to the correct
+ // Unicode values.
+ //
+ // N.B. In the English locale, the UnicodeChar values are correct,
+ // and they identify single-line characters rather than
+ // double-line. In the Chinese Simplified and Traditional locales,
+ // the popups use ASCII characters instead.
+ case 1: return 0x2554; // BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ case 2: return 0x2557; // BOX DRAWINGS DOUBLE DOWN AND LEFT
+ case 3: return 0x255A; // BOX DRAWINGS DOUBLE UP AND RIGHT
+ case 4: return 0x255D; // BOX DRAWINGS DOUBLE UP AND LEFT
+ case 5: return 0x2551; // BOX DRAWINGS DOUBLE VERTICAL
+ case 6: return 0x2550; // BOX DRAWINGS DOUBLE HORIZONTAL
+
+ // Convert an escape character to some other character. This
+ // conversion only applies to console cells containing an escape
+ // character. In newer versions of Windows 10 (e.g. 10.0.10586),
+ // the non-legacy console recognizes escape sequences in
+ // WriteConsole and interprets them without writing them to the
+ // cells of the screen buffer. In that case, the conversion here
+ // does not apply.
+ case 0x1b: return '?';
+ }
+ }
+ return ch;
+}
+
+static inline bool isFullWidthCharacter(const CHAR_INFO *data, int width)
+{
+ if (width < 2) {
+ return false;
+ }
+ return
+ (data[0].Attributes & WINPTY_COMMON_LVB_LEADING_BYTE) &&
+ (data[1].Attributes & WINPTY_COMMON_LVB_TRAILING_BYTE) &&
+ data[0].Char.UnicodeChar == data[1].Char.UnicodeChar;
+}
+
+// Scan to find a single Unicode Scalar Value. Full-width characters occupy
+// two console cells, and this code also tries to handle UTF-16 surrogate
+// pairs.
+//
+// Windows expands at least some wide characters outside the Basic
+// Multilingual Plane into four cells, such as U+20000:
+// 1. 0xD840, attr=0x107
+// 2. 0xD840, attr=0x207
+// 3. 0xDC00, attr=0x107
+// 4. 0xDC00, attr=0x207
+// Even in the Traditional Chinese locale on Windows 10, this text is rendered
+// as two boxes, but if those boxes are copied-and-pasted, the character is
+// copied correctly.
+static inline void scanUnicodeScalarValue(
+ const CHAR_INFO *data, int width,
+ int &outCellCount, unsigned int &outCharValue)
+{
+ ASSERT(width >= 1);
+
+ const int w1 = isFullWidthCharacter(data, width) ? 2 : 1;
+ const wchar_t c1 = data[0].Char.UnicodeChar;
+
+ if ((c1 & 0xF800) == 0xD800) {
+ // The first cell is either a leading or trailing surrogate pair.
+ if ((c1 & 0xFC00) != 0xD800 ||
+ width <= w1 ||
+ ((data[w1].Char.UnicodeChar & 0xFC00) != 0xDC00)) {
+ // Invalid surrogate pair
+ outCellCount = w1;
+ outCharValue = '?';
+ } else {
+ // Valid surrogate pair
+ outCellCount = w1 + (isFullWidthCharacter(&data[w1], width - w1) ? 2 : 1);
+ outCharValue = decodeSurrogatePair(c1, data[w1].Char.UnicodeChar);
+ }
+ } else {
+ outCellCount = w1;
+ outCharValue = c1;
+ }
+}
+
+} // anonymous namespace
+
+void Terminal::reset(SendClearFlag sendClearFirst, int64_t newLine)
+{
+ if (sendClearFirst == SendClear && !m_plainMode) {
+ // 0m ==> reset SGR parameters
+ // 1;1H ==> move cursor to top-left position
+ // 2J ==> clear the entire screen
+ m_output.write(CSI "0m" CSI "1;1H" CSI "2J");
+ }
+ m_remoteLine = newLine;
+ m_remoteColumn = 0;
+ m_lineData.clear();
+ m_cursorHidden = false;
+ m_remoteColor = -1;
+}
+
+void Terminal::sendLine(int64_t line, const CHAR_INFO *lineData, int width,
+ int cursorColumn)
+{
+ ASSERT(width >= 1);
+
+ moveTerminalToLine(line);
+
+ // If possible, see if we can append to what we've already output for this
+ // line.
+ if (m_lineDataValid) {
+ ASSERT(m_lineData.size() == static_cast<size_t>(m_remoteColumn));
+ if (m_remoteColumn > 0) {
+ // In normal mode, if m_lineData.size() equals `width`, then we
+ // will have trouble outputing the "erase rest of line" command,
+ // which must be output before reaching the end of the line. In
+ // plain mode, we don't output that command, so we're OK with a
+ // full line.
+ bool okWidth = false;
+ if (m_plainMode) {
+ okWidth = static_cast<size_t>(width) >= m_lineData.size();
+ } else {
+ okWidth = static_cast<size_t>(width) > m_lineData.size();
+ }
+ if (!okWidth ||
+ memcmp(m_lineData.data(), lineData,
+ sizeof(CHAR_INFO) * m_lineData.size()) != 0) {
+ m_lineDataValid = false;
+ }
+ }
+ }
+ if (!m_lineDataValid) {
+ // We can't reuse, so we must reset this line.
+ hideTerminalCursor();
+ if (m_plainMode) {
+ // We can't backtrack, so repeat this line.
+ m_output.write("\r\n");
+ } else {
+ m_output.write("\r");
+ }
+ m_lineDataValid = true;
+ m_lineData.clear();
+ m_remoteColumn = 0;
+ }
+
+ std::string &termLine = m_termLineWorkingBuffer;
+ termLine.clear();
+ size_t trimmedLineLength = 0;
+ int trimmedCellCount = m_lineData.size();
+ bool alreadyErasedLine = false;
+
+ int cellCount = 1;
+ for (int i = m_lineData.size(); i < width; i += cellCount) {
+ if (m_outputColor) {
+ int color = lineData[i].Attributes & COLOR_ATTRIBUTE_MASK;
+ if (color != m_remoteColor) {
+ outputSetColor(termLine, color);
+ trimmedLineLength = termLine.size();
+ m_remoteColor = color;
+
+ // All the cells just up to this color change will be output.
+ trimmedCellCount = i;
+ }
+ }
+ unsigned int ch;
+ scanUnicodeScalarValue(&lineData[i], width - i, cellCount, ch);
+ if (ch == ' ') {
+ // Tentatively add this space character. We'll only output it if
+ // we see something interesting after it.
+ termLine.push_back(' ');
+ } else {
+ if (i + cellCount == width) {
+ // We'd like to erase the line after outputting all non-blank
+ // characters, but this doesn't work if the last cell in the
+ // line is non-blank. At the point, the cursor is positioned
+ // just past the end of the line, but in many terminals,
+ // issuing a CSI 0K at that point also erases the last cell in
+ // the line. Work around this behavior by issuing the erase
+ // one character early in that case.
+ if (!m_plainMode) {
+ termLine.append(CSI "0K"); // Erase from cursor to EOL
+ }
+ alreadyErasedLine = true;
+ }
+ ch = fixSpecialCharacters(ch);
+ char enc[4];
+ int enclen = encodeUtf8(enc, ch);
+ if (enclen == 0) {
+ enc[0] = '?';
+ enclen = 1;
+ }
+ termLine.append(enc, enclen);
+ trimmedLineLength = termLine.size();
+
+ // All the cells up to and including this cell will be output.
+ trimmedCellCount = i + cellCount;
+ }
+ }
+
+ if (cursorColumn != -1 && trimmedCellCount > cursorColumn) {
+ // The line content would run past the cursor, so hide it before we
+ // output.
+ hideTerminalCursor();
+ }
+
+ m_output.write(termLine.data(), trimmedLineLength);
+ if (!alreadyErasedLine && !m_plainMode) {
+ m_output.write(CSI "0K"); // Erase from cursor to EOL
+ }
+
+ ASSERT(trimmedCellCount <= width);
+ m_lineData.insert(m_lineData.end(),
+ &lineData[m_lineData.size()],
+ &lineData[trimmedCellCount]);
+ m_remoteColumn = trimmedCellCount;
+}
+
+void Terminal::showTerminalCursor(int column, int64_t line)
+{
+ moveTerminalToLine(line);
+ if (!m_plainMode) {
+ if (m_remoteColumn != column) {
+ char buffer[32];
+ winpty_snprintf(buffer, CSI "%dG", column + 1);
+ m_output.write(buffer);
+ m_lineDataValid = (column == 0);
+ m_lineData.clear();
+ m_remoteColumn = column;
+ }
+ if (m_cursorHidden) {
+ m_output.write(CSI "?25h");
+ m_cursorHidden = false;
+ }
+ }
+}
+
+void Terminal::hideTerminalCursor()
+{
+ if (!m_plainMode) {
+ if (m_cursorHidden) {
+ return;
+ }
+ m_output.write(CSI "?25l");
+ m_cursorHidden = true;
+ }
+}
+
+void Terminal::moveTerminalToLine(int64_t line)
+{
+ if (line == m_remoteLine) {
+ return;
+ }
+
+ // Do not use CPL or CNL. Konsole 2.5.4 does not support Cursor Previous
+ // Line (CPL) -- there are "Undecodable sequence" errors. gnome-terminal
+ // 2.32.0 does handle it. Cursor Next Line (CNL) does nothing if the
+ // cursor is on the last line already.
+
+ hideTerminalCursor();
+
+ if (line < m_remoteLine) {
+ if (m_plainMode) {
+ // We can't backtrack, so instead repeat the lines again.
+ m_output.write("\r\n");
+ m_remoteLine = line;
+ } else {
+ // Backtrack and overwrite previous lines.
+ // CUrsor Up (CUU)
+ char buffer[32];
+ winpty_snprintf(buffer, "\r" CSI "%uA",
+ static_cast<unsigned int>(m_remoteLine - line));
+ m_output.write(buffer);
+ m_remoteLine = line;
+ }
+ } else if (line > m_remoteLine) {
+ while (line > m_remoteLine) {
+ m_output.write("\r\n");
+ m_remoteLine++;
+ }
+ }
+
+ m_lineDataValid = true;
+ m_lineData.clear();
+ m_remoteColumn = 0;
+}
+
+void Terminal::enableMouseMode(bool enabled)
+{
+ if (m_mouseModeEnabled == enabled || m_plainMode) {
+ return;
+ }
+ m_mouseModeEnabled = enabled;
+ if (enabled) {
+ // Start by disabling UTF-8 coordinate mode (1005), just in case we
+ // have a terminal that does not support 1006/1015 modes, and 1005
+ // happens to be enabled. The UTF-8 coordinates can't be unambiguously
+ // decoded.
+ //
+ // Enable basic mouse support first (1000), then try to switch to
+ // button-move mode (1002), then try full mouse-move mode (1003).
+ // Terminals that don't support a mode will be stuck at the highest
+ // mode they do support.
+ //
+ // Enable encoding mode 1015 first, then try to switch to 1006. On
+ // some terminals, both modes will be enabled, but 1006 will have
+ // priority. On other terminals, 1006 wins because it's listed last.
+ //
+ // See misc/MouseInputNotes.txt for details.
+ m_output.write(
+ CSI "?1005l"
+ CSI "?1000h" CSI "?1002h" CSI "?1003h" CSI "?1015h" CSI "?1006h");
+ } else {
+ // Resetting both encoding modes (1006 and 1015) is necessary, but
+ // apparently we only need to use reset on one of the 100[023] modes.
+ // Doing both doesn't hurt.
+ m_output.write(
+ CSI "?1006l" CSI "?1015l" CSI "?1003l" CSI "?1002l" CSI "?1000l");
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Terminal.h b/src/libs/3rdparty/winpty/src/agent/Terminal.h
new file mode 100644
index 0000000000..058eb2650e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Terminal.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef TERMINAL_H
+#define TERMINAL_H
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "Coord.h"
+
+class NamedPipe;
+
+class Terminal
+{
+public:
+ explicit Terminal(NamedPipe &output, bool plainMode, bool outputColor)
+ : m_output(output), m_plainMode(plainMode), m_outputColor(outputColor)
+ {
+ }
+
+ enum SendClearFlag { OmitClear, SendClear };
+ void reset(SendClearFlag sendClearFirst, int64_t newLine);
+ void sendLine(int64_t line, const CHAR_INFO *lineData, int width,
+ int cursorColumn);
+ void showTerminalCursor(int column, int64_t line);
+ void hideTerminalCursor();
+
+private:
+ void moveTerminalToLine(int64_t line);
+
+public:
+ void enableMouseMode(bool enabled);
+
+private:
+ NamedPipe &m_output;
+ int64_t m_remoteLine = 0;
+ int m_remoteColumn = 0;
+ bool m_lineDataValid = true;
+ std::vector<CHAR_INFO> m_lineData;
+ bool m_cursorHidden = false;
+ int m_remoteColor = -1;
+ std::string m_termLineWorkingBuffer;
+ bool m_plainMode = false;
+ bool m_outputColor = true;
+ bool m_mouseModeEnabled = false;
+};
+
+#endif // TERMINAL_H
diff --git a/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h b/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h
new file mode 100644
index 0000000000..6b0de3eff9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h
@@ -0,0 +1,157 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNICODE_ENCODING_H
+#define UNICODE_ENCODING_H
+
+#include <stdint.h>
+
+// Encode the Unicode codepoint with UTF-8. The buffer must be at least 4
+// bytes in size.
+static inline int encodeUtf8(char *out, uint32_t code) {
+ if (code < 0x80) {
+ out[0] = code;
+ return 1;
+ } else if (code < 0x800) {
+ out[0] = ((code >> 6) & 0x1F) | 0xC0;
+ out[1] = ((code >> 0) & 0x3F) | 0x80;
+ return 2;
+ } else if (code < 0x10000) {
+ if (code >= 0xD800 && code <= 0xDFFF) {
+ // The code points 0xD800 to 0xDFFF are reserved for UTF-16
+ // surrogate pairs and do not have an encoding in UTF-8.
+ return 0;
+ }
+ out[0] = ((code >> 12) & 0x0F) | 0xE0;
+ out[1] = ((code >> 6) & 0x3F) | 0x80;
+ out[2] = ((code >> 0) & 0x3F) | 0x80;
+ return 3;
+ } else if (code < 0x110000) {
+ out[0] = ((code >> 18) & 0x07) | 0xF0;
+ out[1] = ((code >> 12) & 0x3F) | 0x80;
+ out[2] = ((code >> 6) & 0x3F) | 0x80;
+ out[3] = ((code >> 0) & 0x3F) | 0x80;
+ return 4;
+ } else {
+ // Encoding error
+ return 0;
+ }
+}
+
+// Encode the Unicode codepoint with UTF-16. The buffer must be large enough
+// to hold the output -- either 1 or 2 elements.
+static inline int encodeUtf16(wchar_t *out, uint32_t code) {
+ if (code < 0x10000) {
+ if (code >= 0xD800 && code <= 0xDFFF) {
+ // The code points 0xD800 to 0xDFFF are reserved for UTF-16
+ // surrogate pairs and do not have an encoding in UTF-16.
+ return 0;
+ }
+ out[0] = code;
+ return 1;
+ } else if (code < 0x110000) {
+ code -= 0x10000;
+ out[0] = 0xD800 | (code >> 10);
+ out[1] = 0xDC00 | (code & 0x3FF);
+ return 2;
+ } else {
+ // Encoding error
+ return 0;
+ }
+}
+
+// Return the byte size of a UTF-8 character using the value of the first
+// byte.
+static inline int utf8CharLength(char firstByte) {
+ // This code would probably be faster if it used __builtin_clz.
+ if ((firstByte & 0x80) == 0) {
+ return 1;
+ } else if ((firstByte & 0xE0) == 0xC0) {
+ return 2;
+ } else if ((firstByte & 0xF0) == 0xE0) {
+ return 3;
+ } else if ((firstByte & 0xF8) == 0xF0) {
+ return 4;
+ } else {
+ // Malformed UTF-8.
+ return 0;
+ }
+}
+
+// The pointer must point to 1-4 bytes, as indicated by the first byte.
+// Returns -1 on decoding error.
+static inline uint32_t decodeUtf8(const char *in) {
+ const uint32_t kInvalid = static_cast<uint32_t>(-1);
+ switch (utf8CharLength(in[0])) {
+ case 1: {
+ return in[0];
+ }
+ case 2: {
+ if ((in[1] & 0xC0) != 0x80) {
+ return kInvalid;
+ }
+ uint32_t tmp = 0;
+ tmp = (in[0] & 0x1F) << 6;
+ tmp |= (in[1] & 0x3F);
+ return tmp <= 0x7F ? kInvalid : tmp;
+ }
+ case 3: {
+ if ((in[1] & 0xC0) != 0x80 ||
+ (in[2] & 0xC0) != 0x80) {
+ return kInvalid;
+ }
+ uint32_t tmp = 0;
+ tmp = (in[0] & 0x0F) << 12;
+ tmp |= (in[1] & 0x3F) << 6;
+ tmp |= (in[2] & 0x3F);
+ if (tmp <= 0x07FF || (tmp >= 0xD800 && tmp <= 0xDFFF)) {
+ return kInvalid;
+ } else {
+ return tmp;
+ }
+ }
+ case 4: {
+ if ((in[1] & 0xC0) != 0x80 ||
+ (in[2] & 0xC0) != 0x80 ||
+ (in[3] & 0xC0) != 0x80) {
+ return kInvalid;
+ }
+ uint32_t tmp = 0;
+ tmp = (in[0] & 0x07) << 18;
+ tmp |= (in[1] & 0x3F) << 12;
+ tmp |= (in[2] & 0x3F) << 6;
+ tmp |= (in[3] & 0x3F);
+ if (tmp <= 0xFFFF || tmp > 0x10FFFF) {
+ return kInvalid;
+ } else {
+ return tmp;
+ }
+ }
+ default: {
+ return kInvalid;
+ }
+ }
+}
+
+static inline uint32_t decodeSurrogatePair(wchar_t ch1, wchar_t ch2) {
+ return ((ch1 - 0xD800) << 10) + (ch2 - 0xDC00) + 0x10000;
+}
+
+#endif // UNICODE_ENCODING_H
diff --git a/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc b/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc
new file mode 100644
index 0000000000..cd4abeb191
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Encode every code-point using this module and verify that it matches the
+// encoding generated using Windows WideCharToMultiByte.
+
+#include "UnicodeEncoding.h"
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+static void correctnessByCode()
+{
+ char mbstr1[4];
+ char mbstr2[4];
+ wchar_t wch[2];
+ for (unsigned int code = 0; code < 0x110000; ++code) {
+
+ // Surrogate pair reserved region.
+ const bool isReserved = (code >= 0xD800 && code <= 0xDFFF);
+
+ int mblen1 = encodeUtf8(mbstr1, code);
+ if (isReserved ? mblen1 != 0 : mblen1 <= 0) {
+ printf("Error: 0x%04X: mblen1=%d\n", code, mblen1);
+ continue;
+ }
+
+ int wlen = encodeUtf16(wch, code);
+ if (isReserved ? wlen != 0 : wlen <= 0) {
+ printf("Error: 0x%04X: wlen=%d\n", code, wlen);
+ continue;
+ }
+
+ if (isReserved) {
+ continue;
+ }
+
+ if (mblen1 != utf8CharLength(mbstr1[0])) {
+ printf("Error: 0x%04X: mblen1=%d, utf8CharLength(mbstr1[0])=%d\n",
+ code, mblen1, utf8CharLength(mbstr1[0]));
+ continue;
+ }
+
+ if (code != decodeUtf8(mbstr1)) {
+ printf("Error: 0x%04X: decodeUtf8(mbstr1)=%u\n",
+ code, decodeUtf8(mbstr1));
+ continue;
+ }
+
+ int mblen2 = WideCharToMultiByte(CP_UTF8, 0, wch, wlen, mbstr2, 4, NULL, NULL);
+ if (mblen1 != mblen2) {
+ printf("Error: 0x%04X: mblen1=%d, mblen2=%d\n", code, mblen1, mblen2);
+ continue;
+ }
+
+ if (memcmp(mbstr1, mbstr2, mblen1) != 0) {
+ printf("Error: 0x%04x: encodings are different\n", code);
+ continue;
+ }
+ }
+}
+
+static const char *encodingStr(char (&output)[128], char (&buf)[4])
+{
+ sprintf(output, "Encoding %02X %02X %02X %02X",
+ static_cast<uint8_t>(buf[0]),
+ static_cast<uint8_t>(buf[1]),
+ static_cast<uint8_t>(buf[2]),
+ static_cast<uint8_t>(buf[3]));
+ return output;
+}
+
+// This test can take a couple of minutes to run.
+static void correctnessByUtf8Encoding()
+{
+ for (uint64_t encoding = 0; encoding <= 0xFFFFFFFF; ++encoding) {
+
+ char mb[4];
+ mb[0] = encoding;
+ mb[1] = encoding >> 8;
+ mb[2] = encoding >> 16;
+ mb[3] = encoding >> 24;
+
+ const int mblen = utf8CharLength(mb[0]);
+ if (mblen == 0) {
+ continue;
+ }
+
+ // Test this module.
+ const uint32_t code1 = decodeUtf8(mb);
+ wchar_t ws1[2] = {};
+ const int wslen1 = encodeUtf16(ws1, code1);
+
+ // Test using Windows. We can't decode a codepoint directly; we have
+ // to do UTF8->UTF16, then decode the surrogate pair.
+ wchar_t ws2[2] = {};
+ const int wslen2 = MultiByteToWideChar(
+ CP_UTF8, MB_ERR_INVALID_CHARS, mb, mblen, ws2, 2);
+ const uint32_t code2 =
+ (wslen2 == 1 ? ws2[0] :
+ wslen2 == 2 ? decodeSurrogatePair(ws2[0], ws2[1]) :
+ static_cast<uint32_t>(-1));
+
+ // Verify that the two implementations match.
+ char prefix[128];
+ if (code1 != code2) {
+ printf("%s: code1=0x%04x code2=0x%04x\n",
+ encodingStr(prefix, mb),
+ code1, code2);
+ continue;
+ }
+ if (wslen1 != wslen2) {
+ printf("%s: wslen1=%d wslen2=%d\n",
+ encodingStr(prefix, mb),
+ wslen1, wslen2);
+ continue;
+ }
+ if (memcmp(ws1, ws2, wslen1 * sizeof(wchar_t)) != 0) {
+ printf("%s: ws1 != ws2\n", encodingStr(prefix, mb));
+ continue;
+ }
+ }
+}
+
+wchar_t g_wch_TEST[] = { 0xD840, 0xDC00 };
+char g_ch_TEST[4];
+wchar_t *volatile g_pwch = g_wch_TEST;
+char *volatile g_pch = g_ch_TEST;
+unsigned int volatile g_code = 0xA2000;
+
+static void performance()
+{
+ {
+ clock_t start = clock();
+ for (long long i = 0; i < 250000000LL; ++i) {
+ int mblen = WideCharToMultiByte(CP_UTF8, 0, g_pwch, 2, g_pch, 4, NULL, NULL);
+ assert(mblen == 4);
+ }
+ clock_t stop = clock();
+ printf("%.3fns per char\n", (double)(stop - start) / CLOCKS_PER_SEC * 4.0);
+ }
+
+ {
+ clock_t start = clock();
+ for (long long i = 0; i < 3000000000LL; ++i) {
+ int mblen = encodeUtf8(g_pch, g_code);
+ assert(mblen == 4);
+ }
+ clock_t stop = clock();
+ printf("%.3fns per char\n", (double)(stop - start) / CLOCKS_PER_SEC / 3.0);
+ }
+}
+
+int main()
+{
+ printf("Testing correctnessByCode...\n");
+ fflush(stdout);
+ correctnessByCode();
+
+ printf("Testing correctnessByUtf8Encoding... (may take a couple minutes)\n");
+ fflush(stdout);
+ correctnessByUtf8Encoding();
+
+ printf("Testing performance...\n");
+ fflush(stdout);
+ performance();
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32Console.cc b/src/libs/3rdparty/winpty/src/agent/Win32Console.cc
new file mode 100644
index 0000000000..d53de021f5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32Console.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Win32Console.h"
+
+#include <windows.h>
+#include <wchar.h>
+
+#include <string>
+
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+
+Win32Console::Win32Console() : m_titleWorkBuf(16)
+{
+ // The console window must be non-NULL. It is used for two purposes:
+ // (1) "Freezing" the console to detect the exact number of lines that
+ // have scrolled.
+ // (2) Killing processes attached to the console, by posting a WM_CLOSE
+ // message to the console window.
+ m_hwnd = GetConsoleWindow();
+ ASSERT(m_hwnd != nullptr);
+}
+
+std::wstring Win32Console::title()
+{
+ while (true) {
+ // Calling GetConsoleTitleW is tricky, because its behavior changed
+ // from XP->Vista, then again from Win7->Win8. The Vista+Win7 behavior
+ // is especially broken.
+ //
+ // The MSDN documentation documents nSize as the "size of the buffer
+ // pointed to by the lpConsoleTitle parameter, in characters" and the
+ // successful return value as "the length of the console window's
+ // title, in characters."
+ //
+ // On XP, the function returns the title length, AFTER truncation
+ // (excluding the NUL terminator). If the title is blank, the API
+ // returns 0 and does not NUL-terminate the buffer. To accommodate
+ // XP, the function must:
+ // * Terminate the buffer itself.
+ // * Double the size of the title buffer in a loop.
+ //
+ // On Vista and up, the function returns the non-truncated title
+ // length (excluding the NUL terminator).
+ //
+ // On Vista and Windows 7, there is a bug where the buffer size is
+ // interpreted as a byte count rather than a wchar_t count. To
+ // work around this, we must pass GetConsoleTitleW a buffer that is
+ // twice as large as what is actually needed.
+ //
+ // See misc/*/Test_GetConsoleTitleW.cc for tests demonstrating Windows'
+ // behavior.
+
+ DWORD count = GetConsoleTitleW(m_titleWorkBuf.data(),
+ m_titleWorkBuf.size());
+ const size_t needed = (count + 1) * sizeof(wchar_t);
+ if (m_titleWorkBuf.size() < needed) {
+ m_titleWorkBuf.resize(needed);
+ continue;
+ }
+ m_titleWorkBuf[count] = L'\0';
+ return m_titleWorkBuf.data();
+ }
+}
+
+void Win32Console::setTitle(const std::wstring &title)
+{
+ if (!SetConsoleTitleW(title.c_str())) {
+ trace("SetConsoleTitleW failed");
+ }
+}
+
+void Win32Console::setFrozen(bool frozen) {
+ const int SC_CONSOLE_MARK = 0xFFF2;
+ const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+ if (frozen == m_frozen) {
+ // Do nothing.
+ } else if (frozen) {
+ // Enter selection mode by activating either Mark or SelectAll.
+ const int command = m_freezeUsesMark ? SC_CONSOLE_MARK
+ : SC_CONSOLE_SELECT_ALL;
+ SendMessage(m_hwnd, WM_SYSCOMMAND, command, 0);
+ m_frozen = true;
+ } else {
+ // Send Escape to cancel the selection.
+ SendMessage(m_hwnd, WM_CHAR, 27, 0x00010001);
+ m_frozen = false;
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32Console.h b/src/libs/3rdparty/winpty/src/agent/Win32Console.h
new file mode 100644
index 0000000000..ed83877e99
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32Console.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_WIN32_CONSOLE_H
+#define AGENT_WIN32_CONSOLE_H
+
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+class Win32Console
+{
+public:
+ class FreezeGuard {
+ public:
+ FreezeGuard(Win32Console &console, bool frozen) :
+ m_console(console), m_previous(console.frozen()) {
+ m_console.setFrozen(frozen);
+ }
+ ~FreezeGuard() {
+ m_console.setFrozen(m_previous);
+ }
+ FreezeGuard(const FreezeGuard &other) = delete;
+ FreezeGuard &operator=(const FreezeGuard &other) = delete;
+ private:
+ Win32Console &m_console;
+ bool m_previous;
+ };
+
+ Win32Console();
+
+ HWND hwnd() { return m_hwnd; }
+ std::wstring title();
+ void setTitle(const std::wstring &title);
+ void setFreezeUsesMark(bool useMark) { m_freezeUsesMark = useMark; }
+ void setNewW10(bool isNewW10) { m_isNewW10 = isNewW10; }
+ bool isNewW10() { return m_isNewW10; }
+ void setFrozen(bool frozen=true);
+ bool frozen() { return m_frozen; }
+
+private:
+ HWND m_hwnd = nullptr;
+ bool m_frozen = false;
+ bool m_freezeUsesMark = false;
+ bool m_isNewW10 = false;
+ std::vector<wchar_t> m_titleWorkBuf;
+};
+
+#endif // AGENT_WIN32_CONSOLE_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc
new file mode 100644
index 0000000000..ed93f4081f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Win32ConsoleBuffer.h"
+
+#include <windows.h>
+
+#include "../shared/DebugClient.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/WinptyAssert.h"
+
+std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::openStdout() {
+ return std::unique_ptr<Win32ConsoleBuffer>(
+ new Win32ConsoleBuffer(GetStdHandle(STD_OUTPUT_HANDLE), false));
+}
+
+std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::openConout() {
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+ return std::unique_ptr<Win32ConsoleBuffer>(
+ new Win32ConsoleBuffer(conout, true));
+}
+
+std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::createErrorBuffer() {
+ SECURITY_ATTRIBUTES sa = {};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ const HANDLE conout =
+ CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ CONSOLE_TEXTMODE_BUFFER,
+ nullptr);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+ return std::unique_ptr<Win32ConsoleBuffer>(
+ new Win32ConsoleBuffer(conout, true));
+}
+
+HANDLE Win32ConsoleBuffer::conout() {
+ return m_conout;
+}
+
+void Win32ConsoleBuffer::clearLines(
+ int row,
+ int count,
+ const ConsoleScreenBufferInfo &info) {
+ // TODO: error handling
+ const int width = info.bufferSize().X;
+ DWORD actual = 0;
+ if (!FillConsoleOutputCharacterW(
+ m_conout, L' ', width * count, Coord(0, row),
+ &actual) || static_cast<int>(actual) != width * count) {
+ trace("FillConsoleOutputCharacterW failed");
+ }
+ if (!FillConsoleOutputAttribute(
+ m_conout, kDefaultAttributes, width * count, Coord(0, row),
+ &actual) || static_cast<int>(actual) != width * count) {
+ trace("FillConsoleOutputAttribute failed");
+ }
+}
+
+void Win32ConsoleBuffer::clearAllLines(const ConsoleScreenBufferInfo &info) {
+ clearLines(0, info.bufferSize().Y, info);
+}
+
+ConsoleScreenBufferInfo Win32ConsoleBuffer::bufferInfo() {
+ // TODO: error handling
+ ConsoleScreenBufferInfo info;
+ if (!GetConsoleScreenBufferInfo(m_conout, &info)) {
+ trace("GetConsoleScreenBufferInfo failed");
+ }
+ return info;
+}
+
+Coord Win32ConsoleBuffer::bufferSize() {
+ return bufferInfo().bufferSize();
+}
+
+SmallRect Win32ConsoleBuffer::windowRect() {
+ return bufferInfo().windowRect();
+}
+
+bool Win32ConsoleBuffer::resizeBufferRange(const Coord &initialSize,
+ Coord &finalSize) {
+ if (SetConsoleScreenBufferSize(m_conout, initialSize)) {
+ finalSize = initialSize;
+ return true;
+ }
+ // The font might be too small to accommodate a very narrow console window.
+ // In that case, rather than simply give up, it's better to try wider
+ // buffer sizes until the call succeeds.
+ Coord size = initialSize;
+ while (size.X < 20) {
+ size.X++;
+ if (SetConsoleScreenBufferSize(m_conout, size)) {
+ finalSize = size;
+ trace("SetConsoleScreenBufferSize: initial size (%d,%d) failed, "
+ "but wider size (%d,%d) succeeded",
+ initialSize.X, initialSize.Y,
+ finalSize.X, finalSize.Y);
+ return true;
+ }
+ }
+ trace("SetConsoleScreenBufferSize failed: "
+ "tried (%d,%d) through (%d,%d)",
+ initialSize.X, initialSize.Y,
+ size.X, size.Y);
+ return false;
+}
+
+void Win32ConsoleBuffer::resizeBuffer(const Coord &size) {
+ // TODO: error handling
+ if (!SetConsoleScreenBufferSize(m_conout, size)) {
+ trace("SetConsoleScreenBufferSize failed: size=(%d,%d)",
+ size.X, size.Y);
+ }
+}
+
+void Win32ConsoleBuffer::moveWindow(const SmallRect &rect) {
+ // TODO: error handling
+ if (!SetConsoleWindowInfo(m_conout, TRUE, &rect)) {
+ trace("SetConsoleWindowInfo failed");
+ }
+}
+
+Coord Win32ConsoleBuffer::cursorPosition() {
+ return bufferInfo().dwCursorPosition;
+}
+
+void Win32ConsoleBuffer::setCursorPosition(const Coord &coord) {
+ // TODO: error handling
+ if (!SetConsoleCursorPosition(m_conout, coord)) {
+ trace("SetConsoleCursorPosition failed");
+ }
+}
+
+void Win32ConsoleBuffer::read(const SmallRect &rect, CHAR_INFO *data) {
+ // TODO: error handling
+ SmallRect tmp(rect);
+ if (!ReadConsoleOutputW(m_conout, data, rect.size(), Coord(), &tmp) &&
+ isTracingEnabled()) {
+ StringBuilder sb(256);
+ auto outStruct = [&](const SMALL_RECT &sr) {
+ sb << "{L=" << sr.Left << ",T=" << sr.Top
+ << ",R=" << sr.Right << ",B=" << sr.Bottom << '}';
+ };
+ sb << "Win32ConsoleBuffer::read: ReadConsoleOutput failed: readRegion=";
+ outStruct(rect);
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ if (GetConsoleScreenBufferInfo(m_conout, &info)) {
+ sb << ", dwSize=(" << info.dwSize.X << ',' << info.dwSize.Y
+ << "), srWindow=";
+ outStruct(info.srWindow);
+ } else {
+ sb << ", GetConsoleScreenBufferInfo also failed";
+ }
+ trace("%s", sb.c_str());
+ }
+}
+
+void Win32ConsoleBuffer::write(const SmallRect &rect, const CHAR_INFO *data) {
+ // TODO: error handling
+ SmallRect tmp(rect);
+ if (!WriteConsoleOutputW(m_conout, data, rect.size(), Coord(), &tmp)) {
+ trace("WriteConsoleOutput failed");
+ }
+}
+
+void Win32ConsoleBuffer::setTextAttribute(WORD attributes) {
+ if (!SetConsoleTextAttribute(m_conout, attributes)) {
+ trace("SetConsoleTextAttribute failed");
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h
new file mode 100644
index 0000000000..a68d8d304f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_WIN32_CONSOLE_BUFFER_H
+#define AGENT_WIN32_CONSOLE_BUFFER_H
+
+#include <windows.h>
+
+#include <string.h>
+
+#include <memory>
+
+#include "Coord.h"
+#include "SmallRect.h"
+
+class ConsoleScreenBufferInfo : public CONSOLE_SCREEN_BUFFER_INFO {
+public:
+ ConsoleScreenBufferInfo()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+
+ Coord bufferSize() const { return dwSize; }
+ SmallRect windowRect() const { return srWindow; }
+ Coord cursorPosition() const { return dwCursorPosition; }
+};
+
+class Win32ConsoleBuffer {
+private:
+ Win32ConsoleBuffer(HANDLE conout, bool owned) :
+ m_conout(conout), m_owned(owned)
+ {
+ }
+
+public:
+ static const int kDefaultAttributes = 7;
+
+ ~Win32ConsoleBuffer() {
+ if (m_owned) {
+ CloseHandle(m_conout);
+ }
+ }
+
+ static std::unique_ptr<Win32ConsoleBuffer> openStdout();
+ static std::unique_ptr<Win32ConsoleBuffer> openConout();
+ static std::unique_ptr<Win32ConsoleBuffer> createErrorBuffer();
+
+ Win32ConsoleBuffer(const Win32ConsoleBuffer &other) = delete;
+ Win32ConsoleBuffer &operator=(const Win32ConsoleBuffer &other) = delete;
+
+ HANDLE conout();
+ void clearLines(int row, int count, const ConsoleScreenBufferInfo &info);
+ void clearAllLines(const ConsoleScreenBufferInfo &info);
+
+ // Buffer and window sizes.
+ ConsoleScreenBufferInfo bufferInfo();
+ Coord bufferSize();
+ SmallRect windowRect();
+ void resizeBuffer(const Coord &size);
+ bool resizeBufferRange(const Coord &initialSize, Coord &finalSize);
+ bool resizeBufferRange(const Coord &initialSize) {
+ Coord dummy;
+ return resizeBufferRange(initialSize, dummy);
+ }
+ void moveWindow(const SmallRect &rect);
+
+ // Cursor.
+ Coord cursorPosition();
+ void setCursorPosition(const Coord &point);
+
+ // Screen content.
+ void read(const SmallRect &rect, CHAR_INFO *data);
+ void write(const SmallRect &rect, const CHAR_INFO *data);
+
+ void setTextAttribute(WORD attributes);
+
+private:
+ HANDLE m_conout = nullptr;
+ bool m_owned = false;
+};
+
+#endif // AGENT_WIN32_CONSOLE_BUFFER_H
diff --git a/src/libs/3rdparty/winpty/src/agent/main.cc b/src/libs/3rdparty/winpty/src/agent/main.cc
new file mode 100644
index 0000000000..427cb3a3aa
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/main.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <wchar.h>
+#include <shellapi.h>
+
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/WinptyVersion.h"
+
+#include "Agent.h"
+#include "AgentCreateDesktop.h"
+#include "DebugShowInput.h"
+
+const char USAGE[] =
+"Usage: %ls controlPipeName flags mouseMode cols rows\n"
+"Usage: %ls controlPipeName --create-desktop\n"
+"\n"
+"Ordinarily, this program is launched by winpty.dll and is not directly\n"
+"useful to winpty users. However, it also has options intended for\n"
+"debugging winpty.\n"
+"\n"
+"Usage: %ls [options]\n"
+"\n"
+"Options:\n"
+" --show-input [--with-mouse] [--escape-input]\n"
+" Dump INPUT_RECORDs from the console input buffer\n"
+" --with-mouse: Include MOUSE_INPUT_RECORDs in the dump\n"
+" output\n"
+" --escape-input: Direct the new Windows 10 console to use\n"
+" escape sequences for input\n"
+" --version Print the winpty version\n";
+
+static uint64_t winpty_atoi64(const char *str) {
+ return strtoll(str, NULL, 10);
+}
+
+int main() {
+ dumpWindowsVersion();
+ dumpVersionToTrace();
+
+ // Technically, we should free the CommandLineToArgvW return value using
+ // a single call to LocalFree, but the call will never actually happen in
+ // the normal case.
+ int argc = 0;
+ wchar_t *cmdline = GetCommandLineW();
+ ASSERT(cmdline != nullptr && "GetCommandLineW returned NULL");
+ wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
+ ASSERT(argv != nullptr && "CommandLineToArgvW returned NULL");
+
+ if (argc == 2 && !wcscmp(argv[1], L"--version")) {
+ dumpVersionToStdout();
+ return 0;
+ }
+
+ if (argc >= 2 && !wcscmp(argv[1], L"--show-input")) {
+ bool withMouse = false;
+ bool escapeInput = false;
+ for (int i = 2; i < argc; ++i) {
+ if (!wcscmp(argv[i], L"--with-mouse")) {
+ withMouse = true;
+ } else if (!wcscmp(argv[i], L"--escape-input")) {
+ escapeInput = true;
+ } else {
+ fprintf(stderr, "Unrecognized --show-input option: %ls\n",
+ argv[i]);
+ return 1;
+ }
+ }
+ debugShowInput(withMouse, escapeInput);
+ return 0;
+ }
+
+ if (argc == 3 && !wcscmp(argv[2], L"--create-desktop")) {
+ handleCreateDesktop(argv[1]);
+ return 0;
+ }
+
+ if (argc != 6) {
+ fprintf(stderr, USAGE, argv[0], argv[0], argv[0]);
+ return 1;
+ }
+
+ Agent agent(argv[1],
+ winpty_atoi64(utf8FromWide(argv[2]).c_str()),
+ atoi(utf8FromWide(argv[3]).c_str()),
+ atoi(utf8FromWide(argv[4]).c_str()),
+ atoi(utf8FromWide(argv[5]).c_str()));
+ agent.run();
+
+ // The Agent destructor shouldn't return, but if it does, exit
+ // unsuccessfully.
+ return 1;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/subdir.mk b/src/libs/3rdparty/winpty/src/agent/subdir.mk
new file mode 100644
index 0000000000..1c7d37e3e5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/subdir.mk
@@ -0,0 +1,61 @@
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/winpty-agent.exe
+
+$(eval $(call def_mingw_target,agent,-DWINPTY_AGENT_ASSERT))
+
+AGENT_OBJECTS = \
+ build/agent/agent/Agent.o \
+ build/agent/agent/AgentCreateDesktop.o \
+ build/agent/agent/ConsoleFont.o \
+ build/agent/agent/ConsoleInput.o \
+ build/agent/agent/ConsoleInputReencoding.o \
+ build/agent/agent/ConsoleLine.o \
+ build/agent/agent/DebugShowInput.o \
+ build/agent/agent/DefaultInputMap.o \
+ build/agent/agent/EventLoop.o \
+ build/agent/agent/InputMap.o \
+ build/agent/agent/LargeConsoleRead.o \
+ build/agent/agent/NamedPipe.o \
+ build/agent/agent/Scraper.o \
+ build/agent/agent/Terminal.o \
+ build/agent/agent/Win32Console.o \
+ build/agent/agent/Win32ConsoleBuffer.o \
+ build/agent/agent/main.o \
+ build/agent/shared/BackgroundDesktop.o \
+ build/agent/shared/Buffer.o \
+ build/agent/shared/DebugClient.o \
+ build/agent/shared/GenRandom.o \
+ build/agent/shared/OwnedHandle.o \
+ build/agent/shared/StringUtil.o \
+ build/agent/shared/WindowsSecurity.o \
+ build/agent/shared/WindowsVersion.o \
+ build/agent/shared/WinptyAssert.o \
+ build/agent/shared/WinptyException.o \
+ build/agent/shared/WinptyVersion.o
+
+build/agent/shared/WinptyVersion.o : build/gen/GenVersion.h
+
+build/winpty-agent.exe : $(AGENT_OBJECTS)
+ $(info Linking $@)
+ @$(MINGW_CXX) $(MINGW_LDFLAGS) -o $@ $^
+
+-include $(AGENT_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/configurations.gypi b/src/libs/3rdparty/winpty/src/configurations.gypi
new file mode 100644
index 0000000000..e990a60338
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/configurations.gypi
@@ -0,0 +1,60 @@
+# By default gyp/msbuild build for 32-bit Windows. This gyp include file
+# defines configurations for both 32-bit and 64-bit Windows. To use it, run:
+#
+# C:\...\winpty\src>gyp -I configurations.gypi
+#
+# This command generates Visual Studio project files with a Release
+# configuration and two Platforms--Win32 and x64. Both can be built:
+#
+# C:\...\winpty\src>msbuild winpty.sln /p:Platform=Win32
+# C:\...\winpty\src>msbuild winpty.sln /p:Platform=x64
+#
+# The output is placed in:
+#
+# C:\...\winpty\src\Release\Win32
+# C:\...\winpty\src\Release\x64
+#
+# Windows XP note: By default, the project files will use the default "toolset"
+# for the given MSVC version. For MSVC 2013 and MSVC 2015, the default toolset
+# generates binaries that do not run on Windows XP. To target Windows XP,
+# select the XP-specific toolset by passing
+# -D WINPTY_MSBUILD_TOOLSET={v120_xp,v140_xp} to gyp (v120_xp == MSVC 2013,
+# v140_xp == MSVC 2015). Unfortunately, it isn't possible to have a single
+# project file with configurations for both XP and post-XP. This seems to be a
+# limitation of the MSVC project file format.
+#
+# This file is not included by default, because I suspect it would interfere
+# with node-gyp, which has a different system for building 32-vs-64-bit
+# binaries. It uses a common.gypi, and the project files it generates can only
+# build a single architecture, the output paths are not differentiated by
+# architecture.
+
+{
+ 'variables': {
+ 'WINPTY_MSBUILD_TOOLSET%': '',
+ },
+ 'target_defaults': {
+ 'default_configuration': 'Release_Win32',
+ 'configurations': {
+ 'Release_Win32': {
+ 'msvs_configuration_platform': 'Win32',
+ },
+ 'Release_x64': {
+ 'msvs_configuration_platform': 'x64',
+ },
+ },
+ 'msvs_configuration_attributes': {
+ 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)\\$(Platform)',
+ 'IntermediateDirectory': '$(ConfigurationName)\\$(Platform)\\obj\\$(ProjectName)',
+ },
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '1', # /SUBSYSTEM:CONSOLE
+ },
+ 'VCCLCompilerTool': {
+ 'RuntimeLibrary': '0', # MultiThreaded (/MT)
+ },
+ },
+ 'msbuild_toolset' : '<(WINPTY_MSBUILD_TOOLSET)',
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc b/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc
new file mode 100644
index 0000000000..353d31c1c6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <windows.h>
+
+#include "../shared/WindowsSecurity.h"
+#include "../shared/WinptyException.h"
+
+const wchar_t *kPipeName = L"\\\\.\\pipe\\DebugServer";
+
+// A message may not be larger than this size.
+const int MSG_SIZE = 4096;
+
+static void usage(const char *program, int code) {
+ printf("Usage: %s [--everyone]\n"
+ "\n"
+ "Creates the named pipe %ls and reads messages. Prints each\n"
+ "message to stdout. By default, only the current user can send messages.\n"
+ "Pass --everyone to let anyone send a message.\n"
+ "\n"
+ "Use the WINPTY_DEBUG environment variable to enable winpty trace output.\n"
+ "(e.g. WINPTY_DEBUG=trace for the default trace output.) Set WINPTYDBG=1\n"
+ "to enable trace with older winpty versions.\n",
+ program, kPipeName);
+ exit(code);
+}
+
+int main(int argc, char *argv[]) {
+ bool everyone = false;
+ for (int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+ if (arg == "--everyone") {
+ everyone = true;
+ } else if (arg == "-h" || arg == "--help") {
+ usage(argv[0], 0);
+ } else {
+ usage(argv[0], 1);
+ }
+ }
+
+ SecurityDescriptor sd;
+ PSECURITY_ATTRIBUTES psa = nullptr;
+ SECURITY_ATTRIBUTES sa = {};
+ if (everyone) {
+ try {
+ sd = createPipeSecurityDescriptorOwnerFullControlEveryoneWrite();
+ } catch (const WinptyException &e) {
+ fprintf(stderr,
+ "error creating security descriptor: %ls\n", e.what());
+ exit(1);
+ }
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd.get();
+ psa = &sa;
+ }
+
+ HANDLE serverPipe = CreateNamedPipeW(
+ kPipeName,
+ /*dwOpenMode=*/PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ /*dwPipeMode=*/PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE |
+ rejectRemoteClientsPipeFlag(),
+ /*nMaxInstances=*/1,
+ /*nOutBufferSize=*/MSG_SIZE,
+ /*nInBufferSize=*/MSG_SIZE,
+ /*nDefaultTimeOut=*/10 * 1000,
+ psa);
+
+ if (serverPipe == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "error: could not create %ls pipe: error %u\n",
+ kPipeName, static_cast<unsigned>(GetLastError()));
+ exit(1);
+ }
+
+ char msgBuffer[MSG_SIZE + 1];
+
+ while (true) {
+ if (!ConnectNamedPipe(serverPipe, nullptr)) {
+ fprintf(stderr, "error: ConnectNamedPipe failed\n");
+ fflush(stderr);
+ exit(1);
+ }
+ DWORD bytesRead = 0;
+ if (!ReadFile(serverPipe, msgBuffer, MSG_SIZE, &bytesRead, nullptr)) {
+ fprintf(stderr, "error: ReadFile on pipe failed\n");
+ fflush(stderr);
+ DisconnectNamedPipe(serverPipe);
+ continue;
+ }
+ msgBuffer[bytesRead] = '\n';
+ fwrite(msgBuffer, 1, bytesRead + 1, stdout);
+ fflush(stdout);
+
+ DWORD bytesWritten = 0;
+ WriteFile(serverPipe, "OK", 2, &bytesWritten, nullptr);
+ DisconnectNamedPipe(serverPipe);
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/debugserver/subdir.mk b/src/libs/3rdparty/winpty/src/debugserver/subdir.mk
new file mode 100644
index 0000000000..beed1bd597
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/debugserver/subdir.mk
@@ -0,0 +1,41 @@
+# Copyright (c) 2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/winpty-debugserver.exe
+
+$(eval $(call def_mingw_target,debugserver,))
+
+DEBUGSERVER_OBJECTS = \
+ build/debugserver/debugserver/DebugServer.o \
+ build/debugserver/shared/DebugClient.o \
+ build/debugserver/shared/OwnedHandle.o \
+ build/debugserver/shared/StringUtil.o \
+ build/debugserver/shared/WindowsSecurity.o \
+ build/debugserver/shared/WindowsVersion.o \
+ build/debugserver/shared/WinptyAssert.o \
+ build/debugserver/shared/WinptyException.o
+
+build/debugserver/shared/WindowsVersion.o : build/gen/GenVersion.h
+
+build/winpty-debugserver.exe : $(DEBUGSERVER_OBJECTS)
+ $(info Linking $@)
+ @$(MINGW_CXX) $(MINGW_LDFLAGS) -o $@ $^
+
+-include $(DEBUGSERVER_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/include/winpty.h b/src/libs/3rdparty/winpty/src/include/winpty.h
new file mode 100644
index 0000000000..fdfe4bca21
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/include/winpty.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2011-2016 Ryan Prichard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef WINPTY_H
+#define WINPTY_H
+
+#include <windows.h>
+
+#include "winpty_constants.h"
+
+/* On 32-bit Windows, winpty functions have the default __cdecl (not __stdcall)
+ * calling convention. (64-bit Windows has only a single calling convention.)
+ * When compiled with __declspec(dllexport), with either MinGW or MSVC, the
+ * winpty functions are unadorned--no underscore prefix or '@nn' suffix--so
+ * GetProcAddress can be used easily. */
+#ifdef COMPILING_WINPTY_DLL
+#define WINPTY_API __declspec(dllexport)
+#else
+#define WINPTY_API __declspec(dllimport)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The winpty API uses wide characters, instead of UTF-8, to avoid conversion
+ * complications related to surrogates. Windows generally tolerates unpaired
+ * surrogates in text, which makes conversion to and from UTF-8 ambiguous and
+ * complicated. (There are different UTF-8 variants that deal with UTF-16
+ * surrogates differently.) */
+
+
+
+/*****************************************************************************
+ * Error handling. */
+
+/* All the APIs have an optional winpty_error_t output parameter. If a
+ * non-NULL argument is specified, then either the API writes NULL to the
+ * value (on success) or writes a newly allocated winpty_error_t object. The
+ * object must be freed using winpty_error_free. */
+
+/* An error object. */
+typedef struct winpty_error_s winpty_error_t;
+typedef winpty_error_t *winpty_error_ptr_t;
+
+/* An error code -- one of WINPTY_ERROR_xxx. */
+typedef DWORD winpty_result_t;
+
+/* Gets the error code from the error object. */
+WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err);
+
+/* Returns a textual representation of the error. The string is freed when
+ * the error is freed. */
+WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err);
+
+/* Free the error object. Every error returned from the winpty API must be
+ * freed. */
+WINPTY_API void winpty_error_free(winpty_error_ptr_t err);
+
+
+
+/*****************************************************************************
+ * Configuration of a new agent. */
+
+/* The winpty_config_t object is not thread-safe. */
+typedef struct winpty_config_s winpty_config_t;
+
+/* Allocate a winpty_config_t value. Returns NULL on error. There are no
+ * required settings -- the object may immediately be used. agentFlags is a
+ * set of zero or more WINPTY_FLAG_xxx values. An unrecognized flag results
+ * in an assertion failure. */
+WINPTY_API winpty_config_t *
+winpty_config_new(UINT64 agentFlags, winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Free the cfg object after passing it to winpty_open. */
+WINPTY_API void winpty_config_free(winpty_config_t *cfg);
+
+WINPTY_API void
+winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows);
+
+/* Set the mouse mode to one of the WINPTY_MOUSE_MODE_xxx constants. */
+WINPTY_API void
+winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode);
+
+/* Amount of time to wait for the agent to startup and to wait for any given
+ * agent RPC request. Must be greater than 0. Can be INFINITE. */
+WINPTY_API void
+winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs);
+
+
+
+/*****************************************************************************
+ * Start the agent. */
+
+/* The winpty_t object is thread-safe. */
+typedef struct winpty_s winpty_t;
+
+/* Starts the agent. Returns NULL on error. This process will connect to the
+ * agent over a control pipe, and the agent will open data pipes (e.g. CONIN
+ * and CONOUT). */
+WINPTY_API winpty_t *
+winpty_open(const winpty_config_t *cfg,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* A handle to the agent process. This value is valid for the lifetime of the
+ * winpty_t object. Do not close it. */
+WINPTY_API HANDLE winpty_agent_process(winpty_t *wp);
+
+
+
+/*****************************************************************************
+ * I/O pipes. */
+
+/* Returns the names of named pipes used for terminal I/O. Each input or
+ * output direction uses a different half-duplex pipe. The agent creates
+ * these pipes, and the client can connect to them using ordinary I/O methods.
+ * The strings are freed when the winpty_t object is freed.
+ *
+ * winpty_conerr_name returns NULL unless WINPTY_FLAG_CONERR is specified.
+ *
+ * N.B.: CreateFile does not block when connecting to a local server pipe. If
+ * the server pipe does not exist or is already connected, then it fails
+ * instantly. */
+WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp);
+WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp);
+WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp);
+
+
+
+/*****************************************************************************
+ * winpty agent RPC call: process creation. */
+
+/* The winpty_spawn_config_t object is not thread-safe. */
+typedef struct winpty_spawn_config_s winpty_spawn_config_t;
+
+/* winpty_spawn_config strings do not need to live as long as the config
+ * object. They are copied. Returns NULL on error. spawnFlags is a set of
+ * zero or more WINPTY_SPAWN_FLAG_xxx values. An unrecognized flag results in
+ * an assertion failure.
+ *
+ * env is a a pointer to an environment block like that passed to
+ * CreateProcess--a contiguous array of NUL-terminated "VAR=VAL" strings
+ * followed by a final NUL terminator.
+ *
+ * N.B.: If you want to gather all of the child's output, you may want the
+ * WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN flag.
+ */
+WINPTY_API winpty_spawn_config_t *
+winpty_spawn_config_new(UINT64 spawnFlags,
+ LPCWSTR appname /*OPTIONAL*/,
+ LPCWSTR cmdline /*OPTIONAL*/,
+ LPCWSTR cwd /*OPTIONAL*/,
+ LPCWSTR env /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Free the cfg object after passing it to winpty_spawn. */
+WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg);
+
+/*
+ * Spawns the new process.
+ *
+ * The function initializes all output parameters to zero or NULL.
+ *
+ * On success, the function returns TRUE. For each of process_handle and
+ * thread_handle that is non-NULL, the HANDLE returned from CreateProcess is
+ * duplicated from the agent and returned to the winpty client. The client is
+ * responsible for closing these HANDLES.
+ *
+ * On failure, the function returns FALSE, and if err is non-NULL, then *err
+ * is set to an error object.
+ *
+ * If the agent's CreateProcess call failed, then *create_process_error is set
+ * to GetLastError(), and the WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED error
+ * is returned.
+ *
+ * winpty_spawn can only be called once per winpty_t object. If it is called
+ * before the output data pipe(s) is/are connected, then collected output is
+ * buffered until the pipes are connected, rather than being discarded.
+ *
+ * N.B.: GetProcessId works even if the process has exited. The PID is not
+ * recycled until the NT process object is freed.
+ * (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
+ */
+WINPTY_API BOOL
+winpty_spawn(winpty_t *wp,
+ const winpty_spawn_config_t *cfg,
+ HANDLE *process_handle /*OPTIONAL*/,
+ HANDLE *thread_handle /*OPTIONAL*/,
+ DWORD *create_process_error /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+
+
+/*****************************************************************************
+ * winpty agent RPC calls: everything else */
+
+/* Change the size of the Windows console window. */
+WINPTY_API BOOL
+winpty_set_size(winpty_t *wp, int cols, int rows,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Gets a list of processes attached to the console. */
+WINPTY_API int
+winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Frees the winpty_t object and the OS resources contained in it. This
+ * call breaks the connection with the agent, which should then close its
+ * console, terminating the processes attached to it.
+ *
+ * This function must not be called if any other threads are using the
+ * winpty_t object. Undefined behavior results. */
+WINPTY_API void winpty_free(winpty_t *wp);
+
+
+
+/****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINPTY_H */
diff --git a/src/libs/3rdparty/winpty/src/include/winpty_constants.h b/src/libs/3rdparty/winpty/src/include/winpty_constants.h
new file mode 100644
index 0000000000..11e34cf171
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/include/winpty_constants.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016 Ryan Prichard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef WINPTY_CONSTANTS_H
+#define WINPTY_CONSTANTS_H
+
+/*
+ * You may want to include winpty.h instead, which includes this header.
+ *
+ * This file is split out from winpty.h so that the agent can access the
+ * winpty flags without also declaring the libwinpty APIs.
+ */
+
+/*****************************************************************************
+ * Error codes. */
+
+#define WINPTY_ERROR_SUCCESS 0
+#define WINPTY_ERROR_OUT_OF_MEMORY 1
+#define WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED 2
+#define WINPTY_ERROR_LOST_CONNECTION 3
+#define WINPTY_ERROR_AGENT_EXE_MISSING 4
+#define WINPTY_ERROR_UNSPECIFIED 5
+#define WINPTY_ERROR_AGENT_DIED 6
+#define WINPTY_ERROR_AGENT_TIMEOUT 7
+#define WINPTY_ERROR_AGENT_CREATION_FAILED 8
+
+
+
+/*****************************************************************************
+ * Configuration of a new agent. */
+
+/* Create a new screen buffer (connected to the "conerr" terminal pipe) and
+ * pass it to child processes as the STDERR handle. This flag also prevents
+ * the agent from reopening CONOUT$ when it polls -- regardless of whether the
+ * active screen buffer changes, winpty continues to monitor the original
+ * primary screen buffer. */
+#define WINPTY_FLAG_CONERR 0x1ull
+
+/* Don't output escape sequences. */
+#define WINPTY_FLAG_PLAIN_OUTPUT 0x2ull
+
+/* Do output color escape sequences. These escapes are output by default, but
+ * are suppressed with WINPTY_FLAG_PLAIN_OUTPUT. Use this flag to reenable
+ * them. */
+#define WINPTY_FLAG_COLOR_ESCAPES 0x4ull
+
+/* On XP and Vista, winpty needs to put the hidden console on a desktop in a
+ * service window station so that its polling does not interfere with other
+ * (visible) console windows. To create this desktop, it must change the
+ * process' window station (i.e. SetProcessWindowStation) for the duration of
+ * the winpty_open call. In theory, this change could interfere with the
+ * winpty client (e.g. other threads, spawning children), so winpty by default
+ * spawns a special agent process to create the hidden desktop. Spawning
+ * processes on Windows is slow, though, so if
+ * WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION is set, winpty changes this
+ * process' window station instead.
+ * See https://github.com/rprichard/winpty/issues/58. */
+#define WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION 0x8ull
+
+#define WINPTY_FLAG_MASK (0ull \
+ | WINPTY_FLAG_CONERR \
+ | WINPTY_FLAG_PLAIN_OUTPUT \
+ | WINPTY_FLAG_COLOR_ESCAPES \
+ | WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION \
+)
+
+/* QuickEdit mode is initially disabled, and the agent does not send mouse
+ * mode sequences to the terminal. If it receives mouse input, though, it
+ * still writes MOUSE_EVENT_RECORD values into CONIN. */
+#define WINPTY_MOUSE_MODE_NONE 0
+
+/* QuickEdit mode is initially enabled. As CONIN enters or leaves mouse
+ * input mode (i.e. where ENABLE_MOUSE_INPUT is on and ENABLE_QUICK_EDIT_MODE
+ * is off), the agent enables or disables mouse input on the terminal.
+ *
+ * This is the default mode. */
+#define WINPTY_MOUSE_MODE_AUTO 1
+
+/* QuickEdit mode is initially disabled, and the agent enables the terminal's
+ * mouse input mode. It does not disable terminal mouse mode (until exit). */
+#define WINPTY_MOUSE_MODE_FORCE 2
+
+
+
+/*****************************************************************************
+ * winpty agent RPC call: process creation. */
+
+/* If the spawn is marked "auto-shutdown", then the agent shuts down console
+ * output once the process exits. The agent stops polling for new console
+ * output, and once all pending data has been written to the output pipe, the
+ * agent closes the pipe. (At that point, the pipe may still have data in it,
+ * which the client may read. Once all the data has been read, further reads
+ * return EOF.) */
+#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ull
+
+/* After the agent shuts down output, and after all output has been written
+ * into the pipe(s), exit the agent by closing the console. If there any
+ * surviving processes still attached to the console, they are killed.
+ *
+ * Note: With this flag, an RPC call (e.g. winpty_set_size) issued after the
+ * agent exits will fail with an I/O or dead-agent error. */
+#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
+
+/* All the spawn flags. */
+#define WINPTY_SPAWN_FLAG_MASK (0ull \
+ | WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN \
+ | WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN \
+)
+
+
+
+#endif /* WINPTY_CONSTANTS_H */
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc
new file mode 100644
index 0000000000..82d00b2da2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "AgentLocation.h"
+
+#include <windows.h>
+
+#include <string>
+
+#include "../shared/WinptyAssert.h"
+
+#include "LibWinptyException.h"
+
+#define AGENT_EXE L"winpty-agent.exe"
+
+static HMODULE getCurrentModule() {
+ HMODULE module;
+ if (!GetModuleHandleExW(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(getCurrentModule),
+ &module)) {
+ ASSERT(false && "GetModuleHandleEx failed");
+ }
+ return module;
+}
+
+static std::wstring getModuleFileName(HMODULE module) {
+ const int bufsize = 4096;
+ wchar_t path[bufsize];
+ int size = GetModuleFileNameW(module, path, bufsize);
+ ASSERT(size != 0 && size != bufsize);
+ return std::wstring(path);
+}
+
+static std::wstring dirname(const std::wstring &path) {
+ std::wstring::size_type pos = path.find_last_of(L"\\/");
+ if (pos == std::wstring::npos) {
+ return L"";
+ } else {
+ return path.substr(0, pos);
+ }
+}
+
+static bool pathExists(const std::wstring &path) {
+ return GetFileAttributesW(path.c_str()) != 0xFFFFFFFF;
+}
+
+std::wstring findAgentProgram() {
+ std::wstring progDir = dirname(getModuleFileName(getCurrentModule()));
+ std::wstring ret = progDir + (L"\\" AGENT_EXE);
+ if (!pathExists(ret)) {
+ throw LibWinptyException(
+ WINPTY_ERROR_AGENT_EXE_MISSING,
+ (L"agent executable does not exist: '" + ret + L"'").c_str());
+ }
+ return ret;
+}
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h
new file mode 100644
index 0000000000..a96b854cd2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBWINPTY_AGENT_LOCATION_H
+#define LIBWINPTY_AGENT_LOCATION_H
+
+#include <string>
+
+std::wstring findAgentProgram();
+
+#endif // LIBWINPTY_AGENT_LOCATION_H
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h b/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h
new file mode 100644
index 0000000000..2274798d23
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIB_WINPTY_EXCEPTION_H
+#define LIB_WINPTY_EXCEPTION_H
+
+#include "../include/winpty.h"
+
+#include "../shared/WinptyException.h"
+
+#include <memory>
+#include <string>
+
+class LibWinptyException : public WinptyException {
+public:
+ LibWinptyException(winpty_result_t code, const wchar_t *what) :
+ m_code(code), m_what(std::make_shared<std::wstring>(what)) {}
+
+ winpty_result_t code() const WINPTY_NOEXCEPT {
+ return m_code;
+ }
+
+ const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return m_what->c_str();
+ }
+
+ std::shared_ptr<std::wstring> whatSharedStr() const WINPTY_NOEXCEPT {
+ return m_what;
+ }
+
+private:
+ winpty_result_t m_code;
+ // Using a shared_ptr ensures that copying the object raises no exception.
+ std::shared_ptr<std::wstring> m_what;
+};
+
+#endif // LIB_WINPTY_EXCEPTION_H
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h b/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h
new file mode 100644
index 0000000000..93e992d5c5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBWINPTY_WINPTY_INTERNAL_H
+#define LIBWINPTY_WINPTY_INTERNAL_H
+
+#include <memory>
+#include <string>
+
+#include "../include/winpty.h"
+
+#include "../shared/Mutex.h"
+#include "../shared/OwnedHandle.h"
+
+// The structures in this header are not intended to be accessed directly by
+// client programs.
+
+struct winpty_error_s {
+ winpty_result_t code;
+ const wchar_t *msgStatic;
+ // Use a pointer to a std::shared_ptr so that the struct remains simple
+ // enough to statically initialize, for the benefit of static error
+ // objects like kOutOfMemory.
+ std::shared_ptr<std::wstring> *msgDynamic;
+};
+
+struct winpty_config_s {
+ uint64_t flags = 0;
+ int cols = 80;
+ int rows = 25;
+ int mouseMode = WINPTY_MOUSE_MODE_AUTO;
+ DWORD timeoutMs = 30000;
+};
+
+struct winpty_s {
+ Mutex mutex;
+ OwnedHandle agentProcess;
+ OwnedHandle controlPipe;
+ DWORD agentTimeoutMs = 0;
+ OwnedHandle ioEvent;
+ std::wstring spawnDesktopName;
+ std::wstring coninPipeName;
+ std::wstring conoutPipeName;
+ std::wstring conerrPipeName;
+};
+
+struct winpty_spawn_config_s {
+ uint64_t winptyFlags = 0;
+ std::wstring appname;
+ std::wstring cmdline;
+ std::wstring cwd;
+ std::wstring env;
+};
+
+#endif // LIBWINPTY_WINPTY_INTERNAL_H
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk b/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk
new file mode 100644
index 0000000000..ba32bad6e6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk
@@ -0,0 +1,46 @@
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/winpty.dll
+
+$(eval $(call def_mingw_target,libwinpty,-DCOMPILING_WINPTY_DLL))
+
+LIBWINPTY_OBJECTS = \
+ build/libwinpty/libwinpty/AgentLocation.o \
+ build/libwinpty/libwinpty/winpty.o \
+ build/libwinpty/shared/BackgroundDesktop.o \
+ build/libwinpty/shared/Buffer.o \
+ build/libwinpty/shared/DebugClient.o \
+ build/libwinpty/shared/GenRandom.o \
+ build/libwinpty/shared/OwnedHandle.o \
+ build/libwinpty/shared/StringUtil.o \
+ build/libwinpty/shared/WindowsSecurity.o \
+ build/libwinpty/shared/WindowsVersion.o \
+ build/libwinpty/shared/WinptyAssert.o \
+ build/libwinpty/shared/WinptyException.o \
+ build/libwinpty/shared/WinptyVersion.o
+
+build/libwinpty/shared/WinptyVersion.o : build/gen/GenVersion.h
+
+build/winpty.dll : $(LIBWINPTY_OBJECTS)
+ $(info Linking $@)
+ @$(MINGW_CXX) $(MINGW_LDFLAGS) -shared -o $@ $^ -Wl,--out-implib,build/winpty.lib
+
+-include $(LIBWINPTY_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc b/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc
new file mode 100644
index 0000000000..3d977498ef
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc
@@ -0,0 +1,970 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <windows.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "../include/winpty.h"
+
+#include "../shared/AgentMsg.h"
+#include "../shared/BackgroundDesktop.h"
+#include "../shared/Buffer.h"
+#include "../shared/DebugClient.h"
+#include "../shared/GenRandom.h"
+#include "../shared/OwnedHandle.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsSecurity.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/WinptyException.h"
+#include "../shared/WinptyVersion.h"
+
+#include "AgentLocation.h"
+#include "LibWinptyException.h"
+#include "WinptyInternal.h"
+
+
+
+/*****************************************************************************
+ * Error handling -- translate C++ exceptions to an optional error object
+ * output and log the result. */
+
+static const winpty_error_s kOutOfMemory = {
+ WINPTY_ERROR_OUT_OF_MEMORY,
+ L"Out of memory",
+ nullptr
+};
+
+static const winpty_error_s kBadRpcPacket = {
+ WINPTY_ERROR_UNSPECIFIED,
+ L"Bad RPC packet",
+ nullptr
+};
+
+static const winpty_error_s kUncaughtException = {
+ WINPTY_ERROR_UNSPECIFIED,
+ L"Uncaught C++ exception",
+ nullptr
+};
+
+/* Gets the error code from the error object. */
+WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err) {
+ return err != nullptr ? err->code : WINPTY_ERROR_SUCCESS;
+}
+
+/* Returns a textual representation of the error. The string is freed when
+ * the error is freed. */
+WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err) {
+ if (err != nullptr) {
+ if (err->msgStatic != nullptr) {
+ return err->msgStatic;
+ } else {
+ ASSERT(err->msgDynamic != nullptr);
+ std::wstring *msgPtr = err->msgDynamic->get();
+ ASSERT(msgPtr != nullptr);
+ return msgPtr->c_str();
+ }
+ } else {
+ return L"Success";
+ }
+}
+
+/* Free the error object. Every error returned from the winpty API must be
+ * freed. */
+WINPTY_API void winpty_error_free(winpty_error_ptr_t err) {
+ if (err != nullptr && err->msgDynamic != nullptr) {
+ delete err->msgDynamic;
+ delete err;
+ }
+}
+
+static void translateException(winpty_error_ptr_t *&err) {
+ winpty_error_ptr_t ret = nullptr;
+ try {
+ try {
+ throw;
+ } catch (const ReadBuffer::DecodeError&) {
+ ret = const_cast<winpty_error_ptr_t>(&kBadRpcPacket);
+ } catch (const LibWinptyException &e) {
+ std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
+ obj->code = e.code();
+ obj->msgStatic = nullptr;
+ obj->msgDynamic =
+ new std::shared_ptr<std::wstring>(e.whatSharedStr());
+ ret = obj.release();
+ } catch (const WinptyException &e) {
+ std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
+ std::shared_ptr<std::wstring> msg(new std::wstring(e.what()));
+ obj->code = WINPTY_ERROR_UNSPECIFIED;
+ obj->msgStatic = nullptr;
+ obj->msgDynamic = new std::shared_ptr<std::wstring>(msg);
+ ret = obj.release();
+ }
+ } catch (const std::bad_alloc&) {
+ ret = const_cast<winpty_error_ptr_t>(&kOutOfMemory);
+ } catch (...) {
+ ret = const_cast<winpty_error_ptr_t>(&kUncaughtException);
+ }
+ trace("libwinpty error: code=%u msg='%s'",
+ static_cast<unsigned>(ret->code),
+ utf8FromWide(winpty_error_msg(ret)).c_str());
+ if (err != nullptr) {
+ *err = ret;
+ } else {
+ winpty_error_free(ret);
+ }
+}
+
+#define API_TRY \
+ if (err != nullptr) { *err = nullptr; } \
+ try
+
+#define API_CATCH(ret) \
+ catch (...) { translateException(err); return (ret); }
+
+
+
+/*****************************************************************************
+ * Configuration of a new agent. */
+
+WINPTY_API winpty_config_t *
+winpty_config_new(UINT64 flags, winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT((flags & WINPTY_FLAG_MASK) == flags);
+ std::unique_ptr<winpty_config_t> ret(new winpty_config_t);
+ ret->flags = flags;
+ return ret.release();
+ } API_CATCH(nullptr)
+}
+
+WINPTY_API void winpty_config_free(winpty_config_t *cfg) {
+ delete cfg;
+}
+
+WINPTY_API void
+winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows) {
+ ASSERT(cfg != nullptr && cols > 0 && rows > 0);
+ cfg->cols = cols;
+ cfg->rows = rows;
+}
+
+WINPTY_API void
+winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode) {
+ ASSERT(cfg != nullptr &&
+ mouseMode >= WINPTY_MOUSE_MODE_NONE &&
+ mouseMode <= WINPTY_MOUSE_MODE_FORCE);
+ cfg->mouseMode = mouseMode;
+}
+
+WINPTY_API void
+winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs) {
+ ASSERT(cfg != nullptr && timeoutMs > 0);
+ cfg->timeoutMs = timeoutMs;
+}
+
+
+
+/*****************************************************************************
+ * Agent I/O. */
+
+namespace {
+
+// Once an I/O operation fails with ERROR_IO_PENDING, the caller *must* wait
+// for it to complete, even after calling CancelIo on it! See
+// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613. This
+// class enforces that requirement.
+class PendingIo {
+ HANDLE m_file;
+ OVERLAPPED &m_over;
+ bool m_finished;
+public:
+ // The file handle and OVERLAPPED object must live as long as the PendingIo
+ // object.
+ PendingIo(HANDLE file, OVERLAPPED &over) :
+ m_file(file), m_over(over), m_finished(false) {}
+ ~PendingIo() {
+ if (!m_finished) {
+ // We're not usually that interested in CancelIo's return value.
+ // In any case, we must not throw an exception in this dtor.
+ CancelIo(m_file);
+ waitForCompletion();
+ }
+ }
+ std::tuple<BOOL, DWORD> waitForCompletion(DWORD &actual) WINPTY_NOEXCEPT {
+ m_finished = true;
+ const BOOL success =
+ GetOverlappedResult(m_file, &m_over, &actual, TRUE);
+ return std::make_tuple(success, GetLastError());
+ }
+ std::tuple<BOOL, DWORD> waitForCompletion() WINPTY_NOEXCEPT {
+ DWORD actual = 0;
+ return waitForCompletion(actual);
+ }
+};
+
+} // anonymous namespace
+
+static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
+ DWORD &lastError, DWORD &actual) {
+ if (!success && lastError == ERROR_IO_PENDING) {
+ PendingIo io(wp.controlPipe.get(), over);
+ const HANDLE waitHandles[2] = { wp.ioEvent.get(),
+ wp.agentProcess.get() };
+ DWORD waitRet = WaitForMultipleObjects(
+ 2, waitHandles, FALSE, wp.agentTimeoutMs);
+ if (waitRet != WAIT_OBJECT_0) {
+ // The I/O is still pending. Cancel it, close the I/O event, and
+ // throw an exception.
+ if (waitRet == WAIT_OBJECT_0 + 1) {
+ throw LibWinptyException(WINPTY_ERROR_AGENT_DIED, L"agent died");
+ } else if (waitRet == WAIT_TIMEOUT) {
+ throw LibWinptyException(WINPTY_ERROR_AGENT_TIMEOUT,
+ L"agent timed out");
+ } else if (waitRet == WAIT_FAILED) {
+ throwWindowsError(L"WaitForMultipleObjects failed");
+ } else {
+ ASSERT(false &&
+ "unexpected WaitForMultipleObjects return value");
+ }
+ }
+ std::tie(success, lastError) = io.waitForCompletion(actual);
+ }
+}
+
+static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
+ DWORD &lastError) {
+ DWORD actual = 0;
+ handlePendingIo(wp, over, success, lastError, actual);
+}
+
+static void handleReadWriteErrors(winpty_t &wp, BOOL success, DWORD lastError,
+ const wchar_t *genericErrMsg) {
+ if (!success) {
+ // If the pipe connection is broken after it's been connected, then
+ // later I/O operations fail with ERROR_BROKEN_PIPE (reads) or
+ // ERROR_NO_DATA (writes). With Wine, they may also fail with
+ // ERROR_PIPE_NOT_CONNECTED. See this gist[1].
+ //
+ // [1] https://gist.github.com/rprichard/8dd8ca134b39534b7da2733994aa07ba
+ if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_NO_DATA ||
+ lastError == ERROR_PIPE_NOT_CONNECTED) {
+ throw LibWinptyException(WINPTY_ERROR_LOST_CONNECTION,
+ L"lost connection to agent");
+ } else {
+ throwWindowsError(genericErrMsg, lastError);
+ }
+ }
+}
+
+// Calls ConnectNamedPipe to wait until the agent connects to the control pipe.
+static void
+connectControlPipe(winpty_t &wp) {
+ OVERLAPPED over = {};
+ over.hEvent = wp.ioEvent.get();
+ BOOL success = ConnectNamedPipe(wp.controlPipe.get(), &over);
+ DWORD lastError = GetLastError();
+ handlePendingIo(wp, over, success, lastError);
+ if (!success && lastError == ERROR_PIPE_CONNECTED) {
+ success = TRUE;
+ }
+ if (!success) {
+ throwWindowsError(L"ConnectNamedPipe failed", lastError);
+ }
+}
+
+static void writeData(winpty_t &wp, const void *data, size_t amount) {
+ // Perform a single pipe write.
+ DWORD actual = 0;
+ OVERLAPPED over = {};
+ over.hEvent = wp.ioEvent.get();
+ BOOL success = WriteFile(wp.controlPipe.get(), data, amount,
+ &actual, &over);
+ DWORD lastError = GetLastError();
+ if (!success) {
+ handlePendingIo(wp, over, success, lastError, actual);
+ handleReadWriteErrors(wp, success, lastError, L"WriteFile failed");
+ ASSERT(success);
+ }
+ // TODO: Can a partial write actually happen somehow?
+ ASSERT(actual == amount && "WriteFile wrote fewer bytes than requested");
+}
+
+static inline WriteBuffer newPacket() {
+ WriteBuffer packet;
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
+ return packet;
+}
+
+static void writePacket(winpty_t &wp, WriteBuffer &packet) {
+ const auto &buf = packet.buf();
+ packet.replaceRawValue<uint64_t>(0, buf.size());
+ writeData(wp, buf.data(), buf.size());
+}
+
+static size_t readData(winpty_t &wp, void *data, size_t amount) {
+ DWORD actual = 0;
+ OVERLAPPED over = {};
+ over.hEvent = wp.ioEvent.get();
+ BOOL success = ReadFile(wp.controlPipe.get(), data, amount,
+ &actual, &over);
+ DWORD lastError = GetLastError();
+ if (!success) {
+ handlePendingIo(wp, over, success, lastError, actual);
+ handleReadWriteErrors(wp, success, lastError, L"ReadFile failed");
+ }
+ return actual;
+}
+
+static void readAll(winpty_t &wp, void *data, size_t amount) {
+ while (amount > 0) {
+ const size_t chunk = readData(wp, data, amount);
+ ASSERT(chunk <= amount && "readData result is larger than amount");
+ data = reinterpret_cast<char*>(data) + chunk;
+ amount -= chunk;
+ }
+}
+
+static uint64_t readUInt64(winpty_t &wp) {
+ uint64_t ret = 0;
+ readAll(wp, &ret, sizeof(ret));
+ return ret;
+}
+
+// Returns a reply packet's payload.
+static ReadBuffer readPacket(winpty_t &wp) {
+ const uint64_t packetSize = readUInt64(wp);
+ if (packetSize < sizeof(packetSize) || packetSize > SIZE_MAX) {
+ throwWinptyException(L"Agent RPC error: invalid packet size");
+ }
+ const size_t payloadSize = packetSize - sizeof(packetSize);
+ std::vector<char> bytes(payloadSize);
+ readAll(wp, bytes.data(), bytes.size());
+ return ReadBuffer(std::move(bytes));
+}
+
+static OwnedHandle createControlPipe(const std::wstring &name) {
+ const auto sd = createPipeSecurityDescriptorOwnerFullControl();
+ if (!sd) {
+ throwWinptyException(
+ L"could not create the control pipe's SECURITY_DESCRIPTOR");
+ }
+ SECURITY_ATTRIBUTES sa = {};
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd.get();
+ HANDLE ret = CreateNamedPipeW(name.c_str(),
+ /*dwOpenMode=*/
+ PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_FIRST_PIPE_INSTANCE |
+ FILE_FLAG_OVERLAPPED,
+ /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
+ /*nMaxInstances=*/1,
+ /*nOutBufferSize=*/8192,
+ /*nInBufferSize=*/256,
+ /*nDefaultTimeOut=*/30000,
+ &sa);
+ if (ret == INVALID_HANDLE_VALUE) {
+ throwWindowsError(L"CreateNamedPipeW failed");
+ }
+ return OwnedHandle(ret);
+}
+
+
+
+/*****************************************************************************
+ * Start the agent. */
+
+static OwnedHandle createEvent() {
+ // manual reset, initially unset
+ HANDLE h = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ if (h == nullptr) {
+ throwWindowsError(L"CreateEventW failed");
+ }
+ return OwnedHandle(h);
+}
+
+// For debugging purposes, provide a way to keep the console on the main window
+// station, visible.
+static bool shouldShowConsoleWindow() {
+ char buf[32];
+ return GetEnvironmentVariableA("WINPTY_SHOW_CONSOLE", buf, sizeof(buf)) > 0;
+}
+
+static bool shouldCreateBackgroundDesktop(bool &createUsingAgent) {
+ // Prior to Windows 7, winpty's repeated selection-deselection loop
+ // prevented the user from interacting with their *visible* console
+ // windows, unless we placed the console onto a background desktop.
+ // The SetProcessWindowStation call interferes with the clipboard and
+ // isn't thread-safe, though[1]. The call should perhaps occur in a
+ // special agent subprocess. Spawning a process in a background desktop
+ // also breaks ConEmu, but marking the process SW_HIDE seems to correct
+ // that[2].
+ //
+ // Windows 7 moved a lot of console handling out of csrss.exe and into
+ // a per-console conhost.exe process, which may explain why it isn't
+ // affected.
+ //
+ // This is a somewhat risky change, so there are low-level flags to
+ // assist in debugging if there are issues.
+ //
+ // [1] https://github.com/rprichard/winpty/issues/58
+ // [2] https://github.com/rprichard/winpty/issues/70
+ bool ret = !shouldShowConsoleWindow() && !isAtLeastWindows7();
+ const bool force = hasDebugFlag("force_desktop");
+ const bool force_spawn = hasDebugFlag("force_desktop_spawn");
+ const bool force_curproc = hasDebugFlag("force_desktop_curproc");
+ const bool suppress = hasDebugFlag("no_desktop");
+ if (force + force_spawn + force_curproc + suppress > 1) {
+ trace("error: Only one of force_desktop, force_desktop_spawn, "
+ "force_desktop_curproc, and no_desktop may be set");
+ } else if (force) {
+ ret = true;
+ } else if (force_spawn) {
+ ret = true;
+ createUsingAgent = true;
+ } else if (force_curproc) {
+ ret = true;
+ createUsingAgent = false;
+ } else if (suppress) {
+ ret = false;
+ }
+ return ret;
+}
+
+static bool shouldSpecifyHideFlag() {
+ const bool force = hasDebugFlag("force_sw_hide");
+ const bool suppress = hasDebugFlag("no_sw_hide");
+ bool ret = !shouldShowConsoleWindow();
+ if (force && suppress) {
+ trace("error: Both the force_sw_hide and no_sw_hide flags are set");
+ } else if (force) {
+ ret = true;
+ } else if (suppress) {
+ ret = false;
+ }
+ return ret;
+}
+
+static OwnedHandle startAgentProcess(
+ const std::wstring &desktop,
+ const std::wstring &controlPipeName,
+ const std::wstring &params,
+ DWORD creationFlags,
+ DWORD &agentPid) {
+ const std::wstring exePath = findAgentProgram();
+ const std::wstring cmdline =
+ (WStringBuilder(256)
+ << L"\"" << exePath << L"\" "
+ << controlPipeName << L' '
+ << params).str_moved();
+
+ auto cmdlineV = vectorWithNulFromString(cmdline);
+ auto desktopV = vectorWithNulFromString(desktop);
+
+ // Start the agent.
+ STARTUPINFOW sui = {};
+ sui.cb = sizeof(sui);
+ sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
+
+ if (shouldSpecifyHideFlag()) {
+ sui.dwFlags |= STARTF_USESHOWWINDOW;
+ sui.wShowWindow = SW_HIDE;
+ }
+ PROCESS_INFORMATION pi = {};
+ const BOOL success =
+ CreateProcessW(exePath.c_str(),
+ cmdlineV.data(),
+ nullptr, nullptr,
+ /*bInheritHandles=*/FALSE,
+ /*dwCreationFlags=*/creationFlags,
+ nullptr, nullptr,
+ &sui, &pi);
+ if (!success) {
+ const DWORD lastError = GetLastError();
+ const auto errStr =
+ (WStringBuilder(256)
+ << L"winpty-agent CreateProcess failed: cmdline='" << cmdline
+ << L"' err=0x" << whexOfInt(lastError)).str_moved();
+ throw LibWinptyException(
+ WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str());
+ }
+ CloseHandle(pi.hThread);
+ TRACE("Created agent successfully, pid=%u, cmdline=%s",
+ static_cast<unsigned int>(pi.dwProcessId),
+ utf8FromWide(cmdline).c_str());
+ agentPid = pi.dwProcessId;
+ return OwnedHandle(pi.hProcess);
+}
+
+static void verifyPipeClientPid(HANDLE serverPipe, DWORD agentPid) {
+ const auto client = getNamedPipeClientProcessId(serverPipe);
+ const auto success = std::get<0>(client);
+ const auto lastError = std::get<2>(client);
+ if (success == GetNamedPipeClientProcessId_Result::Success) {
+ const auto clientPid = std::get<1>(client);
+ if (clientPid != agentPid) {
+ WStringBuilder errMsg;
+ errMsg << L"Security check failed: pipe client pid (" << clientPid
+ << L") does not match agent pid (" << agentPid << L")";
+ throwWinptyException(errMsg.c_str());
+ }
+ } else if (success == GetNamedPipeClientProcessId_Result::UnsupportedOs) {
+ trace("Pipe client PID security check skipped: "
+ "GetNamedPipeClientProcessId unsupported on this OS version");
+ } else {
+ throwWindowsError(L"GetNamedPipeClientProcessId failed", lastError);
+ }
+}
+
+static std::unique_ptr<winpty_t>
+createAgentSession(const winpty_config_t *cfg,
+ const std::wstring &desktop,
+ const std::wstring &params,
+ DWORD creationFlags) {
+ std::unique_ptr<winpty_t> wp(new winpty_t);
+ wp->agentTimeoutMs = cfg->timeoutMs;
+ wp->ioEvent = createEvent();
+
+ // Create control server pipe.
+ const auto pipeName =
+ L"\\\\.\\pipe\\winpty-control-" + GenRandom().uniqueName();
+ wp->controlPipe = createControlPipe(pipeName);
+
+ DWORD agentPid = 0;
+ wp->agentProcess = startAgentProcess(
+ desktop, pipeName, params, creationFlags, agentPid);
+ connectControlPipe(*wp.get());
+ verifyPipeClientPid(wp->controlPipe.get(), agentPid);
+
+ return std::move(wp);
+}
+
+namespace {
+
+class AgentDesktop {
+public:
+ virtual std::wstring name() = 0;
+ virtual ~AgentDesktop() {}
+};
+
+class AgentDesktopDirect : public AgentDesktop {
+public:
+ AgentDesktopDirect(BackgroundDesktop &&desktop) :
+ m_desktop(std::move(desktop))
+ {
+ }
+ std::wstring name() override { return m_desktop.desktopName(); }
+private:
+ BackgroundDesktop m_desktop;
+};
+
+class AgentDesktopIndirect : public AgentDesktop {
+public:
+ AgentDesktopIndirect(std::unique_ptr<winpty_t> &&wp,
+ std::wstring &&desktopName) :
+ m_wp(std::move(wp)),
+ m_desktopName(std::move(desktopName))
+ {
+ }
+ std::wstring name() override { return m_desktopName; }
+private:
+ std::unique_ptr<winpty_t> m_wp;
+ std::wstring m_desktopName;
+};
+
+} // anonymous namespace
+
+std::unique_ptr<AgentDesktop>
+setupBackgroundDesktop(const winpty_config_t *cfg) {
+ bool useDesktopAgent =
+ !(cfg->flags & WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION);
+ const bool useDesktop = shouldCreateBackgroundDesktop(useDesktopAgent);
+
+ if (!useDesktop) {
+ return std::unique_ptr<AgentDesktop>();
+ }
+
+ if (useDesktopAgent) {
+ auto wp = createAgentSession(
+ cfg, std::wstring(), L"--create-desktop", DETACHED_PROCESS);
+
+ // Read the desktop name.
+ auto packet = readPacket(*wp.get());
+ auto desktopName = packet.getWString();
+ packet.assertEof();
+
+ if (desktopName.empty()) {
+ return std::unique_ptr<AgentDesktop>();
+ } else {
+ return std::unique_ptr<AgentDesktop>(
+ new AgentDesktopIndirect(std::move(wp),
+ std::move(desktopName)));
+ }
+ } else {
+ try {
+ BackgroundDesktop desktop;
+ return std::unique_ptr<AgentDesktop>(new AgentDesktopDirect(
+ std::move(desktop)));
+ } catch (const WinptyException &e) {
+ trace("Error: failed to create background desktop, "
+ "using original desktop instead: %s",
+ utf8FromWide(e.what()).c_str());
+ return std::unique_ptr<AgentDesktop>();
+ }
+ }
+}
+
+WINPTY_API winpty_t *
+winpty_open(const winpty_config_t *cfg,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(cfg != nullptr);
+ dumpWindowsVersion();
+ dumpVersionToTrace();
+
+ // Setup a background desktop for the agent.
+ auto desktop = setupBackgroundDesktop(cfg);
+ const auto desktopName = desktop ? desktop->name() : std::wstring();
+
+ // Start the primary agent session.
+ const auto params =
+ (WStringBuilder(128)
+ << cfg->flags << L' '
+ << cfg->mouseMode << L' '
+ << cfg->cols << L' '
+ << cfg->rows).str_moved();
+ auto wp = createAgentSession(cfg, desktopName, params,
+ CREATE_NEW_CONSOLE);
+
+ // Close handles to the background desktop and restore the original
+ // window station. This must wait until we know the agent is running
+ // -- if we close these handles too soon, then the desktop and
+ // windowstation will be destroyed before the agent can connect with
+ // them.
+ //
+ // If we used a separate agent process to create the desktop, we
+ // disconnect from that process here, allowing it to exit.
+ desktop.reset();
+
+ // If we ran the agent process on a background desktop, then when we
+ // spawn a child process from the agent, it will need to be explicitly
+ // placed back onto the original desktop.
+ if (!desktopName.empty()) {
+ wp->spawnDesktopName = getCurrentDesktopName();
+ }
+
+ // Get the CONIN/CONOUT pipe names.
+ auto packet = readPacket(*wp.get());
+ wp->coninPipeName = packet.getWString();
+ wp->conoutPipeName = packet.getWString();
+ if (cfg->flags & WINPTY_FLAG_CONERR) {
+ wp->conerrPipeName = packet.getWString();
+ }
+ packet.assertEof();
+
+ return wp.release();
+ } API_CATCH(nullptr)
+}
+
+WINPTY_API HANDLE winpty_agent_process(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ return wp->agentProcess.get();
+}
+
+
+
+/*****************************************************************************
+ * I/O pipes. */
+
+static const wchar_t *cstrFromWStringOrNull(const std::wstring &str) {
+ try {
+ return str.c_str();
+ } catch (const std::bad_alloc&) {
+ return nullptr;
+ }
+}
+
+WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ return cstrFromWStringOrNull(wp->coninPipeName);
+}
+
+WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ return cstrFromWStringOrNull(wp->conoutPipeName);
+}
+
+WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ if (wp->conerrPipeName.empty()) {
+ return nullptr;
+ } else {
+ return cstrFromWStringOrNull(wp->conerrPipeName);
+ }
+}
+
+
+
+/*****************************************************************************
+ * winpty agent RPC calls. */
+
+namespace {
+
+// Close the control pipe if something goes wrong with the pipe communication,
+// which could leave the control pipe in an inconsistent state.
+class RpcOperation {
+public:
+ RpcOperation(winpty_t &wp) : m_wp(wp) {
+ if (m_wp.controlPipe.get() == nullptr) {
+ throwWinptyException(L"Agent shutdown due to RPC failure");
+ }
+ }
+ ~RpcOperation() {
+ if (!m_success) {
+ trace("~RpcOperation: Closing control pipe");
+ m_wp.controlPipe.dispose(true);
+ }
+ }
+ void success() { m_success = true; }
+private:
+ winpty_t &m_wp;
+ bool m_success = false;
+};
+
+} // anonymous namespace
+
+
+
+/*****************************************************************************
+ * winpty agent RPC call: process creation. */
+
+// Return a std::wstring containing every character of the environment block.
+// Typically, the block is non-empty, so the std::wstring returned ends with
+// two NUL terminators. (These two terminators are counted in size(), so
+// calling c_str() produces a triply-terminated string.)
+static std::wstring wstringFromEnvBlock(const wchar_t *env) {
+ std::wstring envStr;
+ if (env != NULL) {
+ const wchar_t *p = env;
+ while (*p != L'\0') {
+ p += wcslen(p) + 1;
+ }
+ p++;
+ envStr.assign(env, p);
+
+ // Assuming the environment was non-empty, envStr now ends with two NUL
+ // terminators.
+ //
+ // If the environment were empty, though, then envStr would only be
+ // singly terminated, but the MSDN documentation thinks an env block is
+ // always doubly-terminated, so add an extra NUL just in case it
+ // matters.
+ const auto envStrSz = envStr.size();
+ if (envStrSz == 1) {
+ ASSERT(envStr[0] == L'\0');
+ envStr.push_back(L'\0');
+ } else {
+ ASSERT(envStrSz >= 3);
+ ASSERT(envStr[envStrSz - 3] != L'\0');
+ ASSERT(envStr[envStrSz - 2] == L'\0');
+ ASSERT(envStr[envStrSz - 1] == L'\0');
+ }
+ }
+ return envStr;
+}
+
+WINPTY_API winpty_spawn_config_t *
+winpty_spawn_config_new(UINT64 winptyFlags,
+ LPCWSTR appname /*OPTIONAL*/,
+ LPCWSTR cmdline /*OPTIONAL*/,
+ LPCWSTR cwd /*OPTIONAL*/,
+ LPCWSTR env /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT((winptyFlags & WINPTY_SPAWN_FLAG_MASK) == winptyFlags);
+ std::unique_ptr<winpty_spawn_config_t> cfg(new winpty_spawn_config_t);
+ cfg->winptyFlags = winptyFlags;
+ if (appname != nullptr) { cfg->appname = appname; }
+ if (cmdline != nullptr) { cfg->cmdline = cmdline; }
+ if (cwd != nullptr) { cfg->cwd = cwd; }
+ if (env != nullptr) { cfg->env = wstringFromEnvBlock(env); }
+ return cfg.release();
+ } API_CATCH(nullptr)
+}
+
+WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg) {
+ delete cfg;
+}
+
+// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
+// back to 64-bits. See the MSDN article, "Interprocess Communication Between
+// 32-bit and 64-bit Applications".
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
+static inline HANDLE handleFromInt64(int64_t i) {
+ return reinterpret_cast<HANDLE>(static_cast<intptr_t>(i));
+}
+
+// Given a process and a handle in that process, duplicate the handle into the
+// current process and close it in the originating process.
+static inline OwnedHandle stealHandle(HANDLE process, HANDLE handle) {
+ HANDLE result = nullptr;
+ if (!DuplicateHandle(process, handle,
+ GetCurrentProcess(),
+ &result, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ throwWindowsError(L"DuplicateHandle of process handle");
+ }
+ return OwnedHandle(result);
+}
+
+WINPTY_API BOOL
+winpty_spawn(winpty_t *wp,
+ const winpty_spawn_config_t *cfg,
+ HANDLE *process_handle /*OPTIONAL*/,
+ HANDLE *thread_handle /*OPTIONAL*/,
+ DWORD *create_process_error /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(wp != nullptr && cfg != nullptr);
+
+ if (process_handle != nullptr) { *process_handle = nullptr; }
+ if (thread_handle != nullptr) { *thread_handle = nullptr; }
+ if (create_process_error != nullptr) { *create_process_error = 0; }
+
+ LockGuard<Mutex> lock(wp->mutex);
+ RpcOperation rpc(*wp);
+
+ // Send spawn request.
+ auto packet = newPacket();
+ packet.putInt32(AgentMsg::StartProcess);
+ packet.putInt64(cfg->winptyFlags);
+ packet.putInt32(process_handle != nullptr);
+ packet.putInt32(thread_handle != nullptr);
+ packet.putWString(cfg->appname);
+ packet.putWString(cfg->cmdline);
+ packet.putWString(cfg->cwd);
+ packet.putWString(cfg->env);
+ packet.putWString(wp->spawnDesktopName);
+ writePacket(*wp, packet);
+
+ // Receive reply.
+ auto reply = readPacket(*wp);
+ const auto result = static_cast<StartProcessResult>(reply.getInt32());
+ if (result == StartProcessResult::CreateProcessFailed) {
+ const DWORD lastError = reply.getInt32();
+ reply.assertEof();
+ if (create_process_error != nullptr) {
+ *create_process_error = lastError;
+ }
+ rpc.success();
+ throw LibWinptyException(WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED,
+ L"CreateProcess failed");
+ } else if (result == StartProcessResult::ProcessCreated) {
+ const HANDLE remoteProcess = handleFromInt64(reply.getInt64());
+ const HANDLE remoteThread = handleFromInt64(reply.getInt64());
+ reply.assertEof();
+ OwnedHandle localProcess;
+ OwnedHandle localThread;
+ if (remoteProcess != nullptr) {
+ localProcess =
+ stealHandle(wp->agentProcess.get(), remoteProcess);
+ }
+ if (remoteThread != nullptr) {
+ localThread =
+ stealHandle(wp->agentProcess.get(), remoteThread);
+ }
+ if (process_handle != nullptr) {
+ *process_handle = localProcess.release();
+ }
+ if (thread_handle != nullptr) {
+ *thread_handle = localThread.release();
+ }
+ rpc.success();
+ } else {
+ throwWinptyException(
+ L"Agent RPC error: invalid StartProcessResult");
+ }
+ return TRUE;
+ } API_CATCH(FALSE)
+}
+
+
+
+/*****************************************************************************
+ * winpty agent RPC calls: everything else */
+
+WINPTY_API BOOL
+winpty_set_size(winpty_t *wp, int cols, int rows,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(wp != nullptr && cols > 0 && rows > 0);
+ LockGuard<Mutex> lock(wp->mutex);
+ RpcOperation rpc(*wp);
+ auto packet = newPacket();
+ packet.putInt32(AgentMsg::SetSize);
+ packet.putInt32(cols);
+ packet.putInt32(rows);
+ writePacket(*wp, packet);
+ readPacket(*wp).assertEof();
+ rpc.success();
+ return TRUE;
+ } API_CATCH(FALSE)
+}
+
+WINPTY_API int
+winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(wp != nullptr);
+ ASSERT(processList != nullptr);
+ LockGuard<Mutex> lock(wp->mutex);
+ RpcOperation rpc(*wp);
+ auto packet = newPacket();
+ packet.putInt32(AgentMsg::GetConsoleProcessList);
+ writePacket(*wp, packet);
+ auto reply = readPacket(*wp);
+
+ auto actualProcessCount = reply.getInt32();
+
+ if (actualProcessCount <= processCount) {
+ for (auto i = 0; i < actualProcessCount; i++) {
+ processList[i] = reply.getInt32();
+ }
+ }
+
+ reply.assertEof();
+ rpc.success();
+ return actualProcessCount;
+ } API_CATCH(0)
+}
+
+WINPTY_API void winpty_free(winpty_t *wp) {
+ // At least in principle, CloseHandle can fail, so this deletion can
+ // fail. It won't throw an exception, but maybe there's an error that
+ // should be propagated?
+ delete wp;
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/AgentMsg.h b/src/libs/3rdparty/winpty/src/shared/AgentMsg.h
new file mode 100644
index 0000000000..ab60c6b961
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/AgentMsg.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_AGENT_MSG_H
+#define WINPTY_SHARED_AGENT_MSG_H
+
+struct AgentMsg
+{
+ enum Type {
+ StartProcess,
+ SetSize,
+ GetConsoleProcessList,
+ };
+};
+
+enum class StartProcessResult {
+ CreateProcessFailed,
+ ProcessCreated,
+};
+
+#endif // WINPTY_SHARED_AGENT_MSG_H
diff --git a/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc
new file mode 100644
index 0000000000..1bea7e53dd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "BackgroundDesktop.h"
+
+#include <memory>
+
+#include "DebugClient.h"
+#include "StringUtil.h"
+#include "WinptyException.h"
+
+namespace {
+
+static std::wstring getObjectName(HANDLE object) {
+ BOOL success;
+ DWORD lengthNeeded = 0;
+ GetUserObjectInformationW(object, UOI_NAME,
+ nullptr, 0,
+ &lengthNeeded);
+ ASSERT(lengthNeeded % sizeof(wchar_t) == 0);
+ std::unique_ptr<wchar_t[]> tmp(
+ new wchar_t[lengthNeeded / sizeof(wchar_t)]);
+ success = GetUserObjectInformationW(object, UOI_NAME,
+ tmp.get(), lengthNeeded,
+ nullptr);
+ if (!success) {
+ throwWindowsError(L"GetUserObjectInformationW failed");
+ }
+ return std::wstring(tmp.get());
+}
+
+static std::wstring getDesktopName(HWINSTA winsta, HDESK desk) {
+ return getObjectName(winsta) + L"\\" + getObjectName(desk);
+}
+
+} // anonymous namespace
+
+// Get a non-interactive window station for the agent.
+// TODO: review security w.r.t. windowstation and desktop.
+BackgroundDesktop::BackgroundDesktop() {
+ try {
+ m_originalStation = GetProcessWindowStation();
+ if (m_originalStation == nullptr) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: "
+ L"GetProcessWindowStation returned NULL");
+ }
+ m_newStation =
+ CreateWindowStationW(nullptr, 0, WINSTA_ALL_ACCESS, nullptr);
+ if (m_newStation == nullptr) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: CreateWindowStationW returned NULL");
+ }
+ if (!SetProcessWindowStation(m_newStation)) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: SetProcessWindowStation failed");
+ }
+ m_newDesktop = CreateDesktopW(
+ L"Default", nullptr, nullptr, 0, GENERIC_ALL, nullptr);
+ if (m_newDesktop == nullptr) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: CreateDesktopW failed");
+ }
+ m_newDesktopName = getDesktopName(m_newStation, m_newDesktop);
+ TRACE("Created background desktop: %s",
+ utf8FromWide(m_newDesktopName).c_str());
+ } catch (...) {
+ dispose();
+ throw;
+ }
+}
+
+void BackgroundDesktop::dispose() WINPTY_NOEXCEPT {
+ if (m_originalStation != nullptr) {
+ SetProcessWindowStation(m_originalStation);
+ m_originalStation = nullptr;
+ }
+ if (m_newDesktop != nullptr) {
+ CloseDesktop(m_newDesktop);
+ m_newDesktop = nullptr;
+ }
+ if (m_newStation != nullptr) {
+ CloseWindowStation(m_newStation);
+ m_newStation = nullptr;
+ }
+}
+
+std::wstring getCurrentDesktopName() {
+ // MSDN says that the handles returned by GetProcessWindowStation and
+ // GetThreadDesktop do not need to be passed to CloseWindowStation and
+ // CloseDesktop, respectively.
+ const HWINSTA winsta = GetProcessWindowStation();
+ if (winsta == nullptr) {
+ throwWindowsError(
+ L"getCurrentDesktopName: "
+ L"GetProcessWindowStation returned NULL");
+ }
+ const HDESK desk = GetThreadDesktop(GetCurrentThreadId());
+ if (desk == nullptr) {
+ throwWindowsError(
+ L"getCurrentDesktopName: "
+ L"GetThreadDesktop returned NULL");
+ }
+ return getDesktopName(winsta, desk);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h
new file mode 100644
index 0000000000..c692e57dc4
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_BACKGROUND_DESKTOP_H
+#define WINPTY_SHARED_BACKGROUND_DESKTOP_H
+
+#include <windows.h>
+
+#include <string>
+
+#include "WinptyException.h"
+
+class BackgroundDesktop {
+public:
+ BackgroundDesktop();
+ ~BackgroundDesktop() { dispose(); }
+ void dispose() WINPTY_NOEXCEPT;
+ const std::wstring &desktopName() const { return m_newDesktopName; }
+
+ BackgroundDesktop(const BackgroundDesktop &other) = delete;
+ BackgroundDesktop &operator=(const BackgroundDesktop &other) = delete;
+
+ // We can't default the move constructor and assignment operator with
+ // MSVC 2013. We *could* if we required at least MSVC 2015 to build.
+
+ BackgroundDesktop(BackgroundDesktop &&other) :
+ m_originalStation(other.m_originalStation),
+ m_newStation(other.m_newStation),
+ m_newDesktop(other.m_newDesktop),
+ m_newDesktopName(std::move(other.m_newDesktopName)) {
+ other.m_originalStation = nullptr;
+ other.m_newStation = nullptr;
+ other.m_newDesktop = nullptr;
+ }
+ BackgroundDesktop &operator=(BackgroundDesktop &&other) {
+ dispose();
+ m_originalStation = other.m_originalStation;
+ m_newStation = other.m_newStation;
+ m_newDesktop = other.m_newDesktop;
+ m_newDesktopName = std::move(other.m_newDesktopName);
+ other.m_originalStation = nullptr;
+ other.m_newStation = nullptr;
+ other.m_newDesktop = nullptr;
+ return *this;
+ }
+
+private:
+ HWINSTA m_originalStation = nullptr;
+ HWINSTA m_newStation = nullptr;
+ HDESK m_newDesktop = nullptr;
+ std::wstring m_newDesktopName;
+};
+
+std::wstring getCurrentDesktopName();
+
+#endif // WINPTY_SHARED_BACKGROUND_DESKTOP_H
diff --git a/src/libs/3rdparty/winpty/src/shared/Buffer.cc b/src/libs/3rdparty/winpty/src/shared/Buffer.cc
new file mode 100644
index 0000000000..158a629d56
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/Buffer.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Buffer.h"
+
+#include <stdint.h>
+
+#include "DebugClient.h"
+#include "WinptyAssert.h"
+
+// Define the READ_BUFFER_CHECK() macro. It *must* evaluate its condition,
+// exactly once.
+#define READ_BUFFER_CHECK(cond) \
+ do { \
+ if (!(cond)) { \
+ trace("decode error: %s", #cond); \
+ throw DecodeError(); \
+ } \
+ } while (false)
+
+enum class Piece : uint8_t { Int32, Int64, WString };
+
+void WriteBuffer::putRawData(const void *data, size_t len) {
+ const auto p = reinterpret_cast<const char*>(data);
+ m_buf.insert(m_buf.end(), p, p + len);
+}
+
+void WriteBuffer::replaceRawData(size_t pos, const void *data, size_t len) {
+ ASSERT(pos <= m_buf.size() && len <= m_buf.size() - pos);
+ const auto p = reinterpret_cast<const char*>(data);
+ std::copy(p, p + len, &m_buf[pos]);
+}
+
+void WriteBuffer::putInt32(int32_t i) {
+ putRawValue(Piece::Int32);
+ putRawValue(i);
+}
+
+void WriteBuffer::putInt64(int64_t i) {
+ putRawValue(Piece::Int64);
+ putRawValue(i);
+}
+
+// len is in characters, excluding NUL, i.e. the number of wchar_t elements
+void WriteBuffer::putWString(const wchar_t *str, size_t len) {
+ putRawValue(Piece::WString);
+ putRawValue(static_cast<uint64_t>(len));
+ putRawData(str, sizeof(wchar_t) * len);
+}
+
+void ReadBuffer::getRawData(void *data, size_t len) {
+ ASSERT(m_off <= m_buf.size());
+ READ_BUFFER_CHECK(len <= m_buf.size() - m_off);
+ const char *const inp = &m_buf[m_off];
+ std::copy(inp, inp + len, reinterpret_cast<char*>(data));
+ m_off += len;
+}
+
+int32_t ReadBuffer::getInt32() {
+ READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::Int32);
+ return getRawValue<int32_t>();
+}
+
+int64_t ReadBuffer::getInt64() {
+ READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::Int64);
+ return getRawValue<int64_t>();
+}
+
+std::wstring ReadBuffer::getWString() {
+ READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::WString);
+ const uint64_t charLen = getRawValue<uint64_t>();
+ READ_BUFFER_CHECK(charLen <= SIZE_MAX / sizeof(wchar_t));
+ // To be strictly conforming, we can't use the convenient wstring
+ // constructor, because the string in m_buf mightn't be aligned.
+ std::wstring ret;
+ if (charLen > 0) {
+ const size_t byteLen = charLen * sizeof(wchar_t);
+ ret.resize(charLen);
+ getRawData(&ret[0], byteLen);
+ }
+ return ret;
+}
+
+void ReadBuffer::assertEof() {
+ READ_BUFFER_CHECK(m_off == m_buf.size());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/Buffer.h b/src/libs/3rdparty/winpty/src/shared/Buffer.h
new file mode 100644
index 0000000000..c2dd382e5b
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/Buffer.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_BUFFER_H
+#define WINPTY_SHARED_BUFFER_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <string>
+
+#include "WinptyException.h"
+
+class WriteBuffer {
+private:
+ std::vector<char> m_buf;
+
+public:
+ WriteBuffer() {}
+
+ template <typename T> void putRawValue(const T &t) {
+ putRawData(&t, sizeof(t));
+ }
+ template <typename T> void replaceRawValue(size_t pos, const T &t) {
+ replaceRawData(pos, &t, sizeof(t));
+ }
+
+ void putRawData(const void *data, size_t len);
+ void replaceRawData(size_t pos, const void *data, size_t len);
+ void putInt32(int32_t i);
+ void putInt64(int64_t i);
+ void putWString(const wchar_t *str, size_t len);
+ void putWString(const wchar_t *str) { putWString(str, wcslen(str)); }
+ void putWString(const std::wstring &str) { putWString(str.data(), str.size()); }
+ std::vector<char> &buf() { return m_buf; }
+
+ // MSVC 2013 does not generate these automatically, so help it out.
+ WriteBuffer(WriteBuffer &&other) : m_buf(std::move(other.m_buf)) {}
+ WriteBuffer &operator=(WriteBuffer &&other) {
+ m_buf = std::move(other.m_buf);
+ return *this;
+ }
+};
+
+class ReadBuffer {
+public:
+ class DecodeError : public WinptyException {
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return L"DecodeError: RPC message decoding error";
+ }
+ };
+
+private:
+ std::vector<char> m_buf;
+ size_t m_off = 0;
+
+public:
+ explicit ReadBuffer(std::vector<char> &&buf) : m_buf(std::move(buf)) {}
+
+ template <typename T> T getRawValue() {
+ T ret = {};
+ getRawData(&ret, sizeof(ret));
+ return ret;
+ }
+
+ void getRawData(void *data, size_t len);
+ int32_t getInt32();
+ int64_t getInt64();
+ std::wstring getWString();
+ void assertEof();
+
+ // MSVC 2013 does not generate these automatically, so help it out.
+ ReadBuffer(ReadBuffer &&other) :
+ m_buf(std::move(other.m_buf)), m_off(other.m_off) {}
+ ReadBuffer &operator=(ReadBuffer &&other) {
+ m_buf = std::move(other.m_buf);
+ m_off = other.m_off;
+ return *this;
+ }
+};
+
+#endif // WINPTY_SHARED_BUFFER_H
diff --git a/src/libs/3rdparty/winpty/src/shared/DebugClient.cc b/src/libs/3rdparty/winpty/src/shared/DebugClient.cc
new file mode 100644
index 0000000000..bafe0c8954
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/DebugClient.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "DebugClient.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+
+#include "winpty_snprintf.h"
+
+const wchar_t *const kPipeName = L"\\\\.\\pipe\\DebugServer";
+
+void *volatile g_debugConfig;
+
+namespace {
+
+// It would be easy to accidentally trample on the Windows LastError value
+// by adding logging/debugging code. Ensure that can't happen by saving and
+// restoring the value. This saving and restoring doesn't happen along the
+// fast path.
+class PreserveLastError {
+public:
+ PreserveLastError() : m_lastError(GetLastError()) {}
+ ~PreserveLastError() { SetLastError(m_lastError); }
+private:
+ DWORD m_lastError;
+};
+
+} // anonymous namespace
+
+static void sendToDebugServer(const char *message)
+{
+ HANDLE tracePipe = INVALID_HANDLE_VALUE;
+
+ do {
+ // The default impersonation level is SECURITY_IMPERSONATION, which allows
+ // a sufficiently authorized named pipe server to impersonate the client.
+ // There's no need for impersonation in this debugging system, so reduce
+ // the impersonation level to SECURITY_IDENTIFICATION, which allows a
+ // server to merely identify us.
+ tracePipe = CreateFileW(
+ kPipeName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
+ NULL);
+ } while (tracePipe == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_PIPE_BUSY &&
+ WaitNamedPipeW(kPipeName, NMPWAIT_WAIT_FOREVER));
+
+ if (tracePipe != INVALID_HANDLE_VALUE) {
+ DWORD newMode = PIPE_READMODE_MESSAGE;
+ SetNamedPipeHandleState(tracePipe, &newMode, NULL, NULL);
+ char response[16];
+ DWORD actual = 0;
+ TransactNamedPipe(tracePipe,
+ const_cast<char*>(message), strlen(message),
+ response, sizeof(response), &actual, NULL);
+ CloseHandle(tracePipe);
+ }
+}
+
+// Get the current UTC time as milliseconds from the epoch (ignoring leap
+// seconds). Use the Unix epoch for consistency with DebugClient.py. There
+// are 134774 days between 1601-01-01 (the Win32 epoch) and 1970-01-01 (the
+// Unix epoch).
+static long long unixTimeMillis()
+{
+ FILETIME fileTime;
+ GetSystemTimeAsFileTime(&fileTime);
+ long long msTime = (((long long)fileTime.dwHighDateTime << 32) +
+ fileTime.dwLowDateTime) / 10000;
+ return msTime - 134774LL * 24 * 3600 * 1000;
+}
+
+static const char *getDebugConfig()
+{
+ if (g_debugConfig == NULL) {
+ PreserveLastError preserve;
+ const int bufSize = 256;
+ char buf[bufSize];
+ DWORD actualSize =
+ GetEnvironmentVariableA("WINPTY_DEBUG", buf, bufSize);
+ if (actualSize == 0 || actualSize >= static_cast<DWORD>(bufSize)) {
+ buf[0] = '\0';
+ }
+ const size_t len = strlen(buf) + 1;
+ char *newConfig = new char[len];
+ std::copy(buf, buf + len, newConfig);
+ void *oldValue = InterlockedCompareExchangePointer(
+ &g_debugConfig, newConfig, NULL);
+ if (oldValue != NULL) {
+ delete [] newConfig;
+ }
+ }
+ return static_cast<const char*>(g_debugConfig);
+}
+
+bool isTracingEnabled()
+{
+ static bool disabled, enabled;
+ if (disabled) {
+ return false;
+ } else if (enabled) {
+ return true;
+ } else {
+ // Recognize WINPTY_DEBUG=1 for backwards compatibility.
+ PreserveLastError preserve;
+ bool value = hasDebugFlag("trace") || hasDebugFlag("1");
+ disabled = !value;
+ enabled = value;
+ return value;
+ }
+}
+
+bool hasDebugFlag(const char *flag)
+{
+ if (strchr(flag, ',') != NULL) {
+ trace("INTERNAL ERROR: hasDebugFlag flag has comma: '%s'", flag);
+ abort();
+ }
+ const char *const configCStr = getDebugConfig();
+ if (configCStr[0] == '\0') {
+ return false;
+ }
+ PreserveLastError preserve;
+ std::string config(configCStr);
+ std::string flagStr(flag);
+ config = "," + config + ",";
+ flagStr = "," + flagStr + ",";
+ return config.find(flagStr) != std::string::npos;
+}
+
+void trace(const char *format, ...)
+{
+ if (!isTracingEnabled())
+ return;
+
+ PreserveLastError preserve;
+ char message[1024];
+
+ va_list ap;
+ va_start(ap, format);
+ winpty_vsnprintf(message, format, ap);
+ message[sizeof(message) - 1] = '\0';
+ va_end(ap);
+
+ const int currentTime = (int)(unixTimeMillis() % (100000 * 1000));
+
+ char moduleName[1024];
+ moduleName[0] = '\0';
+ GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
+ const char *baseName = strrchr(moduleName, '\\');
+ baseName = (baseName != NULL) ? baseName + 1 : moduleName;
+
+ char fullMessage[1024];
+ winpty_snprintf(fullMessage,
+ "[%05d.%03d %s,p%04d,t%04d]: %s",
+ currentTime / 1000, currentTime % 1000,
+ baseName, (int)GetCurrentProcessId(), (int)GetCurrentThreadId(),
+ message);
+ fullMessage[sizeof(fullMessage) - 1] = '\0';
+
+ sendToDebugServer(fullMessage);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/DebugClient.h b/src/libs/3rdparty/winpty/src/shared/DebugClient.h
new file mode 100644
index 0000000000..b126071130
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/DebugClient.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef DEBUGCLIENT_H
+#define DEBUGCLIENT_H
+
+#include "winpty_snprintf.h"
+
+bool isTracingEnabled();
+bool hasDebugFlag(const char *flag);
+void trace(const char *format, ...) WINPTY_SNPRINTF_FORMAT(1, 2);
+
+// This macro calls trace without evaluating the arguments.
+#define TRACE(format, ...) \
+ do { \
+ if (isTracingEnabled()) { \
+ trace((format), ## __VA_ARGS__); \
+ } \
+ } while (false)
+
+#endif // DEBUGCLIENT_H
diff --git a/src/libs/3rdparty/winpty/src/shared/GenRandom.cc b/src/libs/3rdparty/winpty/src/shared/GenRandom.cc
new file mode 100644
index 0000000000..6d7920643a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/GenRandom.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "GenRandom.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "DebugClient.h"
+#include "StringBuilder.h"
+
+static volatile LONG g_pipeCounter;
+
+GenRandom::GenRandom() : m_advapi32(L"advapi32.dll") {
+ // First try to use the pseudo-documented RtlGenRandom function from
+ // advapi32.dll. Creating a CryptoAPI context is slow, and RtlGenRandom
+ // avoids the overhead. It's documented in this blog post[1] and on
+ // MSDN[2] with a disclaimer about future breakage. This technique is
+ // apparently built-in into the MSVC CRT, though, for the rand_s function,
+ // so perhaps it is stable enough.
+ //
+ // [1] http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx
+ // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
+ //
+ // Both RtlGenRandom and the Crypto API functions exist in XP and up.
+ m_rtlGenRandom = reinterpret_cast<RtlGenRandom_t*>(
+ m_advapi32.proc("SystemFunction036"));
+ // The OsModule class logs an error message if the proc is nullptr.
+ if (m_rtlGenRandom != nullptr) {
+ return;
+ }
+
+ // Fall back to the crypto API.
+ m_cryptProvIsValid =
+ CryptAcquireContext(&m_cryptProv, nullptr, nullptr,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) != 0;
+ if (!m_cryptProvIsValid) {
+ trace("GenRandom: CryptAcquireContext failed: %u",
+ static_cast<unsigned>(GetLastError()));
+ }
+}
+
+GenRandom::~GenRandom() {
+ if (m_cryptProvIsValid) {
+ CryptReleaseContext(m_cryptProv, 0);
+ }
+}
+
+// Returns false if the context is invalid or the generation fails.
+bool GenRandom::fillBuffer(void *buffer, size_t size) {
+ memset(buffer, 0, size);
+ bool success = false;
+ if (m_rtlGenRandom != nullptr) {
+ success = m_rtlGenRandom(buffer, size) != 0;
+ if (!success) {
+ trace("GenRandom: RtlGenRandom/SystemFunction036 failed: %u",
+ static_cast<unsigned>(GetLastError()));
+ }
+ } else if (m_cryptProvIsValid) {
+ success =
+ CryptGenRandom(m_cryptProv, size,
+ reinterpret_cast<BYTE*>(buffer)) != 0;
+ if (!success) {
+ trace("GenRandom: CryptGenRandom failed, size=%d, lasterror=%u",
+ static_cast<int>(size),
+ static_cast<unsigned>(GetLastError()));
+ }
+ }
+ return success;
+}
+
+// Returns an empty string if either of CryptAcquireContext or CryptGenRandom
+// fail.
+std::string GenRandom::randomBytes(size_t numBytes) {
+ std::string ret(numBytes, '\0');
+ if (!fillBuffer(&ret[0], numBytes)) {
+ return std::string();
+ }
+ return ret;
+}
+
+std::wstring GenRandom::randomHexString(size_t numBytes) {
+ const std::string bytes = randomBytes(numBytes);
+ std::wstring ret(bytes.size() * 2, L'\0');
+ for (size_t i = 0; i < bytes.size(); ++i) {
+ static const wchar_t hex[] = L"0123456789abcdef";
+ ret[i * 2] = hex[static_cast<uint8_t>(bytes[i]) >> 4];
+ ret[i * 2 + 1] = hex[static_cast<uint8_t>(bytes[i]) & 0xF];
+ }
+ return ret;
+}
+
+// Returns a 64-bit value representing the number of 100-nanosecond intervals
+// since January 1, 1601.
+static uint64_t systemTimeAsUInt64() {
+ FILETIME monotonicTime = {};
+ GetSystemTimeAsFileTime(&monotonicTime);
+ return (static_cast<uint64_t>(monotonicTime.dwHighDateTime) << 32) |
+ static_cast<uint64_t>(monotonicTime.dwLowDateTime);
+}
+
+// Generates a unique and hard-to-guess case-insensitive string suitable for
+// use in a pipe filename or a Windows object name.
+std::wstring GenRandom::uniqueName() {
+ // First include enough information to avoid collisions assuming
+ // cooperative software. This code assumes that a process won't die and
+ // be replaced with a recycled PID within a single GetSystemTimeAsFileTime
+ // interval.
+ WStringBuilder sb(64);
+ sb << GetCurrentProcessId()
+ << L'-' << InterlockedIncrement(&g_pipeCounter)
+ << L'-' << whexOfInt(systemTimeAsUInt64());
+ // It isn't clear to me how the crypto APIs would fail. It *probably*
+ // doesn't matter that much anyway? In principle, a predictable pipe name
+ // is subject to a local denial-of-service attack.
+ auto random = randomHexString(16);
+ if (!random.empty()) {
+ sb << L'-' << random;
+ }
+ return sb.str_moved();
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/GenRandom.h b/src/libs/3rdparty/winpty/src/shared/GenRandom.h
new file mode 100644
index 0000000000..746cb1ecf7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/GenRandom.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_GEN_RANDOM_H
+#define WINPTY_GEN_RANDOM_H
+
+// The original MinGW requires that we include wincrypt.h. With MinGW-w64 and
+// MSVC, including windows.h is sufficient.
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <string>
+
+#include "OsModule.h"
+
+class GenRandom {
+ typedef BOOLEAN WINAPI RtlGenRandom_t(PVOID, ULONG);
+
+ OsModule m_advapi32;
+ RtlGenRandom_t *m_rtlGenRandom = nullptr;
+ bool m_cryptProvIsValid = false;
+ HCRYPTPROV m_cryptProv = 0;
+
+public:
+ GenRandom();
+ ~GenRandom();
+ bool fillBuffer(void *buffer, size_t size);
+ std::string randomBytes(size_t numBytes);
+ std::wstring randomHexString(size_t numBytes);
+ std::wstring uniqueName();
+
+ // Return true if the crypto context was successfully initialized.
+ bool valid() const {
+ return m_rtlGenRandom != nullptr || m_cryptProvIsValid;
+ }
+};
+
+#endif // WINPTY_GEN_RANDOM_H
diff --git a/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat b/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat
new file mode 100644
index 0000000000..a9f8e9cef0
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat
@@ -0,0 +1,13 @@
+@echo off
+
+REM -- Echo the git commit hash. If git isn't available for some reason,
+REM -- output nothing instead.
+
+git rev-parse HEAD >NUL 2>NUL && (
+ git rev-parse HEAD
+) || (
+ echo none
+)
+
+REM -- Set ERRORLEVEL to 0 using this cryptic syntax.
+(call )
diff --git a/src/libs/3rdparty/winpty/src/shared/Mutex.h b/src/libs/3rdparty/winpty/src/shared/Mutex.h
new file mode 100644
index 0000000000..98215365ad
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/Mutex.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Recent 4.x MinGW and MinGW-w64 gcc compilers lack std::mutex and
+// std::lock_guard. I have a 5.2.0 MinGW-w64 compiler packaged through MSYS2
+// that *is* new enough, but that's one compiler against several deficient
+// ones. Wrap CRITICAL_SECTION instead.
+
+#ifndef WINPTY_SHARED_MUTEX_H
+#define WINPTY_SHARED_MUTEX_H
+
+#include <windows.h>
+
+class Mutex {
+ CRITICAL_SECTION m_mutex;
+public:
+ Mutex() { InitializeCriticalSection(&m_mutex); }
+ ~Mutex() { DeleteCriticalSection(&m_mutex); }
+ void lock() { EnterCriticalSection(&m_mutex); }
+ void unlock() { LeaveCriticalSection(&m_mutex); }
+
+ Mutex(const Mutex &other) = delete;
+ Mutex &operator=(const Mutex &other) = delete;
+};
+
+template <typename T>
+class LockGuard {
+ T &m_lock;
+public:
+ LockGuard(T &lock) : m_lock(lock) { m_lock.lock(); }
+ ~LockGuard() { m_lock.unlock(); }
+
+ LockGuard(const LockGuard &other) = delete;
+ LockGuard &operator=(const LockGuard &other) = delete;
+};
+
+#endif // WINPTY_SHARED_MUTEX_H
diff --git a/src/libs/3rdparty/winpty/src/shared/OsModule.h b/src/libs/3rdparty/winpty/src/shared/OsModule.h
new file mode 100644
index 0000000000..9713fa2b2d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/OsModule.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_OS_MODULE_H
+#define WINPTY_SHARED_OS_MODULE_H
+
+#include <windows.h>
+
+#include <string>
+
+#include "DebugClient.h"
+#include "WinptyAssert.h"
+#include "WinptyException.h"
+
+class OsModule {
+ HMODULE m_module;
+public:
+ enum class LoadErrorBehavior { Abort, Throw };
+ OsModule(const wchar_t *fileName,
+ LoadErrorBehavior behavior=LoadErrorBehavior::Abort) {
+ m_module = LoadLibraryW(fileName);
+ if (behavior == LoadErrorBehavior::Abort) {
+ ASSERT(m_module != NULL);
+ } else {
+ if (m_module == nullptr) {
+ const auto err = GetLastError();
+ throwWindowsError(
+ (L"LoadLibraryW error: " + std::wstring(fileName)).c_str(),
+ err);
+ }
+ }
+ }
+ ~OsModule() {
+ FreeLibrary(m_module);
+ }
+ HMODULE handle() const { return m_module; }
+ FARPROC proc(const char *funcName) {
+ FARPROC ret = GetProcAddress(m_module, funcName);
+ if (ret == NULL) {
+ trace("GetProcAddress: %s is missing", funcName);
+ }
+ return ret;
+ }
+};
+
+#endif // WINPTY_SHARED_OS_MODULE_H
diff --git a/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc
new file mode 100644
index 0000000000..7b173536e6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "OwnedHandle.h"
+
+#include "DebugClient.h"
+#include "WinptyException.h"
+
+void OwnedHandle::dispose(bool nothrow) {
+ if (m_h != nullptr && m_h != INVALID_HANDLE_VALUE) {
+ if (!CloseHandle(m_h)) {
+ trace("CloseHandle(%p) failed", m_h);
+ if (!nothrow) {
+ throwWindowsError(L"CloseHandle failed");
+ }
+ }
+ }
+ m_h = nullptr;
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h
new file mode 100644
index 0000000000..70a8d6163a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_OWNED_HANDLE_H
+#define WINPTY_SHARED_OWNED_HANDLE_H
+
+#include <windows.h>
+
+class OwnedHandle {
+ HANDLE m_h;
+public:
+ OwnedHandle() : m_h(nullptr) {}
+ explicit OwnedHandle(HANDLE h) : m_h(h) {}
+ ~OwnedHandle() { dispose(true); }
+ void dispose(bool nothrow=false);
+ HANDLE get() const { return m_h; }
+ HANDLE release() { HANDLE ret = m_h; m_h = nullptr; return ret; }
+ OwnedHandle(const OwnedHandle &other) = delete;
+ OwnedHandle(OwnedHandle &&other) : m_h(other.release()) {}
+ OwnedHandle &operator=(const OwnedHandle &other) = delete;
+ OwnedHandle &operator=(OwnedHandle &&other) {
+ dispose();
+ m_h = other.release();
+ return *this;
+ }
+};
+
+#endif // WINPTY_SHARED_OWNED_HANDLE_H
diff --git a/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h b/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h
new file mode 100644
index 0000000000..7d9b8f8b4a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_PRECOMPILED_HEADER_H
+#define WINPTY_PRECOMPILED_HEADER_H
+
+#include <windows.h>
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <array>
+#include <limits>
+#include <memory>
+#include <new>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#endif // WINPTY_PRECOMPILED_HEADER_H
diff --git a/src/libs/3rdparty/winpty/src/shared/StringBuilder.h b/src/libs/3rdparty/winpty/src/shared/StringBuilder.h
new file mode 100644
index 0000000000..f3155bdd29
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringBuilder.h
@@ -0,0 +1,227 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Efficient integer->string conversion and string concatenation. The
+// hexadecimal conversion may optionally have leading zeros. Other ways to
+// convert integers to strings in C++ suffer these drawbacks:
+//
+// * std::stringstream: Inefficient, even more so than stdio.
+//
+// * std::to_string: No hexadecimal output, tends to use heap allocation, not
+// supported on Cygwin.
+//
+// * stdio routines: Requires parsing a format string (inefficient). The
+// caller *must* know how large the content is for correctness. The
+// string-printf functions are extremely inconsistent on Windows. In
+// particular, 64-bit integers, wide strings, and return values are
+// problem areas.
+//
+// StringBuilderTest.cc is a standalone program that tests this header.
+
+#ifndef WINPTY_STRING_BUILDER_H
+#define WINPTY_STRING_BUILDER_H
+
+#include <array>
+#include <string>
+#include <type_traits>
+
+#ifdef STRING_BUILDER_TESTING
+#include <assert.h>
+#define STRING_BUILDER_CHECK(cond) assert(cond)
+#else
+#define STRING_BUILDER_CHECK(cond)
+#endif // STRING_BUILDER_TESTING
+
+#include "WinptyAssert.h"
+
+template <typename C, size_t sz>
+struct ValueString {
+ std::array<C, sz> m_array;
+ size_t m_offset;
+ size_t m_size;
+
+ const C *c_str() const { return m_array.data() + m_offset; }
+ const C *data() const { return m_array.data() + m_offset; }
+ size_t size() const { return m_size; }
+ std::basic_string<C> str() const {
+ return std::basic_string<C>(data(), m_size);
+ }
+};
+
+#ifdef _MSC_VER
+// Disable an MSVC /SDL error that forbids unsigned negation. Signed negation
+// invokes undefined behavior for INTxx_MIN, so unsigned negation is simpler to
+// reason about. (We assume twos-complement in any case.)
+#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) \
+ ( \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4146)) \
+ (x) \
+ __pragma(warning(pop)) \
+ )
+#else
+#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) (x)
+#endif
+
+// Formats an integer as decimal without leading zeros.
+template <typename C, typename I>
+ValueString<C, sizeof(I) * 3 + 1 + 1> gdecOfInt(const I value) {
+ typedef typename std::make_unsigned<I>::type U;
+ auto unsValue = static_cast<U>(value);
+ const bool isNegative = (value < 0);
+ if (isNegative) {
+ unsValue = STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(-unsValue);
+ }
+ decltype(gdecOfInt<C, I>(value)) out;
+ auto &arr = out.m_array;
+ C *const endp = arr.data() + arr.size();
+ C *outp = endp;
+ *(--outp) = '\0';
+ STRING_BUILDER_CHECK(outp >= arr.data());
+ do {
+ const int digit = unsValue % 10;
+ unsValue /= 10;
+ *(--outp) = '0' + digit;
+ STRING_BUILDER_CHECK(outp >= arr.data());
+ } while (unsValue != 0);
+ if (isNegative) {
+ *(--outp) = '-';
+ STRING_BUILDER_CHECK(outp >= arr.data());
+ }
+ out.m_offset = outp - arr.data();
+ out.m_size = endp - outp - 1;
+ return out;
+}
+
+template <typename I> decltype(gdecOfInt<char, I>(0)) decOfInt(I i) {
+ return gdecOfInt<char>(i);
+}
+
+template <typename I> decltype(gdecOfInt<wchar_t, I>(0)) wdecOfInt(I i) {
+ return gdecOfInt<wchar_t>(i);
+}
+
+// Formats an integer as hexadecimal, with or without leading zeros.
+template <typename C, bool leadingZeros=false, typename I>
+ValueString<C, sizeof(I) * 2 + 1> ghexOfInt(const I value) {
+ typedef typename std::make_unsigned<I>::type U;
+ const auto unsValue = static_cast<U>(value);
+ static const C hex[16] = {'0','1','2','3','4','5','6','7',
+ '8','9','a','b','c','d','e','f'};
+ decltype(ghexOfInt<C, leadingZeros, I>(value)) out;
+ auto &arr = out.m_array;
+ C *outp = arr.data();
+ int inIndex = 0;
+ int shift = sizeof(I) * 8 - 4;
+ const int len = sizeof(I) * 2;
+ if (!leadingZeros) {
+ for (; inIndex < len - 1; ++inIndex, shift -= 4) {
+ STRING_BUILDER_CHECK(shift >= 0 && shift < sizeof(unsValue) * 8);
+ const int digit = (unsValue >> shift) & 0xF;
+ if (digit != 0) {
+ break;
+ }
+ }
+ }
+ for (; inIndex < len; ++inIndex, shift -= 4) {
+ const int digit = (unsValue >> shift) & 0xF;
+ *(outp++) = hex[digit];
+ STRING_BUILDER_CHECK(outp <= arr.data() + arr.size());
+ }
+ *(outp++) = '\0';
+ STRING_BUILDER_CHECK(outp <= arr.data() + arr.size());
+ out.m_offset = 0;
+ out.m_size = outp - arr.data() - 1;
+ return out;
+}
+
+template <bool leadingZeros=false, typename I>
+decltype(ghexOfInt<char, leadingZeros, I>(0)) hexOfInt(I i) {
+ return ghexOfInt<char, leadingZeros, I>(i);
+}
+
+template <bool leadingZeros=false, typename I>
+decltype(ghexOfInt<wchar_t, leadingZeros, I>(0)) whexOfInt(I i) {
+ return ghexOfInt<wchar_t, leadingZeros, I>(i);
+}
+
+template <typename C>
+class GStringBuilder {
+public:
+ typedef std::basic_string<C> StringType;
+
+ GStringBuilder() {}
+ GStringBuilder(size_t capacity) {
+ m_out.reserve(capacity);
+ }
+
+ GStringBuilder &operator<<(C ch) { m_out.push_back(ch); return *this; }
+ GStringBuilder &operator<<(const C *str) { m_out.append(str); return *this; }
+ GStringBuilder &operator<<(const StringType &str) { m_out.append(str); return *this; }
+
+ template <size_t sz>
+ GStringBuilder &operator<<(const ValueString<C, sz> &str) {
+ m_out.append(str.data(), str.size());
+ return *this;
+ }
+
+private:
+ // Forbid output of char/wchar_t for GStringBuilder if the type doesn't
+ // exactly match the builder element type. The code still allows
+ // signed char and unsigned char, but I'm a little worried about what
+ // happens if a user tries to output int8_t or uint8_t.
+ template <typename P>
+ typename std::enable_if<
+ (std::is_same<P, char>::value || std::is_same<P, wchar_t>::value) &&
+ !std::is_same<C, P>::value, GStringBuilder&>::type
+ operator<<(P ch) {
+ ASSERT(false && "Method was not supposed to be reachable.");
+ return *this;
+ }
+
+public:
+ GStringBuilder &operator<<(short i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned short i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(int i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned int i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(long i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned long i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(long long i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned long long i) { return *this << gdecOfInt<C>(i); }
+
+ GStringBuilder &operator<<(const void *p) {
+ m_out.push_back(static_cast<C>('0'));
+ m_out.push_back(static_cast<C>('x'));
+ *this << ghexOfInt<C>(reinterpret_cast<uintptr_t>(p));
+ return *this;
+ }
+
+ StringType str() { return m_out; }
+ StringType str_moved() { return std::move(m_out); }
+ const C *c_str() const { return m_out.c_str(); }
+
+private:
+ StringType m_out;
+};
+
+typedef GStringBuilder<char> StringBuilder;
+typedef GStringBuilder<wchar_t> WStringBuilder;
+
+#endif // WINPTY_STRING_BUILDER_H
diff --git a/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc b/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc
new file mode 100644
index 0000000000..e6c2d3138c
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#define STRING_BUILDER_TESTING
+
+#include "StringBuilder.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <iomanip>
+#include <sstream>
+
+void display(const std::string &str) { fprintf(stderr, "%s", str.c_str()); }
+void display(const std::wstring &str) { fprintf(stderr, "%ls", str.c_str()); }
+
+#define CHECK_EQ(x, y) \
+ do { \
+ const auto xval = (x); \
+ const auto yval = (y); \
+ if (xval != yval) { \
+ fprintf(stderr, "error: %s:%d: %s != %s: ", \
+ __FILE__, __LINE__, #x, #y); \
+ display(xval); \
+ fprintf(stderr, " != "); \
+ display(yval); \
+ fprintf(stderr, "\n"); \
+ } \
+ } while(0)
+
+template <typename C, typename I>
+std::basic_string<C> decOfIntSS(const I value) {
+ // std::to_string and std::to_wstring are missing in Cygwin as of this
+ // writing (early 2016).
+ std::basic_stringstream<C> ss;
+ ss << +value; // We must promote char to print it as an integer.
+ return ss.str();
+}
+
+
+template <typename C, bool leadingZeros=false, typename I>
+std::basic_string<C> hexOfIntSS(const I value) {
+ typedef typename std::make_unsigned<I>::type U;
+ const unsigned long long u64Value = value & static_cast<U>(~0);
+ std::basic_stringstream<C> ss;
+ if (leadingZeros) {
+ ss << std::setfill(static_cast<C>('0')) << std::setw(sizeof(I) * 2);
+ }
+ ss << std::hex << u64Value;
+ return ss.str();
+}
+
+template <typename I>
+void testValue(I value) {
+ CHECK_EQ(decOfInt(value).str(), (decOfIntSS<char>(value)));
+ CHECK_EQ(wdecOfInt(value).str(), (decOfIntSS<wchar_t>(value)));
+ CHECK_EQ((hexOfInt<false>(value).str()), (hexOfIntSS<char, false>(value)));
+ CHECK_EQ((hexOfInt<true>(value).str()), (hexOfIntSS<char, true>(value)));
+ CHECK_EQ((whexOfInt<false>(value).str()), (hexOfIntSS<wchar_t, false>(value)));
+ CHECK_EQ((whexOfInt<true>(value).str()), (hexOfIntSS<wchar_t, true>(value)));
+}
+
+template <typename I>
+void testType() {
+ typedef typename std::make_unsigned<I>::type U;
+ const U quarter = static_cast<U>(1) << (sizeof(U) * 8 - 2);
+ for (unsigned quarterIndex = 0; quarterIndex < 4; ++quarterIndex) {
+ for (int offset = -18; offset <= 18; ++offset) {
+ const I value = quarter * quarterIndex + static_cast<U>(offset);
+ testValue(value);
+ }
+ }
+ testValue(static_cast<I>(42));
+ testValue(static_cast<I>(123456));
+ testValue(static_cast<I>(0xdeadfacecafebeefull));
+}
+
+int main() {
+ testType<char>();
+
+ testType<signed char>();
+ testType<signed short>();
+ testType<signed int>();
+ testType<signed long>();
+ testType<signed long long>();
+
+ testType<unsigned char>();
+ testType<unsigned short>();
+ testType<unsigned int>();
+ testType<unsigned long>();
+ testType<unsigned long long>();
+
+ StringBuilder() << static_cast<const void*>("TEST");
+ WStringBuilder() << static_cast<const void*>("TEST");
+
+ fprintf(stderr, "All tests completed!\n");
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/StringUtil.cc b/src/libs/3rdparty/winpty/src/shared/StringUtil.cc
new file mode 100644
index 0000000000..3a85a3ec94
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringUtil.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "StringUtil.h"
+
+#include <windows.h>
+
+#include "WinptyAssert.h"
+
+// Workaround. MinGW (from mingw.org) does not have wcsnlen. MinGW-w64 *does*
+// have wcsnlen, but use this function for consistency.
+size_t winpty_wcsnlen(const wchar_t *s, size_t maxlen) {
+ ASSERT(s != NULL);
+ for (size_t i = 0; i < maxlen; ++i) {
+ if (s[i] == L'\0') {
+ return i;
+ }
+ }
+ return maxlen;
+}
+
+std::string utf8FromWide(const std::wstring &input) {
+ int mblen = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ NULL, 0, NULL, NULL);
+ if (mblen <= 0) {
+ return std::string();
+ }
+ std::vector<char> tmp(mblen);
+ int mblen2 = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ tmp.data(), tmp.size(),
+ NULL, NULL);
+ ASSERT(mblen2 == mblen);
+ return std::string(tmp.data(), tmp.size());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/StringUtil.h b/src/libs/3rdparty/winpty/src/shared/StringUtil.h
new file mode 100644
index 0000000000..e4bf3c9121
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringUtil.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_STRING_UTIL_H
+#define WINPTY_SHARED_STRING_UTIL_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "WinptyAssert.h"
+
+size_t winpty_wcsnlen(const wchar_t *s, size_t maxlen);
+std::string utf8FromWide(const std::wstring &input);
+
+// Return a vector containing each character in the string.
+template <typename T>
+std::vector<T> vectorFromString(const std::basic_string<T> &str) {
+ return std::vector<T>(str.begin(), str.end());
+}
+
+// Return a vector containing each character in the string, followed by a
+// NUL terminator.
+template <typename T>
+std::vector<T> vectorWithNulFromString(const std::basic_string<T> &str) {
+ std::vector<T> ret;
+ ret.reserve(str.size() + 1);
+ ret.insert(ret.begin(), str.begin(), str.end());
+ ret.push_back('\0');
+ return ret;
+}
+
+// A safer(?) version of wcsncpy that is accepted by MSVC's /SDL mode.
+template <size_t N>
+wchar_t *winpty_wcsncpy(wchar_t (&d)[N], const wchar_t *s) {
+ ASSERT(s != nullptr);
+ size_t i = 0;
+ for (; i < N; ++i) {
+ if (s[i] == L'\0') {
+ break;
+ }
+ d[i] = s[i];
+ }
+ for (; i < N; ++i) {
+ d[i] = L'\0';
+ }
+ return d;
+}
+
+// Like wcsncpy, but ensure that the destination buffer is NUL-terminated.
+template <size_t N>
+wchar_t *winpty_wcsncpy_nul(wchar_t (&d)[N], const wchar_t *s) {
+ static_assert(N > 0, "array cannot be 0-size");
+ winpty_wcsncpy(d, s);
+ d[N - 1] = L'\0';
+ return d;
+}
+
+#endif // WINPTY_SHARED_STRING_UTIL_H
diff --git a/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h b/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h
new file mode 100644
index 0000000000..716a027fcb
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Convenience header library for using the high-resolution performance counter
+// to measure how long some process takes.
+
+#ifndef TIME_MEASUREMENT_H
+#define TIME_MEASUREMENT_H
+
+#include <windows.h>
+#include <assert.h>
+#include <stdint.h>
+
+class TimeMeasurement {
+public:
+ TimeMeasurement() {
+ static double freq = static_cast<double>(getFrequency());
+ m_freq = freq;
+ m_start = value();
+ }
+
+ double elapsed() {
+ uint64_t elapsedTicks = value() - m_start;
+ return static_cast<double>(elapsedTicks) / m_freq;
+ }
+
+private:
+ uint64_t getFrequency() {
+ LARGE_INTEGER freq;
+ BOOL success = QueryPerformanceFrequency(&freq);
+ assert(success && "QueryPerformanceFrequency failed");
+ return freq.QuadPart;
+ }
+
+ uint64_t value() {
+ LARGE_INTEGER ret;
+ BOOL success = QueryPerformanceCounter(&ret);
+ assert(success && "QueryPerformanceCounter failed");
+ return ret.QuadPart;
+ }
+
+ uint64_t m_start;
+ double m_freq;
+};
+
+#endif // TIME_MEASUREMENT_H
diff --git a/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h b/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h
new file mode 100644
index 0000000000..39dfa62ec9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_CTRL_CHARS_H
+#define UNIX_CTRL_CHARS_H
+
+inline char decodeUnixCtrlChar(char ch) {
+ const char ctrlKeys[] = {
+ /* 0x00 */ '@', /* 0x01 */ 'A', /* 0x02 */ 'B', /* 0x03 */ 'C',
+ /* 0x04 */ 'D', /* 0x05 */ 'E', /* 0x06 */ 'F', /* 0x07 */ 'G',
+ /* 0x08 */ 'H', /* 0x09 */ 'I', /* 0x0A */ 'J', /* 0x0B */ 'K',
+ /* 0x0C */ 'L', /* 0x0D */ 'M', /* 0x0E */ 'N', /* 0x0F */ 'O',
+ /* 0x10 */ 'P', /* 0x11 */ 'Q', /* 0x12 */ 'R', /* 0x13 */ 'S',
+ /* 0x14 */ 'T', /* 0x15 */ 'U', /* 0x16 */ 'V', /* 0x17 */ 'W',
+ /* 0x18 */ 'X', /* 0x19 */ 'Y', /* 0x1A */ 'Z', /* 0x1B */ '[',
+ /* 0x1C */ '\\', /* 0x1D */ ']', /* 0x1E */ '^', /* 0x1F */ '_',
+ };
+ unsigned char uch = ch;
+ if (uch < 32) {
+ return ctrlKeys[uch];
+ } else if (uch == 127) {
+ return '?';
+ } else {
+ return '\0';
+ }
+}
+
+#endif // UNIX_CTRL_CHARS_H
diff --git a/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat b/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat
new file mode 100644
index 0000000000..ea2a7d64ed
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -- Echo the git commit hash. If git isn't available for some reason,
+rem -- output nothing instead.
+
+mkdir ..\gen 2>nul
+
+set /p VERSION=<..\..\VERSION.txt
+set COMMIT=%1
+
+echo // AUTO-GENERATED BY %0 %*>..\gen\GenVersion.h
+echo const char GenVersion_Version[] = "%VERSION%";>>..\gen\GenVersion.h
+echo const char GenVersion_Commit[] = "%COMMIT%";>>..\gen\GenVersion.h
+
+rem -- The winpty.gyp file expects the script to output the include directory,
+rem -- relative to src.
+echo gen
+
+rem -- Set ERRORLEVEL to 0 using this cryptic syntax.
+(call )
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc
new file mode 100644
index 0000000000..711a8637c8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc
@@ -0,0 +1,460 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WindowsSecurity.h"
+
+#include <array>
+
+#include "DebugClient.h"
+#include "OsModule.h"
+#include "OwnedHandle.h"
+#include "StringBuilder.h"
+#include "WindowsVersion.h"
+#include "WinptyAssert.h"
+#include "WinptyException.h"
+
+namespace {
+
+struct LocalFreer {
+ void operator()(void *ptr) {
+ if (ptr != nullptr) {
+ LocalFree(reinterpret_cast<HLOCAL>(ptr));
+ }
+ }
+};
+
+typedef std::unique_ptr<void, LocalFreer> PointerLocal;
+
+template <typename T>
+SecurityItem<T> localItem(typename T::type v) {
+ typedef typename T::type P;
+ struct Impl : SecurityItem<T>::Impl {
+ P m_v;
+ Impl(P v) : m_v(v) {}
+ virtual ~Impl() {
+ LocalFree(reinterpret_cast<HLOCAL>(m_v));
+ }
+ };
+ return SecurityItem<T>(v, std::unique_ptr<Impl>(new Impl { v }));
+}
+
+Sid allocatedSid(PSID v) {
+ struct Impl : Sid::Impl {
+ PSID m_v;
+ Impl(PSID v) : m_v(v) {}
+ virtual ~Impl() {
+ if (m_v != nullptr) {
+ FreeSid(m_v);
+ }
+ }
+ };
+ return Sid(v, std::unique_ptr<Impl>(new Impl { v }));
+}
+
+} // anonymous namespace
+
+// Returns a handle to the thread's effective security token. If the thread
+// is impersonating another user, its token is returned, and otherwise, the
+// process' security token is opened. The handle is opened with TOKEN_QUERY.
+static OwnedHandle openSecurityTokenForQuery() {
+ HANDLE token = nullptr;
+ // It is unclear to me whether OpenAsSelf matters for winpty, or what the
+ // most appropriate value is.
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY,
+ /*OpenAsSelf=*/FALSE, &token)) {
+ if (GetLastError() != ERROR_NO_TOKEN) {
+ throwWindowsError(L"OpenThreadToken failed");
+ }
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
+ throwWindowsError(L"OpenProcessToken failed");
+ }
+ }
+ ASSERT(token != nullptr &&
+ "OpenThreadToken/OpenProcessToken token is NULL");
+ return OwnedHandle(token);
+}
+
+// Returns the TokenOwner of the thread's effective security token.
+Sid getOwnerSid() {
+ struct Impl : Sid::Impl {
+ std::unique_ptr<char[]> buffer;
+ };
+
+ OwnedHandle token = openSecurityTokenForQuery();
+ DWORD actual = 0;
+ BOOL success;
+ success = GetTokenInformation(token.get(), TokenOwner,
+ nullptr, 0, &actual);
+ if (success) {
+ throwWinptyException(L"getOwnerSid: GetTokenInformation: "
+ L"expected ERROR_INSUFFICIENT_BUFFER");
+ } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ throwWindowsError(L"getOwnerSid: GetTokenInformation: "
+ L"expected ERROR_INSUFFICIENT_BUFFER");
+ }
+ std::unique_ptr<Impl> impl(new Impl);
+ impl->buffer = std::unique_ptr<char[]>(new char[actual]);
+ success = GetTokenInformation(token.get(), TokenOwner,
+ impl->buffer.get(), actual, &actual);
+ if (!success) {
+ throwWindowsError(L"getOwnerSid: GetTokenInformation");
+ }
+ TOKEN_OWNER tmp;
+ ASSERT(actual >= sizeof(tmp));
+ std::copy(
+ impl->buffer.get(),
+ impl->buffer.get() + sizeof(tmp),
+ reinterpret_cast<char*>(&tmp));
+ return Sid(tmp.Owner, std::move(impl));
+}
+
+Sid wellKnownSid(
+ const wchar_t *debuggingName,
+ SID_IDENTIFIER_AUTHORITY authority,
+ BYTE authorityCount,
+ DWORD subAuthority0/*=0*/,
+ DWORD subAuthority1/*=0*/) {
+ PSID psid = nullptr;
+ if (!AllocateAndInitializeSid(&authority, authorityCount,
+ subAuthority0,
+ subAuthority1,
+ 0, 0, 0, 0, 0, 0,
+ &psid)) {
+ const auto err = GetLastError();
+ const auto msg =
+ std::wstring(L"wellKnownSid: error getting ") +
+ debuggingName + L" SID";
+ throwWindowsError(msg.c_str(), err);
+ }
+ return allocatedSid(psid);
+}
+
+Sid builtinAdminsSid() {
+ // S-1-5-32-544
+ SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
+ return wellKnownSid(L"BUILTIN\\Administrators group",
+ authority, 2,
+ SECURITY_BUILTIN_DOMAIN_RID, // 32
+ DOMAIN_ALIAS_RID_ADMINS); // 544
+}
+
+Sid localSystemSid() {
+ // S-1-5-18
+ SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
+ return wellKnownSid(L"LocalSystem account",
+ authority, 1,
+ SECURITY_LOCAL_SYSTEM_RID); // 18
+}
+
+Sid everyoneSid() {
+ // S-1-1-0
+ SID_IDENTIFIER_AUTHORITY authority = { SECURITY_WORLD_SID_AUTHORITY };
+ return wellKnownSid(L"Everyone account",
+ authority, 1,
+ SECURITY_WORLD_RID); // 0
+}
+
+static SecurityDescriptor finishSecurityDescriptor(
+ size_t daclEntryCount,
+ EXPLICIT_ACCESSW *daclEntries,
+ Acl &outAcl) {
+ {
+ PACL aclRaw = nullptr;
+ DWORD aclError =
+ SetEntriesInAclW(daclEntryCount,
+ daclEntries,
+ nullptr, &aclRaw);
+ if (aclError != ERROR_SUCCESS) {
+ WStringBuilder sb(64);
+ sb << L"finishSecurityDescriptor: "
+ << L"SetEntriesInAcl failed: " << aclError;
+ throwWinptyException(sb.c_str());
+ }
+ outAcl = localItem<AclTag>(aclRaw);
+ }
+
+ const PSECURITY_DESCRIPTOR sdRaw =
+ reinterpret_cast<PSECURITY_DESCRIPTOR>(
+ LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
+ if (sdRaw == nullptr) {
+ throwWinptyException(L"finishSecurityDescriptor: LocalAlloc failed");
+ }
+ SecurityDescriptor sd = localItem<SecurityDescriptorTag>(sdRaw);
+ if (!InitializeSecurityDescriptor(sdRaw, SECURITY_DESCRIPTOR_REVISION)) {
+ throwWindowsError(
+ L"finishSecurityDescriptor: InitializeSecurityDescriptor");
+ }
+ if (!SetSecurityDescriptorDacl(sdRaw, TRUE, outAcl.get(), FALSE)) {
+ throwWindowsError(
+ L"finishSecurityDescriptor: SetSecurityDescriptorDacl");
+ }
+
+ return std::move(sd);
+}
+
+// Create a security descriptor that grants full control to the local system
+// account, built-in administrators, and the owner.
+SecurityDescriptor
+createPipeSecurityDescriptorOwnerFullControl() {
+
+ struct Impl : SecurityDescriptor::Impl {
+ Sid localSystem;
+ Sid builtinAdmins;
+ Sid owner;
+ std::array<EXPLICIT_ACCESSW, 3> daclEntries = {};
+ Acl dacl;
+ SecurityDescriptor value;
+ };
+
+ std::unique_ptr<Impl> impl(new Impl);
+ impl->localSystem = localSystemSid();
+ impl->builtinAdmins = builtinAdminsSid();
+ impl->owner = getOwnerSid();
+
+ for (auto &ea : impl->daclEntries) {
+ ea.grfAccessPermissions = GENERIC_ALL;
+ ea.grfAccessMode = SET_ACCESS;
+ ea.grfInheritance = NO_INHERITANCE;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ }
+ impl->daclEntries[0].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->localSystem.get());
+ impl->daclEntries[1].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
+ impl->daclEntries[2].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->owner.get());
+
+ impl->value = finishSecurityDescriptor(
+ impl->daclEntries.size(),
+ impl->daclEntries.data(),
+ impl->dacl);
+
+ const auto retValue = impl->value.get();
+ return SecurityDescriptor(retValue, std::move(impl));
+}
+
+SecurityDescriptor
+createPipeSecurityDescriptorOwnerFullControlEveryoneWrite() {
+
+ struct Impl : SecurityDescriptor::Impl {
+ Sid localSystem;
+ Sid builtinAdmins;
+ Sid owner;
+ Sid everyone;
+ std::array<EXPLICIT_ACCESSW, 4> daclEntries = {};
+ Acl dacl;
+ SecurityDescriptor value;
+ };
+
+ std::unique_ptr<Impl> impl(new Impl);
+ impl->localSystem = localSystemSid();
+ impl->builtinAdmins = builtinAdminsSid();
+ impl->owner = getOwnerSid();
+ impl->everyone = everyoneSid();
+
+ for (auto &ea : impl->daclEntries) {
+ ea.grfAccessPermissions = GENERIC_ALL;
+ ea.grfAccessMode = SET_ACCESS;
+ ea.grfInheritance = NO_INHERITANCE;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ }
+ impl->daclEntries[0].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->localSystem.get());
+ impl->daclEntries[1].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
+ impl->daclEntries[2].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->owner.get());
+ impl->daclEntries[3].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->everyone.get());
+ // Avoid using FILE_GENERIC_WRITE because it includes FILE_APPEND_DATA,
+ // which is equal to FILE_CREATE_PIPE_INSTANCE. Instead, include all the
+ // flags that comprise FILE_GENERIC_WRITE, except for the one.
+ impl->daclEntries[3].grfAccessPermissions =
+ FILE_GENERIC_READ |
+ FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA |
+ STANDARD_RIGHTS_WRITE | SYNCHRONIZE;
+
+ impl->value = finishSecurityDescriptor(
+ impl->daclEntries.size(),
+ impl->daclEntries.data(),
+ impl->dacl);
+
+ const auto retValue = impl->value.get();
+ return SecurityDescriptor(retValue, std::move(impl));
+}
+
+SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle) {
+ PACL dacl = nullptr;
+ PSECURITY_DESCRIPTOR sd = nullptr;
+ const DWORD errCode = GetSecurityInfo(handle, SE_KERNEL_OBJECT,
+ OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, &dacl, nullptr, &sd);
+ if (errCode != ERROR_SUCCESS) {
+ throwWindowsError(L"GetSecurityInfo failed");
+ }
+ return localItem<SecurityDescriptorTag>(sd);
+}
+
+// The (SID/SD)<->string conversion APIs are useful for testing/debugging, so
+// create convenient accessor functions for them. They're too slow for
+// ordinary use. The APIs exist in XP and up, but the MinGW headers only
+// declare the SID<->string APIs, not the SD APIs. MinGW also gets the
+// prototype wrong for ConvertStringSidToSidW (LPWSTR instead of LPCWSTR) and
+// requires WINVER to be defined. MSVC and MinGW-w64 get everything right, but
+// for consistency, use LoadLibrary/GetProcAddress for all four APIs.
+
+typedef BOOL WINAPI ConvertStringSidToSidW_t(
+ LPCWSTR StringSid,
+ PSID *Sid);
+
+typedef BOOL WINAPI ConvertSidToStringSidW_t(
+ PSID Sid,
+ LPWSTR *StringSid);
+
+typedef BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW_t(
+ LPCWSTR StringSecurityDescriptor,
+ DWORD StringSDRevision,
+ PSECURITY_DESCRIPTOR *SecurityDescriptor,
+ PULONG SecurityDescriptorSize);
+
+typedef BOOL WINAPI ConvertSecurityDescriptorToStringSecurityDescriptorW_t(
+ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ DWORD RequestedStringSDRevision,
+ SECURITY_INFORMATION SecurityInformation,
+ LPWSTR *StringSecurityDescriptor,
+ PULONG StringSecurityDescriptorLen);
+
+#define GET_MODULE_PROC(mod, funcName) \
+ const auto p##funcName = \
+ reinterpret_cast<funcName##_t*>( \
+ mod.proc(#funcName)); \
+ if (p##funcName == nullptr) { \
+ throwWinptyException( \
+ L"" L ## #funcName L" API is missing from ADVAPI32.DLL"); \
+ }
+
+const DWORD kSDDL_REVISION_1 = 1;
+
+std::wstring sidToString(PSID sid) {
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertSidToStringSidW);
+ wchar_t *sidString = NULL;
+ BOOL success = pConvertSidToStringSidW(sid, &sidString);
+ if (!success) {
+ throwWindowsError(L"ConvertSidToStringSidW failed");
+ }
+ PointerLocal freer(sidString);
+ return std::wstring(sidString);
+}
+
+Sid stringToSid(const std::wstring &str) {
+ // Cast the string from const wchar_t* to LPWSTR because the function is
+ // incorrectly prototyped in the MinGW sddl.h header. The API does not
+ // modify the string -- it is correctly prototyped as taking LPCWSTR in
+ // MinGW-w64, MSVC, and MSDN.
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertStringSidToSidW);
+ PSID psid = nullptr;
+ BOOL success = pConvertStringSidToSidW(const_cast<LPWSTR>(str.c_str()),
+ &psid);
+ if (!success) {
+ const auto err = GetLastError();
+ throwWindowsError(
+ (std::wstring(L"ConvertStringSidToSidW failed on \"") +
+ str + L'"').c_str(),
+ err);
+ }
+ return localItem<SidTag>(psid);
+}
+
+SecurityDescriptor stringToSd(const std::wstring &str) {
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertStringSecurityDescriptorToSecurityDescriptorW);
+ PSECURITY_DESCRIPTOR desc = nullptr;
+ if (!pConvertStringSecurityDescriptorToSecurityDescriptorW(
+ str.c_str(), kSDDL_REVISION_1, &desc, nullptr)) {
+ const auto err = GetLastError();
+ throwWindowsError(
+ (std::wstring(L"ConvertStringSecurityDescriptorToSecurityDescriptorW failed on \"") +
+ str + L'"').c_str(),
+ err);
+ }
+ return localItem<SecurityDescriptorTag>(desc);
+}
+
+std::wstring sdToString(PSECURITY_DESCRIPTOR sd) {
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertSecurityDescriptorToStringSecurityDescriptorW);
+ wchar_t *sdString = nullptr;
+ if (!pConvertSecurityDescriptorToStringSecurityDescriptorW(
+ sd,
+ kSDDL_REVISION_1,
+ OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION,
+ &sdString,
+ nullptr)) {
+ throwWindowsError(
+ L"ConvertSecurityDescriptorToStringSecurityDescriptor failed");
+ }
+ PointerLocal freer(sdString);
+ return std::wstring(sdString);
+}
+
+// Vista added a useful flag to CreateNamedPipe, PIPE_REJECT_REMOTE_CLIENTS,
+// that rejects remote connections. Return this flag on Vista, or return 0
+// otherwise.
+DWORD rejectRemoteClientsPipeFlag() {
+ if (isAtLeastWindowsVista()) {
+ // MinGW lacks this flag; MinGW-w64 has it.
+ const DWORD kPIPE_REJECT_REMOTE_CLIENTS = 8;
+ return kPIPE_REJECT_REMOTE_CLIENTS;
+ } else {
+ trace("Omitting PIPE_REJECT_REMOTE_CLIENTS on pre-Vista OS");
+ return 0;
+ }
+}
+
+typedef BOOL WINAPI GetNamedPipeClientProcessId_t(
+ HANDLE Pipe,
+ PULONG ClientProcessId);
+
+std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD>
+getNamedPipeClientProcessId(HANDLE serverPipe) {
+ OsModule kernel32(L"kernel32.dll");
+ const auto pGetNamedPipeClientProcessId =
+ reinterpret_cast<GetNamedPipeClientProcessId_t*>(
+ kernel32.proc("GetNamedPipeClientProcessId"));
+ if (pGetNamedPipeClientProcessId == nullptr) {
+ return std::make_tuple(
+ GetNamedPipeClientProcessId_Result::UnsupportedOs, 0, 0);
+ }
+ ULONG pid = 0;
+ if (!pGetNamedPipeClientProcessId(serverPipe, &pid)) {
+ return std::make_tuple(
+ GetNamedPipeClientProcessId_Result::Failure, 0, GetLastError());
+ }
+ return std::make_tuple(
+ GetNamedPipeClientProcessId_Result::Success,
+ static_cast<DWORD>(pid),
+ 0);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h
new file mode 100644
index 0000000000..5f9d53aff6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_WINDOWS_SECURITY_H
+#define WINPTY_WINDOWS_SECURITY_H
+
+#include <windows.h>
+#include <aclapi.h>
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+
+// PSID and PSECURITY_DESCRIPTOR are both pointers to void, but we want
+// Sid and SecurityDescriptor to be different types.
+struct SidTag { typedef PSID type; };
+struct AclTag { typedef PACL type; };
+struct SecurityDescriptorTag { typedef PSECURITY_DESCRIPTOR type; };
+
+template <typename T>
+class SecurityItem {
+public:
+ struct Impl {
+ virtual ~Impl() {}
+ };
+
+private:
+ typedef typename T::type P;
+ P m_v;
+ std::unique_ptr<Impl> m_pimpl;
+
+public:
+ P get() const { return m_v; }
+ operator bool() const { return m_v != nullptr; }
+
+ SecurityItem() : m_v(nullptr) {}
+ SecurityItem(P v, std::unique_ptr<Impl> &&pimpl) :
+ m_v(v), m_pimpl(std::move(pimpl)) {}
+ SecurityItem(SecurityItem &&other) :
+ m_v(other.m_v), m_pimpl(std::move(other.m_pimpl)) {
+ other.m_v = nullptr;
+ }
+ SecurityItem &operator=(SecurityItem &&other) {
+ m_v = other.m_v;
+ other.m_v = nullptr;
+ m_pimpl = std::move(other.m_pimpl);
+ return *this;
+ }
+};
+
+typedef SecurityItem<SidTag> Sid;
+typedef SecurityItem<AclTag> Acl;
+typedef SecurityItem<SecurityDescriptorTag> SecurityDescriptor;
+
+Sid getOwnerSid();
+Sid wellKnownSid(
+ const wchar_t *debuggingName,
+ SID_IDENTIFIER_AUTHORITY authority,
+ BYTE authorityCount,
+ DWORD subAuthority0=0,
+ DWORD subAuthority1=0);
+Sid builtinAdminsSid();
+Sid localSystemSid();
+Sid everyoneSid();
+
+SecurityDescriptor createPipeSecurityDescriptorOwnerFullControl();
+SecurityDescriptor createPipeSecurityDescriptorOwnerFullControlEveryoneWrite();
+SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle);
+
+std::wstring sidToString(PSID sid);
+Sid stringToSid(const std::wstring &str);
+SecurityDescriptor stringToSd(const std::wstring &str);
+std::wstring sdToString(PSECURITY_DESCRIPTOR sd);
+
+DWORD rejectRemoteClientsPipeFlag();
+
+enum class GetNamedPipeClientProcessId_Result {
+ Success,
+ Failure,
+ UnsupportedOs,
+};
+
+std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD>
+getNamedPipeClientProcessId(HANDLE serverPipe);
+
+#endif // WINPTY_WINDOWS_SECURITY_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc
new file mode 100644
index 0000000000..d89b00d838
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WindowsVersion.h"
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "DebugClient.h"
+#include "OsModule.h"
+#include "StringBuilder.h"
+#include "StringUtil.h"
+#include "WinptyAssert.h"
+#include "WinptyException.h"
+
+namespace {
+
+typedef std::tuple<DWORD, DWORD> Version;
+
+// This function can only return a version up to 6.2 unless the executable is
+// manifested for a newer version of Windows. See the MSDN documentation for
+// GetVersionEx.
+OSVERSIONINFOEX getWindowsVersionInfo() {
+ // Allow use of deprecated functions (i.e. GetVersionEx). We need to use
+ // GetVersionEx for the old MinGW toolchain and with MSVC when it targets XP.
+ // Having two code paths makes code harder to test, and it's not obvious how
+ // to detect the presence of a new enough SDK. (Including ntverp.h and
+ // examining VER_PRODUCTBUILD apparently works, but even then, MinGW-w64 and
+ // MSVC seem to use different version numbers.)
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4996)
+#endif
+ OSVERSIONINFOEX info = {};
+ info.dwOSVersionInfoSize = sizeof(info);
+ const auto success = GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
+ ASSERT(success && "GetVersionEx failed");
+ return info;
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+}
+
+Version getWindowsVersion() {
+ const auto info = getWindowsVersionInfo();
+ return Version(info.dwMajorVersion, info.dwMinorVersion);
+}
+
+struct ModuleNotFound : WinptyException {
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return L"ModuleNotFound";
+ }
+};
+
+// Throws WinptyException on error.
+std::wstring getSystemDirectory() {
+ wchar_t systemDirectory[MAX_PATH];
+ const UINT size = GetSystemDirectoryW(systemDirectory, MAX_PATH);
+ if (size == 0) {
+ throwWindowsError(L"GetSystemDirectory failed");
+ } else if (size >= MAX_PATH) {
+ throwWinptyException(
+ L"GetSystemDirectory: path is longer than MAX_PATH");
+ }
+ return systemDirectory;
+}
+
+#define GET_VERSION_DLL_API(name) \
+ const auto p ## name = \
+ reinterpret_cast<decltype(name)*>( \
+ versionDll.proc(#name)); \
+ if (p ## name == nullptr) { \
+ throwWinptyException(L ## #name L" is missing"); \
+ }
+
+// Throws WinptyException on error.
+VS_FIXEDFILEINFO getFixedFileInfo(const std::wstring &path) {
+ // version.dll is not a conventional KnownDll, so if we link to it, there's
+ // a danger of accidentally loading a malicious DLL. In a more typical
+ // application, perhaps we'd guard against this security issue by
+ // controlling which directories this code runs in (e.g. *not* the
+ // "Downloads" directory), but that's harder for the winpty library.
+ OsModule versionDll(
+ (getSystemDirectory() + L"\\version.dll").c_str(),
+ OsModule::LoadErrorBehavior::Throw);
+ GET_VERSION_DLL_API(GetFileVersionInfoSizeW);
+ GET_VERSION_DLL_API(GetFileVersionInfoW);
+ GET_VERSION_DLL_API(VerQueryValueW);
+ DWORD size = pGetFileVersionInfoSizeW(path.c_str(), nullptr);
+ if (!size) {
+ // I see ERROR_FILE_NOT_FOUND on Win7 and
+ // ERROR_RESOURCE_DATA_NOT_FOUND on WinXP.
+ if (GetLastError() == ERROR_FILE_NOT_FOUND ||
+ GetLastError() == ERROR_RESOURCE_DATA_NOT_FOUND) {
+ throw ModuleNotFound();
+ } else {
+ throwWindowsError(
+ (L"GetFileVersionInfoSizeW failed on " + path).c_str());
+ }
+ }
+ std::unique_ptr<char[]> versionBuffer(new char[size]);
+ if (!pGetFileVersionInfoW(path.c_str(), 0, size, versionBuffer.get())) {
+ throwWindowsError((L"GetFileVersionInfoW failed on " + path).c_str());
+ }
+ VS_FIXEDFILEINFO *versionInfo = nullptr;
+ UINT versionInfoSize = 0;
+ if (!pVerQueryValueW(
+ versionBuffer.get(), L"\\",
+ reinterpret_cast<void**>(&versionInfo), &versionInfoSize) ||
+ versionInfo == nullptr ||
+ versionInfoSize != sizeof(VS_FIXEDFILEINFO) ||
+ versionInfo->dwSignature != 0xFEEF04BD) {
+ throwWinptyException((L"VerQueryValueW failed on " + path).c_str());
+ }
+ return *versionInfo;
+}
+
+uint64_t productVersionFromInfo(const VS_FIXEDFILEINFO &info) {
+ return (static_cast<uint64_t>(info.dwProductVersionMS) << 32) |
+ (static_cast<uint64_t>(info.dwProductVersionLS));
+}
+
+uint64_t fileVersionFromInfo(const VS_FIXEDFILEINFO &info) {
+ return (static_cast<uint64_t>(info.dwFileVersionMS) << 32) |
+ (static_cast<uint64_t>(info.dwFileVersionLS));
+}
+
+std::string versionToString(uint64_t version) {
+ StringBuilder b(32);
+ b << ((uint16_t)(version >> 48));
+ b << '.';
+ b << ((uint16_t)(version >> 32));
+ b << '.';
+ b << ((uint16_t)(version >> 16));
+ b << '.';
+ b << ((uint16_t)(version >> 0));
+ return b.str_moved();
+}
+
+} // anonymous namespace
+
+// Returns true for Windows Vista (or Windows Server 2008) or newer.
+bool isAtLeastWindowsVista() {
+ return getWindowsVersion() >= Version(6, 0);
+}
+
+// Returns true for Windows 7 (or Windows Server 2008 R2) or newer.
+bool isAtLeastWindows7() {
+ return getWindowsVersion() >= Version(6, 1);
+}
+
+// Returns true for Windows 8 (or Windows Server 2012) or newer.
+bool isAtLeastWindows8() {
+ return getWindowsVersion() >= Version(6, 2);
+}
+
+#define WINPTY_IA32 1
+#define WINPTY_X64 2
+
+#if defined(_M_IX86) || defined(__i386__)
+#define WINPTY_ARCH WINPTY_IA32
+#elif defined(_M_X64) || defined(__x86_64__)
+#define WINPTY_ARCH WINPTY_X64
+#endif
+
+typedef BOOL WINAPI IsWow64Process_t(HANDLE hProcess, PBOOL Wow64Process);
+
+void dumpWindowsVersion() {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ const auto info = getWindowsVersionInfo();
+ StringBuilder b;
+ b << info.dwMajorVersion << '.' << info.dwMinorVersion
+ << '.' << info.dwBuildNumber << ' '
+ << "SP" << info.wServicePackMajor << '.' << info.wServicePackMinor
+ << ' ';
+ switch (info.wProductType) {
+ case VER_NT_WORKSTATION: b << "Client"; break;
+ case VER_NT_DOMAIN_CONTROLLER: b << "DomainController"; break;
+ case VER_NT_SERVER: b << "Server"; break;
+ default:
+ b << "product=" << info.wProductType; break;
+ }
+ b << ' ';
+#if WINPTY_ARCH == WINPTY_IA32
+ b << "IA32";
+ OsModule kernel32(L"kernel32.dll");
+ IsWow64Process_t *pIsWow64Process =
+ reinterpret_cast<IsWow64Process_t*>(
+ kernel32.proc("IsWow64Process"));
+ if (pIsWow64Process != nullptr) {
+ BOOL result = false;
+ const BOOL success = pIsWow64Process(GetCurrentProcess(), &result);
+ if (!success) {
+ b << " WOW64:error";
+ } else if (success && result) {
+ b << " WOW64";
+ }
+ } else {
+ b << " WOW64:missingapi";
+ }
+#elif WINPTY_ARCH == WINPTY_X64
+ b << "X64";
+#endif
+ const auto dllVersion = [](const wchar_t *dllPath) -> std::string {
+ try {
+ const auto info = getFixedFileInfo(dllPath);
+ StringBuilder fb(64);
+ fb << utf8FromWide(dllPath) << ':';
+ fb << "F:" << versionToString(fileVersionFromInfo(info)) << '/'
+ << "P:" << versionToString(productVersionFromInfo(info));
+ return fb.str_moved();
+ } catch (const ModuleNotFound&) {
+ return utf8FromWide(dllPath) + ":none";
+ } catch (const WinptyException &e) {
+ trace("Error getting %s version: %s",
+ utf8FromWide(dllPath).c_str(), utf8FromWide(e.what()).c_str());
+ return utf8FromWide(dllPath) + ":error";
+ }
+ };
+ b << ' ' << dllVersion(L"kernel32.dll");
+ // ConEmu provides a DLL that hooks many Windows APIs, especially console
+ // APIs. Its existence and version number could be useful in debugging.
+#if WINPTY_ARCH == WINPTY_IA32
+ b << ' ' << dllVersion(L"ConEmuHk.dll");
+#elif WINPTY_ARCH == WINPTY_X64
+ b << ' ' << dllVersion(L"ConEmuHk64.dll");
+#endif
+ trace("Windows version: %s", b.c_str());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h
new file mode 100644
index 0000000000..a80798417e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_WINDOWS_VERSION_H
+#define WINPTY_SHARED_WINDOWS_VERSION_H
+
+bool isAtLeastWindowsVista();
+bool isAtLeastWindows7();
+bool isAtLeastWindows8();
+void dumpWindowsVersion();
+
+#endif // WINPTY_SHARED_WINDOWS_VERSION_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc
new file mode 100644
index 0000000000..1ff0de475a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WinptyAssert.h"
+
+#include <windows.h>
+#include <stdlib.h>
+
+#include "DebugClient.h"
+
+void assertTrace(const char *file, int line, const char *cond) {
+ trace("Assertion failed: %s, file %s, line %d",
+ cond, file, line);
+}
+
+#ifdef WINPTY_AGENT_ASSERT
+
+void agentShutdown() {
+ HWND hwnd = GetConsoleWindow();
+ if (hwnd != NULL) {
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+ Sleep(30000);
+ trace("Agent shutdown: WM_CLOSE did not end agent process");
+ } else {
+ trace("Agent shutdown: GetConsoleWindow() is NULL");
+ }
+ // abort() prints a message to the console, and if it is frozen, then the
+ // process would hang, so instead use exit(). (We shouldn't ever get here,
+ // though, because the WM_CLOSE message should have ended this process.)
+ exit(1);
+}
+
+void agentAssertFail(const char *file, int line, const char *cond) {
+ assertTrace(file, line, cond);
+ agentShutdown();
+}
+
+#endif
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h
new file mode 100644
index 0000000000..b2b8b5e64c
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_ASSERT_H
+#define WINPTY_ASSERT_H
+
+#ifdef WINPTY_AGENT_ASSERT
+
+void agentShutdown();
+void agentAssertFail(const char *file, int line, const char *cond);
+
+// Calling the standard assert() function does not work in the agent because
+// the error message would be printed to the console, and the only way the
+// user can see the console is via a working agent! Moreover, the console may
+// be frozen, so attempting to write to it would block forever. This custom
+// assert function instead sends the message to the DebugServer, then attempts
+// to close the console, then quietly exits.
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ agentAssertFail(__FILE__, __LINE__, #cond); \
+ } \
+ } while(0)
+
+#else
+
+void assertTrace(const char *file, int line, const char *cond);
+
+// In the other targets, log the assert failure to the debugserver, then fail
+// using the ordinary assert mechanism. In case assert is compiled out, fail
+// using abort. The amount of code inlined is unfortunate, but asserts aren't
+// used much outside the agent.
+#include <assert.h>
+#include <stdlib.h>
+#define ASSERT_CONDITION(cond) (false && (cond))
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ assertTrace(__FILE__, __LINE__, #cond); \
+ assert(ASSERT_CONDITION(#cond)); \
+ abort(); \
+ } \
+ } while(0)
+
+#endif
+
+#endif // WINPTY_ASSERT_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyException.cc b/src/libs/3rdparty/winpty/src/shared/WinptyException.cc
new file mode 100644
index 0000000000..d0d48823d2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyException.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WinptyException.h"
+
+#include <memory>
+#include <string>
+
+#include "StringBuilder.h"
+
+namespace {
+
+class ExceptionImpl : public WinptyException {
+public:
+ ExceptionImpl(const wchar_t *what) :
+ m_what(std::make_shared<std::wstring>(what)) {}
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return m_what->c_str();
+ }
+private:
+ // Using a shared_ptr ensures that copying the object raises no exception.
+ std::shared_ptr<std::wstring> m_what;
+};
+
+} // anonymous namespace
+
+void throwWinptyException(const wchar_t *what) {
+ throw ExceptionImpl(what);
+}
+
+void throwWindowsError(const wchar_t *prefix, DWORD errorCode) {
+ WStringBuilder sb(64);
+ if (prefix != nullptr) {
+ sb << prefix << L": ";
+ }
+ // It might make sense to use FormatMessage here, but IIRC, its API is hard
+ // to figure out.
+ sb << L"Windows error " << errorCode;
+ throwWinptyException(sb.c_str());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyException.h b/src/libs/3rdparty/winpty/src/shared/WinptyException.h
new file mode 100644
index 0000000000..ec353369e5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyException.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_EXCEPTION_H
+#define WINPTY_EXCEPTION_H
+
+#include <windows.h>
+
+#if defined(__GNUC__)
+#define WINPTY_NOEXCEPT noexcept
+#elif defined(_MSC_VER) && _MSC_VER >= 1900
+#define WINPTY_NOEXCEPT noexcept
+#else
+#define WINPTY_NOEXCEPT
+#endif
+
+class WinptyException {
+public:
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT = 0;
+ virtual ~WinptyException() {}
+};
+
+void throwWinptyException(const wchar_t *what);
+void throwWindowsError(const wchar_t *prefix, DWORD error=GetLastError());
+
+#endif // WINPTY_EXCEPTION_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc
new file mode 100644
index 0000000000..76bb8a584d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WinptyVersion.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "DebugClient.h"
+
+// This header is auto-generated by either the Makefile (Unix) or
+// UpdateGenVersion.bat (gyp). It is placed in a 'gen' directory, which is
+// added to the search path.
+#include "GenVersion.h"
+
+void dumpVersionToStdout() {
+ printf("winpty version %s\n", GenVersion_Version);
+ printf("commit %s\n", GenVersion_Commit);
+}
+
+void dumpVersionToTrace() {
+ trace("winpty version %s (commit %s)",
+ GenVersion_Version,
+ GenVersion_Commit);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h
new file mode 100644
index 0000000000..e6224d7b84
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_VERSION_H
+#define WINPTY_VERSION_H
+
+void dumpVersionToStdout();
+void dumpVersionToTrace();
+
+#endif // WINPTY_VERSION_H
diff --git a/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h b/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h
new file mode 100644
index 0000000000..e716f245e8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SNPRINTF_H
+#define WINPTY_SNPRINTF_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "WinptyAssert.h"
+
+#if defined(__CYGWIN__) || defined(__MSYS__)
+#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) \
+ __attribute__((format(printf, (fmtarg), ((vararg)))))
+#elif defined(__GNUC__)
+#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) \
+ __attribute__((format(ms_printf, (fmtarg), ((vararg)))))
+#else
+#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg)
+#endif
+
+// Returns a value between 0 and size - 1 (inclusive) on success. Returns -1
+// on failure (including truncation). The output buffer is always
+// NUL-terminated.
+inline int
+winpty_vsnprintf(char *out, size_t size, const char *fmt, va_list ap) {
+ ASSERT(size > 0);
+ out[0] = '\0';
+#if defined(_MSC_VER) && _MSC_VER < 1900
+ // MSVC 2015 added a C99-conforming vsnprintf.
+ int count = _vsnprintf_s(out, size, _TRUNCATE, fmt, ap);
+#else
+ // MinGW configurations frequently provide a vsnprintf function that simply
+ // calls one of the MS _vsnprintf* functions, which are not C99 conformant.
+ int count = vsnprintf(out, size, fmt, ap);
+#endif
+ if (count < 0 || static_cast<size_t>(count) >= size) {
+ // On truncation, some *printf* implementations return the
+ // non-truncated size, but other implementations returns -1. Return
+ // -1 for consistency.
+ count = -1;
+ // Guarantee NUL termination.
+ out[size - 1] = '\0';
+ } else {
+ // Guarantee NUL termination.
+ out[count] = '\0';
+ }
+ return count;
+}
+
+// Wraps winpty_vsnprintf.
+inline int winpty_snprintf(char *out, size_t size, const char *fmt, ...)
+ WINPTY_SNPRINTF_FORMAT(3, 4);
+inline int winpty_snprintf(char *out, size_t size, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ const int count = winpty_vsnprintf(out, size, fmt, ap);
+ va_end(ap);
+ return count;
+}
+
+// Wraps winpty_vsnprintf with automatic size determination.
+template <size_t size>
+int winpty_vsnprintf(char (&out)[size], const char *fmt, va_list ap) {
+ return winpty_vsnprintf(out, size, fmt, ap);
+}
+
+// Wraps winpty_vsnprintf with automatic size determination.
+template <size_t size>
+int winpty_snprintf(char (&out)[size], const char *fmt, ...)
+ WINPTY_SNPRINTF_FORMAT(2, 3);
+template <size_t size>
+int winpty_snprintf(char (&out)[size], const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ const int count = winpty_vsnprintf(out, size, fmt, ap);
+ va_end(ap);
+ return count;
+}
+
+#endif // WINPTY_SNPRINTF_H
diff --git a/src/libs/3rdparty/winpty/src/subdir.mk b/src/libs/3rdparty/winpty/src/subdir.mk
new file mode 100644
index 0000000000..9ae8031b08
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/subdir.mk
@@ -0,0 +1,5 @@
+include src/agent/subdir.mk
+include src/debugserver/subdir.mk
+include src/libwinpty/subdir.mk
+include src/tests/subdir.mk
+include src/unix-adapter/subdir.mk
diff --git a/src/libs/3rdparty/winpty/src/tests/subdir.mk b/src/libs/3rdparty/winpty/src/tests/subdir.mk
new file mode 100644
index 0000000000..18799c4a5a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/tests/subdir.mk
@@ -0,0 +1,28 @@
+# Copyright (c) 2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+build/%.exe : src/tests/%.cc build/winpty.dll
+ $(info Building $@)
+ @$(MINGW_CXX) $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS) -o $@ $^
+
+TEST_PROGRAMS = \
+ build/trivial_test.exe
+
+-include $(TEST_PROGRAMS:.exe=.d)
diff --git a/src/libs/3rdparty/winpty/src/tests/trivial_test.cc b/src/libs/3rdparty/winpty/src/tests/trivial_test.cc
new file mode 100644
index 0000000000..2188a4befb
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/tests/trivial_test.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <windows.h>
+
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cwchar>
+#include <vector>
+
+#include "../include/winpty.h"
+#include "../shared/DebugClient.h"
+
+static std::vector<unsigned char> filterContent(
+ const std::vector<unsigned char> &content) {
+ std::vector<unsigned char> result;
+ auto it = content.begin();
+ const auto itEnd = content.end();
+ while (it < itEnd) {
+ if (*it == '\r') {
+ // Filter out carriage returns. Sometimes the output starts with
+ // a single CR; other times, it has multiple CRs.
+ it++;
+ } else if (*it == '\x1b' && (it + 1) < itEnd && *(it + 1) == '[') {
+ // Filter out escape sequences. They have no interior letters and
+ // end with a single letter.
+ it += 2;
+ while (it < itEnd && !isalpha(*it)) {
+ it++;
+ }
+ it++;
+ } else {
+ // Let everything else through.
+ result.push_back(*it);
+ it++;
+ }
+ }
+ return result;
+}
+
+// Read bytes from the non-overlapped file handle until the file is closed or
+// until an I/O error occurs.
+static std::vector<unsigned char> readAll(HANDLE handle) {
+ unsigned char buf[1024];
+ std::vector<unsigned char> result;
+ while (true) {
+ DWORD amount = 0;
+ BOOL ret = ReadFile(handle, buf, sizeof(buf), &amount, nullptr);
+ if (!ret || amount == 0) {
+ break;
+ }
+ result.insert(result.end(), buf, buf + amount);
+ }
+ return result;
+}
+
+static void parentTest() {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(nullptr, program, 1024);
+
+ {
+ // XXX: We'd like to use swprintf, which is part of C99 and takes a
+ // size_t maxlen argument. MinGW-w64 has this function, as does MSVC.
+ // The old MinGW doesn't, though -- instead, it apparently provides an
+ // swprintf taking no maxlen argument. This *might* be a regression?
+ // (There is also no swnprintf, but that function is obsolescent with a
+ // correct swprintf, and it isn't in POSIX or ISO C.)
+ //
+ // Visual C++ 6 also provided this non-conformant swprintf, and I'm
+ // guessing MSVCRT.DLL does too. (My impression is that the old MinGW
+ // prefers to rely on MSVCRT.DLL for convenience?)
+ //
+ // I could compile differently for old MinGW, but what if it fixes its
+ // function later? Instead, use a workaround. It's starting to make
+ // sense to drop MinGW support in favor of MinGW-w64. This is too
+ // annoying.
+ //
+ // grepbait: OLD-MINGW / WINPTY_TARGET_MSYS1
+ cmdline[0] = L'\0';
+ wcscat(cmdline, L"\"");
+ wcscat(cmdline, program);
+ wcscat(cmdline, L"\" CHILD");
+ }
+ // swnprintf(cmdline, sizeof(cmdline) / sizeof(cmdline[0]),
+ // L"\"%ls\" CHILD", program);
+
+ auto agentCfg = winpty_config_new(0, nullptr);
+ assert(agentCfg != nullptr);
+ auto pty = winpty_open(agentCfg, nullptr);
+ assert(pty != nullptr);
+ winpty_config_free(agentCfg);
+
+ HANDLE conin = CreateFileW(
+ winpty_conin_name(pty),
+ GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
+ HANDLE conout = CreateFileW(
+ winpty_conout_name(pty),
+ GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
+ assert(conin != INVALID_HANDLE_VALUE);
+ assert(conout != INVALID_HANDLE_VALUE);
+
+ auto spawnCfg = winpty_spawn_config_new(
+ WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, program, cmdline,
+ nullptr, nullptr, nullptr);
+ assert(spawnCfg != nullptr);
+ HANDLE process = nullptr;
+ BOOL spawnSuccess = winpty_spawn(
+ pty, spawnCfg, &process, nullptr, nullptr, nullptr);
+ assert(spawnSuccess && process != nullptr);
+
+ auto content = readAll(conout);
+ content = filterContent(content);
+
+ std::vector<unsigned char> expectedContent = {
+ 'H', 'I', '\n', 'X', 'Y', '\n'
+ };
+ DWORD exitCode = 0;
+ assert(GetExitCodeProcess(process, &exitCode) && exitCode == 42);
+ CloseHandle(process);
+ CloseHandle(conin);
+ CloseHandle(conout);
+ assert(content == expectedContent);
+ winpty_free(pty);
+}
+
+static void childTest() {
+ printf("HI\nXY\n");
+ exit(42);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ parentTest();
+ } else {
+ childTest();
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc
new file mode 100644
index 0000000000..39f1e09685
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "InputHandler.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "../shared/DebugClient.h"
+#include "Util.h"
+#include "WakeupFd.h"
+
+InputHandler::InputHandler(
+ HANDLE conin, int inputfd, WakeupFd &completionWakeup) :
+ m_conin(conin),
+ m_inputfd(inputfd),
+ m_completionWakeup(completionWakeup),
+ m_threadHasBeenJoined(false),
+ m_shouldShutdown(0),
+ m_threadCompleted(0)
+{
+ pthread_create(&m_thread, NULL, InputHandler::threadProcS, this);
+}
+
+void InputHandler::shutdown() {
+ startShutdown();
+ if (!m_threadHasBeenJoined) {
+ int ret = pthread_join(m_thread, NULL);
+ assert(ret == 0 && "pthread_join failed");
+ m_threadHasBeenJoined = true;
+ }
+}
+
+void InputHandler::threadProc() {
+ std::vector<char> buffer(4096);
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ while (true) {
+ // Handle shutdown.
+ m_wakeup.reset();
+ if (m_shouldShutdown) {
+ trace("InputHandler: shutting down");
+ break;
+ }
+
+ // Block until data arrives.
+ {
+ const int max_fd = std::max(m_inputfd, m_wakeup.fd());
+ FD_SET(m_inputfd, &readfds);
+ FD_SET(m_wakeup.fd(), &readfds);
+ selectWrapper("InputHandler", max_fd + 1, &readfds);
+ if (!FD_ISSET(m_inputfd, &readfds)) {
+ continue;
+ }
+ }
+
+ const int numRead = read(m_inputfd, &buffer[0], buffer.size());
+ if (numRead == -1 && errno == EINTR) {
+ // Apparently, this read is interrupted on Cygwin 1.7 by a SIGWINCH
+ // signal even though I set the SA_RESTART flag on the handler.
+ continue;
+ }
+
+ // tty is closed, or the read failed for some unexpected reason.
+ if (numRead <= 0) {
+ trace("InputHandler: tty read failed: numRead=%d", numRead);
+ break;
+ }
+
+ DWORD written = 0;
+ BOOL ret = WriteFile(m_conin,
+ &buffer[0], numRead,
+ &written, NULL);
+ if (!ret || written != static_cast<DWORD>(numRead)) {
+ if (!ret && GetLastError() == ERROR_BROKEN_PIPE) {
+ trace("InputHandler: pipe closed: written=%u",
+ static_cast<unsigned int>(written));
+ } else {
+ trace("InputHandler: write failed: "
+ "ret=%d lastError=0x%x numRead=%d written=%u",
+ ret,
+ static_cast<unsigned int>(GetLastError()),
+ numRead,
+ static_cast<unsigned int>(written));
+ }
+ break;
+ }
+ }
+ m_threadCompleted = 1;
+ m_completionWakeup.set();
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h
new file mode 100644
index 0000000000..9c3f540d63
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_INPUT_HANDLER_H
+#define UNIX_ADAPTER_INPUT_HANDLER_H
+
+#include <windows.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "WakeupFd.h"
+
+// Connect a Cygwin blocking fd to winpty CONIN.
+class InputHandler {
+public:
+ InputHandler(HANDLE conin, int inputfd, WakeupFd &completionWakeup);
+ ~InputHandler() { shutdown(); }
+ bool isComplete() { return m_threadCompleted; }
+ void startShutdown() { m_shouldShutdown = 1; m_wakeup.set(); }
+ void shutdown();
+
+private:
+ static void *threadProcS(void *pvthis) {
+ reinterpret_cast<InputHandler*>(pvthis)->threadProc();
+ return NULL;
+ }
+ void threadProc();
+
+ HANDLE m_conin;
+ int m_inputfd;
+ pthread_t m_thread;
+ WakeupFd &m_completionWakeup;
+ WakeupFd m_wakeup;
+ bool m_threadHasBeenJoined;
+ volatile sig_atomic_t m_shouldShutdown;
+ volatile sig_atomic_t m_threadCompleted;
+};
+
+#endif // UNIX_ADAPTER_INPUT_HANDLER_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc
new file mode 100644
index 0000000000..573b8adced
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "OutputHandler.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "../shared/DebugClient.h"
+#include "Util.h"
+#include "WakeupFd.h"
+
+OutputHandler::OutputHandler(
+ HANDLE conout, int outputfd, WakeupFd &completionWakeup) :
+ m_conout(conout),
+ m_outputfd(outputfd),
+ m_completionWakeup(completionWakeup),
+ m_threadHasBeenJoined(false),
+ m_threadCompleted(0)
+{
+ pthread_create(&m_thread, NULL, OutputHandler::threadProcS, this);
+}
+
+void OutputHandler::shutdown() {
+ if (!m_threadHasBeenJoined) {
+ int ret = pthread_join(m_thread, NULL);
+ assert(ret == 0 && "pthread_join failed");
+ m_threadHasBeenJoined = true;
+ }
+}
+
+void OutputHandler::threadProc() {
+ std::vector<char> buffer(4096);
+ while (true) {
+ DWORD numRead = 0;
+ BOOL ret = ReadFile(m_conout,
+ &buffer[0], buffer.size(),
+ &numRead, NULL);
+ if (!ret || numRead == 0) {
+ if (!ret && GetLastError() == ERROR_BROKEN_PIPE) {
+ trace("OutputHandler: pipe closed: numRead=%u",
+ static_cast<unsigned int>(numRead));
+ } else {
+ trace("OutputHandler: read failed: "
+ "ret=%d lastError=0x%x numRead=%u",
+ ret,
+ static_cast<unsigned int>(GetLastError()),
+ static_cast<unsigned int>(numRead));
+ }
+ break;
+ }
+ if (!writeAll(m_outputfd, &buffer[0], numRead)) {
+ break;
+ }
+ }
+ m_threadCompleted = 1;
+ m_completionWakeup.set();
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h
new file mode 100644
index 0000000000..48241c5538
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_OUTPUT_HANDLER_H
+#define UNIX_ADAPTER_OUTPUT_HANDLER_H
+
+#include <windows.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "WakeupFd.h"
+
+// Connect winpty CONOUT/CONERR to a Cygwin blocking fd.
+class OutputHandler {
+public:
+ OutputHandler(HANDLE conout, int outputfd, WakeupFd &completionWakeup);
+ ~OutputHandler() { shutdown(); }
+ bool isComplete() { return m_threadCompleted; }
+ void shutdown();
+
+private:
+ static void *threadProcS(void *pvthis) {
+ reinterpret_cast<OutputHandler*>(pvthis)->threadProc();
+ return NULL;
+ }
+ void threadProc();
+
+ HANDLE m_conout;
+ int m_outputfd;
+ pthread_t m_thread;
+ WakeupFd &m_completionWakeup;
+ bool m_threadHasBeenJoined;
+ volatile sig_atomic_t m_threadCompleted;
+};
+
+#endif // UNIX_ADAPTER_OUTPUT_HANDLER_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc b/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc
new file mode 100644
index 0000000000..e13f84a529
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Util.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../shared/DebugClient.h"
+
+// Write the entire buffer, restarting it as necessary.
+bool writeAll(int fd, const void *buffer, size_t size) {
+ size_t written = 0;
+ while (written < size) {
+ int ret = write(fd,
+ reinterpret_cast<const char*>(buffer) + written,
+ size - written);
+ if (ret == -1 && errno == EINTR) {
+ continue;
+ }
+ if (ret <= 0) {
+ trace("write failed: "
+ "fd=%d errno=%d size=%u written=%d ret=%d",
+ fd,
+ errno,
+ static_cast<unsigned int>(size),
+ static_cast<unsigned int>(written),
+ ret);
+ return false;
+ }
+ assert(static_cast<size_t>(ret) <= size - written);
+ written += ret;
+ }
+ assert(written == size);
+ return true;
+}
+
+bool writeStr(int fd, const char *str) {
+ return writeAll(fd, str, strlen(str));
+}
+
+void selectWrapper(const char *diagName, int nfds, fd_set *readfds) {
+ int ret = select(nfds, readfds, NULL, NULL, NULL);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ FD_ZERO(readfds);
+ return;
+ }
+#ifdef WINPTY_TARGET_MSYS1
+ // The select system call sometimes fails with EAGAIN instead of EINTR.
+ // This apparantly only happens with the old Cygwin fork "MSYS" used in
+ // the mingw.org project. select is not supposed to fail with EAGAIN,
+ // and EAGAIN does not make much sense as an error code. (The whole
+ // point of select is to block.)
+ if (errno == EAGAIN) {
+ trace("%s select returned EAGAIN: interpreting like EINTR",
+ diagName);
+ FD_ZERO(readfds);
+ return;
+ }
+#endif
+ fprintf(stderr, "Internal error: %s select failed: "
+ "error %d", diagName, errno);
+ abort();
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/Util.h b/src/libs/3rdparty/winpty/src/unix-adapter/Util.h
new file mode 100644
index 0000000000..cadb4c82a9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/Util.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_UTIL_H
+#define UNIX_ADAPTER_UTIL_H
+
+#include <stdlib.h>
+#include <sys/select.h>
+
+bool writeAll(int fd, const void *buffer, size_t size);
+bool writeStr(int fd, const char *str);
+void selectWrapper(const char *diagName, int nfds, fd_set *readfds);
+
+#endif // UNIX_ADAPTER_UTIL_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc
new file mode 100644
index 0000000000..6b47379015
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WakeupFd.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void setFdNonBlock(int fd) {
+ int status = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, status | O_NONBLOCK);
+}
+
+WakeupFd::WakeupFd() {
+ int pipeFd[2];
+ if (pipe(pipeFd) != 0) {
+ perror("Could not create internal wakeup pipe");
+ abort();
+ }
+ m_pipeReadFd = pipeFd[0];
+ m_pipeWriteFd = pipeFd[1];
+ setFdNonBlock(m_pipeReadFd);
+ setFdNonBlock(m_pipeWriteFd);
+}
+
+WakeupFd::~WakeupFd() {
+ close(m_pipeReadFd);
+ close(m_pipeWriteFd);
+}
+
+void WakeupFd::set() {
+ char dummy = 0;
+ int ret;
+ do {
+ ret = write(m_pipeWriteFd, &dummy, 1);
+ } while (ret < 0 && errno == EINTR);
+}
+
+void WakeupFd::reset() {
+ char tmpBuf[256];
+ while (true) {
+ int amount = read(m_pipeReadFd, tmpBuf, sizeof(tmpBuf));
+ if (amount < 0 && errno == EAGAIN) {
+ break;
+ } else if (amount <= 0) {
+ perror("error reading from internal wakeup pipe");
+ abort();
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h
new file mode 100644
index 0000000000..dd8d362aa1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_WAKEUP_FD_H
+#define UNIX_ADAPTER_WAKEUP_FD_H
+
+class WakeupFd {
+public:
+ WakeupFd();
+ ~WakeupFd();
+ int fd() { return m_pipeReadFd; }
+ void set();
+ void reset();
+
+private:
+ // Do not allow copying the WakeupFd object.
+ WakeupFd(const WakeupFd &other);
+ WakeupFd &operator=(const WakeupFd &other);
+
+private:
+ int m_pipeReadFd;
+ int m_pipeWriteFd;
+};
+
+#endif // UNIX_ADAPTER_WAKEUP_FD_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/main.cc b/src/libs/3rdparty/winpty/src/unix-adapter/main.cc
new file mode 100644
index 0000000000..992cb70e44
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/main.cc
@@ -0,0 +1,729 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// MSYS's sys/cygwin.h header only declares cygwin_internal if WINVER is
+// defined, which is defined in windows.h. Therefore, include windows.h early.
+#include <windows.h>
+
+#include <assert.h>
+#include <cygwin/version.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/cygwin.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <winpty.h>
+#include "../shared/DebugClient.h"
+#include "../shared/UnixCtrlChars.h"
+#include "../shared/WinptyVersion.h"
+#include "InputHandler.h"
+#include "OutputHandler.h"
+#include "Util.h"
+#include "WakeupFd.h"
+
+#define CSI "\x1b["
+
+static WakeupFd *g_mainWakeup = NULL;
+
+static WakeupFd &mainWakeup()
+{
+ if (g_mainWakeup == NULL) {
+ static const char msg[] = "Internal error: g_mainWakeup is NULL\r\n";
+ write(STDERR_FILENO, msg, sizeof(msg) - 1);
+ abort();
+ }
+ return *g_mainWakeup;
+}
+
+struct SavedTermiosMode {
+ int count;
+ bool valid[3];
+ termios mode[3];
+};
+
+// Put the input terminal into non-canonical mode.
+static SavedTermiosMode setRawTerminalMode(
+ bool allowNonTtys, bool setStdout, bool setStderr)
+{
+ SavedTermiosMode ret;
+ const char *const kNames[3] = { "stdin", "stdout", "stderr" };
+
+ ret.valid[0] = true;
+ ret.valid[1] = setStdout;
+ ret.valid[2] = setStderr;
+
+ for (int i = 0; i < 3; ++i) {
+ if (!ret.valid[i]) {
+ continue;
+ }
+ if (!isatty(i)) {
+ ret.valid[i] = false;
+ if (!allowNonTtys) {
+ fprintf(stderr, "%s is not a tty\n", kNames[i]);
+ exit(1);
+ }
+ } else {
+ ret.valid[i] = true;
+ if (tcgetattr(i, &ret.mode[i]) < 0) {
+ perror("tcgetattr failed");
+ exit(1);
+ }
+ }
+ }
+
+ if (ret.valid[STDIN_FILENO]) {
+ termios buf;
+ if (tcgetattr(STDIN_FILENO, &buf) < 0) {
+ perror("tcgetattr failed");
+ exit(1);
+ }
+ buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ buf.c_cflag &= ~(CSIZE | PARENB);
+ buf.c_cflag |= CS8;
+ buf.c_cc[VMIN] = 1; // blocking read
+ buf.c_cc[VTIME] = 0;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf) < 0) {
+ fprintf(stderr, "tcsetattr failed\n");
+ exit(1);
+ }
+ }
+
+ for (int i = STDOUT_FILENO; i <= STDERR_FILENO; ++i) {
+ if (!ret.valid[i]) {
+ continue;
+ }
+ termios buf;
+ if (tcgetattr(i, &buf) < 0) {
+ perror("tcgetattr failed");
+ exit(1);
+ }
+ buf.c_cflag &= ~(CSIZE | PARENB);
+ buf.c_cflag |= CS8;
+ buf.c_oflag &= ~OPOST;
+ if (tcsetattr(i, TCSAFLUSH, &buf) < 0) {
+ fprintf(stderr, "tcsetattr failed\n");
+ exit(1);
+ }
+ }
+
+ return ret;
+}
+
+static void restoreTerminalMode(const SavedTermiosMode &original)
+{
+ for (int i = 0; i < 3; ++i) {
+ if (!original.valid[i]) {
+ continue;
+ }
+ if (tcsetattr(i, TCSAFLUSH, &original.mode[i]) < 0) {
+ perror("error restoring terminal mode");
+ exit(1);
+ }
+ }
+}
+
+static void debugShowKey(bool allowNonTtys)
+{
+ printf("\nPress any keys -- Ctrl-D exits\n\n");
+ const SavedTermiosMode saved =
+ setRawTerminalMode(allowNonTtys, false, false);
+ char buf[128];
+ while (true) {
+ const ssize_t len = read(STDIN_FILENO, buf, sizeof(buf));
+ if (len <= 0) {
+ break;
+ }
+ for (int i = 0; i < len; ++i) {
+ char ctrl = decodeUnixCtrlChar(buf[i]);
+ if (ctrl == '\0') {
+ putchar(buf[i]);
+ } else {
+ putchar('^');
+ putchar(ctrl);
+ }
+ }
+ for (int i = 0; i < len; ++i) {
+ unsigned char uch = buf[i];
+ printf("\t%3d %04o 0x%02x\n", uch, uch, uch);
+ fflush(stdout);
+ }
+ if (buf[0] == 4) {
+ // Ctrl-D
+ break;
+ }
+ }
+ restoreTerminalMode(saved);
+}
+
+static void terminalResized(int signo)
+{
+ mainWakeup().set();
+}
+
+static void registerResizeSignalHandler()
+{
+ struct sigaction resizeSigAct;
+ memset(&resizeSigAct, 0, sizeof(resizeSigAct));
+ resizeSigAct.sa_handler = terminalResized;
+ resizeSigAct.sa_flags = SA_RESTART;
+ sigaction(SIGWINCH, &resizeSigAct, NULL);
+}
+
+// Convert the path to a Win32 path if it is a POSIX path, and convert slashes
+// to backslashes.
+static std::string convertPosixPathToWin(const std::string &path)
+{
+ char *tmp;
+#if defined(CYGWIN_VERSION_CYGWIN_CONV) && \
+ CYGWIN_VERSION_API_MINOR >= CYGWIN_VERSION_CYGWIN_CONV
+ // MSYS2 and versions of Cygwin released after 2009 or so use this API.
+ // The original MSYS still lacks this API.
+ ssize_t newSize = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
+ path.c_str(), NULL, 0);
+ assert(newSize >= 0);
+ tmp = new char[newSize + 1];
+ ssize_t success = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
+ path.c_str(), tmp, newSize + 1);
+ assert(success == 0);
+#else
+ // In the current Cygwin header file, this API is documented as deprecated
+ // because it's restricted to paths of MAX_PATH length. In the CVS version
+ // of MSYS, the newer API doesn't exist, and this older API is implemented
+ // using msys_p2w, which seems like it would handle paths larger than
+ // MAX_PATH, but there's no way to query how large the new path is.
+ // Hopefully, this is large enough.
+ tmp = new char[MAX_PATH + path.size()];
+ cygwin_conv_to_win32_path(path.c_str(), tmp);
+#endif
+ for (int i = 0; tmp[i] != '\0'; ++i) {
+ if (tmp[i] == '/')
+ tmp[i] = '\\';
+ }
+ std::string ret(tmp);
+ delete [] tmp;
+ return ret;
+}
+
+static std::string resolvePath(const std::string &path)
+{
+ char ret[PATH_MAX];
+ ret[0] = '\0';
+ if (realpath(path.c_str(), ret) != ret) {
+ return std::string();
+ }
+ return ret;
+}
+
+template <size_t N>
+static bool endsWith(const std::string &path, const char (&suf)[N])
+{
+ const size_t suffixLen = N - 1;
+ char actualSuf[N];
+ if (path.size() < suffixLen) {
+ return false;
+ }
+ strcpy(actualSuf, &path.c_str()[path.size() - suffixLen]);
+ for (size_t i = 0; i < suffixLen; ++i) {
+ actualSuf[i] = tolower(actualSuf[i]);
+ }
+ return !strcmp(actualSuf, suf);
+}
+
+static std::string findProgram(
+ const char *winptyProgName,
+ const std::string &prog)
+{
+ std::string candidate;
+ if (prog.find('/') == std::string::npos &&
+ prog.find('\\') == std::string::npos) {
+ // XXX: It would be nice to use a lambda here (once/if old MSYS support
+ // is dropped).
+ // Search the PATH.
+ const char *const pathVar = getenv("PATH");
+ const std::string pathList(pathVar ? pathVar : "");
+ size_t elpos = 0;
+ while (true) {
+ const size_t elend = pathList.find(':', elpos);
+ candidate = pathList.substr(elpos, elend - elpos);
+ if (!candidate.empty() && *(candidate.end() - 1) != '/') {
+ candidate += '/';
+ }
+ candidate += prog;
+ candidate = resolvePath(candidate);
+ if (!candidate.empty()) {
+ int perm = X_OK;
+ if (endsWith(candidate, ".bat") || endsWith(candidate, ".cmd")) {
+#ifdef __MSYS__
+ // In MSYS/MSYS2, batch files don't have the execute bit
+ // set, so just check that they're readable.
+ perm = R_OK;
+#endif
+ } else if (endsWith(candidate, ".com") || endsWith(candidate, ".exe")) {
+ // Do nothing.
+ } else {
+ // Make the exe extension explicit so that we don't try to
+ // run shell scripts with CreateProcess/winpty_spawn.
+ candidate += ".exe";
+ }
+ if (!access(candidate.c_str(), perm)) {
+ break;
+ }
+ }
+ if (elend == std::string::npos) {
+ fprintf(stderr, "%s: error: cannot start '%s': Not found in PATH\n",
+ winptyProgName, prog.c_str());
+ exit(1);
+ } else {
+ elpos = elend + 1;
+ }
+ }
+ } else {
+ candidate = resolvePath(prog);
+ if (candidate.empty()) {
+ std::string errstr(strerror(errno));
+ fprintf(stderr, "%s: error: cannot start '%s': %s\n",
+ winptyProgName, prog.c_str(), errstr.c_str());
+ exit(1);
+ }
+ }
+ return convertPosixPathToWin(candidate);
+}
+
+// Convert argc/argv into a Win32 command-line following the escaping convention
+// documented on MSDN. (e.g. see CommandLineToArgvW documentation)
+static std::string argvToCommandLine(const std::vector<std::string> &argv)
+{
+ std::string result;
+ for (size_t argIndex = 0; argIndex < argv.size(); ++argIndex) {
+ if (argIndex > 0)
+ result.push_back(' ');
+ const char *arg = argv[argIndex].c_str();
+ const bool quote =
+ strchr(arg, ' ') != NULL ||
+ strchr(arg, '\t') != NULL ||
+ *arg == '\0';
+ if (quote)
+ result.push_back('\"');
+ int bsCount = 0;
+ for (const char *p = arg; *p != '\0'; ++p) {
+ if (*p == '\\') {
+ bsCount++;
+ } else if (*p == '\"') {
+ result.append(bsCount * 2 + 1, '\\');
+ result.push_back('\"');
+ bsCount = 0;
+ } else {
+ result.append(bsCount, '\\');
+ bsCount = 0;
+ result.push_back(*p);
+ }
+ }
+ if (quote) {
+ result.append(bsCount * 2, '\\');
+ result.push_back('\"');
+ } else {
+ result.append(bsCount, '\\');
+ }
+ }
+ return result;
+}
+
+static wchar_t *heapMbsToWcs(const char *text)
+{
+ // Calling mbstowcs with a NULL first argument seems to be broken on MSYS.
+ // Instead of returning the size of the converted string, it returns 0.
+ // Using strlen(text) * 2 is probably big enough.
+ size_t maxLen = strlen(text) * 2 + 1;
+ wchar_t *ret = new wchar_t[maxLen];
+ size_t len = mbstowcs(ret, text, maxLen);
+ assert(len != (size_t)-1 && len < maxLen);
+ return ret;
+}
+
+static char *heapWcsToMbs(const wchar_t *text)
+{
+ // Calling wcstombs with a NULL first argument seems to be broken on MSYS.
+ // Instead of returning the size of the converted string, it returns 0.
+ // Using wcslen(text) * 3 is big enough for UTF-8 and probably other
+ // encodings. For UTF-8, codepoints that fit in a single wchar
+ // (U+0000 to U+FFFF) are encoded using 1-3 bytes. The remaining code
+ // points needs two wchar's and are encoded using 4 bytes.
+ size_t maxLen = wcslen(text) * 3 + 1;
+ char *ret = new char[maxLen];
+ size_t len = wcstombs(ret, text, maxLen);
+ if (len == (size_t)-1 || len >= maxLen) {
+ delete [] ret;
+ return NULL;
+ } else {
+ return ret;
+ }
+}
+
+static std::string wcsToMbs(const wchar_t *text)
+{
+ std::string ret;
+ const char *ptr = heapWcsToMbs(text);
+ if (ptr != NULL) {
+ ret = ptr;
+ delete [] ptr;
+ }
+ return ret;
+}
+
+void setupWin32Environment()
+{
+ std::map<std::string, std::string> varsToCopy;
+ const char *vars[] = {
+ "WINPTY_DEBUG",
+ "WINPTY_SHOW_CONSOLE",
+ NULL
+ };
+ for (int i = 0; vars[i] != NULL; ++i) {
+ const char *cstr = getenv(vars[i]);
+ if (cstr != NULL && cstr[0] != '\0') {
+ varsToCopy[vars[i]] = cstr;
+ }
+ }
+
+#if defined(__MSYS__) && CYGWIN_VERSION_API_MINOR >= 48 || \
+ !defined(__MSYS__) && CYGWIN_VERSION_API_MINOR >= 153
+ // Use CW_SYNC_WINENV to copy the Unix environment to the Win32
+ // environment. The command performs special translation on some variables
+ // (such as PATH and TMP). It also copies the debugging environment
+ // variables.
+ //
+ // Note that the API minor versions have diverged in Cygwin and MSYS.
+ // CW_SYNC_WINENV was added to Cygwin in version 153. (Cygwin's
+ // include/cygwin/version.h says that CW_SETUP_WINENV was added in 153.
+ // The flag was renamed 8 days after it was added, but the API docs weren't
+ // updated.) The flag was added to MSYS in version 48.
+ //
+ // Also, in my limited testing, this call seems to be necessary with Cygwin
+ // but unnecessary with MSYS. Perhaps MSYS is automatically syncing the
+ // Unix environment with the Win32 environment before starting console.exe?
+ // It shouldn't hurt to call it for MSYS.
+ cygwin_internal(CW_SYNC_WINENV);
+#endif
+
+ // Copy debugging environment variables from the Cygwin environment
+ // to the Win32 environment so the agent will inherit it.
+ for (std::map<std::string, std::string>::iterator it = varsToCopy.begin();
+ it != varsToCopy.end();
+ ++it) {
+ wchar_t *nameW = heapMbsToWcs(it->first.c_str());
+ wchar_t *valueW = heapMbsToWcs(it->second.c_str());
+ SetEnvironmentVariableW(nameW, valueW);
+ delete [] nameW;
+ delete [] valueW;
+ }
+
+ // Clear the TERM variable. The child process's immediate console/terminal
+ // environment is a Windows console, not the terminal that winpty is
+ // communicating with. Leaving the TERM variable set can break programs in
+ // various ways. (e.g. arrows keys broken in Cygwin less, IronPython's
+ // help(...) function doesn't start, misc programs decide they should
+ // output color escape codes on pre-Win10). See
+ // https://github.com/rprichard/winpty/issues/43.
+ SetEnvironmentVariableW(L"TERM", NULL);
+}
+
+static void usage(const char *program, int exitCode)
+{
+ printf("Usage: %s [options] [--] program [args]\n", program);
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help Show this help message\n");
+ printf(" --mouse Enable terminal mouse input\n");
+ printf(" --showkey Dump STDIN escape sequences\n");
+ printf(" --version Show the winpty version number\n");
+ exit(exitCode);
+}
+
+struct Arguments {
+ std::vector<std::string> childArgv;
+ bool mouseInput;
+ bool testAllowNonTtys;
+ bool testConerr;
+ bool testPlainOutput;
+ bool testColorEscapes;
+};
+
+static void parseArguments(int argc, char *argv[], Arguments &out)
+{
+ out.mouseInput = false;
+ out.testAllowNonTtys = false;
+ out.testConerr = false;
+ out.testPlainOutput = false;
+ out.testColorEscapes = false;
+ bool doShowKeys = false;
+ const char *const program = argc >= 1 ? argv[0] : "<program>";
+ int argi = 1;
+ while (argi < argc) {
+ std::string arg(argv[argi++]);
+ if (arg.size() >= 1 && arg[0] == '-') {
+ if (arg == "-h" || arg == "--help") {
+ usage(program, 0);
+ } else if (arg == "--mouse") {
+ out.mouseInput = true;
+ } else if (arg == "--showkey") {
+ doShowKeys = true;
+ } else if (arg == "--version") {
+ dumpVersionToStdout();
+ exit(0);
+ } else if (arg == "-Xallow-non-tty") {
+ out.testAllowNonTtys = true;
+ } else if (arg == "-Xconerr") {
+ out.testConerr = true;
+ } else if (arg == "-Xplain") {
+ out.testPlainOutput = true;
+ } else if (arg == "-Xcolor") {
+ out.testColorEscapes = true;
+ } else if (arg == "--") {
+ break;
+ } else {
+ fprintf(stderr, "Error: unrecognized option: '%s'\n",
+ arg.c_str());
+ exit(1);
+ }
+ } else {
+ out.childArgv.push_back(arg);
+ break;
+ }
+ }
+ for (; argi < argc; ++argi) {
+ out.childArgv.push_back(argv[argi]);
+ }
+ if (doShowKeys) {
+ debugShowKey(out.testAllowNonTtys);
+ exit(0);
+ }
+ if (out.childArgv.size() == 0) {
+ usage(program, 1);
+ }
+}
+
+static std::string errorMessageToString(DWORD err)
+{
+ // Use FormatMessageW rather than FormatMessageA, because we want to use
+ // wcstombs to convert to the Cygwin locale, which might not match the
+ // codepage FormatMessageA would use. We need to convert using wcstombs,
+ // rather than print using %ls, because %ls doesn't work in the original
+ // MSYS.
+ wchar_t *wideMsgPtr = NULL;
+ const DWORD formatRet = FormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<wchar_t*>(&wideMsgPtr),
+ 0,
+ NULL);
+ if (formatRet == 0 || wideMsgPtr == NULL) {
+ return std::string();
+ }
+ std::string msg = wcsToMbs(wideMsgPtr);
+ LocalFree(wideMsgPtr);
+ const size_t pos = msg.find_last_not_of(" \r\n\t");
+ if (pos == std::string::npos) {
+ msg.clear();
+ } else {
+ msg.erase(pos + 1);
+ }
+ return msg;
+}
+
+static std::string formatErrorMessage(DWORD err)
+{
+ char buf[64];
+ sprintf(buf, "error %#x", static_cast<unsigned int>(err));
+ std::string ret = errorMessageToString(err);
+ if (ret.empty()) {
+ ret += buf;
+ } else {
+ ret += " (";
+ ret += buf;
+ ret += ")";
+ }
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ setlocale(LC_ALL, "");
+
+ g_mainWakeup = new WakeupFd();
+
+ Arguments args;
+ parseArguments(argc, argv, args);
+
+ setupWin32Environment();
+
+ winsize sz = { 0 };
+ sz.ws_col = 80;
+ sz.ws_row = 25;
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &sz);
+
+ DWORD agentFlags = WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION;
+ if (args.testConerr) { agentFlags |= WINPTY_FLAG_CONERR; }
+ if (args.testPlainOutput) { agentFlags |= WINPTY_FLAG_PLAIN_OUTPUT; }
+ if (args.testColorEscapes) { agentFlags |= WINPTY_FLAG_COLOR_ESCAPES; }
+ winpty_config_t *agentCfg = winpty_config_new(agentFlags, NULL);
+ assert(agentCfg != NULL);
+ winpty_config_set_initial_size(agentCfg, sz.ws_col, sz.ws_row);
+ if (args.mouseInput) {
+ winpty_config_set_mouse_mode(agentCfg, WINPTY_MOUSE_MODE_FORCE);
+ }
+
+ winpty_error_ptr_t openErr = NULL;
+ winpty_t *wp = winpty_open(agentCfg, &openErr);
+ if (wp == NULL) {
+ fprintf(stderr, "Error creating winpty: %s\n",
+ wcsToMbs(winpty_error_msg(openErr)).c_str());
+ exit(1);
+ }
+ winpty_config_free(agentCfg);
+ winpty_error_free(openErr);
+
+ HANDLE conin = CreateFileW(winpty_conin_name(wp), GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ HANDLE conout = CreateFileW(winpty_conout_name(wp), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ assert(conin != INVALID_HANDLE_VALUE);
+ assert(conout != INVALID_HANDLE_VALUE);
+ HANDLE conerr = NULL;
+ if (args.testConerr) {
+ conerr = CreateFileW(winpty_conerr_name(wp), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ assert(conerr != INVALID_HANDLE_VALUE);
+ }
+
+ HANDLE childHandle = NULL;
+
+ {
+ // Start the child process under the console.
+ args.childArgv[0] = findProgram(argv[0], args.childArgv[0]);
+ std::string cmdLine = argvToCommandLine(args.childArgv);
+ wchar_t *cmdLineW = heapMbsToWcs(cmdLine.c_str());
+
+ winpty_spawn_config_t *spawnCfg = winpty_spawn_config_new(
+ WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN,
+ NULL, cmdLineW, NULL, NULL, NULL);
+ assert(spawnCfg != NULL);
+
+ winpty_error_ptr_t spawnErr = NULL;
+ DWORD lastError = 0;
+ BOOL spawnRet = winpty_spawn(wp, spawnCfg, &childHandle, NULL,
+ &lastError, &spawnErr);
+ winpty_spawn_config_free(spawnCfg);
+
+ if (!spawnRet) {
+ winpty_result_t spawnCode = winpty_error_code(spawnErr);
+ if (spawnCode == WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED) {
+ fprintf(stderr, "%s: error: cannot start '%s': %s\n",
+ argv[0],
+ cmdLine.c_str(),
+ formatErrorMessage(lastError).c_str());
+ } else {
+ fprintf(stderr, "%s: error: cannot start '%s': internal error: %s\n",
+ argv[0],
+ cmdLine.c_str(),
+ wcsToMbs(winpty_error_msg(spawnErr)).c_str());
+ }
+ exit(1);
+ }
+ winpty_error_free(spawnErr);
+ delete [] cmdLineW;
+ }
+
+ registerResizeSignalHandler();
+ SavedTermiosMode mode =
+ setRawTerminalMode(args.testAllowNonTtys, true, args.testConerr);
+
+ InputHandler inputHandler(conin, STDIN_FILENO, mainWakeup());
+ OutputHandler outputHandler(conout, STDOUT_FILENO, mainWakeup());
+ OutputHandler *errorHandler = NULL;
+ if (args.testConerr) {
+ errorHandler = new OutputHandler(conerr, STDERR_FILENO, mainWakeup());
+ }
+
+ while (true) {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(mainWakeup().fd(), &readfds);
+ selectWrapper("main thread", mainWakeup().fd() + 1, &readfds);
+ mainWakeup().reset();
+
+ // Check for terminal resize.
+ {
+ winsize sz2;
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &sz2);
+ if (memcmp(&sz, &sz2, sizeof(sz)) != 0) {
+ sz = sz2;
+ winpty_set_size(wp, sz.ws_col, sz.ws_row, NULL);
+ }
+ }
+
+ // Check for an I/O handler shutting down (possibly indicating that the
+ // child process has exited).
+ if (inputHandler.isComplete() || outputHandler.isComplete() ||
+ (errorHandler != NULL && errorHandler->isComplete())) {
+ break;
+ }
+ }
+
+ // Kill the agent connection. This will kill the agent, closing the CONIN
+ // and CONOUT pipes on the agent pipe, prompting our I/O handler to shut
+ // down.
+ winpty_free(wp);
+
+ inputHandler.shutdown();
+ outputHandler.shutdown();
+ CloseHandle(conin);
+ CloseHandle(conout);
+
+ if (errorHandler != NULL) {
+ errorHandler->shutdown();
+ delete errorHandler;
+ CloseHandle(conerr);
+ }
+
+ restoreTerminalMode(mode);
+
+ DWORD exitCode = 0;
+ if (!GetExitCodeProcess(childHandle, &exitCode)) {
+ exitCode = 1;
+ }
+ CloseHandle(childHandle);
+ return exitCode;
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk b/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk
new file mode 100644
index 0000000000..200193a1b1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk
@@ -0,0 +1,41 @@
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/$(UNIX_ADAPTER_EXE)
+
+$(eval $(call def_unix_target,unix-adapter,))
+
+UNIX_ADAPTER_OBJECTS = \
+ build/unix-adapter/unix-adapter/InputHandler.o \
+ build/unix-adapter/unix-adapter/OutputHandler.o \
+ build/unix-adapter/unix-adapter/Util.o \
+ build/unix-adapter/unix-adapter/WakeupFd.o \
+ build/unix-adapter/unix-adapter/main.o \
+ build/unix-adapter/shared/DebugClient.o \
+ build/unix-adapter/shared/WinptyAssert.o \
+ build/unix-adapter/shared/WinptyVersion.o
+
+build/unix-adapter/shared/WinptyVersion.o : build/gen/GenVersion.h
+
+build/$(UNIX_ADAPTER_EXE) : $(UNIX_ADAPTER_OBJECTS) build/winpty.dll
+ $(info Linking $@)
+ @$(UNIX_CXX) $(UNIX_LDFLAGS) -o $@ $^
+
+-include $(UNIX_ADAPTER_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/winpty.gyp b/src/libs/3rdparty/winpty/src/winpty.gyp
new file mode 100644
index 0000000000..7ee68d55e6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/winpty.gyp
@@ -0,0 +1,206 @@
+{
+ # The MSVC generator is the default. Select the compiler version by
+ # passing -G msvs_version=<ver> to gyp. <ver> is a string like 2013e.
+ # See gyp\pylib\gyp\MSVSVersion.py for sample version strings. You
+ # can also pass configurations.gypi to gyp for 32-bit and 64-bit builds.
+ # See that file for details.
+ #
+ # Pass --format=make to gyp to generate a Makefile instead. The Makefile
+ # can be configured by passing variables to make, e.g.:
+ # make -j4 CXX=i686-w64-mingw32-g++ LDFLAGS="-static -static-libgcc -static-libstdc++"
+
+ 'variables': {
+ 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && GetCommitHash.bat")',
+ },
+ 'target_defaults' : {
+ 'defines' : [
+ 'UNICODE',
+ '_UNICODE',
+ '_WIN32_WINNT=0x0501',
+ 'NOMINMAX',
+ ],
+ 'include_dirs': [
+ # Add the 'src/gen' directory to the include path and force gyp to
+ # run the script (re)generating the version header.
+ '<!(cmd /c "cd shared && UpdateGenVersion.bat <(WINPTY_COMMIT_HASH)")',
+ ],
+ },
+ 'targets' : [
+ {
+ 'target_name' : 'winpty-agent',
+ 'type' : 'executable',
+ 'include_dirs' : [
+ 'include',
+ ],
+ 'defines' : [
+ 'WINPTY_AGENT_ASSERT',
+ ],
+ 'libraries' : [
+ '-ladvapi32',
+ '-lshell32',
+ '-luser32',
+ ],
+ 'msvs_settings': {
+ # Specify this setting here to override a setting from somewhere
+ # else, such as node's common.gypi.
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1', # /EHsc
+ },
+ },
+ 'sources' : [
+ 'agent/Agent.h',
+ 'agent/Agent.cc',
+ 'agent/AgentCreateDesktop.h',
+ 'agent/AgentCreateDesktop.cc',
+ 'agent/ConsoleFont.cc',
+ 'agent/ConsoleFont.h',
+ 'agent/ConsoleInput.cc',
+ 'agent/ConsoleInput.h',
+ 'agent/ConsoleInputReencoding.cc',
+ 'agent/ConsoleInputReencoding.h',
+ 'agent/ConsoleLine.cc',
+ 'agent/ConsoleLine.h',
+ 'agent/Coord.h',
+ 'agent/DebugShowInput.h',
+ 'agent/DebugShowInput.cc',
+ 'agent/DefaultInputMap.h',
+ 'agent/DefaultInputMap.cc',
+ 'agent/DsrSender.h',
+ 'agent/EventLoop.h',
+ 'agent/EventLoop.cc',
+ 'agent/InputMap.h',
+ 'agent/InputMap.cc',
+ 'agent/LargeConsoleRead.h',
+ 'agent/LargeConsoleRead.cc',
+ 'agent/NamedPipe.h',
+ 'agent/NamedPipe.cc',
+ 'agent/Scraper.h',
+ 'agent/Scraper.cc',
+ 'agent/SimplePool.h',
+ 'agent/SmallRect.h',
+ 'agent/Terminal.h',
+ 'agent/Terminal.cc',
+ 'agent/UnicodeEncoding.h',
+ 'agent/Win32Console.cc',
+ 'agent/Win32Console.h',
+ 'agent/Win32ConsoleBuffer.cc',
+ 'agent/Win32ConsoleBuffer.h',
+ 'agent/main.cc',
+ 'shared/AgentMsg.h',
+ 'shared/BackgroundDesktop.h',
+ 'shared/BackgroundDesktop.cc',
+ 'shared/Buffer.h',
+ 'shared/Buffer.cc',
+ 'shared/DebugClient.h',
+ 'shared/DebugClient.cc',
+ 'shared/GenRandom.h',
+ 'shared/GenRandom.cc',
+ 'shared/OsModule.h',
+ 'shared/OwnedHandle.h',
+ 'shared/OwnedHandle.cc',
+ 'shared/StringBuilder.h',
+ 'shared/StringUtil.cc',
+ 'shared/StringUtil.h',
+ 'shared/UnixCtrlChars.h',
+ 'shared/WindowsSecurity.cc',
+ 'shared/WindowsSecurity.h',
+ 'shared/WindowsVersion.h',
+ 'shared/WindowsVersion.cc',
+ 'shared/WinptyAssert.h',
+ 'shared/WinptyAssert.cc',
+ 'shared/WinptyException.h',
+ 'shared/WinptyException.cc',
+ 'shared/WinptyVersion.h',
+ 'shared/WinptyVersion.cc',
+ 'shared/winpty_snprintf.h',
+ ],
+ },
+ {
+ 'target_name' : 'winpty',
+ 'type' : 'shared_library',
+ 'include_dirs' : [
+ 'include',
+ ],
+ 'defines' : [
+ 'COMPILING_WINPTY_DLL',
+ ],
+ 'libraries' : [
+ '-ladvapi32',
+ '-luser32',
+ ],
+ 'msvs_settings': {
+ # Specify this setting here to override a setting from somewhere
+ # else, such as node's common.gypi.
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1', # /EHsc
+ },
+ },
+ 'sources' : [
+ 'include/winpty.h',
+ 'libwinpty/AgentLocation.cc',
+ 'libwinpty/AgentLocation.h',
+ 'libwinpty/winpty.cc',
+ 'shared/AgentMsg.h',
+ 'shared/BackgroundDesktop.h',
+ 'shared/BackgroundDesktop.cc',
+ 'shared/Buffer.h',
+ 'shared/Buffer.cc',
+ 'shared/DebugClient.h',
+ 'shared/DebugClient.cc',
+ 'shared/GenRandom.h',
+ 'shared/GenRandom.cc',
+ 'shared/OsModule.h',
+ 'shared/OwnedHandle.h',
+ 'shared/OwnedHandle.cc',
+ 'shared/StringBuilder.h',
+ 'shared/StringUtil.cc',
+ 'shared/StringUtil.h',
+ 'shared/WindowsSecurity.cc',
+ 'shared/WindowsSecurity.h',
+ 'shared/WindowsVersion.h',
+ 'shared/WindowsVersion.cc',
+ 'shared/WinptyAssert.h',
+ 'shared/WinptyAssert.cc',
+ 'shared/WinptyException.h',
+ 'shared/WinptyException.cc',
+ 'shared/WinptyVersion.h',
+ 'shared/WinptyVersion.cc',
+ 'shared/winpty_snprintf.h',
+ ],
+ },
+ {
+ 'target_name' : 'winpty-debugserver',
+ 'type' : 'executable',
+ 'msvs_settings': {
+ # Specify this setting here to override a setting from somewhere
+ # else, such as node's common.gypi.
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1', # /EHsc
+ },
+ },
+ 'sources' : [
+ 'debugserver/DebugServer.cc',
+ 'shared/DebugClient.h',
+ 'shared/DebugClient.cc',
+ 'shared/OwnedHandle.h',
+ 'shared/OwnedHandle.cc',
+ 'shared/OsModule.h',
+ 'shared/StringBuilder.h',
+ 'shared/StringUtil.cc',
+ 'shared/StringUtil.h',
+ 'shared/WindowsSecurity.h',
+ 'shared/WindowsSecurity.cc',
+ 'shared/WindowsVersion.h',
+ 'shared/WindowsVersion.cc',
+ 'shared/WinptyAssert.h',
+ 'shared/WinptyAssert.cc',
+ 'shared/WinptyException.h',
+ 'shared/WinptyException.cc',
+ 'shared/winpty_snprintf.h',
+ ],
+ 'libraries' : [
+ '-ladvapi32',
+ ],
+ }
+ ],
+}
diff --git a/src/libs/3rdparty/winpty/vcbuild.bat b/src/libs/3rdparty/winpty/vcbuild.bat
new file mode 100644
index 0000000000..f3787a20f1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/vcbuild.bat
@@ -0,0 +1,83 @@
+@echo off
+
+REM -- Script requirements:
+REM --
+REM -- * git This program must be in the Path to check out
+REM -- build-gyp. If that directory already exists, then
+REM -- git isn't necessary, but if it is missing, no
+REM -- commit hash will be embedded into binaries.
+REM --
+REM -- * python A non-Cygwin Python 2 python.exe must be in the
+REM -- Path to run gyp.
+REM --
+REM -- * msbuild msbuild must be in the Path. It is probably
+REM -- important to have msbuild from the correct MSVC
+REM -- release.
+REM --
+REM -- The script's output binaries are in the src/Release/{Win32,x64}
+REM -- directory.
+
+REM -------------------------------------------------------------------------
+REM -- Parse arguments
+
+setlocal
+cd %~dp0
+set GYP_ARGS=
+set MSVC_PLATFORM=x64
+
+:ParamLoop
+if "%1" == "" goto :ParamDone
+if "%1" == "--msvc-platform" (
+ REM -- One of Win32 or x64.
+ set MSVC_PLATFORM=%2
+ shift && shift
+ goto :ParamLoop
+)
+if "%1" == "--gyp-msvs-version" (
+ set GYP_ARGS=%GYP_ARGS% -G msvs_version=%2
+ shift && shift
+ goto :ParamLoop
+)
+if "%1" == "--toolset" (
+ set GYP_ARGS=%GYP_ARGS% -D WINPTY_MSBUILD_TOOLSET=%2
+ shift && shift
+ goto :ParamLoop
+)
+if "%1" == "--commit-hash" (
+ set GYP_ARGS=%GYP_ARGS% -D WINPTY_COMMIT_HASH=%2
+ shift && shift
+ goto :ParamLoop
+)
+echo error: Unrecognized argument: %1
+exit /b 1
+:ParamDone
+
+REM -------------------------------------------------------------------------
+REM -- Check out GYP. GYP doesn't seem to have releases, so just use the
+REM -- current master commit.
+
+if not exist build-gyp (
+ git clone https://chromium.googlesource.com/external/gyp build-gyp || (
+ echo error: GYP clone failed
+ exit /b 1
+ )
+)
+
+REM -------------------------------------------------------------------------
+REM -- Run gyp to generate MSVC project files.
+
+cd src
+
+call ..\build-gyp\gyp.bat winpty.gyp -I configurations.gypi %GYP_ARGS%
+if errorlevel 1 (
+ echo error: GYP failed
+ exit /b 1
+)
+
+REM -------------------------------------------------------------------------
+REM -- Compile the project.
+
+msbuild winpty.sln /m /p:Platform=%MSVC_PLATFORM% || (
+ echo error: msbuild failed
+ exit /b 1
+)
diff --git a/src/libs/3rdparty/winpty/winpty.qbs b/src/libs/3rdparty/winpty/winpty.qbs
new file mode 100644
index 0000000000..63d7611364
--- /dev/null
+++ b/src/libs/3rdparty/winpty/winpty.qbs
@@ -0,0 +1,207 @@
+import qbs
+import qbs.TextFile
+
+Project {
+ name: "Winpty"
+ condition: qbs.targetOS.contains("windows")
+
+
+ Product {
+ name: "winpty_genversion_header"
+ type: "hpp"
+
+ Group {
+ files: "VERSION.txt"
+ fileTags: "txt.in"
+ }
+
+ Rule {
+ inputs: "txt.in"
+ Artifact {
+ filePath: "GenVersion.h"
+ fileTags: "hpp"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating GenVersion.h";
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ var inFile = new TextFile(input.filePath);
+ var versionTxt = inFile.readAll();
+ inFile.close();
+ // remove any line endings
+ versionTxt = versionTxt.replace(/[\r\n]/g, "");
+
+ var content = 'const char GenVersion_Version[] = "@VERSION@";\n'
+ + 'const char GenVersion_Commit[] = "@COMMIT_HASH@";\n';
+ content = content.replace(/@VERSION@/g, versionTxt);
+
+ var outFile = new TextFile(output.filePath, TextFile.WriteOnly);
+ outFile.truncate();
+ outFile.write(content);
+ outFile.close();
+ }
+ return cmd;
+ }
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: exportingProduct.buildDirectory
+ }
+ }
+
+ QtcTool {
+ name: "winpty-agent"
+ Depends { name: "winpty_genversion_header" }
+ Depends { name: "cpp" }
+
+ useNonGuiPchFile: false
+ useGuiPchFile: false
+
+ cpp.includePaths: base.concat([sourceDirectory + "/include", buildDirectory])
+ cpp.defines: base.concat(["WINPTY_AGENT_ASSERT",
+ "NOMINMAX", "UNICODE", "_UNICODE"
+ ])
+ cpp.dynamicLibraries: ["user32", "shell32", "advapi32"]
+
+ files: [
+ "src/agent/Agent.h",
+ "src/agent/Agent.cc",
+ "src/agent/AgentCreateDesktop.h",
+ "src/agent/AgentCreateDesktop.cc",
+ "src/agent/ConsoleFont.cc",
+ "src/agent/ConsoleFont.h",
+ "src/agent/ConsoleInput.cc",
+ "src/agent/ConsoleInput.h",
+ "src/agent/ConsoleInputReencoding.cc",
+ "src/agent/ConsoleInputReencoding.h",
+ "src/agent/ConsoleLine.cc",
+ "src/agent/ConsoleLine.h",
+ "src/agent/Coord.h",
+ "src/agent/DebugShowInput.h",
+ "src/agent/DebugShowInput.cc",
+ "src/agent/DefaultInputMap.h",
+ "src/agent/DefaultInputMap.cc",
+ "src/agent/DsrSender.h",
+ "src/agent/EventLoop.h",
+ "src/agent/EventLoop.cc",
+ "src/agent/InputMap.h",
+ "src/agent/InputMap.cc",
+ "src/agent/LargeConsoleRead.h",
+ "src/agent/LargeConsoleRead.cc",
+ "src/agent/NamedPipe.h",
+ "src/agent/NamedPipe.cc",
+ "src/agent/Scraper.h",
+ "src/agent/Scraper.cc",
+ "src/agent/SimplePool.h",
+ "src/agent/SmallRect.h",
+ "src/agent/Terminal.h",
+ "src/agent/Terminal.cc",
+ "src/agent/UnicodeEncoding.h",
+ "src/agent/Win32Console.cc",
+ "src/agent/Win32Console.h",
+ "src/agent/Win32ConsoleBuffer.cc",
+ "src/agent/Win32ConsoleBuffer.h",
+ "src/agent/main.cc",
+ ]
+
+ Group {
+ name: "Shared sources"
+ prefix: "src/shared/"
+ files: [
+ "AgentMsg.h",
+ "BackgroundDesktop.h",
+ "BackgroundDesktop.cc",
+ "Buffer.h",
+ "Buffer.cc",
+ "DebugClient.h",
+ "DebugClient.cc",
+ "GenRandom.h",
+ "GenRandom.cc",
+ "OsModule.h",
+ "OwnedHandle.h",
+ "OwnedHandle.cc",
+ "StringBuilder.h",
+ "StringUtil.cc",
+ "StringUtil.h",
+ "UnixCtrlChars.h",
+ "WindowsSecurity.cc",
+ "WindowsSecurity.h",
+ "WindowsVersion.h",
+ "WindowsVersion.cc",
+ "WinptyAssert.h",
+ "WinptyAssert.cc",
+ "WinptyException.h",
+ "WinptyException.cc",
+ "WinptyVersion.h",
+ "WinptyVersion.cc",
+ "winpty_snprintf.h",
+ ]
+ }
+ }
+
+ QtcLibrary {
+ name: "winpty"
+ type: "staticlibrary"
+
+ Depends { name: "winpty_genversion_header" }
+ Depends { name: "cpp" }
+
+ useNonGuiPchFile: false
+ useGuiPchFile: false
+
+ cpp.defines: base.concat(["COMPILING_WINPTY_DLL",
+ "NOMINMAX", "UNICODE", "_UNICODE"
+ ])
+ cpp.dynamicLibraries: ["user32", "shell32", "advapi32"]
+ cpp.includePaths: base.concat(sourceDirectory + "/src/include")
+
+
+ files: [
+ "src/libwinpty/AgentLocation.cc",
+ "src/libwinpty/AgentLocation.h",
+ "src/libwinpty/winpty.cc",
+ ]
+
+ Group {
+ name: "Shared sources" // FIXME duplication
+ prefix: "src/shared/"
+ files: [
+ "AgentMsg.h",
+ "BackgroundDesktop.h",
+ "BackgroundDesktop.cc",
+ "Buffer.h",
+ "Buffer.cc",
+ "DebugClient.h",
+ "DebugClient.cc",
+ "GenRandom.h",
+ "GenRandom.cc",
+ "OsModule.h",
+ "OwnedHandle.h",
+ "OwnedHandle.cc",
+ "StringBuilder.h",
+ "StringUtil.cc",
+ "StringUtil.h",
+ "UnixCtrlChars.h",
+ "WindowsSecurity.cc",
+ "WindowsSecurity.h",
+ "WindowsVersion.h",
+ "WindowsVersion.cc",
+ "WinptyAssert.h",
+ "WinptyAssert.cc",
+ "WinptyException.h",
+ "WinptyException.cc",
+ "WinptyVersion.h",
+ "WinptyVersion.cc",
+ "winpty_snprintf.h",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.defines: base.concat("COMPILING_WINPTY_DLL")
+ cpp.includePaths: base.concat(exportingProduct.sourceDirectory + "/src/include")
+ }
+ }
+}
diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt
index fc6b53d81c..2ee109d5cd 100644
--- a/src/libs/CMakeLists.txt
+++ b/src/libs/CMakeLists.txt
@@ -2,22 +2,22 @@ add_subdirectory(3rdparty)
add_subdirectory(advanceddockingsystem)
add_subdirectory(aggregation)
+add_subdirectory(cplusplus)
add_subdirectory(extensionsystem)
-add_subdirectory(utils)
+add_subdirectory(glsl)
+add_subdirectory(languageserverprotocol)
add_subdirectory(languageutils)
-add_subdirectory(cplusplus)
add_subdirectory(modelinglib)
add_subdirectory(nanotrace)
-add_subdirectory(qmljs)
add_subdirectory(qmldebug)
add_subdirectory(qmleditorwidgets)
-add_subdirectory(glsl)
-add_subdirectory(languageserverprotocol)
-add_subdirectory(sqlite)
-add_subdirectory(tracing)
+add_subdirectory(qmljs)
add_subdirectory(qmlpuppetcommunication)
-
add_subdirectory(qtcreatorcdbext)
+add_subdirectory(solutions)
+add_subdirectory(sqlite)
+add_subdirectory(tracing)
+add_subdirectory(utils)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/qlitehtml/src/CMakeLists.txt)
option(BUILD_LIBRARY_QLITEHTML "Build library qlitehtml." ${BUILD_LIBRARIES_BY_DEFAULT})
diff --git a/src/libs/advanceddockingsystem/ads_globals_p.h b/src/libs/advanceddockingsystem/ads_globals_p.h
new file mode 100644
index 0000000000..e4b32eb201
--- /dev/null
+++ b/src/libs/advanceddockingsystem/ads_globals_p.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2023
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
+
+#pragma once
+
+#include <QLoggingCategory>
+
+Q_DECLARE_LOGGING_CATEGORY(adsLog)
diff --git a/src/libs/advanceddockingsystem/dockareatabbar.cpp b/src/libs/advanceddockingsystem/dockareatabbar.cpp
index ec1bf782ff..62f0b80b49 100644
--- a/src/libs/advanceddockingsystem/dockareatabbar.cpp
+++ b/src/libs/advanceddockingsystem/dockareatabbar.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
#include "dockareatabbar.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
#include "dockwidget.h"
@@ -16,8 +17,6 @@
#include <iostream>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
/**
diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp
index ed1d76b67a..02c83489fa 100644
--- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp
+++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp
@@ -4,6 +4,7 @@
#include "dockareatitlebar.h"
#include "ads_globals.h"
+#include "ads_globals_p.h"
#include "advanceddockingsystemtr.h"
#include "dockareatabbar.h"
#include "dockareawidget.h"
@@ -16,6 +17,8 @@
#include "floatingdragpreview.h"
#include "iconprovider.h"
+#include <utils/qtcassert.h>
+
#include <QBoxLayout>
#include <QLoggingCategory>
#include <QMenu>
@@ -26,8 +29,6 @@
#include <iostream>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
/**
@@ -380,6 +381,8 @@ namespace ADS
void DockAreaTitleBar::updateDockWidgetActionsButtons()
{
+ QTC_ASSERT(d->m_tabBar->currentTab(), return );
+ QTC_ASSERT(d->m_tabBar->currentTab()->dockWidget(), return );
DockWidget* dockWidget = d->m_tabBar->currentTab()->dockWidget();
if (!d->m_dockWidgetActionsButtons.isEmpty()) {
for (auto button : std::as_const(d->m_dockWidgetActionsButtons)) {
diff --git a/src/libs/advanceddockingsystem/dockareawidget.cpp b/src/libs/advanceddockingsystem/dockareawidget.cpp
index b4c21f7119..522b6b985e 100644
--- a/src/libs/advanceddockingsystem/dockareawidget.cpp
+++ b/src/libs/advanceddockingsystem/dockareawidget.cpp
@@ -3,6 +3,7 @@
#include "dockareawidget.h"
+#include "ads_globals_p.h"
#include "dockareatabbar.h"
#include "dockareatitlebar.h"
#include "dockcomponentsfactory.h"
@@ -29,8 +30,6 @@
#include <iostream>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
static const char *const INDEX_PROPERTY = "index";
diff --git a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp
index 80393a4671..253937c4d1 100644
--- a/src/libs/advanceddockingsystem/dockcontainerwidget.cpp
+++ b/src/libs/advanceddockingsystem/dockcontainerwidget.cpp
@@ -4,6 +4,7 @@
#include "dockcontainerwidget.h"
#include "ads_globals.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
#include "dockingstatereader.h"
#include "dockmanager.h"
@@ -25,8 +26,6 @@
#include <functional>
#include <iostream>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
static unsigned int zOrderCounter = 0;
diff --git a/src/libs/advanceddockingsystem/dockfocuscontroller.cpp b/src/libs/advanceddockingsystem/dockfocuscontroller.cpp
index 79b6c6cad2..a5878ba856 100644
--- a/src/libs/advanceddockingsystem/dockfocuscontroller.cpp
+++ b/src/libs/advanceddockingsystem/dockfocuscontroller.cpp
@@ -105,11 +105,16 @@ namespace ADS
q, &DockFocusController::onFocusedDockAreaViewToggled);
}
- auto newFloatingWidget = m_focusedDockWidget->dockContainer()->floatingWidget();
+ auto dockContainer = m_focusedDockWidget->dockContainer();
+ FloatingDockContainer *newFloatingWidget = nullptr;
+
+ if (dockContainer)
+ newFloatingWidget = dockContainer->floatingWidget();
+
if (newFloatingWidget)
newFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(dockWidget));
- #ifdef Q_OS_LINUX
+#ifdef Q_OS_LINUX
// This code is required for styling the floating widget titlebar for linux
// depending on the current focus state
if (m_floatingWidget == newFloatingWidget)
@@ -122,7 +127,7 @@ namespace ADS
if (m_floatingWidget)
updateFloatingWidgetFocusStyle(m_floatingWidget, true);
- #endif
+#endif
if (old != dockWidget)
emit m_dockManager->focusedDockWidgetChanged(old, dockWidget);
@@ -162,6 +167,8 @@ namespace ADS
if (!dockWidget)
dockWidget = qobject_cast<DockWidget *>(focusedNow);
+ bool focusActual = dockWidget && dockWidget->widget();
+
if (!dockWidget)
dockWidget = internal::findParent<DockWidget *>(focusedNow);
@@ -174,6 +181,12 @@ namespace ADS
#endif
d->updateDockWidgetFocus(dockWidget);
+
+ if (focusActual) {
+ // Do this async to avoid issues when changing focus in middle of another focus handling
+ QMetaObject::invokeMethod(dockWidget->widget(), QOverload<>::of(&QWidget::setFocus),
+ Qt::QueuedConnection);
+ }
}
void DockFocusController::setDockWidgetFocused(DockWidget *focusedNow)
diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp
index 59fc2f64fc..46e7b5e5af 100644
--- a/src/libs/advanceddockingsystem/dockmanager.cpp
+++ b/src/libs/advanceddockingsystem/dockmanager.cpp
@@ -4,6 +4,7 @@
#include "dockmanager.h"
#include "ads_globals.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
#include "dockfocuscontroller.h"
#include "dockingstatereader.h"
@@ -38,7 +39,7 @@
#include <QVariant>
#include <QXmlStreamWriter>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg);
+Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg);
using namespace Utils;
@@ -598,10 +599,13 @@ namespace ADS
return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool();
}
- const QString m_dirName = QLatin1String("workspaces");
- const QString m_fileExt = QLatin1String(".wrk"); // TODO
+ constexpr QStringView m_dirName{u"workspaces"};
+ constexpr QStringView m_fileExt{u".wrk"}; // TODO
- QString DockManager::workspaceFileExtension() const { return m_fileExt; }
+ QStringView DockManager::workspaceFileExtension() const
+ {
+ return m_fileExt;
+ }
QStringList DockManager::workspaces()
{
diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h
index 9dbfc30cda..9ef2a6f3f1 100644
--- a/src/libs/advanceddockingsystem/dockmanager.h
+++ b/src/libs/advanceddockingsystem/dockmanager.h
@@ -484,7 +484,7 @@ public:
QString activeWorkspace() const;
QString lastWorkspace() const;
bool autoRestorLastWorkspace() const;
- QString workspaceFileExtension() const;
+ QStringView workspaceFileExtension() const;
QStringList workspaces();
QSet<QString> workspacePresets() const;
QDateTime workspaceDateTime(const QString &workspace) const;
diff --git a/src/libs/advanceddockingsystem/docksplitter.cpp b/src/libs/advanceddockingsystem/docksplitter.cpp
index e8537dbd77..a10161da59 100644
--- a/src/libs/advanceddockingsystem/docksplitter.cpp
+++ b/src/libs/advanceddockingsystem/docksplitter.cpp
@@ -3,14 +3,15 @@
#include "docksplitter.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
+#include <utils/stylehelper.h>
+
#include <QChildEvent>
#include <QLoggingCategory>
#include <QVariant>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
/**
@@ -31,7 +32,7 @@ namespace ADS
, d(new DockSplitterPrivate(this))
{
//setProperty("ads-splitter", true); // TODO
- setProperty("minisplitter", true);
+ setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
setChildrenCollapsible(false);
}
diff --git a/src/libs/advanceddockingsystem/dockwidget.cpp b/src/libs/advanceddockingsystem/dockwidget.cpp
index b39c58f4c2..64269ebfdb 100644
--- a/src/libs/advanceddockingsystem/dockwidget.cpp
+++ b/src/libs/advanceddockingsystem/dockwidget.cpp
@@ -4,6 +4,7 @@
#include "dockwidget.h"
#include "ads_globals.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
#include "dockcomponentsfactory.h"
#include "dockcontainerwidget.h"
@@ -25,8 +26,6 @@
#include <QXmlStreamWriter>
#include <QWindow>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
/**
diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp
index 6775478497..4531e0da2d 100644
--- a/src/libs/advanceddockingsystem/dockwidgettab.cpp
+++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp
@@ -4,6 +4,7 @@
#include "dockwidgettab.h"
#include "ads_globals.h"
+#include "ads_globals_p.h"
#include "advanceddockingsystemtr.h"
#include "dockareawidget.h"
#include "dockmanager.h"
@@ -32,8 +33,6 @@
#include <iostream>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
using TabLabelType = ElidingLabel;
diff --git a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp
index 4b8ae0913d..c99044a3d5 100644
--- a/src/libs/advanceddockingsystem/floatingdockcontainer.cpp
+++ b/src/libs/advanceddockingsystem/floatingdockcontainer.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
#include "floatingdockcontainer.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
#include "dockcontainerwidget.h"
@@ -29,8 +30,6 @@
#include <QMouseEvent>
#include <QPointer>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
#ifdef Q_OS_WIN
diff --git a/src/libs/advanceddockingsystem/floatingdragpreview.cpp b/src/libs/advanceddockingsystem/floatingdragpreview.cpp
index f427ab85ed..3111e46c6b 100644
--- a/src/libs/advanceddockingsystem/floatingdragpreview.cpp
+++ b/src/libs/advanceddockingsystem/floatingdragpreview.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
#include "floatingdragpreview.h"
+#include "ads_globals_p.h"
#include "dockareawidget.h"
#include "dockcontainerwidget.h"
@@ -19,8 +20,6 @@
#include <iostream>
-static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg)
-
namespace ADS
{
/**
diff --git a/src/libs/advanceddockingsystem/workspacedialog.cpp b/src/libs/advanceddockingsystem/workspacedialog.cpp
index e4f92d92ba..e99ec6946a 100644
--- a/src/libs/advanceddockingsystem/workspacedialog.cpp
+++ b/src/libs/advanceddockingsystem/workspacedialog.cpp
@@ -79,7 +79,7 @@ WorkspaceNameInputDialog::WorkspaceNameInputDialog(DockManager *manager, QWidget
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
label,
@@ -137,7 +137,7 @@ WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent)
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index 6e241fb29b..07714c0fe2 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -26,7 +26,6 @@
#include <QByteArray>
#include <QDebug>
-#include <QFutureInterface>
#include <QStack>
/*!
@@ -297,12 +296,13 @@ void Document::setLastModified(const QDateTime &lastModified)
_lastModified = lastModified;
}
-FilePaths Document::includedFiles() const
+FilePaths Document::includedFiles(Duplicates duplicates) const
{
FilePaths files;
for (const Include &i : std::as_const(_resolvedIncludes))
files.append(i.resolvedFileName());
- FilePath::removeDuplicates(files);
+ if (duplicates == Duplicates::Remove)
+ FilePath::removeDuplicates(files);
return files;
}
@@ -772,7 +772,7 @@ QSet<FilePath> Snapshot::allIncludesForDocument(const FilePath &filePath) const
while (!files.isEmpty()) {
FilePath file = files.pop();
if (Document::Ptr doc = document(file)) {
- const FilePaths includedFiles = doc->includedFiles();
+ const FilePaths includedFiles = doc->includedFiles(Document::Duplicates::Keep);
for (const FilePath &inc : includedFiles) {
if (!result.contains(inc)) {
result.insert(inc);
@@ -821,16 +821,10 @@ FilePaths Snapshot::filesDependingOn(const FilePath &filePath) const
return m_deps.filesDependingOn(filePath);
}
-void Snapshot::updateDependencyTable() const
-{
- QFutureInterfaceBase futureInterface;
- updateDependencyTable(futureInterface);
-}
-
-void Snapshot::updateDependencyTable(QFutureInterfaceBase &futureInterface) const
+void Snapshot::updateDependencyTable(const std::optional<QFuture<void>> &future) const
{
if (m_deps.files.isEmpty())
- m_deps.build(futureInterface, *this);
+ m_deps.build(future, *this);
}
bool Snapshot::operator==(const Snapshot &other) const
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index cfbad3be1e..00bebf277d 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -15,12 +15,9 @@
#include <QDateTime>
#include <QHash>
#include <QFileInfo>
+#include <QFuture>
#include <QAtomicInt>
-QT_BEGIN_NAMESPACE
-class QFutureInterfaceBase;
-QT_END_NAMESPACE
-
namespace CPlusPlus {
class Macro;
@@ -300,7 +297,12 @@ public:
}
};
- Utils::FilePaths includedFiles() const;
+ enum class Duplicates {
+ Remove,
+ Keep,
+ };
+
+ Utils::FilePaths includedFiles(Duplicates duplicates = Duplicates::Remove) const;
void addIncludeFile(const Include &include);
const QList<Include> &resolvedIncludes() const
@@ -406,8 +408,7 @@ public:
Utils::FilePaths filesDependingOn(const Utils::FilePath &filePath) const;
- void updateDependencyTable() const;
- void updateDependencyTable(QFutureInterfaceBase &futureInterface) const;
+ void updateDependencyTable(const std::optional<QFuture<void>> &future = {}) const;
bool operator==(const Snapshot &other) const;
diff --git a/src/libs/cplusplus/DependencyTable.cpp b/src/libs/cplusplus/DependencyTable.cpp
index 00aca7dc65..5960957330 100644
--- a/src/libs/cplusplus/DependencyTable.cpp
+++ b/src/libs/cplusplus/DependencyTable.cpp
@@ -4,7 +4,7 @@
#include "CppDocument.h"
#include <QDebug>
-#include <QFutureInterface>
+#include <QFuture>
using namespace Utils;
@@ -28,14 +28,14 @@ Utils::FilePaths DependencyTable::filesDependingOn(const Utils::FilePath &fileNa
return deps;
}
-void DependencyTable::build(QFutureInterfaceBase &futureInterface, const Snapshot &snapshot)
+void DependencyTable::build(const std::optional<QFuture<void>> &future, const Snapshot &snapshot)
{
files.clear();
fileIndex.clear();
includes.clear();
includeMap.clear();
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
const int documentCount = snapshot.size();
@@ -49,7 +49,7 @@ void DependencyTable::build(QFutureInterfaceBase &futureInterface, const Snapsho
fileIndex[it.key()] = i;
}
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
for (int i = 0; i < files.size(); ++i) {
@@ -68,13 +68,13 @@ void DependencyTable::build(QFutureInterfaceBase &futureInterface, const Snapsho
directIncludes.append(index);
bitmap.setBit(index, true);
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
}
includeMap[i] = bitmap;
includes[i] = directIncludes;
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
}
}
@@ -91,7 +91,7 @@ void DependencyTable::build(QFutureInterfaceBase &futureInterface, const Snapsho
const QList<int> includedFileIndexes = includes.value(i);
for (const int includedFileIndex : includedFileIndexes) {
bitmap |= includeMap.value(includedFileIndex);
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
}
@@ -99,10 +99,10 @@ void DependencyTable::build(QFutureInterfaceBase &futureInterface, const Snapsho
includeMap[i] = bitmap;
changed = true;
}
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
}
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
} while (changed);
}
diff --git a/src/libs/cplusplus/DependencyTable.h b/src/libs/cplusplus/DependencyTable.h
index d905784433..d460ebeb7f 100644
--- a/src/libs/cplusplus/DependencyTable.h
+++ b/src/libs/cplusplus/DependencyTable.h
@@ -14,7 +14,8 @@
#include <QVector>
QT_BEGIN_NAMESPACE
-class QFutureInterfaceBase;
+template <typename T>
+class QFuture;
QT_END_NAMESPACE
namespace CPlusPlus {
@@ -25,7 +26,7 @@ class CPLUSPLUS_EXPORT DependencyTable
{
private:
friend class Snapshot;
- void build(QFutureInterfaceBase &futureInterface, const Snapshot &snapshot);
+ void build(const std::optional<QFuture<void>> &future, const Snapshot &snapshot);
Utils::FilePaths filesDependingOn(const Utils::FilePath &fileName) const;
QVector<Utils::FilePath> files;
diff --git a/src/libs/cplusplus/cplusplus.qbs b/src/libs/cplusplus/cplusplus.qbs
index 80c6174ba2..0aed0ab438 100644
--- a/src/libs/cplusplus/cplusplus.qbs
+++ b/src/libs/cplusplus/cplusplus.qbs
@@ -41,6 +41,7 @@ Project {
"FullySpecifiedType.cpp",
"FullySpecifiedType.h",
"Keywords.cpp",
+ "Keywords.kwgen",
"Lexer.cpp",
"Lexer.h",
"LiteralTable.h",
diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp
index d8221b151f..ef27d9eace 100644
--- a/src/libs/cplusplus/pp-engine.cpp
+++ b/src/libs/cplusplus/pp-engine.cpp
@@ -37,20 +37,17 @@
#include <QDebug>
#include <QList>
#include <QDate>
+#include <QLoggingCategory>
#include <QTime>
#include <QPair>
#include <cctype>
+#include <deque>
#include <list>
#include <algorithm>
-#define NO_DEBUG
-
-#ifndef NO_DEBUG
-# include <iostream>
-#endif // NO_DEBUG
-
-#include <deque>
+// FIXME: This is used for errors that should appear in the editor.
+static Q_LOGGING_CATEGORY(lexerLog, "qtc.cpp.lexer", QtWarningMsg)
using namespace Utils;
@@ -119,13 +116,6 @@ static bool isQtReservedWord(const char *name, int size)
return false;
}
-static void nestingTooDeep()
-{
-#ifndef NO_DEBUG
- std::cerr << "*** WARNING #if / #ifdef nesting exceeded the max level " << MAX_LEVEL << std::endl;
-#endif
-}
-
} // anonymous namespace
namespace CPlusPlus {
@@ -1680,10 +1670,7 @@ void Preprocessor::handleIncludeDirective(PPToken *tk, bool includeNext)
GuardLocker depthLocker(m_includeDepthGuard);
if (m_includeDepthGuard.lockCount() > MAX_INCLUDE_DEPTH) {
- // FIXME: Categorized logging!
-#ifndef NO_DEBUG
- std::cerr << "Maximum include depth exceeded" << m_state.m_currentFileName << std::endl;
-#endif
+ qCWarning(lexerLog) << "Maximum include depth exceeded" << m_state.m_currentFileName;
return;
}
@@ -1929,10 +1916,8 @@ void Preprocessor::handleIfDirective(PPToken *tk)
Value result;
const PPToken lastExpressionToken = evalExpression(tk, result);
- if (m_state.m_ifLevel >= MAX_LEVEL - 1) {
- nestingTooDeep();
+ if (!checkConditionalNesting())
return;
- }
const bool value = !result.is_zero();
@@ -1953,7 +1938,7 @@ void Preprocessor::handleIfDirective(PPToken *tk)
void Preprocessor::handleElifDirective(PPToken *tk, const PPToken &poundToken)
{
if (m_state.m_ifLevel == 0) {
-// std::cerr << "*** WARNING #elif without #if" << std::endl;
+ qCWarning(lexerLog) << "#elif without #if";
handleIfDirective(tk);
} else {
lex(tk); // consume "elif" token
@@ -2000,22 +1985,18 @@ void Preprocessor::handleElseDirective(PPToken *tk, const PPToken &poundToken)
else if (m_client && !wasSkipping && startSkipping)
startSkippingBlocks(poundToken);
}
-#ifndef NO_DEBUG
} else {
- std::cerr << "*** WARNING #else without #if" << std::endl;
-#endif // NO_DEBUG
+ qCWarning(lexerLog) << "#else without #if";
}
}
void Preprocessor::handleEndIfDirective(PPToken *tk, const PPToken &poundToken)
{
if (m_state.m_ifLevel == 0) {
-#ifndef NO_DEBUG
- std::cerr << "*** WARNING #endif without #if";
+ qCWarning(lexerLog) << "#endif without #if";
if (!tk->generated())
- std::cerr << " on line " << tk->lineno << " of file " << m_state.m_currentFileName.toUtf8().constData();
- std::cerr << std::endl;
-#endif // NO_DEBUG
+ qCWarning(lexerLog) << "on line" << tk->lineno << "of file"
+ << m_state.m_currentFileName.toUtf8().constData();
} else {
bool wasSkipping = m_state.m_skipping[m_state.m_ifLevel];
m_state.m_skipping[m_state.m_ifLevel] = false;
@@ -2061,22 +2042,18 @@ void Preprocessor::handleIfDefDirective(bool checkUndefined, PPToken *tk)
const bool wasSkipping = m_state.m_skipping[m_state.m_ifLevel];
- if (m_state.m_ifLevel < MAX_LEVEL - 1) {
+ if (checkConditionalNesting()) {
++m_state.m_ifLevel;
m_state.m_trueTest[m_state.m_ifLevel] = value;
m_state.m_skipping[m_state.m_ifLevel] = wasSkipping ? wasSkipping : !value;
if (m_client && !wasSkipping && !value)
startSkippingBlocks(*tk);
- } else {
- nestingTooDeep();
}
lex(tk); // consume the identifier
-#ifndef NO_DEBUG
} else {
- std::cerr << "*** WARNING #ifdef without identifier" << std::endl;
-#endif // NO_DEBUG
+ qCWarning(lexerLog) << "#ifdef without identifier";
}
}
@@ -2103,10 +2080,8 @@ void Preprocessor::handleUndefDirective(PPToken *tk)
m_client->macroAdded(*macro);
}
lex(tk); // consume macro name
-#ifndef NO_DEBUG
} else {
- std::cerr << "*** WARNING #undef without identifier" << std::endl;
-#endif // NO_DEBUG
+ qCWarning(lexerLog) << "#undef without identifier";
}
}
@@ -2203,4 +2178,14 @@ void Preprocessor::maybeStartOutputLine()
buffer.append('\n');
}
+bool Preprocessor::checkConditionalNesting() const
+{
+ if (m_state.m_ifLevel >= MAX_LEVEL - 1) {
+ qCWarning(lexerLog) << "#if/#ifdef nesting exceeding maximum level" << MAX_LEVEL;
+ return false;
+ }
+ return true;
+}
+
+
} // namespace CPlusPlus
diff --git a/src/libs/cplusplus/pp-engine.h b/src/libs/cplusplus/pp-engine.h
index c888e8775d..2163380dea 100644
--- a/src/libs/cplusplus/pp-engine.h
+++ b/src/libs/cplusplus/pp-engine.h
@@ -237,6 +237,7 @@ private:
PPToken generateConcatenated(const PPToken &leftTk, const PPToken &rightTk);
void startSkippingBlocks(const PPToken &tk) const;
+ bool checkConditionalNesting() const;
private:
Client *m_client;
diff --git a/src/libs/extensionsystem/CMakeLists.txt b/src/libs/extensionsystem/CMakeLists.txt
index ea7cb60beb..0e4e6607a5 100644
--- a/src/libs/extensionsystem/CMakeLists.txt
+++ b/src/libs/extensionsystem/CMakeLists.txt
@@ -19,7 +19,7 @@ add_qtc_library(ExtensionSystem
SKIP_AUTOMOC pluginmanager.cpp
)
-find_package(Qt5 COMPONENTS Test QUIET)
+find_package(Qt6 COMPONENTS Test QUIET)
extend_qtc_library(ExtensionSystem
CONDITION TARGET Qt::Test
diff --git a/src/libs/extensionsystem/iplugin.cpp b/src/libs/extensionsystem/iplugin.cpp
index 9f7ca221e5..6661a46f61 100644
--- a/src/libs/extensionsystem/iplugin.cpp
+++ b/src/libs/extensionsystem/iplugin.cpp
@@ -161,12 +161,47 @@
namespace ExtensionSystem {
namespace Internal {
+class ObjectInitializer
+{
+public:
+ ObjectCreator creator;
+ ObjectDestructor destructor;
+ ObjectCreationPolicy policy;
+};
+
class IPluginPrivate
{
public:
+ void tryCreateObjects();
+
QList<TestCreator> testCreators;
+
+ QList<ObjectInitializer> objectInitializers;
+ QList<std::function<void()>> objectDestructors;
+
+ // For debugging purposes:
+ QList<void *> createdObjects; // Not owned.
};
+void IPluginPrivate::tryCreateObjects()
+{
+ QList<ObjectInitializer> unhandledObjectInitializers;
+
+ for (const ObjectInitializer &initializer : std::as_const(objectInitializers)) {
+ if (!initializer.policy.dependsOn.isEmpty()) {
+ qWarning("Initialization dependencies are not supported yet");
+ unhandledObjectInitializers.append(initializer);
+ continue;
+ }
+
+ void *object = initializer.creator();
+ createdObjects.append(object);
+ objectDestructors.append([initializer, object] { initializer.destructor(object); });
+ }
+
+ objectInitializers = unhandledObjectInitializers;
+}
+
} // Internal
/*!
@@ -182,10 +217,20 @@ IPlugin::IPlugin()
*/
IPlugin::~IPlugin()
{
+ for (const std::function<void()> &dtor : std::as_const(d->objectDestructors))
+ dtor();
+
delete d;
d = nullptr;
}
+void IPlugin::addManagedHelper(const ObjectCreator &creator,
+ const ObjectDestructor &destructor,
+ const ObjectCreationPolicy &policy)
+{
+ d->objectInitializers.append({creator, destructor, policy});
+}
+
bool IPlugin::initialize(const QStringList &arguments, QString *errorString)
{
Q_UNUSED(arguments)
@@ -195,6 +240,14 @@ bool IPlugin::initialize(const QStringList &arguments, QString *errorString)
}
/*!
+ \internal
+*/
+void IPlugin::tryCreateObjects()
+{
+ d->tryCreateObjects();
+}
+
+/*!
Registers a function object that creates a test object.
The created objects are meant to be passed on to \l QTest::qExec().
diff --git a/src/libs/extensionsystem/iplugin.h b/src/libs/extensionsystem/iplugin.h
index 5a8fceb3d6..3e477e42e8 100644
--- a/src/libs/extensionsystem/iplugin.h
+++ b/src/libs/extensionsystem/iplugin.h
@@ -5,6 +5,8 @@
#include "extensionsystem_global.h"
+#include <utils/id.h>
+
#include <QObject>
#include <functional>
@@ -15,6 +17,17 @@ namespace Internal { class IPluginPrivate; }
using TestCreator = std::function<QObject *()>;
+using ObjectCreator = std::function<void *()>;
+using ObjectDestructor = std::function<void(void *)>;
+
+struct EXTENSIONSYSTEM_EXPORT ObjectCreationPolicy
+{
+ // Can be empty if nothing depends on it.
+ Utils::Id id;
+ // Objects with empty dependencies are created as soon as possible.
+ QList<Utils::Id> dependsOn;
+};
+
class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject
{
Q_OBJECT
@@ -39,6 +52,7 @@ public:
// Deprecated in 10.0, use addTest()
virtual QVector<QObject *> createTestObjects() const;
+ virtual void tryCreateObjects();
protected:
virtual void initialize() {}
@@ -47,6 +61,17 @@ protected:
void addTest(Args && ...args) { addTestCreator([args...] { return new Test(args...); }); }
void addTestCreator(const TestCreator &creator);
+ template <typename Type>
+ void addManaged(const ObjectCreationPolicy &policy = {}) {
+ addManagedHelper([]() -> void * { return new Type(); },
+ [](void *p) { delete static_cast<Type *>(p); },
+ policy);
+ }
+
+ void addManagedHelper(const ObjectCreator &creator,
+ const ObjectDestructor &destructor,
+ const ObjectCreationPolicy &policy);
+
signals:
void asynchronousShutdownFinished();
diff --git a/src/libs/extensionsystem/plugindetailsview.cpp b/src/libs/extensionsystem/plugindetailsview.cpp
index 8242d8dac6..49ba0815a3 100644
--- a/src/libs/extensionsystem/plugindetailsview.cpp
+++ b/src/libs/extensionsystem/plugindetailsview.cpp
@@ -53,7 +53,7 @@ public:
, license(createTextEdit())
, dependencies(new QListWidget(q))
{
- using namespace Utils::Layouting;
+ using namespace Layouting;
// clang-format off
Form {
@@ -68,8 +68,9 @@ public:
Tr::tr("Description:"), description, br,
Tr::tr("Copyright:"), copyright, br,
Tr::tr("License:"), license, br,
- Tr::tr("Dependencies:"), dependencies
- }.attachTo(q, WithoutMargins);
+ Tr::tr("Dependencies:"), dependencies,
+ noMargin
+ }.attachTo(q);
// clang-format on
}
diff --git a/src/libs/extensionsystem/pluginerroroverview.cpp b/src/libs/extensionsystem/pluginerroroverview.cpp
index 20e6f5ac90..69562e504d 100644
--- a/src/libs/extensionsystem/pluginerroroverview.cpp
+++ b/src/libs/extensionsystem/pluginerroroverview.cpp
@@ -42,7 +42,7 @@ PluginErrorOverview::PluginErrorOverview(QWidget *parent)
QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
auto createLabel = [this](const QString &text) {
QLabel *label = new QLabel(text, this);
diff --git a/src/libs/extensionsystem/pluginerrorview.cpp b/src/libs/extensionsystem/pluginerrorview.cpp
index fac7fd9a42..31c4334b6a 100644
--- a/src/libs/extensionsystem/pluginerrorview.cpp
+++ b/src/libs/extensionsystem/pluginerrorview.cpp
@@ -41,12 +41,13 @@ public:
errorString->setTabChangesFocus(true);
errorString->setReadOnly(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
Tr::tr("State:"), state, br,
- Tr::tr("Error message:"), errorString
- }.attachTo(q, WithoutMargins);
+ Tr::tr("Error message:"), errorString,
+ noMargin,
+ }.attachTo(q);
}
PluginErrorView *q = nullptr;
diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp
index aec053b6a8..3ee9951055 100644
--- a/src/libs/extensionsystem/pluginmanager.cpp
+++ b/src/libs/extensionsystem/pluginmanager.cpp
@@ -32,10 +32,11 @@
#include <utils/benchmarker.h>
#include <utils/executeondestruction.h>
#include <utils/fileutils.h>
+#include <utils/futuresynchronizer.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <utils/threadutils.h>
@@ -400,7 +401,7 @@ QString PluginManager::systemInformation()
QString result;
CommandLine qtDiag(FilePath::fromString(QLibraryInfo::location(QLibraryInfo::BinariesPath))
.pathAppended("qtdiag").withExecutableSuffix());
- QtcProcess qtDiagProc;
+ Process qtDiagProc;
qtDiagProc.setCommand(qtDiag);
qtDiagProc.runBlocking();
if (qtDiagProc.result() == ProcessResult::FinishedWithSuccess)
@@ -422,6 +423,11 @@ QString PluginManager::systemInformation()
return result;
}
+FutureSynchronizer *PluginManager::futureSynchronizer()
+{
+ return d->m_futureSynchronizer.get();
+}
+
/*!
The list of paths were the plugin manager searches for plugins.
@@ -976,6 +982,7 @@ void PluginManagerPrivate::nextDelayedInitialize()
PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) :
q(pluginManager)
{
+ m_futureSynchronizer.reset(new FutureSynchronizer);
}
@@ -1043,6 +1050,7 @@ void PluginManagerPrivate::stopAll()
*/
void PluginManagerPrivate::deleteAll()
{
+ m_futureSynchronizer.reset(); // Synchronize all futures from all plugins
Utils::reverseForeach(loadQueue(), [this](PluginSpec *spec) {
loadPlugin(spec, PluginSpec::Deleted);
});
diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h
index 8dac9544cc..a56cb7ba9d 100644
--- a/src/libs/extensionsystem/pluginmanager.h
+++ b/src/libs/extensionsystem/pluginmanager.h
@@ -15,6 +15,8 @@ QT_BEGIN_NAMESPACE
class QTextStream;
QT_END_NAMESPACE
+namespace Utils { class FutureSynchronizer; }
+
namespace ExtensionSystem {
class IPlugin;
class PluginSpec;
@@ -133,6 +135,8 @@ public:
static QString systemInformation();
+ static Utils::FutureSynchronizer *futureSynchronizer();
+
signals:
void objectAdded(QObject *obj);
void aboutToRemoveObject(QObject *obj);
diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h
index decd627177..86c3a6c362 100644
--- a/src/libs/extensionsystem/pluginmanager_p.h
+++ b/src/libs/extensionsystem/pluginmanager_p.h
@@ -26,6 +26,7 @@ class QEventLoop;
QT_END_NAMESPACE
namespace Utils {
+class FutureSynchronizer;
class QtcSettings;
}
@@ -133,6 +134,7 @@ public:
QWaitCondition m_scenarioWaitCondition;
PluginManager::ProcessData m_creatorProcessData;
+ std::unique_ptr<Utils::FutureSynchronizer> m_futureSynchronizer;
private:
PluginManager *q;
diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp
index d18f410e81..62b3c0096f 100644
--- a/src/libs/extensionsystem/pluginspec.cpp
+++ b/src/libs/extensionsystem/pluginspec.cpp
@@ -1119,6 +1119,7 @@ bool PluginSpecPrivate::initializePlugin()
hasError = true;
return false;
}
+ plugin->tryCreateObjects();
state = PluginSpec::Initialized;
return true;
}
@@ -1145,6 +1146,7 @@ bool PluginSpecPrivate::initializeExtensions()
return false;
}
plugin->extensionsInitialized();
+ plugin->tryCreateObjects();
state = PluginSpec::Running;
return true;
}
@@ -1164,7 +1166,9 @@ bool PluginSpecPrivate::delayedInitialize()
hasError = true;
return false;
}
- return plugin->delayedInitialize();
+ const bool res = plugin->delayedInitialize();
+ plugin->tryCreateObjects();
+ return res;
}
/*!
diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.h b/src/libs/languageserverprotocol/jsonrpcmessages.h
index 0b018d7f54..680bff9578 100644
--- a/src/libs/languageserverprotocol/jsonrpcmessages.h
+++ b/src/libs/languageserverprotocol/jsonrpcmessages.h
@@ -141,6 +141,7 @@ public:
void setMethod(const QString &method)
{ m_jsonObject.insert(methodKey, method); }
+ using Parameters = Params;
std::optional<Params> params() const
{
const QJsonValue &params = m_jsonObject.value(paramsKey);
diff --git a/src/libs/languageserverprotocol/lsptypes.cpp b/src/libs/languageserverprotocol/lsptypes.cpp
index 4bf3f3746e..bd7798dcef 100644
--- a/src/libs/languageserverprotocol/lsptypes.cpp
+++ b/src/libs/languageserverprotocol/lsptypes.cpp
@@ -311,6 +311,16 @@ bool Range::overlaps(const Range &range) const
return !isLeftOf(range) && !range.isLeftOf(*this);
}
+QTextCursor Range::toSelection(QTextDocument *doc) const
+{
+ QTC_ASSERT(doc, return {});
+ if (!isValid())
+ return {};
+ QTextCursor cursor = start().toTextCursor(doc);
+ cursor.setPosition(end().toPositionInDocument(doc), QTextCursor::KeepAnchor);
+ return cursor;
+}
+
QString expressionForGlob(QString globPattern)
{
const QString anySubDir("qtc_anysubdir_id");
diff --git a/src/libs/languageserverprotocol/lsptypes.h b/src/libs/languageserverprotocol/lsptypes.h
index 239d40f662..3a9adb1b0d 100644
--- a/src/libs/languageserverprotocol/lsptypes.h
+++ b/src/libs/languageserverprotocol/lsptypes.h
@@ -117,6 +117,8 @@ public:
bool isLeftOf(const Range &other) const
{ return isEmpty() || other.isEmpty() ? end() < other.start() : end() <= other.start(); }
+ QTextCursor toSelection(QTextDocument *doc) const;
+
bool isValid() const override
{ return JsonObject::contains(startKey) && JsonObject::contains(endKey); }
};
@@ -556,7 +558,6 @@ enum class SymbolKind {
TypeParameter = 26,
LastSymbolKind = TypeParameter,
};
-using SymbolStringifier = std::function<QString(SymbolKind, const QString &, const QString &)>;
namespace CompletionItemKind {
enum Kind {
diff --git a/src/libs/languageserverprotocol/lsputils.h b/src/libs/languageserverprotocol/lsputils.h
index 5515b51576..0c815bafd8 100644
--- a/src/libs/languageserverprotocol/lsputils.h
+++ b/src/libs/languageserverprotocol/lsputils.h
@@ -87,6 +87,13 @@ public:
return QJsonValue();
}
+ QList<T> toListOrEmpty() const
+ {
+ if (std::holds_alternative<QList<T>>(*this))
+ return std::get<QList<T>>(*this);
+ return {};
+ }
+
QList<T> toList() const
{
QTC_ASSERT(std::holds_alternative<QList<T>>(*this), return {});
diff --git a/src/libs/languageserverprotocol/workspace.h b/src/libs/languageserverprotocol/workspace.h
index efe77a68ec..42cfcf451d 100644
--- a/src/libs/languageserverprotocol/workspace.h
+++ b/src/libs/languageserverprotocol/workspace.h
@@ -175,7 +175,7 @@ class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceSymbolRequest : public Request<
LanguageClientArray<SymbolInformation>, std::nullptr_t, WorkspaceSymbolParams>
{
public:
- WorkspaceSymbolRequest(const WorkspaceSymbolParams &params);
+ explicit WorkspaceSymbolRequest(const WorkspaceSymbolParams &params);
using Request::Request;
constexpr static const char methodName[] = "workspace/symbol";
};
diff --git a/src/libs/libs.qbs b/src/libs/libs.qbs
index 92f84fe4d1..ffc3017cba 100644
--- a/src/libs/libs.qbs
+++ b/src/libs/libs.qbs
@@ -19,12 +19,15 @@ Project {
"qmljs/qmljs.qbs",
"qmldebug/qmldebug.qbs",
"qtcreatorcdbext/qtcreatorcdbext.qbs",
+ "solutions/solutions.qbs",
"sqlite/sqlite.qbs",
"tracing/tracing.qbs",
- "utils/process_stub.qbs",
"utils/process_ctrlc_stub.qbs",
"utils/utils.qbs",
+ "3rdparty/libptyqt/ptyqt.qbs",
+ "3rdparty/libvterm/vterm.qbs",
"3rdparty/syntax-highlighting/syntax-highlighting.qbs",
+ "3rdparty/winpty/winpty.qbs",
"3rdparty/yaml-cpp/yaml-cpp.qbs",
].concat(qlitehtml).concat(project.additionalLibs)
}
diff --git a/src/libs/qlitehtml b/src/libs/qlitehtml
-Subproject f05f78ef33225823d348ee18f2fa464e95024dd
+Subproject 8e541a22b513432ed566fca824af207395ee0c9
diff --git a/src/libs/qmleditorwidgets/contextpanetextwidget.cpp b/src/libs/qmleditorwidgets/contextpanetextwidget.cpp
index 1a7d527294..a7f9e31f02 100644
--- a/src/libs/qmleditorwidgets/contextpanetextwidget.cpp
+++ b/src/libs/qmleditorwidgets/contextpanetextwidget.cpp
@@ -80,7 +80,7 @@ ContextPaneTextWidget::ContextPaneTextWidget(QWidget *parent) :
vAlignButtons->addButton(m_centerVAlignmentButton);
vAlignButtons->addButton(m_bottomAlignmentButton);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { m_fontComboBox, m_colorButton, m_fontSizeSpinBox, },
Row {
diff --git a/src/libs/qmleditorwidgets/contextpanewidget.cpp b/src/libs/qmleditorwidgets/contextpanewidget.cpp
index 688c65ad7b..8d675bf95b 100644
--- a/src/libs/qmleditorwidgets/contextpanewidget.cpp
+++ b/src/libs/qmleditorwidgets/contextpanewidget.cpp
@@ -6,6 +6,7 @@
#include "qmleditorwidgetstr.h"
#include <utils/hostosinfo.h>
+#include <utils/utilsicons.h>
#include <QToolButton>
#include <QFontComboBox>
@@ -27,26 +28,6 @@ using namespace Utils;
namespace QmlEditorWidgets {
-/* XPM */
-static const char * pin_xpm[] = {
-"12 9 7 1",
-" c None",
-". c #000000",
-"+ c #515151",
-"@ c #A8A8A8",
-"# c #A9A9A9",
-"$ c #999999",
-"% c #696969",
-" . ",
-" ......+",
-" .@@@@@.",
-" .#####.",
-"+.....$$$$$.",
-" .%%%%%.",
-" .......",
-" ......+",
-" . "};
-
DragWidget::DragWidget(QWidget *parent) : QFrame(parent)
{
setFrameStyle(QFrame::NoFrame);
@@ -143,7 +124,7 @@ ContextPaneWidget::ContextPaneWidget(QWidget *parent) : DragWidget(parent), m_cu
m_toolButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton));
m_toolButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
- m_toolButton->setFixedSize(16, 16);
+ m_toolButton->setFixedSize(20, 20);
m_toolButton->setToolTip(Tr::tr("Hides this toolbar."));
connect(m_toolButton, &QToolButton::clicked, this, &ContextPaneWidget::onTogglePane);
@@ -464,9 +445,7 @@ void ContextPaneWidget::setPinButton()
m_toolButton->setAutoRaise(true);
m_pinned = true;
- m_toolButton->setIcon(QPixmap::fromImage(QImage(pin_xpm)));
- m_toolButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
- m_toolButton->setFixedSize(20, 20);
+ m_toolButton->setIcon(Utils::Icons::PINNED_SMALL.icon());
m_toolButton->setToolTip(Tr::tr("Unpins the toolbar and moves it to the default position."));
emit pinnedChanged(true);
@@ -481,8 +460,6 @@ void ContextPaneWidget::setLineButton()
m_pinned = false;
m_toolButton->setAutoRaise(true);
m_toolButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton));
- m_toolButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
- m_toolButton->setFixedSize(20, 20);
m_toolButton->setToolTip(Tr::tr("Hides this toolbar. This toolbar can be"
" permanently disabled in the options page or in the context menu."));
diff --git a/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp b/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp
index f4ba3a6a5c..12ec4da3d5 100644
--- a/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp
+++ b/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp
@@ -97,7 +97,7 @@ ContextPaneWidgetImage::ContextPaneWidgetImage(QWidget *parent, bool borderImage
vRadioButtons->addButton(m_borderImage.verticalStretchRadioButton);
vRadioButtons->addButton(m_borderImage.verticalTileRadioButtonNoCrop);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Column { m_previewLabel, m_sizeLabel, },
Column {
@@ -146,7 +146,7 @@ ContextPaneWidgetImage::ContextPaneWidgetImage(QWidget *parent, bool borderImage
m_image.cropAspectFitRadioButton = radioButton("aspect-crop-icon",
Tr::tr("The image is scaled uniformly to fill, cropping if necessary."));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Column { m_previewLabel, m_sizeLabel, },
Column {
diff --git a/src/libs/qmleditorwidgets/contextpanewidgetrectangle.cpp b/src/libs/qmleditorwidgets/contextpanewidgetrectangle.cpp
index 52afeff187..0a8c1242e5 100644
--- a/src/libs/qmleditorwidgets/contextpanewidgetrectangle.cpp
+++ b/src/libs/qmleditorwidgets/contextpanewidgetrectangle.cpp
@@ -56,7 +56,7 @@ ContextPaneWidgetRectangle::ContextPaneWidgetRectangle(QWidget *parent)
borderButtons->addButton(m_borderSolid);
borderButtons->addButton(m_borderNone);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
m_gradientLabel, m_gradientLine, br,
Tr::tr("Color"), Row { m_colorColorButton, m_colorSolid, m_colorGradient, m_colorNone, st, }, br,
diff --git a/src/libs/qmleditorwidgets/easingpane/easingcontextpane.cpp b/src/libs/qmleditorwidgets/easingpane/easingcontextpane.cpp
index e61f3c2592..c35f6d792c 100644
--- a/src/libs/qmleditorwidgets/easingpane/easingcontextpane.cpp
+++ b/src/libs/qmleditorwidgets/easingpane/easingcontextpane.cpp
@@ -145,7 +145,7 @@ EasingContextPane::EasingContextPane(QWidget *parent)
spinBox->setMaximum(999999.9);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { m_graphicsView, m_playButton, },
Row {
diff --git a/src/libs/qmljs/qmljsbundle.cpp b/src/libs/qmljs/qmljsbundle.cpp
index c55edcdf5e..e4d536a3da 100644
--- a/src/libs/qmljs/qmljsbundle.cpp
+++ b/src/libs/qmljs/qmljsbundle.cpp
@@ -7,6 +7,7 @@
#include <QString>
#include <QFile>
+#include <QRegularExpression>
#include <QTextStream>
#include <QHash>
@@ -186,8 +187,10 @@ QString QmlBundle::toString(const QString &indent)
}
QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config,
- const QString &path, const QString &propertyName, bool required)
+ const QString &path, const QString &propertyName,
+ bool required, bool stripVersions)
{
+ static const QRegularExpression versionNumberAtEnd("^(.+)( \\d+\\.\\d+)$");
QStringList res;
if (!config->hasMember(propertyName)) {
if (required)
@@ -202,7 +205,13 @@ QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config,
for (Utils::JsonValue *v : elements) {
Utils::JsonStringValue *impStr = ((v != nullptr) ? v->toString() : nullptr);
if (impStr != nullptr) {
- trie.insert(impStr->value());
+ QString value = impStr->value();
+ if (stripVersions) {
+ const QRegularExpressionMatch match = versionNumberAtEnd.match(value);
+ if (match.hasMatch())
+ value = match.captured(1);
+ }
+ trie.insert(value);
} else {
res.append(QString::fromLatin1("Expected all elements of array in property \"%1\" "
"to be strings in QmlBundle at %2.")
@@ -217,7 +226,7 @@ QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config,
return res;
}
-bool QmlBundle::readFrom(QString path, QStringList *errors)
+bool QmlBundle::readFrom(QString path, bool stripVersions, QStringList *errors)
{
Utils::JsonMemoryPool pool;
@@ -249,8 +258,8 @@ bool QmlBundle::readFrom(QString path, QStringList *errors)
}
errs << maybeReadTrie(m_searchPaths, config, path, QLatin1String("searchPaths"));
errs << maybeReadTrie(m_installPaths, config, path, QLatin1String("installPaths"));
- errs << maybeReadTrie(m_supportedImports, config, path, QLatin1String("supportedImports")
- , true);
+ errs << maybeReadTrie(m_supportedImports, config, path, QLatin1String("supportedImports"),
+ true, stripVersions);
errs << maybeReadTrie(m_implicitImports, config, path, QLatin1String("implicitImports"));
if (errors)
(*errors) << errs;
diff --git a/src/libs/qmljs/qmljsbundle.h b/src/libs/qmljs/qmljsbundle.h
index 8cf3145671..5d2058eef4 100644
--- a/src/libs/qmljs/qmljsbundle.h
+++ b/src/libs/qmljs/qmljsbundle.h
@@ -53,14 +53,15 @@ public:
bool writeTo(const QString &path) const;
bool writeTo(QTextStream &stream, const QString &indent = QString()) const;
QString toString(const QString &indent = QString());
- bool readFrom(QString path, QStringList *errors);
+ bool readFrom(QString path, bool stripVersions, QStringList *errors);
bool operator==(const QmlBundle &o) const;
bool operator!=(const QmlBundle &o) const;
private:
static void printEscaped(QTextStream &s, const QString &str);
static void writeTrie(QTextStream &stream, const Trie &t, const QString &indent);
QStringList maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config, const QString &path,
- const QString &propertyName, bool required = false);
+ const QString &propertyName, bool required = false,
+ bool stripVersions = false);
QString m_name;
Trie m_searchPaths;
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 8b6bd8e7dc..51be467387 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -12,6 +12,7 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <utils/qtcsettings.h>
#include <QColor>
#include <QDir>
@@ -639,7 +640,46 @@ Q_GLOBAL_STATIC(UnsupportedRootObjectTypesByVisualDesigner, unsupportedRootObjec
Q_GLOBAL_STATIC(UnsupportedRootObjectTypesByQmlUi, unsupportedRootObjectTypesByQmlUi)
Q_GLOBAL_STATIC(UnsupportedTypesByQmlUi, unsupportedTypesByQmlUi)
-Check::Check(Document::Ptr doc, const ContextPtr &context)
+QList<StaticAnalysis::Type> Check::defaultDisabledMessages()
+{
+ static const QList<StaticAnalysis::Type> disabled = Utils::sorted(QList<StaticAnalysis::Type>{
+ HintAnonymousFunctionSpacing,
+ HintDeclareVarsInOneLine,
+ HintDeclarationsShouldBeAtStartOfFunction,
+ HintBinaryOperatorSpacing,
+ HintOneStatementPerLine,
+ HintExtraParentheses,
+
+ // QmlDesigner related
+ WarnImperativeCodeNotEditableInVisualDesigner,
+ WarnUnsupportedTypeInVisualDesigner,
+ WarnReferenceToParentItemNotSupportedByVisualDesigner,
+ WarnUndefinedValueForVisualDesigner,
+ WarnStatesOnlyInRootItemForVisualDesigner,
+ ErrUnsupportedRootTypeInVisualDesigner,
+ ErrInvalidIdeInVisualDesigner,
+
+ });
+ return disabled;
+}
+
+QList<StaticAnalysis::Type> Check::defaultDisabledMessagesForNonQuickUi()
+{
+ static const QList<StaticAnalysis::Type> disabled = Utils::sorted(QList<StaticAnalysis::Type>{
+ // QmlDesigner related
+ ErrUnsupportedRootTypeInQmlUi,
+ ErrUnsupportedTypeInQmlUi,
+ ErrFunctionsNotSupportedInQmlUi,
+ ErrBlocksNotSupportedInQmlUi,
+ ErrBehavioursNotSupportedInQmlUi,
+ ErrStatesOnlyInRootItemInQmlUi,
+ ErrReferenceToParentItemNotSupportedInQmlUi,
+ ErrDoNotMixTranslationFunctionsInQmlUi,
+ });
+ return disabled;
+}
+
+Check::Check(Document::Ptr doc, const ContextPtr &context, Utils::QtcSettings *qtcSettings)
: _doc(doc)
, _context(context)
, _scopeChain(doc, _context)
@@ -655,16 +695,25 @@ Check::Check(Document::Ptr doc, const ContextPtr &context)
}
_enabledMessages = Utils::toSet(Message::allMessageTypes());
- disableMessage(HintAnonymousFunctionSpacing);
- disableMessage(HintDeclareVarsInOneLine);
- disableMessage(HintDeclarationsShouldBeAtStartOfFunction);
- disableMessage(HintBinaryOperatorSpacing);
- disableMessage(HintOneStatementPerLine);
- disableMessage(HintExtraParentheses);
+ if (qtcSettings && qtcSettings->value("J.QtQuick/QmlJSEditor.useCustomAnalyzer").toBool()) {
+ auto disabled = qtcSettings->value("J.QtQuick/QmlJSEditor.disabledMessages").toList();
+ for (const QVariant &disabledNumber : disabled)
+ disableMessage(StaticAnalysis::Type(disabledNumber.toInt()));
+
+ if (!isQtQuick2Ui()) {
+ auto disabled = qtcSettings->value("J.QtQuick/QmlJSEditor.disabledMessagesNonQuickUI").toList();
+ for (const QVariant &disabledNumber : disabled)
+ disableMessage(StaticAnalysis::Type(disabledNumber.toInt()));
+ }
+ } else {
+ for (auto type : defaultDisabledMessages())
+ disableMessage(type);
- disableQmlDesignerChecks();
- if (!isQtQuick2Ui())
- disableQmlDesignerUiFileChecks();
+ if (!isQtQuick2Ui()) {
+ for (auto type : defaultDisabledMessagesForNonQuickUi())
+ disableMessage(type);
+ }
+ }
}
Check::~Check()
@@ -702,17 +751,6 @@ void Check::enableQmlDesignerChecks()
//## triggers too often ## check.enableMessage(StaticAnalysis::WarnUndefinedValueForVisualDesigner);
}
-void Check::disableQmlDesignerChecks()
-{
- disableMessage(WarnImperativeCodeNotEditableInVisualDesigner);
- disableMessage(WarnUnsupportedTypeInVisualDesigner);
- disableMessage(WarnReferenceToParentItemNotSupportedByVisualDesigner);
- disableMessage(WarnUndefinedValueForVisualDesigner);
- disableMessage(WarnStatesOnlyInRootItemForVisualDesigner);
- disableMessage(ErrUnsupportedRootTypeInVisualDesigner);
- disableMessage(ErrInvalidIdeInVisualDesigner);
-}
-
void Check::enableQmlDesignerUiFileChecks()
{
enableMessage(ErrUnsupportedRootTypeInQmlUi);
@@ -1838,16 +1876,32 @@ bool Check::visit(CallExpression *ast)
static const QStringList translationFunctions = {"qsTr", "qsTrId", "qsTranslate",
"qsTrNoOp", "qsTrIdNoOp", "qsTranslateNoOp"};
- static const QStringList whiteListedFunctions = {"toString", "toFixed", "toExponential", "toPrecision", "isFinite", "isNaN", "valueOf",
- "toLowerCase", "toLocaleString", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase",
- "substring" , "charAt", "charCodeAt", "concat", "endsWith", "includes", "indexOf", "lastIndexOf"};
+ static const QStringList whiteListedFunctions = {
+ "toString", "toFixed", "toExponential", "toPrecision", "isFinite",
+ "isNaN", "valueOf", "toLowerCase", "toLocaleString", "toLocaleLowerCase",
+ "toUpperCase", "toLocaleUpperCase", "substring", "charAt", "charCodeAt",
+ "concat", "endsWith", "includes", "indexOf", "lastIndexOf",
+ "arg"};
static const QStringList colorFunctions = {"lighter", "darker", "rgba", "tint", "hsla", "hsva"};
- static const QStringList qtFunction = {"point", "rect", "size", "vector2d", "vector3d", "vector4d", "quaternion" "matrix4x4", "formatDate",
- "formatDateTime", "formatTime", "resolvedUrl"};
+ static const QStringList qtFunction = {"point",
+ "rect",
+ "size",
+ "vector2d",
+ "vector3d",
+ "vector4d",
+ "quaternion",
+ "matrix4x4",
+ "formatDate",
+ "formatDateTime",
+ "formatTime",
+ "resolvedUrl"};
+
+ const bool whiteListedFunction = translationFunctions.contains(name)
+ || whiteListedFunctions.contains(name)
+ || colorFunctions.contains(name) || qtFunction.contains(name);
- const bool whiteListedFunction = translationFunctions.contains(name) || whiteListedFunctions.contains(name) || colorFunctions.contains(name) || qtFunction.contains(name);
// We allow the Math. functions
const bool isMathFunction = namespaceName == "Math";
diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h
index a868bbe15e..60484fc43f 100644
--- a/src/libs/qmljs/qmljscheck.h
+++ b/src/libs/qmljs/qmljscheck.h
@@ -12,6 +12,8 @@
#include <QSet>
#include <QStack>
+namespace Utils { class QtcSettings; }
+
namespace QmlJS {
class Imports;
@@ -22,7 +24,7 @@ class QMLJS_EXPORT Check: protected AST::Visitor
public:
// prefer taking root scope chain?
- Check(Document::Ptr doc, const ContextPtr &context);
+ Check(Document::Ptr doc, const ContextPtr &context, Utils::QtcSettings *qtcSettings = nullptr);
~Check();
QList<StaticAnalysis::Message> operator()();
@@ -31,11 +33,13 @@ public:
void disableMessage(StaticAnalysis::Type type);
void enableQmlDesignerChecks();
- void disableQmlDesignerChecks();
void enableQmlDesignerUiFileChecks();
void disableQmlDesignerUiFileChecks();
+ static QList<StaticAnalysis::Type> defaultDisabledMessages();
+ static QList<StaticAnalysis::Type> defaultDisabledMessagesForNonQuickUi();
+
protected:
bool preVisit(AST::Node *ast) override;
void postVisit(AST::Node *ast) override;
diff --git a/src/libs/qmljs/qmljscodeformatter.cpp b/src/libs/qmljs/qmljscodeformatter.cpp
index 55ed2e6190..a2f944700f 100644
--- a/src/libs/qmljs/qmljscodeformatter.cpp
+++ b/src/libs/qmljs/qmljscodeformatter.cpp
@@ -253,9 +253,16 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case function_arglist_closed:
switch (kind) {
case LeftBrace: turnInto(jsblock_open); break;
+ case Colon: turnInto(function_type_annotated_return); break;
default: leave(true); continue; // error recovery
} break;
+ case function_type_annotated_return:
+ switch (kind) {
+ case LeftBrace: turnInto(jsblock_open); break;
+ default: break;
+ } break;
+
case expression_or_objectdefinition:
switch (kind) {
case Dot:
diff --git a/src/libs/qmljs/qmljscodeformatter.h b/src/libs/qmljs/qmljscodeformatter.h
index 4800fccfdf..abef85a782 100644
--- a/src/libs/qmljs/qmljscodeformatter.h
+++ b/src/libs/qmljs/qmljscodeformatter.h
@@ -99,6 +99,7 @@ public: // must be public to make Q_GADGET introspection work
function_start, // after 'function'
function_arglist_open, // after '(' starting function argument list
function_arglist_closed, // after ')' in argument list, expecting '{'
+ function_type_annotated_return, // after ':' expecting a type
binding_or_objectdefinition, // after an identifier
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 5054ce5703..ed1c506df7 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -523,8 +523,8 @@ bool LinkPrivate::importLibrary(const Document::Ptr &doc,
bool subImportFound = importLibrary(doc, subImport.libraryPath, &subImport, targetObject, importPath, true);
if (!subImportFound && errorLoc.isValid()) {
- import->valid = false;
if (!(optional || (toImport.flags & QmlDirParser::Import::Optional))) {
+ import->valid = false;
error(doc,
errorLoc,
Tr::tr("Implicit import '%1' of QML module '%2' not found.\n\n"
diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp
index 3d2fc32120..6bcfe7ee25 100644
--- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp
+++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp
@@ -15,8 +15,8 @@
#include <cplusplus/cppmodelmanagerbase.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/hostosinfo.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#ifdef WITH_TESTS
@@ -337,11 +337,9 @@ QFuture<void> ModelManagerInterface::refreshSourceFiles(const QList<Utils::FileP
if (sourceFiles.isEmpty())
return QFuture<void>();
- QFuture<void> result = Utils::runAsync(&m_threadPool,
- &ModelManagerInterface::parse,
- workingCopyInternal(), sourceFiles,
- this, Dialect(Dialect::Qml),
- emitDocumentOnDiskChanged);
+ QFuture<void> result = Utils::asyncRun(&m_threadPool, &ModelManagerInterface::parse,
+ workingCopyInternal(), sourceFiles, this,
+ Dialect(Dialect::Qml), emitDocumentOnDiskChanged);
addFuture(result);
if (sourceFiles.count() > 1)
@@ -365,13 +363,8 @@ QFuture<void> ModelManagerInterface::refreshSourceFiles(const QList<Utils::FileP
void ModelManagerInterface::fileChangedOnDisk(const Utils::FilePath &path)
{
- addFuture(Utils::runAsync(&m_threadPool,
- &ModelManagerInterface::parse,
- workingCopyInternal(),
- FilePaths({path}),
- this,
- Dialect(Dialect::AnyLanguage),
- true));
+ addFuture(Utils::asyncRun(&m_threadPool, &ModelManagerInterface::parse, workingCopyInternal(),
+ FilePaths({path}), this, Dialect(Dialect::AnyLanguage), true));
}
void ModelManagerInterface::removeFiles(const QList<Utils::FilePath> &files)
@@ -1044,24 +1037,24 @@ void ModelManagerInterface::parseLoop(QSet<Utils::FilePath> &scannedPaths,
class FutureReporter
{
public:
- FutureReporter(QFutureInterface<void> &future, int multiplier, int base)
- : future(future), multiplier(multiplier), base(base)
+ FutureReporter(QPromise<void> &promise, int multiplier, int base)
+ : m_promise(promise), m_multiplier(multiplier), m_base(base)
{}
bool operator()(qreal val)
{
- if (future.isCanceled())
+ if (m_promise.isCanceled())
return false;
- future.setProgressValue(int(base + multiplier * val));
+ m_promise.setProgressValue(int(m_base + m_multiplier * val));
return true;
}
private:
- QFutureInterface<void> &future;
- int multiplier;
- int base;
+ QPromise<void> &m_promise;
+ int m_multiplier;
+ int m_base;
};
-void ModelManagerInterface::parse(QFutureInterface<void> &future,
+void ModelManagerInterface::parse(QPromise<void> &promise,
const WorkingCopy &workingCopy,
QList<Utils::FilePath> files,
ModelManagerInterface *modelManager,
@@ -1069,8 +1062,8 @@ void ModelManagerInterface::parse(QFutureInterface<void> &future,
bool emitDocChangedOnDisk)
{
const int progressMax = 100;
- FutureReporter reporter(future, progressMax, 0);
- future.setProgressRange(0, progressMax);
+ FutureReporter reporter(promise, progressMax, 0);
+ promise.setProgressRange(0, progressMax);
// paths we have scanned for files and added to the files list
QSet<Utils::FilePath> scannedPaths;
@@ -1078,7 +1071,7 @@ void ModelManagerInterface::parse(QFutureInterface<void> &future,
QSet<Utils::FilePath> newLibraries;
parseLoop(scannedPaths, newLibraries, workingCopy, std::move(files), modelManager, mainLanguage,
emitDocChangedOnDisk, reporter);
- future.setProgressValue(progressMax);
+ promise.setProgressValue(progressMax);
}
struct ScanItem {
@@ -1087,11 +1080,20 @@ struct ScanItem {
Dialect language = Dialect::AnyLanguage;
};
-void ModelManagerInterface::importScan(QFutureInterface<void> &future,
- const ModelManagerInterface::WorkingCopy &workingCopy,
+void ModelManagerInterface::importScan(const WorkingCopy &workingCopy,
const PathsAndLanguages &paths,
ModelManagerInterface *modelManager,
- bool emitDocChangedOnDisk, bool libOnly, bool forceRescan)
+ bool emitDocChanged, bool libOnly, bool forceRescan)
+{
+ QPromise<void> promise;
+ promise.start();
+ importScanAsync(promise, workingCopy, paths, modelManager, emitDocChanged, libOnly, forceRescan);
+}
+
+void ModelManagerInterface::importScanAsync(QPromise<void> &promise, const WorkingCopy &workingCopy,
+ const PathsAndLanguages &paths,
+ ModelManagerInterface *modelManager,
+ bool emitDocChanged, bool libOnly, bool forceRescan)
{
// paths we have scanned for files and added to the files list
QSet<Utils::FilePath> scannedPaths;
@@ -1118,9 +1120,9 @@ void ModelManagerInterface::importScan(QFutureInterface<void> &future,
int progressRange = pathsToScan.size() * (1 << (2 + maxScanDepth));
int totalWork = progressRange;
int workDone = 0;
- future.setProgressRange(0, progressRange); // update max length while iterating?
+ promise.setProgressRange(0, progressRange); // update max length while iterating?
const Snapshot snapshot = modelManager->snapshot();
- bool isCanceled = future.isCanceled();
+ bool isCanceled = promise.isCanceled();
while (!pathsToScan.isEmpty() && !isCanceled) {
ScanItem toScan = pathsToScan.last();
pathsToScan.pop_back();
@@ -1135,16 +1137,16 @@ void ModelManagerInterface::importScan(QFutureInterface<void> &future,
toScan.language.companionLanguages());
}
workDone += 1;
- future.setProgressValue(progressRange * workDone / totalWork);
+ promise.setProgressValue(progressRange * workDone / totalWork);
if (!importedFiles.isEmpty()) {
- FutureReporter reporter(future, progressRange * pathBudget / (4 * totalWork),
+ FutureReporter reporter(promise, progressRange * pathBudget / (4 * totalWork),
progressRange * workDone / totalWork);
parseLoop(scannedPaths, newLibraries, workingCopy, importedFiles, modelManager,
- toScan.language, emitDocChangedOnDisk, reporter); // run in parallel??
+ toScan.language, emitDocChanged, reporter); // run in parallel??
importedFiles.clear();
}
workDone += pathBudget / 4 - 1;
- future.setProgressValue(progressRange * workDone / totalWork);
+ promise.setProgressValue(progressRange * workDone / totalWork);
} else {
workDone += pathBudget / 4;
}
@@ -1159,10 +1161,10 @@ void ModelManagerInterface::importScan(QFutureInterface<void> &future,
} else {
workDone += pathBudget * 3 / 4;
}
- future.setProgressValue(progressRange * workDone / totalWork);
- isCanceled = future.isCanceled();
+ promise.setProgressValue(progressRange * workDone / totalWork);
+ isCanceled = promise.isCanceled();
}
- future.setProgressValue(progressRange);
+ promise.setProgressValue(progressRange);
if (isCanceled) {
// assume no work has been done
QMutexLocker l(&modelManager->m_mutex);
@@ -1206,8 +1208,8 @@ void ModelManagerInterface::maybeScan(const PathsAndLanguages &importPaths)
}
if (pathToScan.length() >= 1) {
- QFuture<void> result = Utils::runAsync(&m_threadPool,
- &ModelManagerInterface::importScan,
+ QFuture<void> result = Utils::asyncRun(&m_threadPool,
+ &ModelManagerInterface::importScanAsync,
workingCopyInternal(), pathToScan,
this, true, true, false);
addFuture(result);
@@ -1373,8 +1375,8 @@ void ModelManagerInterface::startCppQmlTypeUpdate()
if (!cppModelManager)
return;
- m_cppQmlTypesUpdater = Utils::runAsync(&ModelManagerInterface::updateCppQmlTypes,
- this, cppModelManager->snapshot(), m_queuedCppDocuments);
+ m_cppQmlTypesUpdater = Utils::asyncRun(&ModelManagerInterface::updateCppQmlTypes, this,
+ cppModelManager->snapshot(), m_queuedCppDocuments);
m_queuedCppDocuments.clear();
}
@@ -1415,13 +1417,12 @@ bool rescanExports(const QString &fileName, FindExportedCppTypes &finder,
return hasNewInfo;
}
-void ModelManagerInterface::updateCppQmlTypes(
- QFutureInterface<void> &futureInterface, ModelManagerInterface *qmlModelManager,
- const CPlusPlus::Snapshot &snapshot,
+void ModelManagerInterface::updateCppQmlTypes(QPromise<void> &promise,
+ ModelManagerInterface *qmlModelManager, const CPlusPlus::Snapshot &snapshot,
const QHash<QString, QPair<CPlusPlus::Document::Ptr, bool>> &documents)
{
- futureInterface.setProgressRange(0, documents.size());
- futureInterface.setProgressValue(0);
+ promise.setProgressRange(0, documents.size());
+ promise.setProgressValue(0);
CppDataHash newData;
QHash<QString, QList<CPlusPlus::Document::Ptr>> newDeclarations;
@@ -1436,9 +1437,9 @@ void ModelManagerInterface::updateCppQmlTypes(
bool hasNewInfo = false;
using DocScanPair = QPair<CPlusPlus::Document::Ptr, bool>;
for (const DocScanPair &pair : documents) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
- futureInterface.setProgressValue(futureInterface.progressValue() + 1);
+ promise.setProgressValue(promise.future().progressValue() + 1);
CPlusPlus::Document::Ptr doc = pair.first;
const bool scan = pair.second;
diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h
index b7b88c24ff..e702b91afb 100644
--- a/src/libs/qmljs/qmljsmodelmanagerinterface.h
+++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h
@@ -179,10 +179,12 @@ public:
void addFuture(const QFuture<void> &future);
QmlJS::Document::Ptr ensuredGetDocumentForPath(const Utils::FilePath &filePath);
- static void importScan(QFutureInterface<void> &future, const WorkingCopy& workingCopyInternal,
- const PathsAndLanguages& paths, ModelManagerInterface *modelManager,
- bool emitDocChangedOnDisk, bool libOnly = true,
- bool forceRescan = false);
+ static void importScan(const WorkingCopy &workingCopy, const PathsAndLanguages &paths,
+ ModelManagerInterface *modelManager, bool emitDocChanged,
+ bool libOnly = true, bool forceRescan = false);
+ static void importScanAsync(QPromise<void> &promise, const WorkingCopy& workingCopyInternal,
+ const PathsAndLanguages& paths, ModelManagerInterface *modelManager,
+ bool emitDocChanged, bool libOnly = true, bool forceRescan = false);
virtual void resetCodeModel();
void removeProjectInfo(ProjectExplorer::Project *project);
@@ -218,16 +220,15 @@ protected:
QmlJS::Dialect mainLanguage,
bool emitDocChangedOnDisk,
const std::function<bool(qreal)> &reportProgress);
- static void parse(QFutureInterface<void> &future,
+ static void parse(QPromise<void> &promise,
const WorkingCopy &workingCopyInternal,
QList<Utils::FilePath> files,
ModelManagerInterface *modelManager,
QmlJS::Dialect mainLanguage,
bool emitDocChangedOnDisk);
- static void updateCppQmlTypes(
- QFutureInterface<void> &futureInterface, ModelManagerInterface *qmlModelManager,
- const CPlusPlus::Snapshot &snapshot,
- const QHash<QString, QPair<CPlusPlus::Document::Ptr, bool>> &documents);
+ static void updateCppQmlTypes(QPromise<void> &promise, ModelManagerInterface *qmlModelManager,
+ const CPlusPlus::Snapshot &snapshot,
+ const QHash<QString, QPair<CPlusPlus::Document::Ptr, bool>> &documents);
void maybeScan(const PathsAndLanguages &importPaths);
void updateImportPaths();
diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp
index a7bafdde94..761df90ddc 100644
--- a/src/libs/qmljs/qmljsplugindumper.cpp
+++ b/src/libs/qmljs/qmljsplugindumper.cpp
@@ -7,14 +7,13 @@
#include "qmljsmodelmanagerinterface.h"
#include "qmljstr.h"
#include "qmljsutils.h"
-#include "qmljsviewercontext.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <QDir>
#include <QDirIterator>
@@ -204,7 +203,7 @@ static void printParseWarnings(const FilePath &libraryPath, const QString &warni
"%2").arg(libraryPath.toUserOutput(), warning));
}
-static QString qmlPluginDumpErrorMessage(QtcProcess *process)
+static QString qmlPluginDumpErrorMessage(Process *process)
{
QString errorMessage;
const QString binary = process->commandLine().executable().toUserOutput();
@@ -238,7 +237,7 @@ static QString qmlPluginDumpErrorMessage(QtcProcess *process)
return errorMessage;
}
-void PluginDumper::qmlPluginTypeDumpDone(QtcProcess *process)
+void PluginDumper::qmlPluginTypeDumpDone(Process *process)
{
process->deleteLater();
@@ -273,14 +272,13 @@ void PluginDumper::qmlPluginTypeDumpDone(QtcProcess *process)
QStringList dependencies;
};
- auto future = Utils::runAsync(m_modelManager->threadPool(),
- [output, libraryPath](QFutureInterface<CppQmlTypesInfo>& future)
- {
+ auto future = Utils::asyncRun(m_modelManager->threadPool(),
+ [output, libraryPath](QPromise<CppQmlTypesInfo> &promise) {
CppQmlTypesInfo infos;
- CppQmlTypesLoader::parseQmlTypeDescriptions(output, &infos.objectsList, &infos.moduleApis, &infos.dependencies,
- &infos.error, &infos.warning,
- "<dump of " + libraryPath.toUserOutput() + '>');
- future.reportFinished(&infos);
+ CppQmlTypesLoader::parseQmlTypeDescriptions(output, &infos.objectsList,
+ &infos.moduleApis, &infos.dependencies, &infos.error, &infos.warning,
+ "<dump of " + libraryPath.toUserOutput() + '>');
+ promise.addResult(infos);
});
m_modelManager->addFuture(future);
@@ -327,8 +325,8 @@ void PluginDumper::pluginChanged(const QString &pluginLibrary)
QFuture<PluginDumper::QmlTypeDescription> PluginDumper::loadQmlTypeDescription(const FilePaths &paths) const
{
- auto future = Utils::runAsync(m_modelManager->threadPool(), [=](QFutureInterface<PluginDumper::QmlTypeDescription> &future)
- {
+ auto future = Utils::asyncRun(m_modelManager->threadPool(),
+ [=](QPromise<PluginDumper::QmlTypeDescription> &promise) {
PluginDumper::QmlTypeDescription result;
for (const FilePath &p: paths) {
@@ -355,8 +353,7 @@ QFuture<PluginDumper::QmlTypeDescription> PluginDumper::loadQmlTypeDescription(c
if (!warning.isEmpty())
result.warnings += warning;
}
-
- future.reportFinished(&result);
+ promise.addResult(result);
});
m_modelManager->addFuture(future);
@@ -600,11 +597,11 @@ void PluginDumper::loadQmltypesFile(const FilePaths &qmltypesFilePaths,
void PluginDumper::runQmlDump(const ModelManagerInterface::ProjectInfo &info,
const QStringList &arguments, const FilePath &importPath)
{
- auto process = new QtcProcess(this);
+ auto process = new Process(this);
process->setEnvironment(info.qmlDumpEnvironment);
process->setWorkingDirectory(importPath);
process->setCommand({info.qmlDumpPath, arguments});
- connect(process, &QtcProcess::done, this, [this, process] { qmlPluginTypeDumpDone(process); });
+ connect(process, &Process::done, this, [this, process] { qmlPluginTypeDumpDone(process); });
process->start();
m_runningQmldumps.insert(process, importPath);
}
diff --git a/src/libs/qmljs/qmljsplugindumper.h b/src/libs/qmljs/qmljsplugindumper.h
index 1b672cefdc..e27bfeba96 100644
--- a/src/libs/qmljs/qmljsplugindumper.h
+++ b/src/libs/qmljs/qmljsplugindumper.h
@@ -14,7 +14,7 @@ QT_END_NAMESPACE
namespace Utils {
class FileSystemWatcher;
-class QtcProcess;
+class Process;
}
namespace QmlJS {
@@ -41,7 +41,7 @@ private:
const QString &importUri,
const QString &importVersion);
Q_INVOKABLE void dumpAllPlugins();
- void qmlPluginTypeDumpDone(Utils::QtcProcess *process);
+ void qmlPluginTypeDumpDone(Utils::Process *process);
void pluginChanged(const QString &pluginLibrary);
private:
@@ -102,7 +102,7 @@ private:
ModelManagerInterface *m_modelManager;
Utils::FileSystemWatcher *m_pluginWatcher;
- QHash<Utils::QtcProcess *, Utils::FilePath> m_runningQmldumps;
+ QHash<Utils::Process *, Utils::FilePath> m_runningQmldumps;
QList<Plugin> m_plugins;
QHash<Utils::FilePath, int> m_libraryToPluginIndex;
QHash<QString, QmlJS::ModelManagerInterface::ProjectInfo> m_qtToInfo;
diff --git a/src/libs/qmljs/qmljsreformatter.cpp b/src/libs/qmljs/qmljsreformatter.cpp
index 461c944afa..232c0361a1 100644
--- a/src/libs/qmljs/qmljsreformatter.cpp
+++ b/src/libs/qmljs/qmljsreformatter.cpp
@@ -101,22 +101,23 @@ public:
_hadEmptyLine = false;
_binaryExpDepth = 0;
-
+ const QString &source = _doc->source();
// emit directives
if (_doc->bind()->isJsLibrary()) {
- out(QLatin1String(".pragma library"));
+ const QString pragmaLine(".pragma library");
+ out(pragmaLine, SourceLocation(source.indexOf(".pragma"), pragmaLine.length()));
newLine();
}
const QList<SourceLocation> &directives = _doc->jsDirectives();
for (const auto &d: directives) {
- quint32 line = 1;
- int i = 0;
- while (line++ < d.startLine && i++ >= 0)
- i = _doc->source().indexOf(QChar('\n'), i);
+ quint32 line = 0;
+ int i = -1;
+ while (++line < d.startLine)
+ i = source.indexOf(QChar('\n'), i + 1);
quint32 offset = static_cast<quint32>(i) + d.startColumn;
- int endline = _doc->source().indexOf('\n', static_cast<int>(offset) + 1);
- int end = endline == -1 ? _doc->source().length() : endline;
- quint32 length = static_cast<quint32>(end) - offset;
+ int endline = source.indexOf('\n', static_cast<int>(offset) + 1);
+ int end = endline == -1 ? source.length() : endline;
+ quint32 length = static_cast<quint32>(end) - offset + 1;
out(SourceLocation(offset, length, d.startLine, d.startColumn));
}
if (!directives.isEmpty())
@@ -1087,7 +1088,10 @@ protected:
out(" ");
out(ast->lparenToken);
accept(ast->lhs);
- out(" in ");
+ if (ast->type == ForEachType::In)
+ out(" in ");
+ else
+ out(" of ");
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->statement);
diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp
index e1f38f3013..9e277e6328 100644
--- a/src/libs/qmljs/qmljsscopebuilder.cpp
+++ b/src/libs/qmljs/qmljsscopebuilder.cpp
@@ -157,6 +157,7 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
if ((qmlMetaObject->className() == "ListElement"
|| qmlMetaObject->className() == "Connections")
&& (qmlMetaObject->moduleName() == "Qt" || qmlMetaObject->moduleName() == "QtQml"
+ || qmlMetaObject->moduleName() == "QtQml.Base"
|| qmlMetaObject->moduleName() == "QtQuick")) {
qmlScopeObjects.clear();
break;
diff --git a/src/libs/qmljs/qmljsscopechain.cpp b/src/libs/qmljs/qmljsscopechain.cpp
index 73ab49f4d1..1465aab5aa 100644
--- a/src/libs/qmljs/qmljsscopechain.cpp
+++ b/src/libs/qmljs/qmljsscopechain.cpp
@@ -11,6 +11,8 @@
using namespace QmlJS;
+bool ScopeChain::s_setSkipmakeComponentChain = false;
+
/*!
\class QmlJS::ScopeChain
\brief The ScopeChain class describes the scopes used for global lookup in
@@ -210,6 +212,11 @@ QList<const ObjectValue *> ScopeChain::all() const
return m_all;
}
+void ScopeChain::setSkipmakeComponentChain(bool b)
+{
+ s_setSkipmakeComponentChain = b;
+}
+
static void collectScopes(const QmlComponentChain *chain, QList<const ObjectValue *> *target)
{
for (const QmlComponentChain *parent : chain->instantiatingComponents())
@@ -351,6 +358,9 @@ void ScopeChain::makeComponentChain(
const Snapshot &snapshot,
QHash<const Document *, QmlComponentChain *> *components)
{
+ if (s_setSkipmakeComponentChain)
+ return;
+
Document::Ptr doc = target->document();
if (!doc->qmlProgram())
return;
diff --git a/src/libs/qmljs/qmljsscopechain.h b/src/libs/qmljs/qmljsscopechain.h
index f798821388..52e7c95131 100644
--- a/src/libs/qmljs/qmljsscopechain.h
+++ b/src/libs/qmljs/qmljsscopechain.h
@@ -77,6 +77,8 @@ public:
QList<const ObjectValue *> all() const;
+ static void setSkipmakeComponentChain(bool b);
+
private:
void update() const;
void initializeRootScope();
@@ -97,6 +99,8 @@ private:
mutable bool m_modified;
mutable QList<const ObjectValue *> m_all;
+
+ static bool s_setSkipmakeComponentChain;
};
} // namespace QmlJS
diff --git a/src/libs/qmljs/qmljsutils.cpp b/src/libs/qmljs/qmljsutils.cpp
index 1609e49c68..e33a6b3e80 100644
--- a/src/libs/qmljs/qmljsutils.cpp
+++ b/src/libs/qmljs/qmljsutils.cpp
@@ -115,8 +115,9 @@ SourceLocation QmlJS::fullLocationForQualifiedId(AST::UiQualifiedId *qualifiedId
}
/*!
- \returns the value of the 'id:' binding in \a object
- \param idBinding optional out parameter to get the UiScriptBinding for the id binding
+ Returns the value of the 'id:' binding in \a object.
+
+ \a idBinding is optional out parameter to get the UiScriptBinding for the id binding.
*/
QString QmlJS::idOfObject(Node *object, UiScriptBinding **idBinding)
{
diff --git a/src/libs/solutions/CMakeLists.txt b/src/libs/solutions/CMakeLists.txt
new file mode 100644
index 0000000000..694d940195
--- /dev/null
+++ b/src/libs/solutions/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(tasking)
diff --git a/src/libs/solutions/README.md b/src/libs/solutions/README.md
new file mode 100644
index 0000000000..ab4cc9727d
--- /dev/null
+++ b/src/libs/solutions/README.md
@@ -0,0 +1,48 @@
+# Solutions project
+
+The Solutions project is designed to contain a collection of
+object libraries, independent on any Creator's specific code,
+ready to be a part of Qt. Kind of a staging area for possible
+inclusions into Qt.
+
+## Motivation and benefits
+
+- Such a separation will ensure no future back dependencies to the Creator
+ specific code are introduced by mistake during maintenance.
+- Easy to compile outside of Creator code.
+- General hub of ideas to be considered by foundation team to be integrated
+ into Qt.
+- The more stuff of a general purpose goes into Qt, the less maintenance work
+ for Creator.
+
+## Conformity of solutions
+
+Each solution:
+- Is a separate object lib.
+- Is placed in a separate subdirectory.
+- Is enclosed within a namespace (namespace name = solution name).
+- Should compile independently, i.e. there are no cross-includes
+ between solutions.
+
+## Dependencies of solution libraries
+
+**Do not add dependencies to non-Qt libraries.**
+The only allowed dependencies are to Qt libraries.
+Especially, don't add dependencies to any Creator's library
+nor to any 3rd party library.
+
+If you can't avoid a dependency to the other Creator's library
+in your solution, place it somewhere else (e.g. inside Utils library).
+
+The Utils lib depends on the solution libraries.
+
+## Predictions on possible integration into Qt
+
+The solutions in this project may have a bigger / faster chance to be
+integrated into Qt when they:
+- Conform to Qt API style.
+- Integrate easily with existing classes / types in Qt
+ (instead of providing own structures / data types).
+- Have full docs.
+- Have auto tests.
+- Have at least one example (however, autotests often play this role, too).
diff --git a/src/libs/solutions/solutions.qbs b/src/libs/solutions/solutions.qbs
new file mode 100644
index 0000000000..6184dce2af
--- /dev/null
+++ b/src/libs/solutions/solutions.qbs
@@ -0,0 +1,7 @@
+Project {
+ name: "Solutions"
+
+ references: [
+ "tasking/tasking.qbs",
+ ].concat(project.additionalLibs)
+}
diff --git a/src/libs/solutions/tasking/CMakeLists.txt b/src/libs/solutions/tasking/CMakeLists.txt
new file mode 100644
index 0000000000..5beed2fe5b
--- /dev/null
+++ b/src/libs/solutions/tasking/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_qtc_library(Tasking OBJECT
+# Never add dependencies to non-Qt libraries for this library
+ DEPENDS Qt::Core
+ PUBLIC_DEFINES TASKING_LIBRARY
+ SOURCES
+ barrier.cpp barrier.h
+ tasking_global.h
+ tasktree.cpp tasktree.h
+)
diff --git a/src/libs/solutions/tasking/barrier.cpp b/src/libs/solutions/tasking/barrier.cpp
new file mode 100644
index 0000000000..c4daa033b4
--- /dev/null
+++ b/src/libs/solutions/tasking/barrier.cpp
@@ -0,0 +1,52 @@
+// 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 "barrier.h"
+
+namespace Tasking {
+
+// That's cut down qtcassert.{c,h} to avoid the dependency.
+#define QTC_STRINGIFY_HELPER(x) #x
+#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
+#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
+#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
+
+void Barrier::setLimit(int value)
+{
+ QTC_ASSERT(!isRunning(), return);
+ QTC_ASSERT(value > 0, return);
+
+ m_limit = value;
+}
+
+void Barrier::start()
+{
+ QTC_ASSERT(!isRunning(), return);
+ m_current = 0;
+ m_result = {};
+}
+
+void Barrier::advance()
+{
+ // Calling advance on finished is OK
+ QTC_ASSERT(isRunning() || m_result, return);
+ if (!isRunning()) // no-op
+ return;
+ ++m_current;
+ if (m_current == m_limit)
+ stopWithResult(true);
+}
+
+void Barrier::stopWithResult(bool success)
+{
+ // Calling stopWithResult on finished is OK when the same success is passed
+ QTC_ASSERT(isRunning() || (m_result && *m_result == success), return);
+ if (!isRunning()) // no-op
+ return;
+ m_current = -1;
+ m_result = success;
+ emit done(success);
+}
+
+} // namespace Tasking
diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h
new file mode 100644
index 0000000000..6939da5b36
--- /dev/null
+++ b/src/libs/solutions/tasking/barrier.h
@@ -0,0 +1,97 @@
+// 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 "tasking_global.h"
+
+#include "tasktree.h"
+
+namespace Tasking {
+
+class TASKING_EXPORT Barrier final : public QObject
+{
+ Q_OBJECT
+
+public:
+ void setLimit(int value);
+ int limit() const { return m_limit; }
+
+ void start();
+ void advance(); // If limit reached, stops with true
+ void stopWithResult(bool success); // Ignores limit
+
+ bool isRunning() const { return m_current >= 0; }
+ int current() const { return m_current; }
+ std::optional<bool> result() const { return m_result; }
+
+signals:
+ void done(bool success);
+
+private:
+ std::optional<bool> m_result = {};
+ int m_limit = 1;
+ int m_current = -1;
+};
+
+class TASKING_EXPORT BarrierTaskAdapter : public Tasking::TaskAdapter<Barrier>
+{
+public:
+ BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); }
+ void start() final { task()->start(); }
+};
+
+} // namespace Tasking
+
+TASKING_DECLARE_TASK(BarrierTask, Tasking::BarrierTaskAdapter);
+
+namespace Tasking {
+
+template <int Limit = 1>
+class SharedBarrier
+{
+public:
+ static_assert(Limit > 0, "SharedBarrier's limit should be 1 or more.");
+ SharedBarrier() : m_barrier(new Barrier) {
+ m_barrier->setLimit(Limit);
+ m_barrier->start();
+ }
+ Barrier *barrier() const { return m_barrier.get(); }
+
+private:
+ std::shared_ptr<Barrier> m_barrier;
+};
+
+template <int Limit = 1>
+using MultiBarrier = TreeStorage<SharedBarrier<Limit>>;
+
+// Can't write: "MultiBarrier barrier;". Only "MultiBarrier<> barrier;" would work.
+// Can't have one alias with default type in C++17, getting the following error:
+// alias template deduction only available with C++20.
+using SingleBarrier = MultiBarrier<1>;
+
+class TASKING_EXPORT WaitForBarrierTask : public BarrierTask
+{
+public:
+ template <int Limit>
+ WaitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
+ : BarrierTask([sharedBarrier](Barrier &barrier) {
+ SharedBarrier<Limit> *activeBarrier = sharedBarrier.activeStorage();
+ if (!activeBarrier) {
+ qWarning("The barrier referenced from WaitForBarrier element "
+ "is not reachable in the running tree. "
+ "It is possible that no barrier was added to the tree, "
+ "or the storage is not reachable from where it is referenced. "
+ "The WaitForBarrier task will finish with error. ");
+ return TaskAction::StopWithError;
+ }
+ Barrier *activeSharedBarrier = activeBarrier->barrier();
+ const std::optional<bool> result = activeSharedBarrier->result();
+ if (result.has_value())
+ return result.value() ? TaskAction::StopWithDone : TaskAction::StopWithError;
+ QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult);
+ return TaskAction::Continue;
+ }) {}
+};
+
+} // namespace Tasking
diff --git a/src/libs/solutions/tasking/tasking.qbs b/src/libs/solutions/tasking/tasking.qbs
new file mode 100644
index 0000000000..8697b9c009
--- /dev/null
+++ b/src/libs/solutions/tasking/tasking.qbs
@@ -0,0 +1,14 @@
+QtcLibrary {
+ name: "Tasking"
+ Depends { name: "Qt"; submodules: ["core"] }
+ cpp.defines: base.concat("TASKING_LIBRARY")
+
+ files: [
+ "barrier.cpp",
+ "barrier.h",
+ "tasking_global.h",
+ "tasktree.cpp",
+ "tasktree.h",
+ ]
+}
+
diff --git a/src/libs/solutions/tasking/tasking_global.h b/src/libs/solutions/tasking/tasking_global.h
new file mode 100644
index 0000000000..d7e76fa9e6
--- /dev/null
+++ b/src/libs/solutions/tasking/tasking_global.h
@@ -0,0 +1,14 @@
+// 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 <qglobal.h>
+
+#if defined(TASKING_LIBRARY)
+# define TASKING_EXPORT Q_DECL_EXPORT
+#elif defined(TASKING_STATIC_LIBRARY)
+# define TASKING_EXPORT
+#else
+# define TASKING_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/src/libs/utils/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp
index 90e4073e03..4a66d7f674 100644
--- a/src/libs/utils/tasktree.cpp
+++ b/src/libs/solutions/tasking/tasktree.cpp
@@ -1,16 +1,41 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 "tasktree.h"
-#include "guard.h"
-#include "qtcassert.h"
-
#include <QSet>
-namespace Utils {
namespace Tasking {
+// That's cut down qtcassert.{c,h} to avoid the dependency.
+#define QTC_STRINGIFY_HELPER(x) #x
+#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
+#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
+#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
+
+class Guard
+{
+ Q_DISABLE_COPY(Guard)
+public:
+ Guard() = default;
+ ~Guard() { QTC_CHECK(m_lockCount == 0); }
+ bool isLocked() const { return m_lockCount; }
+private:
+ int m_lockCount = 0;
+ friend class GuardLocker;
+};
+
+class GuardLocker
+{
+ Q_DISABLE_COPY(GuardLocker)
+public:
+ GuardLocker(Guard &guard) : m_guard(guard) { ++m_guard.m_lockCount; }
+ ~GuardLocker() { --m_guard.m_lockCount; }
+private:
+ Guard &m_guard;
+};
+
static TaskAction toTaskAction(bool success)
{
return success ? TaskAction::StopWithDone : TaskAction::StopWithError;
@@ -33,7 +58,12 @@ TreeStorageBase::StorageData::~StorageData()
void *TreeStorageBase::activeStorageVoid() const
{
- QTC_ASSERT(m_storageData->m_activeStorage, return nullptr);
+ QTC_ASSERT(m_storageData->m_activeStorage, qWarning(
+ "The referenced storage is not reachable in the running tree. "
+ "A nullptr will be returned which might lead to a crash in the calling code. "
+ "It is possible that no storage was added to the tree, "
+ "or the storage is not reachable from where it is referenced.");
+ return nullptr);
const auto it = m_storageData->m_storageHash.constFind(m_storageData->m_activeStorage);
QTC_ASSERT(it != m_storageData->m_storageHash.constEnd(), return nullptr);
return it.value();
@@ -84,32 +114,31 @@ Workflow optional(WorkflowPolicy::Optional);
void TaskItem::addChildren(const QList<TaskItem> &children)
{
- QTC_ASSERT(m_type == Type::Group, qWarning("Only Task may have children, skipping..."); return);
+ QTC_ASSERT(m_type == Type::Group, qWarning("Only Group may have children, skipping...");
+ return);
for (const TaskItem &child : children) {
switch (child.m_type) {
case Type::Group:
m_children.append(child);
break;
case Type::Limit:
- QTC_ASSERT(m_type == Type::Group,
- qWarning("Mode may only be a child of Group, skipping..."); return);
+ QTC_ASSERT(m_type == Type::Group, qWarning("Execution Mode may only be a child of a "
+ "Group, skipping..."); return);
m_parallelLimit = child.m_parallelLimit; // TODO: Assert on redefinition?
break;
case Type::Policy:
- QTC_ASSERT(m_type == Type::Group,
- qWarning("Workflow Policy may only be a child of Group, skipping..."); return);
+ QTC_ASSERT(m_type == Type::Group, qWarning("Workflow Policy may only be a child of a "
+ "Group, skipping..."); return);
m_workflowPolicy = child.m_workflowPolicy; // TODO: Assert on redefinition?
break;
case Type::TaskHandler:
QTC_ASSERT(child.m_taskHandler.m_createHandler,
qWarning("Task Create Handler can't be null, skipping..."); return);
- QTC_ASSERT(child.m_taskHandler.m_setupHandler,
- qWarning("Task Setup Handler can't be null, skipping..."); return);
m_children.append(child);
break;
case Type::GroupHandler:
QTC_ASSERT(m_type == Type::Group, qWarning("Group Handler may only be a "
- "child of Group, skipping..."); break);
+ "child of a Group, skipping..."); break);
QTC_ASSERT(!child.m_groupHandler.m_setupHandler
|| !m_groupHandler.m_setupHandler,
qWarning("Group Setup Handler redefinition, overriding..."));
@@ -133,19 +162,94 @@ void TaskItem::addChildren(const QList<TaskItem> &children)
}
}
-} // namespace Tasking
+void TaskItem::setTaskSetupHandler(const TaskSetupHandler &handler)
+{
+ if (!handler) {
+ qWarning("Setting empty Setup Handler is no-op, skipping...");
+ return;
+ }
+ if (m_taskHandler.m_setupHandler)
+ qWarning("Setup Handler redefinition, overriding...");
+ m_taskHandler.m_setupHandler = handler;
+}
+
+void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler)
+{
+ if (!handler) {
+ qWarning("Setting empty Done Handler is no-op, skipping...");
+ return;
+ }
+ if (m_taskHandler.m_doneHandler)
+ qWarning("Done Handler redefinition, overriding...");
+ m_taskHandler.m_doneHandler = handler;
+}
-using namespace Tasking;
+void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler)
+{
+ if (!handler) {
+ qWarning("Setting empty Error Handler is no-op, skipping...");
+ return;
+ }
+ if (m_taskHandler.m_errorHandler)
+ qWarning("Error Handler redefinition, overriding...");
+ m_taskHandler.m_errorHandler = handler;
+}
class TaskTreePrivate;
class TaskNode;
+class TaskTreePrivate
+{
+public:
+ TaskTreePrivate(TaskTree *taskTree)
+ : q(taskTree) {}
+
+ void start();
+ void stop();
+ void advanceProgress(int byValue);
+ void emitStartedAndProgress();
+ void emitProgress();
+ void emitDone();
+ void emitError();
+ QList<TreeStorageBase> addStorages(const QList<TreeStorageBase> &storages);
+ void callSetupHandler(TreeStorageBase storage, int storageId) {
+ callStorageHandler(storage, storageId, &StorageHandler::m_setupHandler);
+ }
+ void callDoneHandler(TreeStorageBase storage, int storageId) {
+ callStorageHandler(storage, storageId, &StorageHandler::m_doneHandler);
+ }
+ struct StorageHandler {
+ TaskTree::StorageVoidHandler m_setupHandler = {};
+ TaskTree::StorageVoidHandler m_doneHandler = {};
+ };
+ typedef TaskTree::StorageVoidHandler StorageHandler::*HandlerPtr; // ptr to class member
+ void callStorageHandler(TreeStorageBase storage, int storageId, HandlerPtr ptr)
+ {
+ const auto it = m_storageHandlers.constFind(storage);
+ if (it == m_storageHandlers.constEnd())
+ return;
+ GuardLocker locker(m_guard);
+ const StorageHandler storageHandler = *it;
+ storage.activateStorage(storageId);
+ if (storageHandler.*ptr)
+ (storageHandler.*ptr)(storage.activeStorageVoid());
+ storage.activateStorage(0);
+ }
+
+ TaskTree *q = nullptr;
+ Guard m_guard;
+ int m_progressValue = 0;
+ QSet<TreeStorageBase> m_storages;
+ QHash<TreeStorageBase, StorageHandler> m_storageHandlers;
+ std::unique_ptr<TaskNode> m_root = nullptr; // Keep me last in order to destruct first
+};
+
class TaskContainer
{
public:
TaskContainer(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
- TaskContainer *parentContainer)
- : m_constData(taskTreePrivate, task, parentContainer, this) {}
+ TaskNode *parentNode, TaskContainer *parentContainer)
+ : m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {}
TaskAction start();
TaskAction continueStart(TaskAction startAction, int nextChild);
TaskAction startChildren(int nextChild);
@@ -156,10 +260,11 @@ public:
bool isStarting() const { return isRunning() && m_runtimeData->m_startGuard.isLocked(); }
struct ConstData {
- ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
+ ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskNode *parentNode,
TaskContainer *parentContainer, TaskContainer *thisContainer);
~ConstData() { qDeleteAll(m_children); }
TaskTreePrivate * const m_taskTreePrivate = nullptr;
+ TaskNode * const m_parentNode = nullptr;
TaskContainer * const m_parentContainer = nullptr;
const int m_parallelLimit = 1;
@@ -196,7 +301,7 @@ public:
TaskNode(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
TaskContainer *parentContainer)
: m_taskHandler(task.taskHandler())
- , m_container(taskTreePrivate, task, parentContainer)
+ , m_container(taskTreePrivate, task, this, parentContainer)
{}
// If returned value != Continue, childDone() needs to be called in parent container (in caller)
@@ -205,7 +310,7 @@ public:
void stop();
void invokeEndHandler(bool success);
bool isRunning() const { return m_task || m_container.isRunning(); }
- bool isTask() const { return m_taskHandler.m_createHandler && m_taskHandler.m_setupHandler; }
+ bool isTask() const { return bool(m_taskHandler.m_createHandler); }
int taskCount() const { return isTask() ? 1 : m_container.m_constData.m_taskCount; }
TaskContainer *parentContainer() const { return m_container.m_constData.m_parentContainer; }
@@ -215,129 +320,107 @@ private:
std::unique_ptr<TaskInterface> m_task;
};
-class TaskTreePrivate
+void TaskTreePrivate::start()
{
-public:
- TaskTreePrivate(TaskTree *taskTree)
- : q(taskTree) {}
-
- void start() {
- QTC_ASSERT(m_root, return);
- m_progressValue = 0;
- emitStartedAndProgress();
- // TODO: check storage handlers for not existing storages in tree
- for (auto it = m_storageHandlers.cbegin(); it != m_storageHandlers.cend(); ++it) {
- QTC_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't "
- "exist in task tree. Its handlers will never be called."));
- }
- m_root->start();
- }
- void stop() {
- QTC_ASSERT(m_root, return);
- if (!m_root->isRunning())
- return;
- // TODO: should we have canceled flag (passed to handler)?
- // Just one done handler with result flag:
- // FinishedWithSuccess, FinishedWithError, Canceled, TimedOut.
- // Canceled either directly by user, or by workflow policy - doesn't matter, in both
- // cases canceled from outside.
- m_root->stop();
- emitError();
- }
- void advanceProgress(int byValue) {
- if (byValue == 0)
- return;
- QTC_CHECK(byValue > 0);
- QTC_CHECK(m_progressValue + byValue <= m_root->taskCount());
- m_progressValue += byValue;
- emitProgress();
- }
- void emitStartedAndProgress() {
- GuardLocker locker(m_guard);
- emit q->started();
- emit q->progressValueChanged(m_progressValue);
- }
- void emitProgress() {
- GuardLocker locker(m_guard);
- emit q->progressValueChanged(m_progressValue);
- }
- void emitDone() {
- QTC_CHECK(m_progressValue == m_root->taskCount());
- GuardLocker locker(m_guard);
- emit q->done();
- }
- void emitError() {
- QTC_CHECK(m_progressValue == m_root->taskCount());
- GuardLocker locker(m_guard);
- emit q->errorOccurred();
- }
- QList<TreeStorageBase> addStorages(const QList<TreeStorageBase> &storages) {
- QList<TreeStorageBase> addedStorages;
- for (const TreeStorageBase &storage : storages) {
- QTC_ASSERT(!m_storages.contains(storage), qWarning("Can't add the same storage into "
- "one TaskTree twice, skipping..."); continue);
- addedStorages << storage;
- m_storages << storage;
- }
- return addedStorages;
- }
- void callSetupHandler(TreeStorageBase storage, int storageId) {
- callStorageHandler(storage, storageId, &StorageHandler::m_setupHandler);
- }
- void callDoneHandler(TreeStorageBase storage, int storageId) {
- callStorageHandler(storage, storageId, &StorageHandler::m_doneHandler);
- }
- struct StorageHandler {
- TaskTree::StorageVoidHandler m_setupHandler = {};
- TaskTree::StorageVoidHandler m_doneHandler = {};
- };
- typedef TaskTree::StorageVoidHandler StorageHandler::*HandlerPtr; // ptr to class member
- void callStorageHandler(TreeStorageBase storage, int storageId, HandlerPtr ptr)
- {
- const auto it = m_storageHandlers.constFind(storage);
- if (it == m_storageHandlers.constEnd())
- return;
- GuardLocker locker(m_guard);
- const StorageHandler storageHandler = *it;
- storage.activateStorage(storageId);
- if (storageHandler.*ptr)
- (storageHandler.*ptr)(storage.activeStorageVoid());
- storage.activateStorage(0);
+ QTC_ASSERT(m_root, return);
+ m_progressValue = 0;
+ emitStartedAndProgress();
+ // TODO: check storage handlers for not existing storages in tree
+ for (auto it = m_storageHandlers.cbegin(); it != m_storageHandlers.cend(); ++it) {
+ QTC_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't "
+ "exist in task tree. Its handlers will never be called."));
}
+ m_root->start();
+}
- TaskTree *q = nullptr;
- Guard m_guard;
- int m_progressValue = 0;
- QSet<TreeStorageBase> m_storages;
- QHash<TreeStorageBase, StorageHandler> m_storageHandlers;
- std::unique_ptr<TaskNode> m_root = nullptr; // Keep me last in order to destruct first
-};
+void TaskTreePrivate::stop()
+{
+ QTC_ASSERT(m_root, return);
+ if (!m_root->isRunning())
+ return;
+ // TODO: should we have canceled flag (passed to handler)?
+ // Just one done handler with result flag:
+ // FinishedWithSuccess, FinishedWithError, Canceled, TimedOut.
+ // Canceled either directly by user, or by workflow policy - doesn't matter, in both
+ // cases canceled from outside.
+ m_root->stop();
+ emitError();
+}
-class StorageActivator
+void TaskTreePrivate::advanceProgress(int byValue)
+{
+ if (byValue == 0)
+ return;
+ QTC_CHECK(byValue > 0);
+ QTC_CHECK(m_progressValue + byValue <= m_root->taskCount());
+ m_progressValue += byValue;
+ emitProgress();
+}
+
+void TaskTreePrivate::emitStartedAndProgress()
+{
+ GuardLocker locker(m_guard);
+ emit q->started();
+ emit q->progressValueChanged(m_progressValue);
+}
+
+void TaskTreePrivate::emitProgress()
+{
+ GuardLocker locker(m_guard);
+ emit q->progressValueChanged(m_progressValue);
+}
+
+void TaskTreePrivate::emitDone()
+{
+ QTC_CHECK(m_progressValue == m_root->taskCount());
+ GuardLocker locker(m_guard);
+ emit q->done();
+}
+
+void TaskTreePrivate::emitError()
+{
+ QTC_CHECK(m_progressValue == m_root->taskCount());
+ GuardLocker locker(m_guard);
+ emit q->errorOccurred();
+}
+
+QList<TreeStorageBase> TaskTreePrivate::addStorages(const QList<TreeStorageBase> &storages)
+{
+ QList<TreeStorageBase> addedStorages;
+ for (const TreeStorageBase &storage : storages) {
+ QTC_ASSERT(!m_storages.contains(storage), qWarning("Can't add the same storage into "
+ "one TaskTree twice, skipping..."); continue);
+ addedStorages << storage;
+ m_storages << storage;
+ }
+ return addedStorages;
+}
+
+class ExecutionContextActivator
{
public:
- StorageActivator(TaskContainer *container)
- : m_container(container) { activateStorages(m_container); }
- ~StorageActivator() { deactivateStorages(m_container); }
+ ExecutionContextActivator(TaskContainer *container)
+ : m_container(container) { activateContext(m_container); }
+ ~ExecutionContextActivator() { deactivateContext(m_container); }
private:
- static void activateStorages(TaskContainer *container)
+ static void activateContext(TaskContainer *container)
{
QTC_ASSERT(container && container->isRunning(), return);
const TaskContainer::ConstData &constData = container->m_constData;
if (constData.m_parentContainer)
- activateStorages(constData.m_parentContainer);
+ activateContext(constData.m_parentContainer);
for (int i = 0; i < constData.m_storageList.size(); ++i)
constData.m_storageList[i].activateStorage(container->m_runtimeData->m_storageIdList.value(i));
}
- static void deactivateStorages(TaskContainer *container)
+ static void deactivateContext(TaskContainer *container)
{
QTC_ASSERT(container && container->isRunning(), return);
const TaskContainer::ConstData &constData = container->m_constData;
for (int i = constData.m_storageList.size() - 1; i >= 0; --i) // iterate in reverse order
constData.m_storageList[i].activateStorage(0);
if (constData.m_parentContainer)
- deactivateStorages(constData.m_parentContainer);
+ deactivateContext(constData.m_parentContainer);
}
TaskContainer *m_container = nullptr;
};
@@ -346,7 +429,7 @@ template <typename Handler, typename ...Args,
typename ReturnType = typename std::invoke_result_t<Handler, Args...>>
ReturnType invokeHandler(TaskContainer *container, Handler &&handler, Args &&...args)
{
- StorageActivator activator(container);
+ ExecutionContextActivator activator(container);
GuardLocker locker(container->m_constData.m_taskTreePrivate->m_guard);
return std::invoke(std::forward<Handler>(handler), std::forward<Args>(args)...);
}
@@ -362,8 +445,10 @@ static QList<TaskNode *> createChildren(TaskTreePrivate *taskTreePrivate, TaskCo
}
TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
- TaskContainer *parentContainer, TaskContainer *thisContainer)
+ TaskNode *parentNode, TaskContainer *parentContainer,
+ TaskContainer *thisContainer)
: m_taskTreePrivate(taskTreePrivate)
+ , m_parentNode(parentNode)
, m_parentContainer(parentContainer)
, m_parallelLimit(task.parallelLimit())
, m_workflowPolicy(task.workflowPolicy())
@@ -440,8 +525,10 @@ TaskAction TaskContainer::start()
if (startAction != TaskAction::Continue)
m_constData.m_taskTreePrivate->advanceProgress(m_constData.m_taskCount);
}
- if (m_constData.m_children.isEmpty() && startAction == TaskAction::Continue)
- startAction = TaskAction::StopWithDone;
+ if (startAction == TaskAction::Continue) {
+ if (m_constData.m_children.isEmpty())
+ startAction = TaskAction::StopWithDone;
+ }
return continueStart(startAction, 0);
}
@@ -547,8 +634,9 @@ TaskAction TaskNode::start()
return m_container.start();
m_task.reset(m_taskHandler.m_createHandler());
- const TaskAction startAction = invokeHandler(parentContainer(), m_taskHandler.m_setupHandler,
- *m_task.get());
+ const TaskAction startAction = m_taskHandler.m_setupHandler
+ ? invokeHandler(parentContainer(), m_taskHandler.m_setupHandler, *m_task.get())
+ : TaskAction::Continue;
if (startAction != TaskAction::Continue) {
m_container.m_constData.m_taskTreePrivate->advanceProgress(1);
m_task.reset();
@@ -598,33 +686,32 @@ void TaskNode::invokeEndHandler(bool success)
}
/*!
- \class Utils::TaskTree
- \inheaderfile utils/tasktree.h
+ \class TaskTree
+ \inheaderfile solutions/tasking/tasktree.h
\inmodule QtCreator
\ingroup mainclasses
\brief The TaskTree class runs an async task tree structure defined in a
declarative way.
Use the Tasking namespace to build extensible, declarative task tree
- structures that contain possibly asynchronous tasks, such as QtcProcess,
- FileTransfer, or AsyncTask<ReturnType>. TaskTree structures enable you
+ structures that contain possibly asynchronous tasks, such as Process,
+ FileTransfer, or Async<ReturnType>. TaskTree structures enable you
to create a sophisticated mixture of a parallel or sequential flow of tasks
in the form of a tree and to run it any time later.
\section1 Root Element and Tasks
The TaskTree has a mandatory Group root element, which may contain
- any number of tasks of various types, such as Process, FileTransfer,
+ any number of tasks of various types, such as ProcessTask, FileTransferTask,
or AsyncTask<ReturnType>:
\code
- using namespace Utils;
using namespace Tasking;
const Group root {
- Process(...),
- Async<int>(...),
- Transfer(...)
+ ProcessTask(...),
+ AsyncTask<int>(...),
+ FileTransferTask(...)
};
TaskTree *taskTree = new TaskTree(root);
@@ -634,11 +721,11 @@ void TaskNode::invokeEndHandler(bool success)
\endcode
The task tree above has a top level element of the Group type that contains
- tasks of the type QtcProcess, FileTransfer, and AsyncTask<int>.
+ tasks of the type ProcessTask, FileTransferTask, and AsyncTask<int>.
After taskTree->start() is called, the tasks are run in a chain, starting
- with Process. When Process finishes successfully, the Async<int> task is
+ with ProcessTask. When the ProcessTask finishes successfully, the AsyncTask<int> task is
started. Finally, when the asynchronous task finishes successfully, the
- FileTransfer task is started.
+ FileTransferTask task is started.
When the last running task finishes with success, the task tree is considered
to have run successfully and the TaskTree::done() signal is emitted.
@@ -656,27 +743,27 @@ void TaskNode::invokeEndHandler(bool success)
const Group root {
Group {
parallel,
- Process(...),
- Async<int>(...)
+ ProcessTask(...),
+ AsyncTask<int>(...)
},
- Transfer(...)
+ FileTransferTask(...)
};
\endcode
The example above differs from the first example in that the root element has
- a subgroup that contains the Process and Async<int> tasks. The subgroup is a
- sibling element of the Transfer task in the root. The subgroup contains an
+ a subgroup that contains the ProcessTask and AsyncTask<int>. The subgroup is a
+ sibling element of the FileTransferTask in the root. The subgroup contains an
additional \e parallel element that instructs its Group to execute its tasks
in parallel.
- So, when the tree above is started, the Process and Async<int> tasks start
+ So, when the tree above is started, the ProcessTask and AsyncTask<int> start
immediately and run in parallel. Since the root group doesn't contain a
\e parallel element, its direct child tasks are run in sequence. Thus, the
- Transfer task starts when the whole subgroup finishes. The group is
+ FileTransferTask starts when the whole subgroup finishes. The group is
considered as finished when all its tasks have finished. The order in which
the tasks finish is not relevant.
- So, depending on which task lasts longer (Process or Async<int>), the
+ So, depending on which task lasts longer (ProcessTask or AsyncTask<int>), the
following scenarios can take place:
\table
@@ -690,35 +777,35 @@ void TaskNode::invokeEndHandler(bool success)
\li Sub Group starts
\li Sub Group starts
\row
- \li Process starts
- \li Process starts
+ \li ProcessTask starts
+ \li ProcessTask starts
\row
- \li Async<int> starts
- \li Async<int> starts
+ \li AsyncTask<int> starts
+ \li AsyncTask<int> starts
\row
\li ...
\li ...
\row
- \li \b {Process finishes}
- \li \b {Async<int> finishes}
+ \li \b {ProcessTask finishes}
+ \li \b {AsyncTask<int> finishes}
\row
\li ...
\li ...
\row
- \li \b {Async<int> finishes}
- \li \b {Process finishes}
+ \li \b {AsyncTask<int> finishes}
+ \li \b {ProcessTask finishes}
\row
\li Sub Group finishes
\li Sub Group finishes
\row
- \li Transfer starts
- \li Transfer starts
+ \li FileTransferTask starts
+ \li FileTransferTask starts
\row
\li ...
\li ...
\row
- \li Transfer finishes
- \li Transfer finishes
+ \li FileTransferTask finishes
+ \li FileTransferTask finishes
\row
\li Root Group finishes
\li Root Group finishes
@@ -731,24 +818,24 @@ void TaskNode::invokeEndHandler(bool success)
The presented scenarios assume that all tasks run successfully. If a task
fails during execution, the task tree finishes with an error. In particular,
- when Process finishes with an error while Async<int> is still being executed,
- Async<int> is automatically stopped, the subgroup finishes with an error,
- Transfer is skipped, and the tree finishes with an error.
+ when ProcessTask finishes with an error while AsyncTask<int> is still being executed,
+ the AsyncTask<int> is automatically stopped, the subgroup finishes with an error,
+ the FileTransferTask is skipped, and the tree finishes with an error.
\section1 Task Types
Each task type is associated with its corresponding task class that executes
- the task. For example, a Process task inside a task tree is associated with
- the QtcProcess class that executes the process. The associated objects are
+ the task. For example, a ProcessTask inside a task tree is associated with
+ the Process class that executes the process. The associated objects are
automatically created, started, and destructed exclusively by the task tree
at the appropriate time.
- If a root group consists of five sequential Process tasks, and the task tree
- executes the group, it creates an instance of QtcProcess for the first
- Process task and starts it. If the QtcProcess instance finishes successfully,
- the task tree destructs it and creates a new QtcProcess instance for the
- second Process, and so on. If the first task finishes with an error, the task
- tree stops creating QtcProcess instances, and the root group finishes with an
+ If a root group consists of five sequential ProcessTask tasks, and the task tree
+ executes the group, it creates an instance of Process for the first
+ ProcessTask and starts it. If the Process instance finishes successfully,
+ the task tree destructs it and creates a new Process instance for the
+ second ProcessTask, and so on. If the first task finishes with an error, the task
+ tree stops creating Process instances, and the root group finishes with an
error.
The following table shows examples of task types and their corresponding task
@@ -760,19 +847,19 @@ void TaskNode::invokeEndHandler(bool success)
\li Associated Task Class
\li Brief Description
\row
- \li Process
- \li Utils::QtcProcess
+ \li ProcessTask
+ \li Utils::Process
\li Starts processes.
\row
- \li Async<ReturnType>
- \li Utils::AsyncTask<ReturnType>
+ \li AsyncTask<ReturnType>
+ \li Utils::Async<ReturnType>
\li Starts asynchronous tasks; run in separate thread.
\row
- \li Tree
+ \li TaskTreeTask
\li Utils::TaskTree
\li Starts a nested task tree.
\row
- \li Transfer
+ \li FileTransferTask
\li ProjectExplorer::FileTransfer
\li Starts file transfer between different devices.
\endtable
@@ -789,15 +876,15 @@ void TaskNode::invokeEndHandler(bool success)
handler should always take a \e reference to the associated task class object:
\code
- const auto onSetup = [](QtcProcess &process) {
+ const auto onSetup = [](Process &process) {
process.setCommand({"sleep", {"3"}});
};
const Group root {
- Process(onSetup)
+ ProcessTask(onSetup)
};
\endcode
- You can modify the passed QtcProcess in the setup handler, so that the task
+ You can modify the passed Process in the setup handler, so that the task
tree can start the process according to your configuration.
You do not need to call \e {process.start();} in the setup handler,
as the task tree calls it when needed. The setup handler is mandatory
@@ -837,21 +924,21 @@ void TaskNode::invokeEndHandler(bool success)
to the associated task class object:
\code
- const auto onSetup = [](QtcProcess &process) {
+ const auto onSetup = [](Process &process) {
process.setCommand({"sleep", {"3"}});
};
- const auto onDone = [](const QtcProcess &process) {
+ const auto onDone = [](const Process &process) {
qDebug() << "Success" << process.cleanedStdOut();
};
- const auto onError = [](const QtcProcess &process) {
+ const auto onError = [](const Process &process) {
qDebug() << "Failure" << process.cleanedStdErr();
};
const Group root {
- Process(onSetup, onDone, onError)
+ ProcessTask(onSetup, onDone, onError)
};
\endcode
- The done and error handlers may collect output data from QtcProcess, and store it
+ The done and error handlers may collect output data from Process, and store it
for further processing or perform additional actions. The done handler is optional.
When used, it must be the second argument of the task constructor.
The error handler must always be the third argument.
@@ -877,7 +964,7 @@ void TaskNode::invokeEndHandler(bool success)
};
const Group root {
OnGroupSetup(onGroupSetup),
- Process(...)
+ ProcessTask(...)
};
\endcode
@@ -900,17 +987,17 @@ void TaskNode::invokeEndHandler(bool success)
OnGroupSetup([] { qDebug() << "Root setup"; }),
Group {
OnGroupSetup([] { qDebug() << "Group 1 setup"; return TaskAction::Continue; }),
- Process(...) // Process 1
+ ProcessTask(...) // Process 1
},
Group {
OnGroupSetup([] { qDebug() << "Group 2 setup"; return TaskAction::StopWithDone; }),
- Process(...) // Process 2
+ ProcessTask(...) // Process 2
},
Group {
OnGroupSetup([] { qDebug() << "Group 3 setup"; return TaskAction::StopWithError; }),
- Process(...) // Process 3
+ ProcessTask(...) // Process 3
},
- Process(...) // Process 4
+ ProcessTask(...) // Process 4
};
\endcode
@@ -972,7 +1059,7 @@ void TaskNode::invokeEndHandler(bool success)
\code
const Group root {
OnGroupSetup([] { qDebug() << "Root setup"; }),
- Process(...),
+ ProcessTask(...),
OnGroupDone([] { qDebug() << "Root finished with success"; }),
OnGroupError([] { qDebug() << "Root finished with error"; })
};
@@ -1124,7 +1211,7 @@ void TaskNode::invokeEndHandler(bool success)
const TreeStorage<CopyStorage> storage;
const auto onLoaderSetup = [source](Async<QByteArray> &async) {
- async.setAsyncCallData(&load, source);
+ async.setConcurrentCallData(&load, source);
};
// [4] runtime: task tree activates the instance from [5] before invoking handler
const auto onLoaderDone = [storage](const Async<QByteArray> &async) {
@@ -1133,7 +1220,7 @@ void TaskNode::invokeEndHandler(bool success)
// [4] runtime: task tree activates the instance from [5] before invoking handler
const auto onSaverSetup = [storage, destination](Async<void> &async) {
- async.setAsyncCallData(&save, destination, storage->content);
+ async.setConcurrentCallData(&save, destination, storage->content);
};
const auto onSaverDone = [](const Async<void> &async) {
qDebug() << "Save done successfully";
@@ -1142,8 +1229,8 @@ void TaskNode::invokeEndHandler(bool success)
const Group root {
// [5] runtime: task tree creates an instance of CopyStorage when root is entered
Storage(storage),
- Async<QByteArray>(onLoaderSetup, onLoaderDone),
- Async<void>(onSaverSetup, onSaverDone)
+ AsyncTask<QByteArray>(onLoaderSetup, onLoaderDone),
+ AsyncTask<void>(onSaverSetup, onSaverDone)
};
return root;
}
@@ -1197,7 +1284,7 @@ void TaskNode::invokeEndHandler(bool success)
recipe described by the Group root element.
As TaskTree is also an asynchronous task, it can be a part of another TaskTree.
- To place a nested TaskTree inside another TaskTree, insert the Tasking::Tree
+ To place a nested TaskTree inside another TaskTree, insert the TaskTreeTask
element into other tree's Group element.
TaskTree reports progress of completed tasks when running. The progress value
@@ -1256,7 +1343,7 @@ void TaskNode::invokeEndHandler(bool success)
asynchronous task:
\code
- class TimeoutAdapter : public Utils::Tasking::TaskAdapter<QTimer>
+ class TimeoutAdapter : public Tasking::TaskAdapter<QTimer>
{
public:
TimeoutAdapter() {
@@ -1284,7 +1371,7 @@ void TaskNode::invokeEndHandler(bool success)
To make QTimer accessible inside TaskTree under the \e Timeout name,
register it with QTC_DECLARE_CUSTOM_TASK(Timeout, TimeoutAdapter). Timeout
- becomes a new task type inside Utils::Tasking namespace, using TimeoutAdapter.
+ becomes a new task type inside Tasking namespace, using TimeoutAdapter.
The new task type is now registered, and you can use it in TaskTree:
@@ -1324,10 +1411,11 @@ TaskTree::~TaskTree()
{
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("Deleting TaskTree instance directly from "
"one of its handlers will lead to crash!"));
+ // TODO: delete storages explicitly here?
delete d;
}
-void TaskTree::setupRoot(const Tasking::Group &root)
+void TaskTree::setupRoot(const Group &root)
{
QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return);
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The setupRoot() is called from one of the"
@@ -1366,7 +1454,7 @@ int TaskTree::progressValue() const
return d->m_progressValue;
}
-void TaskTree::setupStorageHandler(const Tasking::TreeStorageBase &storage,
+void TaskTree::setupStorageHandler(const TreeStorageBase &storage,
StorageVoidHandler setupHandler,
StorageVoidHandler doneHandler)
{
@@ -1387,15 +1475,15 @@ void TaskTree::setupStorageHandler(const Tasking::TreeStorageBase &storage,
}
}
-TaskTreeAdapter::TaskTreeAdapter()
+TaskTreeTaskAdapter::TaskTreeTaskAdapter()
{
connect(task(), &TaskTree::done, this, [this] { emit done(true); });
connect(task(), &TaskTree::errorOccurred, this, [this] { emit done(false); });
}
-void TaskTreeAdapter::start()
+void TaskTreeTaskAdapter::start()
{
task()->start();
}
-} // namespace Utils
+} // namespace Tasking
diff --git a/src/libs/utils/tasktree.h b/src/libs/solutions/tasking/tasktree.h
index 54a0cfda53..bbee1a3fe8 100644
--- a/src/libs/utils/tasktree.h
+++ b/src/libs/solutions/tasking/tasktree.h
@@ -1,23 +1,22 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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_global.h"
+#include "tasking_global.h"
#include <QHash>
#include <QObject>
#include <QSharedPointer>
-namespace Utils {
+namespace Tasking {
-class StorageActivator;
+class ExecutionContextActivator;
class TaskContainer;
+class TaskNode;
class TaskTreePrivate;
-namespace Tasking {
-
-class QTCREATOR_UTILS_EXPORT TaskInterface : public QObject
+class TASKING_EXPORT TaskInterface : public QObject
{
Q_OBJECT
@@ -29,7 +28,7 @@ signals:
void done(bool success);
};
-class QTCREATOR_UTILS_EXPORT TreeStorageBase
+class TASKING_EXPORT TreeStorageBase
{
public:
bool isValid() const;
@@ -66,7 +65,7 @@ private:
QSharedPointer<StorageData> m_storageData;
friend TaskContainer;
friend TaskTreePrivate;
- friend StorageActivator;
+ friend ExecutionContextActivator;
};
template <typename StorageStruct>
@@ -74,6 +73,7 @@ class TreeStorage : public TreeStorageBase
{
public:
TreeStorage() : TreeStorageBase(TreeStorage::ctor(), TreeStorage::dtor()) {}
+ StorageStruct &operator*() const noexcept { return *activeStorage(); }
StorageStruct *operator->() const noexcept { return activeStorage(); }
StorageStruct *activeStorage() const {
return static_cast<StorageStruct *>(activeStorageVoid());
@@ -86,20 +86,21 @@ private:
}
};
-// 4 policies:
+// WorkflowPolicy:
// 1. When all children finished with done -> report done, otherwise:
// a) Report error on first error and stop executing other children (including their subtree)
-// b) On first error - wait for all children to be finished and report error afterwards
+// b) On first error - continue executing all children and report error afterwards
// 2. When all children finished with error -> report error, otherwise:
// a) Report done on first done and stop executing other children (including their subtree)
-// b) On first done - wait for all children to be finished and report done afterwards
+// b) On first done - continue executing all children and report done afterwards
+// 3. Always run all children, ignore their result and report done afterwards
enum class WorkflowPolicy {
- StopOnError, // 1a - Will report error on any child error, otherwise done (if all children were done)
- ContinueOnError, // 1b - the same. When no children it reports done.
- StopOnDone, // 2a - Will report done on any child done, otherwise error (if all children were error)
- ContinueOnDone, // 2b - the same. When no children it reports done. (?)
- Optional // Returns always done after all children finished
+ StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done)
+ ContinueOnError, // 1b - The same, but children execution continues. When no children it reports done.
+ StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error)
+ ContinueOnDone, // 2b - The same, but children execution continues. When no children it reports done. (?)
+ Optional // 3 - Always reports done after all children finished
};
enum class TaskAction
@@ -109,7 +110,7 @@ enum class TaskAction
StopWithError
};
-class QTCREATOR_UTILS_EXPORT TaskItem
+class TASKING_EXPORT TaskItem
{
public:
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
@@ -125,9 +126,9 @@ public:
struct TaskHandler {
TaskCreateHandler m_createHandler;
- TaskSetupHandler m_setupHandler;
- TaskEndHandler m_doneHandler;
- TaskEndHandler m_errorHandler;
+ TaskSetupHandler m_setupHandler = {};
+ TaskEndHandler m_doneHandler = {};
+ TaskEndHandler m_errorHandler = {};
};
struct GroupHandler {
@@ -171,6 +172,10 @@ protected:
, m_storageList{storage} {}
void addChildren(const QList<TaskItem> &children);
+ void setTaskSetupHandler(const TaskSetupHandler &handler);
+ void setTaskDoneHandler(const TaskEndHandler &handler);
+ void setTaskErrorHandler(const TaskEndHandler &handler);
+
private:
Type m_type = Type::Group;
int m_parallelLimit = 1; // 0 means unlimited
@@ -181,32 +186,32 @@ private:
QList<TaskItem> m_children;
};
-class QTCREATOR_UTILS_EXPORT Group : public TaskItem
+class TASKING_EXPORT Group : public TaskItem
{
public:
Group(const QList<TaskItem> &children) { addChildren(children); }
Group(std::initializer_list<TaskItem> children) { addChildren(children); }
};
-class QTCREATOR_UTILS_EXPORT Storage : public TaskItem
+class TASKING_EXPORT Storage : public TaskItem
{
public:
Storage(const TreeStorageBase &storage) : TaskItem(storage) { }
};
-class QTCREATOR_UTILS_EXPORT ParallelLimit : public TaskItem
+class TASKING_EXPORT ParallelLimit : public TaskItem
{
public:
ParallelLimit(int parallelLimit) : TaskItem(qMax(parallelLimit, 0)) {}
};
-class QTCREATOR_UTILS_EXPORT Workflow : public TaskItem
+class TASKING_EXPORT Workflow : public TaskItem
{
public:
Workflow(WorkflowPolicy policy) : TaskItem(policy) {}
};
-class QTCREATOR_UTILS_EXPORT OnGroupSetup : public TaskItem
+class TASKING_EXPORT OnGroupSetup : public TaskItem
{
public:
template <typename SetupFunction>
@@ -232,25 +237,52 @@ private:
};
};
-class QTCREATOR_UTILS_EXPORT OnGroupDone : public TaskItem
+class TASKING_EXPORT OnGroupDone : public TaskItem
{
public:
OnGroupDone(const GroupEndHandler &handler) : TaskItem({{}, handler}) {}
};
-class QTCREATOR_UTILS_EXPORT OnGroupError : public TaskItem
+class TASKING_EXPORT OnGroupError : public TaskItem
{
public:
OnGroupError(const GroupEndHandler &handler) : TaskItem({{}, {}, handler}) {}
};
-QTCREATOR_UTILS_EXPORT extern ParallelLimit sequential;
-QTCREATOR_UTILS_EXPORT extern ParallelLimit parallel;
-QTCREATOR_UTILS_EXPORT extern Workflow stopOnError;
-QTCREATOR_UTILS_EXPORT extern Workflow continueOnError;
-QTCREATOR_UTILS_EXPORT extern Workflow stopOnDone;
-QTCREATOR_UTILS_EXPORT extern Workflow continueOnDone;
-QTCREATOR_UTILS_EXPORT extern Workflow optional;
+// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
+class TASKING_EXPORT Sync : public Group
+{
+
+public:
+ template<typename Function>
+ Sync(Function &&function) : Group(init(std::forward<Function>(function))) {}
+
+private:
+ template<typename Function>
+ static QList<TaskItem> init(Function &&function) {
+ constexpr bool isInvocable = std::is_invocable_v<std::decay_t<Function>>;
+ static_assert(isInvocable,
+ "Sync element: The synchronous function can't take any arguments.");
+ constexpr bool isBool = std::is_same_v<bool, std::invoke_result_t<std::decay_t<Function>>>;
+ constexpr bool isVoid = std::is_same_v<void, std::invoke_result_t<std::decay_t<Function>>>;
+ static_assert(isBool || isVoid,
+ "Sync element: The synchronous function has to return void or bool.");
+ if constexpr (isBool) {
+ return {OnGroupSetup([function] { return function() ? TaskAction::StopWithDone
+ : TaskAction::StopWithError; })};
+ }
+ return {OnGroupSetup([function] { function(); return TaskAction::StopWithDone; })};
+ };
+
+};
+
+TASKING_EXPORT extern ParallelLimit sequential;
+TASKING_EXPORT extern ParallelLimit parallel;
+TASKING_EXPORT extern Workflow stopOnError;
+TASKING_EXPORT extern Workflow continueOnError;
+TASKING_EXPORT extern Workflow stopOnDone;
+TASKING_EXPORT extern Workflow continueOnDone;
+TASKING_EXPORT extern Workflow optional;
template <typename Task>
class TaskAdapter : public TaskInterface
@@ -271,11 +303,26 @@ public:
using Task = typename Adapter::Type;
using EndHandler = std::function<void(const Task &)>;
static Adapter *createAdapter() { return new Adapter; }
+ CustomTask() : TaskItem({&createAdapter}) {}
template <typename SetupFunction>
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {})
: TaskItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)),
wrapEnd(done), wrapEnd(error)}) {}
+ template <typename SetupFunction>
+ CustomTask &onSetup(SetupFunction &&function) {
+ setTaskSetupHandler(wrapSetup(std::forward<SetupFunction>(function)));
+ return *this;
+ }
+ CustomTask &onDone(const EndHandler &handler) {
+ setTaskDoneHandler(wrapEnd(handler));
+ return *this;
+ }
+ CustomTask &onError(const EndHandler &handler) {
+ setTaskErrorHandler(wrapEnd(handler));
+ return *this;
+ }
+
private:
template<typename SetupFunction>
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
@@ -305,20 +352,18 @@ private:
};
};
-} // namespace Tasking
-
class TaskTreePrivate;
-class QTCREATOR_UTILS_EXPORT TaskTree : public QObject
+class TASKING_EXPORT TaskTree final : public QObject
{
Q_OBJECT
public:
TaskTree();
- TaskTree(const Tasking::Group &root);
+ TaskTree(const Group &root);
~TaskTree();
- void setupRoot(const Tasking::Group &root);
+ void setupRoot(const Group &root);
void start();
void stop();
@@ -329,14 +374,12 @@ public:
int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded
template <typename StorageStruct, typename StorageHandler>
- void onStorageSetup(const Tasking::TreeStorage<StorageStruct> &storage,
- StorageHandler &&handler) {
+ void onStorageSetup(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
setupStorageHandler(storage,
wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)), {});
}
template <typename StorageStruct, typename StorageHandler>
- void onStorageDone(const Tasking::TreeStorage<StorageStruct> &storage,
- StorageHandler &&handler) {
+ void onStorageDone(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
setupStorageHandler(storage,
{}, wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)));
}
@@ -349,7 +392,7 @@ signals:
private:
using StorageVoidHandler = std::function<void(void *)>;
- void setupStorageHandler(const Tasking::TreeStorageBase &storage,
+ void setupStorageHandler(const TreeStorageBase &storage,
StorageVoidHandler setupHandler,
StorageVoidHandler doneHandler);
template <typename StorageStruct, typename StorageHandler>
@@ -364,22 +407,22 @@ private:
TaskTreePrivate *d;
};
-class QTCREATOR_UTILS_EXPORT TaskTreeAdapter : public Tasking::TaskAdapter<TaskTree>
+class TASKING_EXPORT TaskTreeTaskAdapter : public TaskAdapter<TaskTree>
{
public:
- TaskTreeAdapter();
+ TaskTreeTaskAdapter();
void start() final;
};
-} // namespace Utils
+} // namespace Tasking
-#define QTC_DECLARE_CUSTOM_TASK(CustomTaskName, TaskAdapterClass)\
-namespace Utils::Tasking { using CustomTaskName = CustomTask<TaskAdapterClass>; }
+#define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\
+namespace Tasking { using CustomTaskName = CustomTask<TaskAdapterClass>; }
-#define QTC_DECLARE_CUSTOM_TEMPLATE_TASK(CustomTaskName, TaskAdapterClass)\
-namespace Utils::Tasking {\
+#define TASKING_DECLARE_TEMPLATE_TASK(CustomTaskName, TaskAdapterClass)\
+namespace Tasking {\
template <typename ...Args>\
using CustomTaskName = CustomTask<TaskAdapterClass<Args...>>;\
-} // namespace Utils::Tasking
+} // namespace Tasking
-QTC_DECLARE_CUSTOM_TASK(Tree, Utils::TaskTreeAdapter);
+TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter);
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 6f5b165af1..4c7cd774c6 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -1,5 +1,5 @@
add_qtc_library(SqliteC OBJECT
- PROPERTIES AUTOMOC OFF AUTOUIC OFF QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON POSITION_INDEPENDENT_CODE ON
+ PROPERTIES AUTOMOC OFF AUTOUIC OFF QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
DEFINES SQLITE_CORE SQLITE_CUSTOM_INCLUDE=config.h $<$<CONFIG:Debug>:SQLITE_DEBUG>
PROPERTIES COMPILE_OPTIONS $<IF:$<CXX_COMPILER_ID:MSVC>,/FIconfig.h,-includeconfig.h>
PUBLIC_INCLUDES
@@ -32,7 +32,9 @@ add_qtc_library(Sqlite
sqlitedatabaseinterface.h
sqliteexception.cpp sqliteexception.h
sqliteglobal.cpp sqliteglobal.h
+ sqlitefunctionregistry.cpp sqlitefunctionregistry.h
sqliteindex.h
+ sqliteprogresshandler.h
sqlitereadstatement.h
sqlitereadwritestatement.h
sqlitesessionchangeset.cpp sqlitesessionchangeset.h
diff --git a/src/libs/sqlite/constraints.h b/src/libs/sqlite/constraints.h
index 575c1e6c82..c934c1f830 100644
--- a/src/libs/sqlite/constraints.h
+++ b/src/libs/sqlite/constraints.h
@@ -4,8 +4,8 @@
#pragma once
#include "sqliteglobal.h"
+#include "sqlitevalue.h"
-#include <sqlitevalue.h>
#include <utils/smallstring.h>
#include <variant>
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index 9bdc9ceec6..91b417bea1 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -7,7 +7,7 @@
#include "sqlitedatabasebackend.h"
#include "sqliteexception.h"
-#include "sqlite.h"
+#include <sqlite.h>
#include <condition_variable>
#include <mutex>
@@ -27,17 +27,11 @@ extern "C" int sqlite3_carray_bind(
namespace Sqlite {
BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database)
- : m_compiledStatement(nullptr, deleteCompiledStatement)
- , m_database(database)
+ : m_database(database)
{
prepare(sqlStatement);
}
-void BaseStatement::deleteCompiledStatement(sqlite3_stmt *compiledStatement)
-{
- sqlite3_finalize(compiledStatement);
-}
-
class UnlockNotification
{
public:
@@ -79,7 +73,7 @@ void BaseStatement::waitForUnlockNotify() const
&unlockNotification);
if (resultCode == SQLITE_LOCKED)
- throw DeadLock("SqliteStatement::waitForUnlockNotify: database is in a dead lock!");
+ throw DeadLock();
unlockNotification.wait();
}
@@ -107,7 +101,7 @@ bool BaseStatement::next() const
else if (resultCode == SQLITE_DONE)
return false;
- checkForStepError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::step() const
@@ -119,7 +113,7 @@ void BaseStatement::bindNull(int index)
{
int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, NullValue)
@@ -131,21 +125,21 @@ void BaseStatement::bind(int index, int value)
{
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, long long value)
{
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, double value)
{
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, void *pointer)
@@ -156,7 +150,7 @@ void BaseStatement::bind(int index, void *pointer)
"carray",
nullptr);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, Utils::span<const int> values)
@@ -168,7 +162,7 @@ void BaseStatement::bind(int index, Utils::span<const int> values)
CARRAY_INT32,
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, Utils::span<const long long> values)
@@ -180,7 +174,7 @@ void BaseStatement::bind(int index, Utils::span<const long long> values)
CARRAY_INT64,
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, Utils::span<const double> values)
@@ -192,7 +186,7 @@ void BaseStatement::bind(int index, Utils::span<const double> values)
CARRAY_DOUBLE,
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, Utils::span<const char *> values)
@@ -204,7 +198,7 @@ void BaseStatement::bind(int index, Utils::span<const char *> values)
CARRAY_TEXT,
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, Utils::SmallStringView text)
@@ -215,7 +209,7 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
int(text.size()),
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, BlobView blobView)
@@ -233,7 +227,7 @@ void BaseStatement::bind(int index, BlobView blobView)
}
if (resultCode != SQLITE_OK)
- checkForBindingError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void BaseStatement::bind(int index, const Value &value)
@@ -298,7 +292,7 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
if (resultCode != SQLITE_OK)
- checkForPrepareError(resultCode);
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
sqlite3 *BaseStatement::sqliteDatabaseHandle() const
@@ -306,197 +300,16 @@ sqlite3 *BaseStatement::sqliteDatabaseHandle() const
return m_database.backend().sqliteDatabaseHandle();
}
-void BaseStatement::checkForStepError(int resultCode) const
-{
- switch (resultCode) {
- case SQLITE_BUSY_RECOVERY:
- case SQLITE_BUSY_SNAPSHOT:
- case SQLITE_BUSY_TIMEOUT:
- case SQLITE_BUSY:
- throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to "
- "acquire the database locks!");
- case SQLITE_ERROR_MISSING_COLLSEQ:
- case SQLITE_ERROR_RETRY:
- case SQLITE_ERROR_SNAPSHOT:
- case SQLITE_ERROR:
- throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a "
- "constraint violation) has occurred!");
- case SQLITE_MISUSE:
- throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
- case SQLITE_CONSTRAINT_CHECK:
- case SQLITE_CONSTRAINT_COMMITHOOK:
- case SQLITE_CONSTRAINT_DATATYPE:
- case SQLITE_CONSTRAINT_FOREIGNKEY:
- case SQLITE_CONSTRAINT_FUNCTION:
- case SQLITE_CONSTRAINT_NOTNULL:
- case SQLITE_CONSTRAINT_PINNED:
- case SQLITE_CONSTRAINT_PRIMARYKEY:
- case SQLITE_CONSTRAINT_ROWID:
- case SQLITE_CONSTRAINT_TRIGGER:
- case SQLITE_CONSTRAINT_UNIQUE:
- case SQLITE_CONSTRAINT_VTAB:
- case SQLITE_CONSTRAINT:
- throwConstraintPreventsModification(
- "SqliteStatement::stepStatement: contraint prevent insert or update!");
- case SQLITE_TOOBIG:
- throwTooBig("SqliteStatement::stepStatement: Some is to bigger than SQLITE_MAX_LENGTH.");
- case SQLITE_SCHEMA:
- throwSchemaChangeError("SqliteStatement::stepStatement: Schema changed but the statement "
- "cannot be recompiled.");
- case SQLITE_READONLY_CANTINIT:
- case SQLITE_READONLY_CANTLOCK:
- case SQLITE_READONLY_DBMOVED:
- case SQLITE_READONLY_DIRECTORY:
- case SQLITE_READONLY_RECOVERY:
- case SQLITE_READONLY_ROLLBACK:
- case SQLITE_READONLY:
- throwCannotWriteToReadOnlyConnection(
- "SqliteStatement::stepStatement: Cannot write to read only connection");
- case SQLITE_PROTOCOL:
- throwProtocolError(
- "SqliteStatement::stepStatement: Something strang with the file locking happened.");
- case SQLITE_NOMEM:
- throw std::bad_alloc();
- case SQLITE_NOLFS:
- throwDatabaseExceedsMaximumFileSize(
- "SqliteStatement::stepStatement: Database exceeds maximum file size.");
- case SQLITE_MISMATCH:
- throwDataTypeMismatch(
- "SqliteStatement::stepStatement: Most probably you used not an integer for a rowid.");
- case SQLITE_LOCKED_SHAREDCACHE:
- case SQLITE_LOCKED_VTAB:
- case SQLITE_LOCKED:
- throwConnectionIsLocked("SqliteStatement::stepStatement: Database connection is locked.");
- case SQLITE_IOERR_AUTH:
- case SQLITE_IOERR_BEGIN_ATOMIC:
- case SQLITE_IOERR_BLOCKED:
- case SQLITE_IOERR_CHECKRESERVEDLOCK:
- case SQLITE_IOERR_CLOSE:
- case SQLITE_IOERR_COMMIT_ATOMIC:
- case SQLITE_IOERR_CONVPATH:
- case SQLITE_IOERR_DATA:
- case SQLITE_IOERR_DELETE:
- case SQLITE_IOERR_DELETE_NOENT:
- case SQLITE_IOERR_DIR_CLOSE:
- case SQLITE_IOERR_DIR_FSYNC:
- case SQLITE_IOERR_FSTAT:
- case SQLITE_IOERR_FSYNC:
- case SQLITE_IOERR_GETTEMPPATH:
- case SQLITE_IOERR_LOCK:
- case SQLITE_IOERR_MMAP:
- case SQLITE_IOERR_NOMEM:
- case SQLITE_IOERR_RDLOCK:
- case SQLITE_IOERR_READ:
- case SQLITE_IOERR_ROLLBACK_ATOMIC:
- case SQLITE_IOERR_SEEK:
- case SQLITE_IOERR_SHMLOCK:
- case SQLITE_IOERR_SHMMAP:
- case SQLITE_IOERR_SHMOPEN:
- case SQLITE_IOERR_SHMSIZE:
- case SQLITE_IOERR_SHORT_READ:
- case SQLITE_IOERR_TRUNCATE:
- case SQLITE_IOERR_UNLOCK:
- case SQLITE_IOERR_VNODE:
- case SQLITE_IOERR_WRITE:
- case SQLITE_IOERR:
- throwInputOutputError("SqliteStatement::stepStatement: An IO error happened.");
- case SQLITE_INTERRUPT:
- throwExecutionInterrupted("SqliteStatement::stepStatement: Execution was interrupted.");
- case SQLITE_CORRUPT_INDEX:
- case SQLITE_CORRUPT_SEQUENCE:
- case SQLITE_CORRUPT_VTAB:
- case SQLITE_CORRUPT:
- throwDatabaseIsCorrupt("SqliteStatement::stepStatement: Database is corrupt.");
- case SQLITE_CANTOPEN_CONVPATH:
- case SQLITE_CANTOPEN_DIRTYWAL:
- case SQLITE_CANTOPEN_FULLPATH:
- case SQLITE_CANTOPEN_ISDIR:
- case SQLITE_CANTOPEN_NOTEMPDIR:
- case SQLITE_CANTOPEN_SYMLINK:
- case SQLITE_CANTOPEN:
- throwCannotOpen("SqliteStatement::stepStatement: Cannot open database or temporary file.");
- }
-
- throwUnknowError("SqliteStatement::stepStatement: unknown error has happened");
-}
-
-void BaseStatement::checkForPrepareError(int resultCode) const
-{
- switch (resultCode) {
- case SQLITE_BUSY_RECOVERY:
- case SQLITE_BUSY_SNAPSHOT:
- case SQLITE_BUSY_TIMEOUT:
- case SQLITE_BUSY:
- throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to "
- "acquire the database locks!");
- case SQLITE_ERROR_MISSING_COLLSEQ:
- case SQLITE_ERROR_RETRY:
- case SQLITE_ERROR_SNAPSHOT:
- case SQLITE_ERROR:
- throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a "
- "constraint violation) has occurred!");
- case SQLITE_MISUSE:
- throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!");
- case SQLITE_IOERR_AUTH:
- case SQLITE_IOERR_BEGIN_ATOMIC:
- case SQLITE_IOERR_BLOCKED:
- case SQLITE_IOERR_CHECKRESERVEDLOCK:
- case SQLITE_IOERR_CLOSE:
- case SQLITE_IOERR_COMMIT_ATOMIC:
- case SQLITE_IOERR_CONVPATH:
- case SQLITE_IOERR_DATA:
- case SQLITE_IOERR_DELETE:
- case SQLITE_IOERR_DELETE_NOENT:
- case SQLITE_IOERR_DIR_CLOSE:
- case SQLITE_IOERR_DIR_FSYNC:
- case SQLITE_IOERR_FSTAT:
- case SQLITE_IOERR_FSYNC:
- case SQLITE_IOERR_GETTEMPPATH:
- case SQLITE_IOERR_LOCK:
- case SQLITE_IOERR_MMAP:
- case SQLITE_IOERR_NOMEM:
- case SQLITE_IOERR_RDLOCK:
- case SQLITE_IOERR_READ:
- case SQLITE_IOERR_ROLLBACK_ATOMIC:
- case SQLITE_IOERR_SEEK:
- case SQLITE_IOERR_SHMLOCK:
- case SQLITE_IOERR_SHMMAP:
- case SQLITE_IOERR_SHMOPEN:
- case SQLITE_IOERR_SHMSIZE:
- case SQLITE_IOERR_SHORT_READ:
- case SQLITE_IOERR_TRUNCATE:
- case SQLITE_IOERR_UNLOCK:
- case SQLITE_IOERR_VNODE:
- case SQLITE_IOERR_WRITE:
- case SQLITE_IOERR:
- throwInputOutputError("SqliteStatement::prepareStatement: IO error happened!");
- }
-
- throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened");
-}
-
-void BaseStatement::checkForBindingError(int resultCode) const
-{
- switch (resultCode) {
- case SQLITE_TOOBIG: throwBingingTooBig("SqliteStatement::bind: string or blob are over size limits(SQLITE_LIMIT_LENGTH)!");
- case SQLITE_RANGE : throwBindingIndexIsOutOfRange("SqliteStatement::bind: binding index is out of range!");
- case SQLITE_NOMEM: throw std::bad_alloc();
- case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::bind: was called inappropriately!");
- }
-
- throwUnknowError("SqliteStatement::bind: unknown error has happened");
-}
-
void BaseStatement::checkBindingParameterCount(int bindingParameterCount) const
{
if (bindingParameterCount != sqlite3_bind_parameter_count(m_compiledStatement.get()))
- throw WrongBindingParameterCount{"Sqlite: wrong binding parameter count!"};
+ throw WrongBindingParameterCount{};
}
void BaseStatement::checkColumnCount(int columnCount) const
{
if (columnCount != sqlite3_column_count(m_compiledStatement.get()))
- throw WrongColumnCount{"Sqlite: wrong column count!"};
+ throw WrongColumnCount{};
}
bool BaseStatement::isReadOnlyStatement() const
@@ -504,104 +317,6 @@ bool BaseStatement::isReadOnlyStatement() const
return sqlite3_stmt_readonly(m_compiledStatement.get());
}
-void BaseStatement::throwStatementIsBusy(const char *whatHasHappened) const
-{
- throw StatementIsBusy(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
-}
-
-void BaseStatement::throwStatementHasError(const char *whatHasHappened) const
-{
- throw StatementHasError(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
-}
-
-void BaseStatement::throwStatementIsMisused(const char *whatHasHappened) const
-{
- throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
-}
-
-void BaseStatement::throwInputOutputError(const char *whatHasHappened) const
-{
- throw InputOutputError(whatHasHappened);
-}
-
-void BaseStatement::throwConstraintPreventsModification(const char *whatHasHappened) const
-{
- throw ConstraintPreventsModification(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
-}
-
-void BaseStatement::throwNoValuesToFetch(const char *whatHasHappened) const
-{
- throw NoValuesToFetch(whatHasHappened);
-}
-
-void BaseStatement::throwBindingIndexIsOutOfRange(const char *whatHasHappened) const
-{
- throw BindingIndexIsOutOfRange(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
-}
-
-void BaseStatement::throwUnknowError(const char *whatHasHappened) const
-{
- if (sqliteDatabaseHandle())
- throw UnknowError(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
- else
- throw UnknowError(whatHasHappened);
-}
-
-void BaseStatement::throwBingingTooBig(const char *whatHasHappened) const
-{
- throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
-}
-
-void BaseStatement::throwTooBig(const char *whatHasHappened) const
-{
- throw TooBig{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwSchemaChangeError(const char *whatHasHappened) const
-{
- throw SchemeChangeError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const
-{
- throw CannotWriteToReadOnlyConnection{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwProtocolError(const char *whatHasHappened) const
-{
- throw ProtocolError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const
-{
- throw DatabaseExceedsMaximumFileSize{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwDataTypeMismatch(const char *whatHasHappened) const
-{
- throw DataTypeMismatch{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwConnectionIsLocked(const char *whatHasHappened) const
-{
- throw ConnectionIsLocked{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
-}
-
-void BaseStatement::throwExecutionInterrupted(const char *whatHasHappened) const
-{
- throw ExecutionInterrupted{whatHasHappened};
-}
-
-void BaseStatement::throwDatabaseIsCorrupt(const char *whatHasHappened) const
-{
- throw DatabaseIsCorrupt{whatHasHappened};
-}
-
-void BaseStatement::throwCannotOpen(const char *whatHasHappened) const
-{
- throw CannotOpen{whatHasHappened};
-}
-
QString BaseStatement::columnName(int column) const
{
return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column));
@@ -764,4 +479,9 @@ ValueView BaseStatement::fetchValueView(int column) const
return ValueView::create(NullValue{});
}
+void BaseStatement::Deleter::operator()(sqlite3_stmt *statement)
+{
+ sqlite3_finalize(statement);
+}
+
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index 4f6eba16c6..1178b97f3a 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -50,8 +50,6 @@ public:
BaseStatement(const BaseStatement &) = delete;
BaseStatement &operator=(const BaseStatement &) = delete;
- static void deleteCompiledStatement(sqlite3_stmt *m_compiledStatement);
-
bool next() const;
void step() const;
void reset() const noexcept;
@@ -109,35 +107,11 @@ public:
sqlite3 *sqliteDatabaseHandle() const;
- [[noreturn]] void checkForStepError(int resultCode) const;
- [[noreturn]] void checkForPrepareError(int resultCode) const;
- [[noreturn]] void checkForBindingError(int resultCode) const;
void setIfIsReadyToFetchValues(int resultCode) const;
void checkBindingName(int index) const;
void checkBindingParameterCount(int bindingParameterCount) const;
void checkColumnCount(int columnCount) const;
bool isReadOnlyStatement() const;
- [[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const;
- [[noreturn]] void throwStatementHasError(const char *whatHasHappened) const;
- [[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const;
- [[noreturn]] void throwInputOutputError(const char *whatHasHappened) const;
- [[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const;
- [[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const;
- [[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const;
- [[noreturn]] void throwBindingIndexIsOutOfRange(const char *whatHasHappened) const;
- [[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const;
- [[noreturn]] void throwUnknowError(const char *whatHasHappened) const;
- [[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const;
- [[noreturn]] void throwTooBig(const char *whatHasHappened) const;
- [[noreturn]] void throwSchemaChangeError(const char *whatHasHappened) const;
- [[noreturn]] void throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const;
- [[noreturn]] void throwProtocolError(const char *whatHasHappened) const;
- [[noreturn]] void throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const;
- [[noreturn]] void throwDataTypeMismatch(const char *whatHasHappened) const;
- [[noreturn]] void throwConnectionIsLocked(const char *whatHasHappened) const;
- [[noreturn]] void throwExecutionInterrupted(const char *whatHasHappened) const;
- [[noreturn]] void throwDatabaseIsCorrupt(const char *whatHasHappened) const;
- [[noreturn]] void throwCannotOpen(const char *whatHasHappened) const;
QString columnName(int column) const;
@@ -147,7 +121,13 @@ protected:
~BaseStatement() = default;
private:
- std::unique_ptr<sqlite3_stmt, void (*)(sqlite3_stmt *)> m_compiledStatement;
+ struct Deleter
+ {
+ SQLITE_EXPORT void operator()(sqlite3_stmt *statement);
+ };
+
+private:
+ std::unique_ptr<sqlite3_stmt, Deleter> m_compiledStatement;
Database &m_database;
};
@@ -417,7 +397,7 @@ private:
: statement(statement)
{
if (statement && !statement->database().isLocked())
- throw DatabaseIsNotLocked{"Database connection is not locked!"};
+ throw DatabaseIsNotLocked{};
}
Resetter(Resetter &) = delete;
diff --git a/src/libs/sqlite/sqlitedatabase.cpp b/src/libs/sqlite/sqlitedatabase.cpp
index 940ee7f42a..bb92943776 100644
--- a/src/libs/sqlite/sqlitedatabase.cpp
+++ b/src/libs/sqlite/sqlitedatabase.cpp
@@ -69,7 +69,7 @@ void Database::activateLogging()
void Database::open(LockingMode lockingMode)
{
- m_databaseBackend.open(m_databaseFilePath, m_openMode);
+ m_databaseBackend.open(m_databaseFilePath, m_openMode, m_journalMode);
if (m_busyTimeout > 0ms)
m_databaseBackend.setBusyTimeout(m_busyTimeout);
else
diff --git a/src/libs/sqlite/sqlitedatabase.h b/src/libs/sqlite/sqlitedatabase.h
index c181390ea8..70be8f4305 100644
--- a/src/libs/sqlite/sqlitedatabase.h
+++ b/src/libs/sqlite/sqlitedatabase.h
@@ -90,15 +90,12 @@ public:
return m_databaseBackend.lastInsertedRowId();
}
- void setLastInsertedRowId(int64_t rowId)
- {
- m_databaseBackend.setLastInsertedRowId(rowId);
- }
+ void setLastInsertedRowId(int64_t rowId) { m_databaseBackend.setLastInsertedRowId(rowId); }
- int changesCount()
- {
- return m_databaseBackend.changesCount();
- }
+ int version() const { return m_databaseBackend.version(); }
+ void setVersion(int version) { m_databaseBackend.setVersion(version); }
+
+ int changesCount() { return m_databaseBackend.changesCount(); }
int totalChangesCount() { return m_databaseBackend.totalChangesCount(); }
diff --git a/src/libs/sqlite/sqlitedatabasebackend.cpp b/src/libs/sqlite/sqlitedatabasebackend.cpp
index 07c2da46fe..0fd18dfaa1 100644
--- a/src/libs/sqlite/sqlitedatabasebackend.cpp
+++ b/src/libs/sqlite/sqlitedatabasebackend.cpp
@@ -84,19 +84,22 @@ void DatabaseBackend::checkpointFullWalLog()
checkIfLogCouldBeCheckpointed(resultCode);
}
-void DatabaseBackend::open(Utils::SmallStringView databaseFilePath, OpenMode mode)
+void DatabaseBackend::open(Utils::SmallStringView databaseFilePath,
+ OpenMode openMode,
+ JournalMode journalMode)
{
checkCanOpenDatabase(databaseFilePath);
+ sqlite3 *handle = m_databaseHandle.get();
int resultCode = sqlite3_open_v2(std::string(databaseFilePath).c_str(),
- &m_databaseHandle,
- openMode(mode),
+ &handle,
+ createOpenFlags(openMode, journalMode),
nullptr);
+ m_databaseHandle.reset(handle);
checkDatabaseCouldBeOpened(resultCode);
- sqlite3_extended_result_codes(m_databaseHandle, true);
- resultCode = sqlite3_carray_init(m_databaseHandle, nullptr, nullptr);
+ resultCode = sqlite3_carray_init(m_databaseHandle.get(), nullptr, nullptr);
checkCarrayCannotBeIntialized(resultCode);
}
@@ -104,7 +107,7 @@ void DatabaseBackend::open(Utils::SmallStringView databaseFilePath, OpenMode mod
sqlite3 *DatabaseBackend::sqliteDatabaseHandle() const
{
checkDatabaseHandleIsNotNull();
- return m_databaseHandle;
+ return m_databaseHandle.get();
}
void DatabaseBackend::setPragmaValue(Utils::SmallStringView pragmaKey, Utils::SmallStringView newPragmaValue)
@@ -168,6 +171,19 @@ LockingMode DatabaseBackend::lockingMode() const
return pragmaToLockingMode(pragmaValue("main.locking_mode"));
}
+int DatabaseBackend::version() const
+{
+ return toValue<int>("PRAGMA main.user_version");
+}
+
+void DatabaseBackend::setVersion(int version)
+{
+ ReadWriteStatement<>{Utils::SmallString{"PRAGMA main.user_version = "}
+ + Utils::SmallString::number(version),
+ m_database}
+ .execute();
+}
+
int DatabaseBackend::changesCount() const
{
return sqlite3_changes(sqliteDatabaseHandle());
@@ -202,12 +218,11 @@ void DatabaseBackend::close()
{
checkForOpenDatabaseWhichCanBeClosed();
- int resultCode = sqlite3_close(m_databaseHandle);
+ int resultCode = sqlite3_close(m_databaseHandle.get());
checkDatabaseClosing(resultCode);
- m_databaseHandle = nullptr;
-
+ m_databaseHandle.release();
}
bool DatabaseBackend::databaseIsOpen() const
@@ -217,12 +232,7 @@ bool DatabaseBackend::databaseIsOpen() const
void DatabaseBackend::closeWithoutException()
{
- if (m_databaseHandle) {
- int resultCode = sqlite3_close_v2(m_databaseHandle);
- m_databaseHandle = nullptr;
- if (resultCode != SQLITE_OK)
- qWarning() << "SqliteDatabaseBackend::closeWithoutException: Unexpected error at closing the database!";
- }
+ m_databaseHandle.reset();
}
namespace {
@@ -246,15 +256,17 @@ void DatabaseBackend::registerBusyHandler()
void DatabaseBackend::checkForOpenDatabaseWhichCanBeClosed()
{
if (m_databaseHandle == nullptr)
- throw DatabaseIsAlreadyClosed("SqliteDatabaseBackend::close: database is not open so it cannot be closed.");
+ throw DatabaseIsAlreadyClosed();
}
void DatabaseBackend::checkDatabaseClosing(int resultCode)
{
switch (resultCode) {
case SQLITE_OK: return;
- case SQLITE_BUSY: throw DatabaseIsBusy("SqliteDatabaseBackend::close: database is busy because of e.g. unfinalized statements and will stay open!");
- default: throwUnknowError("SqliteDatabaseBackend::close: unknown error happens at closing!");
+ case SQLITE_BUSY:
+ throw DatabaseIsBusy();
+ default:
+ throw UnknowError("SqliteDatabaseBackend::close: unknown error happens at closing!");
}
}
@@ -264,8 +276,7 @@ void DatabaseBackend::checkCanOpenDatabase(Utils::SmallStringView databaseFilePa
throw DatabaseFilePathIsEmpty("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because the file path is empty!");
if (!QFileInfo::exists(QFileInfo(QString(databaseFilePath)).path()))
- throw WrongFilePath("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because of wrong file path!",
- Utils::SmallString(databaseFilePath));
+ throw WrongFilePath(Utils::SmallString(databaseFilePath));
if (databaseIsOpen())
throw DatabaseIsAlreadyOpen("SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened because it is already open!");
@@ -273,81 +284,75 @@ void DatabaseBackend::checkCanOpenDatabase(Utils::SmallStringView databaseFilePa
void DatabaseBackend::checkDatabaseCouldBeOpened(int resultCode)
{
- switch (resultCode) {
- case SQLITE_OK:
- return;
- default:
+ if (resultCode != SQLITE_OK) {
+ try {
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
+ } catch (...) {
closeWithoutException();
- throw UnknowError(
- "SqliteDatabaseBackend::SqliteDatabaseBackend: database cannot be opened:",
- sqlite3_errmsg(sqliteDatabaseHandle()));
+ throw;
}
+ }
}
void DatabaseBackend::checkCarrayCannotBeIntialized(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwDatabaseIsNotOpen(
- "SqliteDatabaseBackend: database cannot be opened because carray failed!");
+ throw DatabaseIsNotOpen();
}
void DatabaseBackend::checkPragmaValue(Utils::SmallStringView databaseValue,
Utils::SmallStringView expectedValue)
{
if (databaseValue != expectedValue)
- throw PragmaValueNotSet("SqliteDatabaseBackend::setPragmaValue: pragma value is not set!");
+ throw PragmaValueNotSet();
}
void DatabaseBackend::checkDatabaseHandleIsNotNull() const
{
if (m_databaseHandle == nullptr)
- throwDatabaseIsNotOpen("SqliteDatabaseBackend: database is not open!");
+ throw DatabaseIsNotOpen();
}
void DatabaseBackend::checkIfMultithreadingIsActivated(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwExceptionStatic(
- "SqliteDatabaseBackend::activateMultiThreading: multithreading can't be activated!");
+ throw MultiTheadingCannotBeActivated{};
}
void DatabaseBackend::checkIfLoogingIsActivated(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwExceptionStatic("SqliteDatabaseBackend::activateLogging: logging can't be activated!");
+ throw LoggingCannotBeActivated{};
}
void DatabaseBackend::checkMmapSizeIsSet(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwExceptionStatic(
- "SqliteDatabaseBackend::checkMmapSizeIsSet: mmap size can't be changed!");
+ throw MemoryMappingCannotBeChanged{};
}
void DatabaseBackend::checkInitializeSqliteLibraryWasSuccesful(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwExceptionStatic(
- "SqliteDatabaseBackend::initializeSqliteLibrary: SqliteLibrary cannot initialized!");
+ throw LibraryCannotBeInitialized{};
}
void DatabaseBackend::checkShutdownSqliteLibraryWasSuccesful(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwExceptionStatic(
- "SqliteDatabaseBackend::shutdownSqliteLibrary: SqliteLibrary cannot be shutdowned!");
+ throw LibraryCannotBeShutdown{};
}
void DatabaseBackend::checkIfLogCouldBeCheckpointed(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwException("SqliteDatabaseBackend::checkpointFullWalLog: WAL log could not be checkpointed!");
+ throw LogCannotBeCheckpointed{};
}
void DatabaseBackend::checkIfBusyTimeoutWasSet(int resultCode)
{
if (resultCode != SQLITE_OK)
- throwException("SqliteDatabaseBackend::setBusyTimeout: Busy timeout cannot be set!");
+ throw BusyTimerCannotBeSet{};
}
namespace {
@@ -379,65 +384,57 @@ JournalMode DatabaseBackend::pragmaToJournalMode(Utils::SmallStringView pragma)
int index = indexOfPragma(pragma, journalModeStrings);
if (index < 0)
- throwExceptionStatic("SqliteDatabaseBackend::pragmaToJournalMode: pragma can't be transformed in a journal mode enumeration!");
+ throw PragmaValueCannotBeTransformed{};
return static_cast<JournalMode>(index);
}
-int DatabaseBackend::openMode(OpenMode mode)
+int DatabaseBackend::createOpenFlags(OpenMode openMode, JournalMode journalMode)
{
- int sqliteMode = SQLITE_OPEN_CREATE;
+ int sqliteOpenFlags = SQLITE_OPEN_CREATE | SQLITE_OPEN_EXRESCODE;
+
+ if (journalMode == JournalMode::Memory)
+ sqliteOpenFlags |= SQLITE_OPEN_MEMORY;
- switch (mode) {
- case OpenMode::ReadOnly: sqliteMode |= SQLITE_OPEN_READONLY; break;
- case OpenMode::ReadWrite: sqliteMode |= SQLITE_OPEN_READWRITE; break;
+ switch (openMode) {
+ case OpenMode::ReadOnly:
+ sqliteOpenFlags |= SQLITE_OPEN_READONLY;
+ break;
+ case OpenMode::ReadWrite:
+ sqliteOpenFlags |= SQLITE_OPEN_READWRITE;
+ break;
}
- return sqliteMode;
+ return sqliteOpenFlags;
}
void DatabaseBackend::setBusyTimeout(std::chrono::milliseconds timeout)
{
- sqlite3_busy_timeout(m_databaseHandle, int(timeout.count()));
+ sqlite3_busy_timeout(m_databaseHandle.get(), int(timeout.count()));
}
void DatabaseBackend::walCheckpointFull()
{
- int resultCode = sqlite3_wal_checkpoint_v2(m_databaseHandle,
+ int resultCode = sqlite3_wal_checkpoint_v2(m_databaseHandle.get(),
nullptr,
SQLITE_CHECKPOINT_TRUNCATE,
nullptr,
nullptr);
- switch (resultCode) {
- case SQLITE_OK:
- break;
- case SQLITE_BUSY_RECOVERY:
- case SQLITE_BUSY_SNAPSHOT:
- case SQLITE_BUSY_TIMEOUT:
- case SQLITE_BUSY:
- throw DatabaseIsBusy("DatabaseBackend::walCheckpointFull: Operation could not concluded "
- "because database is busy!");
- case SQLITE_ERROR_MISSING_COLLSEQ:
- case SQLITE_ERROR_RETRY:
- case SQLITE_ERROR_SNAPSHOT:
- case SQLITE_ERROR:
- throwException("DatabaseBackend::walCheckpointFull: Error occurred!");
- case SQLITE_MISUSE:
- throwExceptionStatic("DatabaseBackend::walCheckpointFull: Misuse of database!");
- }
+ if (resultCode != SQLITE_OK)
+ Sqlite::throwError(resultCode, sqliteDatabaseHandle());
}
void DatabaseBackend::setUpdateHook(
void *object,
void (*callback)(void *object, int, char const *database, char const *, long long rowId))
{
- sqlite3_update_hook(m_databaseHandle, callback, object);
+ sqlite3_update_hook(m_databaseHandle.get(), callback, object);
}
void DatabaseBackend::resetUpdateHook()
{
- sqlite3_update_hook(m_databaseHandle, nullptr, nullptr);
+ sqlite3_update_hook(m_databaseHandle.get(), nullptr, nullptr);
}
void DatabaseBackend::setBusyHandler(DatabaseBackend::BusyHandler &&busyHandler)
@@ -446,27 +443,34 @@ void DatabaseBackend::setBusyHandler(DatabaseBackend::BusyHandler &&busyHandler)
registerBusyHandler();
}
-void DatabaseBackend::throwExceptionStatic(const char *whatHasHappens)
-{
- throw Exception(whatHasHappens);
-}
+namespace {
-void DatabaseBackend::throwException(const char *whatHasHappens) const
+int progressHandlerCallback(void *userData)
{
- if (m_databaseHandle)
- throw ExceptionWithMessage(whatHasHappens, sqlite3_errmsg(m_databaseHandle));
- else
- throw Exception(whatHasHappens);
+ auto &&progressHandler = *static_cast<DatabaseBackend::ProgressHandler *>(userData);
+
+ return progressHandler() == Progress::Interrupt;
}
-void DatabaseBackend::throwUnknowError(const char *whatHasHappens) const
+} // namespace
+
+void DatabaseBackend::setProgressHandler(int operationCount, ProgressHandler &&progressHandler)
{
- throw UnknowError(whatHasHappens);
+ m_progressHandler = std::move(progressHandler);
+
+ if (m_progressHandler)
+ sqlite3_progress_handler(sqliteDatabaseHandle(),
+ operationCount,
+ &progressHandlerCallback,
+ &m_progressHandler);
+ else {
+ sqlite3_progress_handler(sqliteDatabaseHandle(), 0, nullptr, nullptr);
+ }
}
-void DatabaseBackend::throwDatabaseIsNotOpen(const char *whatHasHappens) const
+void DatabaseBackend::resetProgressHandler()
{
- throw DatabaseIsNotOpen(whatHasHappens);
+ sqlite3_progress_handler(sqliteDatabaseHandle(), 0, nullptr, nullptr);
}
template<typename Type>
@@ -483,4 +487,14 @@ Type DatabaseBackend::toValue(Utils::SmallStringView sqlStatement) const
}
}
+void DatabaseBackend::Deleter::operator()(sqlite3 *database)
+{
+ if (database) {
+ int resultCode = sqlite3_close_v2(database);
+ if (resultCode != SQLITE_OK)
+ qWarning() << "SqliteDatabaseBackend::closeWithoutException: Unexpected error at "
+ "closing the database!";
+ }
+}
+
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitedatabasebackend.h b/src/libs/sqlite/sqlitedatabasebackend.h
index 1f602a4ff8..9a1caa92d6 100644
--- a/src/libs/sqlite/sqlitedatabasebackend.h
+++ b/src/libs/sqlite/sqlitedatabasebackend.h
@@ -15,10 +15,13 @@ namespace Sqlite {
class Database;
+enum class Progress { Interrupt, Continue };
+
class SQLITE_EXPORT DatabaseBackend
{
public:
using BusyHandler = std::function<bool(int count)>;
+ using ProgressHandler = std::function<Progress()>;
DatabaseBackend(Database &database);
~DatabaseBackend();
@@ -36,7 +39,7 @@ public:
static void shutdownSqliteLibrary();
void checkpointFullWalLog();
- void open(Utils::SmallStringView databaseFilePath, OpenMode openMode);
+ void open(Utils::SmallStringView databaseFilePath, OpenMode openMode, JournalMode journalMode);
void close();
void closeWithoutException();
@@ -50,6 +53,9 @@ public:
Utils::SmallStringVector columnNames(Utils::SmallStringView tableName);
+ int version() const;
+ void setVersion(int version);
+
int changesCount() const;
int totalChangesCount() const;
@@ -61,7 +67,7 @@ public:
template<typename Type>
Type toValue(Utils::SmallStringView sqlStatement) const;
- static int openMode(OpenMode);
+ static int createOpenFlags(OpenMode openMode, JournalMode journalMode);
void setBusyTimeout(std::chrono::milliseconds timeout);
@@ -73,6 +79,8 @@ public:
void resetUpdateHook();
void setBusyHandler(BusyHandler &&busyHandler);
+ void setProgressHandler(int operationCount, ProgressHandler &&progressHandler);
+ void resetProgressHandler();
void registerBusyHandler();
@@ -100,15 +108,17 @@ protected:
static Utils::SmallStringView journalModeToPragma(JournalMode journalMode);
static JournalMode pragmaToJournalMode(Utils::SmallStringView pragma);
- Q_NORETURN static void throwExceptionStatic(const char *whatHasHappens);
- [[noreturn]] void throwException(const char *whatHasHappens) const;
- [[noreturn]] void throwUnknowError(const char *whatHasHappens) const;
- [[noreturn]] void throwDatabaseIsNotOpen(const char *whatHasHappens) const;
+private:
+ struct Deleter
+ {
+ SQLITE_EXPORT void operator()(sqlite3 *database);
+ };
private:
Database &m_database;
- sqlite3 *m_databaseHandle;
+ std::unique_ptr<sqlite3, Deleter> m_databaseHandle;
BusyHandler m_busyHandler;
+ ProgressHandler m_progressHandler;
};
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp
index 45276dfed5..b5f581ad68 100644
--- a/src/libs/sqlite/sqliteexception.cpp
+++ b/src/libs/sqlite/sqliteexception.cpp
@@ -5,13 +5,836 @@
#include <utils/smallstringio.h>
+#include <sqlite.h>
+
#include <QDebug>
namespace Sqlite {
+const char *Exception::what() const noexcept
+{
+ return "Sqlite::Exception";
+}
+
+const char *ExceptionWithMessage::what() const noexcept
+{
+ return "Sqlite::ExceptionWithMessage";
+}
+
void ExceptionWithMessage::printWarning() const
{
qWarning() << what() << m_sqliteErrorMessage;
}
+const char *StatementIsBusy::what() const noexcept
+{
+ return "Sqlite::StatementIsBusy";
+}
+
+const char *DatabaseIsBusy::what() const noexcept
+{
+ return "Sqlite::DatabaseIsBusy";
+}
+
+const char *StatementHasError::what() const noexcept
+{
+ return "Sqlite::StatementHasError";
+}
+
+const char *StatementIsMisused::what() const noexcept
+{
+ return "Sqlite::StatementIsMisused";
+}
+
+const char *InputOutputError::what() const noexcept
+{
+ return "Sqlite::InputOutputError";
+}
+
+const char *ConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::ConstraintPreventsModification";
+}
+
+const char *NoValuesToFetch::what() const noexcept
+{
+ return "Sqlite::NoValuesToFetch";
+}
+
+const char *BindingIndexIsOutOfRange::what() const noexcept
+{
+ return "Sqlite::BindingIndexIsOutOfRange";
+}
+
+const char *WrongBindingName::what() const noexcept
+{
+ return "Sqlite::WrongBindingName";
+}
+
+const char *DatabaseIsNotOpen::what() const noexcept
+{
+ return "Sqlite::DatabaseIsNotOpen";
+}
+
+const char *DatabaseCannotBeOpened::what() const noexcept
+{
+ return "Sqlite::DatabaseCannotBeOpened";
+}
+
+const char *DatabaseFilePathIsEmpty::what() const noexcept
+{
+ return "Sqlite::DatabaseFilePathIsEmpty";
+}
+
+const char *DatabaseIsAlreadyOpen::what() const noexcept
+{
+ return "Sqlite::DatabaseIsAlreadyOpen";
+}
+
+const char *DatabaseCannotBeClosed::what() const noexcept
+{
+ return "Sqlite::DatabaseCannotBeClosed";
+}
+
+const char *DatabaseIsAlreadyClosed::what() const noexcept
+{
+ return "Sqlite::DatabaseIsAlreadyClosed";
+}
+
+const char *WrongFilePath::what() const noexcept
+{
+ return "Sqlite::WrongFilePath";
+}
+
+const char *PragmaValueNotSet::what() const noexcept
+{
+ return "Sqlite::PragmaValueNotSet";
+}
+
+const char *NotReadOnlySqlStatement::what() const noexcept
+{
+ return "Sqlite::NotReadOnlySqlStatement";
+}
+
+const char *NotWriteSqlStatement::what() const noexcept
+{
+ return "Sqlite::NotWriteSqlStatement";
+}
+
+const char *DeadLock::what() const noexcept
+{
+ return "Sqlite::DeadLock";
+}
+
+const char *UnknowError::what() const noexcept
+{
+ return "Sqlite::UnknowError";
+}
+
+const char *BindingTooBig::what() const noexcept
+{
+ return "Sqlite::BindingTooBig";
+}
+
+const char *TooBig::what() const noexcept
+{
+ return "Sqlite::TooBig";
+}
+
+const char *CannotConvert::what() const noexcept
+{
+ return "Sqlite::CannotConvert";
+}
+
+const char *ForeignKeyColumnIsNotUnique::what() const noexcept
+{
+ return "Sqlite::ForeignKeyColumnIsNotUnique";
+}
+
+const char *CannotApplyChangeSet::what() const noexcept
+{
+ return "Sqlite::CannotApplyChangeSet";
+}
+
+const char *ChangeSetIsMisused::what() const noexcept
+{
+ return "Sqlite::ChangeSetIsMisused";
+}
+
+const char *SchemeChangeError::what() const noexcept
+{
+ return "Sqlite::SchemeChangeError";
+}
+
+const char *CannotWriteToReadOnlyConnection::what() const noexcept
+{
+ return "Sqlite::CannotWriteToReadOnlyConnection";
+}
+
+const char *ProtocolError::what() const noexcept
+{
+ return "Sqlite::ProtocolError";
+}
+
+const char *DatabaseExceedsMaximumFileSize::what() const noexcept
+{
+ return "Sqlite::DatabaseExceedsMaximumFileSize";
+}
+
+const char *DataTypeMismatch::what() const noexcept
+{
+ return "Sqlite::DataTypeMismatch";
+}
+
+const char *ConnectionIsLocked::what() const noexcept
+{
+ return "Sqlite::ConnectionIsLocked";
+}
+
+const char *ExecutionInterrupted::what() const noexcept
+{
+ return "Sqlite::ExecutionInterrupted";
+}
+
+const char *DatabaseIsCorrupt::what() const noexcept
+{
+ return "Sqlite::DatabaseIsCorrupt";
+}
+
+const char *CannotOpen::what() const noexcept
+{
+ return "Sqlite::CannotOpen";
+}
+
+const char *CannotCreateChangeSetIterator::what() const noexcept
+{
+ return "Sqlite::CannotCreateChangeSetIterator";
+}
+
+const char *CannotGetChangeSetOperation::what() const noexcept
+{
+ return "Sqlite::CannotGetChangeSetOperation";
+}
+
+const char *ChangeSetTupleIsOutOfRange::what() const noexcept
+{
+ return "Sqlite::ChangeSetTupleIsOutOfRange";
+}
+
+const char *ChangeSetTupleIsMisused::what() const noexcept
+{
+ return "Sqlite::ChangeSetTupleIsMisused";
+}
+
+const char *UnknownError::what() const noexcept
+{
+ return "Sqlite::UnknownError";
+}
+
+const char *DatabaseIsNotLocked::what() const noexcept
+{
+ return "Sqlite::DatabaseIsNotLocked";
+}
+
+const char *WrongBindingParameterCount::what() const noexcept
+{
+ return "Sqlite::WrongBindingParameterCount";
+}
+
+const char *WrongColumnCount::what() const noexcept
+{
+ return "Sqlite::WrongColumnCount";
+}
+
+const char *IndexHasNoTableName::what() const noexcept
+{
+ return "Sqlite::IndexHasNoTableName";
+}
+
+const char *IndexHasNoColumns::what() const noexcept
+{
+ return "Sqlite::IndexHasNoColumns";
+}
+
+const char *MultiTheadingCannotBeActivated::what() const noexcept
+{
+ return "Sqlite::MultiTheadingCannotBeActivated";
+}
+
+const char *LoggingCannotBeActivated::what() const noexcept
+{
+ return "Sqlite::LoggingCannotBeActivated";
+}
+
+const char *MemoryMappingCannotBeChanged::what() const noexcept
+{
+ return "Sqlite::MemoryMappingCannotBeChanged";
+}
+
+const char *LibraryCannotBeInitialized::what() const noexcept
+{
+ return "Sqlite::LibraryCannotBeInitialized";
+}
+
+const char *LibraryCannotBeShutdown::what() const noexcept
+{
+ return "Sqlite::LibraryCannotBeShutdown";
+}
+
+const char *LogCannotBeCheckpointed::what() const noexcept
+{
+ return "Sqlite::LogCannotBeCheckPointed";
+}
+
+const char *BusyTimerCannotBeSet::what() const noexcept
+{
+ return "Sqlite::BusyTimerCannotBeSet";
+}
+
+const char *PragmaValueCannotBeTransformed::what() const noexcept
+{
+ return "Sqlite::PragmaValueCannotBeTransformed";
+}
+
+const char *CheckpointIsMisused::what() const noexcept
+{
+ return "Sqlite::CheckpointIsMisused";
+}
+
+const char *InputOutputCannotAuthenticate::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotAuthenticate";
+}
+
+const char *InputOutputCannotBeginAtomic::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotBeginAtomic";
+}
+
+const char *InputOutputBlocked::what() const noexcept
+{
+ return "Sqlite::InputOutputBlocked";
+}
+
+const char *InputOutputCannotCommitAtomic::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotCommitAtomic";
+}
+
+const char *InputOutputCannotRollbackAtomic::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotRollbackAtomic";
+}
+
+const char *InputOutputDataError::what() const noexcept
+{
+ return "Sqlite::InputOutputDataError";
+}
+
+const char *InputOutputFileSystemIsCorrupt::what() const noexcept
+{
+ return "Sqlite::InputOutputFileSystemIsCorrupt";
+}
+
+const char *InputOutputVNodeError::what() const noexcept
+{
+ return "Sqlite::InputOutputVNodeError";
+}
+
+const char *InputOutputConvPathFailed::what() const noexcept
+{
+ return "Sqlite::InputOutputConvPathFailed";
+}
+
+const char *InputOutputCannotGetTemporaryPath::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotGetTemporaryPath";
+}
+
+const char *InputOutputCannotMemoryMap::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotMemoryMap";
+}
+
+const char *InputOutputCannotDeleteNonExistingFile::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotDeleteNonExistingFile";
+}
+
+const char *InputOutputCannotSeek::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotSeek";
+}
+
+const char *InputOutputCannotMapSharedMemory::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotMapSharedMemory";
+}
+
+const char *InputOutputCannotLockSharedMemory::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotLockSharedMemory";
+}
+
+const char *InputOutputCannotEnlargeSharedMemory::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotEnlargeSharedMemory";
+}
+
+const char *InputOutputCannotOpenSharedMemory::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotOpenSharedMemory";
+}
+
+const char *InputOutputCannotCloseDirectory::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotCloseDirectory";
+}
+
+const char *InputOutputCannotClose::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotClose";
+}
+
+const char *InputOutputCannotLock::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotLock";
+}
+
+const char *InputOutputCannotCheckReservedLock::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotCheckReservedLock";
+}
+
+const char *InputOutputCannotAccess::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotAccess";
+}
+
+const char *InputOutputNoMemory::what() const noexcept
+{
+ return "Sqlite::InputOutputNoMemory";
+}
+
+const char *InputOutputCannotDelete::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotDelete";
+}
+
+const char *InputOutputCannotReadLock::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotReadLock";
+}
+
+const char *InputOutputCannotUnlock::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotUnlock";
+}
+
+const char *InputOutputCannotFsStat::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotFsStat";
+}
+
+const char *InputOutputCannotTruncate::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotTruncate";
+}
+
+const char *InputOutputCannotSynchronizeDirectory::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotFsyncDirectory";
+}
+
+const char *InputOutputCannotSynchronizeFile::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotSynchronizeFile";
+}
+
+const char *InputOutputCannotWrite::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotWrite";
+}
+
+const char *InputOutputCannotShortRead::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotShortRead";
+}
+
+const char *InputOutputCannotRead::what() const noexcept
+{
+ return "Sqlite::InputOutputCannotRead";
+}
+
+const char *StatementIsBusyRecovering::what() const noexcept
+{
+ return "Sqlite::StatementIsBusyRecovering";
+}
+
+const char *StatementIsBusySnapshot::what() const noexcept
+{
+ return "Sqlite::StatementIsBusySnapshot";
+}
+
+const char *StatementIsBusyTimeout::what() const noexcept
+{
+ return "Sqlite::StatementIsBusyTimeout";
+}
+
+const char *DatabaseIsBusyRecovering::what() const noexcept
+{
+ return "Sqlite::DatabaseIsBusyRecovering";
+}
+
+const char *DatabaseIsBusySnapshot::what() const noexcept
+{
+ return "Sqlite::DatabaseIsBusySnapshot";
+}
+
+const char *DatabaseIsBusyTimeout::what() const noexcept
+{
+ return "Sqlite::DatabaseIsBusyTimeout";
+}
+
+const char *StatementHasErrorMissingCollatingSequence::what() const noexcept
+{
+ return "Sqlite::StatementHasErrorMissingCollatingSequence";
+}
+
+const char *StatementHasErrorRetry::what() const noexcept
+{
+ return "Sqlite::StatementHasErrorRetry";
+}
+
+const char *StatementHasErrorSnapshot::what() const noexcept
+{
+ return "Sqlite::StatementHasErrorSnapshot";
+}
+
+const char *CheckConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::CheckConstraintPreventsModification";
+}
+
+const char *CommitHookConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::CommitHookConstraintPreventsModification";
+}
+
+const char *DataTypeConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::DataTypeConstraintPreventsModification";
+}
+
+const char *ForeignKeyConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::ForeignKeyConstraintPreventsModification";
+}
+
+const char *FunctionConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::FunctionConstraintPreventsModification";
+}
+
+const char *NotNullConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::NotNullConstraintPreventsModification";
+}
+
+const char *PinnedConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::PinnedConstraintPreventsModification";
+}
+
+const char *PrimaryKeyConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::PrimaryKeyConstraintPreventsModification";
+}
+
+const char *RowIdConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::RowIdConstraintPreventsModification";
+}
+
+const char *TriggerConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::TriggerConstraintPreventsModification";
+}
+
+const char *UniqueConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::UniqueConstraintPreventsModification";
+}
+
+const char *VirtualTableConstraintPreventsModification::what() const noexcept
+{
+ return "Sqlite::VirtualTableConstraintPreventsModification";
+}
+
+const char *DatabaseHasCorruptIndex::what() const noexcept
+{
+ return "Sqlite::DatabaseHasCorruptIndex";
+}
+
+const char *DatabaseHasCorruptSequence::what() const noexcept
+{
+ return "Sqlite::DatabaseHasCorruptSequence";
+}
+
+const char *DatabaseHasCorruptVirtualTable::what() const noexcept
+{
+ return "Sqlite::DatabaseHasCorruptVirtualTable";
+}
+
+const char *CannotInitializeReadOnlyConnection::what() const noexcept
+{
+ return "Sqlite::CannotInitializeReadOnlyConnection";
+}
+
+const char *CannotLockReadOnlyConnection::what() const noexcept
+{
+ return "Sqlite::CannotLockReadOnlyConnection";
+}
+
+const char *CannotWriteToMovedDatabase::what() const noexcept
+{
+ return "Sqlite::CannotWriteToMovedDatabase";
+}
+
+const char *CannotCreateLogInReadonlyDirectory::what() const noexcept
+{
+ return "Sqlite::CannotCreateLogInReadonlyDirectory";
+}
+
+const char *DatabaseNeedsToBeRecovered::what() const noexcept
+{
+ return "Sqlite::DatabaseNeedsToBeRecovered";
+}
+
+const char *CannotRollbackToReadOnlyConnection::what() const noexcept
+{
+ return "Sqlite::CannotRollbackToReadOnlyConnection";
+}
+
+const char *ConnectionsSharedCacheIsLocked::what() const noexcept
+{
+ return "Sqlite::ConnectionsSharedCacheIsLocked";
+}
+
+const char *ConnectionsVirtualTableIsLocked::what() const noexcept
+{
+ return "Sqlite::ConnectionsVirtualTableIsLocked";
+}
+
+const char *CannotOpenConvPath::what() const noexcept
+{
+ return "Sqlite::CannotOpenConvPath";
+}
+
+const char *CannotOpenDirtyWal::what() const noexcept
+{
+ return "Sqlite::CannotOpenDirtyWal";
+}
+
+const char *CannotCovertToFullPath::what() const noexcept
+{
+ return "Sqlite::CannotCovertToFullPath";
+}
+
+const char *CannotOpenDirectoryPath::what() const noexcept
+{
+ return "Sqlite::CannotOpenDirectoryPath";
+}
+
+const char *CannotOpenNoTempDir::what() const noexcept
+{
+ return "Sqlite::CannotOpenNoTempDir";
+}
+
+const char *CannotOpenSynbolicLink::what() const noexcept
+{
+ return "Sqlite::CannotOpenSynbolicLink";
+}
+
+void throwError(int resultCode, sqlite3 *sqliteHandle)
+{
+ switch (resultCode) {
+ case SQLITE_BUSY_RECOVERY:
+ throw StatementIsBusyRecovering(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_BUSY_SNAPSHOT:
+ throw StatementIsBusySnapshot(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_BUSY_TIMEOUT:
+ throw StatementIsBusyTimeout(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_BUSY:
+ throw StatementIsBusy(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_ERROR_MISSING_COLLSEQ:
+ throw StatementHasErrorMissingCollatingSequence(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_ERROR_RETRY:
+ throw StatementHasErrorRetry(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_ERROR_SNAPSHOT:
+ throw StatementHasErrorSnapshot(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_ERROR:
+ throw StatementHasError(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_MISUSE:
+ throw StatementIsMisused(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_CHECK:
+ throw CheckConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_COMMITHOOK:
+ throw CommitHookConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_DATATYPE:
+ throw DataTypeConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_FOREIGNKEY:
+ throw ForeignKeyConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_FUNCTION:
+ throw FunctionConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_NOTNULL:
+ throw NotNullConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_PINNED:
+ throw PinnedConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_PRIMARYKEY:
+ throw PrimaryKeyConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_ROWID:
+ throw RowIdConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_TRIGGER:
+ throw TriggerConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_UNIQUE:
+ throw UniqueConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT_VTAB:
+ throw VirtualTableConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_CONSTRAINT:
+ throw ConstraintPreventsModification(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_TOOBIG:
+ throw TooBig(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_SCHEMA:
+ throw SchemeChangeError(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY_CANTINIT:
+ throw CannotInitializeReadOnlyConnection(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY_CANTLOCK:
+ throw CannotLockReadOnlyConnection(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY_DBMOVED:
+ throw CannotWriteToMovedDatabase(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY_DIRECTORY:
+ throw CannotCreateLogInReadonlyDirectory(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY_RECOVERY:
+ throw DatabaseNeedsToBeRecovered(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY_ROLLBACK:
+ throw CannotRollbackToReadOnlyConnection(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_READONLY:
+ throw CannotWriteToReadOnlyConnection(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_PROTOCOL:
+ throw ProtocolError(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_NOMEM:
+ throw std::bad_alloc();
+ case SQLITE_NOLFS:
+ throw DatabaseExceedsMaximumFileSize(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_MISMATCH:
+ throw DataTypeMismatch(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_LOCKED_SHAREDCACHE:
+ throw ConnectionsSharedCacheIsLocked(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_LOCKED_VTAB:
+ throw ConnectionsVirtualTableIsLocked(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_LOCKED:
+ throw ConnectionIsLocked(sqlite3_errmsg(sqliteHandle));
+ case SQLITE_IOERR_READ:
+ throw InputOutputCannotRead();
+ case SQLITE_IOERR_SHORT_READ:
+ throw InputOutputCannotShortRead();
+ case SQLITE_IOERR_WRITE:
+ throw InputOutputCannotWrite();
+ case SQLITE_IOERR_FSYNC:
+ throw InputOutputCannotSynchronizeFile();
+ case SQLITE_IOERR_DIR_FSYNC:
+ throw InputOutputCannotSynchronizeDirectory();
+ case SQLITE_IOERR_TRUNCATE:
+ throw InputOutputCannotTruncate();
+ case SQLITE_IOERR_FSTAT:
+ throw InputOutputCannotFsStat();
+ case SQLITE_IOERR_UNLOCK:
+ throw InputOutputCannotUnlock();
+ case SQLITE_IOERR_RDLOCK:
+ throw InputOutputCannotReadLock();
+ case SQLITE_IOERR_DELETE:
+ throw InputOutputCannotDelete();
+ case SQLITE_IOERR_BLOCKED:
+ throw InputOutputBlocked();
+ case SQLITE_IOERR_NOMEM:
+ throw InputOutputNoMemory();
+ case SQLITE_IOERR_ACCESS:
+ throw InputOutputCannotAccess();
+ case SQLITE_IOERR_CHECKRESERVEDLOCK:
+ throw InputOutputCannotCheckReservedLock();
+ case SQLITE_IOERR_LOCK:
+ throw InputOutputCannotLock();
+ case SQLITE_IOERR_CLOSE:
+ throw InputOutputCannotClose();
+ case SQLITE_IOERR_DIR_CLOSE:
+ throw InputOutputCannotCloseDirectory();
+ case SQLITE_IOERR_SHMOPEN:
+ throw InputOutputCannotOpenSharedMemory();
+ case SQLITE_IOERR_SHMSIZE:
+ throw InputOutputCannotEnlargeSharedMemory();
+ case SQLITE_IOERR_SHMLOCK:
+ throw InputOutputCannotLockSharedMemory();
+ case SQLITE_IOERR_SHMMAP:
+ throw InputOutputCannotMapSharedMemory();
+ case SQLITE_IOERR_SEEK:
+ throw InputOutputCannotSeek();
+ case SQLITE_IOERR_DELETE_NOENT:
+ throw InputOutputCannotDeleteNonExistingFile();
+ case SQLITE_IOERR_MMAP:
+ throw InputOutputCannotMemoryMap();
+ case SQLITE_IOERR_GETTEMPPATH:
+ throw InputOutputCannotGetTemporaryPath();
+ case SQLITE_IOERR_CONVPATH:
+ throw InputOutputConvPathFailed();
+ case SQLITE_IOERR_VNODE:
+ throw InputOutputVNodeError();
+ case SQLITE_IOERR_AUTH:
+ throw InputOutputCannotAuthenticate();
+ case SQLITE_IOERR_BEGIN_ATOMIC:
+ throw InputOutputCannotBeginAtomic();
+ case SQLITE_IOERR_COMMIT_ATOMIC:
+ throw InputOutputCannotCommitAtomic();
+ case SQLITE_IOERR_ROLLBACK_ATOMIC:
+ throw InputOutputCannotRollbackAtomic();
+ case SQLITE_IOERR_DATA:
+ throw InputOutputDataError();
+ case SQLITE_IOERR_CORRUPTFS:
+ throw InputOutputFileSystemIsCorrupt();
+ case SQLITE_IOERR:
+ throw InputOutputError();
+ case SQLITE_INTERRUPT:
+ throw ExecutionInterrupted();
+ case SQLITE_CORRUPT_INDEX:
+ throw DatabaseHasCorruptIndex();
+ case SQLITE_CORRUPT_SEQUENCE:
+ throw DatabaseHasCorruptSequence();
+ case SQLITE_CORRUPT_VTAB:
+ throw DatabaseHasCorruptVirtualTable();
+ case SQLITE_CORRUPT:
+ throw DatabaseIsCorrupt();
+ case SQLITE_CANTOPEN_CONVPATH:
+ throw CannotOpenConvPath();
+ case SQLITE_CANTOPEN_DIRTYWAL:
+ throw CannotOpenDirtyWal();
+ case SQLITE_CANTOPEN_FULLPATH:
+ throw CannotCovertToFullPath();
+ case SQLITE_CANTOPEN_ISDIR:
+ throw CannotOpenDirectoryPath();
+ case SQLITE_CANTOPEN_NOTEMPDIR:
+ throw CannotOpenNoTempDir();
+ case SQLITE_CANTOPEN_SYMLINK:
+ throw CannotOpenSynbolicLink();
+ case SQLITE_CANTOPEN:
+ throw CannotOpen();
+ case SQLITE_RANGE:
+ throw BindingIndexIsOutOfRange(sqlite3_errmsg(sqliteHandle));
+ }
+
+ if (sqliteHandle)
+ throw UnknowError(sqlite3_errmsg(sqliteHandle));
+ else
+ throw UnknowError();
+}
+
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h
index 319b22f3ff..d030f65280 100644
--- a/src/libs/sqlite/sqliteexception.h
+++ b/src/libs/sqlite/sqliteexception.h
@@ -10,298 +10,880 @@
#include <exception>
#include <iostream>
+extern "C" {
+struct sqlite3;
+}
+
namespace Sqlite {
class SQLITE_EXPORT Exception : public std::exception
{
public:
- Exception(const char *whatErrorHasHappen)
- : m_whatErrorHasHappen(whatErrorHasHappen)
- {}
-
- const char *what() const noexcept override { return m_whatErrorHasHappen; }
-
-private:
- const char *m_whatErrorHasHappen;
+ Exception() = default;
+ const char *what() const noexcept override;
};
class SQLITE_EXPORT ExceptionWithMessage : public Exception
{
public:
- ExceptionWithMessage(const char *whatErrorHasHappen,
- Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{})
- : Exception{whatErrorHasHappen}
- , m_sqliteErrorMessage(std::move(sqliteErrorMessage))
+ ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{})
+ : m_sqliteErrorMessage(std::move(sqliteErrorMessage))
{}
+ const char *what() const noexcept override;
void printWarning() const;
private:
Utils::SmallString m_sqliteErrorMessage;
};
-class StatementIsBusy : public ExceptionWithMessage
+class SQLITE_EXPORT StatementIsBusy : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementIsBusyRecovering : public StatementIsBusy
+{
+public:
+ using StatementIsBusy::StatementIsBusy;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementIsBusySnapshot : public StatementIsBusy
+{
+public:
+ using StatementIsBusy::StatementIsBusy;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementIsBusyTimeout : public StatementIsBusy
+{
+public:
+ using StatementIsBusy::StatementIsBusy;
+ const char *what() const noexcept override;
};
-class DatabaseIsBusy : public Exception
+class SQLITE_EXPORT DatabaseIsBusy : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT DatabaseIsBusyRecovering : public DatabaseIsBusy
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT DatabaseIsBusySnapshot : public DatabaseIsBusy
+{
+public:
+ const char *what() const noexcept override;
};
-class StatementHasError : public ExceptionWithMessage
+class SQLITE_EXPORT DatabaseIsBusyTimeout : public DatabaseIsBusy
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementHasError : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementHasErrorMissingCollatingSequence : public StatementHasError
+{
+public:
+ using StatementHasError::StatementHasError;
+ const char *what() const noexcept override;
};
-class StatementIsMisused : public ExceptionWithMessage
+class SQLITE_EXPORT StatementHasErrorRetry : public StatementHasError
+{
+public:
+ using StatementHasError::StatementHasError;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementHasErrorSnapshot : public StatementHasError
+{
+public:
+ using StatementHasError::StatementHasError;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT StatementIsMisused : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class InputOutputError : public Exception
+class SQLITE_EXPORT InputOutputError : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotAuthenticate : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotBeginAtomic : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotCommitAtomic : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotRollbackAtomic : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputDataError : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputBlocked : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputFileSystemIsCorrupt : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputVNodeError : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputConvPathFailed : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotGetTemporaryPath : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotMemoryMap : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotDeleteNonExistingFile : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotSeek : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotMapSharedMemory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotLockSharedMemory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotEnlargeSharedMemory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotOpenSharedMemory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotCloseDirectory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotClose : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotLock : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotCheckReservedLock : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotAccess : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputNoMemory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotDelete : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotReadLock : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotUnlock : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotFsStat : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotTruncate : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotSynchronizeDirectory : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotSynchronizeFile : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotWrite : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
};
-class ConstraintPreventsModification : public ExceptionWithMessage
+class SQLITE_EXPORT InputOutputCannotShortRead : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT InputOutputCannotRead : public InputOutputError
+{
+public:
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ConstraintPreventsModification : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class NoValuesToFetch : public Exception
+class SQLITE_EXPORT CheckConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CommitHookConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT DataTypeConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ForeignKeyConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT FunctionConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT NotNullConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT PinnedConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT PrimaryKeyConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT RowIdConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT TriggerConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT UniqueConstraintPreventsModification : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT VirtualTableConstraintPreventsModification
+ : public ConstraintPreventsModification
+{
+public:
+ using ConstraintPreventsModification::ConstraintPreventsModification;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT NoValuesToFetch : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class BindingIndexIsOutOfRange : public ExceptionWithMessage
+class SQLITE_EXPORT BindingIndexIsOutOfRange : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class WrongBindingName : public Exception
+class SQLITE_EXPORT WrongBindingName : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class DatabaseIsNotOpen : public Exception
+class SQLITE_EXPORT DatabaseIsNotOpen : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class DatabaseCannotBeOpened : public ExceptionWithMessage
+class SQLITE_EXPORT DatabaseCannotBeOpened : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class DatabaseFilePathIsEmpty : public DatabaseCannotBeOpened
+class SQLITE_EXPORT DatabaseFilePathIsEmpty : public DatabaseCannotBeOpened
{
public:
using DatabaseCannotBeOpened::DatabaseCannotBeOpened;
+ const char *what() const noexcept override;
};
-class DatabaseIsAlreadyOpen : public DatabaseCannotBeOpened
+class SQLITE_EXPORT DatabaseIsAlreadyOpen : public DatabaseCannotBeOpened
{
public:
using DatabaseCannotBeOpened::DatabaseCannotBeOpened;
+ const char *what() const noexcept override;
};
-class DatabaseCannotBeClosed : public Exception
+class SQLITE_EXPORT DatabaseCannotBeClosed : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class DatabaseIsAlreadyClosed : public DatabaseCannotBeClosed
+class SQLITE_EXPORT DatabaseIsAlreadyClosed : public DatabaseCannotBeClosed
{
public:
using DatabaseCannotBeClosed::DatabaseCannotBeClosed;
+ const char *what() const noexcept override;
};
-class WrongFilePath : public ExceptionWithMessage
+class SQLITE_EXPORT WrongFilePath : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT PragmaValueNotSet : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
};
-class PragmaValueNotSet : public Exception
+class SQLITE_EXPORT PragmaValueCannotBeTransformed : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class NotReadOnlySqlStatement : public Exception
+class SQLITE_EXPORT NotReadOnlySqlStatement : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class NotWriteSqlStatement : public Exception
+class SQLITE_EXPORT NotWriteSqlStatement : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class DeadLock : public Exception
+class SQLITE_EXPORT DeadLock : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class UnknowError : public ExceptionWithMessage
+class SQLITE_EXPORT UnknowError : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class BindingTooBig : public ExceptionWithMessage
+class SQLITE_EXPORT BindingTooBig : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class TooBig : public ExceptionWithMessage
+class SQLITE_EXPORT TooBig : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class CannotConvert : public Exception
+class SQLITE_EXPORT CannotConvert : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class ForeignKeyColumnIsNotUnique : public Exception
+class SQLITE_EXPORT ForeignKeyColumnIsNotUnique : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class CannotApplyChangeSet : public Exception
+class SQLITE_EXPORT CannotApplyChangeSet : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class ChangeSetIsMisused : public Exception
+class SQLITE_EXPORT ChangeSetIsMisused : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class SchemeChangeError : public ExceptionWithMessage
+class SQLITE_EXPORT SchemeChangeError : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class CannotWriteToReadOnlyConnection : public ExceptionWithMessage
+class SQLITE_EXPORT CannotWriteToReadOnlyConnection : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class ProtocolError : public ExceptionWithMessage
+class SQLITE_EXPORT CannotInitializeReadOnlyConnection : public CannotWriteToReadOnlyConnection
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ using CannotWriteToReadOnlyConnection::CannotWriteToReadOnlyConnection;
+ const char *what() const noexcept override;
};
-class DatabaseExceedsMaximumFileSize : public ExceptionWithMessage
+class SQLITE_EXPORT CannotLockReadOnlyConnection : public CannotWriteToReadOnlyConnection
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ using CannotWriteToReadOnlyConnection::CannotWriteToReadOnlyConnection;
+ const char *what() const noexcept override;
};
-class DataTypeMismatch : public ExceptionWithMessage
+class SQLITE_EXPORT CannotWriteToMovedDatabase : public CannotWriteToReadOnlyConnection
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ using CannotWriteToReadOnlyConnection::CannotWriteToReadOnlyConnection;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotCreateLogInReadonlyDirectory : public CannotWriteToReadOnlyConnection
+{
+public:
+ using CannotWriteToReadOnlyConnection::CannotWriteToReadOnlyConnection;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT DatabaseNeedsToBeRecovered : public CannotWriteToReadOnlyConnection
+{
+public:
+ using CannotWriteToReadOnlyConnection::CannotWriteToReadOnlyConnection;
+ const char *what() const noexcept override;
};
-class ConnectionIsLocked : public ExceptionWithMessage
+class SQLITE_EXPORT CannotRollbackToReadOnlyConnection : public CannotWriteToReadOnlyConnection
+{
+public:
+ using CannotWriteToReadOnlyConnection::CannotWriteToReadOnlyConnection;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ProtocolError : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class ExecutionInterrupted : public ExceptionWithMessage
+class SQLITE_EXPORT DatabaseExceedsMaximumFileSize : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class DatabaseIsCorrupt : public ExceptionWithMessage
+class SQLITE_EXPORT DataTypeMismatch : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
};
-class CannotOpen : public ExceptionWithMessage
+class SQLITE_EXPORT ConnectionIsLocked : public ExceptionWithMessage
{
public:
using ExceptionWithMessage::ExceptionWithMessage;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ConnectionsSharedCacheIsLocked : public ConnectionIsLocked
+{
+public:
+ using ConnectionIsLocked::ConnectionIsLocked;
+ const char *what() const noexcept override;
};
-class CannotCreateChangeSetIterator : public Exception
+class SQLITE_EXPORT ConnectionsVirtualTableIsLocked : public ConnectionIsLocked
+{
+public:
+ using ConnectionIsLocked::ConnectionIsLocked;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ExecutionInterrupted : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class CannotGetChangeSetOperation : public Exception
+class SQLITE_EXPORT DatabaseIsCorrupt : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT DatabaseHasCorruptIndex : public DatabaseIsCorrupt
+{
+public:
+ using DatabaseIsCorrupt::DatabaseIsCorrupt;
+ const char *what() const noexcept override;
};
-class ChangeSetTupleIsOutOfRange : public Exception
+class SQLITE_EXPORT DatabaseHasCorruptSequence : public DatabaseIsCorrupt
+{
+public:
+ using DatabaseIsCorrupt::DatabaseIsCorrupt;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT DatabaseHasCorruptVirtualTable : public DatabaseIsCorrupt
+{
+public:
+ using DatabaseIsCorrupt::DatabaseIsCorrupt;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotOpen : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class ChangeSetTupleIsMisused : public Exception
+class SQLITE_EXPORT CannotOpenConvPath : public CannotOpen
+{
+public:
+ using CannotOpen::CannotOpen;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotOpenDirtyWal : public CannotOpen
+{
+public:
+ using CannotOpen::CannotOpen;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotCovertToFullPath : public CannotOpen
+{
+public:
+ using CannotOpen::CannotOpen;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotOpenDirectoryPath : public CannotOpen
+{
+public:
+ using CannotOpen::CannotOpen;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotOpenNoTempDir : public CannotOpen
+{
+public:
+ using CannotOpen::CannotOpen;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotOpenSynbolicLink : public CannotOpen
+{
+public:
+ using CannotOpen::CannotOpen;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotCreateChangeSetIterator : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CannotGetChangeSetOperation : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ChangeSetTupleIsOutOfRange : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT ChangeSetTupleIsMisused : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class UnknownError : public Exception
+class SQLITE_EXPORT UnknownError : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class DatabaseIsNotLocked : public Exception
+class SQLITE_EXPORT DatabaseIsNotLocked : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class WrongBindingParameterCount : public Exception
+class SQLITE_EXPORT WrongBindingParameterCount : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
-class WrongColumnCount : public Exception
+class SQLITE_EXPORT WrongColumnCount : public Exception
{
public:
using Exception::Exception;
+ const char *what() const noexcept override;
};
+class SQLITE_EXPORT IndexHasNoTableName : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT IndexHasNoColumns : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT MultiTheadingCannotBeActivated : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT LoggingCannotBeActivated : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT MemoryMappingCannotBeChanged : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT LibraryCannotBeInitialized : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT LibraryCannotBeShutdown : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT LogCannotBeCheckpointed : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT BusyTimerCannotBeSet : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+class SQLITE_EXPORT CheckpointIsMisused : public Exception
+{
+public:
+ using Exception::Exception;
+ const char *what() const noexcept override;
+};
+
+[[noreturn]] SQLITE_EXPORT void throwError(int resultCode, sqlite3 *sqliteHandle);
+
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitefunctionregistry.cpp b/src/libs/sqlite/sqlitefunctionregistry.cpp
new file mode 100644
index 0000000000..c2cb6a696a
--- /dev/null
+++ b/src/libs/sqlite/sqlitefunctionregistry.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "sqlitefunctionregistry.h"
+
+#include "sqlite.h"
+
+#include <QFileInfo>
+
+namespace {
+extern "C" {
+void pathExists(sqlite3_context *context, int, sqlite3_value **arguments)
+{
+ auto argument = arguments[0];
+
+ auto errorText = "pathExists only accepts text";
+
+ if (sqlite3_value_type(argument) != SQLITE_TEXT) {
+ sqlite3_result_error(context, errorText, int(std::char_traits<char>::length(errorText)));
+ return;
+ }
+
+ auto size = sqlite3_value_bytes(argument);
+
+ auto content = QByteArrayView{sqlite3_value_text(argument), size};
+
+ QString path = QString::fromUtf8(content);
+
+ bool exists = QFileInfo::exists(path);
+
+ sqlite3_result_int(context, exists);
+}
+}
+} // namespace
+
+namespace Sqlite::FunctionRegistry {
+
+void registerPathExists(Sqlite::Database &database)
+{
+ sqlite3_create_function(database.backend().sqliteDatabaseHandle(),
+ "pathExists",
+ 1,
+ SQLITE_UTF8 | SQLITE_INNOCUOUS,
+ nullptr,
+ pathExists,
+ nullptr,
+ nullptr);
+}
+
+} // namespace Sqlite::FunctionRegistry
diff --git a/src/libs/sqlite/sqlitefunctionregistry.h b/src/libs/sqlite/sqlitefunctionregistry.h
new file mode 100644
index 0000000000..6c66070122
--- /dev/null
+++ b/src/libs/sqlite/sqlitefunctionregistry.h
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "sqlitedatabase.h"
+
+namespace Sqlite::FunctionRegistry {
+
+SQLITE_EXPORT void registerPathExists(Sqlite::Database &database);
+
+} // namespace Sqlite::FunctionRegistry
diff --git a/src/libs/sqlite/sqliteindex.h b/src/libs/sqlite/sqliteindex.h
index 488aa8770a..f320fcd599 100644
--- a/src/libs/sqlite/sqliteindex.h
+++ b/src/libs/sqlite/sqliteindex.h
@@ -55,13 +55,13 @@ public:
void checkTableName() const
{
if (m_tableName.isEmpty())
- throw Exception("SqliteIndex has not table name!");
+ throw IndexHasNoTableName();
}
void checkColumns() const
{
if (m_columnNames.empty())
- throw Exception("SqliteIndex has no columns!");
+ throw IndexHasNoColumns();
}
private:
diff --git a/src/libs/sqlite/sqliteprogresshandler.h b/src/libs/sqlite/sqliteprogresshandler.h
new file mode 100644
index 0000000000..c4c670c71a
--- /dev/null
+++ b/src/libs/sqlite/sqliteprogresshandler.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "sqlitedatabase.h"
+
+namespace Sqlite {
+
+class ProgressHandler
+{
+public:
+ template<typename Callable>
+ ProgressHandler(Callable &&callable, int operationCount, Database &database)
+ : m_database{database}
+ {
+ std::unique_lock<TransactionInterface> locker{m_database};
+ m_database.backend().setProgressHandler(operationCount, std::forward<Callable>(callable));
+ }
+
+ ~ProgressHandler()
+ {
+ std::unique_lock<TransactionInterface> locker{m_database};
+ m_database.backend().resetProgressHandler();
+ }
+
+private:
+ Database &m_database;
+};
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h
index 5db0cead8d..ac764166ce 100644
--- a/src/libs/sqlite/sqlitereadstatement.h
+++ b/src/libs/sqlite/sqlitereadstatement.h
@@ -91,8 +91,7 @@ protected:
void checkIsReadOnlyStatement()
{
if (!Base::isReadOnlyStatement())
- throw NotReadOnlySqlStatement(
- "SqliteStatement::SqliteReadStatement: is not read only statement!");
+ throw NotReadOnlySqlStatement();
}
};
diff --git a/src/libs/sqlite/sqlitesessionchangeset.cpp b/src/libs/sqlite/sqlitesessionchangeset.cpp
index 6a568bd48f..a7b4b0b1c4 100644
--- a/src/libs/sqlite/sqlitesessionchangeset.cpp
+++ b/src/libs/sqlite/sqlitesessionchangeset.cpp
@@ -26,15 +26,13 @@ void checkSessionChangeSetCreation(int resultCode)
void checkIteratorCreation(int resultCode)
{
if (resultCode != SQLITE_OK)
- throw Sqlite::CannotCreateChangeSetIterator{
- "SessionChangeSet: Cannot create iterator from blob."};
+ throw Sqlite::CannotCreateChangeSetIterator{};
}
void checkIteratorOperation(int resultCode)
{
if (resultCode != SQLITE_OK)
- throw Sqlite::CannotGetChangeSetOperation{
- "SessionChangeSet: Cannot create iterator from blob."};
+ throw Sqlite::CannotGetChangeSetOperation{};
}
void checkChangeSetValue(int resultCode)
@@ -43,14 +41,12 @@ void checkChangeSetValue(int resultCode)
case SQLITE_OK:
return;
case SQLITE_RANGE:
- throw Sqlite::ChangeSetTupleIsOutOfRange{
- "SessionChangeSet: You tried to access a non existing column."};
+ throw Sqlite::ChangeSetTupleIsOutOfRange{};
case SQLITE_MISUSE:
- throw Sqlite::ChangeSetIsMisused{
- "SessionChangeSet: Some misuse happened as you tried to access."};
+ throw Sqlite::ChangeSetIsMisused{};
}
- throw Sqlite::UnknownError{"SessionChangeSet: Some unknown error happened."};
+ throw Sqlite::UnknownError{};
}
ValueView convertSqliteValue(sqlite3_value *value)
diff --git a/src/libs/sqlite/sqlitesessions.cpp b/src/libs/sqlite/sqlitesessions.cpp
index c4aa4c868b..0dea908e5f 100644
--- a/src/libs/sqlite/sqlitesessions.cpp
+++ b/src/libs/sqlite/sqlitesessions.cpp
@@ -20,13 +20,13 @@ void checkResultCode(int resultCode)
case SQLITE_NOMEM:
throw std::bad_alloc();
case SQLITE_SCHEMA:
- throw CannotApplyChangeSet("Cannot apply change set!");
+ throw CannotApplyChangeSet();
case SQLITE_MISUSE:
- throw ChangeSetIsMisused("Change set is misused!");
+ throw ChangeSetIsMisused();
}
if (resultCode != SQLITE_OK)
- throw UnknowError("Unknow exception");
+ throw UnknowError();
}
int xConflict(void *, int conflict, sqlite3_changeset_iter *)
@@ -173,4 +173,9 @@ SessionChangeSets Sessions::changeSets() const
return selectChangeSets.values<SessionChangeSet>(1024);
}
+void Sessions::Deleter::operator()(sqlite3_session *session)
+{
+ sqlite3session_delete(session);
+}
+
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitesessions.h b/src/libs/sqlite/sqlitesessions.h
index 33ce267a97..151155d417 100644
--- a/src/libs/sqlite/sqlitesessions.h
+++ b/src/libs/sqlite/sqlitesessions.h
@@ -8,10 +8,6 @@
#include "sqlitesessionchangeset.h"
#include "sqlitewritestatement.h"
-extern "C" {
-void sqlite3session_delete(sqlite3_session *pSession);
-};
-
namespace Sqlite {
namespace Internal {
@@ -44,7 +40,6 @@ public:
{"INSERT INTO ", sessionsTableName, "(changeset) VALUES(?)"}),
database}
, databaseName(databaseName)
- , session{nullptr, sqlite3session_delete}
{}
~Sessions();
@@ -64,12 +59,17 @@ public:
private:
void attachTables(const Utils::SmallStringVector &tables);
+ struct Deleter
+ {
+ SQLITE_EXPORT void operator()(sqlite3_session *statement);
+ };
+
public:
Database &database;
WriteStatement<1> insertSession;
Utils::SmallString databaseName;
Utils::SmallStringVector tableNames;
- std::unique_ptr<sqlite3_session, decltype(&sqlite3session_delete)> session;
+ std::unique_ptr<sqlite3_session, Deleter> session;
};
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitetable.h b/src/libs/sqlite/sqlitetable.h
index e0c829420d..38355c7f67 100644
--- a/src/libs/sqlite/sqlitetable.h
+++ b/src/libs/sqlite/sqlitetable.h
@@ -88,7 +88,7 @@ public:
Constraints &&constraints = {})
{
if (!constainsUniqueIndex(referencedColumn.constraints))
- throw ForeignKeyColumnIsNotUnique("Foreign column key must be unique!");
+ throw ForeignKeyColumnIsNotUnique();
constraints.emplace_back(ForeignKey{referencedColumn.tableName,
referencedColumn.name,
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index 2bbaa55985..49ab76d89f 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -347,7 +347,7 @@ private:
case QVariant::ByteArray:
return VariantType{Blob{value.toByteArray()}};
default:
- throw CannotConvert("Cannot convert this QVariant to a ValueBase");
+ throw CannotConvert();
}
}
@@ -365,7 +365,7 @@ private:
case ValueType::Blob:
return VariantType{view.toBlobView()};
default:
- throw CannotConvert("Cannot convert this QVariant to a ValueBase");
+ throw CannotConvert();
}
}
};
diff --git a/src/libs/sqlite/sqlitewritestatement.h b/src/libs/sqlite/sqlitewritestatement.h
index d1b2e2f74e..1abf76bd42 100644
--- a/src/libs/sqlite/sqlitewritestatement.h
+++ b/src/libs/sqlite/sqlitewritestatement.h
@@ -28,8 +28,7 @@ protected:
void checkIsWritableStatement()
{
if (Base::isReadOnlyStatement())
- throw NotWriteSqlStatement(
- "SqliteStatement::SqliteWriteStatement: is not a writable statement!");
+ throw NotWriteSqlStatement();
}
};
diff --git a/src/libs/sqlite/sqlstatementbuilder.cpp b/src/libs/sqlite/sqlstatementbuilder.cpp
index a5b6ba766e..e756b460c7 100644
--- a/src/libs/sqlite/sqlstatementbuilder.cpp
+++ b/src/libs/sqlite/sqlstatementbuilder.cpp
@@ -197,30 +197,37 @@ void SqlStatementBuilder::checkIfPlaceHolderExists(Utils::SmallStringView name)
void SqlStatementBuilder::checkIfNoPlaceHoldersAynmoreExists() const
{
if (m_sqlStatement.contains('$'))
- throwException("SqlStatementBuilder::bind: there are still placeholder in the sql statement!", m_sqlTemplate.constData());
+ throwException(
+ "SqlStatementBuilder::bind: there are still placeholder in the sql statement!",
+ m_sqlTemplate);
}
void SqlStatementBuilder::checkBindingTextIsNotEmpty(Utils::SmallStringView text) const
{
if (text.isEmpty())
- throwException("SqlStatementBuilder::bind: binding text it empty!", m_sqlTemplate.constData());
+ throwException("SqlStatementBuilder::bind: binding text it empty!", m_sqlTemplate);
}
void SqlStatementBuilder::checkBindingTextVectorIsNotEmpty(const Utils::SmallStringVector &textVector) const
{
if (textVector.empty())
- throwException("SqlStatementBuilder::bind: binding text vector it empty!", m_sqlTemplate.constData());
+ throwException("SqlStatementBuilder::bind: binding text vector it empty!", m_sqlTemplate);
}
void SqlStatementBuilder::checkBindingIntegerVectorIsNotEmpty(const std::vector<int> &integerVector) const
{
if (integerVector.empty())
- throwException("SqlStatementBuilder::bind: binding integer vector it empty!", m_sqlTemplate.constData());
+ throwException("SqlStatementBuilder::bind: binding integer vector it empty!", m_sqlTemplate);
}
-void SqlStatementBuilder::throwException(const char *whatHasHappened, const char *errorMessage)
+void SqlStatementBuilder::throwException(const char *whatHasHappened, Utils::SmallString sqlTemplate)
{
- throw SqlStatementBuilderException(whatHasHappened, errorMessage);
+ throw SqlStatementBuilderException(whatHasHappened, std::move(sqlTemplate));
+}
+
+const char *SqlStatementBuilderException::what() const noexcept
+{
+ return m_whatHasHappened;
}
} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlstatementbuilder.h b/src/libs/sqlite/sqlstatementbuilder.h
index 743f743b6b..3e202572c9 100644
--- a/src/libs/sqlite/sqlstatementbuilder.h
+++ b/src/libs/sqlite/sqlstatementbuilder.h
@@ -52,7 +52,8 @@ protected:
void checkBindingTextVectorIsNotEmpty(const Utils::SmallStringVector &textVector) const;
void checkBindingIntegerVectorIsNotEmpty(const std::vector<int> &integerVector) const;
- Q_NORETURN static void throwException(const char *whatHasHappened, const char *errorMessage);
+ Q_NORETURN static void throwException(const char *whatHasHappened,
+ Utils::SmallString sqlTemplate);
private:
Utils::BasicSmallString<510> m_sqlTemplate;
diff --git a/src/libs/sqlite/sqlstatementbuilderexception.h b/src/libs/sqlite/sqlstatementbuilderexception.h
index 380b683e8d..e5ad187f4b 100644
--- a/src/libs/sqlite/sqlstatementbuilderexception.h
+++ b/src/libs/sqlite/sqlstatementbuilderexception.h
@@ -10,7 +10,16 @@ namespace Sqlite {
class SQLITE_EXPORT SqlStatementBuilderException : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ SqlStatementBuilderException(const char *whatHasHappened,
+ Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage(std::move(sqliteErrorMessage))
+ , m_whatHasHappened{whatHasHappened}
+ {}
+
+ const char *what() const noexcept override;
+
+private:
+ const char *m_whatHasHappened;
};
} // namespace Sqlite
diff --git a/src/libs/sqlite/tableconstraints.h b/src/libs/sqlite/tableconstraints.h
index 6522a5ea28..f02ca9282f 100644
--- a/src/libs/sqlite/tableconstraints.h
+++ b/src/libs/sqlite/tableconstraints.h
@@ -4,8 +4,8 @@
#pragma once
#include "sqliteglobal.h"
+#include "sqlitevalue.h"
-#include <sqlitevalue.h>
#include <utils/smallstringvector.h>
#include <variant>
diff --git a/src/libs/tracing/qml/ImageToolButton.qml b/src/libs/tracing/qml/ImageToolButton.qml
index 2d0231494b..45ca12b899 100644
--- a/src/libs/tracing/qml/ImageToolButton.qml
+++ b/src/libs/tracing/qml/ImageToolButton.qml
@@ -22,10 +22,12 @@ ToolButton {
smooth: false
}
- background: Rectangle {
+ background: PaddedRectangle {
+ padding: Theme.compactToolbar() ? 0 : 3
+ radius: Theme.compactToolbar() ? 0 : 5
color: (parent.checked || parent.pressed)
? Theme.color(Theme.FancyToolButtonSelectedColor)
- : parent.hovered
+ : (parent.hovered && parent.enabled)
? Theme.color(Theme.FancyToolButtonHoverColor)
: "#00000000"
}
diff --git a/src/libs/tracing/qml/MainView.qml b/src/libs/tracing/qml/MainView.qml
index 5aa4e835a3..052ec4aa76 100644
--- a/src/libs/tracing/qml/MainView.qml
+++ b/src/libs/tracing/qml/MainView.qml
@@ -156,7 +156,7 @@ Rectangle {
anchors.top: parent.top
anchors.left: parent.left
width: 150
- height: 24
+ height: Theme.toolBarHeight()
onZoomControlChanged: zoomSliderToolBar.visible = !zoomSliderToolBar.visible
onJumpToNext: {
var next = timelineModelAggregator.nextItem(root.selectedModel, root.selectedItem,
diff --git a/src/libs/tracing/qml/RangeDetails.qml b/src/libs/tracing/qml/RangeDetails.qml
index 8c0e64f25c..9ce3e996d9 100644
--- a/src/libs/tracing/qml/RangeDetails.qml
+++ b/src/libs/tracing/qml/RangeDetails.qml
@@ -9,7 +9,7 @@ import QtCreator.Tracing
Item {
id: rangeDetails
- property real titleBarHeight: 20
+ property real titleBarHeight: Theme.toolBarHeight() / 1.2
property real borderWidth: 1
property real outerMargin: 10
property real innerMargin: 5
diff --git a/src/libs/tracing/qml/TimeDisplay.qml b/src/libs/tracing/qml/TimeDisplay.qml
index ffcbea2e84..64cb0474cf 100644
--- a/src/libs/tracing/qml/TimeDisplay.qml
+++ b/src/libs/tracing/qml/TimeDisplay.qml
@@ -11,7 +11,7 @@ Item {
property double rangeDuration
property int textMargin: 5
- property int labelsHeight: 24
+ property int labelsHeight: Theme.toolBarHeight()
property int fontSize: 8
property int initialBlockLength: 120
property double spacing: width / rangeDuration
diff --git a/src/libs/tracing/timelinetheme.cpp b/src/libs/tracing/timelinetheme.cpp
index df02cb6396..41c0c44b4f 100644
--- a/src/libs/tracing/timelinetheme.cpp
+++ b/src/libs/tracing/timelinetheme.cpp
@@ -5,8 +5,9 @@
#include <utils/icon.h>
#include <utils/qtcassert.h>
-#include <utils/utilsicons.h>
+#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
+#include <utils/utilsicons.h>
#include <QIcon>
#include <QQmlContext>
@@ -92,4 +93,14 @@ void TimelineTheme::setupTheme(QQmlEngine *engine)
engine->addImageProvider(QLatin1String("icons"), new TimelineImageIconProvider);
}
+bool TimelineTheme::compactToolbar() const
+{
+ return StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact;
+}
+
+int TimelineTheme::toolBarHeight() const
+{
+ return StyleHelper::navigationWidgetHeight();
+}
+
} // namespace Timeline
diff --git a/src/libs/tracing/timelinetheme.h b/src/libs/tracing/timelinetheme.h
index ebca62447d..f15eccbdaf 100644
--- a/src/libs/tracing/timelinetheme.h
+++ b/src/libs/tracing/timelinetheme.h
@@ -22,6 +22,8 @@ public:
explicit TimelineTheme(QObject *parent = nullptr);
static void setupTheme(QQmlEngine* engine);
+ Q_INVOKABLE bool compactToolbar() const;
+ Q_INVOKABLE int toolBarHeight() const;
};
} // namespace Timeline
diff --git a/src/libs/tracing/timelinetracemanager.cpp b/src/libs/tracing/timelinetracemanager.cpp
index 96eb147519..98f333da91 100644
--- a/src/libs/tracing/timelinetracemanager.cpp
+++ b/src/libs/tracing/timelinetracemanager.cpp
@@ -6,12 +6,10 @@
#include "timelinetracemanager.h"
#include "tracingtr.h"
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/temporaryfile.h>
-#include <utils/runextensions.h>
#include <QFile>
-#include <QDataStream>
#include <memory>
@@ -223,8 +221,11 @@ QFuture<void> TimelineTraceManager::save(const QString &filename)
connect(writer, &QObject::destroyed, this, &TimelineTraceManager::saveFinished);
connect(writer, &TimelineTraceFile::error, this, &TimelineTraceManager::error);
- return Utils::runAsync([filename, writer] (QFutureInterface<void> &future) {
- writer->setFuture(future);
+ QFutureInterface<void> fi;
+ fi.reportStarted();
+ writer->setFuture(fi);
+
+ Utils::asyncRun([filename, writer, fi] {
QFile file(filename);
if (file.open(QIODevice::WriteOnly))
@@ -232,10 +233,13 @@ QFuture<void> TimelineTraceManager::save(const QString &filename)
else
writer->fail(Tr::tr("Could not open %1 for writing.").arg(filename));
- if (future.isCanceled())
+ if (fi.isCanceled())
file.remove();
writer->deleteLater();
+ QFutureInterface fiCopy = fi;
+ fiCopy.reportFinished();
});
+ return fi.future();
}
QFuture<void> TimelineTraceManager::load(const QString &filename)
@@ -249,8 +253,10 @@ QFuture<void> TimelineTraceManager::load(const QString &filename)
connect(reader, &QObject::destroyed, this, &TimelineTraceManager::loadFinished);
connect(reader, &TimelineTraceFile::error, this, &TimelineTraceManager::error);
- QFuture<void> future = Utils::runAsync([filename, reader] (QFutureInterface<void> &future) {
- reader->setFuture(future);
+ QFutureInterface<void> fi;
+ fi.reportStarted();
+ reader->setFuture(fi);
+ Utils::asyncRun([filename, reader, fi] {
QFile file(filename);
if (file.open(QIODevice::ReadOnly))
@@ -259,11 +265,13 @@ QFuture<void> TimelineTraceManager::load(const QString &filename)
reader->fail(Tr::tr("Could not open %1 for reading.").arg(filename));
reader->deleteLater();
+ QFutureInterface fiCopy = fi;
+ fiCopy.reportFinished();
});
QFutureWatcher<void> *watcher = new QFutureWatcher<void>(reader);
connect(watcher, &QFutureWatcherBase::canceled, this, &TimelineTraceManager::clearAll);
- connect(watcher, &QFutureWatcherBase::finished, this, [this, reader]() {
+ connect(watcher, &QFutureWatcherBase::finished, this, [this, reader] {
if (!reader->isCanceled()) {
if (reader->traceStart() >= 0)
decreaseTraceStart(reader->traceStart());
@@ -272,9 +280,8 @@ QFuture<void> TimelineTraceManager::load(const QString &filename)
finalize();
}
});
- watcher->setFuture(future);
-
- return future;
+ watcher->setFuture(fi.future());
+ return fi.future();
}
qint64 TimelineTraceManager::traceStart() const
@@ -366,10 +373,9 @@ void TimelineTraceManager::restrictByFilter(TraceEventFilter filter)
QFutureInterface<void> future;
replayEvents(filter(std::bind(&TimelineTraceManagerPrivate::dispatch, d,
- std::placeholders::_1, std::placeholders::_2)),
- [this]() {
+ std::placeholders::_1, std::placeholders::_2)), [this] {
initialize();
- }, [this]() {
+ }, [this] {
if (d->notesModel)
d->notesModel->restore();
finalize();
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 76f4e8a314..51511b117d 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -1,8 +1,8 @@
add_qtc_library(Utils
- DEPENDS Qt::Qml Qt::Xml
+ DEPENDS Tasking Qt::Qml Qt::Xml
PUBLIC_DEPENDS
Qt::Concurrent Qt::Core Qt::Network Qt::Gui Qt::Widgets
- Qt6Core5Compat
+ Qt::Core5Compat
SOURCES
../3rdparty/span/span.hpp
../3rdparty/tl_expected/include/tl/expected.hpp
@@ -12,7 +12,7 @@ add_qtc_library(Utils
appmainwindow.cpp appmainwindow.h
archive.cpp archive.h
aspects.cpp aspects.h
- asynctask.cpp asynctask.h
+ async.cpp async.h
basetreeview.cpp basetreeview.h
benchmarker.cpp benchmarker.h
buildablehelperlibrary.cpp buildablehelperlibrary.h
@@ -55,6 +55,8 @@ add_qtc_library(Utils
filepath.cpp filepath.h
filepathinfo.h
filesearch.cpp filesearch.h
+ filestreamer.cpp filestreamer.h
+ filestreamermanager.cpp filestreamermanager.h
filesystemmodel.cpp filesystemmodel.h
filesystemwatcher.cpp filesystemwatcher.h
fileutils.cpp fileutils.h
@@ -86,7 +88,6 @@ add_qtc_library(Utils
launcherpackets.cpp launcherpackets.h
launchersocket.cpp launchersocket.h
layoutbuilder.cpp layoutbuilder.h
- linecolumn.cpp linecolumn.h
link.cpp link.h
listmodel.h
listutils.h
@@ -124,6 +125,7 @@ add_qtc_library(Utils
port.cpp port.h
portlist.cpp portlist.h
predicates.h
+ process.cpp process.h
processenums.h
processhandle.cpp processhandle.h
processinfo.cpp processinfo.h
@@ -136,13 +138,15 @@ add_qtc_library(Utils
qrcparser.cpp qrcparser.h
qtcassert.cpp qtcassert.h
qtcolorbutton.cpp qtcolorbutton.h
- qtcprocess.cpp qtcprocess.h
qtcsettings.cpp qtcsettings.h
+ ranges.h
reloadpromptutils.cpp reloadpromptutils.h
removefiledialog.cpp removefiledialog.h
runextensions.cpp runextensions.h
savefile.cpp savefile.h
scopedswap.h
+ scopedtimer.cpp scopedtimer.h
+ searchresultitem.cpp searchresultitem.h
set_algorithm.h
settingsaccessor.cpp settingsaccessor.h
settingsselector.cpp settingsselector.h
@@ -163,14 +167,15 @@ add_qtc_library(Utils
statuslabel.cpp statuslabel.h
stringtable.cpp stringtable.h
stringutils.cpp stringutils.h
+ styleanimator.cpp styleanimator.h
styledbar.cpp styledbar.h
stylehelper.cpp stylehelper.h
- tasktree.cpp tasktree.h
templateengine.cpp templateengine.h
temporarydirectory.cpp temporarydirectory.h
temporaryfile.cpp temporaryfile.h
terminalcommand.cpp terminalcommand.h
- terminalprocess.cpp terminalprocess_p.h
+ terminalhooks.cpp terminalhooks.h
+ terminalinterface.cpp terminalinterface.h
textfieldcheckbox.cpp textfieldcheckbox.h
textfieldcombobox.cpp textfieldcombobox.h
textfileformat.cpp textfileformat.h
@@ -190,6 +195,7 @@ add_qtc_library(Utils
utils_global.h
utilstr.h
utilsicons.cpp utilsicons.h
+ utiltypes.h
variablechooser.cpp variablechooser.h
winutils.cpp winutils.h
wizard.cpp wizard.h
@@ -256,7 +262,7 @@ extend_qtc_library(Utils CONDITION UNIX AND NOT APPLE
extend_qtc_library(Utils
CONDITION TARGET Qt::CorePrivate
- DEPENDS Qt::CorePrivate
+ DEPENDS Qt::CorePrivate ptyqt
DEFINES QTC_UTILS_WITH_FSENGINE
SOURCES fsengine/fsengine_impl.cpp
fsengine/fsengine_impl.h
@@ -270,19 +276,10 @@ extend_qtc_library(Utils
)
if (WIN32)
- add_qtc_executable(qtcreator_process_stub
- SOURCES process_stub_win.c
- DEPENDS shell32
- DEFINES _UNICODE UNICODE _CRT_SECURE_NO_WARNINGS
- )
-
add_qtc_executable(qtcreator_ctrlc_stub
DEPENDS user32 shell32
DEFINES _UNICODE UNICODE _CRT_SECURE_NO_WARNINGS
SOURCES
process_ctrlc_stub.cpp
)
-else()
- add_qtc_executable(qtcreator_process_stub SOURCES process_stub_unix.c)
endif()
-
diff --git a/src/libs/utils/archive.cpp b/src/libs/utils/archive.cpp
index 408c75d7c5..5e62835a20 100644
--- a/src/libs/utils/archive.cpp
+++ b/src/libs/utils/archive.cpp
@@ -5,8 +5,8 @@
#include "algorithm.h"
#include "mimeutils.h"
+#include "process.h"
#include "qtcassert.h"
-#include "qtcprocess.h"
#include "utilstr.h"
#include <QSettings>
@@ -160,12 +160,12 @@ void Archive::unarchive()
m_workingDirectory.ensureWritableDir();
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setProcessChannelMode(QProcess::MergedChannels);
- QObject::connect(m_process.get(), &QtcProcess::readyReadStandardOutput, this, [this] {
+ QObject::connect(m_process.get(), &Process::readyReadStandardOutput, this, [this] {
emit outputReceived(m_process->readAllStandardOutput());
});
- QObject::connect(m_process.get(), &QtcProcess::done, this, [this] {
+ QObject::connect(m_process.get(), &Process::done, this, [this] {
const bool successfulFinish = m_process->result() == ProcessResult::FinishedWithSuccess;
if (!successfulFinish)
emit outputReceived(Tr::tr("Command failed."));
diff --git a/src/libs/utils/archive.h b/src/libs/utils/archive.h
index 30f58585e3..ccf62d3885 100644
--- a/src/libs/utils/archive.h
+++ b/src/libs/utils/archive.h
@@ -12,7 +12,7 @@
namespace Utils {
class FilePath;
-class QtcProcess;
+class Process;
class QTCREATOR_UTILS_EXPORT Archive : public QObject
{
@@ -33,7 +33,7 @@ signals:
private:
CommandLine m_commandLine;
FilePath m_workingDirectory;
- std::unique_ptr<QtcProcess> m_process;
+ std::unique_ptr<Process> m_process;
};
} // namespace Utils
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp
index 72e667d76a..549a394273 100644
--- a/src/libs/utils/aspects.cpp
+++ b/src/libs/utils/aspects.cpp
@@ -9,6 +9,7 @@
#include "layoutbuilder.h"
#include "pathchooser.h"
#include "qtcassert.h"
+#include "qtcolorbutton.h"
#include "qtcsettings.h"
#include "utilstr.h"
#include "variablechooser.h"
@@ -29,7 +30,7 @@
#include <QSpinBox>
#include <QTextEdit>
-using namespace Utils::Layouting;
+using namespace Layouting;
namespace Utils {
namespace Internal {
@@ -209,17 +210,15 @@ void BaseAspect::setupLabel()
registerSubWidget(d->m_label);
}
-void BaseAspect::addLabeledItem(Layouting::LayoutBuilder &builder, QWidget *widget)
+void BaseAspect::addLabeledItem(LayoutItem &parent, QWidget *widget)
{
setupLabel();
if (QLabel *l = label()) {
l->setBuddy(widget);
- builder.addItem(l);
- LayoutItem item(widget);
- item.span = std::max(d->m_spanX - 1, 1);
- builder.addItem(item);
+ parent.addItem(l);
+ parent.addItem(Span(std::max(d->m_spanX - 1, 1), LayoutItem(widget)));
} else {
- builder.addItem(LayoutItem(widget));
+ parent.addItem(LayoutItem(widget));
}
}
@@ -420,10 +419,21 @@ QAction *BaseAspect::action()
Adds the visual representation of this aspect to a layout using
a layout builder.
*/
-void BaseAspect::addToLayout(Layouting::LayoutBuilder &)
+void BaseAspect::addToLayout(LayoutItem &)
{
}
+void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect)
+{
+ const_cast<BaseAspect &>(aspect).addToLayout(*item);
+}
+
+void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect)
+{
+ const_cast<BaseAspect *>(aspect)->addToLayout(*item);
+}
+
+
/*!
Updates this aspect's value from user-initiated changes in the widget.
@@ -575,8 +585,15 @@ class BoolAspectPrivate
{
public:
BoolAspect::LabelPlacement m_labelPlacement = BoolAspect::LabelPlacement::AtCheckBox;
- QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
+ QPointer<QAbstractButton> m_button; // Owned by configuration widget
QPointer<QGroupBox> m_groupBox; // For BoolAspects handling GroupBox check boxes
+ bool m_buttonIsAdopted = false;
+};
+
+class ColorAspectPrivate
+{
+public:
+ QPointer<QtColorButton> m_colorButton; // Owned by configuration widget
};
class SelectionAspectPrivate
@@ -623,9 +640,12 @@ public:
Qt::TextElideMode m_elideMode = Qt::ElideNone;
QString m_placeHolderText;
+ QString m_prompDialogFilter;
+ QString m_prompDialogTitle;
+ QStringList m_commandVersionArguments;
QString m_historyCompleterKey;
PathChooser::Kind m_expectedKind = PathChooser::File;
- EnvironmentChange m_environmentChange;
+ Environment m_environment;
QPointer<ElidingLabel> m_labelDisplay;
QPointer<FancyLineEdit> m_lineEditDisplay;
QPointer<PathChooser> m_pathChooserDisplay;
@@ -931,6 +951,27 @@ void StringAspect::setPlaceHolderText(const QString &placeHolderText)
d->m_textEditDisplay->setPlaceholderText(placeHolderText);
}
+void StringAspect::setPromptDialogFilter(const QString &filter)
+{
+ d->m_prompDialogFilter = filter;
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setPromptDialogFilter(filter);
+}
+
+void StringAspect::setPromptDialogTitle(const QString &title)
+{
+ d->m_prompDialogTitle = title;
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setPromptDialogTitle(title);
+}
+
+void StringAspect::setCommandVersionArguments(const QStringList &arguments)
+{
+ d->m_commandVersionArguments = arguments;
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setCommandVersionArguments(arguments);
+}
+
/*!
Sets \a elideMode as label elide mode.
*/
@@ -968,16 +1009,11 @@ void StringAspect::setExpectedKind(const PathChooser::Kind expectedKind)
d->m_pathChooserDisplay->setExpectedKind(expectedKind);
}
-void StringAspect::setEnvironmentChange(const EnvironmentChange &change)
-{
- d->m_environmentChange = change;
- if (d->m_pathChooserDisplay)
- d->m_pathChooserDisplay->setEnvironmentChange(change);
-}
-
void StringAspect::setEnvironment(const Environment &env)
{
- setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
+ d->m_environment = env;
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setEnvironment(env);
}
void StringAspect::setBaseFileName(const FilePath &baseFileName)
@@ -1050,11 +1086,11 @@ void StringAspect::setUncheckedSemantics(StringAspect::UncheckedSemantics semant
d->m_uncheckedSemantics = semantics;
}
-void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void StringAspect::addToLayout(LayoutItem &parent)
{
if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Top) {
- d->m_checker->addToLayout(builder);
- builder.finishRow();
+ d->m_checker->addToLayout(parent);
+ parent.addItem(br);
}
const auto useMacroExpander = [this](QWidget *w) {
@@ -1075,9 +1111,12 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
if (d->m_validator)
d->m_pathChooserDisplay->setValidationFunction(d->m_validator);
- d->m_pathChooserDisplay->setEnvironmentChange(d->m_environmentChange);
+ d->m_pathChooserDisplay->setEnvironment(d->m_environment);
d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName);
d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal);
+ d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter);
+ d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle);
+ d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments);
if (defaultValue() == value())
d->m_pathChooserDisplay->setDefaultValue(defaultValue());
else
@@ -1086,7 +1125,7 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
if (d->m_pathChooserDisplay->lineEdit()->placeholderText().isEmpty())
d->m_pathChooserDisplay->lineEdit()->setPlaceholderText(d->m_placeHolderText);
d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
- addLabeledItem(builder, d->m_pathChooserDisplay);
+ addLabeledItem(parent, d->m_pathChooserDisplay);
useMacroExpander(d->m_pathChooserDisplay->lineEdit());
if (isAutoApply()) {
if (d->m_autoApplyOnEditingFinished) {
@@ -1117,7 +1156,7 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
d->m_lineEditDisplay->setReadOnly(isReadOnly());
d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data());
- addLabeledItem(builder, d->m_lineEditDisplay);
+ addLabeledItem(parent, d->m_lineEditDisplay);
useMacroExpander(d->m_lineEditDisplay);
if (isAutoApply()) {
if (d->m_autoApplyOnEditingFinished) {
@@ -1147,7 +1186,7 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
connect(d->m_lineEditDisplay, &QLineEdit::textChanged, this, [this, resetButton] {
resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue());
});
- builder.addItem(resetButton);
+ parent.addItem(resetButton);
}
break;
case TextEditDisplay:
@@ -1159,7 +1198,7 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
d->m_textEditDisplay->setText(displayedString);
d->m_textEditDisplay->setReadOnly(isReadOnly());
d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data());
- addLabeledItem(builder, d->m_textEditDisplay);
+ addLabeledItem(parent, d->m_textEditDisplay);
useMacroExpander(d->m_textEditDisplay);
if (isAutoApply()) {
connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] {
@@ -1173,14 +1212,14 @@ void StringAspect::addToLayout(Layouting::LayoutBuilder &builder)
d->m_labelDisplay->setTextInteractionFlags(Qt::TextSelectableByMouse);
d->m_labelDisplay->setText(displayedString);
d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip());
- addLabeledItem(builder, d->m_labelDisplay);
+ addLabeledItem(parent, d->m_labelDisplay);
break;
}
validateInput();
if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Right)
- d->m_checker->addToLayout(builder);
+ d->m_checker->addToLayout(parent);
}
QVariant StringAspect::volatileValue() const
@@ -1287,6 +1326,92 @@ void StringAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement,
update();
}
+
+/*!
+ \class Utils::FilePathAspect
+ \inmodule QtCreator
+
+ \brief A file path aspect is shallow wrapper around a Utils::StringAspect that
+ represents a file in the file system.
+
+ It is displayed by default using Utils::PathChooser.
+
+ The visual representation often contains a label in front of the display
+ of the actual value.
+
+ \sa Utils::StringAspect
+*/
+
+
+FilePathAspect::FilePathAspect()
+{
+ setDisplayStyle(PathChooserDisplay);
+}
+
+/*!
+ \class Utils::ColorAspect
+ \inmodule QtCreator
+
+ \brief A color aspect is a color property of some object, together with
+ a description of its behavior for common operations like visualizing or
+ persisting.
+
+ The color aspect is displayed using a QtColorButton.
+*/
+
+ColorAspect::ColorAspect(const QString &settingsKey)
+ : d(new Internal::ColorAspectPrivate)
+{
+ setDefaultValue(QColor::fromRgb(0, 0, 0));
+ setSettingsKey(settingsKey);
+ setSpan(1, 1);
+
+ addDataExtractor(this, &ColorAspect::value, &Data::value);
+}
+
+ColorAspect::~ColorAspect() = default;
+
+void ColorAspect::addToLayout(Layouting::LayoutItem &parent)
+{
+ QTC_CHECK(!d->m_colorButton);
+ d->m_colorButton = createSubWidget<QtColorButton>();
+ parent.addItem(d->m_colorButton.data());
+ d->m_colorButton->setColor(value());
+ if (isAutoApply()) {
+ connect(d->m_colorButton.data(),
+ &QtColorButton::colorChanged,
+ this,
+ [this](const QColor &color) { setValue(color); });
+ }
+}
+
+QColor ColorAspect::value() const
+{
+ return BaseAspect::value().value<QColor>();
+}
+
+void ColorAspect::setValue(const QColor &value)
+{
+ if (BaseAspect::setValueQuietly(value))
+ emit changed();
+}
+
+QVariant ColorAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ if (d->m_colorButton)
+ return d->m_colorButton->color();
+ QTC_CHECK(false);
+ return {};
+}
+
+void ColorAspect::setVolatileValue(const QVariant &val)
+{
+ QTC_CHECK(!isAutoApply());
+ if (d->m_colorButton)
+ d->m_colorButton->setColor(val.value<QColor>());
+}
+
/*!
\class Utils::BoolAspect
\inmodule QtCreator
@@ -1320,36 +1445,59 @@ BoolAspect::~BoolAspect() = default;
/*!
\reimp
*/
-void BoolAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void BoolAspect::addToLayout(Layouting::LayoutItem &parent)
{
- QTC_CHECK(!d->m_checkBox);
- d->m_checkBox = createSubWidget<QCheckBox>();
+ if (!d->m_buttonIsAdopted) {
+ QTC_CHECK(!d->m_button);
+ d->m_button = createSubWidget<QCheckBox>();
+ }
switch (d->m_labelPlacement) {
case LabelPlacement::AtCheckBoxWithoutDummyLabel:
- d->m_checkBox->setText(labelText());
- builder.addItem(d->m_checkBox.data());
+ d->m_button->setText(labelText());
+ parent.addItem(d->m_button.data());
break;
case LabelPlacement::AtCheckBox: {
- d->m_checkBox->setText(labelText());
- Layouting::LayoutBuilder::LayoutType type = builder.layoutType();
- if (type == LayoutBuilder::FormLayout)
- builder.addItem(createSubWidget<QLabel>());
- builder.addItem(d->m_checkBox.data());
+ d->m_button->setText(labelText());
+ // FIXME:
+ //if (parent.isForm())
+ // parent.addItem(createSubWidget<QLabel>());
+ parent.addItem(d->m_button.data());
break;
}
case LabelPlacement::InExtraLabel:
- addLabeledItem(builder, d->m_checkBox);
+ addLabeledItem(parent, d->m_button);
break;
}
- d->m_checkBox->setChecked(value());
+ d->m_button->setChecked(value());
if (isAutoApply()) {
- connect(d->m_checkBox.data(), &QAbstractButton::clicked,
+ connect(d->m_button.data(), &QAbstractButton::clicked,
this, [this](bool val) { setValue(val); });
}
- connect(d->m_checkBox.data(), &QAbstractButton::clicked,
+ connect(d->m_button.data(), &QAbstractButton::clicked,
this, &BoolAspect::volatileValueChanged);
}
+void BoolAspect::adoptButton(QAbstractButton *button)
+{
+ QTC_ASSERT(button, return);
+ QTC_CHECK(!d->m_button);
+ d->m_button = button;
+ d->m_buttonIsAdopted = true;
+ registerSubWidget(button);
+}
+
+std::function<void (QObject *)> BoolAspect::groupChecker()
+{
+ return [this](QObject *target) {
+ auto groupBox = qobject_cast<QGroupBox *>(target);
+ QTC_ASSERT(groupBox, return);
+ registerSubWidget(groupBox);
+ groupBox->setCheckable(true);
+ groupBox->setChecked(value());
+ d->m_groupBox = groupBox;
+ };
+}
+
QAction *BoolAspect::action()
{
if (hasAction())
@@ -1372,8 +1520,8 @@ QAction *BoolAspect::action()
QVariant BoolAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
- if (d->m_checkBox)
- return d->m_checkBox->isChecked();
+ if (d->m_button)
+ return d->m_button->isChecked();
if (d->m_groupBox)
return d->m_groupBox->isChecked();
QTC_CHECK(false);
@@ -1383,8 +1531,8 @@ QVariant BoolAspect::volatileValue() const
void BoolAspect::setVolatileValue(const QVariant &val)
{
QTC_CHECK(!isAutoApply());
- if (d->m_checkBox)
- d->m_checkBox->setChecked(val.toBool());
+ if (d->m_button)
+ d->m_button->setChecked(val.toBool());
else if (d->m_groupBox)
d->m_groupBox->setChecked(val.toBool());
}
@@ -1407,8 +1555,8 @@ bool BoolAspect::value() const
void BoolAspect::setValue(bool value)
{
if (BaseAspect::setValueQuietly(value)) {
- if (d->m_checkBox)
- d->m_checkBox->setChecked(value);
+ if (d->m_button)
+ d->m_button->setChecked(value);
//qDebug() << "SetValue: Changing" << labelText() << " to " << value;
emit changed();
//QTC_CHECK(!labelText().isEmpty());
@@ -1442,12 +1590,6 @@ void BoolAspect::setLabelPlacement(BoolAspect::LabelPlacement labelPlacement)
d->m_labelPlacement = labelPlacement;
}
-void BoolAspect::setHandlesGroup(QGroupBox *box)
-{
- registerSubWidget(box);
- d->m_groupBox = box;
-}
-
/*!
\class Utils::SelectionAspect
\inmodule QtCreator
@@ -1473,7 +1615,7 @@ SelectionAspect::~SelectionAspect() = default;
/*!
\reimp
*/
-void SelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void SelectionAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(d->m_buttonGroup == nullptr);
QTC_CHECK(!d->m_comboBox);
@@ -1489,7 +1631,7 @@ void SelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
button->setChecked(i == value());
button->setEnabled(option.enabled);
button->setToolTip(option.tooltip);
- builder.addItems({Layouting::empty, button});
+ parent.addItem(button);
d->m_buttons.append(button);
d->m_buttonGroup->addButton(button, i);
if (isAutoApply()) {
@@ -1511,7 +1653,7 @@ void SelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
connect(d->m_comboBox.data(), &QComboBox::currentIndexChanged,
this, &SelectionAspect::volatileValueChanged);
d->m_comboBox->setCurrentIndex(value());
- addLabeledItem(builder, d->m_comboBox);
+ addLabeledItem(parent, d->m_comboBox);
break;
}
}
@@ -1603,12 +1745,14 @@ void SelectionAspect::setDefaultValue(const QString &val)
QString SelectionAspect::stringValue() const
{
- return d->m_options.at(value()).displayName;
+ const int idx = value();
+ return idx >= 0 && idx < d->m_options.size() ? d->m_options.at(idx).displayName : QString();
}
QVariant SelectionAspect::itemValue() const
{
- return d->m_options.at(value()).itemData;
+ const int idx = value();
+ return idx >= 0 && idx < d->m_options.size() ? d->m_options.at(idx).itemData : QVariant();
}
void SelectionAspect::addOption(const QString &displayName, const QString &toolTip)
@@ -1677,7 +1821,7 @@ MultiSelectionAspect::~MultiSelectionAspect() = default;
/*!
\reimp
*/
-void MultiSelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void MultiSelectionAspect::addToLayout(LayoutItem &builder)
{
QTC_CHECK(d->m_listView == nullptr);
if (d->m_allValues.isEmpty())
@@ -1786,7 +1930,7 @@ IntegerAspect::~IntegerAspect() = default;
/*!
\reimp
*/
-void IntegerAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void IntegerAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QSpinBox>();
@@ -1799,11 +1943,11 @@ void IntegerAspect::addToLayout(Layouting::LayoutBuilder &builder)
d->m_spinBox->setRange(int(d->m_minimumValue.value() / d->m_displayScaleFactor),
int(d->m_maximumValue.value() / d->m_displayScaleFactor));
d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor)); // Must happen after setRange()
- addLabeledItem(builder, d->m_spinBox);
+ addLabeledItem(parent, d->m_spinBox);
if (isAutoApply()) {
connect(d->m_spinBox.data(), &QSpinBox::valueChanged,
- this, [this] { setValue(d->m_spinBox->value()); });
+ this, [this] { setValue(d->m_spinBox->value() * d->m_displayScaleFactor); });
}
}
@@ -1830,7 +1974,7 @@ void IntegerAspect::setValue(qint64 value)
{
if (BaseAspect::setValueQuietly(value)) {
if (d->m_spinBox)
- d->m_spinBox->setValue(value);
+ d->m_spinBox->setValue(value / d->m_displayScaleFactor);
//qDebug() << "SetValue: Changing" << labelText() << " to " << value;
emit changed();
//QTC_CHECK(!labelText().isEmpty());
@@ -1920,7 +2064,7 @@ DoubleAspect::~DoubleAspect() = default;
/*!
\reimp
*/
-void DoubleAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void DoubleAspect::addToLayout(LayoutItem &builder)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QDoubleSpinBox>();
@@ -2074,9 +2218,9 @@ StringListAspect::~StringListAspect() = default;
/*!
\reimp
*/
-void StringListAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void StringListAspect::addToLayout(LayoutItem &parent)
{
- Q_UNUSED(builder)
+ Q_UNUSED(parent)
// TODO - when needed.
}
@@ -2144,9 +2288,9 @@ IntegersAspect::~IntegersAspect() = default;
/*!
\reimp
*/
-void IntegersAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
{
- Q_UNUSED(builder)
+ Q_UNUSED(parent)
// TODO - when needed.
}
@@ -2207,7 +2351,7 @@ TextDisplay::~TextDisplay() = default;
/*!
\reimp
*/
-void TextDisplay::addToLayout(LayoutBuilder &builder)
+void TextDisplay::addToLayout(LayoutItem &parent)
{
if (!d->m_label) {
d->m_label = createSubWidget<InfoLabel>(d->m_message, d->m_type);
@@ -2219,7 +2363,7 @@ void TextDisplay::addToLayout(LayoutBuilder &builder)
if (!isVisible())
d->m_label->setVisible(false);
}
- builder.addItem(d->m_label.data());
+ parent.addItem(d->m_label.data());
}
/*!
@@ -2365,10 +2509,15 @@ void AspectContainer::setSettingsGroups(const QString &groupKey, const QString &
void AspectContainer::apply()
{
+ const bool willChange = isDirty();
+
for (BaseAspect *aspect : std::as_const(d->m_items))
aspect->apply();
emit applied();
+
+ if (willChange)
+ emit changed();
}
void AspectContainer::cancel()
diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h
index 413a17e624..f6a35fa4ef 100644
--- a/src/libs/utils/aspects.h
+++ b/src/libs/utils/aspects.h
@@ -9,28 +9,27 @@
#include "macroexpander.h"
#include "pathchooser.h"
-#include <QCoreApplication>
-
#include <functional>
#include <memory>
#include <optional>
QT_BEGIN_NAMESPACE
class QAction;
-class QGroupBox;
class QSettings;
QT_END_NAMESPACE
+namespace Layouting { class LayoutItem; }
+
namespace Utils {
class AspectContainer;
class BoolAspect;
-namespace Layouting { class LayoutBuilder; }
namespace Internal {
class AspectContainerPrivate;
class BaseAspectPrivate;
class BoolAspectPrivate;
+class ColorAspectPrivate;
class DoubleAspectPrivate;
class IntegerAspectPrivate;
class MultiSelectionAspectPrivate;
@@ -98,7 +97,7 @@ public:
virtual void toMap(QVariantMap &map) const;
virtual void toActiveMap(QVariantMap &map) const { toMap(map); }
- virtual void addToLayout(Layouting::LayoutBuilder &builder);
+ virtual void addToLayout(Layouting::LayoutItem &parent);
virtual QVariant volatileValue() const;
virtual void setVolatileValue(const QVariant &val);
@@ -116,7 +115,7 @@ public:
virtual void apply();
virtual void cancel();
virtual void finish();
- bool isDirty() const;
+ virtual bool isDirty() const;
bool hasAction() const;
class QTCREATOR_UTILS_EXPORT Data
@@ -169,7 +168,7 @@ signals:
protected:
QLabel *label() const;
void setupLabel();
- void addLabeledItem(Layouting::LayoutBuilder &builder, QWidget *widget);
+ void addLabeledItem(Layouting::LayoutItem &parent, QWidget *widget);
void setDataCreatorHelper(const DataCreator &creator) const;
void setDataClonerHelper(const DataCloner &cloner) const;
@@ -205,6 +204,9 @@ private:
std::unique_ptr<Internal::BaseAspectPrivate> d;
};
+QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect);
+QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect);
+
class QTCREATOR_UTILS_EXPORT BoolAspect : public BaseAspect
{
Q_OBJECT
@@ -218,7 +220,8 @@ public:
bool value;
};
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
+ std::function<void(QObject *)> groupChecker();
QAction *action() override;
@@ -226,6 +229,7 @@ public:
void setVolatileValue(const QVariant &val) override;
void emitChangedValue() override;
+ bool operator()() const { return value(); }
bool value() const;
void setValue(bool val);
bool defaultValue() const;
@@ -235,7 +239,8 @@ public:
void setLabel(const QString &labelText,
LabelPlacement labelPlacement = LabelPlacement::InExtraLabel);
void setLabelPlacement(LabelPlacement labelPlacement);
- void setHandlesGroup(QGroupBox *box);
+
+ void adoptButton(QAbstractButton *button);
signals:
void valueChanged(bool newValue);
@@ -245,6 +250,31 @@ private:
std::unique_ptr<Internal::BoolAspectPrivate> d;
};
+class QTCREATOR_UTILS_EXPORT ColorAspect : public BaseAspect
+{
+ Q_OBJECT
+
+public:
+ explicit ColorAspect(const QString &settingsKey = QString());
+ ~ColorAspect() override;
+
+ struct Data : BaseAspect::Data
+ {
+ QColor value;
+ };
+
+ void addToLayout(Layouting::LayoutItem &parent) override;
+
+ QColor value() const;
+ void setValue(const QColor &val);
+
+ QVariant volatileValue() const override;
+ void setVolatileValue(const QVariant &val) override;
+
+private:
+ std::unique_ptr<Internal::ColorAspectPrivate> d;
+};
+
class QTCREATOR_UTILS_EXPORT SelectionAspect : public BaseAspect
{
Q_OBJECT
@@ -253,7 +283,7 @@ public:
SelectionAspect();
~SelectionAspect() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QVariant volatileValue() const override;
void setVolatileValue(const QVariant &val) override;
void finish() override;
@@ -307,7 +337,7 @@ public:
MultiSelectionAspect();
~MultiSelectionAspect() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
enum class DisplayStyle { ListView };
void setDisplayStyle(DisplayStyle style);
@@ -336,7 +366,7 @@ public:
FilePath filePath;
};
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QVariant volatileValue() const override;
void setVolatileValue(const QVariant &val) override;
@@ -345,6 +375,8 @@ public:
// Hook between UI and StringAspect:
using ValueAcceptor = std::function<std::optional<QString>(const QString &, const QString &)>;
void setValueAcceptor(ValueAcceptor &&acceptor);
+
+ QString operator()() const { return value(); }
QString value() const;
void setValue(const QString &val);
@@ -355,9 +387,11 @@ public:
void setDisplayFilter(const std::function<QString (const QString &)> &displayFilter);
void setPlaceHolderText(const QString &placeHolderText);
+ void setPromptDialogFilter(const QString &filter);
+ void setPromptDialogTitle(const QString &title);
+ void setCommandVersionArguments(const QStringList &arguments);
void setHistoryCompleter(const QString &historyCompleterKey);
void setExpectedKind(const PathChooser::Kind expectedKind);
- void setEnvironmentChange(const EnvironmentChange &change);
void setEnvironment(const Environment &env);
void setBaseFileName(const FilePath &baseFileName);
void setUndoRedoEnabled(bool readOnly);
@@ -407,6 +441,14 @@ protected:
std::unique_ptr<Internal::StringAspectPrivate> d;
};
+class QTCREATOR_UTILS_EXPORT FilePathAspect : public StringAspect
+{
+public:
+ FilePathAspect();
+
+ FilePath operator()() const { return filePath(); }
+};
+
class QTCREATOR_UTILS_EXPORT IntegerAspect : public BaseAspect
{
Q_OBJECT
@@ -415,11 +457,12 @@ public:
IntegerAspect();
~IntegerAspect() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QVariant volatileValue() const override;
void setVolatileValue(const QVariant &val) override;
+ qint64 operator()() const { return value(); }
qint64 value() const;
void setValue(qint64 val);
@@ -452,11 +495,12 @@ public:
DoubleAspect();
~DoubleAspect() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QVariant volatileValue() const override;
void setVolatileValue(const QVariant &val) override;
+ double operator()() const { return value(); }
double value() const;
void setValue(double val);
@@ -520,7 +564,7 @@ public:
StringListAspect();
~StringListAspect() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QStringList value() const;
void setValue(const QStringList &val);
@@ -542,7 +586,7 @@ public:
IntegersAspect();
~IntegersAspect() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
void emitChangedValue() override;
QList<int> value() const;
@@ -564,7 +608,7 @@ public:
InfoLabel::InfoType type = InfoLabel::None);
~TextDisplay() override;
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
void setIconType(InfoLabel::InfoType t);
void setText(const QString &message);
@@ -661,6 +705,7 @@ public:
signals:
void applied();
+ void changed();
void fromMapFinished();
private:
diff --git a/src/libs/utils/async.cpp b/src/libs/utils/async.cpp
new file mode 100644
index 0000000000..33487db73b
--- /dev/null
+++ b/src/libs/utils/async.cpp
@@ -0,0 +1,55 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "async.h"
+
+namespace Utils {
+
+static int s_maxThreadCount = INT_MAX;
+
+class AsyncThreadPool : public QThreadPool
+{
+public:
+ AsyncThreadPool(QThread::Priority priority) {
+ setThreadPriority(priority);
+ setMaxThreadCount(s_maxThreadCount);
+ moveToThread(qApp->thread());
+ }
+};
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_idle, (QThread::IdlePriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_lowest, (QThread::LowestPriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_low, (QThread::LowPriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_normal, (QThread::NormalPriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_high, (QThread::HighPriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_highest, (QThread::HighestPriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_timeCritical, (QThread::TimeCriticalPriority));
+Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_inherit, (QThread::InheritPriority));
+#else
+Q_GLOBAL_STATIC(AsyncThreadPool, s_idle, QThread::IdlePriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_lowest, QThread::LowestPriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_low, QThread::LowPriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_normal, QThread::NormalPriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_high, QThread::HighPriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_highest, QThread::HighestPriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_timeCritical, QThread::TimeCriticalPriority);
+Q_GLOBAL_STATIC(AsyncThreadPool, s_inherit, QThread::InheritPriority);
+#endif
+
+QThreadPool *asyncThreadPool(QThread::Priority priority)
+{
+ switch (priority) {
+ case QThread::IdlePriority : return s_idle;
+ case QThread::LowestPriority : return s_lowest;
+ case QThread::LowPriority : return s_low;
+ case QThread::NormalPriority : return s_normal;
+ case QThread::HighPriority : return s_high;
+ case QThread::HighestPriority : return s_highest;
+ case QThread::TimeCriticalPriority : return s_timeCritical;
+ case QThread::InheritPriority : return s_inherit;
+ }
+ return nullptr;
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h
new file mode 100644
index 0000000000..f8dc6813a7
--- /dev/null
+++ b/src/libs/utils/async.h
@@ -0,0 +1,215 @@
+// 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 "utils_global.h"
+
+#include "futuresynchronizer.h"
+#include "qtcassert.h"
+
+#include <solutions/tasking/tasktree.h>
+
+#include <QFutureWatcher>
+#include <QtConcurrent>
+
+namespace Utils {
+
+QTCREATOR_UTILS_EXPORT QThreadPool *asyncThreadPool(QThread::Priority priority);
+
+template <typename Function, typename ...Args>
+auto asyncRun(QThreadPool *threadPool, QThread::Priority priority,
+ Function &&function, Args &&...args)
+{
+ QThreadPool *pool = threadPool ? threadPool : asyncThreadPool(priority);
+ return QtConcurrent::run(pool, std::forward<Function>(function), std::forward<Args>(args)...);
+}
+
+template <typename Function, typename ...Args>
+auto asyncRun(QThread::Priority priority, Function &&function, Args &&...args)
+{
+ return asyncRun<Function, Args...>(nullptr, priority,
+ std::forward<Function>(function), std::forward<Args>(args)...);
+}
+
+template <typename Function, typename ...Args>
+auto asyncRun(QThreadPool *threadPool, Function &&function, Args &&...args)
+{
+ return asyncRun<Function, Args...>(threadPool, QThread::InheritPriority,
+ std::forward<Function>(function), std::forward<Args>(args)...);
+}
+
+template <typename Function, typename ...Args>
+auto asyncRun(Function &&function, Args &&...args)
+{
+ return asyncRun<Function, Args...>(nullptr, QThread::InheritPriority,
+ std::forward<Function>(function), std::forward<Args>(args)...);
+}
+
+/*!
+ Adds a handler for when a result is ready.
+ This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
+ or create a QFutureWatcher already for other reasons.
+*/
+template <typename R, typename T>
+const QFuture<T> &onResultReady(const QFuture<T> &future, R *receiver, void(R::*member)(const T &))
+{
+ auto watcher = new QFutureWatcher<T>();
+ QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
+ QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, receiver, [=](int index) {
+ (receiver->*member)(watcher->future().resultAt(index));
+ });
+ watcher->setFuture(future);
+ return future;
+}
+
+/*!
+ Adds a handler for when a result is ready. The guard object determines the lifetime of
+ the connection.
+ This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
+ or create a QFutureWatcher already for other reasons.
+*/
+template <typename T, typename Function>
+const QFuture<T> &onResultReady(const QFuture<T> &future, QObject *guard, Function f)
+{
+ auto watcher = new QFutureWatcher<T>();
+ QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
+ QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, guard, [f, watcher](int index) {
+ f(watcher->future().resultAt(index));
+ });
+ watcher->setFuture(future);
+ return future;
+}
+
+/*!
+ Adds a handler for when the future is finished.
+ This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
+ or create a QFutureWatcher already for other reasons.
+*/
+template<typename R, typename T>
+const QFuture<T> &onFinished(const QFuture<T> &future,
+ R *receiver, void (R::*member)(const QFuture<T> &))
+{
+ auto watcher = new QFutureWatcher<T>();
+ QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
+ QObject::connect(watcher, &QFutureWatcherBase::finished, receiver,
+ [=] { (receiver->*member)(watcher->future()); });
+ watcher->setFuture(future);
+ return future;
+}
+
+/*!
+ Adds a handler for when the future is finished. The guard object determines the lifetime of
+ the connection.
+ This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
+ or create a QFutureWatcher already for other reasons.
+*/
+template<typename T, typename Function>
+const QFuture<T> &onFinished(const QFuture<T> &future, QObject *guard, Function f)
+{
+ auto watcher = new QFutureWatcher<T>();
+ QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
+ QObject::connect(watcher, &QFutureWatcherBase::finished, guard, [f, watcher] {
+ f(watcher->future());
+ });
+ watcher->setFuture(future);
+ return future;
+}
+
+class QTCREATOR_UTILS_EXPORT AsyncBase : public QObject
+{
+ Q_OBJECT
+
+signals:
+ void started();
+ void done();
+ void resultReadyAt(int index);
+};
+
+template <typename ResultType>
+class Async : public AsyncBase
+{
+public:
+ Async() {
+ connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncBase::done);
+ connect(&m_watcher, &QFutureWatcherBase::resultReadyAt, this, &AsyncBase::resultReadyAt);
+ }
+ ~Async()
+ {
+ if (isDone())
+ return;
+
+ m_watcher.cancel();
+ if (!m_synchronizer)
+ m_watcher.waitForFinished();
+ }
+
+ template <typename Function, typename ...Args>
+ void setConcurrentCallData(Function &&function, Args &&...args)
+ {
+ return wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...);
+ }
+
+ void setFutureSynchronizer(FutureSynchronizer *synchorizer) { m_synchronizer = synchorizer; }
+ void setThreadPool(QThreadPool *pool) { m_threadPool = pool; }
+ void setPriority(QThread::Priority priority) { m_priority = priority; }
+
+ void start()
+ {
+ QTC_ASSERT(m_startHandler, qWarning("No start handler specified."); return);
+ m_watcher.setFuture(m_startHandler());
+ emit started();
+ if (m_synchronizer)
+ m_synchronizer->addFuture(m_watcher.future());
+ }
+
+ bool isDone() const { return m_watcher.isFinished(); }
+ bool isCanceled() const { return m_watcher.isCanceled(); }
+
+ QFuture<ResultType> future() const { return m_watcher.future(); }
+ ResultType result() const { return m_watcher.result(); }
+ ResultType resultAt(int index) const { return m_watcher.resultAt(index); }
+ QList<ResultType> results() const { return future().results(); }
+ bool isResultAvailable() const { return future().resultCount(); }
+
+private:
+ template <typename Function, typename ...Args>
+ void wrapConcurrent(Function &&function, Args &&...args)
+ {
+ m_startHandler = [=] {
+ return asyncRun(m_threadPool, m_priority, function, args...);
+ };
+ }
+
+ template <typename Function, typename ...Args>
+ void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
+ {
+ m_startHandler = [=] {
+ return asyncRun(m_threadPool, m_priority, std::forward<const Function>(wrapper.get()),
+ args...);
+ };
+ }
+
+ using StartHandler = std::function<QFuture<ResultType>()>;
+ StartHandler m_startHandler;
+ FutureSynchronizer *m_synchronizer = nullptr;
+ QThreadPool *m_threadPool = nullptr;
+ QThread::Priority m_priority = QThread::InheritPriority;
+ QFutureWatcher<ResultType> m_watcher;
+};
+
+template <typename ResultType>
+class AsyncTaskAdapter : public Tasking::TaskAdapter<Async<ResultType>>
+{
+public:
+ AsyncTaskAdapter() {
+ this->connect(this->task(), &AsyncBase::done, this, [this] {
+ emit this->done(!this->task()->isCanceled());
+ });
+ }
+ void start() final { this->task()->start(); }
+};
+
+} // namespace Utils
+
+TASKING_DECLARE_TEMPLATE_TASK(AsyncTask, Utils::AsyncTaskAdapter);
diff --git a/src/libs/utils/asynctask.cpp b/src/libs/utils/asynctask.cpp
deleted file mode 100644
index 3576ad804c..0000000000
--- a/src/libs/utils/asynctask.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "asynctask.h"
-
-namespace Utils {
-
-} // namespace Utils
diff --git a/src/libs/utils/asynctask.h b/src/libs/utils/asynctask.h
deleted file mode 100644
index 1a6ae7d1b6..0000000000
--- a/src/libs/utils/asynctask.h
+++ /dev/null
@@ -1,93 +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 "utils_global.h"
-
-#include "futuresynchronizer.h"
-#include "qtcassert.h"
-#include "runextensions.h"
-#include "tasktree.h"
-
-#include <QFutureWatcher>
-
-namespace Utils {
-
-class QTCREATOR_UTILS_EXPORT AsyncTaskBase : public QObject
-{
- Q_OBJECT
-
-signals:
- void started();
- void done();
-};
-
-template <typename ResultType>
-class AsyncTask : public AsyncTaskBase
-{
-public:
- AsyncTask() { connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncTaskBase::done); }
- ~AsyncTask()
- {
- if (isDone())
- return;
-
- m_watcher.cancel();
- if (!m_synchronizer)
- m_watcher.waitForFinished();
- }
-
- using StartHandler = std::function<QFuture<ResultType>()>;
-
- template <typename Function, typename ...Args>
- void setAsyncCallData(const Function &function, const Args &...args)
- {
- m_startHandler = [=] {
- return Utils::runAsync(m_threadPool, m_priority, function, args...);
- };
- }
- void setFutureSynchronizer(FutureSynchronizer *synchorizer) { m_synchronizer = synchorizer; }
- void setThreadPool(QThreadPool *pool) { m_threadPool = pool; }
- void setPriority(QThread::Priority priority) { m_priority = priority; }
-
- void start()
- {
- QTC_ASSERT(m_startHandler, qWarning("No start handler specified."); return);
- m_watcher.setFuture(m_startHandler());
- emit started();
- if (m_synchronizer)
- m_synchronizer->addFuture(m_watcher.future());
- }
-
- bool isDone() const { return m_watcher.isFinished(); }
- bool isCanceled() const { return m_watcher.isCanceled(); }
-
- QFuture<ResultType> future() const { return m_watcher.future(); }
- ResultType result() const { return m_watcher.result(); }
- QList<ResultType> results() const { return future().results(); }
- bool isResultAvailable() const { return future().resultCount(); }
-
-private:
- StartHandler m_startHandler;
- FutureSynchronizer *m_synchronizer = nullptr;
- QThreadPool *m_threadPool = nullptr;
- QThread::Priority m_priority = QThread::InheritPriority;
- QFutureWatcher<ResultType> m_watcher;
-};
-
-template <typename ResultType>
-class AsyncTaskAdapter : public Tasking::TaskAdapter<AsyncTask<ResultType>>
-{
-public:
- AsyncTaskAdapter() {
- this->connect(this->task(), &AsyncTaskBase::done, this, [this] {
- emit this->done(!this->task()->isCanceled());
- });
- }
- void start() final { this->task()->start(); }
-};
-
-} // namespace Utils
-
-QTC_DECLARE_CUSTOM_TEMPLATE_TASK(Async, AsyncTaskAdapter);
diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp
index eac24cfdba..cafd5b0452 100644
--- a/src/libs/utils/buildablehelperlibrary.cpp
+++ b/src/libs/utils/buildablehelperlibrary.cpp
@@ -4,7 +4,7 @@
#include "buildablehelperlibrary.h"
#include "environment.h"
#include "hostosinfo.h"
-#include "qtcprocess.h"
+#include "process.h"
#include <QDebug>
#include <QRegularExpression>
@@ -21,7 +21,7 @@ bool BuildableHelperLibrary::isQtChooser(const FilePath &filePath)
FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser)
{
const QString toolDir = QLatin1String("QTTOOLDIR=\"");
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(1);
proc.setCommand({qtChooser, {"-print-env"}});
proc.runBlocking();
@@ -103,7 +103,7 @@ QString BuildableHelperLibrary::qtVersionForQMake(const FilePath &qmakePath)
if (qmakePath.isEmpty())
return QString();
- QtcProcess qmake;
+ Process qmake;
qmake.setTimeoutS(5);
qmake.setCommand({qmakePath, {"--version"}});
qmake.runBlocking();
diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp
index 4420fc66cb..bf566e53a8 100644
--- a/src/libs/utils/checkablemessagebox.cpp
+++ b/src/libs/utils/checkablemessagebox.cpp
@@ -3,6 +3,7 @@
#include "checkablemessagebox.h"
+#include "hostosinfo.h"
#include "qtcassert.h"
#include "utilstr.h"
@@ -30,398 +31,151 @@ static const char kDoNotAskAgainKey[] = "DoNotAskAgain";
namespace Utils {
-class CheckableMessageBoxPrivate
-{
-public:
- CheckableMessageBoxPrivate(QDialog *q)
- {
- QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
-
- pixmapLabel = new QLabel(q);
- sizePolicy.setHorizontalStretch(0);
- sizePolicy.setVerticalStretch(0);
- sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
- pixmapLabel->setSizePolicy(sizePolicy);
- pixmapLabel->setVisible(false);
- pixmapLabel->setFocusPolicy(Qt::NoFocus);
-
- auto pixmapSpacer =
- new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
-
- messageLabel = new QLabel(q);
- messageLabel->setMinimumSize(QSize(300, 0));
- messageLabel->setWordWrap(true);
- messageLabel->setOpenExternalLinks(true);
- messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
- messageLabel->setFocusPolicy(Qt::NoFocus);
- messageLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
-
- checkBox = new QCheckBox(q);
- checkBox->setText(Tr::tr("Do not ask again"));
-
- const QString showText = Tr::tr("Show Details...");
- detailsButton = new QPushButton(showText, q);
- detailsButton->setAutoDefault(false);
- detailsButton->hide();
- detailsText = new QTextEdit(q);
- detailsText->hide();
- QObject::connect(detailsButton, &QPushButton::clicked, detailsText, [this, showText] {
- detailsText->setVisible(!detailsText->isVisible());
- detailsButton->setText(
- detailsText->isVisible() ? Tr::tr("Hide Details...") : showText);
- });
-
- buttonBox = new QDialogButtonBox(q);
- buttonBox->setOrientation(Qt::Horizontal);
- buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
-
- auto verticalLayout = new QVBoxLayout();
- verticalLayout->addWidget(pixmapLabel);
- verticalLayout->addItem(pixmapSpacer);
-
- auto horizontalLayout_2 = new QHBoxLayout();
- horizontalLayout_2->addLayout(verticalLayout);
- horizontalLayout_2->addWidget(messageLabel, 10);
-
- auto horizontalLayout = new QHBoxLayout();
- horizontalLayout->addWidget(checkBox);
- horizontalLayout->addStretch(10);
-
- auto detailsButtonLayout = new QHBoxLayout;
- detailsButtonLayout->addWidget(detailsButton);
- detailsButtonLayout->addStretch(10);
-
- auto verticalLayout_2 = new QVBoxLayout(q);
- verticalLayout_2->addLayout(horizontalLayout_2);
- verticalLayout_2->addLayout(horizontalLayout);
- verticalLayout_2->addLayout(detailsButtonLayout);
- verticalLayout_2->addWidget(detailsText, 10);
- verticalLayout_2->addStretch(1);
- verticalLayout_2->addWidget(buttonBox);
- }
-
- QLabel *pixmapLabel = nullptr;
- QLabel *messageLabel = nullptr;
- QCheckBox *checkBox = nullptr;
- QDialogButtonBox *buttonBox = nullptr;
- QAbstractButton *clickedButton = nullptr;
- QPushButton *detailsButton = nullptr;
- QTextEdit *detailsText = nullptr;
- QMessageBox::Icon icon = QMessageBox::NoIcon;
-};
-
-CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
- QDialog(parent),
- d(new CheckableMessageBoxPrivate(this))
-{
- setModal(true);
- connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- connect(d->buttonBox, &QDialogButtonBox::clicked,
- this, [this](QAbstractButton *b) { d->clickedButton = b; });
-}
-
-CheckableMessageBox::~CheckableMessageBox()
-{
- delete d;
-}
-
-QAbstractButton *CheckableMessageBox::clickedButton() const
-{
- return d->clickedButton;
-}
-
-QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const
-{
- if (d->clickedButton)
- return d->buttonBox->standardButton(d->clickedButton);
- return QDialogButtonBox::NoButton;
-}
-
-QString CheckableMessageBox::text() const
-{
- return d->messageLabel->text();
-}
-
-void CheckableMessageBox::setText(const QString &t)
-{
- d->messageLabel->setText(t);
-}
-
-QMessageBox::Icon CheckableMessageBox::icon() const
-{
- return d->icon;
-}
-
-// See QMessageBoxPrivate::standardIcon
-static QPixmap pixmapForIcon(QMessageBox::Icon icon, QWidget *w)
-{
- const QStyle *style = w ? w->style() : QApplication::style();
- const int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, w);
- QIcon tmpIcon;
- switch (icon) {
- case QMessageBox::Information:
- tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation, nullptr, w);
- break;
- case QMessageBox::Warning:
- tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, w);
- break;
- case QMessageBox::Critical:
- tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, w);
- break;
- case QMessageBox::Question:
- tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, w);
- break;
- default:
- break;
- }
- if (!tmpIcon.isNull()) {
- QWindow *window = nullptr;
- if (w) {
- window = w->windowHandle();
- if (!window) {
- if (const QWidget *nativeParent = w->nativeParentWidget())
- window = nativeParent->windowHandle();
- }
+static QMessageBox::StandardButton exec(
+ QWidget *parent,
+ QMessageBox::Icon icon,
+ const QString &title,
+ const QString &text,
+ std::optional<CheckableMessageBox::Decider> decider,
+ QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton,
+ QMessageBox::StandardButton acceptButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides)
+{
+ QMessageBox msgBox(parent);
+ msgBox.setWindowTitle(title);
+ msgBox.setIcon(icon);
+ msgBox.setText(text);
+ msgBox.setTextFormat(Qt::RichText);
+ msgBox.setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
+
+ if (HostOsInfo::isMacHost()) {
+ // Message boxes on macOS cannot display links.
+ // If the message contains a link, we need to disable native dialogs.
+ if (text.contains("<a ")) {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+ msgBox.setOptions(QMessageBox::Option::DontUseNativeDialog);
+#endif
}
- return tmpIcon.pixmap(window, QSize(iconSize, iconSize));
- }
- return QPixmap();
-}
-
-void CheckableMessageBox::setIcon(QMessageBox::Icon icon)
-{
- d->icon = icon;
- const QPixmap pixmap = pixmapForIcon(icon, this);
- d->pixmapLabel->setPixmap(pixmap);
- d->pixmapLabel->setVisible(!pixmap.isNull());
-}
-
-bool CheckableMessageBox::isChecked() const
-{
- return d->checkBox->isChecked();
-}
-
-void CheckableMessageBox::setChecked(bool s)
-{
- d->checkBox->setChecked(s);
-}
-
-QString CheckableMessageBox::checkBoxText() const
-{
- return d->checkBox->text();
-}
-
-void CheckableMessageBox::setCheckBoxText(const QString &t)
-{
- d->checkBox->setText(t);
-}
-
-bool CheckableMessageBox::isCheckBoxVisible() const
-{
- return d->checkBox->isVisible();
-}
-
-void CheckableMessageBox::setCheckBoxVisible(bool v)
-{
- d->checkBox->setVisible(v);
-}
-
-QString CheckableMessageBox::detailedText() const
-{
- return d->detailsText->toPlainText();
-}
-
-void CheckableMessageBox::setDetailedText(const QString &text)
-{
- d->detailsText->setText(text);
- if (!text.isEmpty())
- d->detailsButton->setVisible(true);
-}
-
-QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const
-{
- return d->buttonBox->standardButtons();
-}
-
-void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
-{
- d->buttonBox->setStandardButtons(s);
-}
-
-QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
-{
- return d->buttonBox->button(b);
-}
-
-QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
-{
- return d->buttonBox->addButton(text, role);
-}
-
-QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
-{
- const QList<QAbstractButton *> buttons = d->buttonBox->buttons();
- for (QAbstractButton *b : buttons)
- if (auto *pb = qobject_cast<QPushButton *>(b))
- if (pb->isDefault())
- return d->buttonBox->standardButton(pb);
- return QDialogButtonBox::NoButton;
-}
-
-void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
-{
- if (QPushButton *b = d->buttonBox->button(s)) {
- b->setDefault(true);
- b->setFocus();
}
-}
-QDialogButtonBox::StandardButton
-CheckableMessageBox::question(QWidget *parent,
- const QString &title,
- const QString &question,
- const QString &checkBoxText,
- bool *checkBoxSetting,
- QDialogButtonBox::StandardButtons buttons,
- QDialogButtonBox::StandardButton defaultButton)
-{
- CheckableMessageBox mb(parent);
- mb.setWindowTitle(title);
- mb.setIcon(QMessageBox::Question);
- mb.setText(question);
- mb.setCheckBoxText(checkBoxText);
- mb.setChecked(*checkBoxSetting);
- mb.setStandardButtons(buttons);
- mb.setDefaultButton(defaultButton);
- mb.exec();
- *checkBoxSetting = mb.isChecked();
- return mb.clickedStandardButton();
-}
+ if (decider) {
+ if (!CheckableMessageBox::shouldAskAgain(*decider))
+ return acceptButton;
-QDialogButtonBox::StandardButton
-CheckableMessageBox::information(QWidget *parent,
- const QString &title,
- const QString &text,
- const QString &checkBoxText,
- bool *checkBoxSetting,
- QDialogButtonBox::StandardButtons buttons,
- QDialogButtonBox::StandardButton defaultButton)
-{
- CheckableMessageBox mb(parent);
- mb.setWindowTitle(title);
- mb.setIcon(QMessageBox::Information);
- mb.setText(text);
- mb.setCheckBoxText(checkBoxText);
- mb.setChecked(*checkBoxSetting);
- mb.setStandardButtons(buttons);
- mb.setDefaultButton(defaultButton);
- mb.exec();
- *checkBoxSetting = mb.isChecked();
- return mb.clickedStandardButton();
-}
-
-QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
-{
- return static_cast<QMessageBox::StandardButton>(int(db));
-}
+ msgBox.setCheckBox(new QCheckBox);
+ msgBox.checkBox()->setChecked(false);
-bool CheckableMessageBox::shouldAskAgain(QSettings *settings, const QString &settingsSubKey)
-{
- if (QTC_GUARD(settings)) {
- settings->beginGroup(QLatin1String(kDoNotAskAgainKey));
- bool shouldNotAsk = settings->value(settingsSubKey, false).toBool();
- settings->endGroup();
- if (shouldNotAsk)
- return false;
+ std::visit(
+ [&msgBox](auto &&decider) {
+ msgBox.checkBox()->setText(decider.text);
+ },
+ *decider);
}
- return true;
-}
-enum DoNotAskAgainType{Question, Information};
+ msgBox.setStandardButtons(buttons);
+ msgBox.setDefaultButton(defaultButton);
+ for (auto it = buttonTextOverrides.constBegin(); it != buttonTextOverrides.constEnd(); ++it)
+ msgBox.button(it.key())->setText(it.value());
+ msgBox.exec();
+
+ QMessageBox::StandardButton clickedBtn = msgBox.standardButton(msgBox.clickedButton());
+
+ if (decider && msgBox.checkBox()->isChecked()
+ && (acceptButton == QMessageBox::NoButton || clickedBtn == acceptButton))
+ CheckableMessageBox::doNotAskAgain(*decider);
+ return clickedBtn;
+}
+
+QMessageBox::StandardButton CheckableMessageBox::question(
+ QWidget *parent,
+ const QString &title,
+ const QString &question,
+ std::optional<Decider> decider,
+ QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton,
+ QMessageBox::StandardButton acceptButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides)
+{
+ return exec(parent,
+ QMessageBox::Question,
+ title,
+ question,
+ decider,
+ buttons,
+ defaultButton,
+ acceptButton,
+ buttonTextOverrides);
+}
+
+QMessageBox::StandardButton CheckableMessageBox::information(
+ QWidget *parent,
+ const QString &title,
+ const QString &text,
+ std::optional<Decider> decider,
+ QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides)
+{
+ return exec(parent,
+ QMessageBox::Information,
+ title,
+ text,
+ decider,
+ buttons,
+ defaultButton,
+ QMessageBox::NoButton,
+ buttonTextOverrides);
+}
+
+void CheckableMessageBox::doNotAskAgain(Decider &decider)
+{
+ std::visit(
+ [](auto &&decider) {
+ using T = std::decay_t<decltype(decider)>;
+ if constexpr (std::is_same_v<T, BoolDecision>) {
+ decider.doNotAskAgain = true;
+ } else if constexpr (std::is_same_v<T, SettingsDecision>) {
+ decider.settings->beginGroup(QLatin1String(kDoNotAskAgainKey));
+ decider.settings->setValue(decider.settingsSubKey, true);
+ decider.settings->endGroup();
+ } else if constexpr (std::is_same_v<T, AspectDecision>) {
+ decider.aspect.setValue(true);
+ }
+ },
+ decider);
+}
+
+bool CheckableMessageBox::shouldAskAgain(const Decider &decider)
+{
+ bool result = std::visit(
+ [](auto &&decider) {
+ using T = std::decay_t<decltype(decider)>;
+ if constexpr (std::is_same_v<T, BoolDecision>) {
+ return !decider.doNotAskAgain;
+ } else if constexpr (std::is_same_v<T, SettingsDecision>) {
+ decider.settings->beginGroup(QLatin1String(kDoNotAskAgainKey));
+ bool shouldNotAsk = decider.settings->value(decider.settingsSubKey, false).toBool();
+ decider.settings->endGroup();
+ return !shouldNotAsk;
+ } else if constexpr (std::is_same_v<T, AspectDecision>) {
+ return !decider.aspect.value();
+ }
+ },
+ decider);
-void initDoNotAskAgainMessageBox(CheckableMessageBox &messageBox, const QString &title,
- const QString &text, QDialogButtonBox::StandardButtons buttons,
- QDialogButtonBox::StandardButton defaultButton,
- DoNotAskAgainType type)
-{
- messageBox.setWindowTitle(title);
- messageBox.setIcon(type == Information ? QMessageBox::Information : QMessageBox::Question);
- messageBox.setText(text);
- messageBox.setCheckBoxVisible(true);
- messageBox.setCheckBoxText(type == Information ? CheckableMessageBox::msgDoNotShowAgain()
- : CheckableMessageBox::msgDoNotAskAgain());
- messageBox.setChecked(false);
- messageBox.setStandardButtons(buttons);
- messageBox.setDefaultButton(defaultButton);
+ return result;
}
-void CheckableMessageBox::doNotAskAgain(QSettings *settings, const QString &settingsSubKey)
+bool CheckableMessageBox::shouldAskAgain(QSettings *settings, const QString &key)
{
- if (!settings)
- return;
-
- settings->beginGroup(QLatin1String(kDoNotAskAgainKey));
- settings->setValue(settingsSubKey, true);
- settings->endGroup();
+ return shouldAskAgain(make_decider(settings, key));
}
-/*!
- Shows a message box with given \a title and \a text, and a \gui {Do not ask again} check box.
- If the user checks the check box and accepts the dialog with the \a acceptButton,
- further invocations of this function with the same \a settings and \a settingsSubKey will not
- show the dialog, but instantly return \a acceptButton.
-
- Returns the clicked button, or QDialogButtonBox::NoButton if the user rejects the dialog
- with the escape key, or \a acceptButton if the dialog is suppressed.
-*/
-QDialogButtonBox::StandardButton
-CheckableMessageBox::doNotAskAgainQuestion(QWidget *parent, const QString &title,
- const QString &text, QSettings *settings,
- const QString &settingsSubKey,
- QDialogButtonBox::StandardButtons buttons,
- QDialogButtonBox::StandardButton defaultButton,
- QDialogButtonBox::StandardButton acceptButton)
-
-{
- if (!shouldAskAgain(settings, settingsSubKey))
- return acceptButton;
-
- CheckableMessageBox messageBox(parent);
- initDoNotAskAgainMessageBox(messageBox, title, text, buttons, defaultButton, Question);
- messageBox.exec();
- if (messageBox.isChecked() && (messageBox.clickedStandardButton() == acceptButton))
- doNotAskAgain(settings, settingsSubKey);
-
- return messageBox.clickedStandardButton();
-}
-
-/*!
- Shows a message box with given \a title and \a text, and a \gui {Do not show again} check box.
- If the user checks the check box and quits the dialog, further invocations of this
- function with the same \a settings and \a settingsSubKey will not show the dialog, but instantly return.
-
- Returns the clicked button, or QDialogButtonBox::NoButton if the user rejects the dialog
- with the escape key, or \a defaultButton if the dialog is suppressed.
-*/
-QDialogButtonBox::StandardButton
-CheckableMessageBox::doNotShowAgainInformation(QWidget *parent, const QString &title,
- const QString &text, QSettings *settings,
- const QString &settingsSubKey,
- QDialogButtonBox::StandardButtons buttons,
- QDialogButtonBox::StandardButton defaultButton)
-
+void CheckableMessageBox::doNotAskAgain(QSettings *settings, const QString &key)
{
- if (!shouldAskAgain(settings, settingsSubKey))
- return defaultButton;
-
- CheckableMessageBox messageBox(parent);
- initDoNotAskAgainMessageBox(messageBox, title, text, buttons, defaultButton, Information);
- messageBox.exec();
- if (messageBox.isChecked())
- doNotAskAgain(settings, settingsSubKey);
-
- return messageBox.clickedStandardButton();
+ Decider decider = make_decider(settings, key);
+ return doNotAskAgain(decider);
}
/*!
@@ -443,15 +197,9 @@ void CheckableMessageBox::resetAllDoNotAskAgainQuestions(QSettings *settings)
bool CheckableMessageBox::hasSuppressedQuestions(QSettings *settings)
{
QTC_ASSERT(settings, return false);
- bool hasSuppressed = false;
settings->beginGroup(QLatin1String(kDoNotAskAgainKey));
- const QStringList childKeys = settings->childKeys();
- for (const QString &subKey : childKeys) {
- if (settings->value(subKey, false).toBool()) {
- hasSuppressed = true;
- break;
- }
- }
+ const bool hasSuppressed = !settings->childKeys().isEmpty()
+ || !settings->childGroups().isEmpty();
settings->endGroup();
return hasSuppressed;
}
diff --git a/src/libs/utils/checkablemessagebox.h b/src/libs/utils/checkablemessagebox.h
index eb80e15eac..1f4c84e1a7 100644
--- a/src/libs/utils/checkablemessagebox.h
+++ b/src/libs/utils/checkablemessagebox.h
@@ -5,7 +5,8 @@
#include "utils_global.h"
-#include <QDialogButtonBox>
+#include "aspects.h"
+
#include <QMessageBox>
QT_BEGIN_NAMESPACE
@@ -16,100 +17,206 @@ namespace Utils {
class CheckableMessageBoxPrivate;
-class QTCREATOR_UTILS_EXPORT CheckableMessageBox : public QDialog
+class QTCREATOR_UTILS_EXPORT CheckableMessageBox
{
- Q_OBJECT
- Q_PROPERTY(QString text READ text WRITE setText)
- Q_PROPERTY(QMessageBox::Icon icon READ icon WRITE setIcon)
- Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
- Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
- Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
- Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
-
public:
- explicit CheckableMessageBox(QWidget *parent);
- ~CheckableMessageBox() override;
-
- static QDialogButtonBox::StandardButton
- question(QWidget *parent,
- const QString &title,
- const QString &question,
- const QString &checkBoxText,
- bool *checkBoxSetting,
- QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
- QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
-
- static QDialogButtonBox::StandardButton
- information(QWidget *parent,
- const QString &title,
- const QString &text,
- const QString &checkBoxText,
- bool *checkBoxSetting,
- QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Ok,
- QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::NoButton);
-
- static QDialogButtonBox::StandardButton
- doNotAskAgainQuestion(QWidget *parent,
- const QString &title,
- const QString &text,
- QSettings *settings,
- const QString &settingsSubKey,
- QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
- QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No,
- QDialogButtonBox::StandardButton acceptButton = QDialogButtonBox::Yes);
-
- static QDialogButtonBox::StandardButton
- doNotShowAgainInformation(QWidget *parent,
- const QString &title,
- const QString &text,
- QSettings *settings,
- const QString &settingsSubKey,
- QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Ok,
- QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::NoButton);
-
- QString text() const;
- void setText(const QString &);
-
- bool isChecked() const;
- void setChecked(bool s);
-
- QString checkBoxText() const;
- void setCheckBoxText(const QString &);
-
- bool isCheckBoxVisible() const;
- void setCheckBoxVisible(bool);
-
- QString detailedText() const;
- void setDetailedText(const QString &text);
-
- QDialogButtonBox::StandardButtons standardButtons() const;
- void setStandardButtons(QDialogButtonBox::StandardButtons s);
- QPushButton *button(QDialogButtonBox::StandardButton b) const;
- QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
-
- QDialogButtonBox::StandardButton defaultButton() const;
- void setDefaultButton(QDialogButtonBox::StandardButton s);
-
- QMessageBox::Icon icon() const;
- void setIcon(QMessageBox::Icon icon);
-
- // Query the result
- QAbstractButton *clickedButton() const;
- QDialogButtonBox::StandardButton clickedStandardButton() const;
+ struct BoolDecision
+ {
+ QString text;
+ bool &doNotAskAgain;
+ };
+
+ struct SettingsDecision
+ {
+ QString text;
+ QSettings *settings;
+ QString settingsSubKey;
+ };
+
+ struct AspectDecision
+ {
+ QString text;
+ BoolAspect &aspect;
+ };
+
+ using Decider = std::variant<BoolDecision, SettingsDecision, AspectDecision>;
+
+ static Decider make_decider(QSettings *settings,
+ const QString &settingsSubKey,
+ const QString &text = msgDoNotAskAgain())
+ {
+ return Decider{SettingsDecision{text, settings, settingsSubKey}};
+ }
+
+ static Decider make_decider(bool &doNotAskAgain, const QString &text = msgDoNotAskAgain())
+ {
+ return Decider{BoolDecision{text, doNotAskAgain}};
+ }
+
+ static Decider make_decider(BoolAspect &aspect, const QString &text = msgDoNotAskAgain())
+ {
+ return Decider{AspectDecision{text, aspect}};
+ }
+
+ static QMessageBox::StandardButton question(
+ QWidget *parent,
+ const QString &title,
+ const QString &question,
+ std::optional<Decider> decider = std::nullopt,
+ QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::StandardButton defaultButton = QMessageBox::No,
+ QMessageBox::StandardButton acceptButton = QMessageBox::Yes,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {});
+
+ static QMessageBox::StandardButton question(
+ QWidget *parent,
+ const QString &title,
+ const QString &question,
+ bool &value,
+ QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::StandardButton defaultButton = QMessageBox::No,
+ QMessageBox::StandardButton acceptButton = QMessageBox::Yes,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {},
+ const QString &text = msgDoNotAskAgain())
+ {
+ Decider decider = make_decider(value, text);
+ return CheckableMessageBox::question(parent,
+ title,
+ question,
+ decider,
+ buttons,
+ defaultButton,
+ acceptButton,
+ buttonTextOverrides);
+ }
+
+ static QMessageBox::StandardButton question(
+ QWidget *parent,
+ const QString &title,
+ const QString &question,
+ QSettings *settings,
+ const QString &settingsSubKey,
+ QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::StandardButton defaultButton = QMessageBox::No,
+ QMessageBox::StandardButton acceptButton = QMessageBox::Yes,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {},
+ const QString &text = msgDoNotAskAgain())
+ {
+ Decider decider = make_decider(settings, settingsSubKey, text);
+ return CheckableMessageBox::question(parent,
+ title,
+ question,
+ decider,
+ buttons,
+ defaultButton,
+ acceptButton,
+ buttonTextOverrides);
+ }
+
+ static QMessageBox::StandardButton question(
+ QWidget *parent,
+ const QString &title,
+ const QString &question,
+ BoolAspect &aspect,
+ QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::StandardButton defaultButton = QMessageBox::No,
+ QMessageBox::StandardButton acceptButton = QMessageBox::Yes,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {},
+ const QString &text = msgDoNotAskAgain())
+ {
+ Decider decider = make_decider(aspect, text);
+ return CheckableMessageBox::question(parent,
+ title,
+ question,
+ decider,
+ buttons,
+ defaultButton,
+ acceptButton,
+ buttonTextOverrides);
+ }
+
+ static QMessageBox::StandardButton information(
+ QWidget *parent,
+ const QString &title,
+ const QString &text,
+ std::optional<Decider> decider = std::nullopt,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton defaultButton = QMessageBox::NoButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {});
+
+ static QMessageBox::StandardButton information(
+ QWidget *parent,
+ const QString &title,
+ const QString &information,
+ bool &value,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton defaultButton = QMessageBox::NoButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {},
+ const QString &text = msgDoNotAskAgain())
+ {
+ Decider decider = make_decider(value, text);
+ return CheckableMessageBox::information(parent,
+ title,
+ information,
+ decider,
+ buttons,
+ defaultButton,
+ buttonTextOverrides);
+ }
+
+ static QMessageBox::StandardButton information(
+ QWidget *parent,
+ const QString &title,
+ const QString &information,
+ QSettings *settings,
+ const QString &settingsSubKey,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton defaultButton = QMessageBox::NoButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {},
+ const QString &text = msgDoNotAskAgain())
+ {
+ Decider decider = make_decider(settings, settingsSubKey, text);
+ return CheckableMessageBox::information(parent,
+ title,
+ information,
+ decider,
+ buttons,
+ defaultButton,
+ buttonTextOverrides);
+ }
+
+ static QMessageBox::StandardButton information(
+ QWidget *parent,
+ const QString &title,
+ const QString &information,
+ BoolAspect &aspect,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton defaultButton = QMessageBox::NoButton,
+ QMap<QMessageBox::StandardButton, QString> buttonTextOverrides = {},
+ const QString &text = msgDoNotAskAgain())
+ {
+ Decider decider = make_decider(aspect, text);
+ return CheckableMessageBox::information(parent,
+ title,
+ information,
+ decider,
+ buttons,
+ defaultButton,
+ buttonTextOverrides);
+ }
// check and set "ask again" status
- static bool shouldAskAgain(QSettings *settings, const QString &settingsSubKey);
- static void doNotAskAgain(QSettings *settings, const QString &settingsSubKey);
+ static bool shouldAskAgain(const Decider &decider);
+ static void doNotAskAgain(Decider &decider);
+
+ static bool shouldAskAgain(QSettings *settings, const QString &key);
+ static void doNotAskAgain(QSettings *settings, const QString &key);
// Conversion convenience
- static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
static void resetAllDoNotAskAgainQuestions(QSettings *settings);
static bool hasSuppressedQuestions(QSettings *settings);
static QString msgDoNotAskAgain();
static QString msgDoNotShowAgain();
-
-private:
- CheckableMessageBoxPrivate *d;
};
} // namespace Utils
diff --git a/src/libs/utils/clangutils.cpp b/src/libs/utils/clangutils.cpp
index 3f2fb69909..6cf265820e 100644
--- a/src/libs/utils/clangutils.cpp
+++ b/src/libs/utils/clangutils.cpp
@@ -4,7 +4,7 @@
#include "clangutils.h"
#include "filepath.h"
-#include "qtcprocess.h"
+#include "process.h"
#include "utilstr.h"
#include <QVersionNumber>
@@ -13,7 +13,7 @@ namespace Utils {
static QVersionNumber getClangdVersion(const FilePath &clangdFilePath)
{
- QtcProcess clangdProc;
+ Process clangdProc;
clangdProc.setCommand({clangdFilePath, {"--version"}});
clangdProc.runBlocking();
if (clangdProc.result() != ProcessResult::FinishedWithSuccess)
@@ -45,6 +45,11 @@ QVersionNumber clangdVersion(const FilePath &clangd)
bool checkClangdVersion(const FilePath &clangd, QString *error)
{
+ if (clangd.isEmpty()) {
+ *error = Tr::tr("No clangd executable specified.");
+ return false;
+ }
+
const QVersionNumber version = clangdVersion(clangd);
if (version >= minimumClangdVersion())
return true;
diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp
index 6f8d4a4f5d..103049ae24 100644
--- a/src/libs/utils/commandline.cpp
+++ b/src/libs/utils/commandline.cpp
@@ -362,12 +362,12 @@ static QStringList splitArgsUnix(const QString &args, bool abortOnMeta,
if (var == pwdName && pwd && !pwd->isEmpty()) {
cret += *pwd;
} else {
- Environment::const_iterator vit = env->constFind(var);
- if (vit == env->constEnd()) {
+ const Environment::FindResult res = env->find(var);
+ if (!res) {
if (abortOnMeta)
goto metaerr; // Assume this is a shell builtin
} else {
- cret += env->expandedValueForKey(env->key(vit));
+ cret += env->expandedValueForKey(res->key);
}
}
if (!braced)
@@ -412,12 +412,12 @@ static QStringList splitArgsUnix(const QString &args, bool abortOnMeta,
if (var == pwdName && pwd && !pwd->isEmpty()) {
val = *pwd;
} else {
- Environment::const_iterator vit = env->constFind(var);
- if (vit == env->constEnd()) {
+ const Environment::FindResult res = env->find(var);
+ if (!res) {
if (abortOnMeta)
goto metaerr; // Assume this is a shell builtin
} else {
- val = env->expandedValueForKey(env->key(vit));
+ val = env->expandedValueForKey(res->key);
}
}
for (int i = 0; i < val.length(); i++) {
@@ -893,7 +893,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o
break;
case CrtClosed:
// Two consecutive quotes make a literal quote - and
- // still close quoting. See QtcProcess::quoteArg().
+ // still close quoting. See Process::quoteArg().
crtState = CrtInWord;
break;
case CrtHadQuote:
@@ -1409,6 +1409,8 @@ CommandLine::CommandLine(const FilePath &executable)
: m_executable(executable)
{}
+CommandLine::~CommandLine() = default;
+
CommandLine::CommandLine(const FilePath &exe, const QStringList &args)
: m_executable(exe)
{
@@ -1429,16 +1431,20 @@ CommandLine::CommandLine(const FilePath &exe, const QString &args, RawType)
CommandLine CommandLine::fromUserInput(const QString &cmdline, MacroExpander *expander)
{
- CommandLine cmd;
- const int pos = cmdline.indexOf(' ');
- if (pos == -1) {
- cmd.m_executable = FilePath::fromString(cmdline);
- } else {
- cmd.m_executable = FilePath::fromString(cmdline.left(pos));
- cmd.m_arguments = cmdline.right(cmdline.length() - pos - 1);
- if (expander)
- cmd.m_arguments = expander->expand(cmd.m_arguments);
- }
+ if (cmdline.isEmpty())
+ return {};
+
+ QString input = cmdline.trimmed();
+
+ QStringList result = ProcessArgs::splitArgs(cmdline, HostOsInfo::hostOs());
+
+ if (result.isEmpty())
+ return {};
+
+ auto cmd = CommandLine(FilePath::fromUserInput(result.value(0)), result.mid(1));
+ if (expander)
+ cmd.m_arguments = expander->expand(cmd.m_arguments);
+
return cmd;
}
diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h
index 23585adb8a..d7fc0a066b 100644
--- a/src/libs/utils/commandline.h
+++ b/src/libs/utils/commandline.h
@@ -55,7 +55,7 @@ public:
//! Append already quoted arguments to a shell command
static void addArgs(QString *args, const QString &inArgs);
//! Split a shell command into separate arguments.
- static QStringList splitArgs(const QString &cmd, OsType osType = HostOsInfo::hostOs(),
+ static QStringList splitArgs(const QString &cmd, OsType osType,
bool abortOnMeta = false, SplitError *err = nullptr,
const Environment *env = nullptr, const QString *pwd = nullptr);
//! Safely replace the expandos in a shell command
@@ -119,6 +119,8 @@ public:
enum RawType { Raw };
CommandLine();
+ ~CommandLine();
+
explicit CommandLine(const FilePath &executable);
CommandLine(const FilePath &exe, const QStringList &args);
CommandLine(const FilePath &exe, const QStringList &args, OsType osType);
diff --git a/src/libs/utils/delegates.h b/src/libs/utils/delegates.h
index 97f1c774c3..ff012152f5 100644
--- a/src/libs/utils/delegates.h
+++ b/src/libs/utils/delegates.h
@@ -62,18 +62,14 @@ private:
class QTCREATOR_UTILS_EXPORT CompleterDelegate : public QStyledItemDelegate
{
+ Q_DISABLE_COPY_MOVE(CompleterDelegate)
+
public:
CompleterDelegate(const QStringList &candidates, QObject *parent = nullptr);
CompleterDelegate(QAbstractItemModel *model, QObject *parent = nullptr);
CompleterDelegate(QCompleter *completer, QObject *parent = nullptr);
~CompleterDelegate() override;
- CompleterDelegate(const CompleterDelegate &other) = delete;
- CompleterDelegate(CompleterDelegate &&other) = delete;
-
- CompleterDelegate &operator=(const CompleterDelegate &other) = delete;
- CompleterDelegate &operator=(CompleterDelegate &&other) = delete;
-
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp
index 3d8354d7a0..a29c5bb9be 100644
--- a/src/libs/utils/devicefileaccess.cpp
+++ b/src/libs/utils/devicefileaccess.cpp
@@ -8,11 +8,12 @@
#include "environment.h"
#include "expected.h"
#include "hostosinfo.h"
+#include "osspecificaspects.h"
#include "qtcassert.h"
#include "utilstr.h"
#ifndef UTILS_STATIC_LIBRARY
-#include "qtcprocess.h"
+#include "process.h"
#endif
#include <QCoreApplication>
@@ -105,6 +106,13 @@ bool DeviceFileAccess::isSymLink(const FilePath &filePath) const
return false;
}
+bool DeviceFileAccess::hasHardLinks(const FilePath &filePath) const
+{
+ Q_UNUSED(filePath)
+ QTC_CHECK(false);
+ return false;
+}
+
bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const
{
if (isWritableDirectory(filePath))
@@ -203,11 +211,8 @@ expected_str<void> DeviceFileAccess::copyRecursively(const FilePath &src,
#ifdef UTILS_STATIC_LIBRARY
return copyRecursively_fallback(src, target);
#else
- const FilePath tar = FilePath::fromString("tar").onDevice(target);
- const FilePath targetTar = tar.searchInPath();
-
- const FilePath sTar = FilePath::fromString("tar").onDevice(src);
- const FilePath sourceTar = sTar.searchInPath();
+ const FilePath targetTar = target.withNewPath("tar").searchInPath();
+ const FilePath sourceTar = src.withNewPath("tar").searchInPath();
const bool isSrcOrTargetQrc = src.toFSPathString().startsWith(":/")
|| target.toFSPathString().startsWith(":/");
@@ -215,13 +220,13 @@ expected_str<void> DeviceFileAccess::copyRecursively(const FilePath &src,
if (isSrcOrTargetQrc || !targetTar.isExecutableFile() || !sourceTar.isExecutableFile())
return copyRecursively_fallback(src, target);
- QtcProcess srcProcess;
- QtcProcess targetProcess;
+ Process srcProcess;
+ Process targetProcess;
targetProcess.setProcessMode(ProcessMode::Writer);
QObject::connect(&srcProcess,
- &QtcProcess::readyReadStandardOutput,
+ &Process::readyReadStandardOutput,
&targetProcess,
[&srcProcess, &targetProcess]() {
targetProcess.writeRaw(srcProcess.readAllRawStandardOutput());
@@ -268,12 +273,6 @@ bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &targ
return false;
}
-OsType DeviceFileAccess::osType(const FilePath &filePath) const
-{
- Q_UNUSED(filePath)
- return OsTypeOther;
-}
-
FilePath DeviceFileAccess::symLinkTarget(const FilePath &filePath) const
{
Q_UNUSED(filePath)
@@ -379,29 +378,6 @@ std::optional<FilePath> DeviceFileAccess::refersToExecutableFile(
return {};
}
-void DeviceFileAccess::asyncFileContents(const FilePath &filePath,
- const Continuation<expected_str<QByteArray>> &cont,
- qint64 limit,
- qint64 offset) const
-{
- cont(fileContents(filePath, limit, offset));
-}
-
-void DeviceFileAccess::asyncWriteFileContents(const FilePath &filePath,
- const Continuation<expected_str<qint64>> &cont,
- const QByteArray &data,
- qint64 offset) const
-{
- cont(writeFileContents(filePath, data, offset));
-}
-
-void DeviceFileAccess::asyncCopyFile(const FilePath &filePath,
- const Continuation<expected_str<void>> &cont,
- const FilePath &target) const
-{
- cont(copyFile(filePath, target));
-}
-
expected_str<FilePath> DeviceFileAccess::createTempFile(const FilePath &filePath)
{
Q_UNUSED(filePath)
@@ -506,6 +482,23 @@ bool DesktopDeviceFileAccess::isSymLink(const FilePath &filePath) const
return fi.isSymLink();
}
+bool DesktopDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
+{
+#ifdef Q_OS_UNIX
+ struct stat s
+ {};
+ const int r = stat(filePath.absoluteFilePath().toString().toLocal8Bit().constData(), &s);
+ if (r == 0) {
+ // check for hardlinks because these would break without the atomic write implementation
+ if (s.st_nlink > 1)
+ return true;
+ }
+#else
+ Q_UNUSED(filePath)
+#endif
+ return false;
+}
+
bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
@@ -587,10 +580,13 @@ bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QStrin
expected_str<void> DesktopDeviceFileAccess::copyFile(const FilePath &filePath,
const FilePath &target) const
{
- if (QFile::copy(filePath.path(), target.path()))
+ QFile srcFile(filePath.path());
+
+ if (srcFile.copy(target.path()))
return {};
- return make_unexpected(Tr::tr("Failed to copy file \"%1\" to \"%2\".")
- .arg(filePath.toUserOutput(), target.toUserOutput()));
+ return make_unexpected(
+ Tr::tr("Failed to copy file \"%1\" to \"%2\": %3")
+ .arg(filePath.toUserOutput(), target.toUserOutput(), srcFile.errorString()));
}
bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
@@ -708,12 +704,13 @@ expected_str<FilePath> DesktopDeviceFileAccess::createTempFile(const FilePath &f
{
QTemporaryFile file(filePath.path());
file.setAutoRemove(false);
- if (!file.open())
- return make_unexpected(Tr::tr("Could not create temporary file in \"%1\" (%2)").arg(filePath.toUserOutput()).arg(file.errorString()));
- return FilePath::fromString(file.fileName()).onDevice(filePath);
+ if (!file.open()) {
+ return make_unexpected(Tr::tr("Could not create temporary file in \"%1\" (%2)")
+ .arg(filePath.toUserOutput()).arg(file.errorString()));
+ }
+ return filePath.withNewPath(file.fileName());
}
-
QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const
{
return QFileInfo(filePath.path()).lastModified();
@@ -818,12 +815,6 @@ QByteArray DesktopDeviceFileAccess::fileId(const FilePath &filePath) const
return result;
}
-OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const
-{
- Q_UNUSED(filePath);
- return HostOsInfo::hostOs();
-}
-
// UnixDeviceAccess
UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
@@ -882,6 +873,13 @@ bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const
return runInShellSuccess({"test", {"-h", path}, OsType::OsTypeLinux});
}
+bool UnixDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
+{
+ const QStringList args = statArgs(filePath, "%h", "%l");
+ const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
+ return result.stdOut.toLongLong() > 1;
+}
+
bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
{
const QString path = filePath.path();
@@ -964,7 +962,7 @@ expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &file
#ifndef UTILS_STATIC_LIBRARY
const FilePath dd = filePath.withNewPath("dd");
- QtcProcess p;
+ Process p;
p.setCommand({dd, args, OsType::OsTypeLinux});
p.runBlocking();
if (p.exitCode() != 0) {
@@ -1015,7 +1013,7 @@ expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &file
.arg(filePath.toUserOutput(), QString::fromUtf8(result.stdErr)));
}
- return FilePath::fromString(QString::fromUtf8(result.stdOut.trimmed())).onDevice(filePath);
+ return filePath.withNewPath(QString::fromUtf8(result.stdOut.trimmed()));
}
// Manually create a temporary/unique file.
@@ -1039,7 +1037,7 @@ expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &file
for (QChar *it = firstX.base(); it != std::end(tmplate); ++it) {
*it = chars[dist(*QRandomGenerator::global())];
}
- newPath = FilePath::fromString(tmplate).onDevice(filePath);
+ newPath = filePath.withNewPath(tmplate);
if (--maxTries == 0) {
return make_unexpected(Tr::tr("Failed creating temporary file \"%1\" (too many tries)")
.arg(filePath.toUserOutput()));
@@ -1054,12 +1052,6 @@ expected_str<FilePath> UnixDeviceFileAccess::createTempFile(const FilePath &file
return newPath;
}
-OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const
-{
- Q_UNUSED(filePath)
- return OsTypeLinux;
-}
-
QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
{
const RunResult result = runInShell(
@@ -1069,10 +1061,19 @@ QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
return dt;
}
+QStringList UnixDeviceFileAccess::statArgs(const FilePath &filePath,
+ const QString &linuxFormat,
+ const QString &macFormat) const
+{
+ return (filePath.osType() == OsTypeMac ? QStringList{"-f", macFormat} : QStringList{"-c", linuxFormat})
+ << "-L" << filePath.path();
+}
+
QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const
{
- const RunResult result = runInShell(
- {"stat", {"-L", "-c", "%a", filePath.path()}, OsType::OsTypeLinux});
+ QStringList args = statArgs(filePath, "%a", "%p");
+
+ const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
const uint bits = result.stdOut.toUInt(nullptr, 8);
QFileDevice::Permissions perm = {};
#define BIT(n, p) \
@@ -1100,8 +1101,8 @@ bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permi
qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
{
- const RunResult result = runInShell(
- {"stat", {"-L", "-c", "%s", filePath.path()}, OsType::OsTypeLinux});
+ const QStringList args = statArgs(filePath, "%s", "%z");
+ const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
return result.stdOut.toLongLong();
}
@@ -1113,8 +1114,9 @@ qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
{
- const RunResult result = runInShell(
- {"stat", {"-L", "-c", "%D:%i", filePath.path()}, OsType::OsTypeLinux});
+ const QStringList args = statArgs(filePath, "%D:%i", "%d:%i");
+
+ const RunResult result = runInShell({"stat", args, OsType::OsTypeLinux});
if (result.exitCode != 0)
return {};
@@ -1136,9 +1138,12 @@ FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const
return r;
}
- const RunResult stat = runInShell(
- {"stat", {"-L", "-c", "%f %Y %s", filePath.path()}, OsType::OsTypeLinux});
- return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
+
+ const QStringList args = statArgs(filePath, "%f %Y %s", "%p %m %z");
+
+ const RunResult stat = runInShell({"stat", args, OsType::OsTypeLinux});
+ return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut),
+ filePath.osType() == OsTypeMac ? 8 : 16);
}
// returns whether 'find' could be used.
@@ -1152,8 +1157,13 @@ bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath,
// TODO: Using stat -L will always return the link target, not the link itself.
// We may wan't to add the information that it is a link at some point.
+
+ const QString statFormat = filePath.osType() == OsTypeMac
+ ? QLatin1String("-f \"%p %m %z\"") : QLatin1String("-c \"%f %Y %s\"");
+
if (callBack.index() == 1)
- cmdLine.addArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)",
+ cmdLine.addArgs(QString(R"(-exec echo -n \"{}\"" " \; -exec stat -L %1 "{}" \;)")
+ .arg(statFormat),
CommandLine::Raw);
const RunResult result = runInShell(cmdLine);
@@ -1175,23 +1185,26 @@ bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath,
if (entries.isEmpty())
return true;
- const auto toFilePath = [&filePath, &callBack](const QString &entry) {
- if (callBack.index() == 0)
- return std::get<0>(callBack)(filePath.withNewPath(entry));
+ const int modeBase = filePath.osType() == OsTypeMac ? 8 : 16;
- const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
- const QString infos = entry.mid(fileName.length() + 3);
+ const auto toFilePath =
+ [&filePath, &callBack, modeBase](const QString &entry) {
+ if (callBack.index() == 0)
+ return std::get<0>(callBack)(filePath.withNewPath(entry));
- const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
- if (!fi.fileFlags)
- return IterationPolicy::Continue;
+ const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
+ const QString infos = entry.mid(fileName.length() + 3);
- const FilePath fp = filePath.withNewPath(fileName);
- // Do not return the entry for the directory we are searching in.
- if (fp.path() == filePath.path())
- return IterationPolicy::Continue;
- return std::get<1>(callBack)(fp, fi);
- };
+ const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos, modeBase);
+ if (!fi.fileFlags)
+ return IterationPolicy::Continue;
+
+ const FilePath fp = filePath.withNewPath(fileName);
+ // Do not return the entry for the directory we are searching in.
+ if (fp.path() == filePath.path())
+ return IterationPolicy::Continue;
+ return std::get<1>(callBack)(fp, fi);
+ };
// Remove the first line, this can be the directory we are searching in.
// as long as we do not specify "mindepth > 0"
@@ -1210,7 +1223,7 @@ void UnixDeviceFileAccess::findUsingLs(const QString &current,
const FileFilter &filter,
QStringList *found) const
{
- const RunResult result = runInShell({"ls", {"-1", "-p", "--", current}, OsType::OsTypeLinux});
+ const RunResult result = runInShell({"ls", {"-1", "-a", "-p", "--", current}, OsType::OsTypeLinux});
const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
for (QString entry : entries) {
const QChar last = entry.back();
@@ -1272,8 +1285,8 @@ void UnixDeviceFileAccess::iterateDirectory(const FilePath &filePath,
if (m_tryUseFind) {
if (iterateWithFind(filePath, filter, callBack))
return;
- m_tryUseFind
- = false; // remember the failure for the next time and use the 'ls' fallback below.
+ // Remember the failure for the next time and use the 'ls' fallback below.
+ m_tryUseFind = false;
}
// if we do not have find - use ls as fallback
diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h
index 8fe8201e08..0f8282477f 100644
--- a/src/libs/utils/devicefileaccess.h
+++ b/src/libs/utils/devicefileaccess.h
@@ -3,10 +3,13 @@
#pragma once
+#include "hostosinfo.h"
#include "utils_global.h"
#include "fileutils.h"
+class tst_unixdevicefileaccess; // For testing.
+
namespace Utils {
// Base class including dummy implementation usable as fallback.
@@ -19,6 +22,7 @@ public:
protected:
friend class FilePath;
+ friend class ::tst_unixdevicefileaccess; // For testing.
virtual QString mapToDevicePath(const QString &hostPath) const;
@@ -30,6 +34,7 @@ protected:
virtual bool isFile(const FilePath &filePath) const;
virtual bool isDirectory(const FilePath &filePath) const;
virtual bool isSymLink(const FilePath &filePath) const;
+ virtual bool hasHardLinks(const FilePath &filePath) const;
virtual bool ensureWritableDirectory(const FilePath &filePath) const;
virtual bool ensureExistingFile(const FilePath &filePath) const;
virtual bool createDirectory(const FilePath &filePath) const;
@@ -41,7 +46,6 @@ protected:
const FilePath &target) const;
virtual bool renameFile(const FilePath &filePath, const FilePath &target) const;
- virtual OsType osType(const FilePath &filePath) const;
virtual FilePath symLinkTarget(const FilePath &filePath) const;
virtual FilePathInfo filePathInfo(const FilePath &filePath) const;
virtual QDateTime lastModified(const FilePath &filePath) const;
@@ -68,20 +72,6 @@ protected:
const QByteArray &data,
qint64 offset) const;
- virtual void asyncFileContents(const FilePath &filePath,
- const Continuation<expected_str<QByteArray>> &cont,
- qint64 limit,
- qint64 offset) const;
-
- virtual void asyncWriteFileContents(const FilePath &filePath,
- const Continuation<expected_str<qint64>> &cont,
- const QByteArray &data,
- qint64 offset) const;
-
- virtual void asyncCopyFile(const FilePath &filePath,
- const Continuation<expected_str<void>> &cont,
- const FilePath &target) const;
-
virtual expected_str<FilePath> createTempFile(const FilePath &filePath);
};
@@ -101,6 +91,7 @@ protected:
bool isFile(const FilePath &filePath) const override;
bool isDirectory(const FilePath &filePath) const override;
bool isSymLink(const FilePath &filePath) const override;
+ bool hasHardLinks(const FilePath &filePath) const override;
bool ensureWritableDirectory(const FilePath &filePath) const override;
bool ensureExistingFile(const FilePath &filePath) const override;
bool createDirectory(const FilePath &filePath) const override;
@@ -110,7 +101,6 @@ protected:
expected_str<void> copyFile(const FilePath &filePath, const FilePath &target) const override;
bool renameFile(const FilePath &filePath, const FilePath &target) const override;
- OsType osType(const FilePath &filePath) const override;
FilePath symLinkTarget(const FilePath &filePath) const override;
FilePathInfo filePathInfo(const FilePath &filePath) const override;
QDateTime lastModified(const FilePath &filePath) const override;
@@ -160,6 +150,7 @@ protected:
bool isFile(const FilePath &filePath) const override;
bool isDirectory(const FilePath &filePath) const override;
bool isSymLink(const FilePath &filePath) const override;
+ bool hasHardLinks(const FilePath &filePath) const override;
bool ensureExistingFile(const FilePath &filePath) const override;
bool createDirectory(const FilePath &filePath) const override;
bool exists(const FilePath &filePath) const override;
@@ -169,7 +160,6 @@ protected:
bool renameFile(const FilePath &filePath, const FilePath &target) const override;
FilePathInfo filePathInfo(const FilePath &filePath) const override;
- OsType osType(const FilePath &filePath) const override;
FilePath symLinkTarget(const FilePath &filePath) const override;
QDateTime lastModified(const FilePath &filePath) const override;
QFile::Permissions permissions(const FilePath &filePath) const override;
@@ -204,6 +194,10 @@ private:
const FileFilter &filter,
QStringList *found) const;
+ QStringList statArgs(const FilePath &filePath,
+ const QString &linuxFormat,
+ const QString &macFormat) const;
+
mutable bool m_tryUseFind = true;
mutable std::optional<bool> m_hasMkTemp;
};
diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp
index 18e816d38b..f6474721ac 100644
--- a/src/libs/utils/deviceshell.cpp
+++ b/src/libs/utils/deviceshell.cpp
@@ -3,9 +3,9 @@
#include "deviceshell.h"
+#include "process.h"
#include "processinterface.h"
#include "qtcassert.h"
-#include "qtcprocess.h"
#include <QLoggingCategory>
#include <QScopeGuard>
@@ -73,15 +73,15 @@ QStringList DeviceShell::missingFeatures() const { return m_missingFeatures; }
RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData)
{
- // If the script failed to install, use QtcProcess directly instead.
+ // If the script failed to install, use Process directly instead.
bool useProcess = m_shellScriptState == State::Failed;
// Transferring large amounts of stdInData is slow via the shell script.
- // Use QtcProcess directly if the size exceeds 100kb.
+ // Use Process directly if the size exceeds 100kb.
useProcess |= stdInData.size() > (1024 * 100);
if (useProcess) {
- QtcProcess proc;
+ Process proc;
const CommandLine fallbackCmd = createFallbackCommand(cmd);
qCDebug(deviceShellLog) << "Running fallback:" << fallbackCmd;
proc.setCommand(fallbackCmd);
@@ -97,6 +97,7 @@ RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData)
const RunResult errorResult{-1, {}, {}};
QTC_ASSERT(m_shellProcess, return errorResult);
+ QTC_ASSERT(m_shellProcess->isRunning(), return errorResult);
QTC_ASSERT(m_shellScriptState == State::Succeeded, return errorResult);
QMutexLocker lk(&m_commandMutex);
@@ -135,7 +136,7 @@ void DeviceShell::close()
* Override this function to setup the shell process.
* The default implementation just sets the command line to "bash"
*/
-void DeviceShell::setupShellProcess(QtcProcess *shellProcess)
+void DeviceShell::setupShellProcess(Process *shellProcess)
{
shellProcess->setCommand(CommandLine{"bash"});
}
@@ -170,8 +171,8 @@ void DeviceShell::startupFailed(const CommandLine &cmdLine)
*/
bool DeviceShell::start()
{
- m_shellProcess = std::make_unique<QtcProcess>();
- connect(m_shellProcess.get(), &QtcProcess::done, m_shellProcess.get(),
+ m_shellProcess = std::make_unique<Process>();
+ connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(),
[this] { emit done(m_shellProcess->resultData()); });
connect(&m_thread, &QThread::finished, m_shellProcess.get(), [this] { closeShellProcess(); }, Qt::DirectConnection);
@@ -198,11 +199,11 @@ bool DeviceShell::start()
if (installShellScript()) {
connect(m_shellProcess.get(),
- &QtcProcess::readyReadStandardOutput,
+ &Process::readyReadStandardOutput,
m_shellProcess.get(),
[this] { onReadyRead(); });
connect(m_shellProcess.get(),
- &QtcProcess::readyReadStandardError,
+ &Process::readyReadStandardError,
m_shellProcess.get(),
[this] {
const QByteArray stdErr = m_shellProcess->readAllRawStandardError();
@@ -215,7 +216,7 @@ bool DeviceShell::start()
return false;
}
- connect(m_shellProcess.get(), &QtcProcess::done, m_shellProcess.get(), [this] {
+ connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), [this] {
if (m_shellProcess->resultData().m_exitCode != EXIT_SUCCESS
|| m_shellProcess->resultData().m_exitStatus != QProcess::NormalExit) {
qCWarning(deviceShellLog) << "Shell exited with error code:"
diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h
index 83af1db365..052aac1838 100644
--- a/src/libs/utils/deviceshell.h
+++ b/src/libs/utils/deviceshell.h
@@ -19,7 +19,7 @@ namespace Utils {
class CommandLine;
class ProcessResultData;
-class QtcProcess;
+class Process;
class DeviceShellImpl;
@@ -57,7 +57,7 @@ protected:
void close();
private:
- virtual void setupShellProcess(QtcProcess *shellProcess);
+ virtual void setupShellProcess(Process *shellProcess);
virtual CommandLine createFallbackCommand(const CommandLine &cmdLine);
bool installShellScript();
@@ -73,7 +73,7 @@ private:
QWaitCondition *waiter;
};
- std::unique_ptr<QtcProcess> m_shellProcess;
+ std::unique_ptr<Process> m_shellProcess;
QThread m_thread;
int m_currentId{0};
diff --git a/src/libs/utils/differ.cpp b/src/libs/utils/differ.cpp
index 538aa6657a..9dc2ce8454 100644
--- a/src/libs/utils/differ.cpp
+++ b/src/libs/utils/differ.cpp
@@ -14,11 +14,10 @@ publication by Neil Fraser: http://neil.fraser.name/writing/diff/
#include "utilstr.h"
#include <QList>
-#include <QRegularExpression>
-#include <QStringList>
#include <QMap>
#include <QPair>
-#include <QFutureInterfaceBase>
+#include <QRegularExpression>
+#include <QStringList>
namespace Utils {
@@ -937,10 +936,9 @@ QString Diff::toString() const
///////////////
-Differ::Differ(QFutureInterfaceBase *jobController)
- : m_jobController(jobController)
+Differ::Differ(const std::optional<QFuture<void>> &future)
+ : m_future(future)
{
-
}
QList<Diff> Differ::diff(const QString &text1, const QString &text2)
@@ -1075,7 +1073,7 @@ QList<Diff> Differ::diffMyers(const QString &text1, const QString &text2)
int kMinReverse = -D;
int kMaxReverse = D;
for (int d = 0; d <= D; d++) {
- if (m_jobController && m_jobController->isCanceled()) {
+ if (m_future && m_future->isCanceled()) {
delete [] forwardV;
delete [] reverseV;
return QList<Diff>();
@@ -1193,17 +1191,10 @@ QList<Diff> Differ::diffNonCharMode(const QString &text1, const QString &text2)
QString lastDelete;
QString lastInsert;
QList<Diff> newDiffList;
- if (m_jobController) {
- m_jobController->setProgressRange(0, diffList.count());
- m_jobController->setProgressValue(0);
- }
for (int i = 0; i <= diffList.count(); i++) {
- if (m_jobController) {
- if (m_jobController->isCanceled()) {
- m_currentDiffMode = diffMode;
- return QList<Diff>();
- }
- m_jobController->setProgressValue(i + 1);
+ if (m_future && m_future->isCanceled()) {
+ m_currentDiffMode = diffMode;
+ return {};
}
const Diff diffItem = i < diffList.count()
? diffList.at(i)
diff --git a/src/libs/utils/differ.h b/src/libs/utils/differ.h
index 62b8bc7ec4..dc6d74f30d 100644
--- a/src/libs/utils/differ.h
+++ b/src/libs/utils/differ.h
@@ -5,12 +5,14 @@
#include "utils_global.h"
+#include <QFuture>
#include <QString>
+#include <optional>
+
QT_BEGIN_NAMESPACE
template <class K, class T>
class QMap;
-class QFutureInterfaceBase;
QT_END_NAMESPACE
namespace Utils {
@@ -42,7 +44,7 @@ public:
WordMode,
LineMode
};
- Differ(QFutureInterfaceBase *jobController = nullptr);
+ Differ(const std::optional<QFuture<void>> &future = {});
QList<Diff> diff(const QString &text1, const QString &text2);
QList<Diff> unifiedDiff(const QString &text1, const QString &text2);
void setDiffMode(DiffMode mode);
@@ -90,7 +92,7 @@ private:
int subTextStart);
DiffMode m_diffMode = Differ::LineMode;
DiffMode m_currentDiffMode = Differ::LineMode;
- QFutureInterfaceBase *m_jobController = nullptr;
+ std::optional<QFuture<void>> m_future;
};
} // namespace Utils
diff --git a/src/libs/utils/dropsupport.cpp b/src/libs/utils/dropsupport.cpp
index 9d914e0f08..6def3fd1af 100644
--- a/src/libs/utils/dropsupport.cpp
+++ b/src/libs/utils/dropsupport.cpp
@@ -40,10 +40,6 @@ static bool isFileDropMime(const QMimeData *d, QList<DropSupport::FileSpec> *fil
const QList<QUrl>::const_iterator cend = urls.constEnd();
for (QList<QUrl>::const_iterator it = urls.constBegin(); it != cend; ++it) {
QUrl url = *it;
-#ifdef Q_OS_OSX
- // for file drops from Finder, working around QTBUG-40449
- url = Internal::filePathUrl(url);
-#endif
const QString fileName = url.toLocalFile();
if (!fileName.isEmpty()) {
hasFiles = true;
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index c99293329c..977e63adb0 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -4,6 +4,7 @@
#include "environment.h"
#include "algorithm.h"
+#include "filepath.h"
#include "qtcassert.h"
#include <QDir>
@@ -18,83 +19,149 @@ Q_GLOBAL_STATIC_WITH_ARGS(Environment, staticSystemEnvironment,
(QProcessEnvironment::systemEnvironment().toStringList()))
Q_GLOBAL_STATIC(QVector<EnvironmentProvider>, environmentProviders)
+Environment::Environment()
+ : m_dict(HostOsInfo::hostOs())
+{}
+
+Environment::Environment(OsType osType)
+ : m_dict(osType)
+{}
+
+Environment::Environment(const QStringList &env, OsType osType)
+ : m_dict(osType)
+{
+ m_changeItems.append(NameValueDictionary(env, osType));
+}
+
+Environment::Environment(const NameValuePairs &nameValues)
+{
+ m_changeItems.append(NameValueDictionary(nameValues));
+}
+
+Environment::Environment(const NameValueDictionary &dict)
+{
+ m_changeItems.append(dict);
+}
+
NameValueItems Environment::diff(const Environment &other, bool checkAppendPrepend) const
{
- return m_dict.diff(other.m_dict, checkAppendPrepend);
+ const NameValueDictionary &dict = resolved();
+ const NameValueDictionary &otherDict = other.resolved();
+ return dict.diff(otherDict, checkAppendPrepend);
+}
+
+Environment::FindResult Environment::find(const QString &name) const
+{
+ const NameValueDictionary &dict = resolved();
+ const auto it = dict.constFind(name);
+ if (it == dict.constEnd())
+ return {};
+ return Entry{it.key().name, it.value().first, it.value().second};
+}
+
+void Environment::forEachEntry(const std::function<void(const QString &, const QString &, bool)> &callBack) const
+{
+ const NameValueDictionary &dict = resolved();
+ for (auto it = dict.m_values.constBegin(); it != dict.m_values.constEnd(); ++it)
+ callBack(it.key().name, it.value().first, it.value().second);
+}
+
+bool Environment::operator==(const Environment &other) const
+{
+ const NameValueDictionary &dict = resolved();
+ const NameValueDictionary &otherDict = other.resolved();
+ return dict == otherDict;
+}
+
+bool Environment::operator!=(const Environment &other) const
+{
+ const NameValueDictionary &dict = resolved();
+ const NameValueDictionary &otherDict = other.resolved();
+ return dict != otherDict;
+}
+
+QString Environment::value(const QString &key) const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.value(key);
+}
+
+QString Environment::value_or(const QString &key, const QString &defaultValue) const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.hasKey(key) ? dict.value(key) : defaultValue;
+}
+
+bool Environment::hasKey(const QString &key) const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.hasKey(key);
}
bool Environment::hasChanges() const
{
- return m_dict.size() != 0;
+ const NameValueDictionary &dict = resolved();
+ return dict.size() != 0;
+}
+
+OsType Environment::osType() const
+{
+ return m_dict.m_osType;
+}
+
+QStringList Environment::toStringList() const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict.toStringList();
}
QProcessEnvironment Environment::toProcessEnvironment() const
{
+ const NameValueDictionary &dict = resolved();
QProcessEnvironment result;
- for (auto it = m_dict.m_values.constBegin(); it != m_dict.m_values.constEnd(); ++it) {
+ for (auto it = dict.m_values.constBegin(); it != dict.m_values.constEnd(); ++it) {
if (it.value().second)
- result.insert(it.key().name, expandedValueForKey(key(it)));
+ result.insert(it.key().name, expandedValueForKey(dict.key(it)));
}
return result;
}
void Environment::appendOrSetPath(const FilePath &value)
{
- QTC_CHECK(value.osType() == osType());
+ QTC_CHECK(value.osType() == m_dict.m_osType);
if (value.isEmpty())
return;
- appendOrSet("PATH", value.nativePath(),
- QString(OsSpecificAspects::pathListSeparator(osType())));
+ appendOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType()));
}
void Environment::prependOrSetPath(const FilePath &value)
{
- QTC_CHECK(value.osType() == osType());
+ QTC_CHECK(value.osType() == m_dict.m_osType);
if (value.isEmpty())
return;
- prependOrSet("PATH", value.nativePath(),
- QString(OsSpecificAspects::pathListSeparator(osType())));
+ prependOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType()));
}
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{
- QTC_ASSERT(!key.contains('='), return );
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Append unless it is already there
- const QString toAppend = sep + value;
- if (!it.value().first.endsWith(toAppend))
- it.value().first.append(toAppend);
- }
+ addItem(Item{std::in_place_index_t<AppendOrSet>(), key, value, sep});
}
void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep)
{
- QTC_ASSERT(!key.contains('='), return );
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Prepend unless it is already there
- const QString toPrepend = value + sep;
- if (!it.value().first.startsWith(toPrepend))
- it.value().first.prepend(toPrepend);
- }
+ addItem(Item{std::in_place_index_t<PrependOrSet>(), key, value, sep});
}
void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
{
QTC_CHECK(value.osType() == osType());
+ const QChar sep = OsSpecificAspects::pathListSeparator(osType());
switch (osType()) {
case OsTypeWindows: {
- const QChar sep = ';';
- prependOrSet("PATH", value.nativePath(), QString(sep));
+ prependOrSet("PATH", value.nativePath(), sep);
break;
}
case OsTypeMac: {
- const QString sep = ":";
const QString nativeValue = value.nativePath();
prependOrSet("DYLD_LIBRARY_PATH", nativeValue, sep);
prependOrSet("DYLD_FRAMEWORK_PATH", nativeValue, sep);
@@ -102,8 +169,7 @@ void Environment::prependOrSetLibrarySearchPath(const FilePath &value)
}
case OsTypeLinux:
case OsTypeOtherUnix: {
- const QChar sep = ':';
- prependOrSet("LD_LIBRARY_PATH", value.nativePath(), QString(sep));
+ prependOrSet("LD_LIBRARY_PATH", value.nativePath(), sep);
break;
}
default:
@@ -126,40 +192,47 @@ Environment Environment::systemEnvironment()
void Environment::setupEnglishOutput()
{
- m_dict.set("LC_MESSAGES", "en_US.utf8");
- m_dict.set("LANGUAGE", "en_US:en");
+ addItem(Item{std::in_place_index_t<SetupEnglishOutput>()});
}
-static FilePath searchInDirectory(const QStringList &execs,
- const FilePath &directory,
- QSet<FilePath> &alreadyChecked)
+using SearchResultCallback = std::function<IterationPolicy(const FilePath &)>;
+
+static IterationPolicy searchInDirectory(const SearchResultCallback &resultCallback,
+ const FilePaths &execs,
+ const FilePath &directory,
+ QSet<FilePath> &alreadyCheckedDirectories,
+ const FilePathPredicate &filter = {})
{
- const int checkedCount = alreadyChecked.count();
- alreadyChecked.insert(directory);
+ // Compare the initial size of the set with the size after insertion to check if the directory
+ // was already checked.
+ const int initialCount = alreadyCheckedDirectories.count();
+ alreadyCheckedDirectories.insert(directory);
+ const bool wasAlreadyChecked = alreadyCheckedDirectories.count() == initialCount;
- if (directory.isEmpty() || alreadyChecked.count() == checkedCount)
- return FilePath();
+ if (directory.isEmpty() || wasAlreadyChecked)
+ return IterationPolicy::Continue;
- for (const QString &exec : execs) {
- const FilePath filePath = directory.pathAppended(exec);
- if (filePath.isExecutableFile())
- return filePath;
+ for (const FilePath &exec : execs) {
+ const FilePath filePath = directory / exec.path();
+ if (filePath.isExecutableFile() && (!filter || filter(filePath))) {
+ if (resultCallback(filePath) == IterationPolicy::Stop)
+ return IterationPolicy::Stop;
+ }
}
- return FilePath();
+ return IterationPolicy::Continue;
}
-static QStringList appendExeExtensions(const Environment &env, const QString &executable)
+static FilePaths appendExeExtensions(const Environment &env, const FilePath &executable)
{
- QStringList execs(executable);
+ FilePaths execs{executable};
if (env.osType() == OsTypeWindows) {
- const QFileInfo fi(executable);
// Check all the executable extensions on windows:
// PATHEXT is only used if the executable has no extension
- if (fi.suffix().isEmpty()) {
+ if (executable.suffix().isEmpty()) {
const QStringList extensions = env.expandedValueForKey("PATHEXT").split(';');
for (const QString &ext : extensions)
- execs << executable + ext.toLower();
+ execs << executable.stringAppended(ext.toLower());
}
}
return execs;
@@ -167,102 +240,105 @@ static QStringList appendExeExtensions(const Environment &env, const QString &ex
QString Environment::expandedValueForKey(const QString &key) const
{
- return expandVariables(m_dict.value(key));
+ const NameValueDictionary &dict = resolved();
+ return expandVariables(dict.value(key));
}
-static FilePath searchInDirectoriesHelper(const Environment &env,
- const QString &executable,
- const FilePaths &dirs,
- const Environment::PathFilter &func,
- bool usePath)
+static void searchInDirectoriesHelper(const SearchResultCallback &resultCallback,
+ const Environment &env,
+ const QString &executable,
+ const FilePaths &dirs,
+ const FilePathPredicate &func,
+ bool usePath)
{
if (executable.isEmpty())
- return FilePath();
-
- const QString exec = QDir::cleanPath(env.expandVariables(executable));
- const QFileInfo fi(exec);
+ return;
- const QStringList execs = appendExeExtensions(env, exec);
+ const FilePath exec = FilePath::fromUserInput(QDir::cleanPath(env.expandVariables(executable)));
+ const FilePaths execs = appendExeExtensions(env, exec);
- if (fi.isAbsolute()) {
- for (const QString &path : execs) {
- QFileInfo pfi = QFileInfo(path);
- if (pfi.isFile() && pfi.isExecutable())
- return FilePath::fromString(path);
+ if (exec.isAbsolutePath()) {
+ for (const FilePath &path : execs) {
+ if (path.isExecutableFile() && (!func || func(path)))
+ if (resultCallback(path) == IterationPolicy::Stop)
+ return;
}
- return FilePath::fromString(exec);
+ return;
}
- QSet<FilePath> alreadyChecked;
+ QSet<FilePath> alreadyCheckedDirectories;
for (const FilePath &dir : dirs) {
- FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- return tmp;
+ if (searchInDirectory(resultCallback, execs, dir, alreadyCheckedDirectories, func)
+ == IterationPolicy::Stop)
+ return;
}
if (usePath) {
- if (executable.contains('/'))
- return FilePath();
+ QTC_ASSERT(!executable.contains('/'), return);
for (const FilePath &p : env.path()) {
- FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- return tmp;
+ if (searchInDirectory(resultCallback, execs, p, alreadyCheckedDirectories, func)
+ == IterationPolicy::Stop)
+ return;
}
}
- return FilePath();
+ return;
}
FilePath Environment::searchInDirectories(const QString &executable,
const FilePaths &dirs,
- const PathFilter &func) const
-{
- return searchInDirectoriesHelper(*this, executable, dirs, func, false);
+ const FilePathPredicate &func) const
+{
+ FilePath result;
+ searchInDirectoriesHelper(
+ [&result](const FilePath &path) {
+ result = path;
+ return IterationPolicy::Stop;
+ },
+ *this,
+ executable,
+ dirs,
+ func,
+ false);
+
+ return result;
}
FilePath Environment::searchInPath(const QString &executable,
const FilePaths &additionalDirs,
- const PathFilter &func) const
-{
- return searchInDirectoriesHelper(*this, executable, additionalDirs, func, true);
+ const FilePathPredicate &func) const
+{
+ FilePath result;
+ searchInDirectoriesHelper(
+ [&result](const FilePath &path) {
+ result = path;
+ return IterationPolicy::Stop;
+ },
+ *this,
+ executable,
+ additionalDirs,
+ func,
+ true);
+
+ return result;
}
FilePaths Environment::findAllInPath(const QString &executable,
- const FilePaths &additionalDirs,
- const Environment::PathFilter &func) const
+ const FilePaths &additionalDirs,
+ const FilePathPredicate &func) const
{
- if (executable.isEmpty())
- return {};
-
- const QString exec = QDir::cleanPath(expandVariables(executable));
- const QFileInfo fi(exec);
-
- const QStringList execs = appendExeExtensions(*this, exec);
-
- if (fi.isAbsolute()) {
- for (const QString &path : execs) {
- QFileInfo pfi = QFileInfo(path);
- if (pfi.isFile() && pfi.isExecutable())
- return {FilePath::fromString(path)};
- }
- return {FilePath::fromString(exec)};
- }
-
QSet<FilePath> result;
- QSet<FilePath> alreadyChecked;
- for (const FilePath &dir : additionalDirs) {
- FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- result << tmp;
- }
+ searchInDirectoriesHelper(
+ [&result](const FilePath &path) {
+ result.insert(path);
+ return IterationPolicy::Continue;
+ },
+ *this,
+ executable,
+ additionalDirs,
+ func,
+ true);
- if (!executable.contains('/')) {
- for (const FilePath &p : path()) {
- FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
- if (!tmp.isEmpty() && (!func || func(tmp)))
- result << tmp;
- }
- }
return result.values();
}
@@ -299,14 +375,16 @@ void Environment::setSystemEnvironment(const Environment &environment)
*/
QString Environment::expandVariables(const QString &input) const
{
+ const NameValueDictionary &dict = resolved();
+
QString result = input;
if (osType() == OsTypeWindows) {
for (int vStart = -1, i = 0; i < result.length(); ) {
if (result.at(i++) == '%') {
if (vStart > 0) {
- const auto it = m_dict.findKey(result.mid(vStart, i - vStart - 1));
- if (it != m_dict.m_values.constEnd()) {
+ const auto it = dict.findKey(result.mid(vStart, i - vStart - 1));
+ if (it != dict.m_values.constEnd()) {
result.replace(vStart - 1, i - vStart + 1, it->first);
i = vStart - 1 + it->first.length();
vStart = -1;
@@ -339,28 +417,30 @@ QString Environment::expandVariables(const QString &input) const
}
} else if (state == BRACEDVARIABLE) {
if (c == '}') {
- const_iterator it = constFind(result.mid(vStart, i - 1 - vStart));
- if (it != constEnd()) {
- result.replace(vStart - 2, i - vStart + 2, it->first);
- i = vStart - 2 + it->first.length();
+ const QString key = result.mid(vStart, i - 1 - vStart);
+ const Environment::FindResult res = find(key);
+ if (res) {
+ result.replace(vStart - 2, i - vStart + 2, res->value);
+ i = vStart - 2 + res->value.length();
}
state = BASE;
}
} else if (state == VARIABLE) {
if (!c.isLetterOrNumber() && c != '_') {
- const_iterator it = constFind(result.mid(vStart, i - vStart - 1));
- if (it != constEnd()) {
- result.replace(vStart - 1, i - vStart, it->first);
- i = vStart - 1 + it->first.length();
+ const QString key = result.mid(vStart, i - vStart - 1);
+ const Environment::FindResult res = find(key);
+ if (res) {
+ result.replace(vStart - 1, i - vStart, res->value);
+ i = vStart - 1 + res->value.length();
}
state = BASE;
}
}
}
if (state == VARIABLE) {
- const_iterator it = constFind(result.mid(vStart));
- if (it != constEnd())
- result.replace(vStart - 1, result.length() - vStart + 1, it->first);
+ const Environment::FindResult res = find(result.mid(vStart));
+ if (res)
+ result.replace(vStart - 1, result.length() - vStart + 1, res->value);
}
}
return result;
@@ -376,6 +456,12 @@ QStringList Environment::expandVariables(const QStringList &variables) const
return transform(variables, [this](const QString &i) { return expandVariables(i); });
}
+NameValueDictionary Environment::toDictionary() const
+{
+ const NameValueDictionary &dict = resolved();
+ return dict;
+}
+
void EnvironmentProvider::addProvider(EnvironmentProvider &&provider)
{
environmentProviders->append(std::move(provider));
@@ -394,63 +480,145 @@ std::optional<EnvironmentProvider> EnvironmentProvider::provider(const QByteArra
return std::nullopt;
}
-void EnvironmentChange::addSetValue(const QString &key, const QString &value)
+void Environment::addItem(const Item &item)
{
- m_changeItems.append(Item{std::in_place_index_t<SetValue>(), QPair<QString, QString>{key, value}});
+ m_dict.clear();
+ m_changeItems.append(item);
}
-void EnvironmentChange::addUnsetValue(const QString &key)
+void Environment::set(const QString &key, const QString &value, bool enabled)
{
- m_changeItems.append(Item{std::in_place_index_t<UnsetValue>(), key});
+ addItem(Item{std::in_place_index_t<SetValue>(),
+ std::tuple<QString, QString, bool>{key, value, enabled}});
}
-void EnvironmentChange::addPrependToPath(const FilePaths &values)
+void Environment::setFallback(const QString &key, const QString &value)
{
+ addItem(Item{std::in_place_index_t<SetFallbackValue>(),
+ std::tuple<QString, QString>{key, value}});
+}
+
+void Environment::unset(const QString &key)
+{
+ addItem(Item{std::in_place_index_t<UnsetValue>(), key});
+}
+
+void Environment::modify(const NameValueItems &items)
+{
+ addItem(Item{std::in_place_index_t<Modify>(), items});
+}
+
+void Environment::prependToPath(const FilePaths &values)
+{
+ m_dict.clear();
for (int i = values.size(); --i >= 0; ) {
const FilePath value = values.at(i);
- m_changeItems.append(Item{std::in_place_index_t<PrependToPath>(), value});
+ m_changeItems.append(Item{
+ std::in_place_index_t<PrependOrSet>(),
+ QString("PATH"),
+ value.nativePath(),
+ value.pathListSeparator()
+ });
}
}
-void EnvironmentChange::addAppendToPath(const FilePaths &values)
+void Environment::appendToPath(const FilePaths &values)
{
- for (const FilePath &value : values)
- m_changeItems.append(Item{std::in_place_index_t<AppendToPath>(), value});
+ m_dict.clear();
+ for (const FilePath &value : values) {
+ m_changeItems.append(Item{
+ std::in_place_index_t<AppendOrSet>(),
+ QString("PATH"),
+ value.nativePath(),
+ value.pathListSeparator()
+ });
+ }
}
-EnvironmentChange EnvironmentChange::fromDictionary(const NameValueDictionary &dict)
+const NameValueDictionary &Environment::resolved() const
{
- EnvironmentChange change;
- change.m_changeItems.append(Item{std::in_place_index_t<SetFixedDictionary>(), dict});
- return change;
-}
+ if (m_dict.size() != 0)
+ return m_dict;
-void EnvironmentChange::applyToEnvironment(Environment &env) const
-{
+ m_fullDict = false;
for (const Item &item : m_changeItems) {
switch (item.index()) {
case SetSystemEnvironment:
- env = Environment::systemEnvironment();
+ m_dict = Environment::systemEnvironment().toDictionary();
+ m_fullDict = true;
break;
case SetFixedDictionary:
- env = Environment(std::get<SetFixedDictionary>(item));
+ m_dict = std::get<SetFixedDictionary>(item);
+ m_fullDict = true;
break;
case SetValue: {
- const QPair<QString, QString> data = std::get<SetValue>(item);
- env.set(data.first, data.second);
+ auto [key, value, enabled] = std::get<SetValue>(item);
+ m_dict.set(key, value, enabled);
+ break;
+ }
+ case SetFallbackValue: {
+ auto [key, value] = std::get<SetFallbackValue>(item);
+ if (m_fullDict) {
+ if (m_dict.value(key).isEmpty())
+ m_dict.set(key, value, true);
+ } else {
+ QTC_ASSERT(false, qDebug() << "operating on partial dictionary");
+ m_dict.set(key, value, true);
+ }
break;
}
case UnsetValue:
- env.unset(std::get<UnsetValue>(item));
+ m_dict.unset(std::get<UnsetValue>(item));
+ break;
+ case PrependOrSet: {
+ auto [key, value, sep] = std::get<PrependOrSet>(item);
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toPrepend = value + sep;
+ if (!it.value().first.startsWith(toPrepend))
+ it.value().first.prepend(toPrepend);
+ }
+ break;
+ }
+ case AppendOrSet: {
+ auto [key, value, sep] = std::get<AppendOrSet>(item);
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toAppend = sep + value;
+ if (!it.value().first.endsWith(toAppend))
+ it.value().first.append(toAppend);
+ }
break;
- case PrependToPath:
- env.prependOrSetPath(std::get<PrependToPath>(item));
+ }
+ case Modify: {
+ NameValueItems items = std::get<Modify>(item);
+ m_dict.modify(items);
break;
- case AppendToPath:
- env.appendOrSetPath(std::get<AppendToPath>(item));
+ }
+ case SetupEnglishOutput:
+ m_dict.set("LC_MESSAGES", "en_US.utf8");
+ m_dict.set("LANGUAGE", "en_US:en");
break;
}
}
+
+ return m_dict;
+}
+
+Environment Environment::appliedToEnvironment(const Environment &base) const
+{
+ Environment res = base;
+ res.m_dict.clear();
+ res.m_changeItems.append(m_changeItems);
+ return res;
}
/*!
diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h
index 2671e972cf..7afa48ba0b 100644
--- a/src/libs/utils/environment.h
+++ b/src/libs/utils/environment.h
@@ -8,6 +8,7 @@
#include "environmentfwd.h"
#include "filepath.h"
#include "namevaluedictionary.h"
+#include "utiltypes.h"
#include <functional>
#include <optional>
@@ -21,28 +22,25 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT Environment final
{
public:
- Environment() : m_dict(HostOsInfo::hostOs()) {}
- explicit Environment(OsType osType) : m_dict(osType) {}
- explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs())
- : m_dict(env, osType) {}
- explicit Environment(const NameValuePairs &nameValues) : m_dict(nameValues) {}
- explicit Environment(const NameValueDictionary &dict) : m_dict(dict) {}
-
- QString value(const QString &key) const { return m_dict.value(key); }
- QString value_or(const QString &key, const QString &defaultValue) const
- {
- return m_dict.hasKey(key) ? m_dict.value(key) : defaultValue;
- }
- bool hasKey(const QString &key) const { return m_dict.hasKey(key); }
-
- void set(const QString &key, const QString &value, bool enabled = true) { m_dict.set(key, value, enabled); }
- void unset(const QString &key) { m_dict.unset(key); }
- void modify(const NameValueItems &items) { m_dict.modify(items); }
+ Environment();
+ explicit Environment(OsType osType);
+ explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs());
+ explicit Environment(const NameValuePairs &nameValues);
+ explicit Environment(const NameValueDictionary &dict);
+
+ QString value(const QString &key) const;
+ QString value_or(const QString &key, const QString &defaultValue) const;
+ bool hasKey(const QString &key) const;
+
+ void set(const QString &key, const QString &value, bool enabled = true);
+ void setFallback(const QString &key, const QString &value);
+ void unset(const QString &key);
+ void modify(const NameValueItems &items);
bool hasChanges() const;
- void clear() { return m_dict.clear(); }
- QStringList toStringList() const { return m_dict.toStringList(); }
+ OsType osType() const;
+ QStringList toStringList() const;
QProcessEnvironment toProcessEnvironment() const;
void appendOrSet(const QString &key, const QString &value, const QString &sep = QString());
@@ -54,18 +52,21 @@ public:
void prependOrSetLibrarySearchPath(const FilePath &value);
void prependOrSetLibrarySearchPaths(const FilePaths &values);
+ void prependToPath(const FilePaths &values);
+ void appendToPath(const FilePaths &values);
+
void setupEnglishOutput();
+ void setupSudoAskPass(const FilePath &askPass);
- using PathFilter = std::function<bool(const FilePath &)>;
FilePath searchInPath(const QString &executable,
const FilePaths &additionalDirs = FilePaths(),
- const PathFilter &func = PathFilter()) const;
+ const FilePathPredicate &func = {}) const;
FilePath searchInDirectories(const QString &executable,
const FilePaths &dirs,
- const PathFilter &func = {}) const;
+ const FilePathPredicate &func = {}) const;
FilePaths findAllInPath(const QString &executable,
const FilePaths &additionalDirs = {},
- const PathFilter &func = {}) const;
+ const FilePathPredicate &func = {}) const;
FilePaths path() const;
FilePaths pathListValue(const QString &varName) const;
@@ -75,80 +76,62 @@ public:
FilePath expandVariables(const FilePath &input) const;
QStringList expandVariables(const QStringList &input) const;
- OsType osType() const { return m_dict.osType(); }
- QString userName() const;
-
- using const_iterator = NameValueMap::const_iterator; // FIXME: avoid
- NameValueDictionary toDictionary() const { return m_dict; } // FIXME: avoid
+ NameValueDictionary toDictionary() const; // FIXME: avoid
NameValueItems diff(const Environment &other, bool checkAppendPrepend = false) const; // FIXME: avoid
- QString key(const_iterator it) const { return m_dict.key(it); } // FIXME: avoid
- QString value(const_iterator it) const { return m_dict.value(it); } // FIXME: avoid
- bool isEnabled(const_iterator it) const { return m_dict.isEnabled(it); } // FIXME: avoid
+ struct Entry { QString key; QString value; bool enabled; };
+ using FindResult = std::optional<Entry>;
+ FindResult find(const QString &name) const; // Note res->key may differ in case from name.
- void setCombineWithDeviceEnvironment(bool combine) { m_combineWithDeviceEnvironment = combine; }
- bool combineWithDeviceEnvironment() const { return m_combineWithDeviceEnvironment; }
+ void forEachEntry(const std::function<void (const QString &, const QString &, bool)> &callBack) const;
- const_iterator constBegin() const { return m_dict.constBegin(); } // FIXME: avoid
- const_iterator constEnd() const { return m_dict.constEnd(); } // FIXME: avoid
- const_iterator constFind(const QString &name) const { return m_dict.constFind(name); } // FIXME: avoid
-
- friend bool operator!=(const Environment &first, const Environment &second)
- {
- return first.m_dict != second.m_dict;
- }
-
- friend bool operator==(const Environment &first, const Environment &second)
- {
- return first.m_dict == second.m_dict;
- }
+ bool operator!=(const Environment &other) const;
+ bool operator==(const Environment &other) const;
static Environment systemEnvironment();
static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!!
static void setSystemEnvironment(const Environment &environment); // don't use at all!!!
-private:
- NameValueDictionary m_dict;
- bool m_combineWithDeviceEnvironment = true;
-};
-
-class QTCREATOR_UTILS_EXPORT EnvironmentChange final
-{
-public:
- EnvironmentChange() = default;
-
enum Type {
SetSystemEnvironment,
SetFixedDictionary,
SetValue,
+ SetFallbackValue,
UnsetValue,
- PrependToPath,
- AppendToPath,
+ PrependOrSet,
+ AppendOrSet,
+ Modify,
+ SetupEnglishOutput
};
using Item = std::variant<
- std::monostate, // SetSystemEnvironment dummy
- NameValueDictionary, // SetFixedDictionary
- QPair<QString, QString>, // SetValue
- QString, // UnsetValue
- FilePath, // PrependToPath
- FilePath // AppendToPath
+ std::monostate, // SetSystemEnvironment dummy
+ NameValueDictionary, // SetFixedDictionary
+ std::tuple<QString, QString, bool>, // SetValue (key, value, enabled)
+ std::tuple<QString, QString>, // SetFallbackValue (key, value)
+ QString, // UnsetValue (key)
+ std::tuple<QString, QString, QString>, // PrependOrSet (key, value, separator)
+ std::tuple<QString, QString, QString>, // AppendOrSet (key, value, separator)
+ NameValueItems, // Modify
+ std::monostate, // SetupEnglishOutput
+ FilePath // SetupSudoAskPass (file path of qtc-askpass or ssh-askpass)
>;
- static EnvironmentChange fromDictionary(const NameValueDictionary &dict);
+ void addItem(const Item &item);
- void applyToEnvironment(Environment &) const;
+ Environment appliedToEnvironment(const Environment &base) const;
- void addSetValue(const QString &key, const QString &value);
- void addUnsetValue(const QString &key);
- void addPrependToPath(const FilePaths &values);
- void addAppendToPath(const FilePaths &values);
+ const NameValueDictionary &resolved() const;
private:
- QList<Item> m_changeItems;
+ mutable QList<Item> m_changeItems;
+ mutable NameValueDictionary m_dict; // Latest resolved.
+ mutable bool m_fullDict = false;
};
+using EnviromentChange = Environment;
+
class QTCREATOR_UTILS_EXPORT EnvironmentProvider
{
public:
diff --git a/src/libs/utils/expected.h b/src/libs/utils/expected.h
index 943cfe5591..33231c1246 100644
--- a/src/libs/utils/expected.h
+++ b/src/libs/utils/expected.h
@@ -9,24 +9,11 @@
namespace Utils {
-template<class T, class E>
-using expected = tl::expected<T, E>;
+using namespace tl;
template<class T>
using expected_str = tl::expected<T, QString>;
-template<class E>
-using unexpected = tl::unexpected<E>;
-using unexpect_t = tl::unexpect_t;
-
-static constexpr unexpect_t unexpect{};
-
-template<class E>
-constexpr unexpected<std::decay_t<E>> make_unexpected(E &&e)
-{
- return tl::make_unexpected(e);
-}
-
} // namespace Utils
//! If 'expected' has an error the error will be printed and the 'action' will be executed.
diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp
index a0d1fd9b8a..03e33c67b6 100644
--- a/src/libs/utils/fancylineedit.cpp
+++ b/src/libs/utils/fancylineedit.cpp
@@ -172,7 +172,6 @@ FancyLineEdit::FancyLineEdit(QWidget *parent) :
CompletingLineEdit(parent),
d(new FancyLineEditPrivate(this))
{
- ensurePolished();
updateMargins();
connect(d->m_iconbutton[Left], &QAbstractButton::clicked, this, [this] {
diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp
index 230dc251cb..5681837582 100644
--- a/src/libs/utils/filepath.cpp
+++ b/src/libs/utils/filepath.cpp
@@ -6,6 +6,7 @@
#include "algorithm.h"
#include "devicefileaccess.h"
#include "environment.h"
+#include "filestreamermanager.h"
#include "fileutils.h"
#include "hostosinfo.h"
#include "qtcassert.h"
@@ -90,7 +91,7 @@ inline bool isWindowsDriveLetter(QChar ch);
executed on the associated OS.
\note The FilePath passed as executable to a CommandLine is typically
- not touched by user code. QtcProcess will use it to determine
+ not touched by user code. The Process will use it to determine
the remote system and apply the necessary conversions internally.
\li FilePath::toFSPathString()
@@ -162,7 +163,7 @@ FilePath FilePath::fromFileInfo(const QFileInfo &info)
}
/*!
- \returns a QFileInfo
+ Returns a QFileInfo.
*/
QFileInfo FilePath::toFileInfo() const
{
@@ -214,7 +215,7 @@ QString decodeHost(QString host)
}
/*!
- \returns a QString for passing through QString based APIs
+ Returns a QString for passing through QString based APIs.
\note This is obsolete API and should be replaced by extended use
of proper \c FilePath, or, in case this is not possible by \c toFSPathString().
@@ -233,13 +234,16 @@ QString FilePath::toString() const
if (!needsDevice())
return path();
+ if (pathView().isEmpty())
+ return scheme() + "://" + encodedHost();
+
if (isRelativePath())
return scheme() + "://" + encodedHost() + "/./" + pathView();
return scheme() + "://" + encodedHost() + pathView();
}
/*!
- \returns a QString for passing on to QString based APIs
+ Returns a QString for passing on to QString based APIs.
This uses a /__qtc_devices__/host/path setup.
@@ -255,6 +259,9 @@ QString FilePath::toFSPathString() const
if (scheme().isEmpty())
return path();
+ if (pathView().isEmpty())
+ return specialRootPath() + '/' + scheme() + '/' + encodedHost();
+
if (isRelativePath())
return specialRootPath() + '/' + scheme() + '/' + encodedHost() + "/./" + pathView();
return specialRootPath() + '/' + scheme() + '/' + encodedHost() + pathView();
@@ -289,7 +296,7 @@ QString FilePath::toUserOutput() const
}
/*!
- \returns a QString to pass to target system native commands, without the device prefix.
+ Returns a QString to pass to target system native commands, without the device prefix.
Converts the separators to the native format of the system
this path belongs to.
@@ -343,7 +350,7 @@ QString FilePath::fileNameWithPathComponents(int pathComponents) const
}
/*!
- \returns the base name of the file without the path.
+ Returns the base name of the file without the path.
The base name consists of all characters in the file up to
(but not including) the first '.' character.
@@ -355,7 +362,7 @@ QString FilePath::baseName() const
}
/*!
- \returns the complete base name of the file without the path.
+ Returns the complete base name of the file without the path.
The complete base name consists of all characters in the file up to
(but not including) the last '.' character. In case of ".ui.qml"
@@ -370,7 +377,7 @@ QString FilePath::completeBaseName() const
}
/*!
- \returns the suffix (extension) of the file.
+ Returns the suffix (extension) of the file.
The suffix consists of all characters in the file after
(but not including) the last '.'. In case of ".ui.qml" it will
@@ -393,7 +400,7 @@ QString FilePath::suffix() const
}
/*!
- \returns the complete suffix (extension) of the file.
+ Returns the complete suffix (extension) of the file.
The complete suffix consists of all characters in the file after
(but not including) the first '.'.
@@ -431,9 +438,10 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, QStrin
{
QTC_CHECK(!scheme.contains('/'));
- if (path.startsWith(u"/./"))
+ if (path.length() >= 3 && path[0] == '/' && path[1] == '.' && path[2] == '/')
path = path.mid(3);
+ m_hash = 0;
m_data = path.toString() + scheme.toString() + host.toString();
m_schemeLen = scheme.size();
m_hostLen = host.size();
@@ -441,7 +449,7 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, QStrin
}
/*!
- \returns a bool indicating whether a file or directory with this FilePath exists.
+ Returns a bool indicating whether a file or directory with this FilePath exists.
*/
bool FilePath::exists() const
{
@@ -449,7 +457,7 @@ bool FilePath::exists() const
}
/*!
- \returns a bool indicating whether this is a writable directory.
+ Returns a bool indicating whether this is a writable directory.
*/
bool FilePath::isWritableDir() const
{
@@ -457,13 +465,20 @@ bool FilePath::isWritableDir() const
}
/*!
- \returns a bool indicating whether this is a writable file.
+ Returns a bool indicating whether this is a writable file.
*/
bool FilePath::isWritableFile() const
{
return fileAccess()->isWritableFile(*this);
}
+/*!
+ \brief Re-uses or creates a directory in this location.
+
+ Returns true if the directory is writable afterwards.
+
+ \sa createDir()
+*/
bool FilePath::ensureWritableDir() const
{
return fileAccess()->ensureWritableDirectory(*this);
@@ -480,7 +495,7 @@ bool FilePath::isExecutableFile() const
}
/*!
- \returns a bool indicating on whether a process with this FilePath's
+ Returns a bool indicating on whether a process with this FilePath's
.nativePath() is likely to start.
This is equivalent to \c isExecutableFile() in general.
@@ -496,14 +511,14 @@ expected_str<FilePath> FilePath::tmpDir() const
if (needsDevice()) {
const Environment env = deviceEnvironment();
if (env.hasKey("TMPDIR"))
- return FilePath::fromUserInput(env.value("TMPDIR")).onDevice(*this);
+ return withNewPath(env.value("TMPDIR")).cleanPath();
if (env.hasKey("TEMP"))
- return FilePath::fromUserInput(env.value("TEMP")).onDevice(*this);
+ return withNewPath(env.value("TEMP")).cleanPath();
if (env.hasKey("TMP"))
- return FilePath::fromUserInput(env.value("TMP")).onDevice(*this);
+ return withNewPath(env.value("TMP")).cleanPath();
if (osType() != OsTypeWindows)
- return FilePath("/tmp").onDevice(*this);
+ return withNewPath("/tmp");
return make_unexpected(QString("Could not find temporary directory on device %1")
.arg(displayName()));
}
@@ -550,6 +565,19 @@ bool FilePath::isSymLink() const
return fileAccess()->isSymLink(*this);
}
+bool FilePath::hasHardLinks() const
+{
+ return fileAccess()->hasHardLinks(*this);
+}
+
+/*!
+ \brief Creates a directory in this location.
+
+ Returns true if the directory could be created, false if not,
+ even if it existed before.
+
+ \sa ensureWriteableDir()
+*/
bool FilePath::createDir() const
{
return fileAccess()->createDirectory(*this);
@@ -620,13 +648,6 @@ bool FilePath::ensureReachable(const FilePath &other) const
return false;
}
-void FilePath::asyncFileContents(const Continuation<const expected_str<QByteArray> &> &cont,
- qint64 maxSize,
- qint64 offset) const
-{
- return fileAccess()->asyncFileContents(*this, cont, maxSize, offset);
-}
-
expected_str<qint64> FilePath::writeFileContents(const QByteArray &data, qint64 offset) const
{
return fileAccess()->writeFileContents(*this, data, offset);
@@ -637,11 +658,21 @@ FilePathInfo FilePath::filePathInfo() const
return fileAccess()->filePathInfo(*this);
}
-void FilePath::asyncWriteFileContents(const Continuation<const expected_str<qint64> &> &cont,
- const QByteArray &data,
- qint64 offset) const
+FileStreamHandle FilePath::asyncCopy(const FilePath &target, QObject *context,
+ const CopyContinuation &cont) const
{
- return fileAccess()->asyncWriteFileContents(*this, cont, data, offset);
+ return FileStreamerManager::copy(*this, target, context, cont);
+}
+
+FileStreamHandle FilePath::asyncRead(QObject *context, const ReadContinuation &cont) const
+{
+ return FileStreamerManager::read(*this, context, cont);
+}
+
+FileStreamHandle FilePath::asyncWrite(const QByteArray &data, QObject *context,
+ const WriteContinuation &cont) const
+{
+ return FileStreamerManager::write(*this, data, context, cont);
}
bool FilePath::needsDevice() const
@@ -716,7 +747,7 @@ bool FilePath::isSameExecutable(const FilePath &other) const
}
/*!
- \returns an empty FilePath if this is not a symbolic linl
+ Returns an empty FilePath if this is not a symbolic link.
*/
FilePath FilePath::symLinkTarget() const
{
@@ -774,13 +805,153 @@ int FilePath::schemeAndHostLength(const QStringView path)
return pos + 1; // scheme://host/ plus something
}
+static QString normalizePathSegmentHelper(const QString &name)
+{
+ const int len = name.length();
+
+ if (len == 0 || name.contains("%{"))
+ return name;
+
+ int i = len - 1;
+ QVarLengthArray<char16_t> outVector(len);
+ int used = len;
+ char16_t *out = outVector.data();
+ const ushort *p = reinterpret_cast<const ushort *>(name.data());
+ const ushort *prefix = p;
+ int up = 0;
+
+ const int prefixLength = name.at(0) == u'/' ? 1 : 0;
+
+ p += prefixLength;
+ i -= prefixLength;
+
+ // replicate trailing slash (i > 0 checks for emptiness of input string p)
+ // except for remote paths because there can be /../ or /./ ending
+ if (i > 0 && p[i] == '/') {
+ out[--used] = '/';
+ --i;
+ }
+
+ while (i >= 0) {
+ if (p[i] == '/') {
+ --i;
+ continue;
+ }
+
+ // remove current directory
+ if (p[i] == '.' && (i == 0 || p[i - 1] == '/')) {
+ --i;
+ continue;
+ }
+
+ // detect up dir
+ if (i >= 1 && p[i] == '.' && p[i - 1] == '.' && (i < 2 || p[i - 2] == '/')) {
+ ++up;
+ i -= i >= 2 ? 3 : 2;
+ continue;
+ }
+
+ // prepend a slash before copying when not empty
+ if (!up && used != len && out[used] != '/')
+ out[--used] = '/';
+
+ // skip or copy
+ while (i >= 0) {
+ if (p[i] == '/') {
+ --i;
+ break;
+ }
+
+ // actual copy
+ if (!up)
+ out[--used] = p[i];
+ --i;
+ }
+
+ // decrement up after copying/skipping
+ if (up)
+ --up;
+ }
+
+ // Indicate failure when ".." are left over for an absolute path.
+ // if (ok)
+ // *ok = prefixLength == 0 || up == 0;
+
+ // add remaining '..'
+ while (up) {
+ if (used != len && out[used] != '/') // is not empty and there isn't already a '/'
+ out[--used] = '/';
+ out[--used] = '.';
+ out[--used] = '.';
+ --up;
+ }
+
+ bool isEmpty = used == len;
+
+ if (prefixLength) {
+ if (!isEmpty && out[used] == '/') {
+ // Even though there is a prefix the out string is a slash. This happens, if the input
+ // string only consists of a prefix followed by one or more slashes. Just skip the slash.
+ ++used;
+ }
+ for (int i = prefixLength - 1; i >= 0; --i)
+ out[--used] = prefix[i];
+ } else {
+ if (isEmpty) {
+ // After resolving the input path, the resulting string is empty (e.g. "foo/.."). Return
+ // a dot in that case.
+ out[--used] = '.';
+ } else if (out[used] == '/') {
+ // After parsing the input string, out only contains a slash. That happens whenever all
+ // parts are resolved and there is a trailing slash ("./" or "foo/../" for example).
+ // Prepend a dot to have the correct return value.
+ out[--used] = '.';
+ }
+ }
+
+ // If path was not modified return the original value
+ if (used == 0)
+ return name;
+ return QString::fromUtf16(out + used, len - used);
+}
+
+QString doCleanPath(const QString &input_)
+{
+ QString input = input_;
+ if (input.contains('\\'))
+ input.replace('\\', '/');
+
+ if (input.startsWith("//?/")) {
+ input = input.mid(4);
+ if (input.startsWith("UNC/"))
+ input = '/' + input.mid(3); // trick it into reporting two slashs at start
+ }
+
+ int prefixLen = 0;
+ const int shLen = FilePath::schemeAndHostLength(input);
+ if (shLen > 0) {
+ prefixLen = shLen + FilePath::rootLength(input.mid(shLen));
+ } else {
+ prefixLen = FilePath::rootLength(input);
+ if (prefixLen > 0 && input.at(prefixLen - 1) == '/')
+ --prefixLen;
+ }
+
+ QString path = normalizePathSegmentHelper(input.mid(prefixLen));
+
+ // Strip away last slash except for root directories
+ if (path.size() > 1 && path.endsWith(u'/'))
+ path.chop(1);
+
+ return input.left(prefixLen) + path;
+}
/*! Find the parent directory of a given directory.
Returns an empty FilePath if the current directory is already
a root level directory.
- \returns \a FilePath with the last segment removed.
+ Returns \a FilePath with the last segment removed.
*/
FilePath FilePath::parentDir() const
{
@@ -1038,10 +1209,10 @@ FilePath FilePath::fromStringWithExtension(const QString &filepath, const QStrin
*/
FilePath FilePath::fromUserInput(const QString &filePath)
{
- QString clean = doCleanPath(filePath);
- if (clean.startsWith(QLatin1String("~/")))
- return FileUtils::homePath().pathAppended(clean.mid(2));
- return FilePath::fromString(clean);
+ const QString expandedPath = filePath.startsWith("~/")
+ ? (QDir::homePath() + "/" + filePath.mid(2))
+ : filePath;
+ return FilePath::fromString(doCleanPath(expandedPath));
}
/*!
@@ -1075,7 +1246,7 @@ QVariant FilePath::toVariant() const
}
/*!
- \returns whether FilePath is a child of \a s
+ Returns whether FilePath is a child of \a s.
*/
bool FilePath::isChildOf(const FilePath &s) const
{
@@ -1097,7 +1268,7 @@ bool FilePath::isChildOf(const FilePath &s) const
}
/*!
- \returns whether \c path() starts with \a s.
+ Returns whether \c path() starts with \a s.
*/
bool FilePath::startsWith(const QString &s) const
{
@@ -1105,7 +1276,7 @@ bool FilePath::startsWith(const QString &s) const
}
/*!
- \returns whether \c path() ends with \a s.
+ Returns whether \c path() ends with \a s.
*/
bool FilePath::endsWith(const QString &s) const
{
@@ -1113,7 +1284,7 @@ bool FilePath::endsWith(const QString &s) const
}
/*!
- \returns whether \c path() contains \a s.
+ Returns whether \c path() contains \a s.
*/
bool FilePath::contains(const QString &s) const
{
@@ -1124,7 +1295,8 @@ bool FilePath::contains(const QString &s) const
\brief Checks whether the FilePath starts with a drive letter.
Defaults to \c false if it is a non-Windows host or represents a path on device
- \returns whether FilePath starts with a drive letter
+
+ Returns whether FilePath starts with a drive letter
*/
bool FilePath::startsWithDriveLetter() const
{
@@ -1138,7 +1310,8 @@ bool FilePath::startsWithDriveLetter() const
Returns a empty FilePath if this is not a child of \p parent.
That is, this never returns a path starting with "../"
\param parent The Parent to calculate the relative path to.
- \returns The relative path of this to \p parent if this is a child of \p parent.
+
+ Returns The relative path of this to \p parent if this is a child of \p parent.
*/
FilePath FilePath::relativeChildPath(const FilePath &parent) const
{
@@ -1153,13 +1326,13 @@ FilePath FilePath::relativeChildPath(const FilePath &parent) const
}
/*!
- \returns the relativePath of FilePath from a given \a anchor.
+ Returns the relative path of FilePath from a given \a anchor.
Both, FilePath and anchor may be files or directories.
Example usage:
\code
FilePath filePath("/foo/b/ar/file.txt");
- FilePath relativePath = filePath.relativePath("/foo/c");
+ FilePath relativePath = filePath.relativePathFrom("/foo/c");
qDebug() << relativePath
\endcode
@@ -1198,8 +1371,10 @@ FilePath FilePath::relativePathFrom(const FilePath &anchor) const
}
/*!
- \returns the relativePath of \a absolutePath to given \a absoluteAnchorPath.
- Both paths must be an absolute path to a directory. Example usage:
+ Returns the relativePath of \a absolutePath to given \a absoluteAnchorPath.
+ Both paths must be an absolute path to a directory.
+
+ Example usage:
\code
qDebug() << FilePath::calcRelativePath("/foo/b/ar", "/foo/c");
@@ -1247,33 +1422,31 @@ QString FilePath::calcRelativePath(const QString &absolutePath, const QString &a
}
/*!
- \brief Returns a path corresponding to the current object on the
+ Returns a path corresponding to \a newPath object on the
+ same device as the current object.
- same device as \a deviceTemplate. The FilePath needs to be local.
+ This may involve device-specific translations like converting
+ windows style paths to unix style paths with suitable file
+ system case or handling of drive letters: C:/dev/src -> /c/dev/src
Example usage:
\code
localDir = FilePath("/tmp/workingdir");
executable = FilePath::fromUrl("docker://123/bin/ls")
- realDir = localDir.onDevice(executable)
+ realDir = executable.withNewMappedPath(localDir)
assert(realDir == FilePath::fromUrl("docker://123/tmp/workingdir"))
\endcode
-
- \param deviceTemplate A path from which the host and scheme is taken.
-
- \returns A path on the same device as \a deviceTemplate.
*/
-FilePath FilePath::onDevice(const FilePath &deviceTemplate) const
+FilePath FilePath::withNewMappedPath(const FilePath &newPath) const
{
- isSameDevice(deviceTemplate);
- const bool sameDevice = scheme() == deviceTemplate.scheme() && host() == deviceTemplate.host();
+ const bool sameDevice = newPath.scheme() == scheme() && newPath.host() == host();
if (sameDevice)
- return *this;
+ return newPath;
// TODO: converting paths between different non local devices is still unsupported
- QTC_CHECK(!needsDevice() || !deviceTemplate.needsDevice());
- return fromParts(deviceTemplate.scheme(),
- deviceTemplate.host(),
- deviceTemplate.fileAccess()->mapToDevicePath(path()));
+ QTC_CHECK(!newPath.needsDevice() || !needsDevice());
+ FilePath res;
+ res.setParts(scheme(), host(), fileAccess()->mapToDevicePath(newPath.path()));
+ return res;
}
/*!
@@ -1305,7 +1478,7 @@ FilePath FilePath::withNewPath(const QString &newPath) const
assert(fullPath == FilePath::fromUrl("docker://123/usr/bin/make"))
\endcode
*/
-FilePath FilePath::searchInDirectories(const FilePaths &dirs, const PathFilter &filter) const
+FilePath FilePath::searchInDirectories(const FilePaths &dirs, const FilePathPredicate &filter) const
{
if (isAbsolutePath())
return *this;
@@ -1314,14 +1487,14 @@ FilePath FilePath::searchInDirectories(const FilePaths &dirs, const PathFilter &
FilePath FilePath::searchInPath(const FilePaths &additionalDirs,
PathAmending amending,
- const PathFilter &filter) const
+ const FilePathPredicate &filter) const
{
if (isAbsolutePath())
return *this;
FilePaths directories = deviceEnvironment().path();
if (needsDevice()) {
- directories = Utils::transform(directories, [this](const FilePath &path) {
- return path.onDevice(*this);
+ directories = Utils::transform(directories, [this](const FilePath &filePath) {
+ return withNewPath(filePath.path());
});
}
if (!additionalDirs.isEmpty()) {
@@ -1359,11 +1532,17 @@ void FilePath::removeDuplicates(FilePaths &files)
void FilePath::sort(FilePaths &files)
{
- // FIXME: Improve.
- // FIXME: This drops the osType information, which is not correct.
- QStringList list = transform<QStringList>(files, &FilePath::toString);
- list.sort();
- files = FileUtils::toFilePathList(list);
+ std::sort(files.begin(), files.end(), [](const FilePath &a, const FilePath &b) {
+ const int scheme = a.scheme().compare(b.scheme());
+ if (scheme != 0)
+ return scheme < 0;
+
+ const int host = a.host().compare(b.host());
+ if (host != 0)
+ return host < 0;
+
+ return a.pathView() < b.pathView();
+ });
}
void join(QString &left, const QString &right)
@@ -1416,7 +1595,11 @@ bool FilePath::setPermissions(QFile::Permissions permissions) const
OsType FilePath::osType() const
{
- return fileAccess()->osType(*this);
+ if (!needsDevice())
+ return HostOsInfo::hostOs();
+
+ QTC_ASSERT(s_deviceHooks.osType, return HostOsInfo::hostOs());
+ return s_deviceHooks.osType(*this);
}
bool FilePath::removeFile() const
@@ -1429,7 +1612,7 @@ bool FilePath::removeFile() const
\note The \a error parameter is optional.
- \returns A bool indicating whether the operation succeeded.
+ Returns a Bool indicating whether the operation succeeded.
*/
bool FilePath::removeRecursively(QString *error) const
{
@@ -1468,26 +1651,6 @@ expected_str<void> FilePath::copyFile(const FilePath &target) const
return fileAccess()->copyFile(*this, target);
}
-void FilePath::asyncCopyFile(const Continuation<const expected_str<void> &> &cont,
- const FilePath &target) const
-{
- if (host() != target.host()) {
- asyncFileContents([cont, target](const expected_str<QByteArray> &contents) {
- if (contents)
- target.asyncWriteFileContents(
- [cont](const expected_str<qint64> &result) {
- if (result)
- cont({});
- else
- cont(make_unexpected(result.error()));
- },
- *contents);
- });
- return;
- }
- return fileAccess()->asyncCopyFile(*this, cont, target);
-}
-
bool FilePath::renameFile(const FilePath &target) const
{
return fileAccess()->renameFile(*this, target);
@@ -1507,7 +1670,7 @@ qint64 FilePath::bytesAvailable() const
\brief Checks if this is newer than \p timeStamp
\param timeStamp The time stamp to compare with
- \returns true if this is newer than \p timeStamp.
+ Returns true if this is newer than \p timeStamp.
If this is a directory, the function will recursively check all files and return
true if one of them is newer than \a timeStamp. If this is a single file, true will
be returned if the file is newer than \a timeStamp.
@@ -1532,7 +1695,6 @@ bool FilePath::isNewerThan(const QDateTime &timeStamp) const
/*!
\brief Returns the caseSensitivity of the path.
- \returns The caseSensitivity of the path.
This is currently only based on the Host OS.
For device paths, \c Qt::CaseSensitive is always returned.
*/
@@ -1551,7 +1713,7 @@ Qt::CaseSensitivity FilePath::caseSensitivity() const
/*!
\brief Returns the separator of path components for this path.
- \returns The path separator of the path.
+ Returns the path separator of the path.
*/
QChar FilePath::pathComponentSeparator() const
{
@@ -1561,7 +1723,7 @@ QChar FilePath::pathComponentSeparator() const
/*!
\brief Returns the path list separator for the device this path belongs to.
- \returns The path list separator of the device for this path
+ Returns the path list separator of the device for this path.
*/
QChar FilePath::pathListSeparator() const
{
@@ -1577,7 +1739,7 @@ QChar FilePath::pathListSeparator() const
\note Maximum recursion depth == 16.
- \returns the symlink target file path.
+ Returns the symlink target file path.
*/
FilePath FilePath::resolveSymlinks() const
{
@@ -1598,7 +1760,7 @@ FilePath FilePath::resolveSymlinks() const
* Unlike QFileInfo::canonicalFilePath(), this function will not return an empty
* string if path doesn't exist.
*
-* \returns the canonical path.
+* Returns the canonical path.
*/
FilePath FilePath::canonicalPath() const
{
@@ -1655,7 +1817,7 @@ void FilePath::clear()
/*!
\brief Checks if the path() is empty.
- \returns true if the path() is empty.
+ Returns true if the path() is empty.
The Host and Scheme of the part are ignored.
*/
bool FilePath::isEmpty() const
@@ -1669,7 +1831,7 @@ bool FilePath::isEmpty() const
Like QDir::toNativeSeparators(), but use prefix '~' instead of $HOME on unix systems when an
absolute path is given.
- \returns the possibly shortened path with native separators.
+ Returns the possibly shortened path with native separators.
*/
QString FilePath::shortNativePath() const
{
@@ -1686,7 +1848,7 @@ QString FilePath::shortNativePath() const
/*!
\brief Checks whether the path is relative
- \returns true if the path is relative.
+ Returns true if the path is relative.
*/
bool FilePath::isRelativePath() const
{
@@ -1704,7 +1866,8 @@ bool FilePath::isRelativePath() const
\brief Appends the tail to this, if the tail is a relative path.
\param tail The tail to append.
- \returns Returns tail if tail is absolute, otherwise this + tail.
+
+ Returns tail if tail is absolute, otherwise this + tail.
*/
FilePath FilePath::resolvePath(const FilePath &tail) const
{
@@ -1719,7 +1882,8 @@ FilePath FilePath::resolvePath(const FilePath &tail) const
\brief Appends the tail to this, if the tail is a relative path.
\param tail The tail to append.
- \returns Returns tail if tail is absolute, otherwise this + tail.
+
+ Returns tail if tail is absolute, otherwise this + tail.
*/
FilePath FilePath::resolvePath(const QString &tail) const
{
@@ -1782,150 +1946,7 @@ QTextStream &operator<<(QTextStream &s, const FilePath &fn)
return s << fn.toString();
}
-static QString normalizePathSegmentHelper(const QString &name)
-{
- const int len = name.length();
-
- if (len == 0 || name.contains("%{"))
- return name;
-
- int i = len - 1;
- QVarLengthArray<char16_t> outVector(len);
- int used = len;
- char16_t *out = outVector.data();
- const ushort *p = reinterpret_cast<const ushort *>(name.data());
- const ushort *prefix = p;
- int up = 0;
-
- const int prefixLength = name.at(0) == u'/' ? 1 : 0;
-
- p += prefixLength;
- i -= prefixLength;
-
- // replicate trailing slash (i > 0 checks for emptiness of input string p)
- // except for remote paths because there can be /../ or /./ ending
- if (i > 0 && p[i] == '/') {
- out[--used] = '/';
- --i;
- }
-
- while (i >= 0) {
- if (p[i] == '/') {
- --i;
- continue;
- }
-
- // remove current directory
- if (p[i] == '.' && (i == 0 || p[i-1] == '/')) {
- --i;
- continue;
- }
-
- // detect up dir
- if (i >= 1 && p[i] == '.' && p[i-1] == '.' && (i < 2 || p[i - 2] == '/')) {
- ++up;
- i -= i >= 2 ? 3 : 2;
- continue;
- }
-
- // prepend a slash before copying when not empty
- if (!up && used != len && out[used] != '/')
- out[--used] = '/';
-
- // skip or copy
- while (i >= 0) {
- if (p[i] == '/') {
- --i;
- break;
- }
-
- // actual copy
- if (!up)
- out[--used] = p[i];
- --i;
- }
-
- // decrement up after copying/skipping
- if (up)
- --up;
- }
-
- // Indicate failure when ".." are left over for an absolute path.
-// if (ok)
-// *ok = prefixLength == 0 || up == 0;
-
- // add remaining '..'
- while (up) {
- if (used != len && out[used] != '/') // is not empty and there isn't already a '/'
- out[--used] = '/';
- out[--used] = '.';
- out[--used] = '.';
- --up;
- }
-
- bool isEmpty = used == len;
-
- if (prefixLength) {
- if (!isEmpty && out[used] == '/') {
- // Even though there is a prefix the out string is a slash. This happens, if the input
- // string only consists of a prefix followed by one or more slashes. Just skip the slash.
- ++used;
- }
- for (int i = prefixLength - 1; i >= 0; --i)
- out[--used] = prefix[i];
- } else {
- if (isEmpty) {
- // After resolving the input path, the resulting string is empty (e.g. "foo/.."). Return
- // a dot in that case.
- out[--used] = '.';
- } else if (out[used] == '/') {
- // After parsing the input string, out only contains a slash. That happens whenever all
- // parts are resolved and there is a trailing slash ("./" or "foo/../" for example).
- // Prepend a dot to have the correct return value.
- out[--used] = '.';
- }
- }
-
- // If path was not modified return the original value
- if (used == 0)
- return name;
- return QString::fromUtf16(out + used, len - used);
-}
-
-QString doCleanPath(const QString &input_)
-{
- QString input = input_;
- if (input.contains('\\'))
- input.replace('\\', '/');
-
- if (input.startsWith("//?/")) {
- input = input.mid(4);
- if (input.startsWith("UNC/"))
- input = '/' + input.mid(3); // trick it into reporting two slashs at start
- }
-
- int prefixLen = 0;
- const int shLen = FilePath::schemeAndHostLength(input);
- if (shLen > 0) {
- prefixLen = shLen + FilePath::rootLength(input.mid(shLen));
- } else {
- prefixLen = FilePath::rootLength(input);
- if (prefixLen > 0 && input.at(prefixLen - 1) == '/')
- --prefixLen;
- }
-
- QString path = normalizePathSegmentHelper(input.mid(prefixLen));
-
- // Strip away last slash except for root directories
- if (path.size() > 1 && path.endsWith(u'/'))
- path.chop(1);
-
- return input.left(prefixLen) + path;
-}
-
-
// FileFilter
-
FileFilter::FileFilter(const QStringList &nameFilters,
const QDir::Filters fileFilters,
const QDirIterator::IteratorFlags flags)
@@ -2021,6 +2042,9 @@ DeviceFileHooks &DeviceFileHooks::instance()
QTCREATOR_UTILS_EXPORT bool operator==(const FilePath &first, const FilePath &second)
{
+ if (first.m_hash != 0 && second.m_hash != 0 && first.m_hash != second.m_hash)
+ return false;
+
return first.pathView().compare(second.pathView(), first.caseSensitivity()) == 0
&& first.host() == second.host()
&& first.scheme() == second.scheme();
@@ -2058,9 +2082,16 @@ QTCREATOR_UTILS_EXPORT bool operator>=(const FilePath &first, const FilePath &se
QTCREATOR_UTILS_EXPORT size_t qHash(const FilePath &filePath, uint seed)
{
- if (filePath.caseSensitivity() == Qt::CaseInsensitive)
- return qHash(filePath.path().toCaseFolded(), seed);
- return qHash(filePath.path(), seed);
+ Q_UNUSED(seed);
+
+ if (filePath.m_hash == 0) {
+ if (filePath.caseSensitivity() == Qt::CaseSensitive)
+ filePath.m_hash = qHash(QStringView(filePath.m_data), 0);
+ else
+ filePath.m_hash = qHash(filePath.m_data.toCaseFolded(), 0);
+ }
+
+ return filePath.m_hash;
}
QTCREATOR_UTILS_EXPORT size_t qHash(const FilePath &filePath)
diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h
index 4c61786eba..142542b734 100644
--- a/src/libs/utils/filepath.h
+++ b/src/libs/utils/filepath.h
@@ -8,6 +8,7 @@
#include "expected.h"
#include "filepathinfo.h"
#include "osspecificaspects.h"
+#include "utiltypes.h"
#include <QDir>
#include <QDirIterator>
@@ -31,9 +32,12 @@ namespace Utils {
class DeviceFileAccess;
class Environment;
-class EnvironmentChange;
+enum class FileStreamHandle;
template <class ...Args> using Continuation = std::function<void(Args...)>;
+using CopyContinuation = Continuation<const expected_str<void> &>;
+using ReadContinuation = Continuation<const expected_str<QByteArray> &>;
+using WriteContinuation = Continuation<const expected_str<qint64> &>;
class QTCREATOR_UTILS_EXPORT FileFilter
{
@@ -51,8 +55,6 @@ public:
using FilePaths = QList<class FilePath>;
-enum class IterationPolicy { Stop, Continue };
-
class QTCREATOR_UTILS_EXPORT FilePath
{
public:
@@ -116,6 +118,7 @@ public:
bool isFile() const;
bool isDir() const;
bool isSymLink() const;
+ bool hasHardLinks() const;
bool isRootPath() const;
bool isNewerThan(const QDateTime &timeStamp) const;
QDateTime lastModified() const;
@@ -159,10 +162,10 @@ public:
[[nodiscard]] FilePath relativeChildPath(const FilePath &parent) const;
[[nodiscard]] FilePath relativePathFrom(const FilePath &anchor) const;
[[nodiscard]] FilePath searchInDirectories(const FilePaths &dirs,
- const PathFilter &filter = {}) const;
+ const FilePathPredicate &filter = {}) const;
[[nodiscard]] Environment deviceEnvironment() const;
- [[nodiscard]] FilePath onDevice(const FilePath &deviceTemplate) const;
[[nodiscard]] FilePath withNewPath(const QString &newPath) const;
+ [[nodiscard]] FilePath withNewMappedPath(const FilePath &newPath) const;
using IterateDirCallback
= std::variant<
@@ -182,7 +185,7 @@ public:
enum PathAmending { AppendToPath, PrependToPath };
[[nodiscard]] FilePath searchInPath(const FilePaths &additionalDirs = {},
PathAmending = AppendToPath,
- const PathFilter &filter = {}) const;
+ const FilePathPredicate &filter = {}) const;
enum MatchScope { ExactMatchOnly, WithExeSuffix, WithBatSuffix,
WithExeOrBatSuffix, WithAnySuffix };
@@ -207,14 +210,11 @@ public:
static void sort(FilePaths &files);
// Asynchronous interface
- void asyncCopyFile(const Continuation<const expected_str<void> &> &cont,
- const FilePath &target) const;
- void asyncFileContents(const Continuation<const expected_str<QByteArray> &> &cont,
- qint64 maxSize = -1,
- qint64 offset = 0) const;
- void asyncWriteFileContents(const Continuation<const expected_str<qint64> &> &cont,
- const QByteArray &data,
- qint64 offset = 0) const;
+ FileStreamHandle asyncCopy(const FilePath &target, QObject *context,
+ const CopyContinuation &cont = {}) const;
+ FileStreamHandle asyncRead(QObject *context, const ReadContinuation &cont = {}) const;
+ FileStreamHandle asyncWrite(const QByteArray &data, QObject *context,
+ const WriteContinuation &cont = {}) const;
// Prefer not to use
// Using needsDevice() in "user" code is likely to result in code that
@@ -277,6 +277,7 @@ private:
unsigned int m_pathLen = 0;
unsigned short m_schemeLen = 0;
unsigned short m_hostLen = 0;
+ mutable size_t m_hash = 0;
};
class QTCREATOR_UTILS_EXPORT DeviceFileHooks
@@ -291,8 +292,12 @@ public:
std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice;
std::function<expected_str<FilePath>(const FilePath &)> localSource;
std::function<void(const FilePath &, const Environment &)> openTerminal;
+ std::function<OsType(const FilePath &)> osType;
};
+// For testing
+QTCREATOR_UTILS_EXPORT QString doCleanPath(const QString &input);
+
} // Utils
Q_DECLARE_METATYPE(Utils::FilePath)
diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp
index ccbcfbfb9f..fe65756276 100644
--- a/src/libs/utils/filesearch.cpp
+++ b/src/libs/utils/filesearch.cpp
@@ -7,8 +7,10 @@
#include "filepath.h"
#include "mapreduce.h"
#include "qtcassert.h"
+#include "searchresultitem.h"
#include "stringutils.h"
#include "utilstr.h"
+#include "utiltypes.h"
#include <QLoggingCategory>
#include <QMutex>
@@ -68,7 +70,7 @@ public:
FileSearch(const QString &searchTerm,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap);
- void operator()(QFutureInterface<FileSearchResultList> &futureInterface,
+ void operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const;
private:
@@ -90,7 +92,7 @@ public:
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap);
FileSearchRegExp(const FileSearchRegExp &other);
- void operator()(QFutureInterface<FileSearchResultList> &futureInterface,
+ void operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const;
private:
@@ -116,7 +118,7 @@ FileSearch::FileSearch(const QString &searchTerm,
termDataUpper = searchTermUpper.constData();
}
-void FileSearch::operator()(QFutureInterface<FileSearchResultList> &futureInterface,
+void FileSearch::operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const
{
if (futureInterface.isCanceled())
@@ -124,7 +126,7 @@ void FileSearch::operator()(QFutureInterface<FileSearchResultList> &futureInterf
qCDebug(searchLog) << "Searching in" << item.filePath;
futureInterface.setProgressRange(0, 1);
futureInterface.setProgressValue(0);
- FileSearchResultList results;
+ SearchResultItems results;
QString tempString;
if (!getFileContent(item.filePath, item.encoding, &tempString, fileToContentsMap)) {
qCDebug(searchLog) << "- failed to get content for" << item.filePath;
@@ -189,13 +191,13 @@ void FileSearch::operator()(QFutureInterface<FileSearchResultList> &futureInterf
}
}
if (equal) {
- const QString resultItemText = clippedText(chunk, MAX_LINE_SIZE);
- results << FileSearchResult(item.filePath,
- lineNr,
- resultItemText,
- regionPtr - chunkPtr,
- termMaxIndex + 1,
- QStringList());
+ SearchResultItem result;
+ result.setFilePath(item.filePath);
+ result.setMainRange(lineNr, regionPtr - chunkPtr, termMaxIndex + 1);
+ result.setDisplayText(clippedText(chunk, MAX_LINE_SIZE));
+ result.setUserData(QStringList());
+ result.setUseTextEditorFont(true);
+ results << result;
regionPtr += termMaxIndex; // another +1 done by for-loop
}
}
@@ -237,7 +239,7 @@ QRegularExpressionMatch FileSearchRegExp::doGuardedMatch(const QString &line, in
return expression.match(line, offset);
}
-void FileSearchRegExp::operator()(QFutureInterface<FileSearchResultList> &futureInterface,
+void FileSearchRegExp::operator()(QFutureInterface<SearchResultItems> &futureInterface,
const FileIterator::Item &item) const
{
if (!expression.isValid()) {
@@ -249,7 +251,7 @@ void FileSearchRegExp::operator()(QFutureInterface<FileSearchResultList> &future
qCDebug(searchLog) << "Searching in" << item.filePath;
futureInterface.setProgressRange(0, 1);
futureInterface.setProgressValue(0);
- FileSearchResultList results;
+ SearchResultItems results;
QString tempString;
if (!getFileContent(item.filePath, item.encoding, &tempString, fileToContentsMap)) {
qCDebug(searchLog) << "- failed to get content for" << item.filePath;
@@ -269,12 +271,13 @@ void FileSearchRegExp::operator()(QFutureInterface<FileSearchResultList> &future
int pos = 0;
while ((match = doGuardedMatch(line, pos)).hasMatch()) {
pos = match.capturedStart();
- results << FileSearchResult(item.filePath,
- lineNr,
- resultItemText,
- pos,
- match.capturedLength(),
- match.capturedTexts());
+ SearchResultItem result;
+ result.setFilePath(item.filePath);
+ result.setMainRange(lineNr, pos, match.capturedLength());
+ result.setDisplayText(resultItemText);
+ result.setUserData(match.capturedTexts());
+ result.setUseTextEditorFont(true);
+ results << result;
if (match.capturedLength() == 0)
break;
pos += match.capturedLength();
@@ -298,12 +301,12 @@ struct SearchState
SearchState(const QString &term, FileIterator *iterator) : searchTerm(term), files(iterator) {}
QString searchTerm;
FileIterator *files = nullptr;
- FileSearchResultList cachedResults;
+ SearchResultItems cachedResults;
int numFilesSearched = 0;
int numMatches = 0;
};
-SearchState initFileSearch(QFutureInterface<FileSearchResultList> &futureInterface,
+SearchState initFileSearch(QFutureInterface<SearchResultItems> &futureInterface,
const QString &searchTerm, FileIterator *files)
{
futureInterface.setProgressRange(0, files->maxProgress());
@@ -311,9 +314,9 @@ SearchState initFileSearch(QFutureInterface<FileSearchResultList> &futureInterfa
return SearchState(searchTerm, files);
}
-void collectSearchResults(QFutureInterface<FileSearchResultList> &futureInterface,
+void collectSearchResults(QFutureInterface<SearchResultItems> &futureInterface,
SearchState &state,
- const FileSearchResultList &results)
+ const SearchResultItems &results)
{
state.numMatches += results.size();
state.cachedResults << results;
@@ -332,7 +335,7 @@ void collectSearchResults(QFutureInterface<FileSearchResultList> &futureInterfac
}
}
-void cleanUpFileSearch(QFutureInterface<FileSearchResultList> &futureInterface,
+void cleanUpFileSearch(QFutureInterface<SearchResultItems> &futureInterface,
SearchState &state)
{
if (!state.cachedResults.isEmpty()) {
@@ -355,13 +358,13 @@ void cleanUpFileSearch(QFutureInterface<FileSearchResultList> &futureInterface,
} // namespace
-QFuture<FileSearchResultList> Utils::findInFiles(const QString &searchTerm,
- FileIterator *files,
- QTextDocument::FindFlags flags,
- const QMap<FilePath, QString> &fileToContentsMap)
+QFuture<SearchResultItems> Utils::findInFiles(const QString &searchTerm,
+ FileIterator *files,
+ QTextDocument::FindFlags flags,
+ const QMap<FilePath, QString> &fileToContentsMap)
{
return mapReduce(files->begin(), files->end(),
- [searchTerm, files](QFutureInterface<FileSearchResultList> &futureInterface) {
+ [searchTerm, files](QFutureInterface<SearchResultItems> &futureInterface) {
return initFileSearch(futureInterface, searchTerm, files);
},
FileSearch(searchTerm, flags, fileToContentsMap),
@@ -369,14 +372,14 @@ QFuture<FileSearchResultList> Utils::findInFiles(const QString &searchTerm,
&cleanUpFileSearch);
}
-QFuture<FileSearchResultList> Utils::findInFilesRegExp(
+QFuture<SearchResultItems> Utils::findInFilesRegExp(
const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
const QMap<FilePath, QString> &fileToContentsMap)
{
return mapReduce(files->begin(), files->end(),
- [searchTerm, files](QFutureInterface<FileSearchResultList> &futureInterface) {
+ [searchTerm, files](QFutureInterface<SearchResultItems> &futureInterface) {
return initFileSearch(futureInterface, searchTerm, files);
},
FileSearchRegExp(searchTerm, flags, fileToContentsMap),
@@ -496,8 +499,7 @@ static bool isFileIncluded(const QList<QRegularExpression> &filterRegs,
return isIncluded && (exclusionRegs.isEmpty() || !matches(exclusionRegs, filePath));
}
-std::function<bool(const FilePath &)> filterFileFunction(const QStringList &filters,
- const QStringList &exclusionFilters)
+FilePathPredicate filterFileFunction(const QStringList &filters, const QStringList &exclusionFilters)
{
const QList<QRegularExpression> filterRegs = filtersToRegExps(filters);
const QList<QRegularExpression> exclusionRegs = filtersToRegExps(exclusionFilters);
@@ -598,19 +600,20 @@ FileIterator::const_iterator FileIterator::end() const
// #pragma mark -- FileListIterator
-QTextCodec *encodingAt(const QList<QTextCodec *> &encodings, int index)
+QList<FileIterator::Item> constructItems(const FilePaths &fileList,
+ const QList<QTextCodec *> &encodings)
{
- if (index >= 0 && index < encodings.size())
- return encodings.at(index);
- return QTextCodec::codecForLocale();
+ QList<FileIterator::Item> items;
+ items.reserve(fileList.size());
+ QTextCodec *defaultEncoding = QTextCodec::codecForLocale();
+ for (int i = 0; i < fileList.size(); ++i)
+ items.append(FileIterator::Item(fileList.at(i), encodings.value(i, defaultEncoding)));
+ return items;
}
-FileListIterator::FileListIterator(const FilePaths &fileList, const QList<QTextCodec *> encodings)
- : m_maxIndex(-1)
+FileListIterator::FileListIterator(const FilePaths &fileList, const QList<QTextCodec *> &encodings)
+ : m_items(constructItems(fileList, encodings))
{
- m_items.reserve(fileList.size());
- for (int i = 0; i < fileList.size(); ++i)
- m_items.append(Item(fileList.at(i), encodingAt(encodings, i)));
}
void FileListIterator::update(int requestedIndex)
diff --git a/src/libs/utils/filesearch.h b/src/libs/utils/filesearch.h
index 9d427f5946..7bb7af5d33 100644
--- a/src/libs/utils/filesearch.h
+++ b/src/libs/utils/filesearch.h
@@ -6,8 +6,8 @@
#include "utils_global.h"
#include "filepath.h"
+#include "searchresultitem.h"
-#include <QDir>
#include <QMap>
#include <QSet>
#include <QStack>
@@ -107,7 +107,8 @@ protected:
class QTCREATOR_UTILS_EXPORT FileListIterator : public FileIterator
{
public:
- explicit FileListIterator(const FilePaths &fileList, const QList<QTextCodec *> encodings);
+ explicit FileListIterator(const FilePaths &fileList = {},
+ const QList<QTextCodec *> &encodings = {});
int maxProgress() const override;
int currentProgress() const override;
@@ -118,8 +119,8 @@ protected:
const Item &itemAt(int index) const override;
private:
- QVector<Item> m_items;
- int m_maxIndex;
+ const QList<Item> m_items;
+ int m_maxIndex = -1;
};
class QTCREATOR_UTILS_EXPORT SubDirFileIterator : public FileIterator
@@ -151,54 +152,20 @@ private:
QList<Item *> m_items;
};
-class QTCREATOR_UTILS_EXPORT FileSearchResult
-{
-public:
- FileSearchResult() = default;
- FileSearchResult(const FilePath &fileName, int lineNumber, const QString &matchingLine,
- int matchStart, int matchLength,
- const QStringList &regexpCapturedTexts)
- : fileName(fileName),
- lineNumber(lineNumber),
- matchingLine(matchingLine),
- matchStart(matchStart),
- matchLength(matchLength),
- regexpCapturedTexts(regexpCapturedTexts)
- {}
-
- bool operator==(const FileSearchResult &o) const
- {
- return fileName == o.fileName && lineNumber == o.lineNumber
- && matchingLine == o.matchingLine && matchStart == o.matchStart
- && matchLength == o.matchLength && regexpCapturedTexts == o.regexpCapturedTexts;
- }
- bool operator!=(const FileSearchResult &o) const { return !(*this == o); }
-
- FilePath fileName;
- int lineNumber;
- QString matchingLine;
- int matchStart;
- int matchLength;
- QStringList regexpCapturedTexts;
-};
-
-using FileSearchResultList = QList<FileSearchResult>;
-
-QTCREATOR_UTILS_EXPORT QFuture<FileSearchResultList> findInFiles(
- const QString &searchTerm,
+QTCREATOR_UTILS_EXPORT QFuture<SearchResultItems> findInFiles(const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
- const QMap<FilePath, QString> &fileToContentsMap = QMap<FilePath, QString>());
+ const QMap<FilePath, QString> &fileToContentsMap = {});
-QTCREATOR_UTILS_EXPORT QFuture<FileSearchResultList> findInFilesRegExp(
+QTCREATOR_UTILS_EXPORT QFuture<SearchResultItems> findInFilesRegExp(
const QString &searchTerm,
FileIterator *files,
QTextDocument::FindFlags flags,
- const QMap<FilePath, QString> &fileToContentsMap = QMap<FilePath, QString>());
+ const QMap<FilePath, QString> &fileToContentsMap = {});
-QTCREATOR_UTILS_EXPORT QString expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts);
-QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText, const QString &replaceText);
+QTCREATOR_UTILS_EXPORT QString expandRegExpReplacement(const QString &replaceText,
+ const QStringList &capturedTexts);
+QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText,
+ const QString &replaceText);
} // namespace Utils
-
-Q_DECLARE_METATYPE(Utils::FileSearchResultList)
diff --git a/src/libs/utils/filestreamer.cpp b/src/libs/utils/filestreamer.cpp
new file mode 100644
index 0000000000..6d4a768336
--- /dev/null
+++ b/src/libs/utils/filestreamer.cpp
@@ -0,0 +1,512 @@
+// 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 "filestreamer.h"
+
+#include "async.h"
+#include "process.h"
+
+#include <solutions/tasking/barrier.h>
+
+#include <QFile>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QWaitCondition>
+
+namespace Utils {
+
+using namespace Tasking;
+
+// TODO: Adjust according to time spent on single buffer read so that it's not more than ~50 ms
+// in case of local read / write. Should it be adjusted dynamically / automatically?
+static const qint64 s_bufferSize = 0x1 << 20; // 1048576
+
+class FileStreamBase : public QObject
+{
+ Q_OBJECT
+
+public:
+ void setFilePath(const FilePath &filePath) { m_filePath = filePath; }
+ void start() {
+ QTC_ASSERT(!m_taskTree, return);
+
+ const TaskItem task = m_filePath.needsDevice() ? remoteTask() : localTask();
+ m_taskTree.reset(new TaskTree({task}));
+ const auto finalize = [this](bool success) {
+ m_taskTree.release()->deleteLater();
+ emit done(success);
+ };
+ connect(m_taskTree.get(), &TaskTree::done, this, [=] { finalize(true); });
+ connect(m_taskTree.get(), &TaskTree::errorOccurred, this, [=] { finalize(false); });
+ m_taskTree->start();
+ }
+
+signals:
+ void done(bool success);
+
+protected:
+ FilePath m_filePath;
+ std::unique_ptr<TaskTree> m_taskTree;
+
+private:
+ virtual TaskItem remoteTask() = 0;
+ virtual TaskItem localTask() = 0;
+};
+
+static void localRead(QPromise<QByteArray> &promise, const FilePath &filePath)
+{
+ if (promise.isCanceled())
+ return;
+
+ QFile file(filePath.path());
+ if (!file.exists()) {
+ promise.future().cancel();
+ return;
+ }
+
+ if (!file.open(QFile::ReadOnly)) {
+ promise.future().cancel();
+ return;
+ }
+
+ while (int chunkSize = qMin(s_bufferSize, file.bytesAvailable())) {
+ if (promise.isCanceled())
+ return;
+ promise.addResult(file.read(chunkSize));
+ }
+}
+
+class FileStreamReader : public FileStreamBase
+{
+ Q_OBJECT
+
+signals:
+ void readyRead(const QByteArray &newData);
+
+private:
+ TaskItem remoteTask() final {
+ const auto setup = [this](Process &process) {
+ const QStringList args = {"if=" + m_filePath.path()};
+ const FilePath dd = m_filePath.withNewPath("dd");
+ process.setCommand({dd, args, OsType::OsTypeLinux});
+ Process *processPtr = &process;
+ connect(processPtr, &Process::readyReadStandardOutput, this, [this, processPtr] {
+ emit readyRead(processPtr->readAllRawStandardOutput());
+ });
+ };
+ return ProcessTask(setup);
+ }
+ TaskItem localTask() final {
+ const auto setup = [this](Async<QByteArray> &async) {
+ async.setConcurrentCallData(localRead, m_filePath);
+ Async<QByteArray> *asyncPtr = &async;
+ connect(asyncPtr, &AsyncBase::resultReadyAt, this, [=](int index) {
+ emit readyRead(asyncPtr->resultAt(index));
+ });
+ };
+ return AsyncTask<QByteArray>(setup);
+ }
+};
+
+class WriteBuffer : public QObject
+{
+ Q_OBJECT
+
+public:
+ WriteBuffer(bool isConcurrent, QObject *parent)
+ : QObject(parent)
+ , m_isConcurrent(isConcurrent) {}
+ struct Data {
+ QByteArray m_writeData;
+ bool m_closeWriteChannel = false;
+ bool m_canceled = false;
+ bool hasNewData() const { return m_closeWriteChannel || !m_writeData.isEmpty(); }
+ };
+
+ void write(const QByteArray &newData) {
+ if (m_isConcurrent) {
+ QMutexLocker locker(&m_mutex);
+ QTC_ASSERT(!m_data.m_closeWriteChannel, return);
+ QTC_ASSERT(!m_data.m_canceled, return);
+ m_data.m_writeData += newData;
+ m_waitCondition.wakeOne();
+ return;
+ }
+ emit writeRequested(newData);
+ }
+ void closeWriteChannel() {
+ if (m_isConcurrent) {
+ QMutexLocker locker(&m_mutex);
+ QTC_ASSERT(!m_data.m_canceled, return);
+ m_data.m_closeWriteChannel = true;
+ m_waitCondition.wakeOne();
+ return;
+ }
+ emit closeWriteChannelRequested();
+ }
+ void cancel() {
+ if (m_isConcurrent) {
+ QMutexLocker locker(&m_mutex);
+ m_data.m_canceled = true;
+ m_waitCondition.wakeOne();
+ return;
+ }
+ emit closeWriteChannelRequested();
+ }
+ Data waitForData() {
+ QTC_ASSERT(m_isConcurrent, return {});
+ QMutexLocker locker(&m_mutex);
+ if (!m_data.hasNewData())
+ m_waitCondition.wait(&m_mutex);
+ return std::exchange(m_data, {});
+ }
+
+signals:
+ void writeRequested(const QByteArray &newData);
+ void closeWriteChannelRequested();
+
+private:
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+ Data m_data;
+ bool m_isConcurrent = false; // Depends on whether FileStreamWriter::m_writeData is empty or not
+};
+
+static void localWrite(QPromise<void> &promise, const FilePath &filePath,
+ const QByteArray &initialData, WriteBuffer *buffer)
+{
+ if (promise.isCanceled())
+ return;
+
+ QFile file(filePath.path());
+
+ if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
+ promise.future().cancel();
+ return;
+ }
+
+ if (!initialData.isEmpty()) {
+ const qint64 res = file.write(initialData);
+ if (res != initialData.size())
+ promise.future().cancel();
+ return;
+ }
+
+ while (true) {
+ if (promise.isCanceled()) {
+ promise.future().cancel();
+ return;
+ }
+ const WriteBuffer::Data data = buffer->waitForData();
+ if (data.m_canceled || promise.isCanceled()) {
+ promise.future().cancel();
+ return;
+ }
+ if (!data.m_writeData.isEmpty()) {
+ // TODO: Write in chunks of s_bufferSize and check for promise.isCanceled()
+ const qint64 res = file.write(data.m_writeData);
+ if (res != data.m_writeData.size()) {
+ promise.future().cancel();
+ return;
+ }
+ }
+ if (data.m_closeWriteChannel)
+ return;
+ }
+}
+
+class FileStreamWriter : public FileStreamBase
+{
+ Q_OBJECT
+
+public:
+ ~FileStreamWriter() { // TODO: should d'tor remove unfinished file write leftovers?
+ if (m_writeBuffer && isBuffered())
+ m_writeBuffer->cancel();
+ }
+
+ void setWriteData(const QByteArray &writeData) {
+ QTC_ASSERT(!m_taskTree, return);
+ m_writeData = writeData;
+ }
+ void write(const QByteArray &newData) {
+ QTC_ASSERT(m_taskTree, return);
+ QTC_ASSERT(m_writeData.isEmpty(), return);
+ QTC_ASSERT(m_writeBuffer, return);
+ m_writeBuffer->write(newData);
+ }
+ void closeWriteChannel() {
+ QTC_ASSERT(m_taskTree, return);
+ QTC_ASSERT(m_writeData.isEmpty(), return);
+ QTC_ASSERT(m_writeBuffer, return);
+ m_writeBuffer->closeWriteChannel();
+ }
+
+signals:
+ void started();
+
+private:
+ TaskItem remoteTask() final {
+ const auto setup = [this](Process &process) {
+ m_writeBuffer = new WriteBuffer(false, &process);
+ connect(m_writeBuffer, &WriteBuffer::writeRequested, &process, &Process::writeRaw);
+ connect(m_writeBuffer, &WriteBuffer::closeWriteChannelRequested,
+ &process, &Process::closeWriteChannel);
+ const QStringList args = {"of=" + m_filePath.path()};
+ const FilePath dd = m_filePath.withNewPath("dd");
+ process.setCommand({dd, args, OsType::OsTypeLinux});
+ if (isBuffered())
+ process.setProcessMode(ProcessMode::Writer);
+ else
+ process.setWriteData(m_writeData);
+ connect(&process, &Process::started, this, [this] { emit started(); });
+ };
+ const auto finalize = [this](const Process &) {
+ delete m_writeBuffer;
+ m_writeBuffer = nullptr;
+ };
+ return ProcessTask(setup, finalize, finalize);
+ }
+ TaskItem localTask() final {
+ const auto setup = [this](Async<void> &async) {
+ m_writeBuffer = new WriteBuffer(isBuffered(), &async);
+ async.setConcurrentCallData(localWrite, m_filePath, m_writeData, m_writeBuffer);
+ emit started();
+ };
+ const auto finalize = [this](const Async<void> &) {
+ delete m_writeBuffer;
+ m_writeBuffer = nullptr;
+ };
+ return AsyncTask<void>(setup, finalize, finalize);
+ }
+
+ bool isBuffered() const { return m_writeData.isEmpty(); }
+ QByteArray m_writeData;
+ WriteBuffer *m_writeBuffer = nullptr;
+};
+
+class FileStreamReaderAdapter : public TaskAdapter<FileStreamReader>
+{
+public:
+ FileStreamReaderAdapter() { connect(task(), &FileStreamBase::done, this, &TaskInterface::done); }
+ void start() override { task()->start(); }
+};
+
+class FileStreamWriterAdapter : public TaskAdapter<FileStreamWriter>
+{
+public:
+ FileStreamWriterAdapter() { connect(task(), &FileStreamBase::done, this, &TaskInterface::done); }
+ void start() override { task()->start(); }
+};
+
+} // namespace Utils
+
+TASKING_DECLARE_TASK(Reader, Utils::FileStreamReaderAdapter);
+TASKING_DECLARE_TASK(Writer, Utils::FileStreamWriterAdapter);
+
+namespace Utils {
+
+static Group sameRemoteDeviceTransferTask(const FilePath &source, const FilePath &destination)
+{
+ QTC_CHECK(source.needsDevice());
+ QTC_CHECK(destination.needsDevice());
+ QTC_CHECK(source.isSameDevice(destination));
+
+ const auto setup = [source, destination](Process &process) {
+ const QStringList args = {source.path(), destination.path()};
+ const FilePath cp = source.withNewPath("cp");
+ process.setCommand({cp, args, OsType::OsTypeLinux});
+ };
+ return {ProcessTask(setup)};
+}
+
+static Group interDeviceTransferTask(const FilePath &source, const FilePath &destination)
+{
+ struct TransferStorage { QPointer<FileStreamWriter> writer; };
+ SingleBarrier writerReadyBarrier;
+ TreeStorage<TransferStorage> storage;
+
+ const auto setupReader = [=](FileStreamReader &reader) {
+ reader.setFilePath(source);
+ QTC_CHECK(storage->writer != nullptr);
+ QObject::connect(&reader, &FileStreamReader::readyRead,
+ storage->writer, &FileStreamWriter::write);
+ };
+ const auto finalizeReader = [=](const FileStreamReader &) {
+ QTC_CHECK(storage->writer != nullptr);
+ storage->writer->closeWriteChannel();
+ };
+ const auto setupWriter = [=](FileStreamWriter &writer) {
+ writer.setFilePath(destination);
+ QObject::connect(&writer, &FileStreamWriter::started,
+ writerReadyBarrier->barrier(), &Barrier::advance);
+ QTC_CHECK(storage->writer == nullptr);
+ storage->writer = &writer;
+ };
+
+ const Group root {
+ Storage(writerReadyBarrier),
+ parallel,
+ Storage(storage),
+ Writer(setupWriter),
+ Group {
+ WaitForBarrierTask(writerReadyBarrier),
+ Reader(setupReader, finalizeReader, finalizeReader)
+ }
+ };
+
+ return root;
+}
+
+static Group transferTask(const FilePath &source, const FilePath &destination)
+{
+ if (source.needsDevice() && destination.needsDevice() && source.isSameDevice(destination))
+ return sameRemoteDeviceTransferTask(source, destination);
+ return interDeviceTransferTask(source, destination);
+}
+
+static void transfer(QPromise<void> &promise, const FilePath &source, const FilePath &destination)
+{
+ if (promise.isCanceled())
+ return;
+
+ std::unique_ptr<TaskTree> taskTree(new TaskTree(transferTask(source, destination)));
+
+ QEventLoop eventLoop;
+ bool finalized = false;
+ const auto finalize = [loop = &eventLoop, &taskTree, &finalized](int exitCode) {
+ if (finalized) // finalize only once
+ return;
+ finalized = true;
+ // Give the tree a chance to delete later all tasks that have finished and caused
+ // emission of tree's done or errorOccurred signal.
+ // TODO: maybe these signals should be sent queued already?
+ QMetaObject::invokeMethod(loop, [loop, &taskTree, exitCode] {
+ taskTree.reset();
+ loop->exit(exitCode);
+ }, Qt::QueuedConnection);
+ };
+ QTimer timer;
+ timer.setInterval(50);
+ QObject::connect(&timer, &QTimer::timeout, [&promise, finalize] {
+ if (promise.isCanceled())
+ finalize(2);
+ });
+ QObject::connect(taskTree.get(), &TaskTree::done, &eventLoop, [=] { finalize(0); });
+ QObject::connect(taskTree.get(), &TaskTree::errorOccurred, &eventLoop, [=] { finalize(1); });
+ taskTree->start();
+ timer.start();
+ if (eventLoop.exec())
+ promise.future().cancel();
+}
+
+class FileStreamerPrivate : public QObject
+{
+public:
+ StreamMode m_streamerMode = StreamMode::Transfer;
+ FilePath m_source;
+ FilePath m_destination;
+ QByteArray m_readBuffer;
+ QByteArray m_writeBuffer;
+ StreamResult m_streamResult = StreamResult::FinishedWithError;
+ std::unique_ptr<TaskTree> m_taskTree;
+
+ TaskItem task() {
+ if (m_streamerMode == StreamMode::Reader)
+ return readerTask();
+ if (m_streamerMode == StreamMode::Writer)
+ return writerTask();
+ return transferTask();
+ }
+
+private:
+ TaskItem readerTask() {
+ const auto setup = [this](FileStreamReader &reader) {
+ m_readBuffer.clear();
+ reader.setFilePath(m_source);
+ connect(&reader, &FileStreamReader::readyRead, this, [this](const QByteArray &data) {
+ m_readBuffer += data;
+ });
+ };
+ return Reader(setup);
+ }
+ TaskItem writerTask() {
+ const auto setup = [this](FileStreamWriter &writer) {
+ writer.setFilePath(m_destination);
+ writer.setWriteData(m_writeBuffer);
+ };
+ return Writer(setup);
+ }
+ TaskItem transferTask() {
+ const auto setup = [this](Async<void> &async) {
+ async.setConcurrentCallData(transfer, m_source, m_destination);
+ };
+ return AsyncTask<void>(setup);
+ }
+};
+
+FileStreamer::FileStreamer(QObject *parent)
+ : QObject(parent)
+ , d(new FileStreamerPrivate)
+{
+}
+
+FileStreamer::~FileStreamer()
+{
+ delete d;
+}
+
+void FileStreamer::setSource(const FilePath &source)
+{
+ d->m_source = source;
+}
+
+void FileStreamer::setDestination(const FilePath &destination)
+{
+ d->m_destination = destination;
+}
+
+void FileStreamer::setStreamMode(StreamMode mode)
+{
+ d->m_streamerMode = mode;
+}
+
+QByteArray FileStreamer::readData() const
+{
+ return d->m_readBuffer;
+}
+
+void FileStreamer::setWriteData(const QByteArray &writeData)
+{
+ d->m_writeBuffer = writeData;
+}
+
+StreamResult FileStreamer::result() const
+{
+ return d->m_streamResult;
+}
+
+void FileStreamer::start()
+{
+ // TODO: Preliminary check if local source exists?
+ QTC_ASSERT(!d->m_taskTree, return);
+ d->m_taskTree.reset(new TaskTree({d->task()}));
+ const auto finalize = [this](bool success) {
+ d->m_streamResult = success ? StreamResult::FinishedWithSuccess
+ : StreamResult::FinishedWithError;
+ d->m_taskTree.release()->deleteLater();
+ emit done();
+ };
+ connect(d->m_taskTree.get(), &TaskTree::done, this, [=] { finalize(true); });
+ connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, [=] { finalize(false); });
+ d->m_taskTree->start();
+}
+
+void FileStreamer::stop()
+{
+ d->m_taskTree.reset();
+}
+
+} // namespace Utils
+
+#include "filestreamer.moc"
diff --git a/src/libs/utils/filestreamer.h b/src/libs/utils/filestreamer.h
new file mode 100644
index 0000000000..a5b6ff4f25
--- /dev/null
+++ b/src/libs/utils/filestreamer.h
@@ -0,0 +1,63 @@
+// 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_global.h"
+
+#include "filepath.h"
+
+#include <solutions/tasking/tasktree.h>
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QByteArray;
+QT_END_NAMESPACE
+
+namespace Utils {
+
+enum class StreamMode { Reader, Writer, Transfer };
+
+enum class StreamResult { FinishedWithSuccess, FinishedWithError };
+
+class QTCREATOR_UTILS_EXPORT FileStreamer final : public QObject
+{
+ Q_OBJECT
+
+public:
+ FileStreamer(QObject *parent = nullptr);
+ ~FileStreamer();
+
+ void setSource(const FilePath &source);
+ void setDestination(const FilePath &destination);
+ void setStreamMode(StreamMode mode); // Transfer by default
+
+ // Only for Reader mode
+ QByteArray readData() const;
+ // Only for Writer mode
+ void setWriteData(const QByteArray &writeData);
+
+ StreamResult result() const;
+
+ void start();
+ void stop();
+
+signals:
+ void done();
+
+private:
+ class FileStreamerPrivate *d = nullptr;
+};
+
+class FileStreamerTaskAdapter : public Tasking::TaskAdapter<FileStreamer>
+{
+public:
+ FileStreamerTaskAdapter() { connect(task(), &FileStreamer::done, this,
+ [this] { emit done(task()->result() == StreamResult::FinishedWithSuccess); }); }
+ void start() override { task()->start(); }
+};
+
+} // namespace Utils
+
+TASKING_DECLARE_TASK(FileStreamerTask, Utils::FileStreamerTaskAdapter);
diff --git a/src/libs/utils/filestreamermanager.cpp b/src/libs/utils/filestreamermanager.cpp
new file mode 100644
index 0000000000..11d05faee5
--- /dev/null
+++ b/src/libs/utils/filestreamermanager.cpp
@@ -0,0 +1,201 @@
+// 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 "filestreamermanager.h"
+
+#include "filestreamer.h"
+#include "threadutils.h"
+#include "utilstr.h"
+
+#include <QMutex>
+#include <QMutexLocker>
+#include <QThread>
+#include <QWaitCondition>
+
+#include <unordered_map>
+
+namespace Utils {
+
+// TODO: destruct the instance before destructing ProjectExplorer::DeviceManager (?)
+
+static FileStreamHandle generateUniqueHandle()
+{
+ static std::atomic_int handleCounter = 1;
+ return FileStreamHandle(handleCounter.fetch_add(1));
+}
+
+static QMutex s_mutex = {};
+static QWaitCondition s_waitCondition = {};
+static std::unordered_map<FileStreamHandle, FileStreamer *> s_fileStreamers = {};
+
+static void addStreamer(FileStreamHandle handle, FileStreamer *streamer)
+{
+ QMutexLocker locker(&s_mutex);
+ const bool added = s_fileStreamers.try_emplace(handle, streamer).second;
+ QTC_CHECK(added);
+}
+
+static void removeStreamer(FileStreamHandle handle)
+{
+ QMutexLocker locker(&s_mutex);
+ auto it = s_fileStreamers.find(handle);
+ QTC_ASSERT(it != s_fileStreamers.end(), return);
+ QTC_ASSERT(QThread::currentThread() == it->second->thread(), return);
+ s_fileStreamers.erase(it);
+ s_waitCondition.wakeAll();
+}
+
+static void deleteStreamer(FileStreamHandle handle)
+{
+ QMutexLocker locker(&s_mutex);
+ auto it = s_fileStreamers.find(handle);
+ if (it == s_fileStreamers.end())
+ return;
+ if (QThread::currentThread() == it->second->thread()) {
+ delete it->second;
+ s_fileStreamers.erase(it);
+ s_waitCondition.wakeAll();
+ } else {
+ QMetaObject::invokeMethod(it->second, [handle] {
+ deleteStreamer(handle);
+ });
+ s_waitCondition.wait(&s_mutex);
+ QTC_CHECK(s_fileStreamers.find(handle) == s_fileStreamers.end());
+ }
+}
+
+static void deleteAllStreamers()
+{
+ QMutexLocker locker(&s_mutex);
+ QTC_ASSERT(Utils::isMainThread(), return);
+ while (s_fileStreamers.size()) {
+ auto it = s_fileStreamers.begin();
+ if (QThread::currentThread() == it->second->thread()) {
+ delete it->second;
+ s_fileStreamers.erase(it);
+ s_waitCondition.wakeAll();
+ } else {
+ const FileStreamHandle handle = it->first;
+ QMetaObject::invokeMethod(it->second, [handle] {
+ deleteStreamer(handle);
+ });
+ s_waitCondition.wait(&s_mutex);
+ QTC_CHECK(s_fileStreamers.find(handle) == s_fileStreamers.end());
+ }
+ }
+}
+
+static FileStreamHandle checkHandle(FileStreamHandle handle)
+{
+ QMutexLocker locker(&s_mutex);
+ return s_fileStreamers.find(handle) != s_fileStreamers.end() ? handle : FileStreamHandle(0);
+}
+
+FileStreamHandle execute(const std::function<void(FileStreamer *)> &onSetup,
+ const std::function<void(FileStreamer *)> &onDone,
+ QObject *context)
+{
+ FileStreamer *streamer = new FileStreamer;
+ onSetup(streamer);
+ const FileStreamHandle handle = generateUniqueHandle();
+ QTC_CHECK(context == nullptr || context->thread() == QThread::currentThread());
+ if (onDone) {
+ QObject *finalContext = context ? context : streamer;
+ QObject::connect(streamer, &FileStreamer::done, finalContext, [=] { onDone(streamer); });
+ }
+ QObject::connect(streamer, &FileStreamer::done, streamer, [=] {
+ removeStreamer(handle);
+ streamer->deleteLater();
+ });
+ addStreamer(handle, streamer);
+ streamer->start();
+ return checkHandle(handle); // The handle could have been already removed
+}
+
+FileStreamHandle FileStreamerManager::copy(const FilePath &source, const FilePath &destination,
+ const CopyContinuation &cont)
+{
+ return copy(source, destination, nullptr, cont);
+}
+
+FileStreamHandle FileStreamerManager::copy(const FilePath &source, const FilePath &destination,
+ QObject *context, const CopyContinuation &cont)
+{
+ const auto onSetup = [=](FileStreamer *streamer) {
+ streamer->setSource(source);
+ streamer->setDestination(destination);
+ };
+ if (!cont)
+ return execute(onSetup, {}, context);
+
+ const auto onDone = [=](FileStreamer *streamer) {
+ if (streamer->result() == StreamResult::FinishedWithSuccess)
+ cont({});
+ else
+ cont(make_unexpected(Tr::tr("Failed copying file")));
+ };
+ return execute(onSetup, onDone, context);
+}
+
+FileStreamHandle FileStreamerManager::read(const FilePath &source, const ReadContinuation &cont)
+{
+ return read(source, nullptr, cont);
+}
+
+FileStreamHandle FileStreamerManager::read(const FilePath &source, QObject *context,
+ const ReadContinuation &cont)
+{
+ const auto onSetup = [=](FileStreamer *streamer) {
+ streamer->setStreamMode(StreamMode::Reader);
+ streamer->setSource(source);
+ };
+ if (!cont)
+ return execute(onSetup, {}, context);
+
+ const auto onDone = [=](FileStreamer *streamer) {
+ if (streamer->result() == StreamResult::FinishedWithSuccess)
+ cont(streamer->readData());
+ else
+ cont(make_unexpected(Tr::tr("Failed reading file")));
+ };
+ return execute(onSetup, onDone, context);
+}
+
+FileStreamHandle FileStreamerManager::write(const FilePath &destination, const QByteArray &data,
+ const WriteContinuation &cont)
+{
+ return write(destination, data, nullptr, cont);
+}
+
+FileStreamHandle FileStreamerManager::write(const FilePath &destination, const QByteArray &data,
+ QObject *context, const WriteContinuation &cont)
+{
+ const auto onSetup = [=](FileStreamer *streamer) {
+ streamer->setStreamMode(StreamMode::Writer);
+ streamer->setDestination(destination);
+ streamer->setWriteData(data);
+ };
+ if (!cont)
+ return execute(onSetup, {}, context);
+
+ const auto onDone = [=](FileStreamer *streamer) {
+ if (streamer->result() == StreamResult::FinishedWithSuccess)
+ cont(0); // TODO: return write count?
+ else
+ cont(make_unexpected(Tr::tr("Failed writing file")));
+ };
+ return execute(onSetup, onDone, context);
+}
+
+void FileStreamerManager::stop(FileStreamHandle handle)
+{
+ deleteStreamer(handle);
+}
+
+void FileStreamerManager::stopAll()
+{
+ deleteAllStreamers();
+}
+
+} // namespace Utils
+
diff --git a/src/libs/utils/filestreamermanager.h b/src/libs/utils/filestreamermanager.h
new file mode 100644
index 0000000000..f86a3db480
--- /dev/null
+++ b/src/libs/utils/filestreamermanager.h
@@ -0,0 +1,42 @@
+// 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_global.h"
+
+#include "filepath.h"
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QByteArray;
+QT_END_NAMESPACE
+
+namespace Utils {
+
+enum class FileStreamHandle : int {};
+
+class QTCREATOR_UTILS_EXPORT FileStreamerManager
+{
+public:
+ static FileStreamHandle copy(const FilePath &source, const FilePath &destination,
+ const CopyContinuation &cont);
+ static FileStreamHandle copy(const FilePath &source, const FilePath &destination,
+ QObject *context, const CopyContinuation &cont);
+
+ static FileStreamHandle read(const FilePath &source, const ReadContinuation &cont = {});
+ static FileStreamHandle read(const FilePath &source, QObject *context,
+ const ReadContinuation &cont = {});
+
+ static FileStreamHandle write(const FilePath &destination, const QByteArray &data,
+ const WriteContinuation &cont = {});
+ static FileStreamHandle write(const FilePath &destination, const QByteArray &data,
+ QObject *context, const WriteContinuation &cont = {});
+
+ // If called from the same thread that started the task, no continuation is going to be called.
+ static void stop(FileStreamHandle handle);
+ static void stopAll();
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/filesystemwatcher.cpp b/src/libs/utils/filesystemwatcher.cpp
index ad7f77c10e..d1c2e00311 100644
--- a/src/libs/utils/filesystemwatcher.cpp
+++ b/src/libs/utils/filesystemwatcher.cpp
@@ -5,13 +5,12 @@
#include "algorithm.h"
#include "globalfilechangeblocker.h"
+#include "filepath.h"
-#include <QDebug>
#include <QDir>
#include <QFileSystemWatcher>
#include <QDateTime>
-
-enum { debug = 0 };
+#include <QLoggingCategory>
// Returns upper limit of file handles that can be opened by this process at
// once. (which is limited on MacOS, exceeding it will probably result in
@@ -63,6 +62,8 @@ static inline quint64 getFileLimit()
namespace Utils {
+static Q_LOGGING_CATEGORY(fileSystemWatcherLog, "qtc.utils.filesystemwatcher", QtInfoMsg)
+
// Centralized file watcher static data per integer id.
class FileSystemWatcherStaticData
{
@@ -216,8 +217,8 @@ void FileSystemWatcher::init()
if (!d->m_staticData->m_watcher) {
d->m_staticData->m_watcher = new QFileSystemWatcher();
- if (debug)
- qDebug() << this << "Created watcher for id " << d->m_id;
+ qCDebug(fileSystemWatcherLog)
+ << this << "Created watcher for id" << d->m_id;
}
++(d->m_staticData->m_objectCount);
connect(d->m_staticData->m_watcher, &QFileSystemWatcher::fileChanged,
@@ -235,8 +236,8 @@ FileSystemWatcher::~FileSystemWatcher()
d->m_staticData->m_watcher = nullptr;
d->m_staticData->m_fileCount.clear();
d->m_staticData->m_directoryCount.clear();
- if (debug)
- qDebug() << this << "Deleted watcher" << d->m_id;
+ qCDebug(fileSystemWatcherLog)
+ << this << "Deleted watcher" << d->m_id;
}
delete d;
}
@@ -253,10 +254,10 @@ void FileSystemWatcher::addFile(const QString &file, WatchMode wm)
void FileSystemWatcher::addFiles(const QStringList &files, WatchMode wm)
{
- if (debug)
- qDebug() << this << d->m_id << "addFiles mode=" << wm << files
- << " limit currently: " << (d->m_files.size() + d->m_directories.size())
- << " of " << d->m_staticData->maxFileOpen;
+ qCDebug(fileSystemWatcherLog)
+ << this << d->m_id << "addFiles mode" << wm << files
+ << "limit currently:" << (d->m_files.size() + d->m_directories.size())
+ << "of" << d->m_staticData->maxFileOpen;
QStringList toAdd;
for (const QString &file : files) {
if (watchesFile(file)) {
@@ -301,8 +302,8 @@ void FileSystemWatcher::removeFile(const QString &file)
void FileSystemWatcher::removeFiles(const QStringList &files)
{
- if (debug)
- qDebug() << this << d->m_id << "removeFiles " << files;
+ qCDebug(fileSystemWatcherLog)
+ << this << d->m_id << "removeFiles" << files;
QStringList toRemove;
for (const QString &file : files) {
const auto it = d->m_files.constFind(file);
@@ -359,10 +360,10 @@ void FileSystemWatcher::addDirectory(const QString &directory, WatchMode wm)
void FileSystemWatcher::addDirectories(const QStringList &directories, WatchMode wm)
{
- if (debug)
- qDebug() << this << d->m_id << "addDirectories mode " << wm << directories
- << " limit currently: " << (d->m_files.size() + d->m_directories.size())
- << " of " << d->m_staticData->maxFileOpen;
+ qCDebug(fileSystemWatcherLog)
+ << this << d->m_id << "addDirectories mode" << wm << directories
+ << "limit currently:" << (d->m_files.size() + d->m_directories.size())
+ << "of" << d->m_staticData->maxFileOpen;
QStringList toAdd;
for (const QString &directory : directories) {
if (watchesDirectory(directory)) {
@@ -396,8 +397,8 @@ void FileSystemWatcher::removeDirectory(const FilePath &file)
void FileSystemWatcher::removeDirectories(const QStringList &directories)
{
- if (debug)
- qDebug() << this << d->m_id << "removeDirectories" << directories;
+ qCDebug(fileSystemWatcherLog)
+ << this << d->m_id << "removeDirectories" << directories;
QStringList toRemove;
for (const QString &directory : directories) {
@@ -428,10 +429,10 @@ void FileSystemWatcher::slotFileChanged(const QString &path)
const auto it = d->m_files.find(path);
QStringList toAdd;
if (it != d->m_files.end() && it.value().trigger(path)) {
- if (debug)
- qDebug() << this << "triggers on file " << path
- << it.value().watchMode
- << it.value().modifiedTime.toString(Qt::ISODate);
+ qCDebug(fileSystemWatcherLog)
+ << this << "triggers on file" << it.key()
+ << it.value().watchMode
+ << it.value().modifiedTime.toString(Qt::ISODate);
d->fileChanged(path);
QFileInfo fi(path);
@@ -453,17 +454,17 @@ void FileSystemWatcher::slotDirectoryChanged(const QString &path)
{
const auto it = d->m_directories.find(path);
if (it != d->m_directories.end() && it.value().trigger(path)) {
- if (debug)
- qDebug() << this << "triggers on dir " << path
- << it.value().watchMode
- << it.value().modifiedTime.toString(Qt::ISODate);
+ qCDebug(fileSystemWatcherLog)
+ << this << "triggers on dir" << it.key()
+ << it.value().watchMode
+ << it.value().modifiedTime.toString(Qt::ISODate);
d->directoryChanged(path);
}
QStringList toReadd;
- const QDir dir(path);
- for (const QFileInfo &entry : dir.entryInfoList(QDir::Files)) {
- const QString file = entry.filePath();
+ const auto dir = FilePath::fromString(path);
+ for (const FilePath &entry : dir.dirEntries(QDir::Files)) {
+ const QString file = entry.toString();
if (d->m_files.contains(file))
toReadd.append(file);
}
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index 1f6d89c43a..98d190e296 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -5,7 +5,7 @@
#include "savefile.h"
#include "algorithm.h"
-#include "hostosinfo.h"
+#include "devicefileaccess.h"
#include "qtcassert.h"
#include "utilstr.h"
@@ -194,12 +194,13 @@ FileSaver::FileSaver(const FilePath &filePath, QIODevice::OpenMode mode)
auto tf = new QTemporaryFile(QDir::tempPath() + "/remotefilesaver-XXXXXX");
tf->setAutoRemove(false);
m_file.reset(tf);
- } else if (mode & (QIODevice::ReadOnly | QIODevice::Append)) {
- m_file.reset(new QFile{filePath.path()});
- m_isSafe = false;
} else {
- m_file.reset(new SaveFile(filePath));
- m_isSafe = true;
+ const bool readOnlyOrAppend = mode & (QIODevice::ReadOnly | QIODevice::Append);
+ m_isSafe = !readOnlyOrAppend && !filePath.hasHardLinks();
+ if (m_isSafe)
+ m_file.reset(new SaveFile(filePath));
+ else
+ m_file.reset(new QFile{filePath.path()});
}
if (!m_file->open(QIODevice::WriteOnly | mode)) {
QString err = filePath.exists() ?
@@ -590,8 +591,7 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
#endif // QT_WIDGETS_LIB
-// Converts a hex string of the st_mode field of a stat structure to FileFlags.
-FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString)
+FilePathInfo::FileFlags fileInfoFlagsfromStatMode(const QString &hexString, int modeBase)
{
// Copied from stat.h
enum st_mode {
@@ -621,7 +621,7 @@ FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString
};
bool ok = false;
- uint mode = hexString.toUInt(&ok, 16);
+ uint mode = hexString.toUInt(&ok, modeBase);
QTC_ASSERT(ok, return {});
@@ -661,13 +661,13 @@ FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString
return result;
}
-FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos)
+FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos, int modeBase)
{
const QStringList parts = infos.split(' ', Qt::SkipEmptyParts);
if (parts.size() != 3)
return {};
- FilePathInfo::FileFlags flags = fileInfoFlagsfromStatRawModeHex(parts[0]);
+ FilePathInfo::FileFlags flags = fileInfoFlagsfromStatMode(parts[0], modeBase);
const QDateTime dt = QDateTime::fromSecsSinceEpoch(parts[1].toLongLong(), Qt::UTC);
qint64 size = parts[2].toLongLong();
@@ -806,14 +806,14 @@ FilePath FileUtils::commonPath(const FilePath &oldCommonPath, const FilePath &fi
FilePath FileUtils::homePath()
{
- return FilePath::fromString(doCleanPath(QDir::homePath()));
+ return FilePath::fromUserInput(QDir::homePath());
}
-FilePaths FileUtils::toFilePathList(const QStringList &paths) {
- return transform(paths, [](const QString &path) { return FilePath::fromString(path); });
+FilePaths FileUtils::toFilePathList(const QStringList &paths)
+{
+ return transform(paths, &FilePath::fromString);
}
-
qint64 FileUtils::bytesAvailableFromDFOutput(const QByteArray &dfOutput)
{
const auto lines = filtered(dfOutput.split('\n'),
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index f34206d8b7..a1a7ffef97 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -83,7 +83,7 @@ public:
static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput);
- static FilePathInfo filePathInfoFromTriple(const QString &infos);
+ static FilePathInfo filePathInfoFromTriple(const QString &infos, int modeBase);
#ifdef QT_WIDGETS_LIB
static void setDialogParentGetter(const std::function<QWidget *()> &getter);
@@ -121,7 +121,6 @@ public:
QString *selectedFilter = nullptr,
QFileDialog::Options options = {});
#endif
-
};
// for actually finding out if e.g. directories are writable on Windows
@@ -232,9 +231,5 @@ QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &f
bool isRelativePathHelper(const QString &path, OsType osType);
-// For testing
-QTCREATOR_UTILS_EXPORT QString doCleanPath(const QString &input);
-QTCREATOR_UTILS_EXPORT QString cleanPathHelper(const QString &path);
-
} // namespace Utils
diff --git a/src/libs/utils/fileutils_mac.h b/src/libs/utils/fileutils_mac.h
index 4c9e7557e1..c7ddd0d2cd 100644
--- a/src/libs/utils/fileutils_mac.h
+++ b/src/libs/utils/fileutils_mac.h
@@ -8,7 +8,6 @@
namespace Utils {
namespace Internal {
-QUrl filePathUrl(const QUrl &url);
QString normalizePathName(const QString &filePath);
} // Internal
diff --git a/src/libs/utils/fileutils_mac.mm b/src/libs/utils/fileutils_mac.mm
index e7ff062f66..c3d494991d 100644
--- a/src/libs/utils/fileutils_mac.mm
+++ b/src/libs/utils/fileutils_mac.mm
@@ -14,17 +14,6 @@
namespace Utils {
namespace Internal {
-QUrl filePathUrl(const QUrl &url)
-{
- QUrl ret = url;
- @autoreleasepool {
- NSURL *nsurl = url.toNSURL();
- if ([nsurl isFileReferenceURL])
- ret = QUrl::fromNSURL([nsurl filePathURL]);
- }
- return ret;
-}
-
QString normalizePathName(const QString &filePath)
{
QString result;
diff --git a/src/libs/utils/filewizardpage.cpp b/src/libs/utils/filewizardpage.cpp
index aae79b1a49..cc1f83b696 100644
--- a/src/libs/utils/filewizardpage.cpp
+++ b/src/libs/utils/filewizardpage.cpp
@@ -45,7 +45,6 @@ FileWizardPage::FileWizardPage(QWidget *parent) :
d(new FileWizardPagePrivate)
{
setTitle(Tr::tr("Choose the Location"));
- resize(368, 102);
d->m_defaultSuffixLabel = new QLabel;
d->m_nameLabel = new QLabel;
diff --git a/src/libs/utils/fsengine/diriterator.h b/src/libs/utils/fsengine/diriterator.h
index 9daebbebaa..54e9d5d2dd 100644
--- a/src/libs/utils/fsengine/diriterator.h
+++ b/src/libs/utils/fsengine/diriterator.h
@@ -39,6 +39,9 @@ public:
QString currentFileName() const override
{
const QString result = it->fileName();
+ if (result.isEmpty() && !it->host().isEmpty()) {
+ return it->host().toString();
+ }
return chopIfEndsWith(result, '/');
}
diff --git a/src/libs/utils/fsengine/fileiconprovider.cpp b/src/libs/utils/fsengine/fileiconprovider.cpp
index e5ed4a1281..07ea4b3f5a 100644
--- a/src/libs/utils/fsengine/fileiconprovider.cpp
+++ b/src/libs/utils/fsengine/fileiconprovider.cpp
@@ -216,46 +216,7 @@ QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const
{
qCDebug(fileIconProvider) << "FileIconProvider::icon" << filePath.absoluteFilePath();
- if (filePath.isEmpty())
- return unknownFileIcon();
-
- // Check if its one of the virtual devices directories
- if (filePath.path().startsWith(FilePath::specialRootPath())) {
- // If the filepath does not need a device, it is a virtual device directory
- if (!filePath.needsDevice())
- return dirIcon();
- }
-
- bool isDir = filePath.isDir();
-
- // Check for cached overlay icons by file suffix.
- const QString filename = !isDir ? filePath.fileName() : QString();
- if (!filename.isEmpty()) {
- const std::optional<QIcon> icon = getIcon(m_filenameCache, filename);
- if (icon)
- return *icon;
- }
-
- const QString suffix = !isDir ? filePath.suffix() : QString();
- if (!suffix.isEmpty()) {
- const std::optional<QIcon> icon = getIcon(m_suffixCache, suffix);
- if (icon)
- return *icon;
- }
-
- if (filePath.needsDevice())
- return isDir ? dirIcon() : unknownFileIcon();
-
- // Get icon from OS (and cache it based on suffix!)
- QIcon icon;
- if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
- icon = QFileIconProvider::icon(filePath.toFileInfo());
- else // File icons are unknown on linux systems.
- icon = isDir ? QFileIconProvider::icon(filePath.toFileInfo()) : unknownFileIcon();
-
- if (!isDir && !suffix.isEmpty())
- m_suffixCache.insert(suffix, icon);
- return icon;
+ return icon(QFileInfo(filePath.toFSPathString()));
}
/*!
diff --git a/src/libs/utils/fsengine/fileiteratordevicesappender.h b/src/libs/utils/fsengine/fileiteratordevicesappender.h
index c08c6cb3f7..9aec917d6b 100644
--- a/src/libs/utils/fsengine/fileiteratordevicesappender.h
+++ b/src/libs/utils/fsengine/fileiteratordevicesappender.h
@@ -93,7 +93,10 @@ private:
void setPath() const
{
if (!m_hasSetPath) {
- const QString p = path();
+ // path() can be "/somedir/.." so we need to clean it first.
+ // We only need QDir::cleanPath here, as the path is always
+ // a fs engine path and will not contain scheme:// etc.
+ const QString p = QDir::cleanPath(path());
if (p.compare(QDir::rootPath(), Qt::CaseInsensitive) == 0)
m_status = State::IteratingRoot;
diff --git a/src/libs/utils/fsengine/fixedlistfsengine.h b/src/libs/utils/fsengine/fixedlistfsengine.h
index 991bc08b1b..3518b5d0b5 100644
--- a/src/libs/utils/fsengine/fixedlistfsengine.h
+++ b/src/libs/utils/fsengine/fixedlistfsengine.h
@@ -43,6 +43,8 @@ public:
return chopIfEndsWith(m_filePath.toString(), '/');
break;
case QAbstractFileEngine::BaseName:
+ if (m_filePath.fileName().isEmpty())
+ return m_filePath.host().toString();
return m_filePath.fileName();
break;
case QAbstractFileEngine::PathName:
diff --git a/src/libs/utils/fsengine/fsengine.cpp b/src/libs/utils/fsengine/fsengine.cpp
index cc622b8786..fd343aaad1 100644
--- a/src/libs/utils/fsengine/fsengine.cpp
+++ b/src/libs/utils/fsengine/fsengine.cpp
@@ -10,6 +10,8 @@ class Utils::Internal::FSEngineHandler
{};
#endif
+#include <QMutex>
+
#include <memory>
namespace Utils {
@@ -29,46 +31,65 @@ bool FSEngine::isAvailable()
#endif
}
-FilePaths FSEngine::registeredDeviceRoots()
+template<class T>
+class Locked
+{
+public:
+ Locked(QMutex *mutex, T &object)
+ : m_object(object)
+ , m_locker(mutex)
+ {}
+
+ T *operator->() const noexcept { return &m_object; }
+ const T operator*() const noexcept { return m_object; }
+
+private:
+ T &m_object;
+ QMutexLocker<QMutex> m_locker;
+};
+
+static Locked<Utils::FilePaths> deviceRoots()
{
- return FSEngine::deviceRoots();
+ static FilePaths g_deviceRoots;
+ static QMutex mutex;
+ return {&mutex, g_deviceRoots};
}
-void FSEngine::addDevice(const FilePath &deviceRoot)
+static Locked<QStringList> deviceSchemes()
{
- deviceRoots().append(deviceRoot);
+ static QStringList g_deviceSchemes{"device"};
+ static QMutex mutex;
+ return {&mutex, g_deviceSchemes};
}
-void FSEngine::removeDevice(const FilePath &deviceRoot)
+FilePaths FSEngine::registeredDeviceRoots()
{
- deviceRoots().removeAll(deviceRoot);
+ return *deviceRoots();
}
-FilePaths &FSEngine::deviceRoots()
+void FSEngine::addDevice(const FilePath &deviceRoot)
{
- static FilePaths g_deviceRoots;
- return g_deviceRoots;
+ deviceRoots()->append(deviceRoot);
}
-QStringList &FSEngine::deviceSchemes()
+void FSEngine::removeDevice(const FilePath &deviceRoot)
{
- static QStringList g_deviceSchemes{"device"};
- return g_deviceSchemes;
+ deviceRoots()->removeAll(deviceRoot);
}
void FSEngine::registerDeviceScheme(const QStringView scheme)
{
- deviceSchemes().append(scheme.toString());
+ deviceSchemes()->append(scheme.toString());
}
void FSEngine::unregisterDeviceScheme(const QStringView scheme)
{
- deviceSchemes().removeAll(scheme.toString());
+ deviceSchemes()->removeAll(scheme.toString());
}
QStringList FSEngine::registeredDeviceSchemes()
{
- return FSEngine::deviceSchemes();
+ return *deviceSchemes();
}
} // namespace Utils
diff --git a/src/libs/utils/fsengine/fsengine.h b/src/libs/utils/fsengine/fsengine.h
index 52c03e9cee..4c486ead6e 100644
--- a/src/libs/utils/fsengine/fsengine.h
+++ b/src/libs/utils/fsengine/fsengine.h
@@ -34,10 +34,6 @@ public:
static QStringList registeredDeviceSchemes();
private:
- static Utils::FilePaths &deviceRoots();
- static QStringList &deviceSchemes();
-
-private:
std::unique_ptr<Internal::FSEngineHandler> m_engineHandler;
};
diff --git a/src/libs/utils/fsengine/fsengine_impl.cpp b/src/libs/utils/fsengine/fsengine_impl.cpp
index 6654e741ac..ade8236d5e 100644
--- a/src/libs/utils/fsengine/fsengine_impl.cpp
+++ b/src/libs/utils/fsengine/fsengine_impl.cpp
@@ -238,6 +238,8 @@ QString FSEngineImpl::fileName(FileName file) const
return m_filePath.toFSPathString();
break;
case QAbstractFileEngine::BaseName:
+ if (m_filePath.fileName().isEmpty())
+ return m_filePath.host().toString();
return m_filePath.fileName();
break;
case QAbstractFileEngine::PathName:
diff --git a/src/libs/utils/fsengine/fsenginehandler.cpp b/src/libs/utils/fsengine/fsenginehandler.cpp
index a713315ae3..e020eebdda 100644
--- a/src/libs/utils/fsengine/fsenginehandler.cpp
+++ b/src/libs/utils/fsengine/fsenginehandler.cpp
@@ -13,6 +13,26 @@
namespace Utils::Internal {
+static FilePath removeDoubleSlash(const QString &fileName)
+{
+ // Reduce every two or more slashes to a single slash.
+ QString result;
+ const QChar slash = QChar('/');
+ bool lastWasSlash = false;
+ for (const QChar &ch : fileName) {
+ if (ch == slash) {
+ if (!lastWasSlash)
+ result.append(ch);
+ lastWasSlash = true;
+ } else {
+ result.append(ch);
+ lastWasSlash = false;
+ }
+ }
+ // We use fromString() here to not normalize / clean the path anymore.
+ return FilePath::fromString(result);
+}
+
QAbstractFileEngine *FSEngineHandler::create(const QString &fileName) const
{
if (fileName.startsWith(':'))
@@ -29,29 +49,30 @@ QAbstractFileEngine *FSEngineHandler::create(const QString &fileName) const
return rootFilePath.pathAppended(scheme);
});
- return new FixedListFSEngine(rootFilePath, paths);
+ return new FixedListFSEngine(removeDoubleSlash(fileName), paths);
}
if (fixedFileName.startsWith(rootPath)) {
const QStringList deviceSchemes = FSEngine::registeredDeviceSchemes();
for (const QString &scheme : deviceSchemes) {
if (fixedFileName == rootFilePath.pathAppended(scheme).toString()) {
- const FilePaths filteredRoots = Utils::filtered(FSEngine::deviceRoots(),
+ const FilePaths filteredRoots = Utils::filtered(FSEngine::registeredDeviceRoots(),
[scheme](const FilePath &root) {
return root.scheme() == scheme;
});
- return new FixedListFSEngine(rootFilePath.pathAppended(scheme), filteredRoots);
+ return new FixedListFSEngine(removeDoubleSlash(fileName), filteredRoots);
}
}
- FilePath filePath = FilePath::fromString(fixedFileName);
- if (filePath.needsDevice())
- return new FSEngineImpl(filePath);
+ FilePath fixedPath = FilePath::fromString(fixedFileName);
+
+ if (fixedPath.needsDevice())
+ return new FSEngineImpl(removeDoubleSlash(fileName));
}
if (fixedFileName.compare(QDir::rootPath(), Qt::CaseInsensitive) == 0)
- return new RootInjectFSEngine(fixedFileName);
+ return new RootInjectFSEngine(fileName);
return nullptr;
}
diff --git a/src/libs/utils/futuresynchronizer.h b/src/libs/utils/futuresynchronizer.h
index 6a8192f0eb..29b1f5e456 100644
--- a/src/libs/utils/futuresynchronizer.h
+++ b/src/libs/utils/futuresynchronizer.h
@@ -31,14 +31,15 @@ public:
void clearFutures();
void setCancelOnWait(bool enabled);
- bool isCancelOnWait() const; // TODO: The original contained cancelOnWait, what suggests action, not a getter
+ // Note: The QFutureSynchronizer contains cancelOnWait(), what suggests action, not a getter.
+ bool isCancelOnWait() const;
void flushFinishedFutures();
private:
-
QList<QFuture<void>> m_futures;
- bool m_cancelOnWait = false; // TODO: True default makes more sense...
+ // Note: This default value is different than QFutureSynchronizer's one. True makes more sense.
+ bool m_cancelOnWait = true;
};
} // namespace Utils
diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp
index 17a7b1ba29..4ce143b56c 100644
--- a/src/libs/utils/icon.cpp
+++ b/src/libs/utils/icon.cpp
@@ -134,8 +134,8 @@ static QPixmap masksToIcon(const MasksAndColors &masks, const QPixmap &combinedM
Icon::Icon() = default;
-Icon::Icon(std::initializer_list<IconMaskAndColor> args, Icon::IconStyleOptions style)
- : m_iconSourceList(args)
+Icon::Icon(QVector<IconMaskAndColor> args, Icon::IconStyleOptions style)
+ : m_iconSourceList(std::move(args))
, m_style(style)
{
}
diff --git a/src/libs/utils/icon.h b/src/libs/utils/icon.h
index 4121e1e921..c2c5e7a3de 100644
--- a/src/libs/utils/icon.h
+++ b/src/libs/utils/icon.h
@@ -40,7 +40,7 @@ public:
Q_DECLARE_FLAGS(IconStyleOptions, IconStyleOption)
Icon();
- Icon(std::initializer_list<IconMaskAndColor> args, IconStyleOptions style = ToolBarStyle);
+ Icon(QVector<IconMaskAndColor> args, IconStyleOptions style = ToolBarStyle);
Icon(const FilePath &imageFileName);
QIcon icon() const;
diff --git a/src/libs/utils/images/iconoverlay_close_small.png b/src/libs/utils/images/iconoverlay_close_small.png
new file mode 100644
index 0000000000..e39b67cfbb
--- /dev/null
+++ b/src/libs/utils/images/iconoverlay_close_small.png
Binary files differ
diff --git a/src/libs/utils/images/iconoverlay_close_small@2x.png b/src/libs/utils/images/iconoverlay_close_small@2x.png
new file mode 100644
index 0000000000..e8a02dff03
--- /dev/null
+++ b/src/libs/utils/images/iconoverlay_close_small@2x.png
Binary files differ
diff --git a/src/libs/utils/images/pinned_small.png b/src/libs/utils/images/pinned_small.png
new file mode 100644
index 0000000000..10f96f3095
--- /dev/null
+++ b/src/libs/utils/images/pinned_small.png
Binary files differ
diff --git a/src/libs/utils/images/pinned_small@2x.png b/src/libs/utils/images/pinned_small@2x.png
new file mode 100644
index 0000000000..672af736fa
--- /dev/null
+++ b/src/libs/utils/images/pinned_small@2x.png
Binary files differ
diff --git a/src/libs/utils/launcherinterface.cpp b/src/libs/utils/launcherinterface.cpp
index e71b09b0ed..218286521d 100644
--- a/src/libs/utils/launcherinterface.cpp
+++ b/src/libs/utils/launcherinterface.cpp
@@ -21,20 +21,6 @@
namespace Utils {
namespace Internal {
-class LauncherProcess : public QProcess
-{
-public:
- LauncherProcess(QObject *parent) : QProcess(parent)
- {
-#ifdef Q_OS_UNIX
- setChildProcessModifier([this] {
- const auto pid = static_cast<pid_t>(processId());
- setpgid(pid, pid);
- });
-#endif
- }
-};
-
static QString launcherSocketName()
{
return TemporaryDirectory::masterDirectoryPath()
@@ -64,7 +50,7 @@ signals:
private:
QLocalServer * const m_server;
Internal::LauncherSocket *const m_socket;
- Internal::LauncherProcess *m_process = nullptr;
+ QProcess *m_process = nullptr;
QString m_pathToLauncher;
};
@@ -89,12 +75,18 @@ void LauncherInterfacePrivate::doStart()
emit errorOccurred(m_server->errorString());
return;
}
- m_process = new LauncherProcess(this);
+ m_process = new QProcess(this);
connect(m_process, &QProcess::errorOccurred, this, &LauncherInterfacePrivate::handleProcessError);
connect(m_process, &QProcess::finished,
this, &LauncherInterfacePrivate::handleProcessFinished);
connect(m_process, &QProcess::readyReadStandardError,
this, &LauncherInterfacePrivate::handleProcessStderr);
+#ifdef Q_OS_UNIX
+ m_process->setChildProcessModifier([] {
+ setpgid(0, 0);
+ });
+#endif
+
m_process->start(launcherFilePath(), QStringList(m_server->fullServerName()));
}
diff --git a/src/libs/utils/launcherpackets.cpp b/src/libs/utils/launcherpackets.cpp
index 13c3a1560d..37261224ef 100644
--- a/src/libs/utils/launcherpackets.cpp
+++ b/src/libs/utils/launcherpackets.cpp
@@ -48,7 +48,8 @@ void StartProcessPacket::doSerialize(QDataStream &stream) const
<< lowPriority
<< unixTerminalDisabled
<< useCtrlCStub
- << reaperTimeout;
+ << reaperTimeout
+ << createConsoleOnWindows;
}
void StartProcessPacket::doDeserialize(QDataStream &stream)
@@ -68,7 +69,8 @@ void StartProcessPacket::doDeserialize(QDataStream &stream)
>> lowPriority
>> unixTerminalDisabled
>> useCtrlCStub
- >> reaperTimeout;
+ >> reaperTimeout
+ >> createConsoleOnWindows;
processMode = Utils::ProcessMode(processModeInt);
processChannelMode = QProcess::ProcessChannelMode(processChannelModeInt);
}
diff --git a/src/libs/utils/launcherpackets.h b/src/libs/utils/launcherpackets.h
index 2f0bae2915..27e98a74e5 100644
--- a/src/libs/utils/launcherpackets.h
+++ b/src/libs/utils/launcherpackets.h
@@ -98,6 +98,7 @@ public:
bool unixTerminalDisabled = false;
bool useCtrlCStub = false;
int reaperTimeout = 500;
+ bool createConsoleOnWindows = false;
private:
void doSerialize(QDataStream &stream) const override;
diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp
index 07ebc84df9..b1ffb77055 100644
--- a/src/libs/utils/launchersocket.cpp
+++ b/src/libs/utils/launchersocket.cpp
@@ -228,7 +228,7 @@ void CallerHandle::start(const QString &program, const QStringList &arguments)
auto startWhenRunning = [&program, &oldProgram = m_command] {
qWarning() << "Trying to start" << program << "while" << oldProgram
- << "is still running for the same QtcProcess instance."
+ << "is still running for the same Process instance."
<< "The current call will be ignored.";
};
QTC_ASSERT(m_processState == QProcess::NotRunning, startWhenRunning(); return);
@@ -246,6 +246,8 @@ void CallerHandle::start(const QString &program, const QStringList &arguments)
p.command = m_command;
p.arguments = m_arguments;
p.env = m_setup->m_environment.toStringList();
+ if (p.env.isEmpty())
+ p.env = Environment::systemEnvironment().toStringList();
p.workingDir = m_setup->m_workingDirectory.path();
p.processMode = m_setup->m_processMode;
p.writeData = m_setup->m_writeData;
@@ -257,6 +259,7 @@ void CallerHandle::start(const QString &program, const QStringList &arguments)
p.unixTerminalDisabled = m_setup->m_unixTerminalDisabled;
p.useCtrlCStub = m_setup->m_useCtrlCStub;
p.reaperTimeout = m_setup->m_reaperTimeout;
+ p.createConsoleOnWindows = m_setup->m_createConsoleOnWindows;
sendPacket(p);
}
@@ -619,7 +622,7 @@ void LauncherSocket::handleSocketDataAvailable()
}
} else {
// qDebug() << "No handler for token" << m_packetParser.token() << m_handles;
- // in this case the QtcProcess was canceled and deleted
+ // in this case the Process was canceled and deleted
}
handleSocketDataAvailable();
}
diff --git a/src/libs/utils/launchersocket.h b/src/libs/utils/launchersocket.h
index 3a9be73e65..be1b7f7f9b 100644
--- a/src/libs/utils/launchersocket.h
+++ b/src/libs/utils/launchersocket.h
@@ -124,7 +124,7 @@ private:
// Moved to the launcher thread, returned to caller's thread.
// It's assumed that this object will be alive at least
-// as long as the corresponding QtcProcess is alive.
+// as long as the corresponding Process is alive.
class LauncherHandle : public QObject
{
diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp
index e7136c5a90..43dd68c790 100644
--- a/src/libs/utils/layoutbuilder.cpp
+++ b/src/libs/utils/layoutbuilder.cpp
@@ -3,36 +3,180 @@
#include "layoutbuilder.h"
-#include "aspects.h"
-#include "qtcassert.h"
-
+#include <QDebug>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
+#include <QLabel>
#include <QPushButton>
#include <QStackedLayout>
+#include <QSpacerItem>
+#include <QSpinBox>
#include <QSplitter>
#include <QStyle>
#include <QTabWidget>
-#include <QWidget>
+#include <QTextEdit>
+#include <QApplication>
-namespace Utils::Layouting {
+namespace Layouting {
-/*!
- \enum Utils::LayoutBuilder::LayoutType
- \inmodule QtCreator
+// That's cut down qtcassert.{c,h} to avoid the dependency.
+#define QTC_STRINGIFY_HELPER(x) #x
+#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
+#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
+#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
- The LayoutType enum describes the type of \c QLayout a layout builder
- operates on.
+class FlowLayout final : public QLayout
+{
+ Q_OBJECT
- \value Form
- \value Grid
- \value HBox
- \value VBox
-*/
+public:
+ explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1)
+ : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
+ {
+ setContentsMargins(margin, margin, margin, margin);
+ }
+
+ FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1)
+ : m_hSpace(hSpacing), m_vSpace(vSpacing)
+ {
+ setContentsMargins(margin, margin, margin, margin);
+ }
+
+ ~FlowLayout() override
+ {
+ QLayoutItem *item;
+ while ((item = takeAt(0)))
+ delete item;
+ }
+
+ void addItem(QLayoutItem *item) override { itemList.append(item); }
+
+ int horizontalSpacing() const
+ {
+ if (m_hSpace >= 0)
+ return m_hSpace;
+ else
+ return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+ }
+
+ int verticalSpacing() const
+ {
+ if (m_vSpace >= 0)
+ return m_vSpace;
+ else
+ return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+ }
+
+ Qt::Orientations expandingDirections() const override
+ {
+ return {};
+ }
+
+ bool hasHeightForWidth() const override { return true; }
+
+ int heightForWidth(int width) const override
+ {
+ int height = doLayout(QRect(0, 0, width, 0), true);
+ return height;
+ }
+
+ int count() const override { return itemList.size(); }
+
+ QLayoutItem *itemAt(int index) const override
+ {
+ return itemList.value(index);
+ }
+
+ QSize minimumSize() const override
+ {
+ QSize size;
+ for (QLayoutItem *item : itemList)
+ size = size.expandedTo(item->minimumSize());
+
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ size += QSize(left + right, top + bottom);
+ return size;
+ }
+
+ void setGeometry(const QRect &rect) override
+ {
+ QLayout::setGeometry(rect);
+ doLayout(rect, false);
+ }
+
+ QSize sizeHint() const override
+ {
+ return minimumSize();
+ }
+
+ QLayoutItem *takeAt(int index) override
+ {
+ if (index >= 0 && index < itemList.size())
+ return itemList.takeAt(index);
+ else
+ return nullptr;
+ }
+
+private:
+ int doLayout(const QRect &rect, bool testOnly) const
+ {
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
+ int x = effectiveRect.x();
+ int y = effectiveRect.y();
+ int lineHeight = 0;
+
+ for (QLayoutItem *item : itemList) {
+ QWidget *wid = item->widget();
+ int spaceX = horizontalSpacing();
+ if (spaceX == -1)
+ spaceX = wid->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+ int spaceY = verticalSpacing();
+ if (spaceY == -1)
+ spaceY = wid->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+ int nextX = x + item->sizeHint().width() + spaceX;
+ if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
+ x = effectiveRect.x();
+ y = y + lineHeight + spaceY;
+ nextX = x + item->sizeHint().width() + spaceX;
+ lineHeight = 0;
+ }
+
+ if (!testOnly)
+ item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
+
+ x = nextX;
+ lineHeight = qMax(lineHeight, item->sizeHint().height());
+ }
+ return y + lineHeight - rect.y() + bottom;
+ }
+
+ int smartSpacing(QStyle::PixelMetric pm) const
+ {
+ QObject *parent = this->parent();
+ if (!parent) {
+ return -1;
+ } else if (parent->isWidgetType()) {
+ auto pw = static_cast<QWidget *>(parent);
+ return pw->style()->pixelMetric(pm, nullptr, pw);
+ } else {
+ return static_cast<QLayout *>(parent)->spacing();
+ }
+ }
+
+ QList<QLayoutItem *> itemList;
+ int m_hSpace;
+ int m_vSpace;
+};
/*!
- \class Utils::LayoutBuilder::LayoutItem
+ \class Layouting::LayoutItem
\inmodule QtCreator
\brief The LayoutItem class represents widgets, layouts, and aggregate
@@ -46,83 +190,58 @@ namespace Utils::Layouting {
/*!
Constructs a layout item instance representing an empty cell.
*/
-LayoutItem::LayoutItem()
-{}
+LayoutItem::LayoutItem() = default;
+LayoutItem::~LayoutItem() = default;
-/*!
- Constructs a layout item proxy for \a layout.
- */
-LayoutItem::LayoutItem(QLayout *layout)
- : layout(layout)
-{}
-
-/*!
- Constructs a layout item proxy for \a widget.
- */
-LayoutItem::LayoutItem(QWidget *widget)
- : widget(widget)
-{}
/*!
- Constructs a layout item representing a \c BaseAspect.
+ \fn template <class T> LayoutItem(const T &t)
- This ultimately uses the \a aspect's \c addToLayout(LayoutBuilder &) function,
- which in turn can add one or more layout items to the target layout.
+ Constructs a layout item proxy for \a t.
- \sa BaseAspect::addToLayout()
+ T could be
+ \list
+ \li \c {QString}
+ \li \c {QWidget *}
+ \li \c {QLayout *}
+ \endlist
*/
-LayoutItem::LayoutItem(BaseAspect &aspect)
- : aspect(&aspect)
-{}
-LayoutItem::LayoutItem(BaseAspect *aspect)
- : aspect(aspect)
-{}
+struct ResultItem
+{
+ ResultItem() = default;
+ explicit ResultItem(QLayout *l) : layout(l) {}
+ explicit ResultItem(QWidget *w) : widget(w) {}
-/*!
- Constructs a layout item containing some static \a text.
- */
-LayoutItem::LayoutItem(const QString &text)
- : text(text)
-{}
+ QString text;
+ QLayout *layout = nullptr;
+ QWidget *widget = nullptr;
+ int space = -1;
+ int stretch = -1;
+ int span = 1;
+};
-QLayout *LayoutBuilder::createLayout() const
+struct Slice
{
+ Slice() = default;
+ Slice(QLayout *l) : layout(l) {}
+ Slice(QWidget *w) : widget(w) {}
+
QLayout *layout = nullptr;
- switch (m_layoutType) {
- case LayoutBuilder::FormLayout: {
- auto formLayout = new QFormLayout;
- formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- layout = formLayout;
- break;
- }
- case LayoutBuilder::GridLayout: {
- auto gridLayout = new QGridLayout;
- layout = gridLayout;
- break;
- }
- case LayoutBuilder::HBoxLayout: {
- auto hboxLayout = new QHBoxLayout;
- layout = hboxLayout;
- break;
- }
- case LayoutBuilder::VBoxLayout: {
- auto vboxLayout = new QVBoxLayout;
- layout = vboxLayout;
- break;
- }
- case LayoutBuilder::StackLayout: {
- auto stackLayout = new QStackedLayout;
- layout = stackLayout;
- break;
- }
- }
- QTC_ASSERT(layout, return nullptr);
- if (m_spacing)
- layout->setSpacing(*m_spacing);
- return layout;
-}
+ QWidget *widget = nullptr;
+
+ void flush();
+
+ // Grid-specific
+ int currentGridColumn = 0;
+ int currentGridRow = 0;
+ bool isFormAlignment = false;
+ Qt::Alignment align = {}; // Can be changed to
+
+ // Grid or Form
+ QList<ResultItem> pendingItems;
+};
static QWidget *widgetForItem(QLayoutItem *item)
{
@@ -147,18 +266,16 @@ static QLabel *createLabel(const QString &text)
return label;
}
-static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
+static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
{
if (QWidget *w = item.widget) {
layout->addWidget(w);
} else if (QLayout *l = item.layout) {
layout->addLayout(l);
- } else if (item.specialType == LayoutItem::SpecialType::Stretch) {
- layout->addStretch(item.specialValue.toInt());
- } else if (item.specialType == LayoutItem::SpecialType::Space) {
- layout->addSpacing(item.specialValue.toInt());
- } else if (item.specialType == LayoutItem::SpecialType::HorizontalRule) {
- layout->addWidget(Layouting::createHr());
+ } else if (item.stretch != -1) {
+ layout->addStretch(item.stretch);
+ } else if (item.space != -1) {
+ layout->addSpacing(item.space);
} else if (!item.text.isEmpty()) {
layout->addWidget(createLabel(item.text));
} else {
@@ -166,138 +283,204 @@ static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
}
}
-static void flushPendingFormItems(QFormLayout *formLayout,
- LayoutBuilder::LayoutItems &pendingFormItems)
+static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
{
- QTC_ASSERT(formLayout, return);
-
- if (pendingFormItems.empty())
- return;
-
- // If there are more than two items, we cram the last ones in one hbox.
- if (pendingFormItems.size() > 2) {
- auto hbox = new QHBoxLayout;
- hbox->setContentsMargins(0, 0, 0, 0);
- for (int i = 1; i < pendingFormItems.size(); ++i)
- addItemToBoxLayout(hbox, pendingFormItems.at(i));
- while (pendingFormItems.size() >= 2)
- pendingFormItems.pop_back();
- pendingFormItems.append(LayoutItem(hbox));
- }
-
- if (pendingFormItems.size() == 1) { // One one item given, so this spans both columns.
- if (auto layout = pendingFormItems.at(0).layout)
- formLayout->addRow(layout);
- else if (auto widget = pendingFormItems.at(0).widget)
- formLayout->addRow(widget);
- } else if (pendingFormItems.size() == 2) { // Normal case, both columns used.
- if (auto label = pendingFormItems.at(0).widget) {
- if (auto layout = pendingFormItems.at(1).layout)
- formLayout->addRow(label, layout);
- else if (auto widget = pendingFormItems.at(1).widget)
- formLayout->addRow(label, widget);
- } else {
- if (auto layout = pendingFormItems.at(1).layout)
- formLayout->addRow(pendingFormItems.at(0).text, layout);
- else if (auto widget = pendingFormItems.at(1).widget)
- formLayout->addRow(pendingFormItems.at(0).text, widget);
- }
+ if (QWidget *w = item.widget) {
+ layout->addWidget(w);
+ } else if (QLayout *l = item.layout) {
+ layout->addItem(l);
+// } else if (item.stretch != -1) {
+// layout->addStretch(item.stretch);
+// } else if (item.space != -1) {
+// layout->addSpacing(item.space);
+ } else if (!item.text.isEmpty()) {
+ layout->addWidget(createLabel(item.text));
} else {
QTC_CHECK(false);
}
+}
+
+void Slice::flush()
+{
+ if (pendingItems.empty())
+ return;
- // Set up label as buddy if possible.
- const int lastRow = formLayout->rowCount() - 1;
- QLayoutItem *l = formLayout->itemAt(lastRow, QFormLayout::LabelRole);
- QLayoutItem *f = formLayout->itemAt(lastRow, QFormLayout::FieldRole);
- if (l && f) {
- if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
- if (QWidget *widget = widgetForItem(f))
- label->setBuddy(widget);
+ if (auto formLayout = qobject_cast<QFormLayout *>(layout)) {
+
+ // If there are more than two items, we cram the last ones in one hbox.
+ if (pendingItems.size() > 2) {
+ auto hbox = new QHBoxLayout;
+ hbox->setContentsMargins(0, 0, 0, 0);
+ for (int i = 1; i < pendingItems.size(); ++i)
+ addItemToBoxLayout(hbox, pendingItems.at(i));
+ while (pendingItems.size() > 1)
+ pendingItems.pop_back();
+ pendingItems.append(ResultItem(hbox));
}
- }
- pendingFormItems.clear();
-}
+ if (pendingItems.size() == 1) { // One one item given, so this spans both columns.
+ const ResultItem &f0 = pendingItems.at(0);
+ if (auto layout = f0.layout)
+ formLayout->addRow(layout);
+ else if (auto widget = f0.widget)
+ formLayout->addRow(widget);
+ } else if (pendingItems.size() == 2) { // Normal case, both columns used.
+ ResultItem &f1 = pendingItems[1];
+ const ResultItem &f0 = pendingItems.at(0);
+ if (!f1.widget && !f1.layout && !f1.text.isEmpty())
+ f1.widget = createLabel(f1.text);
+
+ if (f0.widget) {
+ if (f1.layout)
+ formLayout->addRow(f0.widget, f1.layout);
+ else if (f1.widget)
+ formLayout->addRow(f0.widget, f1.widget);
+ } else {
+ if (f1.layout)
+ formLayout->addRow(f0.text, f1.layout);
+ else if (f1.widget)
+ formLayout->addRow(f0.text, f1.widget);
+ }
+ } else {
+ QTC_CHECK(false);
+ }
-static void doLayoutHelper(QLayout *layout,
- const LayoutBuilder::LayoutItems &items,
- const Layouting::AttachType attachType,
- int currentGridRow = 0)
-{
- int currentGridColumn = 0;
- LayoutBuilder::LayoutItems pendingFormItems;
-
- auto formLayout = qobject_cast<QFormLayout *>(layout);
- auto gridLayout = qobject_cast<QGridLayout *>(layout);
- auto boxLayout = qobject_cast<QBoxLayout *>(layout);
- auto stackLayout = qobject_cast<QStackedLayout *>(layout);
-
- for (const LayoutItem &item : items) {
- if (item.specialType == LayoutItem::SpecialType::Break) {
- if (formLayout)
- flushPendingFormItems(formLayout, pendingFormItems);
- else if (gridLayout) {
- if (currentGridColumn != 0) {
- ++currentGridRow;
- currentGridColumn = 0;
- }
+ // Set up label as buddy if possible.
+ const int lastRow = formLayout->rowCount() - 1;
+ QLayoutItem *l = formLayout->itemAt(lastRow, QFormLayout::LabelRole);
+ QLayoutItem *f = formLayout->itemAt(lastRow, QFormLayout::FieldRole);
+ if (l && f) {
+ if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
+ if (QWidget *widget = widgetForItem(f))
+ label->setBuddy(widget);
}
- continue;
}
- QWidget *widget = item.widget;
+ } else if (auto gridLayout = qobject_cast<QGridLayout *>(layout)) {
- if (gridLayout) {
- Qt::Alignment align = {};
- if (attachType == Layouting::WithFormAlignment && currentGridColumn == 0)
- align = Qt::Alignment(widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
- if (widget)
- gridLayout->addWidget(widget, currentGridRow, currentGridColumn, 1, item.span, align);
+ for (const ResultItem &item : std::as_const(pendingItems)) {
+ Qt::Alignment a = currentGridColumn == 0 ? align : Qt::Alignment();
+ if (item.widget)
+ gridLayout->addWidget(item.widget, currentGridRow, currentGridColumn, 1, item.span, a);
else if (item.layout)
- gridLayout->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, align);
+ gridLayout->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, a);
else if (!item.text.isEmpty())
- gridLayout->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, align);
+ gridLayout->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, a);
currentGridColumn += item.span;
- } else if (boxLayout) {
+ }
+ ++currentGridRow;
+ currentGridColumn = 0;
+
+ } else if (auto boxLayout = qobject_cast<QBoxLayout *>(layout)) {
+
+ for (const ResultItem &item : std::as_const(pendingItems))
addItemToBoxLayout(boxLayout, item);
- } else if (stackLayout) {
- stackLayout->addWidget(item.widget);
- } else {
- pendingFormItems.append(item);
+
+ } else if (auto flowLayout = qobject_cast<FlowLayout *>(layout)) {
+
+ for (const ResultItem &item : std::as_const(pendingItems))
+ addItemToFlowLayout(flowLayout, item);
+
+ } else if (auto stackLayout = qobject_cast<QStackedLayout *>(layout)) {
+ for (const ResultItem &item : std::as_const(pendingItems)) {
+ if (item.widget)
+ stackLayout->addWidget(item.widget);
+ else
+ QTC_CHECK(false);
}
+
+ } else {
+ QTC_CHECK(false);
}
- if (formLayout)
- flushPendingFormItems(formLayout, pendingFormItems);
+ pendingItems.clear();
}
+// LayoutBuilder
-/*!
- Constructs a layout item from the contents of another LayoutBuilder
- */
-LayoutItem::LayoutItem(const LayoutBuilder &builder)
+class LayoutBuilder
{
- layout = builder.createLayout();
- doLayoutHelper(layout, builder.m_items, Layouting::WithoutMargins);
+ Q_DISABLE_COPY_MOVE(LayoutBuilder)
+
+public:
+ LayoutBuilder();
+ ~LayoutBuilder();
+
+ void addItem(const LayoutItem &item);
+ void addItems(const LayoutItems &items);
+
+ QList<Slice> stack;
+};
+
+static void addItemHelper(LayoutBuilder &builder, const LayoutItem &item)
+{
+ if (item.onAdd)
+ item.onAdd(builder);
+
+ if (item.setter) {
+ if (QWidget *widget = builder.stack.last().widget)
+ item.setter(widget);
+ else if (QLayout *layout = builder.stack.last().layout)
+ item.setter(layout);
+ else
+ QTC_CHECK(false);
+ }
+
+ for (const LayoutItem &subItem : item.subItems)
+ addItemHelper(builder, subItem);
+
+ if (item.onExit)
+ item.onExit(builder);
}
+void doAddText(LayoutBuilder &builder, const QString &text)
+{
+ ResultItem fi;
+ fi.text = text;
+ builder.stack.last().pendingItems.append(fi);
+}
+
+void doAddSpace(LayoutBuilder &builder, const Space &space)
+{
+ ResultItem fi;
+ fi.space = space.space;
+ builder.stack.last().pendingItems.append(fi);
+}
+
+void doAddStretch(LayoutBuilder &builder, const Stretch &stretch)
+{
+ ResultItem fi;
+ fi.stretch = stretch.stretch;
+ builder.stack.last().pendingItems.append(fi);
+}
+
+void doAddLayout(LayoutBuilder &builder, QLayout *layout)
+{
+ builder.stack.last().pendingItems.append(ResultItem(layout));
+}
+
+void doAddWidget(LayoutBuilder &builder, QWidget *widget)
+{
+ builder.stack.last().pendingItems.append(ResultItem(widget));
+}
+
+
/*!
- \class Utils::LayoutBuilder::Space
+ \class Layouting::Space
\inmodule QtCreator
- \brief The LayoutBuilder::Space class represents some empty space in a layout.
+ \brief The Layouting::Space class represents some empty space in a layout.
*/
/*!
- \class Utils::LayoutBuilder::Stretch
+ \class Layouting::Stretch
\inmodule QtCreator
- \brief The LayoutBuilder::Stretch class represents some stretch in a layout.
+ \brief The Layouting::Stretch class represents some stretch in a layout.
*/
/*!
- \class Utils::LayoutBuilder
+ \class LayoutBuilder
\inmodule QtCreator
\brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout
@@ -307,22 +490,9 @@ LayoutItem::LayoutItem(const LayoutBuilder &builder)
A LayoutBuilder instance is typically used locally within a function and never stored.
- \sa addItem(), addItems(), addRow(), finishRow()
+ \sa addItem(), addItems()
*/
-LayoutBuilder::LayoutBuilder(LayoutType layoutType, const LayoutItems &items)
- : m_layoutType(layoutType)
-{
- m_items.reserve(items.size() * 2);
- for (const LayoutItem &item : items)
- addItem(item);
-}
-
-LayoutBuilder &LayoutBuilder::setSpacing(int spacing)
-{
- m_spacing = spacing;
- return *this;
-}
LayoutBuilder::LayoutBuilder() = default;
@@ -331,71 +501,43 @@ LayoutBuilder::LayoutBuilder() = default;
*/
LayoutBuilder::~LayoutBuilder() = default;
-/*!
- Instructs a layout builder to finish the current row.
- This is implicitly called by LayoutBuilder's destructor.
- */
-LayoutBuilder &LayoutBuilder::finishRow()
+void LayoutBuilder::addItem(const LayoutItem &item)
{
- addItem(Break());
- return *this;
+ addItemHelper(*this, item);
}
-/*!
- This starts a new row containing the \a item. The row can be further extended by
- other items using \c addItem() or \c addItems().
-
- \sa finishRow(), addItem(), addItems()
- */
-LayoutBuilder &LayoutBuilder::addRow(const LayoutItem &item)
+void LayoutBuilder::addItems(const LayoutItems &items)
{
- return finishRow().addItem(item);
+ for (const LayoutItem &item : items)
+ addItemHelper(*this, item);
}
/*!
This starts a new row containing \a items. The row can be further extended by
other items using \c addItem() or \c addItems().
- \sa finishRow(), addItem(), addItems()
+ \sa addItem(), addItems()
*/
-LayoutBuilder &LayoutBuilder::addRow(const LayoutItems &items)
+void LayoutItem::addRow(const LayoutItems &items)
{
- return finishRow().addItems(items);
+ addItem(br);
+ addItems(items);
}
/*!
- Adds the layout item \a item to the current row.
+ Adds the layout item \a item as sub items.
*/
-LayoutBuilder &LayoutBuilder::addItem(const LayoutItem &item)
-{
- if (item.aspect) {
- item.aspect->addToLayout(*this);
- if (m_layoutType == FormLayout || m_layoutType == VBoxLayout)
- finishRow();
- } else {
- m_items.push_back(item);
- }
- return *this;
-}
-
-void LayoutBuilder::doLayout(QWidget *parent, Layouting::AttachType attachType) const
+void LayoutItem::addItem(const LayoutItem &item)
{
- QLayout *layout = createLayout();
- parent->setLayout(layout);
-
- doLayoutHelper(layout, m_items, attachType);
- if (attachType == Layouting::WithoutMargins)
- layout->setContentsMargins(0, 0, 0, 0);
+ subItems.append(item);
}
/*!
- Adds the layout item \a items to the current row.
+ Adds the layout items \a items as sub items.
*/
-LayoutBuilder &LayoutBuilder::addItems(const LayoutItems &items)
+void LayoutItem::addItems(const LayoutItems &items)
{
- for (const LayoutItem &item : items)
- addItem(item);
- return *this;
+ subItems.append(items);
}
/*!
@@ -403,145 +545,353 @@ LayoutBuilder &LayoutBuilder::addItems(const LayoutItems &items)
This operation can only be performed once per LayoutBuilder instance.
*/
-void LayoutBuilder::attachTo(QWidget *w, Layouting::AttachType attachType) const
+
+void LayoutItem::attachTo(QWidget *w) const
{
- doLayout(w, attachType);
+ LayoutBuilder builder;
+
+ builder.stack.append(w);
+ addItemHelper(builder, *this);
}
-QWidget *LayoutBuilder::emerge(Layouting::AttachType attachType)
+QWidget *LayoutItem::emerge()
{
auto w = new QWidget;
- doLayout(w, attachType);
+ attachTo(w);
return w;
}
-/*!
- Constructs a layout extender to extend an existing \a layout.
+static void layoutExit(LayoutBuilder &builder)
+{
+ builder.stack.last().flush();
+ QLayout *layout = builder.stack.last().layout;
+ builder.stack.pop_back();
- This constructor can be used to continue the work of previous layout building.
- The type of the underlying layout and previous contents will be retained,
- new items will be added below existing ones.
- */
+ if (QWidget *widget = builder.stack.last().widget)
+ widget->setLayout(layout);
+ else
+ builder.stack.last().pendingItems.append(ResultItem(layout));
+}
-LayoutExtender::LayoutExtender(QLayout *layout, Layouting::AttachType attachType)
- : m_layout(layout), m_attachType(attachType)
-{}
+static void widgetExit(LayoutBuilder &builder)
+{
+ QWidget *widget = builder.stack.last().widget;
+ builder.stack.pop_back();
+ builder.stack.last().pendingItems.append(ResultItem(widget));
+}
-LayoutExtender::~LayoutExtender()
+Column::Column(std::initializer_list<LayoutItem> items)
{
- QTC_ASSERT(m_layout, return);
- int currentGridRow = 0;
- if (auto gridLayout = qobject_cast<QGridLayout *>(m_layout))
- currentGridRow = gridLayout->rowCount();
- doLayoutHelper(m_layout, m_items, m_attachType, currentGridRow);
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); };
+ onExit = layoutExit;
}
-// Special items
+Row::Row(std::initializer_list<LayoutItem> items)
+{
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); };
+ onExit = layoutExit;
+}
-Break::Break()
+Flow::Flow(std::initializer_list<LayoutItem> items)
{
- specialType = SpecialType::Break;
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) { builder.stack.append(new FlowLayout); };
+ onExit = layoutExit;
}
-Stretch::Stretch(int stretch)
+Grid::Grid(std::initializer_list<LayoutItem> items)
{
- specialType = SpecialType::Stretch;
- specialValue = stretch;
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QGridLayout); };
+ onExit = layoutExit;
}
-Space::Space(int space)
+static QFormLayout *newFormLayout()
{
- specialType = SpecialType::Space;
- specialValue = space;
+ auto formLayout = new QFormLayout;
+ formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+ return formLayout;
}
-Span::Span(int span_, const LayoutItem &item)
+Form::Form(std::initializer_list<LayoutItem> items)
{
- LayoutItem::operator=(item);
- span = span_;
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); };
+ onExit = layoutExit;
}
-Tab::Tab(const QString &tabName, const LayoutBuilder &item)
+Stack::Stack(std::initializer_list<LayoutItem> items)
{
- text = tabName;
- widget = new QWidget;
- item.attachTo(widget);
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QStackedLayout); };
+ onExit = layoutExit;
}
-HorizontalRule::HorizontalRule()
+LayoutItem br()
{
- specialType = SpecialType::HorizontalRule;
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) {
+ builder.stack.last().flush();
+ };
+ return item;
}
-// "Widgets"
+LayoutItem empty()
+{
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) {
+ ResultItem ri;
+ ri.span = 1;
+ builder.stack.last().pendingItems.append(ResultItem());
+ };
+ return item;
+}
-static void applyItems(QWidget *widget, const QList<LayoutItem> &items)
+LayoutItem hr()
{
- bool hadLayout = false;
- for (const LayoutItem &item : items) {
- if (item.setter) {
- item.setter(widget);
- } else if (item.layout && !hadLayout) {
- hadLayout = true;
- widget->setLayout(item.layout);
- } else {
- QTC_CHECK(false);
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) { doAddWidget(builder, createHr()); };
+ return item;
+}
+
+LayoutItem st()
+{
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) { doAddStretch(builder, Stretch(1)); };
+ return item;
+}
+
+LayoutItem noMargin()
+{
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) {
+ if (auto layout = builder.stack.last().layout)
+ layout->setContentsMargins(0, 0, 0, 0);
+ else if (auto widget = builder.stack.last().widget)
+ widget->setContentsMargins(0, 0, 0, 0);
+ };
+ return item;
+}
+
+LayoutItem normalMargin()
+{
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) {
+ if (auto layout = builder.stack.last().layout)
+ layout->setContentsMargins(9, 9, 9, 9);
+ else if (auto widget = builder.stack.last().widget)
+ widget->setContentsMargins(9, 9, 9, 9);
+ };
+ return item;
+}
+
+LayoutItem withFormAlignment()
+{
+ LayoutItem item;
+ item.onAdd = [](LayoutBuilder &builder) {
+ if (builder.stack.size() >= 2) {
+ if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
+ const Qt::Alignment align(widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
+ builder.stack.last().align = align;
+ }
}
- }
+ };
+ return item;
+}
+
+// "Widgets"
+
+template <class T>
+void setupWidget(LayoutItem *item)
+{
+ item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); };
+ item->onExit = widgetExit;
+};
+
+Widget::Widget(std::initializer_list<LayoutItem> items)
+{
+ this->subItems = items;
+ setupWidget<QWidget>(this);
}
Group::Group(std::initializer_list<LayoutItem> items)
{
- widget = new QGroupBox;
- applyItems(widget, items);
+ this->subItems = items;
+ setupWidget<QGroupBox>(this);
}
PushButton::PushButton(std::initializer_list<LayoutItem> items)
{
- widget = new QPushButton;
- applyItems(widget, items);
+ this->subItems = items;
+ setupWidget<QPushButton>(this);
+}
+
+SpinBox::SpinBox(std::initializer_list<LayoutItem> items)
+{
+ this->subItems = items;
+ setupWidget<QSpinBox>(this);
+}
+
+TextEdit::TextEdit(std::initializer_list<LayoutItem> items)
+{
+ this->subItems = items;
+ setupWidget<QTextEdit>(this);
}
Splitter::Splitter(std::initializer_list<LayoutItem> items)
- : Splitter(new QSplitter(Qt::Vertical), items) {}
+{
+ subItems = items;
+ onAdd = [](LayoutBuilder &builder) {
+ auto splitter = new QSplitter;
+ splitter->setOrientation(Qt::Vertical);
+ builder.stack.append(splitter);
+ };
+ onExit = [](LayoutBuilder &builder) {
+ const Slice slice = builder.stack.last();
+ QSplitter *splitter = qobject_cast<QSplitter *>(slice.widget);
+ for (const ResultItem &ri : slice.pendingItems) {
+ if (ri.widget)
+ splitter->addWidget(ri.widget);
+ }
+ builder.stack.pop_back();
+ builder.stack.last().pendingItems.append(ResultItem(splitter));
+ };
+}
-Splitter::Splitter(QSplitter *splitter, std::initializer_list<LayoutItem> items)
+TabWidget::TabWidget(std::initializer_list<LayoutItem> items)
{
- widget = splitter;
- for (const LayoutItem &item : items)
- splitter->addWidget(item.widget);
+ this->subItems = items;
+ setupWidget<QTabWidget>(this);
}
-TabWidget::TabWidget(std::initializer_list<Tab> tabs)
- : TabWidget(new QTabWidget, tabs) {}
+// Special Tab
-TabWidget::TabWidget(QTabWidget *tabWidget, std::initializer_list<Tab> tabs)
+Tab::Tab(const QString &tabName, const LayoutItem &item)
{
- widget = tabWidget;
- for (const Tab &tab : tabs)
- tabWidget->addTab(tab.widget, tab.text);
+ onAdd = [item](LayoutBuilder &builder) {
+ auto tab = new QWidget;
+ builder.stack.append(tab);
+ item.attachTo(tab);
+ };
+ onExit = [tabName](LayoutBuilder &builder) {
+ QWidget *inner = builder.stack.last().widget;
+ builder.stack.pop_back();
+ auto tabWidget = qobject_cast<QTabWidget *>(builder.stack.last().widget);
+ QTC_ASSERT(tabWidget, return);
+ tabWidget->addTab(inner, tabName);
+ };
+}
+
+// Special Application
+
+Application::Application(std::initializer_list<LayoutItem> items)
+{
+ subItems = items;
+ setupWidget<QWidget>(this);
+ onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget.
+}
+
+int Application::exec(int &argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ LayoutBuilder builder;
+ addItemHelper(builder, *this);
+ if (QWidget *widget = builder.stack.last().widget)
+ widget->show();
+ return app.exec();
}
// "Properties"
-LayoutItem::Setter title(const QString &title, BoolAspect *checker)
+LayoutItem title(const QString &title)
{
- return [title, checker](QObject *target) {
+ return [title](QObject *target) {
if (auto groupBox = qobject_cast<QGroupBox *>(target)) {
groupBox->setTitle(title);
groupBox->setObjectName(title);
- if (checker) {
- groupBox->setCheckable(true);
- groupBox->setChecked(checker->value());
- checker->setHandlesGroup(groupBox);
- }
+ } else if (auto widget = qobject_cast<QWidget *>(target)) {
+ widget->setWindowTitle(title);
+ } else {
+ QTC_CHECK(false);
+ }
+ };
+}
+
+LayoutItem text(const QString &text)
+{
+ return [text](QObject *target) {
+ if (auto button = qobject_cast<QAbstractButton *>(target)) {
+ button->setText(text);
+ } else if (auto textEdit = qobject_cast<QTextEdit *>(target)) {
+ textEdit->setText(text);
+ } else {
+ QTC_CHECK(false);
+ }
+ };
+}
+
+LayoutItem tooltip(const QString &toolTip)
+{
+ return [toolTip](QObject *target) {
+ if (auto widget = qobject_cast<QWidget *>(target)) {
+ widget->setToolTip(toolTip);
+ } else {
+ QTC_CHECK(false);
+ }
+ };
+}
+
+LayoutItem spacing(int spacing)
+{
+ return [spacing](QObject *target) {
+ if (auto layout = qobject_cast<QLayout *>(target)) {
+ layout->setSpacing(spacing);
+ } else {
+ QTC_CHECK(false);
+ }
+ };
+}
+
+LayoutItem resize(int w, int h)
+{
+ return [w, h](QObject *target) {
+ if (auto widget = qobject_cast<QWidget *>(target)) {
+ widget->resize(w, h);
} else {
QTC_CHECK(false);
}
};
}
-LayoutItem::Setter onClicked(const std::function<void ()> &func, QObject *guard)
+LayoutItem columnStretch(int column, int stretch)
+{
+ return [column, stretch](QObject *target) {
+ if (auto grid = qobject_cast<QGridLayout *>(target)) {
+ grid->setColumnStretch(column, stretch);
+ } else {
+ QTC_CHECK(false);
+ }
+ };
+}
+
+// Id based setters
+
+LayoutItem id(ID &out)
+{
+ return [&out](QObject *target) { out.ob = target; };
+}
+
+void setText(ID id, const QString &text)
+{
+ if (auto textEdit = qobject_cast<QTextEdit *>(id.ob))
+ textEdit->setText(text);
+}
+
+// Signals
+
+LayoutItem onClicked(const std::function<void ()> &func, QObject *guard)
{
return [func, guard](QObject *target) {
if (auto button = qobject_cast<QAbstractButton *>(target)) {
@@ -552,28 +902,30 @@ LayoutItem::Setter onClicked(const std::function<void ()> &func, QObject *guard)
};
}
-LayoutItem::Setter text(const QString &text)
+LayoutItem onTextChanged(const std::function<void (const QString &)> &func, QObject *guard)
{
- return [text](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- button->setText(text);
+ return [func, guard](QObject *target) {
+ if (auto button = qobject_cast<QSpinBox *>(target)) {
+ QObject::connect(button, &QSpinBox::textChanged, guard ? guard : target, func);
} else {
QTC_CHECK(false);
}
};
}
-LayoutItem::Setter tooltip(const QString &toolTip)
+LayoutItem onValueChanged(const std::function<void (int)> &func, QObject *guard)
{
- return [toolTip](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setToolTip(toolTip);
+ return [func, guard](QObject *target) {
+ if (auto button = qobject_cast<QSpinBox *>(target)) {
+ QObject::connect(button, &QSpinBox::valueChanged, guard ? guard : target, func);
} else {
QTC_CHECK(false);
}
};
}
+// Convenience
+
QWidget *createHr(QWidget *parent)
{
auto frame = new QFrame(parent);
@@ -583,9 +935,55 @@ QWidget *createHr(QWidget *parent)
}
// Singletons.
-Break br;
-Stretch st;
-Space empty(0);
-HorizontalRule hr;
-} // Utils::Layouting
+LayoutItem::LayoutItem(const LayoutItem &t)
+{
+ operator=(t);
+}
+
+void createItem(LayoutItem *item, LayoutItem(*t)())
+{
+ *item = t();
+}
+
+void createItem(LayoutItem *item, const std::function<void(QObject *target)> &t)
+{
+ item->setter = t;
+}
+
+void createItem(LayoutItem *item, QWidget *t)
+{
+ item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+}
+
+void createItem(LayoutItem *item, QLayout *t)
+{
+ item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); };
+}
+
+void createItem(LayoutItem *item, const QString &t)
+{
+ item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, t); };
+}
+
+void createItem(LayoutItem *item, const Space &t)
+{
+ item->onAdd = [t](LayoutBuilder &builder) { doAddSpace(builder, t); };
+}
+
+void createItem(LayoutItem *item, const Stretch &t)
+{
+ item->onAdd = [t](LayoutBuilder &builder) { doAddStretch(builder, t); };
+}
+
+void createItem(LayoutItem *item, const Span &t)
+{
+ item->onAdd = [t](LayoutBuilder &builder) {
+ addItemHelper(builder, t.item);
+ builder.stack.last().pendingItems.last().span = t.span;
+ };
+}
+
+} // Layouting
+
+#include "layoutbuilder.moc"
diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h
index 2f534cb488..d716ad034c 100644
--- a/src/libs/utils/layoutbuilder.h
+++ b/src/libs/utils/layoutbuilder.h
@@ -3,256 +3,248 @@
#pragma once
-#include "utils_global.h"
-
#include <QList>
#include <QString>
-#include <QVariant>
+#include <QtGlobal>
#include <optional>
+#if defined(UTILS_LIBRARY)
+# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
+#else
+# define QTCREATOR_UTILS_EXPORT Q_DECL_IMPORT
+#endif
+
QT_BEGIN_NAMESPACE
class QLayout;
-class QSplitter;
-class QTabWidget;
+class QObject;
class QWidget;
+template <class T> T qobject_cast(QObject *object);
QT_END_NAMESPACE
-namespace Utils {
-class BaseAspect;
-class BoolAspect;
-} // Utils
-
-namespace Utils::Layouting {
+namespace Layouting {
-enum AttachType {
- WithMargins,
- WithoutMargins,
- WithFormAlignment, // Handle Grid similar to QFormLayout, i.e. use special alignment for the first column on Mac
-};
+// LayoutItem
class LayoutBuilder;
-
-// LayoutItem
+class LayoutItem;
+using LayoutItems = QList<LayoutItem>;
class QTCREATOR_UTILS_EXPORT LayoutItem
{
public:
- enum class AlignmentType {
- DefaultAlignment,
- AlignAsFormLabel,
- };
-
- enum class SpecialType {
- NotSpecial,
- Space,
- Stretch,
- Break,
- HorizontalRule,
- };
-
using Setter = std::function<void(QObject *target)>;
+
LayoutItem();
- LayoutItem(QLayout *layout);
- LayoutItem(QWidget *widget);
- LayoutItem(BaseAspect *aspect); // Remove
- LayoutItem(BaseAspect &aspect);
- LayoutItem(const QString &text);
- LayoutItem(const LayoutBuilder &builder);
- LayoutItem(const Setter &setter) { this->setter = setter; }
-
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
- BaseAspect *aspect = nullptr;
-
- QString text; // FIXME: Use specialValue for that
- int span = 1;
- AlignmentType align = AlignmentType::DefaultAlignment;
- Setter setter;
- SpecialType specialType = SpecialType::NotSpecial;
- QVariant specialValue;
+ ~LayoutItem();
+
+ LayoutItem(const LayoutItem &t);
+ LayoutItem &operator=(const LayoutItem &t) = default;
+
+ template <class T> LayoutItem(const T &t)
+ {
+ if constexpr (std::is_base_of_v<LayoutItem, T>)
+ LayoutItem::operator=(t);
+ else
+ createItem(this, t);
+ }
+
+ void attachTo(QWidget *w) const;
+ QWidget *emerge();
+
+ void addItem(const LayoutItem &item);
+ void addItems(const LayoutItems &items);
+ void addRow(const LayoutItems &items);
+
+ std::function<void(LayoutBuilder &)> onAdd;
+ std::function<void(LayoutBuilder &)> onExit;
+ std::function<void(QObject *target)> setter;
+ LayoutItems subItems;
};
-class QTCREATOR_UTILS_EXPORT Space : public LayoutItem
+// Special items
+
+class QTCREATOR_UTILS_EXPORT Space
{
public:
- explicit Space(int space);
+ explicit Space(int space) : space(space) {}
+ const int space;
};
-class QTCREATOR_UTILS_EXPORT Span : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stretch
{
public:
- Span(int span, const LayoutItem &item);
+ explicit Stretch(int stretch = 1) : stretch(stretch) {}
+ const int stretch;
};
-class QTCREATOR_UTILS_EXPORT Stretch : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Span
{
public:
- explicit Stretch(int stretch = 1);
+ Span(int span, const LayoutItem &item) : span(span), item(item) {}
+ const int span;
+ LayoutItem item;
};
-class QTCREATOR_UTILS_EXPORT Tab : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Column : public LayoutItem
{
public:
- Tab(const QString &tabName, const LayoutBuilder &item);
+ Column(std::initializer_list<LayoutItem> items);
};
-class QTCREATOR_UTILS_EXPORT Break : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Row : public LayoutItem
{
public:
- Break();
+ Row(std::initializer_list<LayoutItem> items);
};
-class QTCREATOR_UTILS_EXPORT HorizontalRule : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Flow : public LayoutItem
{
public:
- HorizontalRule();
+ Flow(std::initializer_list<LayoutItem> items);
};
-class QTCREATOR_UTILS_EXPORT Group : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Grid : public LayoutItem
{
public:
- Group(std::initializer_list<LayoutItem> items);
+ Grid() : Grid({}) {}
+ Grid(std::initializer_list<LayoutItem> items);
};
-class QTCREATOR_UTILS_EXPORT PushButton : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Form : public LayoutItem
{
public:
- PushButton(std::initializer_list<LayoutItem> items);
+ Form() : Form({}) {}
+ Form(std::initializer_list<LayoutItem> items);
};
-class QTCREATOR_UTILS_EXPORT Splitter : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Widget : public LayoutItem
{
public:
- Splitter(std::initializer_list<LayoutItem> items);
- Splitter(QSplitter *splitter, std::initializer_list<LayoutItem> items);
+ Widget(std::initializer_list<LayoutItem> items);
};
-class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stack : public LayoutItem
{
public:
- TabWidget(std::initializer_list<Tab> tabs);
- TabWidget(QTabWidget *tabWidget, std::initializer_list<Tab> tabs);
+ Stack() : Stack({}) {}
+ Stack(std::initializer_list<LayoutItem> items);
};
-// Singleton items.
-
-QTCREATOR_UTILS_EXPORT extern Break br;
-QTCREATOR_UTILS_EXPORT extern Stretch st;
-QTCREATOR_UTILS_EXPORT extern Space empty;
-QTCREATOR_UTILS_EXPORT extern HorizontalRule hr;
-
-// "Properties"
-
-QTCREATOR_UTILS_EXPORT LayoutItem::Setter title(const QString &title,
- BoolAspect *checker = nullptr);
+class QTCREATOR_UTILS_EXPORT Tab : public LayoutItem
+{
+public:
+ Tab(const QString &tabName, const LayoutItem &item);
+};
-QTCREATOR_UTILS_EXPORT LayoutItem::Setter text(const QString &text);
-QTCREATOR_UTILS_EXPORT LayoutItem::Setter tooltip(const QString &toolTip);
-QTCREATOR_UTILS_EXPORT LayoutItem::Setter onClicked(const std::function<void()> &func,
- QObject *guard = nullptr);
+class QTCREATOR_UTILS_EXPORT Group : public LayoutItem
+{
+public:
+ Group(std::initializer_list<LayoutItem> items);
+};
+class QTCREATOR_UTILS_EXPORT TextEdit : public LayoutItem
+{
+public:
+ TextEdit(std::initializer_list<LayoutItem> items);
+};
-// Convenience
+class QTCREATOR_UTILS_EXPORT PushButton : public LayoutItem
+{
+public:
+ PushButton(std::initializer_list<LayoutItem> items);
+};
-QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
+class QTCREATOR_UTILS_EXPORT SpinBox : public LayoutItem
+{
+public:
+ SpinBox(std::initializer_list<LayoutItem> items);
+};
+class QTCREATOR_UTILS_EXPORT Splitter : public LayoutItem
+{
+public:
+ Splitter(std::initializer_list<LayoutItem> items);
+};
-// LayoutBuilder
+class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem
+{
+public:
+ TabWidget(std::initializer_list<LayoutItem> items);
+};
-class QTCREATOR_UTILS_EXPORT LayoutBuilder
+class QTCREATOR_UTILS_EXPORT Application : public LayoutItem
{
public:
- enum LayoutType {
- HBoxLayout,
- VBoxLayout,
- FormLayout,
- GridLayout,
- StackLayout,
- };
+ Application(std::initializer_list<LayoutItem> items);
- using LayoutItems = QList<LayoutItem>;
+ int exec(int &argc, char *argv[]);
+};
- explicit LayoutBuilder(LayoutType layoutType, const LayoutItems &items = {});
- LayoutBuilder(const LayoutBuilder &) = delete;
- LayoutBuilder(LayoutBuilder &&) = default;
- LayoutBuilder &operator=(const LayoutBuilder &) = delete;
- LayoutBuilder &operator=(LayoutBuilder &&) = default;
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const std::function<void(QObject *target)> &t);
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QWidget *t);
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QLayout *t);
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, LayoutItem(*t)());
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const QString &t);
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Span &t);
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Space &t);
+void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Stretch &t);
- ~LayoutBuilder();
- LayoutBuilder &setSpacing(int spacing);
+// "Singletons"
- LayoutBuilder &addItem(const LayoutItem &item);
- LayoutBuilder &addItems(const LayoutItems &items);
+QTCREATOR_UTILS_EXPORT LayoutItem br();
+QTCREATOR_UTILS_EXPORT LayoutItem st();
+QTCREATOR_UTILS_EXPORT LayoutItem empty();
+QTCREATOR_UTILS_EXPORT LayoutItem hr();
+QTCREATOR_UTILS_EXPORT LayoutItem noMargin();
+QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
+QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
- LayoutBuilder &finishRow();
- LayoutBuilder &addRow(const LayoutItem &item);
- LayoutBuilder &addRow(const LayoutItems &items);
+// "Setters"
- LayoutType layoutType() const { return m_layoutType; }
+QTCREATOR_UTILS_EXPORT LayoutItem title(const QString &title);
+QTCREATOR_UTILS_EXPORT LayoutItem text(const QString &text);
+QTCREATOR_UTILS_EXPORT LayoutItem tooltip(const QString &toolTip);
+QTCREATOR_UTILS_EXPORT LayoutItem resize(int, int);
+QTCREATOR_UTILS_EXPORT LayoutItem columnStretch(int column, int stretch);
+QTCREATOR_UTILS_EXPORT LayoutItem spacing(int);
+QTCREATOR_UTILS_EXPORT LayoutItem windowTitle(const QString &windowTitle);
- void attachTo(QWidget *w, Layouting::AttachType attachType = Layouting::WithMargins) const;
- QWidget *emerge(Layouting::AttachType attachType = Layouting::WithMargins);
+// "Getters"
-protected:
- friend class LayoutItem;
+class ID
+{
+public:
+ QObject *ob = nullptr;
+};
- explicit LayoutBuilder(); // Adds to existing layout.
+QTCREATOR_UTILS_EXPORT LayoutItem id(ID &out);
- QLayout *createLayout() const;
- void doLayout(QWidget *parent, Layouting::AttachType attachType) const;
+QTCREATOR_UTILS_EXPORT void setText(ID id, const QString &text);
- LayoutItems m_items;
- LayoutType m_layoutType;
- std::optional<int> m_spacing;
-};
-class QTCREATOR_UTILS_EXPORT LayoutExtender : public LayoutBuilder
-{
-public:
- explicit LayoutExtender(QLayout *layout, Layouting::AttachType attachType);
- ~LayoutExtender();
+// "Signals"
-private:
- QLayout *m_layout = nullptr;
- Layouting::AttachType m_attachType = {};
-};
+QTCREATOR_UTILS_EXPORT LayoutItem onClicked(const std::function<void()> &,
+ QObject *guard = nullptr);
+QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(const std::function<void(const QString &)> &,
+ QObject *guard = nullptr);
+QTCREATOR_UTILS_EXPORT LayoutItem onValueChanged(const std::function<void(int)> &,
+ QObject *guard = nullptr);
-class QTCREATOR_UTILS_EXPORT Column : public LayoutBuilder
-{
-public:
- Column() : LayoutBuilder(VBoxLayout) {}
- Column(std::initializer_list<LayoutItem> items) : LayoutBuilder(VBoxLayout, items) {}
-};
+QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(ID &id, QVariant(*sig)(QObject *));
-class QTCREATOR_UTILS_EXPORT Row : public LayoutBuilder
-{
-public:
- Row() : LayoutBuilder(HBoxLayout) {}
- Row(std::initializer_list<LayoutItem> items) : LayoutBuilder(HBoxLayout, items) {}
-};
+// Convenience
-class QTCREATOR_UTILS_EXPORT Grid : public LayoutBuilder
-{
-public:
- Grid() : LayoutBuilder(GridLayout) {}
- Grid(std::initializer_list<LayoutItem> items) : LayoutBuilder(GridLayout, items) {}
-};
+QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
-class QTCREATOR_UTILS_EXPORT Form : public LayoutBuilder
+template <class T>
+LayoutItem bindTo(T **out)
{
-public:
- Form() : LayoutBuilder(FormLayout) {}
- Form(std::initializer_list<LayoutItem> items) : LayoutBuilder(FormLayout, items) {}
-};
+ return [out](QObject *target) { *out = qobject_cast<T *>(target); };
+}
-class QTCREATOR_UTILS_EXPORT Stack : public LayoutBuilder
-{
-public:
- Stack() : LayoutBuilder(StackLayout) {}
- Stack(std::initializer_list<LayoutItem> items) : LayoutBuilder(StackLayout, items) {}
-};
-} // Utils::Layouting
+} // Layouting
diff --git a/src/libs/utils/linecolumn.cpp b/src/libs/utils/linecolumn.cpp
deleted file mode 100644
index 50a24ada62..0000000000
--- a/src/libs/utils/linecolumn.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "linecolumn.h"
-
-#include <QRegularExpression>
-
-namespace Utils {
-
-/*!
- Returns the line and column of a \a fileName and sets the \a postfixPos if
- it can find a positional postfix.
-
- The following patterns are supported: \c {filepath.txt:19},
- \c{filepath.txt:19:12}, \c {filepath.txt+19},
- \c {filepath.txt+19+12}, and \c {filepath.txt(19)}.
-*/
-
-LineColumn LineColumn::extractFromFileName(QStringView fileName, int &postfixPos)
-{
- static const auto regexp = QRegularExpression("[:+](\\d+)?([:+](\\d+)?)?$");
- // (10) MSVC-style
- static const auto vsRegexp = QRegularExpression("[(]((\\d+)[)]?)?$");
- const QRegularExpressionMatch match = regexp.match(fileName);
- LineColumn lineColumn;
- if (match.hasMatch()) {
- postfixPos = match.capturedStart(0);
- lineColumn.line = 0; // for the case that there's only a : at the end
- if (match.lastCapturedIndex() > 0) {
- lineColumn.line = match.captured(1).toInt();
- if (match.lastCapturedIndex() > 2) // index 2 includes the + or : for the column number
- lineColumn.column = match.captured(3).toInt() - 1; //column is 0 based, despite line being 1 based
- }
- } else {
- const QRegularExpressionMatch vsMatch = vsRegexp.match(fileName);
- postfixPos = vsMatch.capturedStart(0);
- if (vsMatch.lastCapturedIndex() > 1) // index 1 includes closing )
- lineColumn.line = vsMatch.captured(2).toInt();
- }
- return lineColumn;
-}
-
-} // namespace Utils
diff --git a/src/libs/utils/linecolumn.h b/src/libs/utils/linecolumn.h
deleted file mode 100644
index 78a881d7f4..0000000000
--- a/src/libs/utils/linecolumn.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2017 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_global.h"
-
-#include <QMetaType>
-
-#include <optional>
-
-namespace Utils {
-
-class QTCREATOR_UTILS_EXPORT LineColumn
-{
-public:
- constexpr LineColumn() = default;
- constexpr LineColumn(int line, int column) : line(line), column(column) {}
-
- bool isValid() const
- {
- return line >= 0 && column >= 0;
- }
-
- friend bool operator==(LineColumn first, LineColumn second)
- {
- return first.isValid() && first.line == second.line && first.column == second.column;
- }
-
- friend bool operator!=(LineColumn first, LineColumn second)
- {
- return !(first == second);
- }
-
- static LineColumn extractFromFileName(QStringView fileName, int &postfixPos);
-
-public:
- int line = -1;
- int column = -1;
-};
-
-using OptionalLineColumn = std::optional<LineColumn>;
-
-} // namespace Utils
-
-Q_DECLARE_METATYPE(Utils::LineColumn)
diff --git a/src/libs/utils/link.cpp b/src/libs/utils/link.cpp
index e4c7032eeb..1a4b4f9f9a 100644
--- a/src/libs/utils/link.cpp
+++ b/src/libs/utils/link.cpp
@@ -3,7 +3,7 @@
#include "link.h"
-#include "linecolumn.h"
+#include "textutils.h"
namespace Utils {
@@ -24,10 +24,10 @@ Link Link::fromString(const QString &filePathWithNumbers, bool canContainLineNum
link.targetFilePath = FilePath::fromUserInput(filePathWithNumbers);
} else {
int postfixPos = -1;
- const LineColumn lineColumn = LineColumn::extractFromFileName(filePathWithNumbers, postfixPos);
+ const Text::Position pos = Text::Position::fromFileName(filePathWithNumbers, postfixPos);
link.targetFilePath = FilePath::fromUserInput(filePathWithNumbers.left(postfixPos));
- link.targetLine = lineColumn.line;
- link.targetColumn = lineColumn.column;
+ link.targetLine = pos.line;
+ link.targetColumn = pos.column;
}
return link;
}
diff --git a/src/libs/utils/link.h b/src/libs/utils/link.h
index 6f01b48434..00194654c9 100644
--- a/src/libs/utils/link.h
+++ b/src/libs/utils/link.h
@@ -17,7 +17,8 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT Link
{
public:
- Link(const FilePath &filePath = FilePath(), int line = 0, int column = 0)
+ Link() = default;
+ Link(const FilePath &filePath, int line = 0, int column = 0)
: targetFilePath(filePath)
, targetLine(line)
, targetColumn(column)
@@ -26,7 +27,11 @@ public:
static Link fromString(const QString &filePathWithNumbers, bool canContainLineNumber = false);
bool hasValidTarget() const
- { return !targetFilePath.isEmpty(); }
+ {
+ if (!targetFilePath.isEmpty())
+ return true;
+ return !targetFilePath.scheme().isEmpty() || !targetFilePath.host().isEmpty();
+ }
bool hasValidLinkText() const
{ return linkTextStart != linkTextEnd; }
@@ -48,8 +53,8 @@ public:
int linkTextEnd = -1;
FilePath targetFilePath;
- int targetLine;
- int targetColumn;
+ int targetLine = 0;
+ int targetColumn = 0;
};
using LinkHandler = std::function<void(const Link &)>;
@@ -58,3 +63,12 @@ using Links = QList<Link>;
} // namespace Utils
Q_DECLARE_METATYPE(Utils::Link)
+
+namespace std {
+
+template<> struct hash<Utils::Link>
+{
+ size_t operator()(const Utils::Link &fn) const { return qHash(fn); }
+};
+
+} // std
diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp
index 4e24247833..644268d7cf 100644
--- a/src/libs/utils/macroexpander.cpp
+++ b/src/libs/utils/macroexpander.cpp
@@ -140,7 +140,7 @@ using namespace Internal;
MacroExpander::registerVariable(
"MyVariable",
Tr::tr("The current value of whatever I want."));
- []() -> QString {
+ [] {
QString value;
// do whatever is necessary to retrieve the value
[...]
@@ -196,7 +196,7 @@ using namespace Internal;
you would use the one provided by the variable manager). Mostly the same as
MacroExpander::expandedString(), but also has a variant that does the replacement inline
instead of returning a new string.
- \li Using Utils::QtcProcess::expandMacros(). This expands the string while conforming to the
+ \li Using Utils::CommandLine::expandMacros(). This expands the string while conforming to the
quoting rules of the platform it is run on. Use this function with the variable manager's
macro expander if your string will be passed as a command line parameter string to an
external command.
@@ -377,41 +377,45 @@ void MacroExpander::registerIntVariable(const QByteArray &variable,
void MacroExpander::registerFileVariables(const QByteArray &prefix,
const QString &heading, const FileFunction &base, bool visibleInChooser)
{
- registerVariable(prefix + kFilePathPostfix,
- Tr::tr("%1: Full path including file name.").arg(heading),
- [base]() -> QString { QString tmp = base().toString(); return tmp.isEmpty() ? QString() : QFileInfo(tmp).filePath(); },
- visibleInChooser);
-
- registerVariable(prefix + kPathPostfix,
- Tr::tr("%1: Full path excluding file name.").arg(heading),
- [base]() -> QString { QString tmp = base().toString(); return tmp.isEmpty() ? QString() : QFileInfo(tmp).path(); },
- visibleInChooser);
-
- registerVariable(prefix + kNativeFilePathPostfix,
- Tr::tr("%1: Full path including file name, with native path separator (backslash on Windows).").arg(heading),
- [base]() -> QString {
- QString tmp = base().toString();
- return tmp.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(tmp).filePath());
- },
- visibleInChooser);
-
- registerVariable(prefix + kNativePathPostfix,
- Tr::tr("%1: Full path excluding file name, with native path separator (backslash on Windows).").arg(heading),
- [base]() -> QString {
- QString tmp = base().toString();
- return tmp.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(tmp).path());
- },
- visibleInChooser);
-
- registerVariable(prefix + kFileNamePostfix,
- Tr::tr("%1: File name without path.").arg(heading),
- [base]() -> QString { QString tmp = base().toString(); return tmp.isEmpty() ? QString() : FilePath::fromString(tmp).fileName(); },
- visibleInChooser);
-
- registerVariable(prefix + kFileBaseNamePostfix,
- Tr::tr("%1: File base name without path and suffix.").arg(heading),
- [base]() -> QString { QString tmp = base().toString(); return tmp.isEmpty() ? QString() : QFileInfo(tmp).baseName(); },
- visibleInChooser);
+ registerVariable(
+ prefix + kFilePathPostfix,
+ Tr::tr("%1: Full path including file name.").arg(heading),
+ [base] { return base().path(); },
+ visibleInChooser);
+
+ registerVariable(
+ prefix + kPathPostfix,
+ Tr::tr("%1: Full path excluding file name.").arg(heading),
+ [base] { return base().parentDir().path(); },
+ visibleInChooser);
+
+ registerVariable(
+ prefix + kNativeFilePathPostfix,
+ Tr::tr(
+ "%1: Full path including file name, with native path separator (backslash on Windows).")
+ .arg(heading),
+ [base] { return base().nativePath(); },
+ visibleInChooser);
+
+ registerVariable(
+ prefix + kNativePathPostfix,
+ Tr::tr(
+ "%1: Full path excluding file name, with native path separator (backslash on Windows).")
+ .arg(heading),
+ [base] { return base().parentDir().nativePath(); },
+ visibleInChooser);
+
+ registerVariable(
+ prefix + kFileNamePostfix,
+ Tr::tr("%1: File name without path.").arg(heading),
+ [base] { return base().fileName(); },
+ visibleInChooser);
+
+ registerVariable(
+ prefix + kFileBaseNamePostfix,
+ Tr::tr("%1: File base name without path and suffix.").arg(heading),
+ [base] { return base().baseName(); },
+ visibleInChooser);
}
void MacroExpander::registerExtraResolver(const MacroExpander::ResolverFunction &value)
diff --git a/src/libs/utils/multitextcursor.cpp b/src/libs/utils/multitextcursor.cpp
index bb2e38f5eb..0c2e5b0792 100644
--- a/src/libs/utils/multitextcursor.cpp
+++ b/src/libs/utils/multitextcursor.cpp
@@ -16,33 +16,130 @@ namespace Utils {
MultiTextCursor::MultiTextCursor() {}
MultiTextCursor::MultiTextCursor(const QList<QTextCursor> &cursors)
- : m_cursors(cursors)
{
- mergeCursors();
+ setCursors(cursors);
+}
+
+void MultiTextCursor::fillMapWithList()
+{
+ m_cursorMap.clear();
+ for (auto it = m_cursorList.begin(); it != m_cursorList.end(); ++it)
+ m_cursorMap[it->selectionStart()] = it;
+}
+
+MultiTextCursor& MultiTextCursor::operator=(const MultiTextCursor &multiCursor)
+{
+ m_cursorList = multiCursor.m_cursorList;
+ fillMapWithList();
+ return *this;
+}
+
+MultiTextCursor::MultiTextCursor(const MultiTextCursor &multiCursor)
+{
+ *this = multiCursor;
+}
+
+MultiTextCursor& MultiTextCursor::operator=(const MultiTextCursor &&multiCursor)
+{
+ m_cursorList = std::move(multiCursor.m_cursorList);
+ fillMapWithList();
+ return *this;
+}
+
+MultiTextCursor::MultiTextCursor(const MultiTextCursor &&multiCursor)
+{
+ *this = std::move(multiCursor);
+}
+
+MultiTextCursor::~MultiTextCursor() = default;
+
+static bool cursorsOverlap(const QTextCursor &c1, const QTextCursor &c2)
+{
+ if (c1.hasSelection()) {
+ if (c2.hasSelection()) {
+ return c2.selectionEnd() > c1.selectionStart()
+ && c2.selectionStart() < c1.selectionEnd();
+ }
+ const int c2Pos = c2.position();
+ return c2Pos > c1.selectionStart() && c2Pos < c1.selectionEnd();
+ }
+ if (c2.hasSelection()) {
+ const int c1Pos = c1.position();
+ return c1Pos > c2.selectionStart() && c1Pos < c2.selectionEnd();
+ }
+ return c1 == c2;
+};
+
+static void mergeCursors(QTextCursor &c1, const QTextCursor &c2)
+{
+ if (c1.position() == c2.position() && c1.anchor() == c2.anchor())
+ return;
+ if (c1.hasSelection()) {
+ if (!c2.hasSelection())
+ return;
+ int pos = c1.position();
+ int anchor = c1.anchor();
+ if (c1.selectionStart() > c2.selectionStart()) {
+ if (pos < anchor)
+ pos = c2.selectionStart();
+ else
+ anchor = c2.selectionStart();
+ }
+ if (c1.selectionEnd() < c2.selectionEnd()) {
+ if (pos < anchor)
+ anchor = c2.selectionEnd();
+ else
+ pos = c2.selectionEnd();
+ }
+ c1.setPosition(anchor);
+ c1.setPosition(pos, QTextCursor::KeepAnchor);
+ } else {
+ c1 = c2;
+ }
}
void MultiTextCursor::addCursor(const QTextCursor &cursor)
{
QTC_ASSERT(!cursor.isNull(), return);
- m_cursors.append(cursor);
- mergeCursors();
+
+ QTextCursor c1 = cursor;
+ const int pos = c1.selectionStart();
+
+ auto found = m_cursorMap.lower_bound(pos);
+ if (found != m_cursorMap.begin())
+ --found;
+
+ for (; !m_cursorMap.empty() && found != m_cursorMap.end()
+ && found->second->selectionStart() <= cursor.selectionEnd();) {
+ const QTextCursor &c2 = *found->second;
+ if (cursorsOverlap(c1, c2)) {
+ Utils::mergeCursors(c1, c2);
+ m_cursorList.erase(found->second);
+ found = m_cursorMap.erase(found);
+ continue;
+ }
+ ++found;
+ }
+
+ m_cursorMap[pos] = m_cursorList.insert(m_cursorList.end(), c1);
}
void MultiTextCursor::addCursors(const QList<QTextCursor> &cursors)
{
- m_cursors.append(cursors);
- mergeCursors();
+ for (const QTextCursor &c : cursors)
+ addCursor(c);
}
void MultiTextCursor::setCursors(const QList<QTextCursor> &cursors)
{
- m_cursors = cursors;
- mergeCursors();
+ m_cursorList.clear();
+ m_cursorMap.clear();
+ addCursors(cursors);
}
const QList<QTextCursor> MultiTextCursor::cursors() const
{
- return m_cursors;
+ return QList<QTextCursor>(m_cursorList.begin(), m_cursorList.end());
}
void MultiTextCursor::replaceMainCursor(const QTextCursor &cursor)
@@ -54,64 +151,72 @@ void MultiTextCursor::replaceMainCursor(const QTextCursor &cursor)
QTextCursor MultiTextCursor::mainCursor() const
{
- if (m_cursors.isEmpty())
+ if (m_cursorList.empty())
return {};
- return m_cursors.last();
+ return m_cursorList.back();
}
QTextCursor MultiTextCursor::takeMainCursor()
{
- if (m_cursors.isEmpty())
+ if (m_cursorList.empty())
return {};
- return m_cursors.takeLast();
+
+ QTextCursor cursor = m_cursorList.back();
+ auto it = m_cursorList.end();
+ --it;
+ m_cursorMap.erase(it->selectionStart());
+ m_cursorList.erase(it);
+
+ return cursor;
}
void MultiTextCursor::beginEditBlock()
{
- QTC_ASSERT(!m_cursors.empty(), return);
- m_cursors.last().beginEditBlock();
+ QTC_ASSERT(!m_cursorList.empty(), return);
+ m_cursorList.back().beginEditBlock();
}
void MultiTextCursor::endEditBlock()
{
- QTC_ASSERT(!m_cursors.empty(), return);
- m_cursors.last().endEditBlock();
+ QTC_ASSERT(!m_cursorList.empty(), return);
+ m_cursorList.back().endEditBlock();
}
bool MultiTextCursor::isNull() const
{
- return m_cursors.isEmpty();
+ return m_cursorList.empty();
}
bool MultiTextCursor::hasMultipleCursors() const
{
- return m_cursors.size() > 1;
+ return m_cursorList.size() > 1;
}
int MultiTextCursor::cursorCount() const
{
- return static_cast<int>(m_cursors.size());
+ return static_cast<int>(m_cursorList.size());
}
void MultiTextCursor::movePosition(QTextCursor::MoveOperation operation,
QTextCursor::MoveMode mode,
int n)
{
- for (QTextCursor &cursor : m_cursors)
+ for (auto &cursor : m_cursorList)
cursor.movePosition(operation, mode, n);
+
mergeCursors();
}
bool MultiTextCursor::hasSelection() const
{
- return Utils::anyOf(m_cursors, &QTextCursor::hasSelection);
+ return Utils::anyOf(m_cursorList, &QTextCursor::hasSelection);
}
QString MultiTextCursor::selectedText() const
{
QString text;
- const QList<QTextCursor> cursors = Utils::sorted(m_cursors);
- for (const QTextCursor &cursor : cursors) {
+ for (const auto &element : std::as_const(m_cursorMap)) {
+ const QTextCursor &cursor = *element.second;
const QString &cursorText = cursor.selectedText();
if (cursorText.isEmpty())
continue;
@@ -128,8 +233,8 @@ QString MultiTextCursor::selectedText() const
void MultiTextCursor::removeSelectedText()
{
beginEditBlock();
- for (QTextCursor &c : m_cursors)
- c.removeSelectedText();
+ for (auto cursor = m_cursorList.begin(); cursor != m_cursorList.end(); ++cursor)
+ cursor->removeSelectedText();
endEditBlock();
mergeCursors();
}
@@ -149,25 +254,27 @@ static void insertAndSelect(QTextCursor &cursor, const QString &text, bool selec
void MultiTextCursor::insertText(const QString &text, bool selectNewText)
{
- if (m_cursors.isEmpty())
+ if (m_cursorList.empty())
return;
- m_cursors.last().beginEditBlock();
+
+ m_cursorList.back().beginEditBlock();
if (hasMultipleCursors()) {
QStringList lines = text.split('\n');
if (!lines.isEmpty() && lines.last().isEmpty())
lines.pop_back();
int index = 0;
- if (lines.count() == m_cursors.count()) {
- QList<QTextCursor> cursors = Utils::sorted(m_cursors);
- for (QTextCursor &cursor : cursors)
+ if (static_cast<long unsigned int>(lines.count()) == m_cursorList.size()) {
+ for (const auto &element : std::as_const(m_cursorMap)) {
+ QTextCursor &cursor = *element.second;
insertAndSelect(cursor, lines.at(index++), selectNewText);
- m_cursors.last().endEditBlock();
+ }
+ m_cursorList.back().endEditBlock();
return;
}
}
- for (QTextCursor &cursor : m_cursors)
- insertAndSelect(cursor, text, selectNewText);
- m_cursors.last().endEditBlock();
+ for (auto cursor = m_cursorList.begin(); cursor != m_cursorList.end(); ++cursor)
+ insertAndSelect(*cursor, text, selectNewText);
+ m_cursorList.back().endEditBlock();
}
bool equalCursors(const QTextCursor &lhs, const QTextCursor &rhs)
@@ -177,17 +284,21 @@ bool equalCursors(const QTextCursor &lhs, const QTextCursor &rhs)
bool MultiTextCursor::operator==(const MultiTextCursor &other) const
{
- if (m_cursors.size() != other.m_cursors.size())
+ if (m_cursorList.size() != other.m_cursorList.size())
return false;
- if (m_cursors.isEmpty())
+ if (m_cursorList.empty())
return true;
- QList<QTextCursor> thisCursors = m_cursors;
- QList<QTextCursor> otherCursors = other.m_cursors;
- if (!equalCursors(thisCursors.takeLast(), otherCursors.takeLast()))
+
+ if (!equalCursors(m_cursorList.back(), other.m_cursorList.back()))
return false;
- for (const QTextCursor &oc : otherCursors) {
- auto compare = [oc](const QTextCursor &c) { return equalCursors(oc, c); };
- if (!Utils::contains(thisCursors, compare))
+
+ auto it = m_cursorMap.begin();
+ auto otherIt = other.m_cursorMap.begin();
+ for (;it != m_cursorMap.end() && otherIt != other.m_cursorMap.end(); ++it, ++otherIt) {
+ const QTextCursor &cursor = *it->second;
+ const QTextCursor &otherCursor = *otherIt->second;
+ if (it->first != otherIt->first || cursor != otherCursor
+ || cursor.anchor() != otherCursor.anchor())
return false;
}
return true;
@@ -198,70 +309,10 @@ bool MultiTextCursor::operator!=(const MultiTextCursor &other) const
return !operator==(other);
}
-static bool cursorsOverlap(const QTextCursor &c1, const QTextCursor &c2)
-{
- if (c1.hasSelection()) {
- if (c2.hasSelection()) {
- return c2.selectionEnd() > c1.selectionStart()
- && c2.selectionStart() < c1.selectionEnd();
- }
- const int c2Pos = c2.position();
- return c2Pos > c1.selectionStart() && c2Pos < c1.selectionEnd();
- }
- if (c2.hasSelection()) {
- const int c1Pos = c1.position();
- return c1Pos > c2.selectionStart() && c1Pos < c2.selectionEnd();
- }
- return c1 == c2;
-};
-
-static void mergeCursors(QTextCursor &c1, const QTextCursor &c2)
-{
- if (c1.position() == c2.position() && c1.anchor() == c2.anchor())
- return;
- if (c1.hasSelection()) {
- if (!c2.hasSelection())
- return;
- int pos = c1.position();
- int anchor = c1.anchor();
- if (c1.selectionStart() > c2.selectionStart()) {
- if (pos < anchor)
- pos = c2.selectionStart();
- else
- anchor = c2.selectionStart();
- }
- if (c1.selectionEnd() < c2.selectionEnd()) {
- if (pos < anchor)
- anchor = c2.selectionEnd();
- else
- pos = c2.selectionEnd();
- }
- c1.setPosition(anchor);
- c1.setPosition(pos, QTextCursor::KeepAnchor);
- } else {
- c1 = c2;
- }
-}
-
void MultiTextCursor::mergeCursors()
{
- std::list<QTextCursor> cursors(m_cursors.begin(), m_cursors.end());
- cursors = Utils::filtered(cursors, [](const QTextCursor &c){
- return !c.isNull();
- });
- for (auto it = cursors.begin(); it != cursors.end(); ++it) {
- QTextCursor &c1 = *it;
- for (auto other = std::next(it); other != cursors.end();) {
- const QTextCursor &c2 = *other;
- if (cursorsOverlap(c1, c2)) {
- Utils::mergeCursors(c1, c2);
- other = cursors.erase(other);
- continue;
- }
- ++other;
- }
- }
- m_cursors = QList<QTextCursor>(cursors.begin(), cursors.end());
+ QList<QTextCursor> cursors(m_cursorList.begin(), m_cursorList.end());
+ setCursors(cursors);
}
// could go into QTextCursor...
@@ -321,7 +372,7 @@ bool MultiTextCursor::handleMoveKeyEvent(QKeyEvent *e,
return false;
}
- const QList<QTextCursor> cursors = m_cursors;
+ const std::list<QTextCursor> cursors = m_cursorList;
for (QTextCursor cursor : cursors) {
if (camelCaseNavigationEnabled && op == QTextCursor::WordRight)
CamelCaseCursor::right(&cursor, edit, QTextCursor::MoveAnchor);
@@ -329,14 +380,14 @@ bool MultiTextCursor::handleMoveKeyEvent(QKeyEvent *e,
CamelCaseCursor::left(&cursor, edit, QTextCursor::MoveAnchor);
else
cursor.movePosition(op, QTextCursor::MoveAnchor);
- m_cursors << cursor;
- }
- mergeCursors();
+ addCursor(cursor);
+ }
return true;
}
- for (QTextCursor &cursor : m_cursors) {
+ for (auto it = m_cursorList.begin(); it != m_cursorList.end(); ++it) {
+ QTextCursor &cursor = *it;
QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
QTextCursor::MoveOperation op = QTextCursor::NoMove;
diff --git a/src/libs/utils/multitextcursor.h b/src/libs/utils/multitextcursor.h
index 390082e63a..dc554dd227 100644
--- a/src/libs/utils/multitextcursor.h
+++ b/src/libs/utils/multitextcursor.h
@@ -21,15 +21,22 @@ public:
MultiTextCursor();
explicit MultiTextCursor(const QList<QTextCursor> &cursors);
- /// replace all cursors with \param cursors and the last one will be the new main cursors
+ MultiTextCursor(const MultiTextCursor &multiCursor);
+ MultiTextCursor &operator=(const MultiTextCursor &multiCursor);
+ MultiTextCursor(const MultiTextCursor &&multiCursor);
+ MultiTextCursor &operator=(const MultiTextCursor &&multiCursor);
+
+ ~MultiTextCursor();
+
+ /// Replaces all cursors with \param cursors and the last one will be the new main cursors.
void setCursors(const QList<QTextCursor> &cursors);
const QList<QTextCursor> cursors() const;
- /// \returns whether this multi cursor contains any cursor
+ /// Returns whether this multi cursor contains any cursor.
bool isNull() const;
- /// \returns whether this multi cursor contains more than one cursor
+ /// Returns whether this multi cursor contains more than one cursor.
bool hasMultipleCursors() const;
- /// \returns the number of cursors handled by this cursor
+ /// Returns the number of cursors handled by this cursor.
int cursorCount() const;
/// the \param cursor that is appended by added by \brief addCursor
@@ -39,9 +46,9 @@ public:
/// convenience function that removes the old main cursor and appends
/// \param cursor as the new main cursor
void replaceMainCursor(const QTextCursor &cursor);
- /// \returns the main cursor
+ /// Returns the main cursor.
QTextCursor mainCursor() const;
- /// \returns the main cursor and removes it from this multi cursor
+ /// Returns the main cursor and removes it from this multi cursor.
QTextCursor takeMainCursor();
void beginEditBlock();
@@ -55,10 +62,10 @@ public:
/// with the move \param mode
void movePosition(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode, int n = 1);
- /// \returns whether any cursor has a selection
+ /// Returns whether any cursor has a selection.
bool hasSelection() const;
- /// \returns the selected text of all cursors that have a selection separated by
- /// a newline character
+ /// Returns the selected text of all cursors that have a selection separated by
+ /// a newline character.
QString selectedText() const;
/// removes the selected text of all cursors that have a selection from the document
void removeSelectedText();
@@ -69,20 +76,45 @@ public:
bool operator==(const MultiTextCursor &other) const;
bool operator!=(const MultiTextCursor &other) const;
- using iterator = QList<QTextCursor>::iterator;
- using const_iterator = QList<QTextCursor>::const_iterator;
-
- iterator begin() { return m_cursors.begin(); }
- iterator end() { return m_cursors.end(); }
- const_iterator begin() const { return m_cursors.begin(); }
- const_iterator end() const { return m_cursors.end(); }
- const_iterator constBegin() const { return m_cursors.constBegin(); }
- const_iterator constEnd() const { return m_cursors.constEnd(); }
+ template <typename T, typename mapit>
+ class BaseIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using difference_type = int;
+ using value_type = T;
+ using pointer = T *;
+ using reference = T &;
+ BaseIterator(const mapit &it) : internalit(it) {}
+ BaseIterator &operator++() { ++internalit; return *this; }
+ BaseIterator operator++(int) { auto result = *this; ++(*this); return result; }
+ bool operator==(BaseIterator other) const { return internalit == other.internalit; }
+ bool operator!=(BaseIterator other) const { return !(*this == other); }
+ reference operator*() const { return *(internalit->second); }
+
+ private:
+ mapit internalit;
+ };
+
+ using iterator
+ = BaseIterator<QTextCursor, std::map<int, std::list<QTextCursor>::iterator>::iterator>;
+ using const_iterator
+ = BaseIterator<const QTextCursor,
+ std::map<int, std::list<QTextCursor>::iterator>::const_iterator>;
+
+ iterator begin() { return m_cursorMap.begin(); }
+ iterator end() { return m_cursorMap.end(); }
+ const_iterator begin() const { return m_cursorMap.begin(); }
+ const_iterator end() const { return m_cursorMap.end(); }
+ const_iterator constBegin() const { return m_cursorMap.cbegin(); }
+ const_iterator constEnd() const { return m_cursorMap.cend(); }
static bool multiCursorAddEvent(QKeyEvent *e, QKeySequence::StandardKey matchKey);
private:
- QList<QTextCursor> m_cursors;
+ std::list<QTextCursor> m_cursorList;
+ std::map<int, std::list<QTextCursor>::iterator> m_cursorMap;
+
+ void fillMapWithList();
};
} // namespace Utils
diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp
index 5fd3bc39eb..adc7ff5afc 100644
--- a/src/libs/utils/namevalueitem.cpp
+++ b/src/libs/utils/namevalueitem.cpp
@@ -118,14 +118,6 @@ static QString expand(const NameValueDictionary *dictionary, QString value)
return value;
}
-enum : char {
-#ifdef Q_OS_WIN
- pathSepC = ';'
-#else
- pathSepC = ':'
-#endif
-};
-
void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
{
switch (op) {
@@ -142,7 +134,7 @@ void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
const NameValueDictionary::const_iterator it = dictionary->constFind(name);
if (it != dictionary->constEnd()) {
QString v = dictionary->value(it);
- const QChar pathSep{QLatin1Char(pathSepC)};
+ const QChar pathSep = HostOsInfo::pathListSeparator();
int sepCount = 0;
if (v.startsWith(pathSep))
++sepCount;
@@ -162,7 +154,7 @@ void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
const NameValueDictionary::const_iterator it = dictionary->constFind(name);
if (it != dictionary->constEnd()) {
QString v = dictionary->value(it);
- const QChar pathSep{QLatin1Char(pathSepC)};
+ const QChar pathSep = HostOsInfo::pathListSeparator();
int sepCount = 0;
if (v.endsWith(pathSep))
++sepCount;
diff --git a/src/libs/utils/osspecificaspects.h b/src/libs/utils/osspecificaspects.h
index 0cc22efe5f..c735f313ab 100644
--- a/src/libs/utils/osspecificaspects.h
+++ b/src/libs/utils/osspecificaspects.h
@@ -14,6 +14,36 @@ namespace Utils {
// Add more as needed.
enum OsType { OsTypeWindows, OsTypeLinux, OsTypeMac, OsTypeOtherUnix, OsTypeOther };
+inline QString osTypeToString(OsType osType)
+{
+ switch (osType) {
+ case OsTypeWindows:
+ return "Windows";
+ case OsTypeLinux:
+ return "Linux";
+ case OsTypeMac:
+ return "Mac";
+ case OsTypeOtherUnix:
+ return "Other Unix";
+ case OsTypeOther:
+ default:
+ return "Other";
+ }
+}
+
+inline OsType osTypeFromString(const QString &string)
+{
+ if (string == "Windows")
+ return OsTypeWindows;
+ if (string == "Linux")
+ return OsTypeLinux;
+ if (string == "Mac")
+ return OsTypeMac;
+ if (string == "Other Unix")
+ return OsTypeOtherUnix;
+ return OsTypeOther;
+}
+
namespace OsSpecificAspects {
inline QString withExecutableSuffix(OsType osType, const QString &executable)
diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp
index 53825931b9..1fe1e96256 100644
--- a/src/libs/utils/pathchooser.cpp
+++ b/src/libs/utils/pathchooser.cpp
@@ -10,8 +10,8 @@
#include "hostosinfo.h"
#include "macroexpander.h"
#include "optionpushbutton.h"
+#include "process.h"
#include "qtcassert.h"
-#include "qtcprocess.h"
#include "utilstr.h"
#include <QFileDialog>
@@ -130,7 +130,7 @@ QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd)
{
if (cmd.executable().isEmpty())
return QString();
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(1);
proc.setCommand(cmd);
proc.runBlocking();
@@ -171,7 +171,7 @@ public:
FilePath m_initialBrowsePathOverride;
QString m_defaultValue;
FilePath m_baseDirectory;
- EnvironmentChange m_environmentChange;
+ Environment m_environment;
BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr;
QList<QAbstractButton *> m_buttons;
const MacroExpander *m_macroExpander = globalMacroExpander();
@@ -196,8 +196,7 @@ FilePath PathChooserPrivate::expandedPath(const FilePath &input) const
FilePath path = input;
- Environment env = path.deviceEnvironment();
- m_environmentChange.applyToEnvironment(env);
+ Environment env = m_environment.appliedToEnvironment(path.deviceEnvironment());
path = env.expandVariables(path);
if (m_macroExpander)
@@ -324,20 +323,15 @@ void PathChooser::setBaseDirectory(const FilePath &base)
triggerChanged();
}
-void PathChooser::setEnvironment(const Environment &env)
-{
- setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
-}
-
FilePath PathChooser::baseDirectory() const
{
return d->m_baseDirectory;
}
-void PathChooser::setEnvironmentChange(const EnvironmentChange &env)
+void PathChooser::setEnvironment(const Environment &env)
{
QString oldExpand = filePath().toString();
- d->m_environmentChange = env;
+ d->m_environment = env;
if (filePath().toString() != oldExpand) {
triggerChanged();
emit rawPathChanged();
@@ -619,8 +613,8 @@ bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const
*errorMessage = Tr::tr("The path \"%1\" is not a directory.").arg(filePath.toUserOutput());
return false;
}
- if (HostOsInfo::isWindowsHost() && !filePath.startsWithDriveLetter()
- && !filePath.startsWith("\\\\") && !filePath.startsWith("//")) {
+ if (filePath.osType() == OsTypeWindows && !filePath.startsWithDriveLetter()
+ && !filePath.startsWith("\\\\") && !filePath.startsWith("//")) {
if (errorMessage)
*errorMessage = Tr::tr("Invalid path \"%1\".").arg(filePath.toUserOutput());
return false;
diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h
index 92b5973b2d..62a370185d 100644
--- a/src/libs/utils/pathchooser.h
+++ b/src/libs/utils/pathchooser.h
@@ -20,7 +20,6 @@ namespace Utils {
class CommandLine;
class MacroExpander;
class Environment;
-class EnvironmentChange;
class PathChooserPrivate;
class QTCREATOR_UTILS_EXPORT PathChooser : public QWidget
@@ -77,7 +76,6 @@ public:
void setBaseDirectory(const FilePath &base);
void setEnvironment(const Environment &env);
- void setEnvironmentChange(const EnvironmentChange &change);
/** Returns the suggested label title when used in a form layout. */
static QString label();
diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp
index 5d7a35db3a..1db834bff7 100644
--- a/src/libs/utils/persistentsettings.cpp
+++ b/src/libs/utils/persistentsettings.cpp
@@ -336,11 +336,17 @@ bool PersistentSettingsReader::load(const FilePath &fileName)
if (fileName.fileSize() == 0) // skip empty files
return false;
+ m_filePath = fileName.parentDir();
ParseContext ctx;
m_valueMap = ctx.parse(fileName);
return true;
}
+FilePath PersistentSettingsReader::filePath()
+{
+ return m_filePath;
+}
+
/*!
\class Utils::PersistentSettingsWriter
diff --git a/src/libs/utils/persistentsettings.h b/src/libs/utils/persistentsettings.h
index 66a006ab22..7e07f0237c 100644
--- a/src/libs/utils/persistentsettings.h
+++ b/src/libs/utils/persistentsettings.h
@@ -22,9 +22,11 @@ public:
QVariant restoreValue(const QString &variable, const QVariant &defaultValue = QVariant()) const;
QVariantMap restoreValues() const;
bool load(const FilePath &fileName);
+ FilePath filePath();
private:
QMap<QString, QVariant> m_valueMap;
+ FilePath m_filePath;
};
class QTCREATOR_UTILS_EXPORT PersistentSettingsWriter
diff --git a/src/libs/utils/port.cpp b/src/libs/utils/port.cpp
index 3f9166a1a9..c41a65335d 100644
--- a/src/libs/utils/port.cpp
+++ b/src/libs/utils/port.cpp
@@ -6,6 +6,8 @@
#include "qtcassert.h"
#include "stringutils.h"
+#include <QRegularExpression>
+
#include <limits>
/*! \class Utils::Port
@@ -31,27 +33,7 @@ quint16 Port::number() const
QTC_ASSERT(isValid(), return -1); return quint16(m_port);
}
-QList<Port> Port::parseFromSedOutput(const QByteArray &output)
-{
- QList<Port> ports;
- const QList<QByteArray> portStrings = output.split('\n');
- for (const QByteArray &portString : portStrings) {
- if (portString.size() != 4)
- continue;
- bool ok;
- const Port port(portString.toInt(&ok, 16));
- if (ok) {
- if (!ports.contains(port))
- ports << port;
- } else {
- qWarning("%s: Unexpected string '%s' is not a port.",
- Q_FUNC_INFO, portString.data());
- }
- }
- return ports;
-}
-
-QList<Port> Port::parseFromNetstatOutput(const QByteArray &output)
+QList<Port> Port::parseFromCommandOutput(const QByteArray &output)
{
QList<Port> ports;
const QList<QByteArray> lines = output.split('\n');
diff --git a/src/libs/utils/port.h b/src/libs/utils/port.h
index 851b41ceaf..c4b46631de 100644
--- a/src/libs/utils/port.h
+++ b/src/libs/utils/port.h
@@ -24,8 +24,8 @@ public:
QString toString() const { return QString::number(m_port); }
- static QList<Port> parseFromSedOutput(const QByteArray &output);
- static QList<Port> parseFromNetstatOutput(const QByteArray &output);
+ // Parses the output of "netstat -an" and "cat /proc/net/tcp"
+ static QList<Port> parseFromCommandOutput(const QByteArray &output);
friend bool operator<(const Port &p1, const Port &p2) { return p1.number() < p2.number(); }
friend bool operator<=(const Port &p1, const Port &p2) { return p1.number() <= p2.number(); }
diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/process.cpp
index 74bd28560b..f25bc3d3fd 100644
--- a/src/libs/utils/qtcprocess.cpp
+++ b/src/libs/utils/process.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "qtcprocess.h"
+#include "process.h"
#include "algorithm.h"
#include "environment.h"
@@ -12,10 +12,13 @@
#include "processreaper.h"
#include "processutils.h"
#include "stringutils.h"
-#include "terminalprocess_p.h"
+#include "terminalhooks.h"
#include "threadutils.h"
#include "utilstr.h"
+#include <iptyprocess.h>
+#include <ptyqt.h>
+
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
@@ -168,9 +171,9 @@ enum { syncDebug = 0 };
enum { defaultMaxHangTimerCount = 10 };
-static Q_LOGGING_CATEGORY(processLog, "qtc.utils.qtcprocess", QtWarningMsg)
-static Q_LOGGING_CATEGORY(processStdoutLog, "qtc.utils.qtcprocess.stdout", QtWarningMsg)
-static Q_LOGGING_CATEGORY(processStderrLog, "qtc.utils.qtcprocess.stderr", QtWarningMsg)
+static Q_LOGGING_CATEGORY(processLog, "qtc.utils.process", QtWarningMsg)
+static Q_LOGGING_CATEGORY(processStdoutLog, "qtc.utils.process.stdout", QtWarningMsg)
+static Q_LOGGING_CATEGORY(processStderrLog, "qtc.utils.process.stderr", QtWarningMsg)
static DeviceProcessHooks s_deviceHooks;
@@ -304,6 +307,107 @@ private:
QProcess *m_process = nullptr;
};
+class PtyProcessImpl final : public DefaultImpl
+{
+public:
+ ~PtyProcessImpl() { QTC_CHECK(m_setup.m_ptyData); m_setup.m_ptyData->setResizeHandler({}); }
+
+ qint64 write(const QByteArray &data) final
+ {
+ if (m_ptyProcess)
+ return m_ptyProcess->write(data);
+ return -1;
+ }
+
+ void sendControlSignal(ControlSignal controlSignal) final
+ {
+ if (!m_ptyProcess)
+ return;
+
+ switch (controlSignal) {
+ case ControlSignal::Terminate:
+ m_ptyProcess.reset();
+ break;
+ case ControlSignal::Kill:
+ m_ptyProcess->kill();
+ break;
+ default:
+ QTC_CHECK(false);
+ }
+ }
+
+ void doDefaultStart(const QString &program, const QStringList &arguments) final
+ {
+ QTC_CHECK(m_setup.m_ptyData);
+ m_setup.m_ptyData->setResizeHandler([this](const QSize &size) {
+ if (m_ptyProcess)
+ m_ptyProcess->resize(size.width(), size.height());
+ });
+ m_ptyProcess.reset(PtyQt::createPtyProcess(IPtyProcess::AutoPty));
+ if (!m_ptyProcess) {
+ const ProcessResultData result = {-1,
+ QProcess::CrashExit,
+ QProcess::FailedToStart,
+ "Failed to create pty process"};
+ emit done(result);
+ return;
+ }
+
+ QProcessEnvironment penv = m_setup.m_environment.toProcessEnvironment();
+ if (penv.isEmpty())
+ penv = Environment::systemEnvironment().toProcessEnvironment();
+ const QStringList senv = penv.toStringList();
+
+ bool startResult
+ = m_ptyProcess->startProcess(program,
+ HostOsInfo::isWindowsHost()
+ ? QStringList{m_setup.m_nativeArguments} << arguments
+ : arguments,
+ m_setup.m_workingDirectory.nativePath(),
+ senv,
+ m_setup.m_ptyData->size().width(),
+ m_setup.m_ptyData->size().height());
+
+ if (!startResult) {
+ const ProcessResultData result = {-1,
+ QProcess::CrashExit,
+ QProcess::FailedToStart,
+ "Failed to start pty process: "
+ + m_ptyProcess->lastError()};
+ emit done(result);
+ return;
+ }
+
+ if (!m_ptyProcess->lastError().isEmpty()) {
+ const ProcessResultData result
+ = {-1, QProcess::CrashExit, QProcess::FailedToStart, m_ptyProcess->lastError()};
+ emit done(result);
+ return;
+ }
+
+ connect(m_ptyProcess->notifier(), &QIODevice::readyRead, this, [this] {
+ emit readyRead(m_ptyProcess->readAll(), {});
+ });
+
+ connect(m_ptyProcess->notifier(), &QIODevice::aboutToClose, this, [this] {
+ if (m_ptyProcess) {
+ const ProcessResultData result
+ = {m_ptyProcess->exitCode(), QProcess::NormalExit, QProcess::UnknownError, {}};
+ emit done(result);
+ return;
+ }
+
+ const ProcessResultData result = {0, QProcess::NormalExit, QProcess::UnknownError, {}};
+ emit done(result);
+ });
+
+ emit started(m_ptyProcess->pid());
+ }
+
+private:
+ std::unique_ptr<IPtyProcess> m_ptyProcess;
+};
+
class QProcessImpl final : public DefaultImpl
{
public:
@@ -350,15 +454,18 @@ private:
void doDefaultStart(const QString &program, const QStringList &arguments) final
{
QTC_ASSERT(QThread::currentThread()->eventDispatcher(),
- qWarning("QtcProcess::start(): Starting a process in a non QThread thread "
+ qWarning("Process::start(): Starting a process in a non QThread thread "
"may cause infinite hang when destroying the running process."));
ProcessStartHandler *handler = m_process->processStartHandler();
handler->setProcessMode(m_setup.m_processMode);
handler->setWriteData(m_setup.m_writeData);
- if (m_setup.m_belowNormalPriority)
- handler->setBelowNormalPriority();
handler->setNativeArguments(m_setup.m_nativeArguments);
- m_process->setProcessEnvironment(m_setup.m_environment.toProcessEnvironment());
+ handler->setWindowsSpecificStartupFlags(m_setup.m_belowNormalPriority,
+ m_setup.m_createConsoleOnWindows);
+
+ const QProcessEnvironment penv = m_setup.m_environment.toProcessEnvironment();
+ if (!penv.isEmpty())
+ m_process->setProcessEnvironment(penv);
m_process->setWorkingDirectory(m_setup.m_workingDirectory.path());
m_process->setStandardInputFile(m_setup.m_standardInputFile);
m_process->setProcessChannelMode(m_setup.m_processChannelMode);
@@ -577,7 +684,7 @@ private:
class GeneralProcessBlockingImpl : public ProcessBlockingInterface
{
public:
- GeneralProcessBlockingImpl(QtcProcessPrivate *parent);
+ GeneralProcessBlockingImpl(ProcessPrivate *parent);
void flush() { flushSignals(takeAllSignals()); }
bool flushFor(ProcessSignalType signalType) {
@@ -601,21 +708,20 @@ private:
void handleReadyReadSignal(const ReadyReadSignal *launcherSignal);
void handleDoneSignal(const DoneSignal *launcherSignal);
- QtcProcessPrivate *m_caller = nullptr;
+ ProcessPrivate *m_caller = nullptr;
std::unique_ptr<ProcessInterfaceHandler> m_processHandler;
mutable QMutex m_mutex;
QList<ProcessInterfaceSignal *> m_signals;
};
-class QtcProcessPrivate : public QObject
+class ProcessPrivate : public QObject
{
public:
- explicit QtcProcessPrivate(QtcProcess *parent)
+ explicit ProcessPrivate(Process *parent)
: QObject(parent)
, q(parent)
, m_killTimer(this)
{
- m_setup.m_controlEnvironment = Environment::systemEnvironment();
m_killTimer.setSingleShot(true);
connect(&m_killTimer, &QTimer::timeout, this, [this] {
m_killTimer.stop();
@@ -629,14 +735,16 @@ public:
ProcessInterface *createProcessInterface()
{
+ if (m_setup.m_ptyData)
+ return new PtyProcessImpl;
if (m_setup.m_terminalMode != TerminalMode::Off)
- return new TerminalImpl();
+ return Terminal::Hooks::instance().createTerminalProcessInterface();
const ProcessImpl impl = m_setup.m_processImpl == ProcessImpl::Default
? defaultProcessImpl() : m_setup.m_processImpl;
if (impl == ProcessImpl::QProcess)
- return new QProcessImpl();
- return new ProcessLauncherImpl();
+ return new QProcessImpl;
+ return new ProcessLauncherImpl;
}
void setProcessInterface(ProcessInterface *process)
@@ -646,11 +754,11 @@ public:
m_process.reset(process);
m_process->setParent(this);
connect(m_process.get(), &ProcessInterface::started,
- this, &QtcProcessPrivate::handleStarted);
+ this, &ProcessPrivate::handleStarted);
connect(m_process.get(), &ProcessInterface::readyRead,
- this, &QtcProcessPrivate::handleReadyRead);
+ this, &ProcessPrivate::handleReadyRead);
connect(m_process.get(), &ProcessInterface::done,
- this, &QtcProcessPrivate::handleDone);
+ this, &ProcessPrivate::handleDone);
m_blockingInterface.reset(process->processBlockingInterface());
if (!m_blockingInterface)
@@ -667,23 +775,7 @@ public:
return rootCommand;
}
- Environment fullEnvironment() const
- {
- Environment env = m_setup.m_environment;
- if (!env.hasChanges() && env.combineWithDeviceEnvironment()) {
- // FIXME: Either switch to using EnvironmentChange instead of full Environments, or
- // feed the full environment into the QtcProcess instead of fixing it up here.
- // qWarning("QtcProcess::start: Empty environment set when running '%s'.",
- // qPrintable(m_setup.m_commandLine.executable().toString()));
- env = m_setup.m_commandLine.executable().deviceEnvironment();
- }
- // TODO: needs SshSettings
- // if (m_runAsRoot)
- // RunControl::provideAskPassEntry(env);
- return env;
- }
-
- QtcProcess *q;
+ Process *q;
std::unique_ptr<ProcessBlockingInterface> m_blockingInterface;
std::unique_ptr<ProcessInterface> m_process;
ProcessSetupData m_setup;
@@ -694,7 +786,7 @@ public:
void handleDone(const ProcessResultData &data);
void clearForRun();
- void emitGuardedSignal(void (QtcProcess::* signalName)()) {
+ void emitGuardedSignal(void (Process::* signalName)()) {
GuardLocker locker(m_guard);
emit (q->*signalName)();
}
@@ -807,7 +899,7 @@ void ProcessInterfaceHandler::appendSignal(ProcessInterfaceSignal *newSignal)
QMetaObject::invokeMethod(m_caller, &GeneralProcessBlockingImpl::flush);
}
-GeneralProcessBlockingImpl::GeneralProcessBlockingImpl(QtcProcessPrivate *parent)
+GeneralProcessBlockingImpl::GeneralProcessBlockingImpl(ProcessPrivate *parent)
: m_caller(parent)
, m_processHandler(new ProcessInterfaceHandler(this, parent->m_process.get()))
{
@@ -815,7 +907,7 @@ GeneralProcessBlockingImpl::GeneralProcessBlockingImpl(QtcProcessPrivate *parent
parent->m_process.get()->setParent(m_processHandler.get());
m_processHandler->setParent(this);
// So the hierarchy looks like:
- // QtcProcessPrivate
+ // ProcessPrivate
// |
// +- GeneralProcessBlockingImpl
// |
@@ -929,7 +1021,7 @@ void GeneralProcessBlockingImpl::appendSignal(ProcessInterfaceSignal *newSignal)
m_signals.append(newSignal);
}
-bool QtcProcessPrivate::waitForSignal(ProcessSignalType newSignal, int msecs)
+bool ProcessPrivate::waitForSignal(ProcessSignalType newSignal, int msecs)
{
const QDeadlineTimer timeout(msecs);
const QDeadlineTimer currentKillTimeout(m_killTimer.remainingTime());
@@ -945,13 +1037,13 @@ bool QtcProcessPrivate::waitForSignal(ProcessSignalType newSignal, int msecs)
return result;
}
-Qt::ConnectionType QtcProcessPrivate::connectionType() const
+Qt::ConnectionType ProcessPrivate::connectionType() const
{
return (m_process->thread() == thread()) ? Qt::DirectConnection
: Qt::BlockingQueuedConnection;
}
-void QtcProcessPrivate::sendControlSignal(ControlSignal controlSignal)
+void ProcessPrivate::sendControlSignal(ControlSignal controlSignal)
{
QTC_ASSERT(QThread::currentThread() == thread(), return);
if (!m_process || (m_state == QProcess::NotRunning))
@@ -965,7 +1057,7 @@ void QtcProcessPrivate::sendControlSignal(ControlSignal controlSignal)
}, connectionType());
}
-void QtcProcessPrivate::clearForRun()
+void ProcessPrivate::clearForRun()
{
m_hangTimerCount = 0;
m_stdOut.clearForRun();
@@ -981,7 +1073,7 @@ void QtcProcessPrivate::clearForRun()
m_resultData = {};
}
-ProcessResult QtcProcessPrivate::interpretExitCode(int exitCode)
+ProcessResult ProcessPrivate::interpretExitCode(int exitCode)
{
if (m_exitCodeInterpreter)
return m_exitCodeInterpreter(exitCode);
@@ -993,16 +1085,16 @@ ProcessResult QtcProcessPrivate::interpretExitCode(int exitCode)
} // Internal
/*!
- \class Utils::QtcProcess
+ \class Utils::Process
- \brief The QtcProcess class provides functionality for with processes.
+ \brief The Process class provides functionality for with processes.
\sa Utils::ProcessArgs
*/
-QtcProcess::QtcProcess(QObject *parent)
+Process::Process(QObject *parent)
: QObject(parent),
- d(new QtcProcessPrivate(this))
+ d(new ProcessPrivate(this))
{
qRegisterMetaType<ProcessResultData>("ProcessResultData");
static int qProcessExitStatusMeta = qRegisterMetaType<QProcess::ExitStatus>();
@@ -1011,61 +1103,71 @@ QtcProcess::QtcProcess(QObject *parent)
Q_UNUSED(qProcessProcessErrorMeta)
}
-QtcProcess::~QtcProcess()
+Process::~Process()
{
- QTC_ASSERT(!d->m_guard.isLocked(), qWarning("Deleting QtcProcess instance directly from "
+ QTC_ASSERT(!d->m_guard.isLocked(), qWarning("Deleting Process instance directly from "
"one of its signal handlers will lead to crash!"));
if (d->m_process)
d->m_process->disconnect();
delete d;
}
-void QtcProcess::setProcessImpl(ProcessImpl processImpl)
+void Process::setProcessImpl(ProcessImpl processImpl)
{
d->m_setup.m_processImpl = processImpl;
}
-ProcessMode QtcProcess::processMode() const
+void Process::setPtyData(const std::optional<Pty::Data> &data)
+{
+ d->m_setup.m_ptyData = data;
+}
+
+std::optional<Pty::Data> Process::ptyData() const
+{
+ return d->m_setup.m_ptyData;
+}
+
+ProcessMode Process::processMode() const
{
return d->m_setup.m_processMode;
}
-void QtcProcess::setTerminalMode(TerminalMode mode)
+void Process::setTerminalMode(TerminalMode mode)
{
d->m_setup.m_terminalMode = mode;
}
-TerminalMode QtcProcess::terminalMode() const
+TerminalMode Process::terminalMode() const
{
return d->m_setup.m_terminalMode;
}
-void QtcProcess::setProcessMode(ProcessMode processMode)
+void Process::setProcessMode(ProcessMode processMode)
{
d->m_setup.m_processMode = processMode;
}
-void QtcProcess::setEnvironment(const Environment &env)
+void Process::setEnvironment(const Environment &env)
{
d->m_setup.m_environment = env;
}
-const Environment &QtcProcess::environment() const
+const Environment &Process::environment() const
{
return d->m_setup.m_environment;
}
-void QtcProcess::setControlEnvironment(const Environment &environment)
+void Process::setControlEnvironment(const Environment &environment)
{
d->m_setup.m_controlEnvironment = environment;
}
-const Environment &QtcProcess::controlEnvironment() const
+const Environment &Process::controlEnvironment() const
{
return d->m_setup.m_controlEnvironment;
}
-void QtcProcess::setCommand(const CommandLine &cmdLine)
+void Process::setCommand(const CommandLine &cmdLine)
{
if (d->m_setup.m_workingDirectory.needsDevice() && cmdLine.executable().needsDevice()) {
QTC_CHECK(d->m_setup.m_workingDirectory.host() == cmdLine.executable().host());
@@ -1073,17 +1175,17 @@ void QtcProcess::setCommand(const CommandLine &cmdLine)
d->m_setup.m_commandLine = cmdLine;
}
-const CommandLine &QtcProcess::commandLine() const
+const CommandLine &Process::commandLine() const
{
return d->m_setup.m_commandLine;
}
-FilePath QtcProcess::workingDirectory() const
+FilePath Process::workingDirectory() const
{
return d->m_setup.m_workingDirectory;
}
-void QtcProcess::setWorkingDirectory(const FilePath &dir)
+void Process::setWorkingDirectory(const FilePath &dir)
{
if (dir.needsDevice() && d->m_setup.m_commandLine.executable().needsDevice()) {
QTC_CHECK(dir.host() == d->m_setup.m_commandLine.executable().host());
@@ -1091,16 +1193,16 @@ void QtcProcess::setWorkingDirectory(const FilePath &dir)
d->m_setup.m_workingDirectory = dir;
}
-void QtcProcess::setUseCtrlCStub(bool enabled)
+void Process::setUseCtrlCStub(bool enabled)
{
d->m_setup.m_useCtrlCStub = enabled;
}
-void QtcProcess::start()
+void Process::start()
{
QTC_ASSERT(state() == QProcess::NotRunning, return);
QTC_ASSERT(!(d->m_process && d->m_guard.isLocked()),
- qWarning("Restarting the QtcProcess directly from one of its signal handlers will "
+ qWarning("Restarting the Process directly from one of its signal handlers will "
"lead to crash! Consider calling close() prior to direct restart."));
d->clearForRun();
ProcessInterface *processImpl = nullptr;
@@ -1115,37 +1217,36 @@ void QtcProcess::start()
d->m_state = QProcess::Starting;
d->m_process->m_setup = d->m_setup;
d->m_process->m_setup.m_commandLine = d->fullCommandLine();
- d->m_process->m_setup.m_environment = d->fullEnvironment();
- d->emitGuardedSignal(&QtcProcess::starting);
+ d->emitGuardedSignal(&Process::starting);
d->m_process->start();
}
-void QtcProcess::terminate()
+void Process::terminate()
{
d->sendControlSignal(ControlSignal::Terminate);
}
-void QtcProcess::kill()
+void Process::kill()
{
d->sendControlSignal(ControlSignal::Kill);
}
-void QtcProcess::interrupt()
+void Process::interrupt()
{
d->sendControlSignal(ControlSignal::Interrupt);
}
-void QtcProcess::kickoffProcess()
+void Process::kickoffProcess()
{
d->sendControlSignal(ControlSignal::KickOff);
}
-void QtcProcess::closeWriteChannel()
+void Process::closeWriteChannel()
{
d->sendControlSignal(ControlSignal::CloseWriteChannel);
}
-bool QtcProcess::startDetached(const CommandLine &cmd, const FilePath &workingDirectory, qint64 *pid)
+bool Process::startDetached(const CommandLine &cmd, const FilePath &workingDirectory, qint64 *pid)
{
return QProcess::startDetached(cmd.executable().toUserOutput(),
cmd.splitArguments(),
@@ -1153,37 +1254,37 @@ bool QtcProcess::startDetached(const CommandLine &cmd, const FilePath &workingDi
pid);
}
-void QtcProcess::setLowPriority()
+void Process::setLowPriority()
{
d->m_setup.m_lowPriority = true;
}
-void QtcProcess::setDisableUnixTerminal()
+void Process::setDisableUnixTerminal()
{
d->m_setup.m_unixTerminalDisabled = true;
}
-void QtcProcess::setAbortOnMetaChars(bool abort)
+void Process::setAbortOnMetaChars(bool abort)
{
d->m_setup.m_abortOnMetaChars = abort;
}
-void QtcProcess::setRunAsRoot(bool on)
+void Process::setRunAsRoot(bool on)
{
d->m_setup.m_runAsRoot = on;
}
-bool QtcProcess::isRunAsRoot() const
+bool Process::isRunAsRoot() const
{
return d->m_setup.m_runAsRoot;
}
-void QtcProcess::setStandardInputFile(const QString &inputFile)
+void Process::setStandardInputFile(const QString &inputFile)
{
d->m_setup.m_standardInputFile = inputFile;
}
-QString QtcProcess::toStandaloneCommandLine() const
+QString Process::toStandaloneCommandLine() const
{
QStringList parts;
parts.append("/usr/bin/env");
@@ -1202,37 +1303,47 @@ QString QtcProcess::toStandaloneCommandLine() const
return parts.join(" ");
}
-void QtcProcess::setExtraData(const QString &key, const QVariant &value)
+void Process::setCreateConsoleOnWindows(bool create)
+{
+ d->m_setup.m_createConsoleOnWindows = create;
+}
+
+bool Process::createConsoleOnWindows() const
+{
+ return d->m_setup.m_createConsoleOnWindows;
+}
+
+void Process::setExtraData(const QString &key, const QVariant &value)
{
d->m_setup.m_extraData.insert(key, value);
}
-QVariant QtcProcess::extraData(const QString &key) const
+QVariant Process::extraData(const QString &key) const
{
return d->m_setup.m_extraData.value(key);
}
-void QtcProcess::setExtraData(const QVariantHash &extraData)
+void Process::setExtraData(const QVariantHash &extraData)
{
d->m_setup.m_extraData = extraData;
}
-QVariantHash QtcProcess::extraData() const
+QVariantHash Process::extraData() const
{
return d->m_setup.m_extraData;
}
-void QtcProcess::setReaperTimeout(int msecs)
+void Process::setReaperTimeout(int msecs)
{
d->m_setup.m_reaperTimeout = msecs;
}
-int QtcProcess::reaperTimeout() const
+int Process::reaperTimeout() const
{
return d->m_setup.m_reaperTimeout;
}
-void QtcProcess::setRemoteProcessHooks(const DeviceProcessHooks &hooks)
+void Process::setRemoteProcessHooks(const DeviceProcessHooks &hooks)
{
s_deviceHooks = hooks;
}
@@ -1267,7 +1378,7 @@ static bool askToKill(const CommandLine &command)
// occurs on stderr/stdout as opposed to waitForFinished()). Returns false if a timeout
// occurs. Checking of the process' exit state/code still has to be done.
-bool QtcProcess::readDataFromProcess(QByteArray *stdOut, QByteArray *stdErr, int timeoutS)
+bool Process::readDataFromProcess(QByteArray *stdOut, QByteArray *stdErr, int timeoutS)
{
enum { syncDebug = 0 };
if (syncDebug)
@@ -1307,39 +1418,39 @@ bool QtcProcess::readDataFromProcess(QByteArray *stdOut, QByteArray *stdErr, int
return finished;
}
-ProcessResult QtcProcess::result() const
+ProcessResult Process::result() const
{
return d->m_result;
}
-ProcessResultData QtcProcess::resultData() const
+ProcessResultData Process::resultData() const
{
return d->m_resultData;
}
-int QtcProcess::exitCode() const
+int Process::exitCode() const
{
return resultData().m_exitCode;
}
-QProcess::ExitStatus QtcProcess::exitStatus() const
+QProcess::ExitStatus Process::exitStatus() const
{
return resultData().m_exitStatus;
}
-QProcess::ProcessError QtcProcess::error() const
+QProcess::ProcessError Process::error() const
{
return resultData().m_error;
}
-QString QtcProcess::errorString() const
+QString Process::errorString() const
{
return resultData().m_errorString;
}
// Path utilities
-Environment QtcProcess::systemEnvironmentForBinary(const FilePath &filePath)
+Environment Process::systemEnvironmentForBinary(const FilePath &filePath)
{
if (filePath.needsDevice()) {
QTC_ASSERT(s_deviceHooks.systemEnvironmentForBinary, return {});
@@ -1349,48 +1460,48 @@ Environment QtcProcess::systemEnvironmentForBinary(const FilePath &filePath)
return Environment::systemEnvironment();
}
-qint64 QtcProcess::applicationMainThreadId() const
+qint64 Process::applicationMainThreadId() const
{
return d->m_applicationMainThreadId;
}
-QProcess::ProcessChannelMode QtcProcess::processChannelMode() const
+QProcess::ProcessChannelMode Process::processChannelMode() const
{
return d->m_setup.m_processChannelMode;
}
-void QtcProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode)
+void Process::setProcessChannelMode(QProcess::ProcessChannelMode mode)
{
d->m_setup.m_processChannelMode = mode;
}
-QProcess::ProcessState QtcProcess::state() const
+QProcess::ProcessState Process::state() const
{
return d->m_state;
}
-bool QtcProcess::isRunning() const
+bool Process::isRunning() const
{
return state() == QProcess::Running;
}
-qint64 QtcProcess::processId() const
+qint64 Process::processId() const
{
return d->m_processId;
}
-bool QtcProcess::waitForStarted(int msecs)
+bool Process::waitForStarted(int msecs)
{
QTC_ASSERT(d->m_process, return false);
if (d->m_state == QProcess::Running)
return true;
if (d->m_state == QProcess::NotRunning)
return false;
- return s_waitForStarted.measureAndRun(&QtcProcessPrivate::waitForSignal, d,
+ return s_waitForStarted.measureAndRun(&ProcessPrivate::waitForSignal, d,
ProcessSignalType::Started, msecs);
}
-bool QtcProcess::waitForReadyRead(int msecs)
+bool Process::waitForReadyRead(int msecs)
{
QTC_ASSERT(d->m_process, return false);
if (d->m_state == QProcess::NotRunning)
@@ -1398,7 +1509,7 @@ bool QtcProcess::waitForReadyRead(int msecs)
return d->waitForSignal(ProcessSignalType::ReadyRead, msecs);
}
-bool QtcProcess::waitForFinished(int msecs)
+bool Process::waitForFinished(int msecs)
{
QTC_ASSERT(d->m_process, return false);
if (d->m_state == QProcess::NotRunning)
@@ -1406,17 +1517,17 @@ bool QtcProcess::waitForFinished(int msecs)
return d->waitForSignal(ProcessSignalType::Done, msecs);
}
-QByteArray QtcProcess::readAllRawStandardOutput()
+QByteArray Process::readAllRawStandardOutput()
{
return d->m_stdOut.readAllData();
}
-QByteArray QtcProcess::readAllRawStandardError()
+QByteArray Process::readAllRawStandardError()
{
return d->m_stdErr.readAllData();
}
-qint64 QtcProcess::write(const QString &input)
+qint64 Process::write(const QString &input)
{
// Non-windows is assumed to be UTF-8
if (commandLine().executable().osType() != OsTypeWindows)
@@ -1431,7 +1542,7 @@ qint64 QtcProcess::write(const QString &input)
return writeRaw(input.toUtf8());
}
-qint64 QtcProcess::writeRaw(const QByteArray &input)
+qint64 Process::writeRaw(const QByteArray &input)
{
QTC_ASSERT(processMode() == ProcessMode::Writer, return -1);
QTC_ASSERT(d->m_process, return -1);
@@ -1444,7 +1555,7 @@ qint64 QtcProcess::writeRaw(const QByteArray &input)
return result;
}
-void QtcProcess::close()
+void Process::close()
{
QTC_ASSERT(QThread::currentThread() == thread(), return);
if (d->m_process) {
@@ -1464,7 +1575,7 @@ void QtcProcess::close()
Calls terminate() directly and after a delay of reaperTimeout() it calls kill()
if the process is still running.
*/
-void QtcProcess::stop()
+void Process::stop()
{
if (state() == QProcess::NotRunning)
return;
@@ -1473,12 +1584,12 @@ void QtcProcess::stop()
d->m_killTimer.start(d->m_process->m_setup.m_reaperTimeout);
}
-QString QtcProcess::readAllStandardOutput()
+QString Process::readAllStandardOutput()
{
return QString::fromUtf8(readAllRawStandardOutput());
}
-QString QtcProcess::readAllStandardError()
+QString Process::readAllStandardError()
{
return QString::fromUtf8(readAllRawStandardError());
}
@@ -1513,7 +1624,7 @@ QString QtcProcess::readAllStandardError()
as this will cause event loop problems.
*/
-QString QtcProcess::exitMessage() const
+QString Process::exitMessage() const
{
const QString fullCmd = commandLine().toUserOutput();
switch (result()) {
@@ -1533,7 +1644,7 @@ QString QtcProcess::exitMessage() const
return {};
}
-QByteArray QtcProcess::allRawOutput() const
+QByteArray Process::allRawOutput() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
QTC_CHECK(d->m_stdErr.keepRawData);
@@ -1547,7 +1658,7 @@ QByteArray QtcProcess::allRawOutput() const
return !d->m_stdOut.rawData.isEmpty() ? d->m_stdOut.rawData : d->m_stdErr.rawData;
}
-QString QtcProcess::allOutput() const
+QString Process::allOutput() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
QTC_CHECK(d->m_stdErr.keepRawData);
@@ -1564,30 +1675,30 @@ QString QtcProcess::allOutput() const
return !out.isEmpty() ? out : err;
}
-QByteArray QtcProcess::rawStdOut() const
+QByteArray Process::rawStdOut() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
return d->m_stdOut.rawData;
}
-QString QtcProcess::stdOut() const
+QString Process::stdOut() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
return d->m_codec->toUnicode(d->m_stdOut.rawData);
}
-QString QtcProcess::stdErr() const
+QString Process::stdErr() const
{
QTC_CHECK(d->m_stdErr.keepRawData);
return d->m_codec->toUnicode(d->m_stdErr.rawData);
}
-QString QtcProcess::cleanedStdOut() const
+QString Process::cleanedStdOut() const
{
return Utils::normalizeNewlines(stdOut());
}
-QString QtcProcess::cleanedStdErr() const
+QString Process::cleanedStdErr() const
{
return Utils::normalizeNewlines(stdErr());
}
@@ -1602,20 +1713,20 @@ static QStringList splitLines(const QString &text)
return result;
}
-const QStringList QtcProcess::stdOutLines() const
+const QStringList Process::stdOutLines() const
{
return splitLines(cleanedStdOut());
}
-const QStringList QtcProcess::stdErrLines() const
+const QStringList Process::stdErrLines() const
{
return splitLines(cleanedStdErr());
}
-QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const QtcProcess &r)
+QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const Process &r)
{
QDebug nsp = str.nospace();
- nsp << "QtcProcess: result="
+ nsp << "Process: result="
<< int(r.d->m_result) << " ex=" << r.exitCode() << '\n'
<< r.d->m_stdOut.rawData.size() << " bytes stdout, stderr=" << r.d->m_stdErr.rawData << '\n';
return str;
@@ -1691,7 +1802,7 @@ void ChannelBuffer::handleRest()
}
}
-void QtcProcess::setTimeoutS(int timeoutS)
+void Process::setTimeoutS(int timeoutS)
{
if (timeoutS > 0)
d->m_maxHangTimerCount = qMax(2, timeoutS);
@@ -1699,33 +1810,33 @@ void QtcProcess::setTimeoutS(int timeoutS)
d->m_maxHangTimerCount = INT_MAX / 1000;
}
-int QtcProcess::timeoutS() const
+int Process::timeoutS() const
{
return d->m_maxHangTimerCount;
}
-void QtcProcess::setCodec(QTextCodec *c)
+void Process::setCodec(QTextCodec *c)
{
QTC_ASSERT(c, return);
d->m_codec = c;
}
-void QtcProcess::setTimeOutMessageBoxEnabled(bool v)
+void Process::setTimeOutMessageBoxEnabled(bool v)
{
d->m_timeOutMessageBoxEnabled = v;
}
-void QtcProcess::setExitCodeInterpreter(const ExitCodeInterpreter &interpreter)
+void Process::setExitCodeInterpreter(const ExitCodeInterpreter &interpreter)
{
d->m_exitCodeInterpreter = interpreter;
}
-void QtcProcess::setWriteData(const QByteArray &writeData)
+void Process::setWriteData(const QByteArray &writeData)
{
d->m_setup.m_writeData = writeData;
}
-void QtcProcess::runBlocking(EventLoopMode eventLoopMode)
+void Process::runBlocking(EventLoopMode eventLoopMode)
{
// Attach a dynamic property with info about blocking type
d->storeEventLoopDebugInfo(int(eventLoopMode));
@@ -1734,7 +1845,7 @@ void QtcProcess::runBlocking(EventLoopMode eventLoopMode)
static const int blockingThresholdMs = qtcEnvironmentVariableIntValue("QTC_PROCESS_THRESHOLD");
if (blockingThresholdMs > 0 && isMainThread())
startTime = QDateTime::currentDateTime();
- QtcProcess::start();
+ Process::start();
// Remove the dynamic property so that it's not reused in subseqent start()
d->storeEventLoopDebugInfo({});
@@ -1745,7 +1856,7 @@ void QtcProcess::runBlocking(EventLoopMode eventLoopMode)
// Do not start the event loop in that case.
if (state() == QProcess::Starting) {
QTimer timer(this);
- connect(&timer, &QTimer::timeout, d, &QtcProcessPrivate::slotTimeout);
+ connect(&timer, &QTimer::timeout, d, &ProcessPrivate::slotTimeout);
timer.setInterval(1000);
timer.start();
#ifdef QT_GUI_LIB
@@ -1786,33 +1897,33 @@ void QtcProcess::runBlocking(EventLoopMode eventLoopMode)
}
}
-void QtcProcess::setStdOutCallback(const TextChannelCallback &callback)
+void Process::setStdOutCallback(const TextChannelCallback &callback)
{
d->m_stdOut.outputCallback = callback;
d->m_stdOut.emitSingleLines = false;
}
-void QtcProcess::setStdOutLineCallback(const TextChannelCallback &callback)
+void Process::setStdOutLineCallback(const TextChannelCallback &callback)
{
d->m_stdOut.outputCallback = callback;
d->m_stdOut.emitSingleLines = true;
d->m_stdOut.keepRawData = false;
}
-void QtcProcess::setStdErrCallback(const TextChannelCallback &callback)
+void Process::setStdErrCallback(const TextChannelCallback &callback)
{
d->m_stdErr.outputCallback = callback;
d->m_stdErr.emitSingleLines = false;
}
-void QtcProcess::setStdErrLineCallback(const TextChannelCallback &callback)
+void Process::setStdErrLineCallback(const TextChannelCallback &callback)
{
d->m_stdErr.outputCallback = callback;
d->m_stdErr.emitSingleLines = true;
d->m_stdErr.keepRawData = false;
}
-void QtcProcess::setTextChannelMode(Channel channel, TextChannelMode mode)
+void Process::setTextChannelMode(Channel channel, TextChannelMode mode)
{
const TextChannelCallback outputCb = [this](const QString &text) {
GuardLocker locker(d->m_guard);
@@ -1825,7 +1936,7 @@ void QtcProcess::setTextChannelMode(Channel channel, TextChannelMode mode)
const TextChannelCallback callback = (channel == Channel::Output) ? outputCb : errorCb;
ChannelBuffer *buffer = channel == Channel::Output ? &d->m_stdOut : &d->m_stdErr;
QTC_ASSERT(buffer->m_textChannelMode == TextChannelMode::Off, qWarning()
- << "QtcProcess::setTextChannelMode(): Changing text channel mode for"
+ << "Process::setTextChannelMode(): Changing text channel mode for"
<< (channel == Channel::Output ? "Output": "Error")
<< "channel while it was previously set for this channel.");
buffer->m_textChannelMode = mode;
@@ -1848,13 +1959,13 @@ void QtcProcess::setTextChannelMode(Channel channel, TextChannelMode mode)
}
}
-TextChannelMode QtcProcess::textChannelMode(Channel channel) const
+TextChannelMode Process::textChannelMode(Channel channel) const
{
ChannelBuffer *buffer = channel == Channel::Output ? &d->m_stdOut : &d->m_stdErr;
return buffer->m_textChannelMode;
}
-void QtcProcessPrivate::slotTimeout()
+void ProcessPrivate::slotTimeout()
{
if (!m_waitingForUser && (++m_hangTimerCount > m_maxHangTimerCount)) {
if (debug)
@@ -1875,17 +1986,17 @@ void QtcProcessPrivate::slotTimeout()
}
}
-void QtcProcessPrivate::handleStarted(qint64 processId, qint64 applicationMainThreadId)
+void ProcessPrivate::handleStarted(qint64 processId, qint64 applicationMainThreadId)
{
QTC_CHECK(m_state == QProcess::Starting);
m_state = QProcess::Running;
m_processId = processId;
m_applicationMainThreadId = applicationMainThreadId;
- emitGuardedSignal(&QtcProcess::started);
+ emitGuardedSignal(&Process::started);
}
-void QtcProcessPrivate::handleReadyRead(const QByteArray &outputData, const QByteArray &errorData)
+void ProcessPrivate::handleReadyRead(const QByteArray &outputData, const QByteArray &errorData)
{
QTC_CHECK(m_state == QProcess::Running);
@@ -1900,7 +2011,7 @@ void QtcProcessPrivate::handleReadyRead(const QByteArray &outputData, const QByt
std::cout << outputData.constData() << std::flush;
} else {
m_stdOut.append(outputData);
- emitGuardedSignal(&QtcProcess::readyReadStandardOutput);
+ emitGuardedSignal(&Process::readyReadStandardOutput);
}
}
if (!errorData.isEmpty()) {
@@ -1909,12 +2020,12 @@ void QtcProcessPrivate::handleReadyRead(const QByteArray &outputData, const QByt
std::cerr << errorData.constData() << std::flush;
} else {
m_stdErr.append(errorData);
- emitGuardedSignal(&QtcProcess::readyReadStandardError);
+ emitGuardedSignal(&Process::readyReadStandardError);
}
}
}
-void QtcProcessPrivate::handleDone(const ProcessResultData &data)
+void ProcessPrivate::handleDone(const ProcessResultData &data)
{
m_killTimer.stop();
const bool wasCanceled = m_resultData.m_canceledByUser;
@@ -1966,7 +2077,7 @@ void QtcProcessPrivate::handleDone(const ProcessResultData &data)
m_stdOut.handleRest();
m_stdErr.handleRest();
- emitGuardedSignal(&QtcProcess::done);
+ emitGuardedSignal(&Process::done);
m_processId = 0;
m_applicationMainThreadId = 0;
}
@@ -1980,7 +2091,7 @@ static QString blockingMessage(const QVariant &variant)
return "blocking without event loop";
}
-void QtcProcessPrivate::setupDebugLog()
+void ProcessPrivate::setupDebugLog()
{
if (!processLog().isDebugEnabled())
return;
@@ -1990,7 +2101,7 @@ void QtcProcessPrivate::setupDebugLog()
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
};
- connect(q, &QtcProcess::starting, this, [=] {
+ connect(q, &Process::starting, this, [=] {
const quint64 msNow = now();
setProperty(QTC_PROCESS_STARTTIME, msNow);
@@ -2003,7 +2114,7 @@ void QtcProcessPrivate::setupDebugLog()
setProperty(QTC_PROCESS_NUMBER, currentNumber);
});
- connect(q, &QtcProcess::done, this, [=] {
+ connect(q, &Process::done, this, [=] {
if (!m_process.get())
return;
const QVariant n = property(QTC_PROCESS_NUMBER);
@@ -2029,24 +2140,24 @@ void QtcProcessPrivate::setupDebugLog()
});
}
-void QtcProcessPrivate::storeEventLoopDebugInfo(const QVariant &value)
+void ProcessPrivate::storeEventLoopDebugInfo(const QVariant &value)
{
if (processLog().isDebugEnabled())
setProperty(QTC_PROCESS_BLOCKING_TYPE, value);
}
-QtcProcessAdapter::QtcProcessAdapter()
+ProcessTaskAdapter::ProcessTaskAdapter()
{
- connect(task(), &QtcProcess::done, this, [this] {
+ connect(task(), &Process::done, this, [this] {
emit done(task()->result() == ProcessResult::FinishedWithSuccess);
});
}
-void QtcProcessAdapter::start()
+void ProcessTaskAdapter::start()
{
task()->start();
}
} // namespace Utils
-#include "qtcprocess.moc"
+#include "process.moc"
diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/process.h
index 67898ba855..83f1bb990b 100644
--- a/src/libs/utils/qtcprocess.h
+++ b/src/libs/utils/process.h
@@ -1,13 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#if defined(Q_CC_MINGW) && defined(WIN_PTHREADS_H) && !defined(_INC_PROCESS)
+ // Arrived here via <pthread.h> which wants to include <process.h>
+ #include_next <process.h>
+#elif !defined(UTILS_PROCESS_H)
+#define UTILS_PROCESS_H
#include "utils_global.h"
#include "commandline.h"
#include "processenums.h"
-#include "tasktree.h"
+
+#include <solutions/tasking/tasktree.h>
#include <QProcess>
@@ -16,24 +21,23 @@ class QDebug;
class QTextCodec;
QT_END_NAMESPACE
-class tst_QtcProcess;
-
namespace Utils {
-namespace Internal { class QtcProcessPrivate; }
+namespace Internal { class ProcessPrivate; }
+namespace Pty { class Data; }
class Environment;
class DeviceProcessHooks;
class ProcessInterface;
class ProcessResultData;
-class QTCREATOR_UTILS_EXPORT QtcProcess final : public QObject
+class QTCREATOR_UTILS_EXPORT Process final : public QObject
{
Q_OBJECT
public:
- explicit QtcProcess(QObject *parent = nullptr);
- ~QtcProcess();
+ explicit Process(QObject *parent = nullptr);
+ ~Process();
// ProcessInterface related
@@ -76,6 +80,9 @@ public:
void setProcessImpl(ProcessImpl processImpl);
+ void setPtyData(const std::optional<Pty::Data> &data);
+ std::optional<Pty::Data> ptyData() const;
+
void setTerminalMode(TerminalMode mode);
TerminalMode terminalMode() const;
bool usesTerminal() const { return terminalMode() != TerminalMode::Off; }
@@ -125,7 +132,7 @@ public:
// Other enhancements.
// These (or some of them) may be potentially moved outside of the class.
- // For some we may aggregate in another public utils class (or subclass of QtcProcess)?
+ // Some of them could be aggregated in another public utils class.
// TODO: Unused currently? Should it serve as a compartment for contrary of remoteEnvironment?
static Environment systemEnvironmentForBinary(const FilePath &filePath);
@@ -177,6 +184,9 @@ public:
QString toStandaloneCommandLine() const;
+ void setCreateConsoleOnWindows(bool create);
+ bool createConsoleOnWindows() const;
+
signals:
void starting(); // On NotRunning -> Starting state transition
void started(); // On Starting -> Running state transition
@@ -187,10 +197,10 @@ signals:
void textOnStandardError(const QString &text);
private:
- friend QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const QtcProcess &r);
+ friend QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const Process &r);
- friend class Internal::QtcProcessPrivate;
- Internal::QtcProcessPrivate *d = nullptr;
+ friend class Internal::ProcessPrivate;
+ Internal::ProcessPrivate *d = nullptr;
};
class DeviceProcessHooks
@@ -200,13 +210,15 @@ public:
std::function<Environment(const FilePath &)> systemEnvironmentForBinary;
};
-class QTCREATOR_UTILS_EXPORT QtcProcessAdapter : public Tasking::TaskAdapter<QtcProcess>
+class QTCREATOR_UTILS_EXPORT ProcessTaskAdapter : public Tasking::TaskAdapter<Process>
{
public:
- QtcProcessAdapter();
+ ProcessTaskAdapter();
void start() final;
};
} // namespace Utils
-QTC_DECLARE_CUSTOM_TASK(Process, Utils::QtcProcessAdapter);
+TASKING_DECLARE_TASK(ProcessTask, Utils::ProcessTaskAdapter);
+
+#endif // UTILS_PROCESS_H
diff --git a/src/libs/utils/process_ctrlc_stub.cpp b/src/libs/utils/process_ctrlc_stub.cpp
index bbfdf7ff14..b924e74e49 100644
--- a/src/libs/utils/process_ctrlc_stub.cpp
+++ b/src/libs/utils/process_ctrlc_stub.cpp
@@ -44,7 +44,7 @@ public:
fwprintf(stderr, L"qtcreator_ctrlc_stub: CreateJobObject failed: 0x%x.\n", GetLastError());
return;
}
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(m_job, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) {
fwprintf(stderr, L"qtcreator_ctrlc_stub: SetInformationJobObject failed: 0x%x.\n", GetLastError());
@@ -85,7 +85,7 @@ int main(int argc, char **)
uiShutDownWindowMessage = RegisterWindowMessage(L"qtcctrlcstub_shutdown");
uiInterruptMessage = RegisterWindowMessage(L"qtcctrlcstub_interrupt");
- WNDCLASSEX wcex = {0};
+ WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = GetModuleHandle(nullptr);
@@ -188,14 +188,14 @@ DWORD WINAPI processWatcherThread(LPVOID lpParameter)
bool startProcess(wchar_t *pCommandLine, bool lowerPriority, const JobKillOnClose& job)
{
- SECURITY_ATTRIBUTES sa = {0};
+ SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
- STARTUPINFO si = {0};
+ STARTUPINFO si = {};
si.cb = sizeof(si);
- PROCESS_INFORMATION pi;
+ PROCESS_INFORMATION pi = {};
DWORD dwCreationFlags = lowerPriority ? BELOW_NORMAL_PRIORITY_CLASS : 0;
BOOL bSuccess = CreateProcess(NULL, pCommandLine, &sa, &sa, TRUE, dwCreationFlags, NULL, NULL, &si, &pi);
if (!bSuccess) {
diff --git a/src/libs/utils/process_stub.qbs b/src/libs/utils/process_stub.qbs
deleted file mode 100644
index 341fb57791..0000000000
--- a/src/libs/utils/process_stub.qbs
+++ /dev/null
@@ -1,21 +0,0 @@
-import qbs 1.0
-
-QtcTool {
- name: "qtcreator_process_stub"
- consoleApplication: true
-
-
- files: {
- if (qbs.targetOS.contains("windows")) {
- return [ "process_stub_win.c" ]
- } else {
- return [ "process_stub_unix.c" ]
- }
- }
-
- cpp.dynamicLibraries: {
- if (qbs.targetOS.contains("windows")) {
- return [ "shell32" ]
- }
- }
-}
diff --git a/src/libs/utils/process_stub_unix.c b/src/libs/utils/process_stub_unix.c
deleted file mode 100644
index 716e88d101..0000000000
--- a/src/libs/utils/process_stub_unix.c
+++ /dev/null
@@ -1,340 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <assert.h>
-
-#ifdef __linux__
-#include <sys/prctl.h>
-
-// Enable compilation with older header that doesn't contain this constant
-// for running on newer libraries that do support it
-#ifndef PR_SET_PTRACER
-#define PR_SET_PTRACER 0x59616d61
-#endif
-#endif
-
-/* For OpenBSD */
-#ifndef EPROTO
-# define EPROTO EINVAL
-#endif
-
-extern char **environ;
-
-static int qtcFd;
-static char *sleepMsg;
-static int chldPipe[2];
-static int blockingPipe[2];
-static int isDebug;
-static volatile int isDetached;
-static volatile int chldPid;
-
-static void __attribute__((noreturn)) doExit(int code)
-{
- tcsetpgrp(0, getpid());
- puts(sleepMsg);
- const char *rv = fgets(sleepMsg, 2, stdin); /* Minimal size to make it wait */
- (void)rv; // Q_UNUSED
- exit(code);
-}
-
-static void sendMsg(const char *msg, int num)
-{
- int pidStrLen;
- int ioRet;
- char pidStr[64];
-
- pidStrLen = sprintf(pidStr, msg, num);
- if (!isDetached && (ioRet = write(qtcFd, pidStr, pidStrLen)) != pidStrLen) {
- fprintf(stderr, "Cannot write to creator comm socket: %s\n",
- (ioRet < 0) ? strerror(errno) : "short write");
- isDetached = 2;
- }
-}
-
-enum {
- ArgCmd = 0,
- ArgAction,
- ArgSocket,
- ArgMsg,
- ArgDir,
- ArgEnv,
- ArgPid,
- ArgExe
-};
-
-/* Handle sigchld */
-static void sigchldHandler(int sig)
-{
- int chldStatus;
- /* Currently we have only one child, so we exit in case of error. */
- int waitRes;
- (void)sig;
- for (;;) {
- waitRes = waitpid(-1, &chldStatus, WNOHANG);
- if (!waitRes)
- break;
- if (waitRes < 0) {
- perror("Cannot obtain exit status of child process");
- doExit(3);
- }
- if (WIFSTOPPED(chldStatus)) {
- /* The child stopped. This can be the result of the initial SIGSTOP handling.
- * We won't need the notification pipe any more, as we know that
- * the exec() succeeded. */
- close(chldPipe[0]);
- close(chldPipe[1]);
- chldPipe[0] = -1;
- if (isDetached == 2 && isDebug) {
- /* qtcreator was not informed and died while debugging, killing the child */
- kill(chldPid, SIGKILL);
- }
- } else if (WIFEXITED(chldStatus)) {
- sendMsg("exit %d\n", WEXITSTATUS(chldStatus));
- doExit(0);
- } else {
- sendMsg("crash %d\n", WTERMSIG(chldStatus));
- doExit(0);
- }
- }
-}
-
-
-/* syntax: $0 {"run"|"debug"} <pid-socket> <continuation-msg> <workdir> <env-file> <exe> <args...> */
-/* exit codes: 0 = ok, 1 = invocation error, 3 = internal error */
-int main(int argc, char *argv[])
-{
- int errNo, hadInvalidCommand = 0;
- char **env = 0;
- struct sockaddr_un sau;
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
-
- if (argc < ArgEnv) {
- fprintf(stderr, "This is an internal helper of Qt Creator. Do not run it manually.\n");
- return 1;
- }
- sleepMsg = argv[ArgMsg];
-
- /* Connect to the master, i.e. Creator. */
- if ((qtcFd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
- perror("Cannot create creator comm socket");
- doExit(3);
- }
- memset(&sau, 0, sizeof(sau));
- sau.sun_family = AF_UNIX;
- strncpy(sau.sun_path, argv[ArgSocket], sizeof(sau.sun_path) - 1);
- if (connect(qtcFd, (struct sockaddr *)&sau, sizeof(sau))) {
- fprintf(stderr, "Cannot connect creator comm socket %s: %s\n", sau.sun_path, strerror(errno));
- doExit(1);
- }
-
- isDebug = !strcmp(argv[ArgAction], "debug");
- isDetached = 0;
-
- if (*argv[ArgDir] && chdir(argv[ArgDir])) {
- /* Only expected error: no such file or direcotry */
- sendMsg("err:chdir %d\n", errno);
- return 1;
- }
-
- if (*argv[ArgEnv]) {
- FILE *envFd;
- char *envdata, *edp, *termEnv;
- long size;
- int count;
- if (!(envFd = fopen(argv[ArgEnv], "r"))) {
- fprintf(stderr, "Cannot read creator env file %s: %s\n",
- argv[ArgEnv], strerror(errno));
- doExit(1);
- }
- fseek(envFd, 0, SEEK_END);
- size = ftell(envFd);
- if (size < 0) {
- perror("Failed to get size of env file");
- doExit(1);
- }
- rewind(envFd);
- envdata = malloc(size + 1);
- if (fread(envdata, 1, size, envFd) != (size_t)size) {
- perror("Failed to read env file");
- doExit(1);
- }
- envdata[size] = '\0';
- fclose(envFd);
- assert(!size || !envdata[size - 1]);
- for (count = 0, edp = envdata; edp < envdata + size; ++count)
- edp += strlen(edp) + 1;
- env = malloc((count + 2) * sizeof(char *));
- for (count = 0, edp = envdata; edp < envdata + size; ++count) {
- env[count] = edp;
- edp += strlen(edp) + 1;
- }
- if ((termEnv = getenv("TERM")))
- env[count++] = termEnv - 5;
- env[count] = 0;
- }
-
- /* send our pid after we read the environment file (creator will get rid of it) */
- sendMsg("spid %ld\n", (long)getpid());
-
- /*
- * set up the signal handlers
- */
- {
- /* Ignore SIGTTOU. Without this, calling tcsetpgrp() from a background
- * process group (in which we will be, once as child and once as parent)
- * generates the mentioned signal and stops the concerned process. */
- act.sa_handler = SIG_IGN;
- if (sigaction(SIGTTOU, &act, 0)) {
- perror("sigaction SIGTTOU");
- doExit(3);
- }
-
- /* Handle SIGCHLD to keep track of what the child does without blocking */
- act.sa_handler = sigchldHandler;
- if (sigaction(SIGCHLD, &act, 0)) {
- perror("sigaction SIGCHLD");
- doExit(3);
- }
- }
-
- /* Create execution result notification pipe. */
- if (pipe(chldPipe)) {
- perror("Cannot create status pipe");
- doExit(3);
- }
-
- /* The debugged program is not supposed to inherit these handles. But we cannot
- * close the writing end before calling exec(). Just handle both ends the same way ... */
- fcntl(chldPipe[0], F_SETFD, FD_CLOEXEC);
- fcntl(chldPipe[1], F_SETFD, FD_CLOEXEC);
-
- if (isDebug) {
- /* Create execution start notification pipe. The child waits on this until
- the parent writes to it, triggered by an 'c' message from Creator */
- if (pipe(blockingPipe)) {
- perror("Cannot create blocking pipe");
- doExit(3);
- }
- }
-
- switch ((chldPid = fork())) {
- case -1:
- perror("Cannot fork child process");
- doExit(3);
- case 0:
- close(qtcFd);
-
- /* Remove the SIGCHLD handler from the child */
- act.sa_handler = SIG_DFL;
- sigaction(SIGCHLD, &act, 0);
-
- /* Put the process into an own process group and make it the foregroud
- * group on this terminal, so it will receive ctrl-c events, etc.
- * This is the main reason for *all* this stub magic in the first place. */
- /* If one of these calls fails, the world is about to end anyway, so
- * don't bother checking the return values. */
- setpgid(0, 0);
- tcsetpgrp(0, getpid());
-
-#ifdef __linux__
- prctl(PR_SET_PTRACER, atoi(argv[ArgPid]));
-#endif
- /* Block to allow the debugger to attach */
- if (isDebug) {
- char buf;
- int res = read(blockingPipe[0], &buf, 1);
- if (res < 0)
- perror("Could not read from blocking pipe");
- close(blockingPipe[0]);
- close(blockingPipe[1]);
- }
-
- if (env)
- environ = env;
-
- execvp(argv[ArgExe], argv + ArgExe);
- /* Only expected error: no such file or direcotry, i.e. executable not found */
- errNo = errno;
- /* Only realistic error case is SIGPIPE */
- if (write(chldPipe[1], &errNo, sizeof(errNo)) != sizeof(errNo))
- perror("Error passing errno to child");
- _exit(0);
- default:
- sendMsg("pid %d\n", chldPid);
- for (;;) {
- char buffer[100];
- int nbytes;
-
- nbytes = read(qtcFd, buffer, 100);
- if (nbytes <= 0) {
- if (nbytes < 0 && errno == EINTR)
- continue;
- if (!isDetached) {
- isDetached = 2;
- if (nbytes == 0)
- fprintf(stderr, "Lost connection to QtCreator, detaching from it.\n");
- else
- perror("Lost connection to QtCreator, detaching from it");
- }
- break;
- } else {
- int i;
- char c = 'i';
- for (i = 0; i < nbytes; ++i) {
- switch (buffer[i]) {
- case 'k':
- if (chldPid > 0) {
- kill(chldPid, SIGTERM);
- sleep(1);
- kill(chldPid, SIGKILL);
- }
- break;
- case 'i':
- if (chldPid > 0) {
- int res = kill(chldPid, SIGINT);
- if (res)
- perror("Stub could not interrupt inferior");
- }
- break;
- case 'c': {
- int res = write(blockingPipe[1], &c, 1);
- if (res < 0)
- perror("Could not write to blocking pipe");
- break;
- }
- case 'd':
- isDetached = 1;
- break;
- case 's':
- exit(0);
- default:
- if (!hadInvalidCommand) {
- fprintf(stderr, "Ignoring invalid commands from QtCreator.\n");
- hadInvalidCommand = 1;
- }
- }
- }
- }
- }
- if (isDetached) {
- for (;;)
- pause(); /* will exit in the signal handler... */
- }
- }
- assert(0);
- return 0;
-}
diff --git a/src/libs/utils/process_stub_win.c b/src/libs/utils/process_stub_win.c
deleted file mode 100644
index 09f220a6c6..0000000000
--- a/src/libs/utils/process_stub_win.c
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0501 /* WinXP, needed for DebugActiveProcessStop() */
-
-#include <windows.h>
-#include <shellapi.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <direct.h>
-
-static FILE *qtcFd;
-static wchar_t *sleepMsg;
-
-enum RunMode { Run, Debug, Suspend };
-
-/* Print some "press enter" message, wait for that, exit. */
-static void doExit(int code)
-{
- char buf[2];
- _putws(sleepMsg);
- fgets(buf, 2, stdin); /* Minimal size to make it wait */
- exit(code);
-}
-
-/* Print an error message for unexpected Windows system errors, wait, exit. */
-static void systemError(const char *str)
-{
- fprintf(stderr, str, GetLastError());
- doExit(3);
-}
-
-/* Send a message to the master. */
-static void sendMsg(const char *msg, int num)
-{
- int pidStrLen;
- char pidStr[64];
-
- pidStrLen = sprintf(pidStr, msg, num);
- if (fwrite(pidStr, pidStrLen, 1, qtcFd) != 1 || fflush(qtcFd)) {
- fprintf(stderr, "Cannot write to creator comm socket: %s\n",
- strerror(errno));
- doExit(3);
- }
-}
-
-/* Ignore the first ctrl-c/break within a second. */
-static BOOL WINAPI ctrlHandler(DWORD dwCtrlType)
-{
- static ULARGE_INTEGER lastTime;
- ULARGE_INTEGER thisTime;
- SYSTEMTIME sysTime;
- FILETIME fileTime;
-
- if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
- GetSystemTime(&sysTime);
- SystemTimeToFileTime(&sysTime, &fileTime);
- thisTime.LowPart = fileTime.dwLowDateTime;
- thisTime.HighPart = fileTime.dwHighDateTime;
- if (lastTime.QuadPart + 10000000 < thisTime.QuadPart) {
- lastTime.QuadPart = thisTime.QuadPart;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-enum {
- ArgCmd = 0,
- ArgAction,
- ArgSocket,
- ArgDir,
- ArgEnv,
- ArgCmdLine,
- ArgMsg,
- ArgCount
-};
-
-/* syntax: $0 {"run"|"debug"} <pid-socket> <workdir> <env-file> <cmdline> <continuation-msg> */
-/* exit codes: 0 = ok, 1 = invocation error, 3 = internal error */
-int main()
-{
- int argc;
- int creationFlags;
- wchar_t **argv;
- wchar_t *env = 0;
- STARTUPINFOW si;
- PROCESS_INFORMATION pi;
- DEBUG_EVENT dbev;
- enum RunMode mode = Run;
- HANDLE image = NULL;
-
- argv = CommandLineToArgvW(GetCommandLine(), &argc);
-
- if (argc != ArgCount) {
- fprintf(stderr, "This is an internal helper of Qt Creator. Do not run it manually.\n");
- return 1;
- }
- sleepMsg = argv[ArgMsg];
-
- /* Connect to the master, i.e. Creator. */
- if (!(qtcFd = _wfopen(argv[ArgSocket], L"w"))) {
- fprintf(stderr, "Cannot connect creator comm pipe %S: %s\n",
- argv[ArgSocket], strerror(errno));
- doExit(1);
- }
-
- if (*argv[ArgDir] && !SetCurrentDirectoryW(argv[ArgDir])) {
- /* Only expected error: no such file or direcotry */
- sendMsg("err:chdir %d\n", GetLastError());
- return 1;
- }
-
- if (*argv[ArgEnv]) {
- FILE *envFd;
- long size;
- if (!(envFd = _wfopen(argv[ArgEnv], L"rb"))) {
- fprintf(stderr, "Cannot read creator env file %S: %s\n",
- argv[ArgEnv], strerror(errno));
- doExit(1);
- }
- fseek(envFd, 0, SEEK_END);
- size = ftell(envFd);
- rewind(envFd);
- env = malloc(size);
- if (fread(env, 1, size, envFd) != size) {
- perror("Failed to read env file");
- doExit(1);
- }
- fclose(envFd);
- }
-
- ZeroMemory(&pi, sizeof(pi));
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- creationFlags = CREATE_UNICODE_ENVIRONMENT;
- if (!wcscmp(argv[ArgAction], L"debug")) {
- mode = Debug;
- } else if (!wcscmp(argv[ArgAction], L"suspend")) {
- mode = Suspend;
- }
-
- switch (mode) {
- case Debug:
- creationFlags |= DEBUG_ONLY_THIS_PROCESS;
- break;
- case Suspend:
- creationFlags |= CREATE_SUSPENDED;
- break;
- default:
- break;
- }
- if (!CreateProcessW(0, argv[ArgCmdLine], 0, 0, FALSE, creationFlags, env, 0, &si, &pi)) {
- /* Only expected error: no such file or direcotry, i.e. executable not found */
- sendMsg("err:exec %d\n", GetLastError());
- doExit(1);
- }
-
- /* This is somewhat convoluted. What we actually want is creating a
- suspended process and letting gdb attach to it. Unfortunately,
- the Windows kernel runs amok when we attempt this.
- So instead we start a debugged process, eat all the initial
- debug events, suspend the process and detach from it. If gdb
- tries to attach *now*, everything goes smoothly. Yay. */
- if (mode == Debug) {
- do {
- if (!WaitForDebugEvent (&dbev, INFINITE))
- systemError("Cannot fetch debug event, error %d\n");
- if (dbev.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
- image = dbev.u.CreateProcessInfo.hFile;
- if (dbev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
- /* The first exception to be delivered is a trap
- which indicates completion of startup. */
- if (SuspendThread(pi.hThread) == (DWORD)-1)
- systemError("Cannot suspend debugee, error %d\n");
- }
- if (!ContinueDebugEvent(dbev.dwProcessId, dbev.dwThreadId, DBG_CONTINUE))
- systemError("Cannot continue debug event, error %d\n");
- } while (dbev.dwDebugEventCode != EXCEPTION_DEBUG_EVENT);
- if (!DebugActiveProcessStop(dbev.dwProcessId))
- systemError("Cannot detach from debugee, error %d\n");
- if (image)
- CloseHandle(image);
- }
-
- SetConsoleCtrlHandler(ctrlHandler, TRUE);
-
- sendMsg("thread %d\n", pi.dwThreadId);
- sendMsg("pid %d\n", pi.dwProcessId);
-
- if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
- systemError("Wait for debugee failed, error %d\n");
-
- /* Don't close the process/thread handles, so that the kernel doesn't free
- the resources before ConsoleProcess is able to obtain handles to them
- - this would be a problem if the child process exits very quickly. */
- doExit(0);
-
- return 0;
-}
diff --git a/src/libs/utils/processenums.h b/src/libs/utils/processenums.h
index 6ea37a2d37..84019bae53 100644
--- a/src/libs/utils/processenums.h
+++ b/src/libs/utils/processenums.h
@@ -24,10 +24,9 @@ enum class ProcessImpl {
enum class TerminalMode {
Off,
- Run,
- Debug,
- Suspend,
- On = Run // Default mode for terminal set to on
+ Run, // Start with process stub enabled
+ Debug, // Start with process stub enabled and wait for debugger to attach
+ Detached, // Start in a terminal, without process stub.
};
// Miscellaneous, not process core
diff --git a/src/libs/utils/processinfo.cpp b/src/libs/utils/processinfo.cpp
index 366800d316..0d71e380fb 100644
--- a/src/libs/utils/processinfo.cpp
+++ b/src/libs/utils/processinfo.cpp
@@ -3,14 +3,13 @@
#include "processinfo.h"
-#include "qtcprocess.h"
+#include "algorithm.h"
+#include "process.h"
-#if defined(Q_OS_UNIX)
#include <QDir>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
+#include <QRegularExpression>
+
+#if defined(Q_OS_UNIX)
#elif defined(Q_OS_WIN)
#include "winutils.h"
#ifdef QTCREATOR_PCH_H
@@ -32,82 +31,64 @@ bool ProcessInfo::operator<(const ProcessInfo &other) const
return commandLine < other.commandLine;
}
-#if defined(Q_OS_UNIX)
+// Determine UNIX processes by reading "/proc". Default to ps if
+// it does not exist
-static bool isUnixProcessId(const QString &procname)
+static QList<ProcessInfo> getLocalProcessesUsingProc(const FilePath &procDir)
{
- for (int i = 0; i != procname.size(); ++i)
- if (!procname.at(i).isDigit())
- return false;
- return true;
-}
+ static const QString execs = "-exec test -f {}/exe \\; "
+ "-exec test -f {}/cmdline \\; "
+ "-exec echo -en 'p{}\\ne' \\; "
+ "-exec readlink {}/exe \\; "
+ "-exec echo -n c \\; "
+ "-exec head -n 1 {}/cmdline \\; "
+ "-exec echo \\; "
+ "-exec echo __SKIP_ME__ \\;";
-// Determine UNIX processes by reading "/proc". Default to ps if
-// it does not exist
+ CommandLine cmd{procDir.withNewPath("find"),
+ {procDir.nativePath(), "-maxdepth", "1", "-type", "d", "-name", "[0-9]*"}};
-static const char procDirC[] = "/proc/";
+ cmd.addArgs(execs, CommandLine::Raw);
+
+ Process procProcess;
+ procProcess.setCommand(cmd);
+ procProcess.runBlocking();
-static QList<ProcessInfo> getLocalProcessesUsingProc()
-{
QList<ProcessInfo> processes;
- const QString procDirPath = QLatin1String(procDirC);
- const QDir procDir = QDir(QLatin1String(procDirC));
- const QStringList procIds = procDir.entryList();
- for (const QString &procId : procIds) {
- if (!isUnixProcessId(procId))
- continue;
- ProcessInfo proc;
- proc.processId = procId.toInt();
- const QString root = procDirPath + procId;
-
- const QFile exeFile(root + QLatin1String("/exe"));
- proc.executable = exeFile.symLinkTarget();
-
- QFile cmdLineFile(root + QLatin1String("/cmdline"));
- if (cmdLineFile.open(QIODevice::ReadOnly)) { // process may have exited
- const QList<QByteArray> tokens = cmdLineFile.readAll().split('\0');
- if (!tokens.isEmpty()) {
- if (proc.executable.isEmpty())
- proc.executable = QString::fromLocal8Bit(tokens.front());
- for (const QByteArray &t : tokens) {
- if (!proc.commandLine.isEmpty())
- proc.commandLine.append(QLatin1Char(' '));
- proc.commandLine.append(QString::fromLocal8Bit(t));
- }
- }
- }
- if (proc.executable.isEmpty()) {
- QFile statFile(root + QLatin1String("/stat"));
- if (statFile.open(QIODevice::ReadOnly)) {
- const QStringList data = QString::fromLocal8Bit(statFile.readAll()).split(QLatin1Char(' '));
- if (data.size() < 2)
- continue;
- proc.executable = data.at(1);
- proc.commandLine = data.at(1); // PPID is element 3
- if (proc.executable.startsWith(QLatin1Char('(')) && proc.executable.endsWith(QLatin1Char(')'))) {
- proc.executable.truncate(proc.executable.size() - 1);
- proc.executable.remove(0, 1);
- }
- }
+ const auto lines = procProcess.readAllStandardOutput().split('\n');
+ for (auto it = lines.begin(); it != lines.end(); ++it) {
+ if (it->startsWith('p')) {
+ ProcessInfo proc;
+ bool ok;
+ proc.processId = FilePath::fromUserInput(it->mid(1).trimmed()).fileName().toInt(&ok);
+ QTC_ASSERT(ok, continue);
+ ++it;
+
+ QTC_ASSERT(it->startsWith('e'), continue);
+ proc.executable = it->mid(1).trimmed();
+ ++it;
+
+ QTC_ASSERT(it->startsWith('c'), continue);
+ proc.commandLine = it->mid(1).trimmed().replace('\0', ' ');
+ if (!proc.commandLine.contains("__SKIP_ME__"))
+ processes.append(proc);
}
- if (!proc.executable.isEmpty())
- processes.push_back(proc);
}
+
return processes;
}
// Determine UNIX processes by running ps
-static QMap<qint64, QString> getLocalProcessDataUsingPs(const QString &column)
+static QMap<qint64, QString> getLocalProcessDataUsingPs(const FilePath &deviceRoot,
+ const QString &column)
{
- QtcProcess process;
- process.setCommand({"ps", {"-e", "-o", "pid," + column}});
- process.start();
- if (!process.waitForFinished())
- return {};
+ Process process;
+ process.setCommand({deviceRoot.withNewPath("ps"), {"-e", "-o", "pid," + column}});
+ process.runBlocking();
// Split "457 /Users/foo.app arg1 arg2"
- const QStringList lines = process.stdOut().split(QLatin1Char('\n'));
+ const QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
QMap<qint64, QString> result;
for (int i = 1; i < lines.size(); ++i) { // Skip header
const QString line = lines.at(i).trimmed();
@@ -118,14 +99,14 @@ static QMap<qint64, QString> getLocalProcessDataUsingPs(const QString &column)
return result;
}
-static QList<ProcessInfo> getLocalProcessesUsingPs()
+static QList<ProcessInfo> getLocalProcessesUsingPs(const FilePath &deviceRoot)
{
QList<ProcessInfo> processes;
// cmdLines are full command lines, usually with absolute path,
// exeNames only the file part of the executable's path.
- const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs("comm");
- const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs("args");
+ const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs(deviceRoot, "comm");
+ const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs(deviceRoot, "args");
for (auto it = exeNames.begin(), end = exeNames.end(); it != end; ++it) {
const qint64 pid = it.key();
@@ -146,16 +127,68 @@ static QList<ProcessInfo> getLocalProcessesUsingPs()
return processes;
}
-QList<ProcessInfo> ProcessInfo::processInfoList()
+static QList<ProcessInfo> getProcessesUsingPidin(const FilePath &pidin)
+{
+ Process process;
+ process.setCommand({pidin, {"-F", "%a %A {/%n}"}});
+ process.runBlocking();
+
+ QList<ProcessInfo> processes;
+ QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
+ if (lines.isEmpty())
+ return processes;
+
+ lines.pop_front(); // drop headers
+ const QRegularExpression re("\\s*(\\d+)\\s+(.*){(.*)}");
+
+ for (const QString &line : std::as_const(lines)) {
+ const QRegularExpressionMatch match = re.match(line);
+ if (match.hasMatch()) {
+ const QStringList captures = match.capturedTexts();
+ if (captures.size() == 4) {
+ const int pid = captures[1].toInt();
+ const QString args = captures[2];
+ const QString exe = captures[3];
+ ProcessInfo deviceProcess;
+ deviceProcess.processId = pid;
+ deviceProcess.executable = exe.trimmed();
+ deviceProcess.commandLine = args.trimmed();
+ processes.append(deviceProcess);
+ }
+ }
+ }
+
+ return Utils::sorted(std::move(processes));
+}
+
+static QList<ProcessInfo> processInfoListUnix(const FilePath &deviceRoot)
{
- const QDir procDir = QDir(QLatin1String(procDirC));
- return procDir.exists() ? getLocalProcessesUsingProc() : getLocalProcessesUsingPs();
+ const FilePath procDir = deviceRoot.withNewPath("/proc");
+ const FilePath pidin = deviceRoot.withNewPath("pidin").searchInPath();
+
+ if (pidin.isExecutableFile())
+ return getProcessesUsingPidin(pidin);
+
+ if (procDir.isReadableDir())
+ return getLocalProcessesUsingProc(procDir);
+
+ return getLocalProcessesUsingPs(deviceRoot);
+}
+
+#if defined(Q_OS_UNIX)
+
+QList<ProcessInfo> ProcessInfo::processInfoList(const FilePath &deviceRoot)
+{
+ return processInfoListUnix(deviceRoot);
}
#elif defined(Q_OS_WIN)
-QList<ProcessInfo> ProcessInfo::processInfoList()
+QList<ProcessInfo> ProcessInfo::processInfoList(const FilePath &deviceRoot)
{
+ if (deviceRoot.needsDevice())
+ return processInfoListUnix(deviceRoot);
+
QList<ProcessInfo> processes;
PROCESSENTRY32 pe;
diff --git a/src/libs/utils/processinfo.h b/src/libs/utils/processinfo.h
index 47fd2d6d0d..90c1a97374 100644
--- a/src/libs/utils/processinfo.h
+++ b/src/libs/utils/processinfo.h
@@ -5,6 +5,8 @@
#include "utils_global.h"
+#include "filepath.h"
+
#include <QList>
#include <QString>
@@ -19,7 +21,7 @@ public:
bool operator<(const ProcessInfo &other) const;
- static QList<ProcessInfo> processInfoList();
+ static QList<ProcessInfo> processInfoList(const Utils::FilePath &deviceRoot = Utils::FilePath());
};
} // namespace Utils
diff --git a/src/libs/utils/processinterface.cpp b/src/libs/utils/processinterface.cpp
index 9e13494cda..a2dc746905 100644
--- a/src/libs/utils/processinterface.cpp
+++ b/src/libs/utils/processinterface.cpp
@@ -7,6 +7,17 @@
namespace Utils {
+namespace Pty {
+
+void Data::resize(const QSize &size)
+{
+ m_size = size;
+ if (m_data->m_handler)
+ m_data->m_handler(size);
+}
+
+} // namespace Pty
+
/*!
* \brief controlSignalToInt
* \param controlSignal
@@ -18,8 +29,10 @@ int ProcessInterface::controlSignalToInt(ControlSignal controlSignal)
case ControlSignal::Terminate: return 15;
case ControlSignal::Kill: return 9;
case ControlSignal::Interrupt: return 2;
- case ControlSignal::KickOff: QTC_CHECK(false); return 0;
- case ControlSignal::CloseWriteChannel: QTC_CHECK(false); return 0;
+ case ControlSignal::KickOff: return 19;
+ case ControlSignal::CloseWriteChannel:
+ QTC_CHECK(false);
+ return 0;
}
return 0;
}
diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h
index d398d8fe13..a0460c5af3 100644
--- a/src/libs/utils/processinterface.h
+++ b/src/libs/utils/processinterface.h
@@ -10,10 +10,38 @@
#include "processenums.h"
#include <QProcess>
+#include <QSize>
namespace Utils {
-namespace Internal { class QtcProcessPrivate; }
+namespace Internal { class ProcessPrivate; }
+
+namespace Pty {
+
+using ResizeHandler = std::function<void(const QSize &)>;
+
+class QTCREATOR_UTILS_EXPORT SharedData
+{
+public:
+ ResizeHandler m_handler;
+};
+
+class QTCREATOR_UTILS_EXPORT Data
+{
+public:
+ Data() : m_data(new SharedData) {}
+
+ void setResizeHandler(const ResizeHandler &handler) { m_data->m_handler = handler; }
+
+ QSize size() const { return m_size; }
+ void resize(const QSize &size);
+
+private:
+ QSize m_size{80, 60};
+ QSharedPointer<SharedData> m_data;
+};
+
+} // namespace Pty
class QTCREATOR_UTILS_EXPORT ProcessSetupData
{
@@ -22,6 +50,7 @@ public:
ProcessMode m_processMode = ProcessMode::Reader;
TerminalMode m_terminalMode = TerminalMode::Off;
+ std::optional<Pty::Data> m_ptyData;
CommandLine m_commandLine;
FilePath m_workingDirectory;
Environment m_environment;
@@ -39,6 +68,7 @@ public:
bool m_unixTerminalDisabled = false;
bool m_useCtrlCStub = false;
bool m_belowNormalPriority = false; // internal, dependent on other fields and specific code path
+ bool m_createConsoleOnWindows = false;
};
class QTCREATOR_UTILS_EXPORT ProcessResultData
@@ -74,7 +104,7 @@ private:
// - Done is being called in Starting or Running state.
virtual bool waitForSignal(ProcessSignalType signalType, int msecs) = 0;
- friend class Internal::QtcProcessPrivate;
+ friend class Internal::ProcessPrivate;
};
class QTCREATOR_UTILS_EXPORT ProcessInterface : public QObject
@@ -112,8 +142,8 @@ private:
virtual ProcessBlockingInterface *processBlockingInterface() const { return nullptr; }
- friend class QtcProcess;
- friend class Internal::QtcProcessPrivate;
+ friend class Process;
+ friend class Internal::ProcessPrivate;
};
} // namespace Utils
diff --git a/src/libs/utils/processreaper.cpp b/src/libs/utils/processreaper.cpp
index 117d5fcb33..f7235d5529 100644
--- a/src/libs/utils/processreaper.cpp
+++ b/src/libs/utils/processreaper.cpp
@@ -33,7 +33,7 @@ never ending running process:
It looks like when you call terminate() for the adb.exe, it won't stop, never, even after
default 30 seconds timeout. The same happens for blocking processes tested in
- tst_QtcProcess::killBlockingProcess(). It's hard to say whether any process on Windows can
+ tst_Process::killBlockingProcess(). It's hard to say whether any process on Windows can
be finished by a call to terminate(). Until now, no such a process has been found.
Further call to kill() (after a call to terminate()) finishes the process quickly.
diff --git a/src/libs/utils/processutils.cpp b/src/libs/utils/processutils.cpp
index c8394e37dd..62ea514b65 100644
--- a/src/libs/utils/processutils.cpp
+++ b/src/libs/utils/processutils.cpp
@@ -45,16 +45,6 @@ void ProcessStartHandler::handleProcessStarted()
}
}
-void ProcessStartHandler::setBelowNormalPriority()
-{
-#ifdef Q_OS_WIN
- m_process->setCreateProcessArgumentsModifier(
- [](QProcess::CreateProcessArguments *args) {
- args->flags |= BELOW_NORMAL_PRIORITY_CLASS;
- });
-#endif // Q_OS_WIN
-}
-
void ProcessStartHandler::setNativeArguments(const QString &arguments)
{
#ifdef Q_OS_WIN
@@ -65,6 +55,29 @@ void ProcessStartHandler::setNativeArguments(const QString &arguments)
#endif // Q_OS_WIN
}
+void ProcessStartHandler::setWindowsSpecificStartupFlags(bool belowNormalPriority,
+ bool createConsoleWindow)
+{
+#ifdef Q_OS_WIN
+ if (!belowNormalPriority && !createConsoleWindow)
+ return;
+
+ m_process->setCreateProcessArgumentsModifier(
+ [belowNormalPriority, createConsoleWindow](QProcess::CreateProcessArguments *args) {
+ if (createConsoleWindow) {
+ args->flags |= CREATE_NEW_CONSOLE;
+ args->startupInfo->dwFlags &= ~STARTF_USESTDHANDLES;
+ }
+
+ if (belowNormalPriority)
+ args->flags |= BELOW_NORMAL_PRIORITY_CLASS;
+ });
+#else // Q_OS_WIN
+ Q_UNUSED(belowNormalPriority)
+ Q_UNUSED(createConsoleWindow)
+#endif
+}
+
#ifdef Q_OS_WIN
static BOOL sendMessage(UINT message, HWND hwnd, LPARAM lParam)
{
@@ -94,7 +107,18 @@ ProcessHelper::ProcessHelper(QObject *parent)
: QProcess(parent), m_processStartHandler(this)
{
#if defined(Q_OS_UNIX)
- setChildProcessModifier([this] { setupChildProcess_impl(); });
+ setChildProcessModifier([this] {
+ // nice value range is -20 to +19 where -20 is highest, 0 default and +19 is lowest
+ if (m_lowPriority) {
+ errno = 0;
+ if (::nice(5) == -1 && errno != 0)
+ perror("Failed to set nice value");
+ }
+
+ // Disable terminal by becoming a session leader.
+ if (m_unixTerminalDisabled)
+ setsid();
+ });
#endif
}
@@ -140,20 +164,4 @@ void ProcessHelper::interruptProcess(QProcess *process)
ProcessHelper::interruptPid(process->processId());
}
-void ProcessHelper::setupChildProcess_impl()
-{
-#if defined Q_OS_UNIX
- // nice value range is -20 to +19 where -20 is highest, 0 default and +19 is lowest
- if (m_lowPriority) {
- errno = 0;
- if (::nice(5) == -1 && errno != 0)
- perror("Failed to set nice value");
- }
-
- // Disable terminal by becoming a session leader.
- if (m_unixTerminalDisabled)
- setsid();
-#endif
-}
-
} // namespace Utils
diff --git a/src/libs/utils/processutils.h b/src/libs/utils/processutils.h
index 60161f8398..89202c0daf 100644
--- a/src/libs/utils/processutils.h
+++ b/src/libs/utils/processutils.h
@@ -20,8 +20,8 @@ public:
QIODevice::OpenMode openMode() const;
void handleProcessStart();
void handleProcessStarted();
- void setBelowNormalPriority();
void setNativeArguments(const QString &arguments);
+ void setWindowsSpecificStartupFlags(bool belowNormalPriority, bool createConsoleWindow);
private:
ProcessMode m_processMode = ProcessMode::Reader;
@@ -50,7 +50,6 @@ public:
private:
void terminateProcess();
- void setupChildProcess_impl();
bool m_lowPriority = false;
bool m_unixTerminalDisabled = false;
diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp
index 8df64b97f0..cb7018943d 100644
--- a/src/libs/utils/projectintropage.cpp
+++ b/src/libs/utils/projectintropage.cpp
@@ -66,7 +66,6 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
WizardPage(parent),
d(new ProjectIntroPagePrivate)
{
- resize(355, 289);
setTitle(Tr::tr("Introduction and Project Location"));
d->m_descriptionLabel = new QLabel(this);
diff --git a/src/libs/utils/qrcparser.h b/src/libs/utils/qrcparser.h
index 42cd9b6666..14352e26b8 100644
--- a/src/libs/utils/qrcparser.h
+++ b/src/libs/utils/qrcparser.h
@@ -4,7 +4,8 @@
#pragma once
#include "utils_global.h"
-#include "utils/filepath.h"
+
+#include "filepath.h"
#include <QSharedPointer>
#include <QString>
diff --git a/src/libs/utils/qtcolorbutton.cpp b/src/libs/utils/qtcolorbutton.cpp
index ca0aa631ba..6a8537bd5f 100644
--- a/src/libs/utils/qtcolorbutton.cpp
+++ b/src/libs/utils/qtcolorbutton.cpp
@@ -3,12 +3,15 @@
#include "qtcolorbutton.h"
-#include <QMimeData>
+#include "theme/theme.h"
+
#include <QApplication>
#include <QColorDialog>
+#include <QDrag>
#include <QDragEnterEvent>
+#include <QMimeData>
#include <QPainter>
-#include <QDrag>
+#include <QStyleOption>
namespace Utils {
@@ -157,51 +160,52 @@ bool QtColorButton::isDialogOpen() const
void QtColorButton::paintEvent(QPaintEvent *event)
{
- QToolButton::paintEvent(event);
- if (!isEnabled())
- return;
+ Q_UNUSED(event)
- const int pixSize = 10;
- QBrush br(d_ptr->shownColor());
- if (d_ptr->m_backgroundCheckered) {
- QPixmap pm(2 * pixSize, 2 * pixSize);
- QPainter pmp(&pm);
- pmp.fillRect(0, 0, pixSize, pixSize, Qt::white);
- pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white);
- pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black);
- pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black);
- pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor());
- br = QBrush(pm);
- }
+ constexpr Theme::Color overlayColor = Theme::TextColorNormal;
+ constexpr qreal overlayOpacity = 0.25;
QPainter p(this);
- const int corr = 5;
- QRect r = rect().adjusted(corr, corr, -corr, -corr);
- p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr);
- p.fillRect(r, br);
+ const QColor color = d_ptr->shownColor();
+ if (!color.isValid()) {
+ constexpr int size = 11;
+ const qreal horPadding = (width() - size) / 2.0;
+ const qreal verPadding = (height() - size) / 2.0;
+ const QPen pen(creatorTheme()->color(overlayColor), 2);
+
+ p.save();
+ p.setOpacity(overlayOpacity);
+ p.setPen(pen);
+ p.setRenderHint(QPainter::Antialiasing);
+ p.drawLine(QLineF(horPadding, height() - verPadding, width() - horPadding, verPadding));
+ p.restore();
+ } else if (isEnabled()) {
+ QBrush br(color);
+ if (d_ptr->m_backgroundCheckered) {
+ const int pixSize = 10;
+ QPixmap pm(2 * pixSize, 2 * pixSize);
+ pm.fill(Qt::white);
+ QPainter pmp(&pm);
+ pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black);
+ pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black);
+ pmp.fillRect(pm.rect(), color);
+ br = QBrush(pm);
+ p.setBrushOrigin((width() - pixSize) / 2, (height() - pixSize) / 2);
+ }
+ p.fillRect(rect(), br);
+ }
- //const int adjX = qRound(r.width() / 4.0);
- //const int adjY = qRound(r.height() / 4.0);
- //p.fillRect(r.adjusted(adjX, adjY, -adjX, -adjY),
- // QColor(d_ptr->shownColor().rgb()));
- /*
- p.fillRect(r.adjusted(0, r.height() * 3 / 4, 0, 0),
- QColor(d_ptr->shownColor().rgb()));
- p.fillRect(r.adjusted(0, 0, 0, -r.height() * 3 / 4),
- QColor(d_ptr->shownColor().rgb()));
- */
- /*
- const QColor frameColor0(0, 0, 0, qRound(0.2 * (0xFF - d_ptr->shownColor().alpha())));
- p.setPen(frameColor0);
- p.drawRect(r.adjusted(adjX, adjY, -adjX - 1, -adjY - 1));
- */
-
- const QColor frameColor1(0, 0, 0, 26);
- p.setPen(frameColor1);
- p.drawRect(r.adjusted(1, 1, -2, -2));
- const QColor frameColor2(0, 0, 0, 51);
- p.setPen(frameColor2);
- p.drawRect(r.adjusted(0, 0, -1, -1));
+ if (hasFocus()) {
+ QPen pen;
+ pen.setBrush(Qt::white);
+ pen.setStyle(Qt::DotLine);
+ p.setPen(pen);
+ p.setCompositionMode(QPainter::CompositionMode_Difference);
+ } else {
+ p.setPen(creatorTheme()->color(overlayColor));
+ p.setOpacity(overlayOpacity);
+ }
+ p.drawRect(rect().adjusted(0, 0, -1, -1));
}
void QtColorButton::mousePressEvent(QMouseEvent *event)
diff --git a/src/libs/utils/ranges.h b/src/libs/utils/ranges.h
new file mode 100644
index 0000000000..8b574e08d6
--- /dev/null
+++ b/src/libs/utils/ranges.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#if __cplusplus >= 202002L
+#include <ranges>
+
+namespace Utils {
+
+namespace ranges {
+using std::ranges::reverse_view;
+}
+
+namespace views {
+using std::views::reverse;
+}
+
+} // namespace Utils
+#else
+#include <stdexcept>
+
+namespace Utils {
+
+namespace ranges {
+
+template <typename Container>
+class reverse_view
+{
+public:
+ using value_type = typename Container::value_type;
+ using size_type = typename Container::size_type;
+ using Type = value_type;
+ using pointer = Type *;
+ using const_pointer = const Type *;
+ using reference = Type &;
+ using const_reference = const Type &;
+
+ using reverse_iterator = typename Container::iterator;
+ using iterator = std::reverse_iterator<reverse_iterator>;
+
+ using const_reverse_iterator = typename Container::const_iterator;
+ using const_iterator = std::reverse_iterator<const_reverse_iterator>;
+
+ using Iterator = iterator;
+ using ConstIterator = const_iterator;
+
+ reverse_view(const Container &k) : d(&k) {}
+
+ const_reverse_iterator rbegin() const noexcept { return d->begin(); }
+ const_reverse_iterator rend() const noexcept { return d->end(); }
+ const_reverse_iterator crbegin() const noexcept { return d->begin(); }
+ const_reverse_iterator crend() const noexcept { return d->end(); }
+
+ const_iterator begin() const noexcept { return const_iterator(rend()); }
+ const_iterator end() const noexcept { return const_iterator(rbegin()); }
+ const_iterator cbegin() const noexcept { return const_iterator(rend()); }
+ const_iterator cend() const noexcept { return const_iterator(rbegin()); }
+ const_iterator constBegin() const noexcept { return const_iterator(rend()); }
+ const_iterator constEnd() const noexcept { return const_iterator(rbegin()); }
+
+ const_reference front() const noexcept { return *cbegin(); }
+ const_reference back() const noexcept { return *crbegin(); }
+
+ [[nodiscard]] size_type size() const noexcept { return d->size(); }
+ [[nodiscard]] bool empty() const noexcept { return d->size() == 0; }
+ explicit operator bool() const { return d->size(); }
+
+ const_reference operator[](size_type idx) const
+ {
+ if (idx < size())
+ return *(begin() + idx);
+
+ throw std::out_of_range("bad index in reverse side");
+ }
+
+private:
+ const Container *d;
+};
+} // namespace ranges
+
+namespace views {
+
+constexpr struct {} reverse;
+
+template <typename T>
+inline ranges::reverse_view<T> operator|(const T &container, const decltype(reverse)&)
+{
+ return ranges::reverse_view(container);
+}
+
+} // namsepace views
+} // namespace Utils
+
+#endif
diff --git a/src/libs/utils/runextensions.h b/src/libs/utils/runextensions.h
index f81eb56699..6505b12a9c 100644
--- a/src/libs/utils/runextensions.h
+++ b/src/libs/utils/runextensions.h
@@ -476,113 +476,4 @@ runAsync(QThreadPool *pool, Function &&function, Args&&... args)
std::forward<Args>(args)...);
}
-
-/*!
- Adds a handler for when a result is ready.
- This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
- or create a QFutureWatcher already for other reasons.
-*/
-template <typename R, typename T>
-const QFuture<T> &onResultReady(const QFuture<T> &future, R *receiver, void(R::*member)(const T &))
-{
- auto watcher = new QFutureWatcher<T>();
- QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
- QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, receiver,
- [receiver, member, watcher](int index) {
- (receiver->*member)(watcher->future().resultAt(index));
- });
- watcher->setFuture(future);
- return future;
-}
-
-/*!
- Adds a handler for when a result is ready. The guard object determines the lifetime of
- the connection.
- This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
- or create a QFutureWatcher already for other reasons.
-*/
-template <typename T, typename Function>
-const QFuture<T> &onResultReady(const QFuture<T> &future, QObject *guard, Function f)
-{
- auto watcher = new QFutureWatcher<T>();
- QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
- QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, guard, [f, watcher](int index) {
- f(watcher->future().resultAt(index));
- });
- watcher->setFuture(future);
- return future;
-}
-
-/*!
- Adds a handler for when a result is ready.
- This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
- or create a QFutureWatcher already for other reasons.
-*/
-template <typename T, typename Function>
-const QFuture<T> &onResultReady(const QFuture<T> &future, Function f)
-{
- auto watcher = new QFutureWatcher<T>();
- QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
- QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, [f, watcher](int index) {
- f(watcher->future().resultAt(index));
- });
- watcher->setFuture(future);
- return future;
-}
-
-/*!
- Adds a handler for when the future is finished.
- This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
- or create a QFutureWatcher already for other reasons.
-*/
-template<typename R, typename T>
-const QFuture<T> &onFinished(const QFuture<T> &future,
- R *receiver,
- void (R::*member)(const QFuture<T> &))
-{
- auto watcher = new QFutureWatcher<T>();
- QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
- QObject::connect(watcher,
- &QFutureWatcherBase::finished,
- receiver,
- [receiver, member, watcher] { (receiver->*member)(watcher->future()); });
- watcher->setFuture(future);
- return future;
-}
-
-/*!
- Adds a handler for when the future is finished. The guard object determines the lifetime of
- the connection.
- This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
- or create a QFutureWatcher already for other reasons.
-*/
-template<typename T, typename Function>
-const QFuture<T> &onFinished(const QFuture<T> &future, QObject *guard, Function f)
-{
- auto watcher = new QFutureWatcher<T>();
- QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
- QObject::connect(watcher, &QFutureWatcherBase::finished, guard, [f, watcher] {
- f(watcher->future());
- });
- watcher->setFuture(future);
- return future;
-}
-
-/*!
- Adds a handler for when the future is finished.
- This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions
- or create a QFutureWatcher already for other reasons.
-*/
-template<typename T, typename Function>
-const QFuture<T> &onFinished(const QFuture<T> &future, Function f)
-{
- auto watcher = new QFutureWatcher<T>();
- QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
- QObject::connect(watcher, &QFutureWatcherBase::finished, [f, watcher] {
- f(watcher->future());
- });
- watcher->setFuture(future);
- return future;
-}
-
} // namespace Utils
diff --git a/src/libs/utils/scopedtimer.cpp b/src/libs/utils/scopedtimer.cpp
new file mode 100644
index 0000000000..97b8beffda
--- /dev/null
+++ b/src/libs/utils/scopedtimer.cpp
@@ -0,0 +1,64 @@
+// 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 "scopedtimer.h"
+
+#include <QByteArray>
+#include <QDebug>
+#include <QTime>
+
+#include <chrono>
+
+namespace Utils {
+
+static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateWithMs); }
+
+using namespace std::chrono;
+
+class ScopedTimerPrivate
+{
+public:
+ const char *m_fileName = nullptr;
+ const int m_line = 0;
+ std::atomic<int64_t> *m_cumulative = nullptr;
+ const time_point<system_clock, nanoseconds> m_start = system_clock::now();
+};
+
+static const char s_scoped[] = "SCOPED TIMER";
+static const char s_scopedCumulative[] = "STATIC SCOPED TIMER";
+
+ScopedTimer::ScopedTimer(const char *fileName, int line, std::atomic<int64_t> *cumulative)
+ : d(new ScopedTimerPrivate{fileName, line, cumulative})
+{
+ if (d->m_cumulative)
+ return;
+ qDebug().noquote().nospace() << s_scoped << " [" << currentTime() << "] in " << d->m_fileName
+ << ':' << d->m_line << " started";
+}
+
+static int64_t toMs(int64_t ns) { return ns / 1000000; }
+
+ScopedTimer::~ScopedTimer()
+{
+ const auto elapsed = duration_cast<nanoseconds>(system_clock::now() - d->m_start);
+ QString suffix;
+ if (d->m_cumulative) {
+ const int64_t nsOld = d->m_cumulative->fetch_add(elapsed.count());
+ const int64_t msOld = toMs(nsOld);
+ const int64_t nsNew = nsOld + elapsed.count();
+ const int64_t msNew = toMs(nsNew);
+ // Always report the first hit, and later, as not to clog the debug output,
+ // only at 10ms resolution.
+ if (nsOld != 0 && msOld / 10 == msNew / 10)
+ return;
+
+ suffix = " cumulative timeout: " + QString::number(msNew) + "ms";
+ } else {
+ suffix = " stopped with timeout: " + QString::number(toMs(elapsed.count())) + "ms";
+ }
+ const char *header = d->m_cumulative ? s_scopedCumulative : s_scoped;
+ qDebug().noquote().nospace() << header << " [" << currentTime() << "] in " << d->m_fileName
+ << ':' << d->m_line << suffix;
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/scopedtimer.h b/src/libs/utils/scopedtimer.h
new file mode 100644
index 0000000000..e6ec42e6f4
--- /dev/null
+++ b/src/libs/utils/scopedtimer.h
@@ -0,0 +1,38 @@
+// 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_global.h"
+
+#include <atomic>
+#include <memory>
+
+namespace Utils {
+
+class ScopedTimerPrivate;
+
+class QTCREATOR_UTILS_EXPORT ScopedTimer
+{
+public:
+ ScopedTimer(const char *fileName, int line, std::atomic<int64_t> *cumulative = nullptr);
+ ~ScopedTimer();
+
+private:
+ std::unique_ptr<ScopedTimerPrivate> d;
+};
+
+} // Utils
+
+#define QTC_CONCAT_HELPER(x, y) x ## y
+#define QTC_CONCAT(x, y) QTC_CONCAT_HELPER(x, y)
+#define QTC_SCOPED_TIMER() ::Utils::ScopedTimer QTC_CONCAT(_qtc_scoped_timer_, __LINE__)\
+(__FILE__, __LINE__)
+
+// The macro below expands as follows (in one line):
+// static std::atomic<int64_t> _qtc_static_scoped_timer___LINE__ = 0;
+// ScopedTimer _qtc_scoped_timer___LINE__(__FILE__, __LINE__, &_qtc_static_scoped_timer___LINE__)
+#define QTC_STATIC_SCOPED_TIMER() static std::atomic<int64_t> \
+QTC_CONCAT(_qtc_static_scoped_timer_, __LINE__) = 0; \
+::Utils::ScopedTimer QTC_CONCAT(_qtc_scoped_timer_, __LINE__)\
+(__FILE__, __LINE__, &QTC_CONCAT(_qtc_static_scoped_timer_, __LINE__))
diff --git a/src/libs/utils/searchresultitem.cpp b/src/libs/utils/searchresultitem.cpp
new file mode 100644
index 0000000000..2574473c93
--- /dev/null
+++ b/src/libs/utils/searchresultitem.cpp
@@ -0,0 +1,64 @@
+// 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 "searchresultitem.h"
+
+namespace Utils {
+
+SearchResultColor::SearchResultColor(const QColor &textBg, const QColor &textFg,
+ const QColor &highlightBg, const QColor &highlightFg,
+ const QColor &functionBg, const QColor &functionFg)
+ : textBackground(textBg)
+ , textForeground(textFg)
+ , highlightBackground(highlightBg)
+ , highlightForeground(highlightFg)
+ , containingFunctionBackground(functionBg)
+ , containingFunctionForeground(functionFg)
+{
+ if (!highlightBackground.isValid())
+ highlightBackground = textBackground;
+ if (!highlightForeground.isValid())
+ highlightForeground = textForeground;
+ if (!containingFunctionBackground.isValid())
+ containingFunctionBackground = textBackground;
+ if (!containingFunctionForeground.isValid())
+ containingFunctionForeground = textForeground;
+}
+
+static QString displayText(const QString &line)
+{
+ QString result = line;
+ auto end = result.end();
+ for (auto it = result.begin(); it != end; ++it) {
+ if (!it->isSpace() && !it->isPrint())
+ *it = QChar('?');
+ }
+ return result;
+}
+
+void SearchResultItem::setDisplayText(const QString &text)
+{
+ setLineText(displayText(text));
+}
+
+void SearchResultItem::setMainRange(int line, int column, int length)
+{
+ m_mainRange = {{line, column}, {line, column + length}};
+}
+
+QTCREATOR_UTILS_EXPORT size_t qHash(SearchResultColor::Style style, uint seed)
+{
+ int a = int(style);
+ return ::qHash(a, seed);
+}
+
+bool SearchResultItem::operator==(const SearchResultItem &other) const
+{
+ return m_path == other.m_path && m_lineText == other.m_lineText
+ && m_userData == other.m_userData && m_mainRange == other.m_mainRange
+ && m_useTextEditorFont == other.m_useTextEditorFont
+ && m_selectForReplacement == other.m_selectForReplacement && m_style == other.m_style
+ && m_containingFunctionName == other.m_containingFunctionName;
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/searchresultitem.h b/src/libs/utils/searchresultitem.h
new file mode 100644
index 0000000000..ea9d0332d5
--- /dev/null
+++ b/src/libs/utils/searchresultitem.h
@@ -0,0 +1,102 @@
+// 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_global.h"
+
+#include <utils/filepath.h>
+#include <utils/hostosinfo.h>
+#include <utils/textutils.h>
+
+#include <QColor>
+#include <QHash>
+#include <QIcon>
+#include <QStringList>
+#include <QVariant>
+
+#include <optional>
+
+namespace Utils {
+
+class QTCREATOR_UTILS_EXPORT SearchResultColor
+{
+public:
+ enum class Style { Default, Alt1, Alt2 };
+
+ SearchResultColor() = default;
+ SearchResultColor(const QColor &textBg, const QColor &textFg,
+ const QColor &highlightBg, const QColor &highlightFg,
+ const QColor &functionBg, const QColor &functionFg);
+
+ QColor textBackground;
+ QColor textForeground;
+ QColor highlightBackground;
+ QColor highlightForeground;
+ QColor containingFunctionBackground;
+ QColor containingFunctionForeground;
+
+private:
+ QTCREATOR_UTILS_EXPORT friend size_t qHash(Style style, uint seed);
+};
+
+using SearchResultColors = QHash<SearchResultColor::Style, SearchResultColor>;
+
+class QTCREATOR_UTILS_EXPORT SearchResultItem
+{
+public:
+ QStringList path() const { return m_path; }
+ void setPath(const QStringList &path) { m_path = path; }
+ void setFilePath(const Utils::FilePath &filePath) { m_path = {filePath.toUserOutput()}; }
+
+ QString lineText() const { return m_lineText; }
+ void setLineText(const QString &text) { m_lineText = text; }
+ void setDisplayText(const QString &text);
+
+ QIcon icon() const { return m_icon; }
+ void setIcon(const QIcon &icon) { m_icon = icon; }
+
+ QVariant userData() const { return m_userData; }
+ void setUserData(const QVariant &userData) { m_userData = userData; }
+
+ Text::Range mainRange() const { return m_mainRange; }
+ void setMainRange(const Text::Range &mainRange) { m_mainRange = mainRange; }
+ void setMainRange(int line, int column, int length);
+
+ bool useTextEditorFont() const { return m_useTextEditorFont; }
+ void setUseTextEditorFont(bool useTextEditorFont) { m_useTextEditorFont = useTextEditorFont; }
+
+ SearchResultColor::Style style() const { return m_style; }
+ void setStyle(SearchResultColor::Style style) { m_style = style; }
+
+ bool selectForReplacement() const { return m_selectForReplacement; }
+ void setSelectForReplacement(bool select) { m_selectForReplacement = select; }
+
+ std::optional<QString> containingFunctionName() const { return m_containingFunctionName; }
+
+ void setContainingFunctionName(const std::optional<QString> &containingFunctionName)
+ {
+ m_containingFunctionName = containingFunctionName;
+ }
+
+ bool operator==(const SearchResultItem &other) const;
+ bool operator!=(const SearchResultItem &other) const { return !(operator==(other)); }
+
+private:
+ QStringList m_path; // hierarchy to the parent item of this item
+ QString m_lineText; // text to show for the item itself
+ QIcon m_icon; // icon to show in front of the item (by be null icon to hide)
+ QVariant m_userData; // user data for identification of the item
+ Text::Range m_mainRange;
+ bool m_useTextEditorFont = false;
+ bool m_selectForReplacement = true;
+ SearchResultColor::Style m_style = SearchResultColor::Style::Default;
+ std::optional<QString> m_containingFunctionName;
+};
+
+using SearchResultItems = QList<SearchResultItem>;
+
+} // namespace Utils
+
+Q_DECLARE_METATYPE(Utils::SearchResultItem)
+Q_DECLARE_METATYPE(Utils::SearchResultItems)
diff --git a/src/libs/utils/settingsaccessor.cpp b/src/libs/utils/settingsaccessor.cpp
index dc3cd0711e..275290674f 100644
--- a/src/libs/utils/settingsaccessor.cpp
+++ b/src/libs/utils/settingsaccessor.cpp
@@ -39,17 +39,7 @@ QMessageBox::StandardButtons SettingsAccessor::Issue::allButtons() const
/*!
* The SettingsAccessor can be used to read/write settings in XML format.
*/
-SettingsAccessor::SettingsAccessor(const QString &docType,
- const QString &displayName,
- const QString &applicationDisplayName) :
-docType(docType),
-displayName(displayName),
-applicationDisplayName(applicationDisplayName)
-{
- QTC_CHECK(!docType.isEmpty());
- QTC_CHECK(!displayName.isEmpty());
- QTC_CHECK(!applicationDisplayName.isEmpty());
-}
+SettingsAccessor::SettingsAccessor() = default;
SettingsAccessor::~SettingsAccessor() = default;
@@ -68,6 +58,9 @@ QVariantMap SettingsAccessor::restoreSettings(QWidget *parent) const
*/
bool SettingsAccessor::saveSettings(const QVariantMap &data, QWidget *parent) const
{
+ QTC_CHECK(!m_docType.isEmpty());
+ QTC_CHECK(!m_applicationDisplayName.isEmpty());
+
const std::optional<Issue> result = writeData(m_baseFilePath, data, parent);
const ProceedInfo pi = result ? reportIssues(result.value(), m_baseFilePath, parent) : ProceedInfo::Continue;
@@ -99,6 +92,9 @@ std::optional<SettingsAccessor::Issue> SettingsAccessor::writeData(const FilePat
QVariantMap SettingsAccessor::restoreSettings(const FilePath &settingsPath, QWidget *parent) const
{
+ QTC_CHECK(!m_docType.isEmpty());
+ QTC_CHECK(!m_applicationDisplayName.isEmpty());
+
const RestoreData result = readData(settingsPath, parent);
const ProceedInfo pi = result.hasIssue() ? reportIssues(result.issue.value(), result.path, parent)
@@ -123,7 +119,7 @@ SettingsAccessor::RestoreData SettingsAccessor::readFile(const FilePath &path) c
const QVariantMap data = reader.restoreValues();
if (!m_readOnly && path == m_baseFilePath) {
if (!m_writer)
- m_writer = std::make_unique<PersistentSettingsWriter>(m_baseFilePath, docType);
+ m_writer = std::make_unique<PersistentSettingsWriter>(m_baseFilePath, m_docType);
m_writer->setContents(data);
}
@@ -146,7 +142,7 @@ std::optional<SettingsAccessor::Issue> SettingsAccessor::writeFile(const FilePat
QString errorMessage;
if (!m_readOnly && (!m_writer || m_writer->fileName() != path))
- m_writer = std::make_unique<PersistentSettingsWriter>(path, docType);
+ m_writer = std::make_unique<PersistentSettingsWriter>(path, m_docType);
if (!m_writer->save(data, &errorMessage)) {
return Issue(Tr::tr("Failed to Write File"),
@@ -156,8 +152,7 @@ std::optional<SettingsAccessor::Issue> SettingsAccessor::writeFile(const FilePat
}
SettingsAccessor::ProceedInfo
-SettingsAccessor::reportIssues(const SettingsAccessor::Issue &issue, const FilePath &path,
- QWidget *parent)
+SettingsAccessor::reportIssues(const Issue &issue, const FilePath &path, QWidget *parent)
{
if (!path.exists())
return Continue;
@@ -226,18 +221,8 @@ std::optional<FilePath> BackUpStrategy::backupName(const QVariantMap &oldData,
return path.stringAppended(".bak");
}
-BackingUpSettingsAccessor::BackingUpSettingsAccessor(const QString &docType,
- const QString &displayName,
- const QString &applicationDisplayName) :
- BackingUpSettingsAccessor(std::make_unique<BackUpStrategy>(), docType, displayName, applicationDisplayName)
-{ }
-
-BackingUpSettingsAccessor::BackingUpSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy,
- const QString &docType,
- const QString &displayName,
- const QString &applicationDisplayName) :
- SettingsAccessor(docType, displayName, applicationDisplayName),
- m_strategy(std::move(strategy))
+BackingUpSettingsAccessor::BackingUpSettingsAccessor()
+ : m_strategy(std::make_unique<BackUpStrategy>())
{ }
SettingsAccessor::RestoreData
@@ -259,7 +244,7 @@ BackingUpSettingsAccessor::readData(const FilePath &path, QWidget *parent) const
"for instance because they were written by an incompatible "
"version of %2, or because a different settings path "
"was used.</p>")
- .arg(path.toUserOutput(), applicationDisplayName), Issue::Type::ERROR);
+ .arg(path.toUserOutput(), m_applicationDisplayName), Issue::Type::ERROR);
i.buttons.insert(QMessageBox::Ok, DiscardAndContinue);
result.issue = i;
}
@@ -279,6 +264,11 @@ std::optional<SettingsAccessor::Issue> BackingUpSettingsAccessor::writeData(cons
return SettingsAccessor::writeData(path, data, parent);
}
+void BackingUpSettingsAccessor::setStrategy(std::unique_ptr<BackUpStrategy> &&strategy)
+{
+ m_strategy = std::move(strategy);
+}
+
FilePaths BackingUpSettingsAccessor::readFileCandidates(const FilePath &path) const
{
FilePaths result = filteredUnique(m_strategy->readFileCandidates(path));
@@ -410,19 +400,10 @@ QVariantMap VersionUpgrader::renameKeys(const QList<Change> &changes, QVariantMa
* The UpgradingSettingsAccessor keeps version information in the settings file and will
* upgrade the settings on load to the latest supported version (if possible).
*/
-UpgradingSettingsAccessor::UpgradingSettingsAccessor(const QString &docType,
- const QString &displayName,
- const QString &applicationDisplayName) :
- UpgradingSettingsAccessor(std::make_unique<VersionedBackUpStrategy>(this), docType,
- displayName, applicationDisplayName)
-{ }
-
-UpgradingSettingsAccessor::UpgradingSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy,
- const QString &docType,
- const QString &displayName,
- const QString &applicationDisplayName) :
- BackingUpSettingsAccessor(std::move(strategy), docType, displayName, applicationDisplayName)
-{ }
+UpgradingSettingsAccessor::UpgradingSettingsAccessor()
+{
+ setStrategy(std::make_unique<VersionedBackUpStrategy>(this));
+}
int UpgradingSettingsAccessor::currentVersion() const
{
@@ -541,7 +522,7 @@ UpgradingSettingsAccessor::validateVersionRange(const RestoreData &data) const
"version of %2 was used are ignored, and "
"changes made now will <b>not</b> be propagated to "
"the newer version.</p>")
- .arg(result.path.toUserOutput(), applicationDisplayName), Issue::Type::WARNING);
+ .arg(result.path.toUserOutput(), m_applicationDisplayName), Issue::Type::WARNING);
i.buttons.insert(QMessageBox::Ok, Continue);
result.issue = i;
return result;
@@ -550,13 +531,13 @@ UpgradingSettingsAccessor::validateVersionRange(const RestoreData &data) const
const QByteArray readId = settingsIdFromMap(result.data);
if (!settingsId().isEmpty() && !readId.isEmpty() && readId != settingsId()) {
Issue i(Tr::tr("Settings File for \"%1\" from a Different Environment?")
- .arg(applicationDisplayName),
+ .arg(m_applicationDisplayName),
Tr::tr("<p>No settings file created by this instance "
"of %1 was found.</p>"
"<p>Did you work with this project on another machine or "
"using a different settings path before?</p>"
"<p>Do you still want to load the settings file \"%2\"?</p>")
- .arg(applicationDisplayName, result.path.toUserOutput()), Issue::Type::WARNING);
+ .arg(m_applicationDisplayName, result.path.toUserOutput()), Issue::Type::WARNING);
i.defaultButton = QMessageBox::No;
i.escapeButton = QMessageBox::No;
i.buttons.clear();
@@ -577,12 +558,7 @@ UpgradingSettingsAccessor::validateVersionRange(const RestoreData &data) const
* MergingSettingsAccessor allows to merge secondary settings into the main settings.
* This is useful to e.g. handle .shared files together with .user files.
*/
-MergingSettingsAccessor::MergingSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy,
- const QString &docType,
- const QString &displayName,
- const QString &applicationDisplayName) :
- UpgradingSettingsAccessor(std::move(strategy), docType, displayName, applicationDisplayName)
-{ }
+MergingSettingsAccessor::MergingSettingsAccessor() = default;
SettingsAccessor::RestoreData MergingSettingsAccessor::readData(const FilePath &path,
QWidget *parent) const
@@ -614,7 +590,7 @@ SettingsAccessor::RestoreData MergingSettingsAccessor::readData(const FilePath &
secondaryData.issue = Issue(Tr::tr("Unsupported Merge Settings File"),
Tr::tr("\"%1\" is not supported by %2. "
"Do you want to try loading it anyway?")
- .arg(secondaryData.path.toUserOutput(), applicationDisplayName),
+ .arg(secondaryData.path.toUserOutput(), m_applicationDisplayName),
Issue::Type::WARNING);
secondaryData.issue->buttons.clear();
secondaryData.issue->buttons.insert(QMessageBox::Yes, Continue);
diff --git a/src/libs/utils/settingsaccessor.h b/src/libs/utils/settingsaccessor.h
index f1bbb3594e..683743ac62 100644
--- a/src/libs/utils/settingsaccessor.h
+++ b/src/libs/utils/settingsaccessor.h
@@ -51,8 +51,7 @@ using SettingsMergeResult = std::optional<QPair<QString, QVariant>>;
class QTCREATOR_UTILS_EXPORT SettingsAccessor
{
public:
- SettingsAccessor(const QString &docType, const QString &displayName,
- const QString &applicationDisplayName);
+ SettingsAccessor();
virtual ~SettingsAccessor();
enum ProceedInfo { Continue, DiscardAndContinue };
@@ -95,10 +94,6 @@ public:
QVariantMap restoreSettings(QWidget *parent) const;
bool saveSettings(const QVariantMap &data, QWidget *parent) const;
- const QString docType;
- const QString displayName;
- const QString applicationDisplayName;
-
void setBaseFilePath(const FilePath &baseFilePath) { m_baseFilePath = baseFilePath; }
void setReadOnly() { m_readOnly = true; }
FilePath baseFilePath() const { return m_baseFilePath; }
@@ -108,6 +103,9 @@ public:
const QVariantMap &data,
QWidget *parent) const;
+ void setDocType(const QString &docType) { m_docType = docType; }
+ void setApplicationDisplayName(const QString &name) { m_applicationDisplayName = name; }
+
protected:
// Report errors:
QVariantMap restoreSettings(const FilePath &settingsPath, QWidget *parent) const;
@@ -119,6 +117,9 @@ protected:
virtual RestoreData readFile(const FilePath &path) const;
virtual std::optional<Issue> writeFile(const FilePath &path, const QVariantMap &data) const;
+ QString m_docType;
+ QString m_applicationDisplayName;
+
private:
FilePath m_baseFilePath;
mutable std::unique_ptr<PersistentSettingsWriter> m_writer;
@@ -148,10 +149,7 @@ public:
class QTCREATOR_UTILS_EXPORT BackingUpSettingsAccessor : public SettingsAccessor
{
public:
- BackingUpSettingsAccessor(const QString &docType, const QString &displayName,
- const QString &applicationDisplayName);
- BackingUpSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy, const QString &docType,
- const QString &displayName, const QString &applicationDisplayName);
+ BackingUpSettingsAccessor();
RestoreData readData(const FilePath &path, QWidget *parent) const override;
std::optional<Issue> writeData(const FilePath &path,
@@ -159,6 +157,7 @@ public:
QWidget *parent) const override;
BackUpStrategy *strategy() const { return m_strategy.get(); }
+ void setStrategy(std::unique_ptr<BackUpStrategy> &&strategy);
private:
FilePaths readFileCandidates(const FilePath &path) const;
@@ -220,10 +219,7 @@ class MergingSettingsAccessor;
class QTCREATOR_UTILS_EXPORT UpgradingSettingsAccessor : public BackingUpSettingsAccessor
{
public:
- UpgradingSettingsAccessor(const QString &docType,
- const QString &displayName, const QString &applicationDisplayName);
- UpgradingSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy, const QString &docType,
- const QString &displayName, const QString &appDisplayName);
+ UpgradingSettingsAccessor();
int currentVersion() const;
int firstSupportedVersion() const;
@@ -263,9 +259,7 @@ public:
QString key;
};
- MergingSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy,
- const QString &docType, const QString &displayName,
- const QString &applicationDisplayName);
+ MergingSettingsAccessor();
RestoreData readData(const FilePath &path, QWidget *parent) const final;
diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h
index ae32314edd..6235995d94 100644
--- a/src/libs/utils/smallstringlayout.h
+++ b/src/libs/utils/smallstringlayout.h
@@ -111,7 +111,7 @@ struct alignas(16) StringDataLayout
shortString[i] = string[i];
} else {
control = {0, true, true};
- reference = {string, Size - 1, 0};
+ reference = {{string}, Size - 1, 0};
}
}
@@ -163,7 +163,7 @@ struct alignas(16) StringDataLayout<MaximumShortStringDataAreaSize,
shortString[i] = string[i];
} else {
control = {0, true, true};
- reference = {string, Size - 1, 0};
+ reference = {{string}, Size - 1, 0};
}
}
diff --git a/src/libs/utils/stringtable.cpp b/src/libs/utils/stringtable.cpp
index 6345b80c32..73cad02588 100644
--- a/src/libs/utils/stringtable.cpp
+++ b/src/libs/utils/stringtable.cpp
@@ -3,7 +3,7 @@
#include "stringtable.h"
-#include "runextensions.h"
+#include "async.h"
#include <QDebug>
#include <QElapsedTimer>
@@ -34,7 +34,7 @@ public:
void cancelAndWait();
QString insert(const QString &string);
void startGC();
- void GC(QFutureInterface<void> &futureInterface);
+ void GC(QPromise<void> &promise);
QFuture<void> m_future;
QMutex m_lock;
@@ -90,7 +90,7 @@ void StringTablePrivate::startGC()
{
QMutexLocker locker(&m_lock);
cancelAndWait();
- m_future = Utils::runAsync(&StringTablePrivate::GC, this);
+ m_future = Utils::asyncRun(&StringTablePrivate::GC, this);
}
QTCREATOR_UTILS_EXPORT void scheduleGC()
@@ -113,7 +113,7 @@ static inline bool isQStringInUse(const QString &string)
return data_ptr->isShared() || !data_ptr->isMutable() /* QStringLiteral ? */;
}
-void StringTablePrivate::GC(QFutureInterface<void> &futureInterface)
+void StringTablePrivate::GC(QPromise<void> &promise)
{
int initialSize = 0;
bytesSaved = 0;
@@ -125,7 +125,7 @@ void StringTablePrivate::GC(QFutureInterface<void> &futureInterface)
// Collect all QStrings which have refcount 1. (One reference in m_strings and nowhere else.)
for (QSet<QString>::iterator i = m_strings.begin(); i != m_strings.end();) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
if (!isQStringInUse(*i))
diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index 3a1afe1fa1..f860216961 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -3,10 +3,9 @@
#include "stringutils.h"
-#include "algorithm.h"
-#include "hostosinfo.h"
-#include "qtcassert.h"
#include "filepath.h"
+#include "qtcassert.h"
+#include "theme/theme.h"
#include "utilstr.h"
#ifdef QT_WIDGETS_LIB
@@ -15,11 +14,14 @@
#endif
#include <QDir>
+#include <QFontMetrics>
#include <QJsonArray>
#include <QJsonValue>
#include <QLocale>
#include <QRegularExpression>
#include <QSet>
+#include <QTextDocument>
+#include <QTextList>
#include <QTime>
#include <limits.h>
@@ -527,4 +529,101 @@ QTCREATOR_UTILS_EXPORT FilePath appendHelper(const FilePath &base, int n)
return base.stringAppended(QString::number(n));
}
+QTCREATOR_UTILS_EXPORT QPair<QStringView, QStringView> splitAtFirst(const QStringView &stringView,
+ QChar ch)
+{
+ int splitIdx = stringView.indexOf(ch);
+ if (splitIdx == -1)
+ return {stringView, {}};
+
+ QStringView left = stringView.mid(0, splitIdx);
+ QStringView right = stringView.mid(splitIdx + 1);
+
+ return {left, right};
+}
+
+QTCREATOR_UTILS_EXPORT QPair<QStringView, QStringView> splitAtFirst(const QString &string, QChar ch)
+{
+ QStringView view = string;
+ return splitAtFirst(view, ch);
+}
+
+QTCREATOR_UTILS_EXPORT int endOfNextWord(const QString &string, int position)
+{
+ QTC_ASSERT(string.size() > position, return -1);
+
+ static const QString wordSeparators = QStringLiteral(" \t\n\r()[]{}<>");
+
+ const auto predicate = [](const QChar &c) { return wordSeparators.contains(c); };
+
+ auto it = string.begin() + position;
+ if (predicate(*it))
+ it = std::find_if_not(it, string.end(), predicate);
+
+ if (it == string.end())
+ return -1;
+
+ it = std::find_if(it, string.end(), predicate);
+ if (it == string.end())
+ return -1;
+
+ return std::distance(string.begin(), it);
+}
+
+MarkdownHighlighter::MarkdownHighlighter(QTextDocument *parent)
+ : QSyntaxHighlighter(parent)
+ , h2Brush(Qt::NoBrush)
+{
+ parent->setIndentWidth(30); // default value is 40
+}
+
+void MarkdownHighlighter::highlightBlock(const QString &text)
+{
+ if (text.isEmpty())
+ return;
+
+ QTextBlockFormat fmt = currentBlock().blockFormat();
+ QTextCursor cur(currentBlock());
+ if (fmt.hasProperty(QTextFormat::HeadingLevel)) {
+ fmt.setTopMargin(10);
+ fmt.setBottomMargin(10);
+
+ // Draw an underline for Heading 2, by creating a texture brush
+ // with the last pixel visible
+ if (fmt.property(QTextFormat::HeadingLevel) == 2) {
+ QTextCharFormat charFmt = currentBlock().charFormat();
+ charFmt.setBaselineOffset(15);
+ setFormat(0, text.length(), charFmt);
+
+ if (h2Brush.style() == Qt::NoBrush) {
+ const int height = QFontMetrics(charFmt.font()).height();
+ QImage image(1, height, QImage::Format_ARGB32);
+
+ image.fill(QColor(0, 0, 0, 0).rgba());
+ image.setPixel(0,
+ height - 1,
+ Utils::creatorTheme()->color(Theme::TextColorDisabled).rgba());
+
+ h2Brush = QBrush(image);
+ }
+ fmt.setBackground(h2Brush);
+ }
+ cur.setBlockFormat(fmt);
+ } else if (fmt.hasProperty(QTextFormat::BlockCodeLanguage) && fmt.indent() == 0) {
+ // set identation for code blocks
+ fmt.setIndent(1);
+ cur.setBlockFormat(fmt);
+ }
+
+ // Show the bulet points as filled circles
+ QTextList *list = cur.currentList();
+ if (list) {
+ QTextListFormat listFmt = list->format();
+ if (listFmt.indent() == 1 && listFmt.style() == QTextListFormat::ListCircle) {
+ listFmt.setStyle(QTextListFormat::ListDisc);
+ list->setFormat(listFmt);
+ }
+ }
+}
+
} // namespace Utils
diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h
index 5cf2978c07..3bab6110cf 100644
--- a/src/libs/utils/stringutils.h
+++ b/src/libs/utils/stringutils.h
@@ -5,8 +5,10 @@
#include "utils_global.h"
+#include <QBrush>
#include <QList>
#include <QString>
+#include <QSyntaxHighlighter>
#include <functional>
@@ -115,4 +117,20 @@ QTCREATOR_UTILS_EXPORT QString trimFront(const QString &string, QChar ch);
QTCREATOR_UTILS_EXPORT QString trimBack(const QString &string, QChar ch);
QTCREATOR_UTILS_EXPORT QString trim(const QString &string, QChar ch);
+QTCREATOR_UTILS_EXPORT QPair<QStringView, QStringView> splitAtFirst(const QString &string, QChar ch);
+QTCREATOR_UTILS_EXPORT QPair<QStringView, QStringView> splitAtFirst(const QStringView &stringView,
+ QChar ch);
+
+QTCREATOR_UTILS_EXPORT int endOfNextWord(const QString &string, int position = 0);
+
+class QTCREATOR_UTILS_EXPORT MarkdownHighlighter : public QSyntaxHighlighter
+{
+public:
+ MarkdownHighlighter(QTextDocument *parent);
+ void highlightBlock(const QString &text);
+
+private:
+ QBrush h2Brush;
+};
+
} // namespace Utils
diff --git a/src/plugins/coreplugin/styleanimator.cpp b/src/libs/utils/styleanimator.cpp
index f9d57a0d39..0597367732 100644
--- a/src/plugins/coreplugin/styleanimator.cpp
+++ b/src/libs/utils/styleanimator.cpp
@@ -3,9 +3,13 @@
#include "styleanimator.h"
-#include <utils/algorithm.h>
+#include "algorithm.h"
+#include <QPainter>
#include <QStyleOption>
+#include <QWidget>
+
+using namespace Utils;
Animation * StyleAnimator::widgetAnimation(const QWidget *widget) const
{
diff --git a/src/plugins/coreplugin/styleanimator.h b/src/libs/utils/styleanimator.h
index f12690aa71..08c4755cd1 100644
--- a/src/plugins/coreplugin/styleanimator.h
+++ b/src/libs/utils/styleanimator.h
@@ -3,13 +3,17 @@
#pragma once
+#include "utils_global.h"
+
+#include <QBasicTimer>
#include <QPointer>
#include <QTime>
-#include <QBasicTimer>
-#include <QStyle>
-#include <QPainter>
#include <QWidget>
+class QPainter;
+class QStyleOption;
+
+namespace Utils {
/*
* This is a set of helper classes to allow for widget animations in
* the style. Its mostly taken from Vista style so it should be fully documented
@@ -17,7 +21,7 @@
*
*/
-class Animation
+class QTCREATOR_UTILS_EXPORT Animation
{
public :
Animation() = default;
@@ -41,7 +45,7 @@ protected:
};
// Handles state transition animations
-class Transition : public Animation
+class QTCREATOR_UTILS_EXPORT Transition : public Animation
{
public :
Transition() = default;
@@ -54,7 +58,7 @@ public :
int m_duration = 100; //set time in ms to complete a state transition
};
-class StyleAnimator : public QObject
+class QTCREATOR_UTILS_EXPORT StyleAnimator : public QObject
{
Q_OBJECT
@@ -70,3 +74,4 @@ private:
QBasicTimer animationTimer;
QList <Animation*> animations;
};
+}
diff --git a/src/libs/utils/styledbar.cpp b/src/libs/utils/styledbar.cpp
index a20aba6e1a..4e7ec489fb 100644
--- a/src/libs/utils/styledbar.cpp
+++ b/src/libs/utils/styledbar.cpp
@@ -3,6 +3,8 @@
#include "styledbar.h"
+#include "stylehelper.h"
+
#include <QPainter>
#include <QStyleOption>
@@ -11,26 +13,26 @@ using namespace Utils;
StyledBar::StyledBar(QWidget *parent)
: QWidget(parent)
{
- setProperty("panelwidget", true);
- setProperty("panelwidget_singlerow", true);
- setProperty("lightColored", false);
+ StyleHelper::setPanelWidget(this);
+ StyleHelper::setPanelWidgetSingleRow(this);
+ setProperty(StyleHelper::C_LIGHT_COLORED, false);
}
void StyledBar::setSingleRow(bool singleRow)
{
- setProperty("panelwidget_singlerow", singleRow);
+ StyleHelper::setPanelWidgetSingleRow(this, singleRow);
}
bool StyledBar::isSingleRow() const
{
- return property("panelwidget_singlerow").toBool();
+ return property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool();
}
void StyledBar::setLightColored(bool lightColored)
{
if (isLightColored() == lightColored)
return;
- setProperty("lightColored", lightColored);
+ setProperty(StyleHelper::C_LIGHT_COLORED, lightColored);
const QList<QWidget *> children = findChildren<QWidget *>();
for (QWidget *childWidget : children)
childWidget->style()->polish(childWidget);
@@ -38,7 +40,7 @@ void StyledBar::setLightColored(bool lightColored)
bool StyledBar::isLightColored() const
{
- return property("lightColored").toBool();
+ return property(StyleHelper::C_LIGHT_COLORED).toBool();
}
void StyledBar::paintEvent(QPaintEvent *event)
diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp
index 1f57baf580..cc860e715a 100644
--- a/src/libs/utils/stylehelper.cpp
+++ b/src/libs/utils/stylehelper.cpp
@@ -12,6 +12,7 @@
#include <QFileInfo>
#include <QFontDatabase>
#include <QPainter>
+#include <QPainterPath>
#include <QPixmapCache>
#include <QStyleOption>
#include <QWindow>
@@ -36,6 +37,11 @@ static int range(float x, int min, int max)
namespace Utils {
+static StyleHelper::ToolbarStyle m_toolbarStyle = StyleHelper::defaultToolbarStyle;
+// Invalid by default, setBaseColor needs to be called at least once
+static QColor m_baseColor;
+static QColor m_requestedBaseColor;
+
QColor StyleHelper::mergedColors(const QColor &colorA, const QColor &colorB, int factor)
{
const int maxFactor = 100;
@@ -58,6 +64,36 @@ QColor StyleHelper::alphaBlendedColors(const QColor &colorA, const QColor &color
);
}
+QColor StyleHelper::sidebarHighlight()
+{
+ return QColor(255, 255, 255, 40);
+}
+
+QColor StyleHelper::sidebarShadow()
+{
+ return QColor(0, 0, 0, 40);
+}
+
+QColor StyleHelper::toolBarDropShadowColor()
+{
+ return QColor(0, 0, 0, 70);
+}
+
+int StyleHelper::navigationWidgetHeight()
+{
+ return m_toolbarStyle == ToolbarStyleCompact ? 24 : 30;
+}
+
+void StyleHelper::setToolbarStyle(ToolbarStyle style)
+{
+ m_toolbarStyle = style;
+}
+
+StyleHelper::ToolbarStyle StyleHelper::toolbarStyle()
+{
+ return m_toolbarStyle;
+}
+
qreal StyleHelper::sidebarFontSize()
{
return HostOsInfo::isMacHost() ? 10 : 7.5;
@@ -89,10 +125,6 @@ QColor StyleHelper::panelTextColor(bool lightColored)
return Qt::black;
}
-// Invalid by default, setBaseColor needs to be called at least once
-QColor StyleHelper::m_baseColor;
-QColor StyleHelper::m_requestedBaseColor;
-
QColor StyleHelper::baseColor(bool lightColored)
{
static const QColor windowColor = QApplication::palette().color(QPalette::Window);
@@ -101,6 +133,19 @@ QColor StyleHelper::baseColor(bool lightColored)
return (lightColored || windowColorAsBase) ? windowColor : m_baseColor;
}
+QColor StyleHelper::requestedBaseColor()
+{
+ return m_requestedBaseColor;
+}
+
+QColor StyleHelper::toolbarBaseColor(bool lightColored)
+{
+ if (creatorTheme()->flag(Theme::QDSTheme))
+ return creatorTheme()->color(Utils::Theme::DStoolbarBackground);
+ else
+ return StyleHelper::baseColor(lightColored);
+}
+
QColor StyleHelper::highlightColor(bool lightColored)
{
QColor result = baseColor(lightColored);
@@ -141,6 +186,11 @@ QColor StyleHelper::toolBarBorderColor()
clamp(base.value() * 0.80f));
}
+QColor StyleHelper::buttonTextColor()
+{
+ return QColor(0x4c4c4c);
+}
+
// We try to ensure that the actual color used are within
// reasonalbe bounds while generating the actual baseColor
// from the users request.
@@ -165,7 +215,7 @@ void StyleHelper::setBaseColor(const QColor &newcolor)
if (color.isValid() && color != m_baseColor) {
m_baseColor = color;
- const QList<QWidget *> widgets = QApplication::topLevelWidgets();
+ const QWidgetList widgets = QApplication::allWidgets();
for (QWidget *w : widgets)
w->update();
}
@@ -336,6 +386,117 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
painter->drawPixmap(xOffset, yOffset, pixmap);
}
+void StyleHelper::drawMinimalArrow(QStyle::PrimitiveElement element, QPainter *painter, const QStyleOption *option)
+{
+ if (option->rect.width() <= 1 || option->rect.height() <= 1)
+ return;
+
+ const qreal devicePixelRatio = painter->device()->devicePixelRatio();
+ const bool enabled = option->state & QStyle::State_Enabled;
+ QRect r = option->rect;
+ int size = qMin(r.height(), r.width());
+ QPixmap pixmap;
+ const QString pixmapName = QString::asprintf("StyleHelper::drawMinimalArrow-%d-%d-%d-%f",
+ element, size, enabled, devicePixelRatio);
+ if (!QPixmapCache::find(pixmapName, &pixmap)) {
+ QImage image(size * devicePixelRatio, size * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
+ image.fill(Qt::transparent);
+ QPainter painter(&image);
+ QStyleOption tweakedOption(*option);
+
+ double rotation = 0;
+ switch (element) {
+ case QStyle::PE_IndicatorArrowLeft:
+ rotation = 45;
+ break;
+ case QStyle::PE_IndicatorArrowUp:
+ rotation = 135;
+ break;
+ case QStyle::PE_IndicatorArrowRight:
+ rotation = 225;
+ break;
+ case QStyle::PE_IndicatorArrowDown:
+ rotation = 315;
+ break;
+ default:
+ break;
+ }
+
+ auto drawArrow = [&tweakedOption, rotation, &painter](const QRect &rect, const QColor &color) -> void
+ {
+ static const QCommonStyle* const style = qobject_cast<QCommonStyle*>(QApplication::style());
+ if (!style)
+ return;
+
+ // Workaround for QTCREATORBUG-28470
+ QPalette pal = tweakedOption.palette;
+ pal.setBrush(QPalette::Base, pal.text()); // Base and Text differ, causing a detachment.
+ // Inspired by tst_QPalette::cacheKey()
+ pal.setColor(QPalette::ButtonText, color.rgb());
+
+ tweakedOption.palette = pal;
+ tweakedOption.rect = rect;
+
+ painter.save();
+ painter.setOpacity(color.alphaF());
+
+ double minDim = std::min(rect.width(), rect.height());
+ double innerWidth = minDim/M_SQRT2;
+ int penWidth = std::max(innerWidth/4, 1.0);
+ innerWidth -= penWidth;
+
+ QPen pPen(pal.color(QPalette::ButtonText), penWidth);
+ pPen.setJoinStyle(Qt::MiterJoin);
+ painter.setBrush(pal.text());
+ painter.setPen(pPen);
+
+ painter.translate(rect.center());
+ painter.rotate(rotation);
+ painter.translate(-innerWidth/2, -innerWidth/2);
+
+ const QPointF points[3] = {
+ {0, 0},
+ {0, innerWidth},
+ {innerWidth, innerWidth}
+ };
+
+ painter.drawPolyline(points, 3);
+ painter.restore();
+ };
+
+ if (enabled) {
+ if (creatorTheme()->flag(Theme::ToolBarIconShadow))
+ drawArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
+ drawArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ } else {
+ drawArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ }
+ painter.end();
+ pixmap = QPixmap::fromImage(image);
+ pixmap.setDevicePixelRatio(devicePixelRatio);
+ QPixmapCache::insert(pixmapName, pixmap);
+ }
+ int xOffset = r.x() + (r.width() - size)/2;
+ int yOffset = r.y() + (r.height() - size)/2;
+ painter->drawPixmap(xOffset, yOffset, pixmap);
+}
+
+void StyleHelper::drawPanelBgRect(QPainter *painter, const QRectF &rect, const QBrush &brush)
+{
+ if (toolbarStyle() == ToolbarStyleCompact) {
+ painter->fillRect(rect.toRect(), brush);
+ } else {
+ constexpr int margin = 2;
+ constexpr int radius = 5;
+ QPainterPath path;
+ path.addRoundedRect(rect.adjusted(margin, margin, -margin, -margin), radius, radius);
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->fillPath(path, brush);
+ painter->restore();
+ }
+}
+
void StyleHelper::menuGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect)
{
if (StyleHelper::usePixmapCache()) {
@@ -359,6 +520,11 @@ void StyleHelper::menuGradient(QPainter *painter, const QRect &spanRect, const Q
}
}
+bool StyleHelper::usePixmapCache()
+{
+ return true;
+}
+
QPixmap StyleHelper::disabledSideBarIcon(const QPixmap &enabledicon)
{
QImage im = enabledicon.toImage().convertToFormat(QImage::Format_ARGB32);
@@ -531,6 +697,21 @@ QLinearGradient StyleHelper::statusBarGradient(const QRect &statusBarRect)
return grad;
}
+void StyleHelper::setPanelWidget(QWidget *widget, bool value)
+{
+ widget->setProperty(C_PANEL_WIDGET, value);
+}
+
+void StyleHelper::setPanelWidgetSingleRow(QWidget *widget, bool value)
+{
+ widget->setProperty(C_PANEL_WIDGET_SINGLE_ROW, value);
+}
+
+bool StyleHelper::isQDSTheme()
+{
+ return creatorTheme() ? creatorTheme()->flag(Theme::QDSTheme) : false;
+}
+
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<IconFontHelper> &parameters)
{
QFontDatabase a;
@@ -590,7 +771,7 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &i
painter.save();
painter.setPen(color);
painter.setFont(font);
- painter.drawText(QRectF(QPoint(0, 0), size), iconSymbol);
+ painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
painter.restore();
icon.addPixmap(pixmap);
diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h
index 75b82f38a2..2b898cb104 100644
--- a/src/libs/utils/stylehelper.h
+++ b/src/libs/utils/stylehelper.h
@@ -13,111 +13,150 @@ class QPainter;
class QRect;
// Note, this is exported but in a private header as qtopengl depends on it.
// We should consider adding this as a public helper function.
-void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly,
+ int transposed = 0);
QT_END_NAMESPACE
// Helper class holding all custom color values
-
-namespace Utils {
-class QTCREATOR_UTILS_EXPORT StyleHelper
+namespace Utils::StyleHelper {
+
+const unsigned int DEFAULT_BASE_COLOR = 0x666666;
+const int progressFadeAnimationDuration = 600;
+
+constexpr char C_ALIGN_ARROW[] = "alignarrow";
+constexpr char C_DRAW_LEFT_BORDER[] = "drawleftborder";
+constexpr char C_ELIDE_MODE[] = "elidemode";
+constexpr char C_HIDE_BORDER[] = "hideborder";
+constexpr char C_HIDE_ICON[] = "hideicon";
+constexpr char C_HIGHLIGHT_WIDGET[] = "highlightWidget";
+constexpr char C_LIGHT_COLORED[] = "lightColored";
+constexpr char C_MINI_SPLITTER[] = "minisplitter";
+constexpr char C_NOT_ELIDE_ASTERISK[] = "notelideasterisk";
+constexpr char C_NO_ARROW[] = "noArrow";
+constexpr char C_PANEL_WIDGET[] = "panelwidget";
+constexpr char C_PANEL_WIDGET_SINGLE_ROW[] = "panelwidget_singlerow";
+constexpr char C_SHOW_BORDER[] = "showborder";
+constexpr char C_TOP_BORDER[] = "topBorder";
+
+enum ToolbarStyle {
+ ToolbarStyleCompact,
+ ToolbarStyleRelaxed,
+};
+constexpr ToolbarStyle defaultToolbarStyle = ToolbarStyleCompact;
+
+// Height of the project explorer navigation bar
+QTCREATOR_UTILS_EXPORT int navigationWidgetHeight();
+QTCREATOR_UTILS_EXPORT void setToolbarStyle(ToolbarStyle style);
+QTCREATOR_UTILS_EXPORT ToolbarStyle toolbarStyle();
+QTCREATOR_UTILS_EXPORT qreal sidebarFontSize();
+QTCREATOR_UTILS_EXPORT QPalette sidebarFontPalette(const QPalette &original);
+
+// This is our color table, all colors derive from baseColor
+QTCREATOR_UTILS_EXPORT QColor requestedBaseColor();
+QTCREATOR_UTILS_EXPORT QColor baseColor(bool lightColored = false);
+QTCREATOR_UTILS_EXPORT QColor toolbarBaseColor(bool lightColored = false);
+QTCREATOR_UTILS_EXPORT QColor panelTextColor(bool lightColored = false);
+QTCREATOR_UTILS_EXPORT QColor highlightColor(bool lightColored = false);
+QTCREATOR_UTILS_EXPORT QColor shadowColor(bool lightColored = false);
+QTCREATOR_UTILS_EXPORT QColor borderColor(bool lightColored = false);
+QTCREATOR_UTILS_EXPORT QColor toolBarBorderColor();
+QTCREATOR_UTILS_EXPORT QColor buttonTextColor();
+QTCREATOR_UTILS_EXPORT QColor mergedColors(const QColor &colorA, const QColor &colorB,
+ int factor = 50);
+QTCREATOR_UTILS_EXPORT QColor alphaBlendedColors(const QColor &colorA, const QColor &colorB);
+
+QTCREATOR_UTILS_EXPORT QColor sidebarHighlight();
+QTCREATOR_UTILS_EXPORT QColor sidebarShadow();
+QTCREATOR_UTILS_EXPORT QColor toolBarDropShadowColor();
+QTCREATOR_UTILS_EXPORT QColor notTooBrightHighlightColor();
+
+// Sets the base color and makes sure all top level widgets are updated
+QTCREATOR_UTILS_EXPORT void setBaseColor(const QColor &color);
+
+// Draws a shaded anti-aliased arrow
+QTCREATOR_UTILS_EXPORT void drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
+ const QStyleOption *option);
+QTCREATOR_UTILS_EXPORT void drawMinimalArrow(QStyle::PrimitiveElement element, QPainter *painter,
+ const QStyleOption *option);
+
+QTCREATOR_UTILS_EXPORT void drawPanelBgRect(QPainter *painter, const QRectF &rect,
+ const QBrush &brush);
+
+// Gradients used for panels
+QTCREATOR_UTILS_EXPORT void horizontalGradient(QPainter *painter, const QRect &spanRect,
+ const QRect &clipRect, bool lightColored = false);
+QTCREATOR_UTILS_EXPORT void verticalGradient(QPainter *painter, const QRect &spanRect,
+ const QRect &clipRect, bool lightColored = false);
+QTCREATOR_UTILS_EXPORT void menuGradient(QPainter *painter, const QRect &spanRect,
+ const QRect &clipRect);
+QTCREATOR_UTILS_EXPORT bool usePixmapCache();
+
+QTCREATOR_UTILS_EXPORT QPixmap disabledSideBarIcon(const QPixmap &enabledicon);
+QTCREATOR_UTILS_EXPORT void drawIconWithShadow(const QIcon &icon, const QRect &rect, QPainter *p,
+ QIcon::Mode iconMode, int dipRadius = 3,
+ const QColor &color = QColor(0, 0, 0, 130),
+ const QPoint &dipOffset = QPoint(1, -2));
+QTCREATOR_UTILS_EXPORT void drawCornerImage(const QImage &img, QPainter *painter, const QRect &rect,
+ int left = 0, int top = 0, int right = 0,
+ int bottom = 0);
+
+QTCREATOR_UTILS_EXPORT void tintImage(QImage &img, const QColor &tintColor);
+QTCREATOR_UTILS_EXPORT QLinearGradient statusBarGradient(const QRect &statusBarRect);
+QTCREATOR_UTILS_EXPORT void setPanelWidget(QWidget *widget, bool value = true);
+QTCREATOR_UTILS_EXPORT void setPanelWidgetSingleRow(QWidget *widget, bool value = true);
+
+QTCREATOR_UTILS_EXPORT bool isQDSTheme();
+
+class IconFontHelper
{
public:
- static const unsigned int DEFAULT_BASE_COLOR = 0x666666;
- static const int progressFadeAnimationDuration = 600;
-
- // Height of the project explorer navigation bar
- static int navigationWidgetHeight() { return 24; }
- static qreal sidebarFontSize();
- static QPalette sidebarFontPalette(const QPalette &original);
-
- // This is our color table, all colors derive from baseColor
- static QColor requestedBaseColor() { return m_requestedBaseColor; }
- static QColor baseColor(bool lightColored = false);
- static QColor panelTextColor(bool lightColored = false);
- static QColor highlightColor(bool lightColored = false);
- static QColor shadowColor(bool lightColored = false);
- static QColor borderColor(bool lightColored = false);
- static QColor toolBarBorderColor();
- static QColor buttonTextColor() { return QColor(0x4c4c4c); }
- static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50);
- static QColor alphaBlendedColors(const QColor &colorA, const QColor &colorB);
-
- static QColor sidebarHighlight() { return QColor(255, 255, 255, 40); }
- static QColor sidebarShadow() { return QColor(0, 0, 0, 40); }
-
- static QColor toolBarDropShadowColor() { return QColor(0, 0, 0, 70); }
-
- static QColor notTooBrightHighlightColor();
-
- // Sets the base color and makes sure all top level widgets are updated
- static void setBaseColor(const QColor &color);
-
- // Draws a shaded anti-aliased arrow
- static void drawArrow(QStyle::PrimitiveElement element, QPainter *painter, const QStyleOption *option);
-
- // Gradients used for panels
- static void horizontalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored = false);
- static void verticalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored = false);
- static void menuGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect);
- static bool usePixmapCache() { return true; }
-
- static QPixmap disabledSideBarIcon(const QPixmap &enabledicon);
- static void drawIconWithShadow(const QIcon &icon, const QRect &rect, QPainter *p, QIcon::Mode iconMode,
- int dipRadius = 3, const QColor &color = QColor(0, 0, 0, 130),
- const QPoint &dipOffset = QPoint(1, -2));
- static void drawCornerImage(const QImage &img, QPainter *painter, const QRect &rect,
- int left = 0, int top = 0, int right = 0, int bottom = 0);
-
- static void tintImage(QImage &img, const QColor &tintColor);
- static QLinearGradient statusBarGradient(const QRect &statusBarRect);
-
- class IconFontHelper
- {
- public:
- IconFontHelper(const QString &iconSymbol,
- const QColor &color,
- const QSize &size,
- QIcon::Mode mode = QIcon::Normal,
- QIcon::State state = QIcon::Off)
- : m_iconSymbol(iconSymbol)
- , m_color(color)
- , m_size(size)
- , m_mode(mode)
- , m_state(state)
- {}
-
- QString iconSymbol() const { return m_iconSymbol; }
- QColor color() const { return m_color; }
- QSize size() const { return m_size; }
- QIcon::Mode mode() const { return m_mode; }
- QIcon::State state() const { return m_state; }
-
- private:
- QString m_iconSymbol;
- QColor m_color;
- QSize m_size;
- QIcon::Mode m_mode;
- QIcon::State m_state;
- };
-
- static QIcon getIconFromIconFont(const QString &fontName, const QList<IconFontHelper> &parameters);
- static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color);
- static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize);
- static QIcon getCursorFromIconFont(const QString &fontname, const QString &cursorFill, const QString &cursorOutline,
- int fontSize, int iconSize);
-
- static QString dpiSpecificImageFile(const QString &fileName);
- static QString imageFileWithResolution(const QString &fileName, int dpr);
- static QList<int> availableImageResolutions(const QString &fileName);
-
- static double luminance(const QColor &color);
- static bool isReadableOn(const QColor &background, const QColor &foreground);
- // returns a foreground color readable on background (desiredForeground if already readable or adaption fails)
- static QColor ensureReadableOn(const QColor &background, const QColor &desiredForeground);
+ IconFontHelper(const QString &iconSymbol,
+ const QColor &color,
+ const QSize &size,
+ QIcon::Mode mode = QIcon::Normal,
+ QIcon::State state = QIcon::Off)
+ : m_iconSymbol(iconSymbol)
+ , m_color(color)
+ , m_size(size)
+ , m_mode(mode)
+ , m_state(state)
+ {}
+
+ QString iconSymbol() const { return m_iconSymbol; }
+ QColor color() const { return m_color; }
+ QSize size() const { return m_size; }
+ QIcon::Mode mode() const { return m_mode; }
+ QIcon::State state() const { return m_state; }
private:
- static QColor m_baseColor;
- static QColor m_requestedBaseColor;
+ QString m_iconSymbol;
+ QColor m_color;
+ QSize m_size;
+ QIcon::Mode m_mode;
+ QIcon::State m_state;
};
-} // namespace Utils
+QTCREATOR_UTILS_EXPORT QIcon getIconFromIconFont(const QString &fontName,
+ const QList<IconFontHelper> &parameters);
+QTCREATOR_UTILS_EXPORT QIcon getIconFromIconFont(const QString &fontName,
+ const QString &iconSymbol, int fontSize,
+ int iconSize, QColor color);
+QTCREATOR_UTILS_EXPORT QIcon getIconFromIconFont(const QString &fontName,
+ const QString &iconSymbol, int fontSize,
+ int iconSize);
+QTCREATOR_UTILS_EXPORT QIcon getCursorFromIconFont(const QString &fontname,
+ const QString &cursorFill,
+ const QString &cursorOutline,
+ int fontSize, int iconSize);
+
+QTCREATOR_UTILS_EXPORT QString dpiSpecificImageFile(const QString &fileName);
+QTCREATOR_UTILS_EXPORT QString imageFileWithResolution(const QString &fileName, int dpr);
+QTCREATOR_UTILS_EXPORT QList<int> availableImageResolutions(const QString &fileName);
+
+QTCREATOR_UTILS_EXPORT double luminance(const QColor &color);
+QTCREATOR_UTILS_EXPORT bool isReadableOn(const QColor &background, const QColor &foreground);
+// returns a foreground color readable on background (desiredForeground if already readable or adaption fails)
+QTCREATOR_UTILS_EXPORT QColor ensureReadableOn(const QColor &background,
+ const QColor &desiredForeground);
+
+} // namespace Utils::StyleHelper
diff --git a/src/libs/utils/terminalcommand.cpp b/src/libs/utils/terminalcommand.cpp
index c25b379f32..102fc42e05 100644
--- a/src/libs/utils/terminalcommand.cpp
+++ b/src/libs/utils/terminalcommand.cpp
@@ -65,13 +65,7 @@ TerminalCommand TerminalCommand::defaultTerminalEmulator()
if (defaultTerm.command.isEmpty()) {
if (HostOsInfo::isMacHost()) {
- const FilePath termCmd = FilePath::fromString(QCoreApplication::applicationDirPath())
- / "../Resources/scripts/openTerminal.py";
- if (termCmd.exists())
- defaultTerm = {termCmd, "", ""};
- else
- defaultTerm = {"/usr/X11/bin/xterm", "", "-e"};
-
+ return {"Terminal.app", "", ""};
} else if (HostOsInfo::isAnyUnixHost()) {
defaultTerm = {"xterm", "", "-e"};
const Environment env = Environment::systemEnvironment();
diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp
new file mode 100644
index 0000000000..07ebbd98d2
--- /dev/null
+++ b/src/libs/utils/terminalhooks.cpp
@@ -0,0 +1,225 @@
+// 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 "terminalhooks.h"
+
+#include "filepath.h"
+#include "process.h"
+#include "terminalcommand.h"
+#include "terminalinterface.h"
+#include "utilstr.h"
+
+#include <QMutex>
+#include <QTemporaryFile>
+
+namespace Utils::Terminal {
+
+FilePath defaultShellForDevice(const FilePath &deviceRoot)
+{
+ if (deviceRoot.osType() == OsTypeWindows)
+ return deviceRoot.withNewPath("cmd.exe").searchInPath();
+
+ const Environment env = deviceRoot.deviceEnvironment();
+ FilePath shell = FilePath::fromUserInput(env.value_or("SHELL", "/bin/sh"));
+
+ if (!shell.isAbsolutePath())
+ shell = env.searchInPath(shell.nativePath());
+
+ if (shell.isEmpty())
+ return shell;
+
+ return deviceRoot.withNewMappedPath(shell);
+}
+
+class ExternalTerminalProcessImpl final : public TerminalInterface
+{
+ class ProcessStubCreator : public StubCreator
+ {
+ public:
+ ProcessStubCreator(ExternalTerminalProcessImpl *interface)
+ : m_interface(interface)
+ {}
+
+ ~ProcessStubCreator() override = default;
+
+ expected_str<qint64> startStubProcess(const ProcessSetupData &setupData) override
+ {
+ const TerminalCommand terminal = TerminalCommand::terminalEmulator();
+
+ if (HostOsInfo::isMacHost() && terminal.command == "Terminal.app") {
+ QTemporaryFile f;
+ f.setAutoRemove(false);
+ f.open();
+ f.setPermissions(QFile::ExeUser | QFile::ReadUser | QFile::WriteUser);
+ f.write("#!/bin/sh\n");
+ f.write(QString("cd %1\n").arg(setupData.m_workingDirectory.nativePath()).toUtf8());
+ f.write("clear\n");
+ f.write(QString("exec '%1' %2\n")
+ .arg(setupData.m_commandLine.executable().nativePath())
+ .arg(setupData.m_commandLine.arguments())
+ .toUtf8());
+ f.close();
+
+ const QString path = f.fileName();
+ const QString exe
+ = QString("tell app \"Terminal\" to do script \"'%1'; rm -f '%1'; exit\"")
+ .arg(path);
+
+ Process process;
+
+ process.setCommand(
+ {"osascript", {"-e", "tell app \"Terminal\" to activate", "-e", exe}});
+ process.runBlocking();
+
+ if (process.exitCode() != 0) {
+ return make_unexpected(Tr::tr("Failed to start terminal process: \"%1\"")
+ .arg(process.errorString()));
+ }
+
+ return 0;
+ }
+
+ bool detached = setupData.m_terminalMode == TerminalMode::Detached;
+
+ Process *process = new Process(detached ? nullptr : this);
+ if (detached)
+ QObject::connect(process, &Process::done, process, &Process::deleteLater);
+
+ QObject::connect(process,
+ &Process::done,
+ m_interface,
+ &ExternalTerminalProcessImpl::onStubExited);
+
+ process->setWorkingDirectory(setupData.m_workingDirectory);
+
+ if constexpr (HostOsInfo::isWindowsHost()) {
+ process->setCommand(setupData.m_commandLine);
+ process->setCreateConsoleOnWindows(true);
+ process->setProcessMode(ProcessMode::Writer);
+ } else {
+ QString extraArgsFromOptions = detached ? terminal.openArgs : terminal.executeArgs;
+ CommandLine cmdLine = {terminal.command, {}};
+ if (!extraArgsFromOptions.isEmpty())
+ cmdLine.addArgs(extraArgsFromOptions, CommandLine::Raw);
+ cmdLine.addCommandLineAsArgs(setupData.m_commandLine, CommandLine::Raw);
+ process->setCommand(cmdLine);
+ }
+
+ process->start();
+ process->waitForStarted();
+ if (process->error() != QProcess::UnknownError) {
+ return make_unexpected(
+ Tr::tr("Failed to start terminal process: \"%1\"").arg(process->errorString()));
+ }
+
+ qint64 pid = process->processId();
+
+ return pid;
+ }
+
+ ExternalTerminalProcessImpl *m_interface;
+ };
+
+public:
+ ExternalTerminalProcessImpl() { setStubCreator(new ProcessStubCreator(this)); }
+};
+
+class HooksPrivate
+{
+public:
+ HooksPrivate()
+ : m_getTerminalCommandsForDevicesHook([] { return QList<NameAndCommandLine>{}; })
+ {
+ auto openTerminal = [](const OpenTerminalParameters &parameters) {
+ DeviceFileHooks::instance().openTerminal(parameters.workingDirectory.value_or(
+ FilePath{}),
+ parameters.environment.value_or(Environment{}));
+ };
+ auto createProcessInterface = []() { return new ExternalTerminalProcessImpl(); };
+
+ addCallbackSet("External", {openTerminal, createProcessInterface});
+ }
+
+ void addCallbackSet(const QString &name, const Hooks::CallbackSet &callbackSet)
+ {
+ QMutexLocker lk(&m_mutex);
+ m_callbackSets.push_back(qMakePair(name, callbackSet));
+
+ m_createTerminalProcessInterface
+ = m_callbackSets.back().second.createTerminalProcessInterface;
+ m_openTerminal = m_callbackSets.back().second.openTerminal;
+ }
+
+ void removeCallbackSet(const QString &name)
+ {
+ if (name == "External")
+ return;
+
+ QMutexLocker lk(&m_mutex);
+ m_callbackSets.removeIf([name](const auto &pair) { return pair.first == name; });
+
+ m_createTerminalProcessInterface
+ = m_callbackSets.back().second.createTerminalProcessInterface;
+ m_openTerminal = m_callbackSets.back().second.openTerminal;
+ }
+
+ Hooks::CreateTerminalProcessInterface createTerminalProcessInterface()
+ {
+ QMutexLocker lk(&m_mutex);
+ return m_createTerminalProcessInterface;
+ }
+
+ Hooks::OpenTerminal openTerminal()
+ {
+ QMutexLocker lk(&m_mutex);
+ return m_openTerminal;
+ }
+
+ Hooks::GetTerminalCommandsForDevicesHook m_getTerminalCommandsForDevicesHook;
+
+private:
+ Hooks::OpenTerminal m_openTerminal;
+ Hooks::CreateTerminalProcessInterface m_createTerminalProcessInterface;
+
+ QMutex m_mutex;
+ QList<QPair<QString, Hooks::CallbackSet>> m_callbackSets;
+};
+
+Hooks &Hooks::instance()
+{
+ static Hooks manager;
+ return manager;
+}
+
+Hooks::Hooks()
+ : d(new HooksPrivate())
+{}
+
+Hooks::~Hooks() = default;
+
+void Hooks::openTerminal(const OpenTerminalParameters &parameters) const
+{
+ d->openTerminal()(parameters);
+}
+
+ProcessInterface *Hooks::createTerminalProcessInterface() const
+{
+ return d->createTerminalProcessInterface()();
+}
+
+Hooks::GetTerminalCommandsForDevicesHook &Hooks::getTerminalCommandsForDevicesHook()
+{
+ return d->m_getTerminalCommandsForDevicesHook;
+}
+
+void Hooks::addCallbackSet(const QString &name, const CallbackSet &callbackSet)
+{
+ d->addCallbackSet(name, callbackSet);
+}
+
+void Hooks::removeCallbackSet(const QString &name)
+{
+ d->removeCallbackSet(name);
+}
+
+} // namespace Utils::Terminal
diff --git a/src/libs/utils/terminalhooks.h b/src/libs/utils/terminalhooks.h
new file mode 100644
index 0000000000..b37c51517c
--- /dev/null
+++ b/src/libs/utils/terminalhooks.h
@@ -0,0 +1,91 @@
+// 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
+
+#pragma once
+
+#include "commandline.h"
+#include "environment.h"
+#include "filepath.h"
+#include "id.h"
+
+#include <functional>
+#include <memory>
+
+namespace Utils {
+class ProcessInterface;
+
+template<typename R, typename... Params>
+class Hook
+{
+ Q_DISABLE_COPY_MOVE(Hook)
+
+public:
+ using Callback = std::function<R(Params...)>;
+
+public:
+ Hook() = delete;
+
+ explicit Hook(Callback defaultCallback) { set(defaultCallback); }
+
+ void set(Callback cb) { m_callback = cb; }
+ R operator()(Params &&...params) { return m_callback(std::forward<Params>(params)...); }
+
+private:
+ Callback m_callback;
+};
+
+namespace Terminal {
+class HooksPrivate;
+
+enum class ExitBehavior { Close, Restart, Keep };
+
+struct OpenTerminalParameters
+{
+ std::optional<CommandLine> shellCommand;
+ std::optional<FilePath> workingDirectory;
+ std::optional<Environment> environment;
+ ExitBehavior m_exitBehavior{ExitBehavior::Close};
+ std::optional<Id> identifier{std::nullopt};
+};
+
+struct NameAndCommandLine
+{
+ QString name;
+ CommandLine commandLine;
+};
+
+QTCREATOR_UTILS_EXPORT FilePath defaultShellForDevice(const FilePath &deviceRoot);
+
+class QTCREATOR_UTILS_EXPORT Hooks
+{
+public:
+ using OpenTerminal = std::function<void(const OpenTerminalParameters &)>;
+ using CreateTerminalProcessInterface = std::function<ProcessInterface *()>;
+
+ struct CallbackSet
+ {
+ OpenTerminal openTerminal;
+ CreateTerminalProcessInterface createTerminalProcessInterface;
+ };
+
+ using GetTerminalCommandsForDevicesHook = Hook<QList<NameAndCommandLine>>;
+
+public:
+ static Hooks &instance();
+ ~Hooks();
+
+ GetTerminalCommandsForDevicesHook &getTerminalCommandsForDevicesHook();
+
+ void openTerminal(const OpenTerminalParameters &parameters) const;
+ ProcessInterface *createTerminalProcessInterface() const;
+
+ void addCallbackSet(const QString &name, const CallbackSet &callbackSet);
+ void removeCallbackSet(const QString &name);
+
+private:
+ Hooks();
+ std::unique_ptr<HooksPrivate> d;
+};
+
+} // namespace Terminal
+} // namespace Utils
diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp
new file mode 100644
index 0000000000..6767c16c61
--- /dev/null
+++ b/src/libs/utils/terminalinterface.cpp
@@ -0,0 +1,433 @@
+// 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 "terminalinterface.h"
+
+#include "utilstr.h"
+
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QLoggingCategory>
+#include <QTemporaryDir>
+#include <QTemporaryFile>
+#include <QTextStream>
+#include <QThread>
+#include <QTimer>
+
+Q_LOGGING_CATEGORY(terminalInterfaceLog, "qtc.terminalinterface", QtWarningMsg)
+
+namespace Utils {
+
+static QString msgCommChannelFailed(const QString &error)
+{
+ return Tr::tr("Cannot set up communication channel: %1").arg(error);
+}
+
+static QString msgCannotCreateTempFile(const QString &why)
+{
+ return Tr::tr("Cannot create temporary file: %1").arg(why);
+}
+
+static QString msgCannotWriteTempFile()
+{
+ return Tr::tr("Cannot write temporary file. Disk full?");
+}
+
+static QString msgCannotCreateTempDir(const QString &dir, const QString &why)
+{
+ return Tr::tr("Cannot create temporary directory \"%1\": %2").arg(dir, why);
+}
+
+static QString msgUnexpectedOutput(const QByteArray &what)
+{
+ return Tr::tr("Unexpected output from helper program (%1).").arg(QString::fromLatin1(what));
+}
+
+static QString msgCannotChangeToWorkDir(const FilePath &dir, const QString &why)
+{
+ return Tr::tr("Cannot change to working directory \"%1\": %2").arg(dir.toString(), why);
+}
+
+static QString msgCannotExecute(const QString &p, const QString &why)
+{
+ return Tr::tr("Cannot execute \"%1\": %2").arg(p, why);
+}
+
+static QString msgPromptToClose()
+{
+ // Shown in a terminal which might have a different character set on Windows.
+ return Tr::tr("Press <RETURN> to close this window...");
+}
+
+class TerminalInterfacePrivate : public QObject
+{
+ Q_OBJECT
+public:
+ TerminalInterfacePrivate(TerminalInterface *p, bool waitOnExit)
+ : q(p)
+ , waitOnExit(waitOnExit)
+ {
+ connect(&stubServer,
+ &QLocalServer::newConnection,
+ q,
+ &TerminalInterface::onNewStubConnection);
+ }
+
+public:
+ QLocalServer stubServer;
+ QLocalSocket *stubSocket = nullptr;
+
+ int stubProcessId = 0;
+ int inferiorProcessId = 0;
+ int inferiorThreadId = 0;
+
+ std::unique_ptr<QTemporaryFile> envListFile;
+ QTemporaryDir tempDir;
+
+ std::unique_ptr<QTimer> stubConnectTimeoutTimer;
+
+ ProcessResultData processResultData;
+ TerminalInterface *q;
+
+ StubCreator *stubCreator{nullptr};
+
+ const bool waitOnExit;
+};
+
+TerminalInterface::TerminalInterface(bool waitOnExit)
+ : d(new TerminalInterfacePrivate(this, waitOnExit))
+{}
+
+TerminalInterface::~TerminalInterface()
+{
+ if (d->stubSocket && d->stubSocket->state() == QLocalSocket::ConnectedState) {
+ if (d->inferiorProcessId)
+ killInferiorProcess();
+ killStubProcess();
+ }
+ if (d->stubCreator)
+ d->stubCreator->deleteLater();
+ delete d;
+}
+
+void TerminalInterface::setStubCreator(StubCreator *creator)
+{
+ d->stubCreator = creator;
+}
+
+int TerminalInterface::inferiorProcessId() const
+{
+ return d->inferiorProcessId;
+}
+
+int TerminalInterface::inferiorThreadId() const
+{
+ return d->inferiorThreadId;
+}
+
+static QString errnoToString(int code)
+{
+ return QString::fromLocal8Bit(strerror(code));
+}
+
+void TerminalInterface::onNewStubConnection()
+{
+ d->stubConnectTimeoutTimer.reset();
+
+ d->stubSocket = d->stubServer.nextPendingConnection();
+ if (!d->stubSocket)
+ return;
+
+ connect(d->stubSocket, &QIODevice::readyRead, this, &TerminalInterface::onStubReadyRead);
+
+ if (HostOsInfo::isAnyUnixHost())
+ connect(d->stubSocket, &QLocalSocket::disconnected, this, &TerminalInterface::onStubExited);
+}
+
+void TerminalInterface::onStubExited()
+{
+ // The stub exit might get noticed before we read the pid for the kill on Windows
+ // or the error status elsewhere.
+ if (d->stubSocket && d->stubSocket->state() == QLocalSocket::ConnectedState)
+ d->stubSocket->waitForDisconnected();
+
+ shutdownStubServer();
+ d->envListFile.reset();
+
+ if (d->inferiorProcessId)
+ emitFinished(-1, QProcess::CrashExit);
+}
+
+void TerminalInterface::onStubReadyRead()
+{
+ while (d->stubSocket && d->stubSocket->canReadLine()) {
+ QByteArray out = d->stubSocket->readLine();
+ out.chop(1); // remove newline
+ if (out.startsWith("err:chdir ")) {
+ emitError(QProcess::FailedToStart,
+ msgCannotChangeToWorkDir(m_setup.m_workingDirectory,
+ errnoToString(out.mid(10).toInt())));
+ } else if (out.startsWith("err:exec ")) {
+ emitError(QProcess::FailedToStart,
+ msgCannotExecute(m_setup.m_commandLine.executable().toString(),
+ errnoToString(out.mid(9).toInt())));
+ } else if (out.startsWith("spid ")) {
+ d->envListFile.reset();
+ d->envListFile = nullptr;
+ } else if (out.startsWith("pid ")) {
+ d->inferiorProcessId = out.mid(4).toInt();
+ emit started(d->inferiorProcessId, d->inferiorThreadId);
+ } else if (out.startsWith("thread ")) { // Windows only
+ d->inferiorThreadId = out.mid(7).toLongLong();
+ } else if (out.startsWith("exit ")) {
+ emitFinished(out.mid(5).toInt(), QProcess::NormalExit);
+ } else if (out.startsWith("crash ")) {
+ emitFinished(out.mid(6).toInt(), QProcess::CrashExit);
+ } else {
+ emitError(QProcess::UnknownError, msgUnexpectedOutput(out));
+ break;
+ }
+ }
+}
+
+expected_str<void> TerminalInterface::startStubServer()
+{
+ if (HostOsInfo::isWindowsHost()) {
+ if (d->stubServer.listen(QString::fromLatin1("creator-%1-%2")
+ .arg(QCoreApplication::applicationPid())
+ .arg(rand())))
+ return {};
+ return make_unexpected(d->stubServer.errorString());
+ }
+
+ // We need to put the socket in a private directory, as some systems simply do not
+ // check the file permissions of sockets.
+ if (!QDir(d->tempDir.path())
+ .mkdir("socket")) { // QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
+ return make_unexpected(msgCannotCreateTempDir(d->tempDir.filePath("socket"),
+ QString::fromLocal8Bit(strerror(errno))));
+ }
+
+ if (!QFile::setPermissions(d->tempDir.filePath("socket"),
+ QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)) {
+ return make_unexpected(Tr::tr("Cannot set permissions on temporary directory \"%1\": %2")
+ .arg(d->tempDir.filePath("socket"))
+ .arg(QString::fromLocal8Bit(strerror(errno))));
+ }
+
+ const QString socketPath = d->tempDir.filePath("socket/stub-socket");
+ if (!d->stubServer.listen(socketPath)) {
+ return make_unexpected(
+ Tr::tr("Cannot create socket \"%1\": %2").arg(socketPath, d->stubServer.errorString()));
+ }
+ return {};
+}
+
+void TerminalInterface::shutdownStubServer()
+{
+ if (d->stubSocket) {
+ // Read potentially remaining data
+ onStubReadyRead();
+ // avoid getting queued readyRead signals
+ d->stubSocket->disconnect();
+ // we might be called from the disconnected signal of stubSocket
+ d->stubSocket->deleteLater();
+ }
+ d->stubSocket = nullptr;
+ if (d->stubServer.isListening())
+ d->stubServer.close();
+}
+
+void TerminalInterface::emitError(QProcess::ProcessError error, const QString &errorString)
+{
+ d->processResultData.m_error = error;
+ d->processResultData.m_errorString = errorString;
+ if (error == QProcess::FailedToStart)
+ emit done(d->processResultData);
+}
+
+void TerminalInterface::emitFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ d->inferiorProcessId = 0;
+ d->inferiorThreadId = 0;
+ d->processResultData.m_exitCode = exitCode;
+ d->processResultData.m_exitStatus = exitStatus;
+ emit done(d->processResultData);
+}
+
+bool TerminalInterface::isRunning() const
+{
+ return d->stubSocket && d->stubSocket->isOpen();
+}
+
+void TerminalInterface::cleanupAfterStartFailure(const QString &errorMessage)
+{
+ shutdownStubServer();
+ emitError(QProcess::FailedToStart, errorMessage);
+ d->envListFile.reset();
+}
+
+void TerminalInterface::sendCommand(char c)
+{
+ if (d->stubSocket && d->stubSocket->isWritable()) {
+ d->stubSocket->write(&c, 1);
+ d->stubSocket->flush();
+ }
+}
+
+void TerminalInterface::killInferiorProcess()
+{
+ sendCommand('k');
+ if (d->stubSocket)
+ d->stubSocket->waitForReadyRead();
+}
+
+void TerminalInterface::killStubProcess()
+{
+ if (!isRunning())
+ return;
+
+ sendCommand('s');
+ if (d->stubSocket)
+ d->stubSocket->waitForReadyRead();
+ shutdownStubServer();
+}
+
+void TerminalInterface::start()
+{
+ if (isRunning())
+ return;
+
+ if (m_setup.m_terminalMode == TerminalMode::Detached) {
+ expected_str<qint64> result;
+ QMetaObject::invokeMethod(
+ d->stubCreator,
+ [this, &result] { result = d->stubCreator->startStubProcess(m_setup); },
+ d->stubCreator->thread() == QThread::currentThread() ? Qt::DirectConnection
+ : Qt::BlockingQueuedConnection);
+
+ if (result) {
+ emit started(*result, 0);
+ emitFinished(0, QProcess::NormalExit);
+ } else {
+ emitError(QProcess::FailedToStart, result.error());
+ }
+ return;
+ }
+
+ const expected_str<void> result = startStubServer();
+ if (!result) {
+ emitError(QProcess::FailedToStart, msgCommChannelFailed(result.error()));
+ return;
+ }
+
+ Environment finalEnv = m_setup.m_environment;
+
+ if (HostOsInfo::isWindowsHost()) {
+ if (!finalEnv.hasKey("PATH")) {
+ const QString path = qtcEnvironmentVariable("PATH");
+ if (!path.isEmpty())
+ finalEnv.set("PATH", path);
+ }
+ if (!finalEnv.hasKey("SystemRoot")) {
+ const QString systemRoot = qtcEnvironmentVariable("SystemRoot");
+ if (!systemRoot.isEmpty())
+ finalEnv.set("SystemRoot", systemRoot);
+ }
+ } else if (HostOsInfo::isMacHost()) {
+ finalEnv.set("TERM", "xterm-256color");
+ }
+
+ if (finalEnv.hasChanges()) {
+ d->envListFile = std::make_unique<QTemporaryFile>(this);
+ if (!d->envListFile->open()) {
+ cleanupAfterStartFailure(msgCannotCreateTempFile(d->envListFile->errorString()));
+ return;
+ }
+ QTextStream stream(d->envListFile.get());
+ finalEnv.forEachEntry([&stream](const QString &key, const QString &value, bool) {
+ stream << key << '=' << value << '\0';
+ });
+
+ if (d->envListFile->error() != QFile::NoError) {
+ cleanupAfterStartFailure(msgCannotWriteTempFile());
+ return;
+ }
+ }
+
+ const FilePath stubPath = FilePath::fromUserInput(QCoreApplication::applicationDirPath())
+ .pathAppended(QLatin1String(RELATIVE_LIBEXEC_PATH))
+ .pathAppended((HostOsInfo::isWindowsHost()
+ ? QLatin1String("qtcreator_process_stub.exe")
+ : QLatin1String("qtcreator_process_stub")));
+
+ CommandLine cmd{stubPath, {"-s", d->stubServer.fullServerName()}};
+
+ if (!m_setup.m_workingDirectory.isEmpty())
+ cmd.addArgs({"-w", m_setup.m_workingDirectory.nativePath()});
+
+ if (m_setup.m_terminalMode == TerminalMode::Debug)
+ cmd.addArg("-d");
+
+ if (terminalInterfaceLog().isDebugEnabled())
+ cmd.addArg("-v");
+
+ if (d->envListFile)
+ cmd.addArgs({"-e", d->envListFile->fileName()});
+
+ cmd.addArgs({"--wait", d->waitOnExit ? msgPromptToClose() : ""});
+
+ cmd.addArgs({"--", m_setup.m_commandLine.executable().nativePath()});
+ cmd.addArgs(m_setup.m_commandLine.arguments(), CommandLine::Raw);
+
+ QTC_ASSERT(d->stubCreator, return);
+
+ ProcessSetupData stubSetupData = m_setup;
+ stubSetupData.m_commandLine = cmd;
+
+ QMetaObject::invokeMethod(
+ d->stubCreator,
+ [stubSetupData, this] { d->stubCreator->startStubProcess(stubSetupData); },
+ d->stubCreator->thread() == QThread::currentThread() ? Qt::DirectConnection
+ : Qt::BlockingQueuedConnection);
+
+ d->stubConnectTimeoutTimer = std::make_unique<QTimer>();
+
+ connect(d->stubConnectTimeoutTimer.get(), &QTimer::timeout, this, [this] {
+ killInferiorProcess();
+ killStubProcess();
+ });
+ d->stubConnectTimeoutTimer->setSingleShot(true);
+ d->stubConnectTimeoutTimer->start(10000);
+}
+
+qint64 TerminalInterface::write(const QByteArray &data)
+{
+ Q_UNUSED(data);
+ QTC_CHECK(false);
+ return -1;
+}
+void TerminalInterface::sendControlSignal(ControlSignal controlSignal)
+{
+ QTC_ASSERT(m_setup.m_terminalMode != TerminalMode::Detached, return);
+
+ switch (controlSignal) {
+ case ControlSignal::Terminate:
+ case ControlSignal::Kill:
+ killInferiorProcess();
+ break;
+ case ControlSignal::Interrupt:
+ sendCommand('i');
+ break;
+ case ControlSignal::KickOff:
+ sendCommand('c');
+ break;
+ case ControlSignal::CloseWriteChannel:
+ QTC_CHECK(false);
+ break;
+ }
+}
+
+} // namespace Utils
+
+#include "terminalinterface.moc"
diff --git a/src/libs/utils/terminalinterface.h b/src/libs/utils/terminalinterface.h
new file mode 100644
index 0000000000..a1960e7b96
--- /dev/null
+++ b/src/libs/utils/terminalinterface.h
@@ -0,0 +1,61 @@
+// 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
+
+#pragma once
+
+#include "commandline.h"
+#include "expected.h"
+#include "processinterface.h"
+
+namespace Utils {
+
+class TerminalInterfacePrivate;
+
+class StubCreator : public QObject
+{
+public:
+ virtual expected_str<qint64> startStubProcess(const ProcessSetupData &setup) = 0;
+};
+
+class QTCREATOR_UTILS_EXPORT TerminalInterface : public ProcessInterface
+{
+ friend class TerminalInterfacePrivate;
+ friend class StubCreator;
+
+public:
+ TerminalInterface(bool waitOnExit = true);
+ ~TerminalInterface() override;
+
+ int inferiorProcessId() const;
+ int inferiorThreadId() const;
+
+ void setStubCreator(StubCreator *creator);
+
+ void emitError(QProcess::ProcessError error, const QString &errorString);
+ void emitFinished(int exitCode, QProcess::ExitStatus exitStatus);
+ void onStubExited();
+
+protected:
+ void onNewStubConnection();
+ void onStubReadyRead();
+
+ void sendCommand(char c);
+
+ void killInferiorProcess();
+ void killStubProcess();
+
+ expected_str<void> startStubServer();
+ void shutdownStubServer();
+ void cleanupAfterStartFailure(const QString &errorMessage);
+
+ bool isRunning() const;
+
+private:
+ void start() override;
+ qint64 write(const QByteArray &data) override;
+ void sendControlSignal(ControlSignal controlSignal) override;
+
+ TerminalInterfacePrivate *d{nullptr};
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/terminalprocess.cpp b/src/libs/utils/terminalprocess.cpp
deleted file mode 100644
index bd0333c9d7..0000000000
--- a/src/libs/utils/terminalprocess.cpp
+++ /dev/null
@@ -1,721 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "terminalprocess_p.h"
-
-#include "commandline.h"
-#include "environment.h"
-#include "hostosinfo.h"
-#include "qtcassert.h"
-#include "qtcprocess.h"
-#include "terminalcommand.h"
-#include "utilstr.h"
-
-#include <QCoreApplication>
-#include <QLocalServer>
-#include <QLocalSocket>
-#include <QRegularExpression>
-#include <QTemporaryFile>
-#include <QTextCodec>
-#include <QTimer>
-#include <QWinEventNotifier>
-
-#ifdef Q_OS_WIN
-
-#include "winutils.h"
-
-#include <cstring>
-#include <stdlib.h>
-#include <windows.h>
-
-#else
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#endif
-
-namespace Utils {
-namespace Internal {
-
-static QString modeOption(TerminalMode m)
-{
- switch (m) {
- case TerminalMode::Run:
- return QLatin1String("run");
- case TerminalMode::Debug:
- return QLatin1String("debug");
- case TerminalMode::Suspend:
- return QLatin1String("suspend");
- case TerminalMode::Off:
- QTC_CHECK(false);
- break;
- }
- return {};
-}
-
-static QString msgCommChannelFailed(const QString &error)
-{
- return Tr::tr("Cannot set up communication channel: %1").arg(error);
-}
-
-static QString msgPromptToClose()
-{
- // Shown in a terminal which might have a different character set on Windows.
- return Tr::tr("Press <RETURN> to close this window...");
-}
-
-static QString msgCannotCreateTempFile(const QString &why)
-{
- return Tr::tr("Cannot create temporary file: %1").arg(why);
-}
-
-static QString msgCannotWriteTempFile()
-{
- return Tr::tr("Cannot write temporary file. Disk full?");
-}
-
-static QString msgCannotCreateTempDir(const QString & dir, const QString &why)
-{
- return Tr::tr("Cannot create temporary directory \"%1\": %2").arg(dir, why);
-}
-
-static QString msgUnexpectedOutput(const QByteArray &what)
-{
- return Tr::tr("Unexpected output from helper program (%1).")
- .arg(QString::fromLatin1(what));
-}
-
-static QString msgCannotChangeToWorkDir(const FilePath &dir, const QString &why)
-{
- return Tr::tr("Cannot change to working directory \"%1\": %2").arg(dir.toString(), why);
-}
-
-static QString msgCannotExecute(const QString & p, const QString &why)
-{
- return Tr::tr("Cannot execute \"%1\": %2").arg(p, why);
-}
-
-class TerminalProcessPrivate
-{
-public:
- TerminalProcessPrivate(QObject *parent)
- : m_stubServer(parent)
- , m_process(parent) {}
-
- qint64 m_processId = 0;
- ProcessResultData m_result;
- QLocalServer m_stubServer;
- QLocalSocket *m_stubSocket = nullptr;
- QTemporaryFile *m_tempFile = nullptr;
-
- // Used on Unix only
- QtcProcess m_process;
- QTimer *m_stubConnectTimer = nullptr;
- QByteArray m_stubServerDir;
-
- // Used on Windows only
- qint64 m_appMainThreadId = 0;
-
-#ifdef Q_OS_WIN
- PROCESS_INFORMATION *m_pid = nullptr;
- HANDLE m_hInferior = NULL;
- QWinEventNotifier *inferiorFinishedNotifier = nullptr;
- QWinEventNotifier *processFinishedNotifier = nullptr;
-#endif
-};
-
-TerminalImpl::TerminalImpl()
- : d(new TerminalProcessPrivate(this))
-{
- connect(&d->m_stubServer, &QLocalServer::newConnection,
- this, &TerminalImpl::stubConnectionAvailable);
-
- d->m_process.setProcessChannelMode(QProcess::ForwardedChannels);
-}
-
-TerminalImpl::~TerminalImpl()
-{
- stopProcess();
- delete d;
-}
-
-void TerminalImpl::start()
-{
- if (isRunning())
- return;
-
- d->m_result = {};
-
-#ifdef Q_OS_WIN
-
- QString pcmd;
- QString pargs;
- if (m_setup.m_terminalMode != TerminalMode::Run) { // The debugger engines already pre-process the arguments.
- pcmd = m_setup.m_commandLine.executable().toString();
- pargs = m_setup.m_commandLine.arguments();
- } else {
- ProcessArgs outArgs;
- ProcessArgs::prepareCommand(m_setup.m_commandLine, &pcmd, &outArgs,
- &m_setup.m_environment, &m_setup.m_workingDirectory);
- pargs = outArgs.toWindowsArgs();
- }
-
- const QString err = stubServerListen();
- if (!err.isEmpty()) {
- emitError(QProcess::FailedToStart, msgCommChannelFailed(err));
- return;
- }
-
- QStringList env = m_setup.m_environment.toStringList();
- if (!env.isEmpty()) {
- d->m_tempFile = new QTemporaryFile();
- if (!d->m_tempFile->open()) {
- cleanupAfterStartFailure(msgCannotCreateTempFile(d->m_tempFile->errorString()));
- return;
- }
- QString outString;
- QTextStream out(&outString);
- // Add PATH and SystemRoot environment variables in case they are missing
- const QStringList fixedEnvironment = [env] {
- QStringList envStrings = env;
- // add PATH if necessary (for DLL loading)
- if (envStrings.filter(QRegularExpression("^PATH=.*", QRegularExpression::CaseInsensitiveOption)).isEmpty()) {
- const QString path = qtcEnvironmentVariable("PATH");
- if (!path.isEmpty())
- envStrings.prepend(QString::fromLatin1("PATH=%1").arg(path));
- }
- // add systemroot if needed
- if (envStrings.filter(QRegularExpression("^SystemRoot=.*", QRegularExpression::CaseInsensitiveOption)).isEmpty()) {
- const QString systemRoot = qtcEnvironmentVariable("SystemRoot");
- if (!systemRoot.isEmpty())
- envStrings.prepend(QString::fromLatin1("SystemRoot=%1").arg(systemRoot));
- }
- return envStrings;
- }();
-
- for (const QString &var : fixedEnvironment)
- out << var << QChar(0);
- out << QChar(0);
- const QTextCodec *textCodec = QTextCodec::codecForName("UTF-16LE");
- QTC_CHECK(textCodec);
- const QByteArray outBytes = textCodec ? textCodec->fromUnicode(outString) : QByteArray();
- if (!textCodec || d->m_tempFile->write(outBytes) < 0) {
- cleanupAfterStartFailure(msgCannotWriteTempFile());
- return;
- }
- d->m_tempFile->flush();
- }
-
- STARTUPINFO si;
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- d->m_pid = new PROCESS_INFORMATION;
- ZeroMemory(d->m_pid, sizeof(PROCESS_INFORMATION));
-
- QString workDir = m_setup.m_workingDirectory.toUserOutput();
- if (!workDir.isEmpty() && !workDir.endsWith(QLatin1Char('\\')))
- workDir.append(QLatin1Char('\\'));
-
- // Quote a Windows command line correctly for the "CreateProcess" API
- static const auto quoteWinCommand = [](const QString &program) {
- const QChar doubleQuote = QLatin1Char('"');
-
- // add the program as the first arg ... it works better
- QString programName = program;
- programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
- if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote)
- && programName.contains(QLatin1Char(' '))) {
- programName.prepend(doubleQuote);
- programName.append(doubleQuote);
- }
- return programName;
- };
- static const auto quoteWinArgument = [](const QString &arg) {
- if (arg.isEmpty())
- return QString::fromLatin1("\"\"");
-
- QString ret(arg);
- // Quotes are escaped and their preceding backslashes are doubled.
- ret.replace(QRegularExpression("(\\\\*)\""), "\\1\\1\\\"");
- if (ret.contains(QRegularExpression("\\s"))) {
- // The argument must not end with a \ since this would be interpreted
- // as escaping the quote -- rather put the \ behind the quote: e.g.
- // rather use "foo"\ than "foo\"
- int i = ret.length();
- while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
- --i;
- ret.insert(i, QLatin1Char('"'));
- ret.prepend(QLatin1Char('"'));
- }
- return ret;
- };
- static const auto createWinCommandlineMultiArgs = [](const QString &program, const QStringList &args) {
- QString programName = quoteWinCommand(program);
- for (const QString &arg : args) {
- programName += QLatin1Char(' ');
- programName += quoteWinArgument(arg);
- }
- return programName;
- };
- static const auto createWinCommandlineSingleArg = [](const QString &program, const QString &args)
- {
- QString programName = quoteWinCommand(program);
- if (!args.isEmpty()) {
- programName += QLatin1Char(' ');
- programName += args;
- }
- return programName;
- };
-
- QStringList stubArgs;
- stubArgs << modeOption(m_setup.m_terminalMode)
- << d->m_stubServer.fullServerName()
- << workDir
- << (d->m_tempFile ? d->m_tempFile->fileName() : QString())
- << createWinCommandlineSingleArg(pcmd, pargs)
- << msgPromptToClose();
-
- const QString cmdLine = createWinCommandlineMultiArgs(
- QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_process_stub.exe"), stubArgs);
-
- bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(),
- 0, 0, FALSE, CREATE_NEW_CONSOLE,
- 0, 0,
- &si, d->m_pid);
-
- if (!success) {
- delete d->m_pid;
- d->m_pid = nullptr;
- const QString msg = Tr::tr("The process \"%1\" could not be started: %2")
- .arg(cmdLine, winErrorMessage(GetLastError()));
- cleanupAfterStartFailure(msg);
- return;
- }
-
- d->processFinishedNotifier = new QWinEventNotifier(d->m_pid->hProcess, this);
- connect(d->processFinishedNotifier, &QWinEventNotifier::activated,
- this, &TerminalImpl::stubExited);
-
-#else
-
- ProcessArgs::SplitError perr;
- ProcessArgs pargs = ProcessArgs::prepareArgs(m_setup.m_commandLine.arguments(),
- &perr,
- HostOsInfo::hostOs(),
- &m_setup.m_environment,
- &m_setup.m_workingDirectory,
- m_setup.m_abortOnMetaChars);
-
- QString pcmd;
- if (perr == ProcessArgs::SplitOk) {
- pcmd = m_setup.m_commandLine.executable().toString();
- } else {
- if (perr != ProcessArgs::FoundMeta) {
- emitError(QProcess::FailedToStart, Tr::tr("Quoting error in command."));
- return;
- }
- if (m_setup.m_terminalMode == TerminalMode::Debug) {
- // FIXME: QTCREATORBUG-2809
- emitError(QProcess::FailedToStart,
- Tr::tr("Debugging complex shell commands in a terminal"
- " is currently not supported."));
- return;
- }
- pcmd = qtcEnvironmentVariable("SHELL", "/bin/sh");
- pargs = ProcessArgs::createUnixArgs(
- {"-c", (ProcessArgs::quoteArg(m_setup.m_commandLine.executable().toString())
- + ' ' + m_setup.m_commandLine.arguments())});
- }
-
- ProcessArgs::SplitError qerr;
- const TerminalCommand terminal = TerminalCommand::terminalEmulator();
- const ProcessArgs terminalArgs = ProcessArgs::prepareArgs(terminal.executeArgs,
- &qerr,
- HostOsInfo::hostOs(),
- &m_setup.m_environment,
- &m_setup.m_workingDirectory);
- if (qerr != ProcessArgs::SplitOk) {
- emitError(QProcess::FailedToStart,
- qerr == ProcessArgs::BadQuoting
- ? Tr::tr("Quoting error in terminal command.")
- : Tr::tr("Terminal command may not be a shell command."));
- return;
- }
-
- const QString err = stubServerListen();
- if (!err.isEmpty()) {
- emitError(QProcess::FailedToStart, msgCommChannelFailed(err));
- return;
- }
-
- m_setup.m_environment.unset(QLatin1String("TERM"));
-
- const QStringList env = m_setup.m_environment.toStringList();
- if (!env.isEmpty()) {
- d->m_tempFile = new QTemporaryFile(this);
- if (!d->m_tempFile->open()) {
- cleanupAfterStartFailure(msgCannotCreateTempFile(d->m_tempFile->errorString()));
- return;
- }
- QByteArray contents;
- for (const QString &var : env) {
- const QByteArray l8b = var.toLocal8Bit();
- contents.append(l8b.constData(), l8b.size() + 1);
- }
- if (d->m_tempFile->write(contents) != contents.size() || !d->m_tempFile->flush()) {
- cleanupAfterStartFailure(msgCannotWriteTempFile());
- return;
- }
- }
-
- const QString stubPath = QCoreApplication::applicationDirPath()
- + QLatin1String("/" RELATIVE_LIBEXEC_PATH "/qtcreator_process_stub");
-
- QStringList allArgs = terminalArgs.toUnixArgs();
-
- allArgs << stubPath
- << modeOption(m_setup.m_terminalMode)
- << d->m_stubServer.fullServerName()
- << msgPromptToClose()
- << m_setup.m_workingDirectory.path()
- << (d->m_tempFile ? d->m_tempFile->fileName() : QString())
- << QString::number(getpid())
- << pcmd
- << pargs.toUnixArgs();
-
- if (terminal.needsQuotes)
- allArgs = QStringList { ProcessArgs::joinArgs(allArgs) };
-
- d->m_process.setEnvironment(m_setup.m_environment);
- d->m_process.setCommand({terminal.command, allArgs});
- d->m_process.setProcessImpl(m_setup.m_processImpl);
- d->m_process.setReaperTimeout(m_setup.m_reaperTimeout);
-
- d->m_process.start();
- if (!d->m_process.waitForStarted()) {
- const QString msg = Tr::tr("Cannot start the terminal emulator \"%1\", change the "
- "setting in the Environment preferences. (%2)")
- .arg(terminal.command.toUserOutput(), d->m_process.errorString());
- cleanupAfterStartFailure(msg);
- return;
- }
- d->m_stubConnectTimer = new QTimer(this);
- connect(d->m_stubConnectTimer, &QTimer::timeout, this, &TerminalImpl::stopProcess);
- d->m_stubConnectTimer->setSingleShot(true);
- d->m_stubConnectTimer->start(10000);
-
-#endif
-}
-
-void TerminalImpl::cleanupAfterStartFailure(const QString &errorMessage)
-{
- stubServerShutdown();
- emitError(QProcess::FailedToStart, errorMessage);
- delete d->m_tempFile;
- d->m_tempFile = nullptr;
-}
-
-void TerminalImpl::sendControlSignal(ControlSignal controlSignal)
-{
- switch (controlSignal) {
- case ControlSignal::Terminate:
- case ControlSignal::Kill:
- killProcess();
- if (HostOsInfo::isWindowsHost())
- killStub();
- break;
- case ControlSignal::Interrupt:
- sendCommand('i');
- break;
- case ControlSignal::KickOff:
- sendCommand('c');
- break;
- case ControlSignal::CloseWriteChannel:
- QTC_CHECK(false);
- break;
- }
-}
-
-void TerminalImpl::sendCommand(char c)
-{
-#ifdef Q_OS_WIN
- Q_UNUSED(c)
-#else
- if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
- d->m_stubSocket->write(&c, 1);
- d->m_stubSocket->flush();
- }
-#endif
-}
-
-void TerminalImpl::killProcess()
-{
-#ifdef Q_OS_WIN
- if (d->m_hInferior != NULL) {
- TerminateProcess(d->m_hInferior, (unsigned)-1);
- cleanupInferior();
- }
-#else
- sendCommand('k');
-#endif
- d->m_processId = 0;
-}
-
-void TerminalImpl::killStub()
-{
- if (!isRunning())
- return;
-
-#ifdef Q_OS_WIN
- TerminateProcess(d->m_pid->hProcess, (unsigned)-1);
- WaitForSingleObject(d->m_pid->hProcess, INFINITE);
- cleanupStub();
- emitFinished(-1, QProcess::CrashExit);
-#else
- sendCommand('s');
- stubServerShutdown();
- d->m_process.stop();
- d->m_process.waitForFinished();
-#endif
-}
-
-void TerminalImpl::stopProcess()
-{
- killProcess();
- killStub();
-}
-
-bool TerminalImpl::isRunning() const
-{
-#ifdef Q_OS_WIN
- return d->m_pid != nullptr;
-#else
- return d->m_process.state() != QProcess::NotRunning
- || (d->m_stubSocket && d->m_stubSocket->isOpen());
-#endif
-}
-
-QString TerminalImpl::stubServerListen()
-{
-#ifdef Q_OS_WIN
- if (d->m_stubServer.listen(QString::fromLatin1("creator-%1-%2")
- .arg(QCoreApplication::applicationPid())
- .arg(rand())))
- return QString();
- return d->m_stubServer.errorString();
-#else
- // We need to put the socket in a private directory, as some systems simply do not
- // check the file permissions of sockets.
- QString stubFifoDir;
- while (true) {
- {
- QTemporaryFile tf;
- if (!tf.open())
- return msgCannotCreateTempFile(tf.errorString());
- stubFifoDir = tf.fileName();
- }
- // By now the temp file was deleted again
- d->m_stubServerDir = QFile::encodeName(stubFifoDir);
- if (!::mkdir(d->m_stubServerDir.constData(), 0700))
- break;
- if (errno != EEXIST)
- return msgCannotCreateTempDir(stubFifoDir, QString::fromLocal8Bit(strerror(errno)));
- }
- const QString stubServer = stubFifoDir + QLatin1String("/stub-socket");
- if (!d->m_stubServer.listen(stubServer)) {
- ::rmdir(d->m_stubServerDir.constData());
- return Tr::tr("Cannot create socket \"%1\": %2")
- .arg(stubServer, d->m_stubServer.errorString());
- }
- return {};
-#endif
-}
-
-void TerminalImpl::stubServerShutdown()
-{
-#ifdef Q_OS_WIN
- delete d->m_stubSocket;
- d->m_stubSocket = nullptr;
- if (d->m_stubServer.isListening())
- d->m_stubServer.close();
-#else
- if (d->m_stubSocket) {
- readStubOutput(); // we could get the shutdown signal before emptying the buffer
- d->m_stubSocket->disconnect(); // avoid getting queued readyRead signals
- d->m_stubSocket->deleteLater(); // we might be called from the disconnected signal of m_stubSocket
- }
- d->m_stubSocket = nullptr;
- if (d->m_stubServer.isListening()) {
- d->m_stubServer.close();
- ::rmdir(d->m_stubServerDir.constData());
- }
-#endif
-}
-
-void TerminalImpl::stubConnectionAvailable()
-{
- if (d->m_stubConnectTimer) {
- delete d->m_stubConnectTimer;
- d->m_stubConnectTimer = nullptr;
- }
-
- d->m_stubSocket = d->m_stubServer.nextPendingConnection();
- connect(d->m_stubSocket, &QIODevice::readyRead, this, &TerminalImpl::readStubOutput);
-
- if (HostOsInfo::isAnyUnixHost())
- connect(d->m_stubSocket, &QLocalSocket::disconnected, this, &TerminalImpl::stubExited);
-}
-
-static QString errorMsg(int code)
-{
- return QString::fromLocal8Bit(strerror(code));
-}
-
-void TerminalImpl::readStubOutput()
-{
- while (d->m_stubSocket->canReadLine()) {
- QByteArray out = d->m_stubSocket->readLine();
-#ifdef Q_OS_WIN
- out.chop(2); // \r\n
- if (out.startsWith("err:chdir ")) {
- emitError(QProcess::FailedToStart,
- msgCannotChangeToWorkDir(m_setup.m_workingDirectory, winErrorMessage(out.mid(10).toInt())));
- } else if (out.startsWith("err:exec ")) {
- emitError(QProcess::FailedToStart,
- msgCannotExecute(m_setup.m_commandLine.executable().toUserOutput(), winErrorMessage(out.mid(9).toInt())));
- } else if (out.startsWith("thread ")) { // Windows only
- // TODO: ensure that it comes before "pid " comes
- d->m_appMainThreadId = out.mid(7).toLongLong();
- } else if (out.startsWith("pid ")) {
- // Will not need it any more
- delete d->m_tempFile;
- d->m_tempFile = nullptr;
- d->m_processId = out.mid(4).toLongLong();
-
- d->m_hInferior = OpenProcess(
- SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE,
- FALSE, d->m_processId);
- if (d->m_hInferior == NULL) {
- emitError(QProcess::FailedToStart,
- Tr::tr("Cannot obtain a handle to the inferior: %1")
- .arg(winErrorMessage(GetLastError())));
- // Uhm, and now what?
- continue;
- }
- d->inferiorFinishedNotifier = new QWinEventNotifier(d->m_hInferior, this);
- connect(d->inferiorFinishedNotifier, &QWinEventNotifier::activated, this, [this] {
- DWORD chldStatus;
-
- if (!GetExitCodeProcess(d->m_hInferior, &chldStatus))
- emitError(QProcess::UnknownError,
- Tr::tr("Cannot obtain exit status from inferior: %1")
- .arg(winErrorMessage(GetLastError())));
- cleanupInferior();
- emitFinished(chldStatus, QProcess::NormalExit);
- });
-
- emit started(d->m_processId, d->m_appMainThreadId);
- } else {
- emitError(QProcess::UnknownError, msgUnexpectedOutput(out));
- TerminateProcess(d->m_pid->hProcess, (unsigned)-1);
- break;
- }
-#else
- out.chop(1); // \n
- if (out.startsWith("err:chdir ")) {
- emitError(QProcess::FailedToStart,
- msgCannotChangeToWorkDir(m_setup.m_workingDirectory, errorMsg(out.mid(10).toInt())));
- } else if (out.startsWith("err:exec ")) {
- emitError(QProcess::FailedToStart,
- msgCannotExecute(m_setup.m_commandLine.executable().toString(), errorMsg(out.mid(9).toInt())));
- } else if (out.startsWith("spid ")) {
- delete d->m_tempFile;
- d->m_tempFile = nullptr;
- } else if (out.startsWith("pid ")) {
- d->m_processId = out.mid(4).toInt();
- emit started(d->m_processId);
- } else if (out.startsWith("exit ")) {
- emitFinished(out.mid(5).toInt(), QProcess::NormalExit);
- } else if (out.startsWith("crash ")) {
- emitFinished(out.mid(6).toInt(), QProcess::CrashExit);
- } else {
- emitError(QProcess::UnknownError, msgUnexpectedOutput(out));
- d->m_process.terminate();
- break;
- }
-#endif
- } // while
-}
-
-void TerminalImpl::stubExited()
-{
- // The stub exit might get noticed before we read the pid for the kill on Windows
- // or the error status elsewhere.
- if (d->m_stubSocket && d->m_stubSocket->state() == QLocalSocket::ConnectedState)
- d->m_stubSocket->waitForDisconnected();
-
-#ifdef Q_OS_WIN
- cleanupStub();
- if (d->m_hInferior != NULL) {
- TerminateProcess(d->m_hInferior, (unsigned)-1);
- cleanupInferior();
- emitFinished(-1, QProcess::CrashExit);
- }
-#else
- stubServerShutdown();
- delete d->m_tempFile;
- d->m_tempFile = nullptr;
- if (d->m_processId)
- emitFinished(-1, QProcess::CrashExit);
-#endif
-}
-
-void TerminalImpl::cleanupInferior()
-{
-#ifdef Q_OS_WIN
- delete d->inferiorFinishedNotifier;
- d->inferiorFinishedNotifier = nullptr;
- CloseHandle(d->m_hInferior);
- d->m_hInferior = NULL;
-#endif
-}
-
-void TerminalImpl::cleanupStub()
-{
-#ifdef Q_OS_WIN
- stubServerShutdown();
- delete d->processFinishedNotifier;
- d->processFinishedNotifier = nullptr;
- CloseHandle(d->m_pid->hThread);
- CloseHandle(d->m_pid->hProcess);
- delete d->m_pid;
- d->m_pid = nullptr;
- delete d->m_tempFile;
- d->m_tempFile = nullptr;
-#endif
-}
-
-void TerminalImpl::emitError(QProcess::ProcessError error, const QString &errorString)
-{
- d->m_result.m_error = error;
- d->m_result.m_errorString = errorString;
- if (error == QProcess::FailedToStart)
- emit done(d->m_result);
-}
-
-void TerminalImpl::emitFinished(int exitCode, QProcess::ExitStatus exitStatus)
-{
- d->m_processId = 0;
- d->m_result.m_exitCode = exitCode;
- d->m_result.m_exitStatus = exitStatus;
- emit done(d->m_result);
-}
-
-
-} // Internal
-} // Utils
diff --git a/src/libs/utils/terminalprocess_p.h b/src/libs/utils/terminalprocess_p.h
deleted file mode 100644
index 27c99cee26..0000000000
--- a/src/libs/utils/terminalprocess_p.h
+++ /dev/null
@@ -1,54 +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 "processenums.h"
-#include "processinterface.h"
-#include "qtcassert.h"
-
-#include <QProcess>
-
-namespace Utils {
-
-class CommandLine;
-class Environment;
-class FilePath;
-
-namespace Internal {
-
-class TerminalImpl final : public ProcessInterface
-{
-public:
- TerminalImpl();
- ~TerminalImpl() final;
-
-private:
- void start() final;
- qint64 write(const QByteArray &) final { QTC_CHECK(false); return -1; }
- void sendControlSignal(ControlSignal controlSignal) final;
-
- // OK, however, impl looks a bit different (!= NotRunning vs == Running).
- // Most probably changing it into (== Running) should be OK.
- bool isRunning() const;
-
- void stopProcess();
- void stubConnectionAvailable();
- void readStubOutput();
- void stubExited();
- void cleanupAfterStartFailure(const QString &errorMessage);
- void killProcess();
- void killStub();
- void emitError(QProcess::ProcessError error, const QString &errorString);
- void emitFinished(int exitCode, QProcess::ExitStatus exitStatus);
- QString stubServerListen();
- void stubServerShutdown();
- void cleanupStub();
- void cleanupInferior();
- void sendCommand(char c);
-
- class TerminalProcessPrivate *d;
-};
-
-} // Internal
-} // Utils
diff --git a/src/libs/utils/textutils.cpp b/src/libs/utils/textutils.cpp
index 1222038039..8b17cc7e01 100644
--- a/src/libs/utils/textutils.cpp
+++ b/src/libs/utils/textutils.cpp
@@ -2,12 +2,100 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "textutils.h"
+#include "qtcassert.h"
-#include <QTextDocument>
+#include <QRegularExpression>
#include <QTextBlock>
+#include <QTextDocument>
+
+namespace Utils::Text {
+
+bool Position::operator==(const Position &other) const
+{
+ return line == other.line && column == other.column;
+}
+
+/*!
+ Returns the text position of a \a fileName and sets the \a postfixPos if
+ it can find a positional postfix.
+
+ The following patterns are supported: \c {filepath.txt:19},
+ \c{filepath.txt:19:12}, \c {filepath.txt+19},
+ \c {filepath.txt+19+12}, and \c {filepath.txt(19)}.
+*/
+
+Position Position::fromFileName(QStringView fileName, int &postfixPos)
+{
+ static const auto regexp = QRegularExpression("[:+](\\d+)?([:+](\\d+)?)?$");
+ // (10) MSVC-style
+ static const auto vsRegexp = QRegularExpression("[(]((\\d+)[)]?)?$");
+ const QRegularExpressionMatch match = regexp.match(fileName);
+ Position pos;
+ if (match.hasMatch()) {
+ postfixPos = match.capturedStart(0);
+ if (match.lastCapturedIndex() > 0) {
+ pos.line = match.captured(1).toInt();
+ if (match.lastCapturedIndex() > 2) // index 2 includes the + or : for the column number
+ pos.column = match.captured(3).toInt() - 1; //column is 0 based, despite line being 1 based
+ }
+ } else {
+ const QRegularExpressionMatch vsMatch = vsRegexp.match(fileName);
+ postfixPos = vsMatch.capturedStart(0);
+ if (vsMatch.lastCapturedIndex() > 1) // index 1 includes closing )
+ pos.line = vsMatch.captured(2).toInt();
+ }
+ if (pos.line > 0 && pos.column < 0)
+ pos.column = 0; // if we got a valid line make sure to return a valid TextPosition
+ return pos;
+}
+
+Position Position::fromPositionInDocument(const QTextDocument *document, int pos)
+{
+ QTC_ASSERT(document, return {});
+ const QTextBlock block = document->findBlock(pos);
+ if (block.isValid())
+ return {block.blockNumber() + 1, pos - block.position()};
+
+ return {};
+}
+
+Position Position::fromCursor(const QTextCursor &c)
+{
+ return c.isNull() ? Position{} : Position{c.blockNumber() + 1, c.positionInBlock()};
+}
+
+int Range::length(const QString &text) const
+{
+ if (end.line < begin.line)
+ return -1;
-namespace Utils {
-namespace Text {
+ if (begin.line == end.line)
+ return end.column - begin.column;
+
+ int index = 0;
+ int currentLine = 1;
+ while (currentLine < begin.line) {
+ index = text.indexOf(QChar::LineFeed, index);
+ if (index < 0)
+ return -1;
+ ++index;
+ ++currentLine;
+ }
+ const int beginIndex = index + begin.column;
+ while (currentLine < end.line) {
+ index = text.indexOf(QChar::LineFeed, index);
+ if (index < 0)
+ return -1;
+ ++index;
+ ++currentLine;
+ }
+ return index + end.column - beginIndex;
+}
+
+bool Range::operator==(const Range &other) const
+{
+ return begin == other.begin && end == other.end;
+}
bool convertPosition(const QTextDocument *document, int pos, int *line, int *column)
{
@@ -24,18 +112,6 @@ bool convertPosition(const QTextDocument *document, int pos, int *line, int *col
}
}
-OptionalLineColumn convertPosition(const QTextDocument *document, int pos)
-{
- OptionalLineColumn optional;
-
- QTextBlock block = document->findBlock(pos);
-
- if (block.isValid())
- optional.emplace(block.blockNumber() + 1, pos - block.position() + 1);
-
- return optional;
-}
-
int positionInText(const QTextDocument *textDocument, int line, int column)
{
// Deduct 1 from line and column since they are 1-based.
@@ -141,21 +217,6 @@ int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffe
return utf8Offset;
}
-LineColumn utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset)
-{
- LineColumn lineColumn;
- lineColumn.line = static_cast<int>(
- std::count(utf8Buffer.begin(), utf8Buffer.begin() + utf8Offset, '\n'))
- + 1;
- const int startOfLineOffset = utf8Offset ? (utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1)
- : 0;
- lineColumn.column = QString::fromUtf8(
- utf8Buffer.mid(startOfLineOffset, utf8Offset - startOfLineOffset))
- .length()
- + 1;
- return lineColumn;
-}
-
QString utf16LineTextInUtf8Buffer(const QByteArray &utf8Buffer, int currentUtf8Offset)
{
const int lineStartUtf8Offset = currentUtf8Offset
@@ -211,5 +272,10 @@ void applyReplacements(QTextDocument *doc, const Replacements &replacements)
editCursor.endEditBlock();
}
-} // Text
-} // Utils
+QDebug &operator<<(QDebug &stream, const Position &pos)
+{
+ stream << "line: " << pos.line << ", column: " << pos.column;
+ return stream;
+}
+
+} // namespace Utils::Text
diff --git a/src/libs/utils/textutils.h b/src/libs/utils/textutils.h
index e214f74659..96b7afbeb8 100644
--- a/src/libs/utils/textutils.h
+++ b/src/libs/utils/textutils.h
@@ -5,8 +5,7 @@
#include "utils_global.h"
-#include "linecolumn.h"
-
+#include <QMetaType>
#include <QString>
QT_BEGIN_NAMESPACE
@@ -17,6 +16,39 @@ QT_END_NAMESPACE
namespace Utils {
namespace Text {
+class QTCREATOR_UTILS_EXPORT Position
+{
+public:
+ int line = 0; // 1-based
+ int column = -1; // 0-based
+
+ bool operator<(const Position &other) const
+ { return line < other.line || (line == other.line && column < other.column); }
+ bool operator==(const Position &other) const;
+
+ bool operator!=(const Position &other) const { return !(operator==(other)); }
+
+ bool isValid() const { return line > 0 && column >= 0; }
+
+ static Position fromFileName(QStringView fileName, int &postfixPos);
+ static Position fromPositionInDocument(const QTextDocument *document, int pos);
+ static Position fromCursor(const QTextCursor &cursor);
+};
+
+class QTCREATOR_UTILS_EXPORT Range
+{
+public:
+ int length(const QString &text) const;
+
+ Position begin;
+ Position end;
+
+ bool operator<(const Range &other) const { return begin < other.begin; }
+ bool operator==(const Range &other) const;
+
+ bool operator!=(const Range &other) const { return !(operator==(other)); }
+};
+
struct Replacement
{
Replacement() = default;
@@ -40,8 +72,6 @@ QTCREATOR_UTILS_EXPORT void applyReplacements(QTextDocument *doc, const Replacem
QTCREATOR_UTILS_EXPORT bool convertPosition(const QTextDocument *document,
int pos,
int *line, int *column);
-QTCREATOR_UTILS_EXPORT
-OptionalLineColumn convertPosition(const QTextDocument *document, int pos);
// line and column are 1-based
QTCREATOR_UTILS_EXPORT int positionInText(const QTextDocument *textDocument, int line, int column);
@@ -60,9 +90,13 @@ QTCREATOR_UTILS_EXPORT int utf8NthLineOffset(const QTextDocument *textDocument,
const QByteArray &buffer,
int line);
-QTCREATOR_UTILS_EXPORT LineColumn utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset);
QTCREATOR_UTILS_EXPORT QString utf16LineTextInUtf8Buffer(const QByteArray &utf8Buffer,
int currentUtf8Offset);
+QTCREATOR_UTILS_EXPORT QDebug &operator<<(QDebug &stream, const Position &pos);
+
} // Text
} // Utils
+
+Q_DECLARE_METATYPE(Utils::Text::Position)
+Q_DECLARE_METATYPE(Utils::Text::Range)
diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp
index 3cb2cef89c..9b7afa71eb 100644
--- a/src/libs/utils/theme/theme.cpp
+++ b/src/libs/utils/theme/theme.cpp
@@ -91,6 +91,9 @@ Theme::Theme(Theme *originTheme, QObject *parent)
Theme::~Theme()
{
+ if (this == m_creatorTheme)
+ m_creatorTheme = nullptr;
+
delete d;
}
diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h
index 1fbae4934e..2604780a2d 100644
--- a/src/libs/utils/theme/theme.h
+++ b/src/libs/utils/theme/theme.h
@@ -36,7 +36,6 @@ public:
BadgeLabelBackgroundColorUnchecked,
BadgeLabelTextColorChecked,
BadgeLabelTextColorUnchecked,
- CanceledSearchTextColor,
ComboBoxArrowColor,
ComboBoxArrowColorDisabled,
ComboBoxTextColor,
@@ -285,7 +284,6 @@ public:
QmlDesigner_ScrollBarHandleColor,
/* Palette for DS Controls */
-
DSpanelBackground,
DSinteraction,
DSerrorColor,
@@ -305,9 +303,28 @@ public:
DStextSelectionColor,
DStextSelectedTextColor,
+ /*NEW for QtDS 4.0*/
+ DScontrolBackground_toolbarIdle,
+ DScontrolBackground_toolbarHover,
+ DStoolbarBackground,
+ DStoolbarIcon_blocked,
+ DSthumbnailBackground_baseState,
+ DSidleGreen,
+ DSrunningGreen,
+ DSstatusbarBackground,
+ DScontrolBackground_statusbarIdle,
+ DScontrolBackground_topToolbarHover,
+ DSControlBackground_statusbarHover,
+ DScontrolOutline_topToolbarIdle,
+ DScontrolOutline_topToolbarHover,
+ DSprimaryButton_hoverHighlight,
+ DSstateBackgroundColor_hover,
+ DSstateControlBackgroundColor_hover,
+ DSstateControlBackgroundColor_globalHover,
DSplaceholderTextColor,
DSplaceholderTextColorInteraction,
+ /*Legacy QtDS*/
DSiconColor,
DSiconColorHover,
DSiconColorInteraction,
@@ -420,6 +437,28 @@ public:
DSstatePanelBackground,
DSstateHighlight,
+
+ TerminalForeground,
+ TerminalBackground,
+ TerminalSelection,
+ TerminalFindMatch,
+
+ TerminalAnsi0,
+ TerminalAnsi1,
+ TerminalAnsi2,
+ TerminalAnsi3,
+ TerminalAnsi4,
+ TerminalAnsi5,
+ TerminalAnsi6,
+ TerminalAnsi7,
+ TerminalAnsi8,
+ TerminalAnsi9,
+ TerminalAnsi10,
+ TerminalAnsi11,
+ TerminalAnsi12,
+ TerminalAnsi13,
+ TerminalAnsi14,
+ TerminalAnsi15,
};
enum ImageFile {
@@ -448,7 +487,8 @@ public:
FlatMenuBar,
ToolBarIconShadow,
WindowColorAsBase,
- DarkUserInterface
+ DarkUserInterface,
+ QDSTheme
};
Q_ENUM(Color)
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 11c0e34282..18dcf1675c 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -35,7 +35,9 @@ Project {
Depends { name: "Qt"; submodules: ["concurrent", "core-private", "network", "qml", "widgets", "xml"] }
Depends { name: "Qt.macextras"; condition: Qt.core.versionMajor < 6 && qbs.targetOS.contains("macos") }
+ Depends { name: "Tasking" }
Depends { name: "app_version_header" }
+ Depends { name: "ptyqt" }
files: [
"QtConcurrentTools",
@@ -48,8 +50,8 @@ Project {
"archive.h",
"aspects.cpp",
"aspects.h",
- "asynctask.cpp",
- "asynctask.h",
+ "async.cpp",
+ "async.h",
"basetreeview.cpp",
"basetreeview.h",
"benchmarker.cpp",
@@ -126,6 +128,10 @@ Project {
"filepath.h",
"filesearch.cpp",
"filesearch.h",
+ "filestreamer.cpp",
+ "filestreamer.h",
+ "filestreamermanager.cpp",
+ "filestreamermanager.h",
"filesystemmodel.cpp",
"filesystemmodel.h",
"filesystemwatcher.cpp",
@@ -178,8 +184,6 @@ Project {
"launchersocket.h",
"layoutbuilder.cpp",
"layoutbuilder.h",
- "linecolumn.cpp",
- "linecolumn.h",
"link.cpp",
"link.h",
"listmodel.h",
@@ -232,6 +236,8 @@ Project {
"port.h",
"portlist.cpp",
"portlist.h",
+ "process.cpp",
+ "process.h",
"processenums.h",
"processhandle.cpp",
"processhandle.h",
@@ -255,8 +261,6 @@ Project {
"qtcassert.h",
"qtcolorbutton.cpp",
"qtcolorbutton.h",
- "qtcprocess.cpp",
- "qtcprocess.h",
"qtcsettings.cpp",
"qtcsettings.h",
"reloadpromptutils.cpp",
@@ -268,6 +272,10 @@ Project {
"savefile.cpp",
"savefile.h",
"scopedswap.h",
+ "scopedtimer.cpp",
+ "scopedtimer.h",
+ "searchresultitem.cpp",
+ "searchresultitem.h",
"set_algorithm.h",
"settingsaccessor.cpp",
"settingsaccessor.h",
@@ -293,12 +301,12 @@ Project {
"stringtable.h",
"stringutils.cpp",
"stringutils.h",
+ "styleanimator.cpp",
+ "styleanimator.h",
"styledbar.cpp",
"styledbar.h",
"stylehelper.cpp",
"stylehelper.h",
- "tasktree.cpp",
- "tasktree.h",
"templateengine.cpp",
"templateengine.h",
"temporarydirectory.cpp",
@@ -307,8 +315,10 @@ Project {
"temporaryfile.h",
"terminalcommand.cpp",
"terminalcommand.h",
- "terminalprocess.cpp",
- "terminalprocess_p.h",
+ "terminalhooks.cpp",
+ "terminalhooks.h",
+ "terminalinterface.cpp",
+ "terminalinterface.h",
"textfieldcheckbox.cpp",
"textfieldcheckbox.h",
"textfieldcombobox.cpp",
@@ -461,6 +471,7 @@ Project {
Export {
Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] }
+ Depends { name: "Tasking" }
cpp.includePaths: base.concat("mimetypes2")
}
}
diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc
index 1f2ef64898..c0f2d2559a 100644
--- a/src/libs/utils/utils.qrc
+++ b/src/libs/utils/utils.qrc
@@ -36,6 +36,8 @@
<file>images/unlocked@2x.png</file>
<file>images/pinned.png</file>
<file>images/pinned@2x.png</file>
+ <file>images/pinned_small.png</file>
+ <file>images/pinned_small@2x.png</file>
<file>images/broken.png</file>
<file>images/broken@2x.png</file>
<file>images/notloaded.png</file>
@@ -173,6 +175,8 @@
<file>images/iconoverlay_add@2x.png</file>
<file>images/iconoverlay_add_background.png</file>
<file>images/iconoverlay_add_background@2x.png</file>
+ <file>images/iconoverlay_close_small.png</file>
+ <file>images/iconoverlay_close_small@2x.png</file>
<file>images/iconoverlay_error.png</file>
<file>images/iconoverlay_error@2x.png</file>
<file>images/iconoverlay_error_background.png</file>
diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp
index 7263b28594..01b1f0c1db 100644
--- a/src/libs/utils/utilsicons.cpp
+++ b/src/libs/utils/utilsicons.cpp
@@ -24,6 +24,8 @@ const Icon UNLOCKED({
{":/utils/images/unlocked.png", Theme::PanelTextColorDark}}, Icon::Tint);
const Icon PINNED({
{":/utils/images/pinned.png", Theme::PanelTextColorDark}}, Icon::Tint);
+const Icon PINNED_SMALL({
+ {":/utils/images/pinned_small.png", Theme::PanelTextColorDark}}, Icon::Tint);
const Icon NEXT({
{":/utils/images/next.png", Theme::IconsWarningColor}}, Icon::MenuTintedStyle);
const Icon NEXT_TOOLBAR({
@@ -225,6 +227,8 @@ const Icon INTERRUPT_SMALL_TOOLBAR({
{":/utils/images/interrupt_small.png", Theme::IconsInterruptToolBarColor}});
const Icon BOUNDING_RECT({
{":/utils/images/boundingrect.png", Theme::IconsBaseColor}});
+const Icon EYE_OPEN({
+ {":/utils/images/eye_open.png", Theme::PanelTextColorMid}}, Icon::Tint);
const Icon EYE_OPEN_TOOLBAR({
{":/utils/images/eye_open.png", Theme::IconsBaseColor}});
const Icon EYE_CLOSED_TOOLBAR({
diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h
index b8fda0c53f..5a75267a36 100644
--- a/src/libs/utils/utilsicons.h
+++ b/src/libs/utils/utilsicons.h
@@ -19,6 +19,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon LOCKED;
QTCREATOR_UTILS_EXPORT extern const Icon UNLOCKED_TOOLBAR;
QTCREATOR_UTILS_EXPORT extern const Icon UNLOCKED;
QTCREATOR_UTILS_EXPORT extern const Icon PINNED;
+QTCREATOR_UTILS_EXPORT extern const Icon PINNED_SMALL;
QTCREATOR_UTILS_EXPORT extern const Icon NEXT;
QTCREATOR_UTILS_EXPORT extern const Icon NEXT_TOOLBAR;
QTCREATOR_UTILS_EXPORT extern const Icon PREV;
@@ -120,6 +121,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon STOP_SMALL_TOOLBAR;
QTCREATOR_UTILS_EXPORT extern const Icon INTERRUPT_SMALL;
QTCREATOR_UTILS_EXPORT extern const Icon INTERRUPT_SMALL_TOOLBAR;
QTCREATOR_UTILS_EXPORT extern const Icon BOUNDING_RECT;
+QTCREATOR_UTILS_EXPORT extern const Icon EYE_OPEN;
QTCREATOR_UTILS_EXPORT extern const Icon EYE_OPEN_TOOLBAR;
QTCREATOR_UTILS_EXPORT extern const Icon EYE_CLOSED_TOOLBAR;
QTCREATOR_UTILS_EXPORT extern const Icon REPLACE;
diff --git a/src/libs/utils/utiltypes.h b/src/libs/utils/utiltypes.h
new file mode 100644
index 0000000000..967eecb5a5
--- /dev/null
+++ b/src/libs/utils/utiltypes.h
@@ -0,0 +1,14 @@
+// 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
+
+#pragma once
+
+#include <functional>
+
+namespace Utils {
+class FilePath;
+
+enum class IterationPolicy { Stop, Continue };
+
+using FilePathPredicate = std::function<bool(const FilePath &)>;
+} // namespace Utils
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 8c4f2e2bb6..33aa3db6a0 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -27,6 +27,7 @@ add_subdirectory(help)
add_subdirectory(resourceeditor)
add_subdirectory(nim)
add_subdirectory(conan)
+add_subdirectory(vcpkg)
# Level 4: (only depends on Level 3 and below)
add_subdirectory(classview)
@@ -53,17 +54,19 @@ add_subdirectory(mesonprojectmanager)
add_subdirectory(perforce)
add_subdirectory(qmakeprojectmanager)
add_subdirectory(qmljstools)
-add_subdirectory(qmlprojectmanager)
add_subdirectory(scxmleditor)
add_subdirectory(subversion)
add_subdirectory(compilationdatabaseprojectmanager)
add_subdirectory(languageclient)
+add_subdirectory(qmldesignerbase)
+
# Level 6:
add_subdirectory(cmakeprojectmanager)
add_subdirectory(debugger)
add_subdirectory(coco)
add_subdirectory(gitlab)
+add_subdirectory(qmlprojectmanager)
# Level 7:
add_subdirectory(android)
@@ -100,3 +103,5 @@ add_subdirectory(qnx)
add_subdirectory(webassembly)
add_subdirectory(mcusupport)
add_subdirectory(saferenderer)
+add_subdirectory(copilot)
+add_subdirectory(terminal)
diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs
index 5c9b6564c7..c6d1611636 100644
--- a/src/plugins/android/android.qbs
+++ b/src/plugins/android/android.qbs
@@ -116,9 +116,7 @@ Project {
"sdkmanageroutputparser.h"
]
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"android_tst.qrc",
"androidsdkmanager_test.cpp",
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index 7cf62fadf8..6740370302 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -10,25 +10,21 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
-#include <QApplication>
#include <QLoggingCategory>
#include <QMainWindow>
#include <QMessageBox>
-#include <QSettings>
#include <chrono>
-#include <functional>
using namespace Utils;
+using namespace std;
namespace Android::Internal {
-using namespace std;
-
const int avdCreateTimeoutMs = 30000;
static Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager", QtWarningMsg)
@@ -41,7 +37,7 @@ static Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager", QtWarningMsg)
bool AndroidAvdManager::avdManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output)
{
CommandLine cmd(config.avdManagerToolPath(), args);
- QtcProcess proc;
+ Process proc;
Environment env = AndroidConfigurations::toolsEnvironment(config);
proc.setEnvironment(env);
qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
@@ -89,7 +85,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA
avdManager.addArg("-f");
qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << avdManager.toUserOutput();
- QtcProcess proc;
+ Process proc;
proc.setProcessMode(ProcessMode::Writer);
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config));
proc.setCommand(avdManager);
@@ -143,14 +139,14 @@ AndroidAvdManager::~AndroidAvdManager() = default;
QFuture<CreateAvdInfo> AndroidAvdManager::createAvd(CreateAvdInfo info) const
{
- return runAsync(&createAvdCommand, m_config, info);
+ return Utils::asyncRun(&createAvdCommand, m_config, info);
}
bool AndroidAvdManager::removeAvd(const QString &name) const
{
const CommandLine command(m_config.avdManagerToolPath(), {"delete", "avd", "-n", name});
qCDebug(avdManagerLog).noquote() << "Running command (removeAvd):" << command.toUserOutput();
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(5);
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config));
proc.setCommand(command);
@@ -221,14 +217,14 @@ static AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config)
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
{
- return runAsync(listVirtualDevices, m_config);
+ return Utils::asyncRun(listVirtualDevices, m_config);
}
QString AndroidAvdManager::startAvd(const QString &name) const
{
if (!findAvd(name).isEmpty() || startAvdAsync(name))
return waitForAvd(name);
- return QString();
+ return {};
}
static bool is32BitUserSpace()
@@ -236,7 +232,7 @@ static bool is32BitUserSpace()
// Do a similar check as android's emulator is doing:
if (HostOsInfo::isLinuxHost()) {
if (QSysInfo::WordSize == 32) {
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(3);
proc.setCommand({"getconf", {"LONG_BIT"}});
proc.runBlocking();
@@ -262,13 +258,13 @@ bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
return false;
}
- // TODO: Here we are potentially leaking QtcProcess instance in case when shutdown happens
+ // TODO: Here we are potentially leaking Process instance in case when shutdown happens
// after the avdProcess has started and before it has finished. Giving a parent object here
// should solve the issue. However, AndroidAvdManager is not a QObject, so no clue what parent
// would be the most appropriate. Preferably some object taken form android plugin...
- QtcProcess *avdProcess = new QtcProcess;
+ Process *avdProcess = new Process;
avdProcess->setProcessChannelMode(QProcess::MergedChannels);
- QObject::connect(avdProcess, &QtcProcess::done, avdProcess, [avdProcess] {
+ QObject::connect(avdProcess, &Process::done, avdProcess, [avdProcess] {
if (avdProcess->exitCode()) {
const QString errorOutput = QString::fromLatin1(avdProcess->readAllRawStandardOutput());
QMetaObject::invokeMethod(Core::ICore::mainWindow(), [errorOutput] {
@@ -301,21 +297,21 @@ QString AndroidAvdManager::findAvd(const QString &avdName) const
if (device.avdName == avdName)
return device.serialNumber;
}
- return QString();
+ return {};
}
QString AndroidAvdManager::waitForAvd(const QString &avdName,
- const QFutureInterfaceBase &fi) const
+ const std::optional<QFuture<void>> &future) const
{
// we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
// 60 rounds of 2s sleeping, two minutes for the avd to start
QString serialNumber;
for (int i = 0; i < 60; ++i) {
- if (fi.isCanceled())
+ if (future && future->isCanceled())
return {};
serialNumber = findAvd(avdName);
if (!serialNumber.isEmpty())
- return waitForBooted(serialNumber, fi) ? serialNumber : QString();
+ return waitForBooted(serialNumber, future) ? serialNumber : QString();
QThread::sleep(2);
}
return {};
@@ -328,7 +324,7 @@ bool AndroidAvdManager::isAvdBooted(const QString &device) const
const CommandLine command({m_config.adbToolPath(), arguments});
qCDebug(avdManagerLog).noquote() << "Running command (isAvdBooted):" << command.toUserOutput();
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(10);
adbProc.setCommand(command);
adbProc.runBlocking();
@@ -339,11 +335,11 @@ bool AndroidAvdManager::isAvdBooted(const QString &device) const
}
bool AndroidAvdManager::waitForBooted(const QString &serialNumber,
- const QFutureInterfaceBase &fi) const
+ const std::optional<QFuture<void>> &future) const
{
// found a serial number, now wait until it's done booting...
for (int i = 0; i < 60; ++i) {
- if (fi.isCanceled())
+ if (future && future->isCanceled())
return false;
if (isAvdBooted(serialNumber))
return true;
diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h
index cad3a2efe7..545dedbfe2 100644
--- a/src/plugins/android/androidavdmanager.h
+++ b/src/plugins/android/androidavdmanager.h
@@ -4,8 +4,9 @@
#include "androidconfigurations.h"
-#include <functional>
-#include <memory>
+#include <QFuture>
+
+#include <optional>
namespace Android::Internal {
@@ -22,14 +23,14 @@ public:
QString startAvd(const QString &name) const;
bool startAvdAsync(const QString &avdName) const;
QString findAvd(const QString &avdName) const;
- QString waitForAvd(const QString &avdName, const QFutureInterfaceBase &fi = {}) const;
+ QString waitForAvd(const QString &avdName, const std::optional<QFuture<void>> &future = {}) const;
bool isAvdBooted(const QString &device) const;
static bool avdManagerCommand(const AndroidConfig &config,
const QStringList &args,
QString *output);
private:
- bool waitForBooted(const QString &serialNumber, const QFutureInterfaceBase &fi = {}) const;
+ bool waitForBooted(const QString &serialNumber, const std::optional<QFuture<void>> &future = {}) const;
private:
const AndroidConfig &m_config;
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index adf96eb68d..b27730b45b 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -36,7 +36,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCheckBox>
#include <QComboBox>
@@ -137,8 +137,9 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
createSignPackageGroup(),
createApplicationGroup(),
createAdvancedGroup(),
- createAdditionalLibrariesGroup()
- }.attachTo(this, WithoutMargins);
+ createAdditionalLibrariesGroup(),
+ noMargin
+ }.attachTo(this);
connect(m_step->buildConfiguration(), &BuildConfiguration::buildTypeChanged,
this, &AndroidBuildApkWidget::updateSigningWarning);
@@ -711,6 +712,13 @@ void AndroidBuildApkStep::doRun()
return;
}
+ if (AndroidManager::skipInstallationAndPackageSteps(target())) {
+ reportWarningOrError(Tr::tr("Product type is not an application, not building an APK."),
+ Task::Warning);
+ emit finished(true);
+ return;
+ }
+
auto setup = [this] {
const auto androidAbis = AndroidManager::applicationAbis(target());
const QString buildKey = target()->activeBuildKey();
@@ -863,7 +871,7 @@ void AndroidBuildApkStep::updateBuildToolsVersionInJsonFile()
if (!contents)
return;
- QRegularExpression regex(QLatin1String("\"sdkBuildToolsRevision\":.\"[0-9.]+\""));
+ static const QRegularExpression regex(R"("sdkBuildToolsRevision":."[0-9.]+")");
QRegularExpressionMatch match = regex.match(QString::fromUtf8(contents.value()));
const QString version = buildToolsVersion().toString();
if (match.hasMatch() && !version.isEmpty()) {
@@ -925,7 +933,8 @@ void AndroidBuildApkStep::setBuildToolsVersion(const QVersionNumber &version)
void AndroidBuildApkStep::stdError(const QString &output)
{
QString newOutput = output;
- newOutput.remove(QRegularExpression("^(\\n)+"));
+ static const QRegularExpression re("^(\\n)+");
+ newOutput.remove(re);
if (newOutput.isEmpty())
return;
@@ -1041,7 +1050,7 @@ QAbstractItemModel *AndroidBuildApkStep::keystoreCertificates()
const QStringList params = {"-list", "-v", "-keystore", m_keystorePath.toUserOutput(),
"-storepass", m_keystorePasswd, "-J-Duser.language=en"};
- QtcProcess keytoolProc;
+ Process keytoolProc;
keytoolProc.setTimeoutS(30);
keytoolProc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), params});
keytoolProc.runBlocking(EventLoopMode::On);
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index 5477bf14bf..9c444f6e75 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -19,7 +19,7 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/toolchainmanager.h>
#include <debugger/debuggeritemmanager.h>
@@ -34,8 +34,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QApplication>
@@ -306,7 +306,7 @@ void AndroidConfig::parseDependenciesJson()
auto fillQtVersionsRange = [](const QString &shortVersion) {
QList<QVersionNumber> versions;
- const QRegularExpression re(R"(([0-9]\.[0-9]+\.)\[([0-9]+)\-([0-9]+)\])");
+ static const QRegularExpression re(R"(([0-9]\.[0-9]+\.)\[([0-9]+)\-([0-9]+)\])");
QRegularExpressionMatch match = re.match(shortVersion);
if (match.hasMatch() && match.lastCapturedIndex() == 3)
for (int i = match.captured(2).toInt(); i <= match.captured(3).toInt(); ++i)
@@ -432,8 +432,12 @@ QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms)
QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform)
{
- return platform && platform->apiLevel() > 0 ?
- QString("android-%1").arg(platform->apiLevel()) : "";
+ if (platform && platform->apiLevel() > 0) {
+ QString sdkStylePath = platform->sdkStylePath();
+ return sdkStylePath.remove("platforms;");
+ }
+
+ return {};
}
FilePath AndroidConfig::adbToolPath() const
@@ -597,7 +601,7 @@ FilePath AndroidConfig::keytoolPath() const
QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
{
QVector<AndroidDeviceInfo> devices;
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(30);
CommandLine cmd{adbToolPath(), {"devices"}};
adbProc.setCommand(cmd);
@@ -666,7 +670,7 @@ QString AndroidConfig::getDeviceProperty(const QString &device, const QString &p
AndroidDeviceInfo::adbSelector(device));
cmd.addArgs({"shell", "getprop", property});
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(10);
adbProc.setCommand(cmd);
adbProc.runBlocking();
@@ -743,7 +747,7 @@ QStringList AndroidConfig::getAbis(const QString &device)
// First try via ro.product.cpu.abilist
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << "shell" << "getprop" << "ro.product.cpu.abilist";
- QtcProcess adbProc;
+ Process adbProc;
adbProc.setTimeoutS(10);
adbProc.setCommand({adbTool, arguments});
adbProc.runBlocking();
@@ -766,7 +770,7 @@ QStringList AndroidConfig::getAbis(const QString &device)
else
arguments << QString::fromLatin1("ro.product.cpu.abi%1").arg(i);
- QtcProcess abiProc;
+ Process abiProc;
abiProc.setTimeoutS(10);
abiProc.setCommand({adbTool, arguments});
abiProc.runBlocking();
@@ -892,7 +896,7 @@ QVersionNumber AndroidConfig::ndkVersion(const FilePath &ndkPath)
// r6a
// r10e (64 bit)
QString content = QString::fromUtf8(reader.data());
- QRegularExpression re("(r)(?<major>[0-9]{1,2})(?<minor>[a-z]{1,1})");
+ static const QRegularExpression re("(r)(?<major>[0-9]{1,2})(?<minor>[a-z]{1,1})");
QRegularExpressionMatch match = re.match(content);
if (match.hasMatch()) {
QString major = match.captured("major");
@@ -1169,7 +1173,7 @@ void AndroidConfigurations::removeUnusedDebuggers()
uniqueNdks.append(ndkLocation);
}
- uniqueNdks.append(FileUtils::toFilePathList(currentConfig().getCustomNdkList()).toVector());
+ uniqueNdks.append(FileUtils::toFilePathList(currentConfig().getCustomNdkList()));
const QList<Debugger::DebuggerItem> allDebuggers = Debugger::DebuggerItemManager::debuggers();
for (const Debugger::DebuggerItem &debugger : allDebuggers) {
@@ -1526,7 +1530,7 @@ FilePath AndroidConfig::getJdkPath()
args << "-c"
<< "readlink -f $(which java)";
- QtcProcess findJdkPathProc;
+ Process findJdkPathProc;
findJdkPathProc.setCommand({"sh", args});
findJdkPathProc.start();
findJdkPathProc.waitForFinished();
diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp
index 003eb7e9b9..67e6c8ff83 100644
--- a/src/plugins/android/androidcreatekeystorecertificate.cpp
+++ b/src/plugins/android/androidcreatekeystorecertificate.cpp
@@ -7,7 +7,7 @@
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCheckBox>
#include <QDialog>
@@ -217,7 +217,8 @@ bool AndroidCreateKeystoreCertificate::checkCertificateAlias()
bool AndroidCreateKeystoreCertificate::checkCountryCode()
{
- if (!m_countryLineEdit->text().contains(QRegularExpression("[A-Z]{2}"))) {
+ static const QRegularExpression re("[A-Z]{2}");
+ if (!m_countryLineEdit->text().contains(re)) {
m_infoLabel->show();
m_infoLabel->setText(Tr::tr("Invalid country code."));
return false;
@@ -271,7 +272,7 @@ void AndroidCreateKeystoreCertificate::buttonBoxAccepted()
"-keypass", certificatePassword(),
"-dname", distinguishedNames});
- QtcProcess genKeyCertProc;
+ Process genKeyCertProc;
genKeyCertProc.setTimeoutS(15);
genKeyCertProc.setCommand(command);
genKeyCertProc.runBlocking(EventLoopMode::On);
diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp
index 138740e4a3..bb4fc51b98 100644
--- a/src/plugins/android/androiddebugsupport.cpp
+++ b/src/plugins/android/androiddebugsupport.cpp
@@ -21,7 +21,7 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QHostAddress>
#include <QJsonDocument>
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index c305987a50..84f0ffbbe5 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -2,10 +2,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "androiddeployqtstep.h"
+
#include "androidavdmanager.h"
#include "androidbuildapkstep.h"
#include "androidconstants.h"
-#include "androiddeployqtstep.h"
#include "androiddevice.h"
#include "androidmanager.h"
#include "androidqtversion.h"
@@ -16,6 +17,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/buildsteplist.h>
@@ -27,13 +29,17 @@
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
+#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/commandline.h>
+#include <utils/environment.h>
+#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QCheckBox>
#include <QFileDialog>
@@ -59,7 +65,79 @@ const QLatin1String InstallFailedVersionDowngrade("INSTALL_FAILED_VERSION_DOWNGR
// AndroidDeployQtStep
-AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Utils::Id id)
+class AndroidDeployQtStep : public BuildStep
+{
+ Q_OBJECT
+
+ enum DeployErrorCode
+ {
+ NoError = 0,
+ InconsistentCertificates = 0x0001,
+ UpdateIncompatible = 0x0002,
+ PermissionModelDowngrade = 0x0004,
+ VersionDowngrade = 0x0008,
+ Failure = 0x0010
+ };
+
+public:
+ AndroidDeployQtStep(BuildStepList *bc, Id id);
+
+signals:
+ void askForUninstall(DeployErrorCode errorCode);
+
+private:
+ void runCommand(const CommandLine &command);
+
+ bool init() override;
+ void doRun() override;
+ void doCancel() override;
+ void gatherFilesToPull();
+ DeployErrorCode runDeploy();
+ void slotAskForUninstall(DeployErrorCode errorCode);
+
+ void runImpl(QPromise<bool> &promise);
+
+ QWidget *createConfigWidget() override;
+
+ void processReadyReadStdOutput(DeployErrorCode &errorCode);
+ void stdOutput(const QString &line);
+ void processReadyReadStdError(DeployErrorCode &errorCode);
+ void stdError(const QString &line);
+ DeployErrorCode parseDeployErrors(const QString &deployOutputLine) const;
+
+ friend void operator|=(DeployErrorCode &e1, const DeployErrorCode &e2) {
+ e1 = static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
+ }
+
+ friend DeployErrorCode operator|(const DeployErrorCode &e1, const DeployErrorCode &e2) {
+ return static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
+ }
+
+ void reportWarningOrError(const QString &message, Task::TaskType type);
+
+ FilePath m_manifestName;
+ QString m_serialNumber;
+ QString m_avdName;
+ FilePath m_apkPath;
+ QMap<QString, FilePath> m_filesToPull;
+
+ QStringList m_androidABIs;
+ BoolAspect *m_uninstallPreviousPackage = nullptr;
+ bool m_uninstallPreviousPackageRun = false;
+ bool m_useAndroiddeployqt = false;
+ bool m_askForUninstall = false;
+ CommandLine m_androiddeployqtArgs;
+ FilePath m_adbPath;
+ FilePath m_command;
+ FilePath m_workingDirectory;
+ Environment m_environment;
+ AndroidDeviceInfo m_deviceInfo;
+
+ // The synchronizer has cancelOnWait set to true by default.
+ FutureSynchronizer m_synchronizer;
+};
+
+AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Id id)
: BuildStep(parent, id)
{
setImmutable(true);
@@ -209,9 +287,9 @@ bool AndroidDeployQtStep::init()
reportWarningOrError(Tr::tr("The deployment step's project node is invalid."), Task::Error);
return false;
}
- m_apkPath = Utils::FilePath::fromString(node->data(Constants::AndroidApk).toString());
+ m_apkPath = FilePath::fromString(node->data(Constants::AndroidApk).toString());
if (!m_apkPath.isEmpty()) {
- m_manifestName = Utils::FilePath::fromString(node->data(Constants::AndroidManifest).toString());
+ m_manifestName = FilePath::fromString(node->data(Constants::AndroidManifest).toString());
m_command = AndroidConfigurations::currentConfig().adbToolPath();
AndroidManager::setManifestPath(target(), m_manifestName);
} else {
@@ -254,7 +332,7 @@ bool AndroidDeployQtStep::init()
m_apkPath = AndroidManager::packagePath(target());
m_workingDirectory = bc ? AndroidManager::buildDirectory(target()): FilePath();
}
- m_environment = bc ? bc->environment() : Utils::Environment();
+ m_environment = bc ? bc->environment() : Environment();
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath();
@@ -303,7 +381,7 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy()
cmd.addArgs({"install", "-r", m_apkPath.toString()});
}
- QtcProcess process;
+ Process process;
process.setCommand(cmd);
process.setWorkingDirectory(m_workingDirectory);
process.setEnvironment(m_environment);
@@ -401,15 +479,16 @@ void AndroidDeployQtStep::slotAskForUninstall(DeployErrorCode errorCode)
m_askForUninstall = button == QMessageBox::Yes;
}
-void AndroidDeployQtStep::runImpl(QFutureInterface<bool> &fi)
+void AndroidDeployQtStep::runImpl(QPromise<bool> &promise)
{
if (!m_avdName.isEmpty()) {
- QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi);
+ const QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName,
+ QFuture<void>(promise.future()));
qCDebug(deployStepLog) << "Deploying to AVD:" << m_avdName << serialNumber;
if (serialNumber.isEmpty()) {
reportWarningOrError(Tr::tr("The deployment AVD \"%1\" cannot be started.")
.arg(m_avdName), Task::Error);
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
m_serialNumber = serialNumber;
@@ -445,7 +524,7 @@ void AndroidDeployQtStep::runImpl(QFutureInterface<bool> &fi)
reportWarningOrError(error, Task::Error);
}
}
- fi.reportResult(returnValue == NoError);
+ promise.addResult(returnValue == NoError);
}
void AndroidDeployQtStep::gatherFilesToPull()
@@ -485,7 +564,7 @@ void AndroidDeployQtStep::doRun()
emit finished(success);
watcher->deleteLater();
});
- auto future = Utils::runAsync(&AndroidDeployQtStep::runImpl, this);
+ auto future = Utils::asyncRun(&AndroidDeployQtStep::runImpl, this);
watcher->setFuture(future);
m_synchronizer.addFuture(future);
}
@@ -497,7 +576,7 @@ void AndroidDeployQtStep::doCancel()
void AndroidDeployQtStep::runCommand(const CommandLine &command)
{
- QtcProcess buildProc;
+ Process buildProc;
buildProc.setTimeoutS(2 * 60);
emit addOutput(Tr::tr("Package deploy: Running command \"%1\".").arg(command.toUserOutput()),
OutputFormat::NormalMessage);
@@ -524,10 +603,13 @@ QWidget *AndroidDeployQtStep::createConfigWidget()
AndroidManager::installQASIPackage(target(), packagePath);
});
- Layouting::Form builder;
- builder.addRow(m_uninstallPreviousPackage);
- builder.addRow(installCustomApkButton);
- builder.attachTo(widget, Layouting::WithoutMargins);
+ using namespace Layouting;
+
+ Form {
+ m_uninstallPreviousPackage, br,
+ installCustomApkButton,
+ noMargin
+ }.attachTo(widget);
return widget;
}
@@ -542,7 +624,8 @@ void AndroidDeployQtStep::stdError(const QString &line)
emit addOutput(line, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline);
QString newOutput = line;
- newOutput.remove(QRegularExpression("^(\\n)+"));
+ static const QRegularExpression re("^(\\n)+");
+ newOutput.remove(re);
if (newOutput.isEmpty())
return;
@@ -592,3 +675,5 @@ AndroidDeployQtStepFactory::AndroidDeployQtStepFactory()
}
} // Android::Internal
+
+#include "androiddeployqtstep.moc"
diff --git a/src/plugins/android/androiddeployqtstep.h b/src/plugins/android/androiddeployqtstep.h
index 334be8ea22..dda4304b6a 100644
--- a/src/plugins/android/androiddeployqtstep.h
+++ b/src/plugins/android/androiddeployqtstep.h
@@ -4,16 +4,7 @@
#pragma once
-#include "androiddeviceinfo.h"
-
-#include <projectexplorer/abstractprocessstep.h>
-#include <qtsupport/baseqtversion.h>
-
-#include <utils/commandline.h>
-#include <utils/environment.h>
-#include <utils/futuresynchronizer.h>
-
-namespace Utils { class QtcProcess; }
+#include <projectexplorer/buildstep.h>
namespace Android::Internal {
@@ -23,76 +14,4 @@ public:
AndroidDeployQtStepFactory();
};
-class AndroidDeployQtStep : public ProjectExplorer::BuildStep
-{
- Q_OBJECT
-
- enum DeployErrorCode
- {
- NoError = 0,
- InconsistentCertificates = 0x0001,
- UpdateIncompatible = 0x0002,
- PermissionModelDowngrade = 0x0004,
- VersionDowngrade = 0x0008,
- Failure = 0x0010
- };
-
-public:
- AndroidDeployQtStep(ProjectExplorer::BuildStepList *bc, Utils::Id id);
-
-signals:
- void askForUninstall(DeployErrorCode errorCode);
-
-private:
- void runCommand(const Utils::CommandLine &command);
-
- bool init() override;
- void doRun() override;
- void doCancel() override;
- void gatherFilesToPull();
- DeployErrorCode runDeploy();
- void slotAskForUninstall(DeployErrorCode errorCode);
-
- void runImpl(QFutureInterface<bool> &fi);
-
- QWidget *createConfigWidget() override;
-
- void processReadyReadStdOutput(DeployErrorCode &errorCode);
- void stdOutput(const QString &line);
- void processReadyReadStdError(DeployErrorCode &errorCode);
- void stdError(const QString &line);
- DeployErrorCode parseDeployErrors(const QString &deployOutputLine) const;
-
- friend void operator|=(DeployErrorCode &e1, const DeployErrorCode &e2) {
- e1 = static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
- }
-
- friend DeployErrorCode operator|(const DeployErrorCode &e1, const DeployErrorCode &e2) {
- return static_cast<AndroidDeployQtStep::DeployErrorCode>((int)e1 | (int)e2);
- }
-
- void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type);
-
- Utils::FilePath m_manifestName;
- QString m_serialNumber;
- QString m_avdName;
- Utils::FilePath m_apkPath;
- QMap<QString, Utils::FilePath> m_filesToPull;
-
- QStringList m_androidABIs;
- Utils::BoolAspect *m_uninstallPreviousPackage = nullptr;
- bool m_uninstallPreviousPackageRun = false;
- bool m_useAndroiddeployqt = false;
- bool m_askForUninstall = false;
- static const Utils::Id Id;
- Utils::CommandLine m_androiddeployqtArgs;
- Utils::FilePath m_adbPath;
- Utils::FilePath m_command;
- Utils::FilePath m_workingDirectory;
- Utils::Environment m_environment;
- AndroidDeviceInfo m_deviceInfo;
-
- Utils::FutureSynchronizer m_synchronizer;
-};
-
} // Android::Internal
diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp
index 4c034c30af..ce2f4c11ee 100644
--- a/src/plugins/android/androiddevice.cpp
+++ b/src/plugins/android/androiddevice.cpp
@@ -18,15 +18,14 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/url.h>
-#include <QEventLoop>
#include <QFormLayout>
#include <QInputDialog>
#include <QLoggingCategory>
@@ -388,11 +387,6 @@ IDeviceWidget *AndroidDevice::createWidget()
return new AndroidDeviceWidget(sharedFromThis());
}
-bool AndroidDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
DeviceProcessSignalOperation::Ptr AndroidDevice::signalOperation() const
{
return DeviceProcessSignalOperation::Ptr(new AndroidSignalOperation());
@@ -454,7 +448,7 @@ void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device,
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(device.data());
const QString name = androidDev->avdName();
qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name));
- runAsync([this, name, device] {
+ auto future = Utils::asyncRun([this, name, device] {
const QString serialNumber = m_avdManager.startAvd(name);
// Mark the AVD as ReadyToUse once we know it's started
if (!serialNumber.isEmpty()) {
@@ -462,6 +456,7 @@ void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device,
devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse);
}
});
+ // TODO: use future!
}
void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
@@ -479,7 +474,7 @@ void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
return;
qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name);
- m_removeAvdFutureWatcher.setFuture(runAsync([this, name, device] {
+ m_removeAvdFutureWatcher.setFuture(Utils::asyncRun([this, name, device] {
QPair<IDevice::ConstPtr, bool> pair;
pair.first = device;
pair.second = false;
@@ -618,20 +613,20 @@ void AndroidDeviceManager::setupDevicesWatcher()
}
if (!m_adbDeviceWatcherProcess)
- m_adbDeviceWatcherProcess.reset(new QtcProcess(this));
+ m_adbDeviceWatcherProcess.reset(new Process(this));
if (m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "ADB device watcher is already running.";
return;
}
- connect(m_adbDeviceWatcherProcess.get(), &QtcProcess::done, this, [this] {
+ connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] {
if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) {
qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:"
<< m_adbDeviceWatcherProcess->errorString();
if (!m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now.";
- QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &QtcProcess::start);
+ QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start);
}
}
qCDebug(androidDeviceLog) << "ADB device watcher finished.";
@@ -847,7 +842,6 @@ AndroidDeviceFactory::AndroidDeviceFactory()
setDisplayName(Tr::tr("Android Device"));
setCombinedIcon(":/android/images/androiddevicesmall.png",
":/android/images/androiddevice.png");
-
setConstructionFunction(&AndroidDevice::create);
if (m_androidConfig.sdkToolsOk()) {
setCreator([this] {
diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h
index 0d17c73ec8..cf8046cfa9 100644
--- a/src/plugins/android/androiddevice.h
+++ b/src/plugins/android/androiddevice.h
@@ -15,7 +15,7 @@
#include <QFileSystemWatcher>
#include <QSettings>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Android {
namespace Internal {
@@ -61,7 +61,6 @@ private:
void addActionsIfNotFound();
ProjectExplorer::IDevice::DeviceInfo deviceInformation() const override;
ProjectExplorer::IDeviceWidget *createWidget() override;
- bool canAutoDetectPorts() const override;
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;
@@ -109,7 +108,7 @@ private:
QFutureWatcher<AndroidDeviceInfoList> m_avdsFutureWatcher;
QFutureWatcher<QPair<ProjectExplorer::IDevice::ConstPtr, bool>> m_removeAvdFutureWatcher;
QFileSystemWatcher m_avdFileSystemWatcher;
- std::unique_ptr<Utils::QtcProcess> m_adbDeviceWatcherProcess;
+ std::unique_ptr<Utils::Process> m_adbDeviceWatcherProcess;
AndroidConfig &m_androidConfig;
AndroidAvdManager m_avdManager;
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index a15da30048..0ccca7c8f7 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -24,7 +24,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/buildsystem.h>
@@ -36,8 +36,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QApplication>
@@ -359,6 +359,21 @@ Abi AndroidManager::androidAbi2Abi(const QString &androidAbi)
}
}
+bool AndroidManager::skipInstallationAndPackageSteps(const Target *target)
+{
+ const Project *p = target->project();
+
+ const Core::Context cmakeCtx = Core::Context(CMakeProjectManager::Constants::CMAKE_PROJECT_ID);
+ const bool isCmakeProject = p->projectContext() == cmakeCtx;
+ if (isCmakeProject)
+ return false; // CMake reports ProductType::Other for Android Apps
+
+ const ProjectNode *n = p->rootProjectNode()->findProjectNode([] (const ProjectNode *n) {
+ return n->productType() == ProductType::App;
+ });
+ return n == nullptr; // If no Application target found, then skip steps
+}
+
FilePath AndroidManager::manifestSourcePath(const Target *target)
{
if (const ProjectNode *node = currentProjectNode(target)) {
@@ -519,9 +534,9 @@ QString AndroidManager::androidNameForApiLevel(int x)
case 31:
return QLatin1String("Android 12.0 (S)");
case 32:
- return QLatin1String("Android 12L (API 32)");
+ return QLatin1String("Android 12L (Sv2, API 32)");
case 33:
- return QLatin1String("Android Tiramisu");
+ return QLatin1String("Android 13.0 (Tiramisu)");
default:
return Tr::tr("Unknown Android version. API Level: %1").arg(x);
}
@@ -597,7 +612,7 @@ bool AndroidManager::checkKeystorePassword(const FilePath &keystorePath,
const CommandLine cmd(AndroidConfigurations::currentConfig().keytoolPath(),
{"-list", "-keystore", keystorePath.toUserOutput(),
"--storepass", keystorePasswd});
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(10);
proc.setCommand(cmd);
proc.runBlocking(EventLoopMode::On);
@@ -617,7 +632,7 @@ bool AndroidManager::checkCertificatePassword(const FilePath &keystorePath,
else
arguments << certificatePasswd;
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(10);
proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments});
proc.runBlocking(EventLoopMode::On);
@@ -631,7 +646,7 @@ bool AndroidManager::checkCertificateExists(const FilePath &keystorePath,
QStringList arguments = { "-list", "-keystore", keystorePath.toUserOutput(),
"--storepass", keystorePasswd, "-alias", alias };
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(10);
proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments});
proc.runBlocking(EventLoopMode::On);
@@ -666,7 +681,7 @@ SdkToolResult AndroidManager::runCommand(const CommandLine &command,
const QByteArray &writeData, int timeoutS)
{
Android::SdkToolResult cmdResult;
- QtcProcess cmdProc;
+ Process cmdProc;
cmdProc.setTimeoutS(timeoutS);
cmdProc.setWriteData(writeData);
qCDebug(androidManagerLog) << "Running command (sync):" << command.toUserOutput();
diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h
index d468ec3279..ec6aff85bc 100644
--- a/src/plugins/android/androidmanager.h
+++ b/src/plugins/android/androidmanager.h
@@ -82,6 +82,7 @@ public:
static bool matchedAbis(const QStringList &deviceAbis, const QStringList &appAbis);
static QString devicePreferredAbi(const QStringList &deviceAbis, const QStringList &appAbis);
static ProjectExplorer::Abi androidAbi2Abi(const QString &androidAbi);
+ static bool skipInstallationAndPackageSteps(const ProjectExplorer::Target *target);
static QString androidNameForApiLevel(int x);
diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp
index 009a021943..9c6a616abe 100644
--- a/src/plugins/android/androidmanifesteditorwidget.cpp
+++ b/src/plugins/android/androidmanifesteditorwidget.cpp
@@ -19,8 +19,8 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectwindow.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/kitinformation.h>
@@ -73,7 +73,7 @@ static bool checkPackageName(const QString &packageName)
static Target *androidTarget(const FilePath &fileName)
{
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (Target *target = project->activeTarget()) {
Kit *kit = target->kit();
if (DeviceTypeKitAspect::deviceTypeId(kit) == Android::Constants::ANDROID_DEVICE_TYPE
diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp
index 3b8a83fa57..e29bac3476 100644
--- a/src/plugins/android/androidpackageinstallationstep.cpp
+++ b/src/plugins/android/androidpackageinstallationstep.cpp
@@ -22,7 +22,7 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
#include <QLoggingCategory>
@@ -122,6 +122,14 @@ void AndroidPackageInstallationStep::setupOutputFormatter(OutputFormatter *forma
void AndroidPackageInstallationStep::doRun()
{
+ if (AndroidManager::skipInstallationAndPackageSteps(target())) {
+ reportWarningOrError(Tr::tr("Product type is not an application, not running the "
+ "Make install step."),
+ Task::Warning);
+ emit finished(true);
+ return;
+ }
+
QString error;
for (const QString &dir : std::as_const(m_androidDirsToClean)) {
FilePath androidDir = FilePath::fromString(dir);
diff --git a/src/plugins/android/androidplugin.cpp b/src/plugins/android/androidplugin.cpp
index b8ff5dbe0b..459edad2bb 100644
--- a/src/plugins/android/androidplugin.cpp
+++ b/src/plugins/android/androidplugin.cpp
@@ -34,7 +34,6 @@
#endif
#include <coreplugin/icore.h>
-#include <utils/checkablemessagebox.h>
#include <utils/infobar.h>
#include <languageclient/languageclientsettings.h>
@@ -45,11 +44,13 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtversionmanager.h>
+#include <QTimer>
+
using namespace ProjectExplorer;
using namespace ProjectExplorer::Constants;
@@ -151,8 +152,7 @@ void AndroidPlugin::kitsRestored()
void AndroidPlugin::askUserAboutAndroidSetup()
{
- if (!Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(), kSetupAndroidSetting)
- || !Core::ICore::infoBar()->canInfoBeAdded(kSetupAndroidSetting))
+ if (!Core::ICore::infoBar()->canInfoBeAdded(kSetupAndroidSetting))
return;
Utils::InfoBarEntry
diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp
index 914cbc0acd..4d95f485d0 100644
--- a/src/plugins/android/androidpotentialkit.cpp
+++ b/src/plugins/android/androidpotentialkit.cpp
@@ -8,23 +8,35 @@
#include <app/app_version.h>
-#include <utils/detailswidget.h>
-#include <utils/utilsicons.h>
-
#include <coreplugin/coreicons.h>
#include <coreplugin/icore.h>
+
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
+
#include <qtsupport/qtversionmanager.h>
#include <qtsupport/baseqtversion.h>
+#include <utils/detailswidget.h>
+#include <utils/utilsicons.h>
+
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
-using namespace Android;
-using namespace Android::Internal;
+
+namespace Android::Internal {
+
+class AndroidPotentialKitWidget : public Utils::DetailsWidget
+{
+public:
+ AndroidPotentialKitWidget(QWidget *parent);
+
+private:
+ void openOptions();
+ void recheck();
+};
QString AndroidPotentialKit::displayName() const
{
@@ -102,3 +114,5 @@ void AndroidPotentialKitWidget::recheck()
}
}
}
+
+} // Android::Internal
diff --git a/src/plugins/android/androidpotentialkit.h b/src/plugins/android/androidpotentialkit.h
index 9e1ed41742..d111029475 100644
--- a/src/plugins/android/androidpotentialkit.h
+++ b/src/plugins/android/androidpotentialkit.h
@@ -4,14 +4,11 @@
#pragma once
#include <projectexplorer/ipotentialkit.h>
-#include <utils/detailswidget.h>
-namespace Android {
-namespace Internal {
+namespace Android::Internal {
class AndroidPotentialKit : public ProjectExplorer::IPotentialKit
{
- Q_OBJECT
public:
QString displayName() const override;
void executeFromMenu() override;
@@ -19,15 +16,4 @@ public:
bool isEnabled() const override;
};
-class AndroidPotentialKitWidget : public Utils::DetailsWidget
-{
- Q_OBJECT
-public:
- AndroidPotentialKitWidget(QWidget *parent);
-private:
- void openOptions();
- void recheck();
-};
-
-}
-}
+} // Android::Internal
diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp
index ab9fb34de5..d296dfe677 100644
--- a/src/plugins/android/androidqmlpreviewworker.cpp
+++ b/src/plugins/android/androidqmlpreviewworker.cpp
@@ -27,8 +27,8 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <QDateTime>
#include <QDeadlineTimer>
@@ -117,7 +117,7 @@ private:
QStringList m_avdAbis;
int m_viewerPid = -1;
QFutureWatcher<void> m_pidFutureWatcher;
- Utils::QtcProcess m_logcatProcess;
+ Utils::Process m_logcatProcess;
QString m_logcatStartTimeStamp;
UploadInfo m_uploadInfo;
};
@@ -163,7 +163,7 @@ bool AndroidQmlPreviewWorker::isPreviewRunning(int lastKnownPid) const
void AndroidQmlPreviewWorker::startPidWatcher()
{
- m_pidFutureWatcher.setFuture(runAsync([this] {
+ m_pidFutureWatcher.setFuture(Utils::asyncRun([this] {
// wait for started
const int sleepTimeMs = 2000;
QDeadlineTimer deadline(20000);
@@ -226,7 +226,7 @@ AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(RunControl *runControl)
connect(this, &AndroidQmlPreviewWorker::previewPidChanged,
this, &AndroidQmlPreviewWorker::startLogcat);
- connect(this, &RunWorker::stopped, &m_logcatProcess, &QtcProcess::stop);
+ connect(this, &RunWorker::stopped, &m_logcatProcess, &Process::stop);
m_logcatProcess.setStdOutCallback([this](const QString &stdOut) {
filterLogcatAndAppendMessage(stdOut);
});
@@ -376,7 +376,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder,
{
const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(m_rc->kit());
const FilePath rccBinary = qtVersion->rccFilePath();
- QtcProcess rccProcess;
+ Process rccProcess;
FilePath qrcPath = FilePath::fromString(basename + ".qrc4viewer");
const FilePath qmlrcPath = FilePath::fromString(QDir::tempPath()) / (basename + packageSuffix);
diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp
index ef469da2b6..e62145566a 100644
--- a/src/plugins/android/androidrunconfiguration.cpp
+++ b/src/plugins/android/androidrunconfiguration.cpp
@@ -18,8 +18,8 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/detailswidget.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
using namespace ProjectExplorer;
diff --git a/src/plugins/android/androidruncontrol.h b/src/plugins/android/androidruncontrol.h
index a466de7226..f96c19443b 100644
--- a/src/plugins/android/androidruncontrol.h
+++ b/src/plugins/android/androidruncontrol.h
@@ -3,14 +3,10 @@
#pragma once
-#include "androidrunner.h"
-
-#include <projectexplorer/runconfiguration.h>
+#include <projectexplorer/runcontrol.h>
namespace Android::Internal {
-class AndroidRunner;
-
class AndroidRunWorkerFactory final : public ProjectExplorer::RunWorkerFactory
{
public:
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 2676fa6305..65ef869558 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -11,8 +11,8 @@
#include <debugger/debuggerrunconfigurationaspect.h>
#include <projectexplorer/buildconfiguration.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/environmentaspect.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
@@ -20,10 +20,10 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
+#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
#include <utils/url.h>
@@ -78,8 +78,10 @@ static qint64 extractPID(const QString &output, const QString &packageName)
return pid;
}
-static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList selector,
- const QString &packageName, bool preNougat)
+static void findProcessPIDAndUser(QPromise<PidUserPair> &promise,
+ QStringList selector,
+ const QString &packageName,
+ bool preNougat)
{
if (packageName.isEmpty())
return;
@@ -96,7 +98,7 @@ static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
do {
QThread::msleep(200);
- QtcProcess proc;
+ Process proc;
proc.setCommand({adbPath, args});
proc.runBlocking();
const QString out = proc.allOutput();
@@ -106,16 +108,16 @@ static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList
if (!out.isEmpty())
processPID = out.trimmed().toLongLong();
}
- } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !fi.isCanceled());
+ } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !promise.isCanceled());
qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat;
qint64 processUser = 0;
- if (processPID > 0 && !fi.isCanceled()) {
+ if (processPID > 0 && !promise.isCanceled()) {
args = {selector};
args.append({"shell", "ps", "-o", "user", "-p"});
args.append(QString::number(processPID));
- QtcProcess proc;
+ Process proc;
proc.setCommand({adbPath, args});
proc.runBlocking();
const QString out = proc.allOutput();
@@ -133,8 +135,8 @@ static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList
qCDebug(androidRunWorkerLog) << "USER found:" << processUser;
- if (!fi.isCanceled())
- fi.reportResult(PidUserPair(processPID, processUser));
+ if (!promise.isCanceled())
+ promise.addResult(PidUserPair(processPID, processUser));
}
static void deleter(QProcess *p)
@@ -724,8 +726,11 @@ void AndroidRunnerWorker::asyncStart()
{
asyncStartHelper();
- m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPIDAndUser, selector(),
- m_packageName, m_isPreNougat),
+ m_pidFinder = Utils::onResultReady(Utils::asyncRun(findProcessPIDAndUser,
+ selector(),
+ m_packageName,
+ m_isPreNougat),
+ this,
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
}
diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp
index 4494f4837f..1839f7c21c 100644
--- a/src/plugins/android/androidsdkmanager.cpp
+++ b/src/plugins/android/androidsdkmanager.cpp
@@ -8,9 +8,9 @@
#include "sdkmanageroutputparser.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#include <QFutureWatcher>
@@ -34,7 +34,7 @@ namespace Internal {
const int sdkManagerCmdTimeoutS = 60;
const int sdkManagerOperationTimeoutS = 600;
-using SdkCmdFutureInterface = QFutureInterface<AndroidSdkManager::OperationOutput>;
+using SdkCmdPromise = QPromise<AndroidSdkManager::OperationOutput>;
static const QRegularExpression &assertionRegExp()
{
@@ -93,7 +93,7 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
qCDebug(sdkManagerLog).noquote() << "Running SDK Manager command (sync):"
<< CommandLine(config.sdkManagerToolPath(), newArgs)
.toUserOutput();
- QtcProcess proc;
+ Process proc;
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config));
proc.setTimeoutS(timeout);
proc.setTimeOutMessageBoxEnabled(true);
@@ -111,7 +111,7 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
after the lapse of \a timeout seconds. The function blocks the calling thread.
*/
static void sdkManagerCommand(const AndroidConfig &config, const QStringList &args,
- AndroidSdkManager &sdkManager, SdkCmdFutureInterface &fi,
+ AndroidSdkManager &sdkManager, SdkCmdPromise &promise,
AndroidSdkManager::OperationOutput &output, double progressQuota,
bool interruptible = true, int timeout = sdkManagerOperationTimeoutS)
{
@@ -120,19 +120,19 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
qCDebug(sdkManagerLog).noquote() << "Running SDK Manager command (async):"
<< CommandLine(config.sdkManagerToolPath(), newArgs)
.toUserOutput();
- int offset = fi.progressValue();
- QtcProcess proc;
+ int offset = promise.future().progressValue();
+ Process proc;
proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config));
bool assertionFound = false;
proc.setTimeoutS(timeout);
- proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &fi](const QString &out) {
+ proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &promise](const QString &out) {
int progressPercent = parseProgress(out, assertionFound);
if (assertionFound) {
proc.stop();
proc.waitForFinished();
}
if (progressPercent != -1)
- fi.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota));
+ promise.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota));
});
proc.setStdErrCallback([&output](const QString &err) {
output.stdError = err;
@@ -168,12 +168,12 @@ public:
const AndroidSdkPackageList &allPackages(bool forceUpdate = false);
void refreshSdkPackages(bool forceReload = false);
- void parseCommonArguments(QFutureInterface<QString> &fi);
- void updateInstalled(SdkCmdFutureInterface &fi);
- void update(SdkCmdFutureInterface &fi, const QStringList &install,
+ void parseCommonArguments(QPromise<QString> &promise);
+ void updateInstalled(SdkCmdPromise &fi);
+ void update(SdkCmdPromise &fi, const QStringList &install,
const QStringList &uninstall);
- void checkPendingLicense(SdkCmdFutureInterface &fi);
- void getPendingLicense(SdkCmdFutureInterface &fi);
+ void checkPendingLicense(SdkCmdPromise &fi);
+ void getPendingLicense(SdkCmdPromise &fi);
void addWatcher(const QFuture<AndroidSdkManager::OperationOutput> &future);
void setLicenseInput(bool acceptLicense);
@@ -186,7 +186,7 @@ private:
void reloadSdkPackages();
void clearPackages();
bool onLicenseStdOut(const QString &output, bool notify,
- AndroidSdkManager::OperationOutput &result, SdkCmdFutureInterface &fi);
+ AndroidSdkManager::OperationOutput &result, SdkCmdPromise &fi);
AndroidSdkManager &m_sdkManager;
const AndroidConfig &m_config;
@@ -308,7 +308,7 @@ bool AndroidSdkManager::packageListingSuccessful() const
QFuture<QString> AndroidSdkManager::availableArguments() const
{
- return Utils::runAsync(&AndroidSdkManagerPrivate::parseCommonArguments, m_d.get());
+ return Utils::asyncRun(&AndroidSdkManagerPrivate::parseCommonArguments, m_d.get());
}
QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::updateAll()
@@ -316,7 +316,7 @@ QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::updateAll()
if (isBusy()) {
return QFuture<AndroidSdkManager::OperationOutput>();
}
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::updateInstalled, m_d.get());
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::updateInstalled, m_d.get());
m_d->addWatcher(future);
return future;
}
@@ -326,7 +326,7 @@ AndroidSdkManager::update(const QStringList &install, const QStringList &uninsta
{
if (isBusy())
return QFuture<AndroidSdkManager::OperationOutput>();
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::update, m_d.get(), install, uninstall);
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::update, m_d.get(), install, uninstall);
m_d->addWatcher(future);
return future;
}
@@ -335,7 +335,7 @@ QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::checkPendingLicen
{
if (isBusy())
return QFuture<AndroidSdkManager::OperationOutput>();
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::checkPendingLicense, m_d.get());
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::checkPendingLicense, m_d.get());
m_d->addWatcher(future);
return future;
}
@@ -344,7 +344,7 @@ QFuture<AndroidSdkManager::OperationOutput> AndroidSdkManager::runLicenseCommand
{
if (isBusy())
return QFuture<AndroidSdkManager::OperationOutput>();
- auto future = Utils::runAsync(&AndroidSdkManagerPrivate::getPendingLicense, m_d.get());
+ auto future = Utils::asyncRun(&AndroidSdkManagerPrivate::getPendingLicense, m_d.get());
m_d->addWatcher(future);
return future;
}
@@ -422,29 +422,29 @@ void AndroidSdkManagerPrivate::refreshSdkPackages(bool forceReload)
reloadSdkPackages();
}
-void AndroidSdkManagerPrivate::updateInstalled(SdkCmdFutureInterface &fi)
+void AndroidSdkManagerPrivate::updateInstalled(SdkCmdPromise &promise)
{
- fi.setProgressRange(0, 100);
- fi.setProgressValue(0);
+ promise.setProgressRange(0, 100);
+ promise.setProgressValue(0);
AndroidSdkManager::OperationOutput result;
result.type = AndroidSdkManager::UpdateAll;
result.stdOutput = Tr::tr("Updating installed packages.");
- fi.reportResult(result);
+ promise.addResult(result);
QStringList args("--update");
args << m_config.sdkManagerToolArgs();
- if (!fi.isCanceled())
- sdkManagerCommand(m_config, args, m_sdkManager, fi, result, 100);
+ if (!promise.isCanceled())
+ sdkManagerCommand(m_config, args, m_sdkManager, promise, result, 100);
else
qCDebug(sdkManagerLog) << "Update: Operation cancelled before start";
if (result.stdError.isEmpty() && !result.success)
result.stdError = Tr::tr("Failed.");
result.stdOutput = Tr::tr("Done\n\n");
- fi.reportResult(result);
- fi.setProgressValue(100);
+ promise.addResult(result);
+ promise.setProgressValue(100);
}
-void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringList &install,
+void AndroidSdkManagerPrivate::update(SdkCmdPromise &fi, const QStringList &install,
const QStringList &uninstall)
{
fi.setProgressRange(0, 100);
@@ -461,7 +461,7 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi
result.type = AndroidSdkManager::UpdatePackage;
result.stdOutput = QString("%1 %2").arg(isInstall ? installTag : uninstallTag)
.arg(packagePath);
- fi.reportResult(result);
+ fi.addResult(result);
if (fi.isCanceled())
qCDebug(sdkManagerLog) << args << "Update: Operation cancelled before start";
else
@@ -471,7 +471,7 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi
if (result.stdError.isEmpty() && !result.success)
result.stdError = Tr::tr("AndroidSdkManager", "Failed");
result.stdOutput = Tr::tr("AndroidSdkManager", "Done\n\n");
- fi.reportResult(result);
+ fi.addResult(result);
return fi.isCanceled();
};
@@ -495,7 +495,7 @@ void AndroidSdkManagerPrivate::update(SdkCmdFutureInterface &fi, const QStringLi
fi.setProgressValue(100);
}
-void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdFutureInterface &fi)
+void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdPromise &fi)
{
fi.setProgressRange(0, 100);
fi.setProgressValue(0);
@@ -509,11 +509,11 @@ void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdFutureInterface &fi)
qCDebug(sdkManagerLog) << "Update: Operation cancelled before start";
}
- fi.reportResult(result);
+ fi.addResult(result);
fi.setProgressValue(100);
}
-void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
+void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdPromise &fi)
{
fi.setProgressRange(0, 100);
fi.setProgressValue(0);
@@ -521,7 +521,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
AndroidSdkManager::OperationOutput result;
result.type = AndroidSdkManager::LicenseWorkflow;
- QtcProcess licenseCommand;
+ Process licenseCommand;
licenseCommand.setProcessMode(ProcessMode::Writer);
licenseCommand.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config));
bool reviewingLicenses = false;
@@ -549,7 +549,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
} else if (assertionFound) {
// The first assertion is to start reviewing licenses. Always accept.
reviewingLicenses = true;
- QRegularExpression reg("(\\d+\\sof\\s)(?<steps>\\d+)");
+ static const QRegularExpression reg(R"((\d+\sof\s)(?<steps>\d+))");
QRegularExpressionMatch match = reg.match(stdOut);
if (match.hasMatch())
steps = match.captured("steps").toInt();
@@ -571,7 +571,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi)
result.success = licenseCommand.exitStatus() == QProcess::NormalExit;
if (!result.success)
result.stdError = Tr::tr("License command failed.\n\n");
- fi.reportResult(result);
+ fi.addResult(result);
fi.setProgressValue(100);
}
@@ -595,14 +595,14 @@ void AndroidSdkManagerPrivate::clearUserInput()
bool AndroidSdkManagerPrivate::onLicenseStdOut(const QString &output, bool notify,
AndroidSdkManager::OperationOutput &result,
- SdkCmdFutureInterface &fi)
+ SdkCmdPromise &fi)
{
m_licenseTextCache.append(output);
const QRegularExpressionMatch assertionMatch = assertionRegExp().match(m_licenseTextCache);
if (assertionMatch.hasMatch()) {
if (notify) {
result.stdOutput = m_licenseTextCache;
- fi.reportResult(result);
+ fi.addResult(result);
}
// Clear the current contents. The found license text is dispatched. Continue collecting the
// next license text.
@@ -620,7 +620,7 @@ void AndroidSdkManagerPrivate::addWatcher(const QFuture<AndroidSdkManager::Opera
m_activeOperation->setFuture(QFuture<void>(future));
}
-void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface<QString> &fi)
+void AndroidSdkManagerPrivate::parseCommonArguments(QPromise<QString> &promise)
{
QString argumentDetails;
QString output;
@@ -628,7 +628,7 @@ void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface<QString> &f
bool foundTag = false;
const auto lines = output.split('\n');
for (const QString& line : lines) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
break;
if (foundTag)
argumentDetails.append(line + "\n");
@@ -636,8 +636,8 @@ void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface<QString> &f
foundTag = true;
}
- if (!fi.isCanceled())
- fi.reportResult(argumentDetails);
+ if (!promise.isCanceled())
+ promise.addResult(argumentDetails);
}
void AndroidSdkManagerPrivate::clearPackages()
diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp
index c2b1ce6d6b..8627e0c64e 100644
--- a/src/plugins/android/androidsdkmanagerwidget.cpp
+++ b/src/plugins/android/androidsdkmanagerwidget.cpp
@@ -9,10 +9,10 @@
#include <app/app_version.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/utilsicons.h>
#include <QAbstractButton>
@@ -141,14 +141,16 @@ AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config,
}
},
optionsButton
- }
- }.attachTo(m_packagesStack, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(m_packagesStack);
Column {
m_outputEdit,
Row { m_sdkLicenseLabel, m_sdkLicenseButtonBox },
m_operationProgress,
- }.attachTo(m_outputStack, WithoutMargins);
+ noMargin
+ }.attachTo(m_outputStack);
Column {
m_viewStack,
@@ -644,7 +646,7 @@ OptionsDialog::OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &a
}
};
m_optionsFuture = sdkManager->availableArguments();
- Utils::onResultReady(m_optionsFuture, populateOptions);
+ Utils::onResultReady(m_optionsFuture, this, populateOptions);
auto dialogButtons = new QDialogButtonBox(this);
dialogButtons->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index ae2d28ae1a..dfb14c49cf 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -17,8 +17,8 @@
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/progressindicator.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <QCheckBox>
@@ -145,8 +145,6 @@ public:
~AndroidSettingsWidget() final;
private:
- void apply() final { AndroidConfigurations::setConfig(m_androidConfig); }
-
void showEvent(QShowEvent *event) override;
void validateJdk();
@@ -450,6 +448,8 @@ AndroidSettingsWidget::AndroidSettingsWidget()
delete openSslOneShot;
});
});
+
+ setOnApply([this] { AndroidConfigurations::setConfig(m_androidConfig); });
}
AndroidSettingsWidget::~AndroidSettingsWidget()
@@ -659,7 +659,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
openSslProgressDialog->setFixedSize(openSslProgressDialog->sizeHint());
const QString openSslRepo("https://github.com/KDAB/android_openssl.git");
- QtcProcess *gitCloner = new QtcProcess(this);
+ Process *gitCloner = new Process(this);
CommandLine gitCloneCommand("git", {"clone", "--depth=1", openSslRepo, openSslPath.toString()});
gitCloner->setCommand(gitCloneCommand);
@@ -684,7 +684,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
openButton->deleteLater();
};
- connect(gitCloner, &QtcProcess::done, this, [=] {
+ connect(gitCloner, &Process::done, this, [=] {
openSslProgressDialog->close();
if (gitCloner->error() != QProcess::UnknownError) {
if (gitCloner->error() == QProcess::FailedToStart) {
@@ -718,7 +718,7 @@ void AndroidSettingsWidget::updateUI()
const bool openSslOk = m_openSslSummary->allRowsOk();
const QListWidgetItem *currentItem = m_ndkListWidget->currentItem();
- const FilePath currentNdk = FilePath::fromString(currentItem ? currentItem->text() : "");
+ const FilePath currentNdk = FilePath::fromUserInput(currentItem ? currentItem->text() : "");
const QString infoText = Tr::tr("(SDK Version: %1, NDK Version: %2)")
.arg(m_androidConfig.sdkToolsVersion().toString())
.arg(currentNdk.isEmpty() ? "" : m_androidConfig.ndkVersion(currentNdk).toString());
diff --git a/src/plugins/android/androidsignaloperation.cpp b/src/plugins/android/androidsignaloperation.cpp
index 426b63c144..b6a7699dd4 100644
--- a/src/plugins/android/androidsignaloperation.cpp
+++ b/src/plugins/android/androidsignaloperation.cpp
@@ -4,8 +4,8 @@
#include "androidconfigurations.h"
#include "androidsignaloperation.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace Utils;
@@ -90,8 +90,8 @@ void AndroidSignalOperation::startAdbProcess(State state, const Utils::CommandLi
{
m_state = state;
m_timeout->start();
- m_adbProcess.reset(new QtcProcess);
- connect(m_adbProcess.get(), &QtcProcess::done, this, handler);
+ m_adbProcess.reset(new Process);
+ connect(m_adbProcess.get(), &Process::done, this, handler);
m_adbProcess->setCommand(commandLine);
m_adbProcess->start();
}
diff --git a/src/plugins/android/androidsignaloperation.h b/src/plugins/android/androidsignaloperation.h
index 06a88b6705..1c3bb48454 100644
--- a/src/plugins/android/androidsignaloperation.h
+++ b/src/plugins/android/androidsignaloperation.h
@@ -42,7 +42,7 @@ private:
void startAdbProcess(State state, const Utils::CommandLine &commandLine, FinishHandler handler);
Utils::FilePath m_adbPath;
- std::unique_ptr<Utils::QtcProcess> m_adbProcess;
+ std::unique_ptr<Utils::Process> m_adbProcess;
QTimer *m_timeout;
State m_state = Idle;
diff --git a/src/plugins/autotest/autotest.qbs b/src/plugins/autotest/autotest.qbs
index f0bccd79c3..90f1728c4a 100644
--- a/src/plugins/autotest/autotest.qbs
+++ b/src/plugins/autotest/autotest.qbs
@@ -125,9 +125,7 @@ QtcPlugin {
]
}
- Group {
- name: "Test sources"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"autotestunittests.cpp",
"autotestunittests.h",
diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp
index 98b804b16d..d6bfc6f9a8 100644
--- a/src/plugins/autotest/autotestplugin.cpp
+++ b/src/plugins/autotest/autotestplugin.cpp
@@ -32,22 +32,28 @@
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+
#include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
+
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
+
#include <extensionsystem/pluginmanager.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectpanelfactory.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
+
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
+
#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <utils/utilsicons.h>
@@ -90,7 +96,7 @@ public:
void onRunUnderCursorTriggered(TestRunMode mode);
TestSettings m_settings;
- TestSettingsPage m_testSettingPage{&m_settings};
+ TestSettingsPage m_testSettingPage;
TestCodeParser m_testCodeParser;
TestTreeModel m_testTreeModel{&m_testCodeParser};
@@ -148,11 +154,11 @@ AutotestPluginPrivate::AutotestPluginPrivate()
m_testTreeModel.synchronizeTestFrameworks();
m_testTreeModel.synchronizeTestTools();
- auto sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
+ auto sessionManager = ProjectExplorer::ProjectManager::instance();
+ connect(sessionManager, &ProjectExplorer::ProjectManager::startupProjectChanged,
this, [this] { m_runconfigCache.clear(); });
- connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
+ connect(sessionManager, &ProjectExplorer::ProjectManager::aboutToRemoveProject,
this, [](ProjectExplorer::Project *project) {
const auto it = s_projectSettings.constFind(project);
if (it != s_projectSettings.constEnd()) {
@@ -172,11 +178,6 @@ AutotestPluginPrivate::~AutotestPluginPrivate()
delete m_resultsPane;
}
-TestSettings *AutotestPlugin::settings()
-{
- return &dd->m_settings;
-}
-
TestProjectSettings *AutotestPlugin::projectSettings(ProjectExplorer::Project *project)
{
auto &settings = s_projectSettings[project];
@@ -471,7 +472,7 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode)
TestFrameworks AutotestPlugin::activeTestFrameworks()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
TestFrameworks sorted;
if (!project || projectSettings(project)->useGlobalSettings()) {
sorted = Utils::filtered(TestFrameworkManager::registeredFrameworks(),
@@ -489,7 +490,7 @@ TestFrameworks AutotestPlugin::activeTestFrameworks()
void AutotestPlugin::updateMenuItemsEnabledState()
{
- const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
const ProjectExplorer::Target *target = project ? project->activeTarget() : nullptr;
const bool canScan = !dd->m_testRunner.isTestRunning()
&& dd->m_testCodeParser.state() == TestCodeParser::Idle;
diff --git a/src/plugins/autotest/autotestplugin.h b/src/plugins/autotest/autotestplugin.h
index 8705a8a538..e6daa0c739 100644
--- a/src/plugins/autotest/autotestplugin.h
+++ b/src/plugins/autotest/autotestplugin.h
@@ -18,7 +18,6 @@ namespace Autotest {
namespace Internal {
class TestProjectSettings;
-struct TestSettings;
struct ChoicePair
{
@@ -43,7 +42,6 @@ public:
void extensionsInitialized() override;
ShutdownFlag aboutToShutdown() override;
- static TestSettings *settings();
static TestProjectSettings *projectSettings(ProjectExplorer::Project *project);
static TestFrameworks activeTestFrameworks();
static void updateMenuItemsEnabledState();
diff --git a/src/plugins/autotest/autotestunittests.cpp b/src/plugins/autotest/autotestunittests.cpp
index e72cc0cbbb..50d0b496c2 100644
--- a/src/plugins/autotest/autotestunittests.cpp
+++ b/src/plugins/autotest/autotestunittests.cpp
@@ -99,8 +99,8 @@ void AutoTestUnitTests::testCodeParser()
CppEditor::Tests::ProjectOpenerAndCloser projectManager;
QVERIFY(projectManager.open(projectFilePath, true, m_kit));
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
@@ -149,8 +149,8 @@ void AutoTestUnitTests::testCodeParserSwitchStartup()
qDebug() << "Opening project" << projectFilePaths.at(i);
QVERIFY(projectManager.open(projectFilePaths.at(i), true, m_kit));
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
@@ -199,8 +199,8 @@ void AutoTestUnitTests::testCodeParserGTest()
CppEditor::Tests::ProjectOpenerAndCloser projectManager;
QVERIFY(projectManager.open(projectFilePath, true, m_kit));
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
@@ -250,8 +250,8 @@ void AutoTestUnitTests::testCodeParserBoostTest()
= projectManager.open(projectFilePath, true, m_kit);
QVERIFY(projectInfo);
- QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
- QSignalSpy modelUpdateSpy(m_model, SIGNAL(sweepingDone()));
+ QSignalSpy parserSpy(m_model->parser(), &TestCodeParser::parsingFinished);
+ QSignalSpy modelUpdateSpy(m_model, &TestTreeModel::sweepingDone);
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
diff --git a/src/plugins/autotest/boost/boosttestconfiguration.cpp b/src/plugins/autotest/boost/boosttestconfiguration.cpp
index 50d8a863f4..e581cff50e 100644
--- a/src/plugins/autotest/boost/boosttestconfiguration.cpp
+++ b/src/plugins/autotest/boost/boosttestconfiguration.cpp
@@ -6,23 +6,20 @@
#include "boosttestoutputreader.h"
#include "boosttestsettings.h"
-#include "../autotestplugin.h"
#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
-#include <utils/stringutils.h>
using namespace Utils;
namespace Autotest {
namespace Internal {
-TestOutputReader *BoostTestConfiguration::createOutputReader(
- const QFutureInterface<TestResult> &fi, QtcProcess *app) const
+TestOutputReader *BoostTestConfiguration::createOutputReader(Process *app) const
{
auto settings = static_cast<BoostTestSettings *>(framework()->testSettings());
- return new BoostTestOutputReader(fi, app, buildDirectory(), projectFile(),
+ return new BoostTestOutputReader(app, buildDirectory(), projectFile(),
LogLevel(settings->logLevel.value()),
ReportLevel(settings->reportLevel.value()));
}
@@ -108,7 +105,7 @@ QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted)
for (const QString &test : testCases())
arguments << "-t" << test;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments << filterInterfering(runnable().command.arguments().split(
' ', Qt::SkipEmptyParts), omitted);
}
diff --git a/src/plugins/autotest/boost/boosttestconfiguration.h b/src/plugins/autotest/boost/boosttestconfiguration.h
index 8afb74049f..10e50004bc 100644
--- a/src/plugins/autotest/boost/boosttestconfiguration.h
+++ b/src/plugins/autotest/boost/boosttestconfiguration.h
@@ -13,8 +13,7 @@ class BoostTestConfiguration : public DebuggableTestConfiguration
public:
explicit BoostTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/boost/boosttestframework.h b/src/plugins/autotest/boost/boosttestframework.h
index e8f4c98ed8..da015fbd6f 100644
--- a/src/plugins/autotest/boost/boosttestframework.h
+++ b/src/plugins/autotest/boost/boosttestframework.h
@@ -7,8 +7,7 @@
#include "boosttestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class BoostTestFramework : public ITestFramework
{
@@ -23,9 +22,7 @@ private:
ITestParser *createTestParser() override;
ITestTreeItem *createRootNode() override;
- BoostTestSettings m_settings;
- BoostTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ BoostTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp
index 5f549cca32..96b0dffd61 100644
--- a/src/plugins/autotest/boost/boosttestoutputreader.cpp
+++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp
@@ -5,11 +5,12 @@
#include "boosttestsettings.h"
#include "boosttestresult.h"
+
#include "../autotesttr.h"
#include "../testtreeitem.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QLoggingCategory>
#include <QRegularExpression>
@@ -21,12 +22,11 @@ namespace Internal {
static Q_LOGGING_CATEGORY(orLog, "qtc.autotest.boost.outputreader", QtWarningMsg)
-BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+BoostTestOutputReader::BoostTestOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile,
LogLevel log, ReportLevel report)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
, m_logLevel(log)
, m_reportLevel(report)
@@ -34,7 +34,7 @@ BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResult>
if (!testApplication)
return;
- connect(testApplication, &QtcProcess::done, this, [this, testApplication] {
+ connect(testApplication, &Process::done, this, [this, testApplication] {
onDone(testApplication->exitCode());
});
}
diff --git a/src/plugins/autotest/boost/boosttestoutputreader.h b/src/plugins/autotest/boost/boosttestoutputreader.h
index 8449a0708a..648b55547f 100644
--- a/src/plugins/autotest/boost/boosttestoutputreader.h
+++ b/src/plugins/autotest/boost/boosttestoutputreader.h
@@ -15,8 +15,7 @@ class BoostTestOutputReader : public TestOutputReader
{
Q_OBJECT
public:
- BoostTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ BoostTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, LogLevel log, ReportLevel report);
protected:
void processOutputLine(const QByteArray &outputLine) override;
diff --git a/src/plugins/autotest/boost/boosttestparser.cpp b/src/plugins/autotest/boost/boosttestparser.cpp
index bbf6dfd56c..8c701942ad 100644
--- a/src/plugins/autotest/boost/boosttestparser.cpp
+++ b/src/plugins/autotest/boost/boosttestparser.cpp
@@ -9,6 +9,7 @@
#include <cppeditor/cppmodelmanager.h>
#include <QMap>
+#include <QPromise>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
@@ -96,7 +97,7 @@ static BoostTestParseResult *createParseResult(const QString &name, const FilePa
}
-bool BoostTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool BoostTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -148,7 +149,7 @@ bool BoostTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
locationAndType.m_type,
tmpInfo);
currentSuite->children.append(funcResult);
- futureInterface.reportResult(TestParseResultPtr(topLevelSuite));
+ promise.addResult(TestParseResultPtr(topLevelSuite));
}
}
return true;
diff --git a/src/plugins/autotest/boost/boosttestparser.h b/src/plugins/autotest/boost/boosttestparser.h
index 049f42d0c9..3ea67815c9 100644
--- a/src/plugins/autotest/boost/boosttestparser.h
+++ b/src/plugins/autotest/boost/boosttestparser.h
@@ -22,7 +22,7 @@ class BoostTestParser : public CppParser
{
public:
explicit BoostTestParser(ITestFramework *framework) : CppParser(framework) {}
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
};
diff --git a/src/plugins/autotest/boost/boosttestsettings.cpp b/src/plugins/autotest/boost/boosttestsettings.cpp
index c995cab9b6..840647572a 100644
--- a/src/plugins/autotest/boost/boosttestsettings.cpp
+++ b/src/plugins/autotest/boost/boosttestsettings.cpp
@@ -10,15 +10,28 @@
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-BoostTestSettings::BoostTestSettings()
+BoostTestSettings::BoostTestSettings(Id settingsId)
{
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
setSettingsGroups("Autotest", "BoostTest");
- setAutoApply(false);
+
+ setLayouter([this](QWidget *widget) {
+ Row { Form {
+ logLevel, br,
+ reportLevel, br,
+ randomize, Row { seed }, br,
+ systemErrors, br,
+ fpExceptions, br,
+ memLeaks,
+ }, st}.attachTo(widget);
+ });
registerAspect(&logLevel);
logLevel.setSettingsKey("LogLevel");
@@ -52,7 +65,7 @@ BoostTestSettings::BoostTestSettings()
seed.setEnabled(false);
seed.setLabelText(Tr::tr("Seed:"));
seed.setToolTip(Tr::tr("A seed of 0 means no randomization. A value of 1 uses the current "
- "time, any other value is used as random seed generator."));
+ "time, any other value is used as random seed generator."));
seed.setEnabler(&randomize);
registerAspect(&randomize);
@@ -81,30 +94,6 @@ BoostTestSettings::BoostTestSettings()
memLeaks.setToolTip(Tr::tr("Enable memory leak detection."));
}
-BoostTestSettingsPage::BoostTestSettingsPage(BoostTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- BoostTestSettings &s = *settings;
- using namespace Layouting;
-
- Grid grid {
- s.logLevel, br,
- s.reportLevel, br,
- s.randomize, Row { s.seed }, br,
- s.systemErrors, br,
- s.fpExceptions, br,
- s.memLeaks,
- };
-
- Column { Row { Column { grid, st }, st } }.attachTo(widget);
- });
-}
-
QString BoostTestSettings::logLevelToOption(const LogLevel logLevel)
{
switch (logLevel) {
@@ -134,5 +123,4 @@ QString BoostTestSettings::reportLevelToOption(const ReportLevel reportLevel)
return {};
}
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/boost/boosttestsettings.h b/src/plugins/autotest/boost/boosttestsettings.h
index 60a3df5042..f2a906cd79 100644
--- a/src/plugins/autotest/boost/boosttestsettings.h
+++ b/src/plugins/autotest/boost/boosttestsettings.h
@@ -5,10 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
enum class LogLevel
{
@@ -33,10 +30,10 @@ enum class ReportLevel
No
};
-class BoostTestSettings : public Utils::AspectContainer
+class BoostTestSettings : public Core::PagedSettings
{
public:
- BoostTestSettings();
+ explicit BoostTestSettings(Utils::Id settingsId);
static QString logLevelToOption(const LogLevel logLevel);
static QString reportLevelToOption(const ReportLevel reportLevel);
@@ -50,15 +47,7 @@ public:
Utils::BoolAspect memLeaks;
};
-
-class BoostTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- BoostTestSettingsPage(BoostTestSettings *settings, Utils::Id settingsId);
-};
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
Q_DECLARE_METATYPE(Autotest::Internal::LogLevel)
Q_DECLARE_METATYPE(Autotest::Internal::ReportLevel)
diff --git a/src/plugins/autotest/boost/boosttesttreeitem.cpp b/src/plugins/autotest/boost/boosttesttreeitem.cpp
index 4b53d92754..957ff73021 100644
--- a/src/plugins/autotest/boost/boosttesttreeitem.cpp
+++ b/src/plugins/autotest/boost/boosttesttreeitem.cpp
@@ -11,7 +11,9 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
#include <QRegularExpression>
@@ -156,7 +158,7 @@ static QString handleSpecialFunctionNames(const QString &name)
QList<ITestConfiguration *> BoostTestTreeItem::getAllTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -200,7 +202,7 @@ QList<ITestConfiguration *> BoostTestTreeItem::getTestConfigurations(
std::function<bool(BoostTestTreeItem *)> predicate) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -261,7 +263,7 @@ QList<ITestConfiguration *> BoostTestTreeItem::getFailedTestConfigurations() con
ITestConfiguration *BoostTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
diff --git a/src/plugins/autotest/catch/catchconfiguration.cpp b/src/plugins/autotest/catch/catchconfiguration.cpp
index c08f7ac82b..a503639868 100644
--- a/src/plugins/autotest/catch/catchconfiguration.cpp
+++ b/src/plugins/autotest/catch/catchconfiguration.cpp
@@ -2,24 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "catchconfiguration.h"
+
#include "catchoutputreader.h"
#include "catchtestsettings.h"
-#include "../autotestplugin.h"
#include "../itestframework.h"
#include "../testsettings.h"
-#include <utils/stringutils.h>
-
using namespace Utils;
namespace Autotest {
namespace Internal {
-TestOutputReader *CatchConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- QtcProcess *app) const
+TestOutputReader *CatchConfiguration::createOutputReader(Process *app) const
{
- return new CatchOutputReader(fi, app, buildDirectory(), projectFile());
+ return new CatchOutputReader(app, buildDirectory(), projectFile());
}
static QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
@@ -82,7 +79,7 @@ QStringList CatchConfiguration::argumentsForTestRunner(QStringList *omitted) con
arguments << "\"" + testCases().join("\", \"") + "\"";
arguments << "--reporter" << "xml";
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments << filterInterfering(runnable().command.arguments().split(
' ', Qt::SkipEmptyParts), omitted);
}
diff --git a/src/plugins/autotest/catch/catchconfiguration.h b/src/plugins/autotest/catch/catchconfiguration.h
index bfa37f0164..63ac84dba7 100644
--- a/src/plugins/autotest/catch/catchconfiguration.h
+++ b/src/plugins/autotest/catch/catchconfiguration.h
@@ -12,8 +12,7 @@ class CatchConfiguration : public DebuggableTestConfiguration
{
public:
CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/catch/catchframework.h b/src/plugins/autotest/catch/catchframework.h
index 0fe7138ce9..6bd20b44f6 100644
--- a/src/plugins/autotest/catch/catchframework.h
+++ b/src/plugins/autotest/catch/catchframework.h
@@ -7,8 +7,7 @@
#include "catchtestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class CatchFramework : public ITestFramework
{
@@ -25,9 +24,7 @@ protected:
private:
ITestSettings * testSettings() override { return &m_settings; }
- CatchTestSettings m_settings;
- CatchTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ CatchTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/catch/catchoutputreader.cpp b/src/plugins/autotest/catch/catchoutputreader.cpp
index 95e437fdc4..7a3aa277d4 100644
--- a/src/plugins/autotest/catch/catchoutputreader.cpp
+++ b/src/plugins/autotest/catch/catchoutputreader.cpp
@@ -2,13 +2,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "catchoutputreader.h"
+
#include "catchresult.h"
#include "../autotesttr.h"
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-
using namespace Utils;
namespace Autotest {
@@ -31,11 +29,10 @@ namespace CatchXml {
const char TestCaseResultElement[] = "OverallResult";
}
-CatchOutputReader::CatchOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+CatchOutputReader::CatchOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile)
- : TestOutputReader (futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
{
}
diff --git a/src/plugins/autotest/catch/catchoutputreader.h b/src/plugins/autotest/catch/catchoutputreader.h
index 51e8c1e338..b46db333f8 100644
--- a/src/plugins/autotest/catch/catchoutputreader.h
+++ b/src/plugins/autotest/catch/catchoutputreader.h
@@ -14,8 +14,7 @@ namespace Internal {
class CatchOutputReader : public TestOutputReader
{
public:
- CatchOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ CatchOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:
diff --git a/src/plugins/autotest/catch/catchtestparser.cpp b/src/plugins/autotest/catch/catchtestparser.cpp
index dd01c01c90..3cc7a4540b 100644
--- a/src/plugins/autotest/catch/catchtestparser.cpp
+++ b/src/plugins/autotest/catch/catchtestparser.cpp
@@ -11,6 +11,7 @@
#include <cppeditor/projectpart.h>
#include <utils/qtcassert.h>
+#include <QPromise>
#include <QRegularExpression>
using namespace Utils;
@@ -91,7 +92,7 @@ static bool hasCatchNames(const CPlusPlus::Document::Ptr &document)
return false;
}
-bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool CatchTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -144,7 +145,7 @@ bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
parseResult->children.append(testCase);
}
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
return !foundTests.isEmpty();
}
diff --git a/src/plugins/autotest/catch/catchtestparser.h b/src/plugins/autotest/catch/catchtestparser.h
index 6159ff5813..8b72073204 100644
--- a/src/plugins/autotest/catch/catchtestparser.h
+++ b/src/plugins/autotest/catch/catchtestparser.h
@@ -23,7 +23,7 @@ class CatchTestParser : public CppParser
public:
CatchTestParser(ITestFramework *framework)
: CppParser(framework) {}
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
};
diff --git a/src/plugins/autotest/catch/catchtestsettings.cpp b/src/plugins/autotest/catch/catchtestsettings.cpp
index 6fd0cde16d..07dac310eb 100644
--- a/src/plugins/autotest/catch/catchtestsettings.cpp
+++ b/src/plugins/autotest/catch/catchtestsettings.cpp
@@ -10,15 +10,32 @@
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-CatchTestSettings::CatchTestSettings()
+CatchTestSettings::CatchTestSettings(Id settingsId)
{
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr("Catch Test"));
setSettingsGroups("Autotest", "Catch2");
- setAutoApply(false);
+
+ setLayouter([this](QWidget *widget) {
+ Row { Form {
+ showSuccess, br,
+ breakOnFailure, br,
+ noThrow, br,
+ visibleWhitespace, br,
+ abortAfterChecked, abortAfter, br,
+ samplesChecked, benchmarkSamples, br,
+ resamplesChecked, benchmarkResamples, br,
+ confidenceIntervalChecked, confidenceInterval, br,
+ warmupChecked, benchmarkWarmupTime, br,
+ noAnalysis
+ }, st }.attachTo(widget);
+ });
registerAspect(&abortAfter);
abortAfter.setSettingsKey("AbortAfter");
@@ -106,41 +123,6 @@ CatchTestSettings::CatchTestSettings()
warnOnEmpty.setSettingsKey("WarnEmpty");
warnOnEmpty.setLabelText(Tr::tr("Warn on empty tests"));
warnOnEmpty.setToolTip(Tr::tr("Warns if a test section does not check any assertion."));
-
- forEachAspect([](BaseAspect *aspect) {
- // FIXME: Make the positioning part of the LayoutBuilder later
- if (auto boolAspect = dynamic_cast<BoolAspect *>(aspect))
- boolAspect->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
- });
-}
-
-CatchTestSettingsPage::CatchTestSettingsPage(CatchTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr("Catch Test"));
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- CatchTestSettings &s = *settings;
- using namespace Layouting;
-
- Grid col {
- s.showSuccess, br,
- s.breakOnFailure, br,
- s.noThrow, br,
- s.visibleWhitespace, br,
- s.abortAfterChecked, s.abortAfter, br,
- s.samplesChecked, s.benchmarkSamples, br,
- s.resamplesChecked, s.benchmarkResamples, br,
- s.confidenceIntervalChecked, s.confidenceInterval, br,
- s.warmupChecked, s.benchmarkWarmupTime, br,
- s.noAnalysis
- };
-
- Column { Row { col, st }, st }.attachTo(widget);
- });
}
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/catch/catchtestsettings.h b/src/plugins/autotest/catch/catchtestsettings.h
index 9e1b639fd3..07ac558081 100644
--- a/src/plugins/autotest/catch/catchtestsettings.h
+++ b/src/plugins/autotest/catch/catchtestsettings.h
@@ -5,15 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace Autotest::Internal {
-namespace Autotest {
-namespace Internal {
-
-class CatchTestSettings : public Utils::AspectContainer
+class CatchTestSettings : public Core::PagedSettings
{
public:
- CatchTestSettings();
+ explicit CatchTestSettings(Utils::Id settingsId);
Utils::IntegerAspect abortAfter;
Utils::IntegerAspect benchmarkSamples;
@@ -33,11 +30,4 @@ public:
Utils::BoolAspect warnOnEmpty;
};
-class CatchTestSettingsPage : public Core::IOptionsPage
-{
-public:
- CatchTestSettingsPage(CatchTestSettings *settings, Utils::Id settingsId);
-};
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/catch/catchtreeitem.cpp b/src/plugins/autotest/catch/catchtreeitem.cpp
index fe69fece87..034c7ab1d0 100644
--- a/src/plugins/autotest/catch/catchtreeitem.cpp
+++ b/src/plugins/autotest/catch/catchtreeitem.cpp
@@ -10,8 +10,10 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
+
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
using namespace Utils;
@@ -30,7 +32,7 @@ static QString nonRootDisplayName(const CatchTreeItem *it)
{
if (it->type() != TestTreeItem::TestSuite)
return it->name();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return it->name();
TestTreeItem *parent = it->parentItem();
@@ -141,7 +143,7 @@ bool CatchTreeItem::canProvideDebugConfiguration() const
ITestConfiguration *CatchTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
@@ -244,7 +246,7 @@ QList<ITestConfiguration *> CatchTreeItem::getSelectedTestConfigurations() const
QList<ITestConfiguration *> CatchTreeItem::getFailedTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -271,7 +273,7 @@ QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Fi
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return result);
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -293,7 +295,7 @@ QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Fi
testConfig = new CatchConfiguration(framework());
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
- testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
+ testConfig->setProject(ProjectExplorer::ProjectManager::startupProject());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
result << testConfig;
}
@@ -314,7 +316,7 @@ QString CatchTreeItem::stateSuffix() const
QList<ITestConfiguration *> CatchTreeItem::getTestConfigurations(bool ignoreCheckState) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
diff --git a/src/plugins/autotest/ctest/ctestconfiguration.cpp b/src/plugins/autotest/ctest/ctestconfiguration.cpp
index 9208888033..a2e2edfe0a 100644
--- a/src/plugins/autotest/ctest/ctestconfiguration.cpp
+++ b/src/plugins/autotest/ctest/ctestconfiguration.cpp
@@ -13,10 +13,9 @@ CTestConfiguration::CTestConfiguration(ITestBase *testBase)
setDisplayName("CTest");
}
-TestOutputReader *CTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const
+TestOutputReader *CTestConfiguration::createOutputReader(Utils::Process *app) const
{
- return new CTestOutputReader(fi, app, workingDirectory());
+ return new CTestOutputReader(app, workingDirectory());
}
} // namespace Internal
diff --git a/src/plugins/autotest/ctest/ctestconfiguration.h b/src/plugins/autotest/ctest/ctestconfiguration.h
index db0efb1717..a298fec86d 100644
--- a/src/plugins/autotest/ctest/ctestconfiguration.h
+++ b/src/plugins/autotest/ctest/ctestconfiguration.h
@@ -13,8 +13,7 @@ class CTestConfiguration final : public Autotest::TestToolConfiguration
public:
explicit CTestConfiguration(ITestBase *testBase);
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const final;
+ TestOutputReader *createOutputReader(Utils::Process *app) const final;
};
} // namespace Internal
diff --git a/src/plugins/autotest/ctest/ctestoutputreader.cpp b/src/plugins/autotest/ctest/ctestoutputreader.cpp
index 70ec5f0659..10f48d9d46 100644
--- a/src/plugins/autotest/ctest/ctestoutputreader.cpp
+++ b/src/plugins/autotest/ctest/ctestoutputreader.cpp
@@ -5,13 +5,11 @@
#include "../autotesttr.h"
#include "../testframeworkmanager.h"
-#include "../testresult.h"
#include "../testtreeitem.h"
#include <cmakeprojectmanager/cmakeprojectconstants.h>
#include <utils/qtcassert.h>
-#include <utils/treemodel.h>
#include <QRegularExpression>
@@ -52,10 +50,9 @@ public:
{}
};
-CTestOutputReader::CTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+CTestOutputReader::CTestOutputReader(Process *testApplication,
const FilePath &buildDirectory)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
{
}
diff --git a/src/plugins/autotest/ctest/ctestoutputreader.h b/src/plugins/autotest/ctest/ctestoutputreader.h
index 8a6e1f124e..85785d44fc 100644
--- a/src/plugins/autotest/ctest/ctestoutputreader.h
+++ b/src/plugins/autotest/ctest/ctestoutputreader.h
@@ -5,7 +5,7 @@
#include "../testoutputreader.h"
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Autotest {
namespace Internal {
@@ -13,8 +13,7 @@ namespace Internal {
class CTestOutputReader final : public Autotest::TestOutputReader
{
public:
- CTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
+ CTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory);
protected:
void processOutputLine(const QByteArray &outputLineWithNewLine) final;
diff --git a/src/plugins/autotest/ctest/ctestsettings.cpp b/src/plugins/autotest/ctest/ctestsettings.cpp
index 5de02b8480..e200de98e8 100644
--- a/src/plugins/autotest/ctest/ctestsettings.cpp
+++ b/src/plugins/autotest/ctest/ctestsettings.cpp
@@ -8,13 +8,39 @@
#include <utils/layoutbuilder.h>
-namespace Autotest {
-namespace Internal {
+using namespace Layouting;
+using namespace Utils;
-CTestSettings::CTestSettings()
+namespace Autotest::Internal {
+
+CTestSettings::CTestSettings(Id settingsId)
{
setSettingsGroups("Autotest", "CTest");
- setAutoApply(false);
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr("CTest"));
+
+ setLayouter([this](QWidget *w) {
+ Row { Form {
+ outputOnFail, br,
+ scheduleRandom, br,
+ stopOnFailure, br,
+ outputMode, br,
+ Group {
+ title(Tr::tr("Repeat tests")),
+ repeat.groupChecker(),
+ Row { repetitionMode, repetitionCount},
+ }, br,
+ Group {
+ title(Tr::tr("Run in parallel")),
+ parallel.groupChecker(),
+ Column {
+ Row { jobs }, br,
+ Row { testLoad, threshold}
+ }
+ }
+ }, st }.attachTo(w);
+ });
registerAspect(&outputOnFail);
outputOnFail.setSettingsKey("OutputOnFail");
@@ -24,7 +50,7 @@ CTestSettings::CTestSettings()
registerAspect(&outputMode);
outputMode.setSettingsKey("OutputMode");
outputMode.setLabelText(Tr::tr("Output mode"));
- outputMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
+ outputMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
outputMode.addOption({Tr::tr("Default"), {}, 0});
outputMode.addOption({Tr::tr("Verbose"), {}, 1});
outputMode.addOption({Tr::tr("Very Verbose"), {}, 2});
@@ -32,7 +58,7 @@ CTestSettings::CTestSettings()
registerAspect(&repetitionMode);
repetitionMode.setSettingsKey("RepetitionMode");
repetitionMode.setLabelText(Tr::tr("Repetition mode"));
- repetitionMode.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
+ repetitionMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
repetitionMode.addOption({Tr::tr("Until Fail"), {}, 0});
repetitionMode.addOption({Tr::tr("Until Pass"), {}, 1});
repetitionMode.addOption({Tr::tr("After Timeout"), {}, 2});
@@ -69,7 +95,7 @@ CTestSettings::CTestSettings()
testLoad.setSettingsKey("TestLoad");
testLoad.setLabelText(Tr::tr("Test load"));
testLoad.setToolTip(Tr::tr("Try not to start tests when they may cause CPU load to pass a "
- "threshold."));
+ "threshold."));
registerAspect(&threshold);
threshold.setSettingsKey("Threshold");
@@ -115,39 +141,4 @@ QStringList CTestSettings::activeSettingsAsOptions() const
return options;
}
-CTestSettingsPage::CTestSettingsPage(CTestSettings *settings, Utils::Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr("CTest"));
-
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- CTestSettings &s = *settings;
- using namespace Utils::Layouting;
-
- Form form {
- Row {s.outputOnFail}, br,
- Row {s.scheduleRandom}, br,
- Row {s.stopOnFailure}, br,
- Row {s.outputMode}, br,
- Group {
- title(Tr::tr("Repeat tests"), &s.repeat),
- Row {s.repetitionMode, s.repetitionCount},
- }, br,
- Group {
- title(Tr::tr("Run in parallel"), &s.parallel),
- Column {
- Row {s.jobs}, br,
- Row {s.testLoad, s.threshold}
- }
- }
- };
-
- Column { Row { Column { form , st }, st } }.attachTo(widget);
- });
-}
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/ctest/ctestsettings.h b/src/plugins/autotest/ctest/ctestsettings.h
index f9e87f5a8e..a4e9b19605 100644
--- a/src/plugins/autotest/ctest/ctestsettings.h
+++ b/src/plugins/autotest/ctest/ctestsettings.h
@@ -5,15 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace Autotest::Internal {
-namespace Autotest {
-namespace Internal {
-
-class CTestSettings : public Utils::AspectContainer
+class CTestSettings : public Core::PagedSettings
{
public:
- CTestSettings();
+ explicit CTestSettings(Utils::Id settingsId);
QStringList activeSettingsAsOptions() const;
@@ -31,12 +28,5 @@ public:
Utils::IntegerAspect threshold;
};
-class CTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- CTestSettingsPage(CTestSettings *settings, Utils::Id settingsId);
-};
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/ctest/ctesttool.h b/src/plugins/autotest/ctest/ctesttool.h
index ad510ac947..e7a7f74218 100644
--- a/src/plugins/autotest/ctest/ctesttool.h
+++ b/src/plugins/autotest/ctest/ctesttool.h
@@ -6,8 +6,7 @@
#include "../itestframework.h"
#include "ctestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class CTestTool final : public Autotest::ITestTool
{
@@ -26,9 +25,7 @@ protected:
private:
ITestSettings *testSettings() override { return &m_settings; }
- CTestSettings m_settings;
- CTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ CTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/ctest/ctesttreeitem.cpp b/src/plugins/autotest/ctest/ctesttreeitem.cpp
index 3e59c2db81..9ffc06b028 100644
--- a/src/plugins/autotest/ctest/ctesttreeitem.cpp
+++ b/src/plugins/autotest/ctest/ctesttreeitem.cpp
@@ -14,11 +14,11 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/environmentaspect.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/link.h>
-#include <utils/qtcassert.h>
+#include <utils/qtcassert.h>
using namespace Utils;
@@ -79,7 +79,7 @@ QVariant CTestTreeItem::data(int column, int role) const
QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringList &selected) const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
@@ -88,7 +88,7 @@ QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringLi
return {};
const ProjectExplorer::BuildSystem *buildSystem = target->buildSystem();
- QStringList options{"--timeout", QString::number(AutotestPlugin::settings()->timeout / 1000)};
+ QStringList options{"--timeout", QString::number(TestSettings::instance()->timeout() / 1000)};
auto ctestSettings = static_cast<CTestSettings *>(testBase()->testSettings());
options << ctestSettings->activeSettingsAsOptions();
CommandLine command = buildSystem->commandLineForTests(selected, options);
diff --git a/src/plugins/autotest/gtest/gtestconfiguration.cpp b/src/plugins/autotest/gtest/gtestconfiguration.cpp
index d2f869d764..8680da1fca 100644
--- a/src/plugins/autotest/gtest/gtestconfiguration.cpp
+++ b/src/plugins/autotest/gtest/gtestconfiguration.cpp
@@ -5,22 +5,20 @@
#include "gtestoutputreader.h"
#include "gtestsettings.h"
-#include "../autotestplugin.h"
+
#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
-#include <utils/stringutils.h>
using namespace Utils;
namespace Autotest {
namespace Internal {
-TestOutputReader *GTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- QtcProcess *app) const
+TestOutputReader *GTestConfiguration::createOutputReader(Process *app) const
{
- return new GTestOutputReader(fi, app, buildDirectory(), projectFile());
+ return new GTestOutputReader(app, buildDirectory(), projectFile());
}
QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
@@ -56,7 +54,7 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
{
QStringList arguments;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments << filterInterfering(runnable().command.arguments().split(
' ', Qt::SkipEmptyParts), omitted);
}
diff --git a/src/plugins/autotest/gtest/gtestconfiguration.h b/src/plugins/autotest/gtest/gtestconfiguration.h
index 97bcbd654f..0cbfc8b0d8 100644
--- a/src/plugins/autotest/gtest/gtestconfiguration.h
+++ b/src/plugins/autotest/gtest/gtestconfiguration.h
@@ -14,8 +14,7 @@ public:
explicit GTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/gtest/gtestframework.h b/src/plugins/autotest/gtest/gtestframework.h
index c9e8cbddc0..03c8a4fc0d 100644
--- a/src/plugins/autotest/gtest/gtestframework.h
+++ b/src/plugins/autotest/gtest/gtestframework.h
@@ -7,8 +7,7 @@
#include "gtestconstants.h"
#include "gtestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class GTestFramework : public ITestFramework
{
@@ -28,9 +27,7 @@ private:
ITestParser *createTestParser() override;
ITestTreeItem *createRootNode() override;
- GTestSettings m_settings;
- GTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ GTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp
index 7c6365d2f0..1e980c06d0 100644
--- a/src/plugins/autotest/gtest/gtestoutputreader.cpp
+++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp
@@ -8,7 +8,7 @@
#include "../autotesttr.h"
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QRegularExpression>
@@ -17,15 +17,14 @@ using namespace Utils;
namespace Autotest {
namespace Internal {
-GTestOutputReader::GTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+GTestOutputReader::GTestOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
{
if (testApplication) {
- connect(testApplication, &QtcProcess::done, this, [this, testApplication] {
+ connect(testApplication, &Process::done, this, [this, testApplication] {
const int exitCode = testApplication->exitCode();
if (exitCode == 1 && !m_description.isEmpty()) {
createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2")
@@ -116,7 +115,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
testResult.setResult(ResultType::MessageInternal);
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
reportResult(testResult);
- m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
+ // TODO: bump progress?
} else if (ExactMatch match = testSetFail.match(line)) {
m_testSetStarted = false;
TestResult testResult = createDefaultResult();
@@ -127,7 +126,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
testResult.setResult(ResultType::MessageInternal);
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
reportResult(testResult);
- m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
+ // TODO: bump progress?
} else if (ExactMatch match = testSetSkipped.match(line)) {
if (!m_testSetStarted) // ignore SKIPPED at summary
return;
diff --git a/src/plugins/autotest/gtest/gtestoutputreader.h b/src/plugins/autotest/gtest/gtestoutputreader.h
index e73ab0e823..a63c566829 100644
--- a/src/plugins/autotest/gtest/gtestoutputreader.h
+++ b/src/plugins/autotest/gtest/gtestoutputreader.h
@@ -11,8 +11,7 @@ namespace Internal {
class GTestOutputReader : public TestOutputReader
{
public:
- GTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ GTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:
void processOutputLine(const QByteArray &outputLine) override;
diff --git a/src/plugins/autotest/gtest/gtestparser.cpp b/src/plugins/autotest/gtest/gtestparser.cpp
index aa59bb94c2..c8cc92005e 100644
--- a/src/plugins/autotest/gtest/gtestparser.cpp
+++ b/src/plugins/autotest/gtest/gtestparser.cpp
@@ -10,6 +10,7 @@
#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/projectpart.h>
+#include <QPromise>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
@@ -70,7 +71,7 @@ static bool hasGTestNames(const CPlusPlus::Document::Ptr &document)
return false;
}
-bool GTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool GTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -124,7 +125,7 @@ bool GTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureIn
parseResult->children.append(testSet);
}
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
}
return !result.isEmpty();
}
diff --git a/src/plugins/autotest/gtest/gtestparser.h b/src/plugins/autotest/gtest/gtestparser.h
index 665a8a496c..52febe6b89 100644
--- a/src/plugins/autotest/gtest/gtestparser.h
+++ b/src/plugins/autotest/gtest/gtestparser.h
@@ -22,7 +22,7 @@ class GTestParser : public CppParser
{
public:
explicit GTestParser(ITestFramework *framework) : CppParser(framework) {}
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &futureInterface,
const Utils::FilePath &fileName) override;
};
diff --git a/src/plugins/autotest/gtest/gtestsettings.cpp b/src/plugins/autotest/gtest/gtestsettings.cpp
index babea83f78..d7302d6bd5 100644
--- a/src/plugins/autotest/gtest/gtestsettings.cpp
+++ b/src/plugins/autotest/gtest/gtestsettings.cpp
@@ -11,15 +11,28 @@
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-GTestSettings::GTestSettings()
+GTestSettings::GTestSettings(Id settingsId)
{
setSettingsGroups("Autotest", "GTest");
- setAutoApply(false);
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
+
+ setLayouter([this](QWidget *widget) {
+ Row { Form {
+ runDisabled, br,
+ breakOnFailure, br,
+ repeat, iterations, br,
+ shuffle, seed, br,
+ groupMode, br,
+ gtestFilter, br
+ }, st }.attachTo(widget);
+ });
registerAspect(&iterations);
iterations.setSettingsKey("Iterations");
@@ -49,7 +62,8 @@ GTestSettings::GTestSettings()
registerAspect(&repeat);
repeat.setSettingsKey("Repeat");
repeat.setLabelText(Tr::tr("Repeat tests"));
- repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to avoid canceling the tests)."));
+ repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to "
+ "avoid canceling the tests)."));
registerAspect(&throwOnFailure);
throwOnFailure.setSettingsKey("ThrowOnFailure");
@@ -93,8 +107,8 @@ GTestSettings::GTestSettings()
});
gtestFilter.setEnabled(false);
gtestFilter.setLabelText(Tr::tr("Active filter:"));
- gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\n"
- "See Google Test documentation for further information on GTest filters."));
+ gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\nSee Google Test "
+ "documentation for further information on GTest filters."));
gtestFilter.setValidationFunction([](FancyLineEdit *edit, QString * /*error*/) {
return edit && GTestUtils::isValidGTestFilter(edit->text());
@@ -104,39 +118,11 @@ GTestSettings::GTestSettings()
&gtestFilter, [this](int val) {
gtestFilter.setEnabled(groupMode.itemValueForIndex(val) == GTest::Constants::GTestFilter);
});
-}
-
-GTestSettingsPage::GTestSettingsPage(GTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
- setSettings(settings);
- QObject::connect(settings, &AspectContainer::applied, this, [] {
+ QObject::connect(this, &AspectContainer::applied, this, [] {
Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix(GTest::Constants::FRAMEWORK_NAME);
TestTreeModel::instance()->rebuild({id});
});
-
- setLayouter([settings](QWidget *widget) {
- GTestSettings &s = *settings;
- using namespace Layouting;
-
- Grid grid {
- s.runDisabled, br,
- s.breakOnFailure, br,
- s.repeat, s.iterations, br,
- s.shuffle, s.seed
- };
-
- Form form {
- s.groupMode,
- s.gtestFilter
- };
-
- Column { Row { Column { grid, form, st }, st } }.attachTo(widget);
- });
}
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/gtest/gtestsettings.h b/src/plugins/autotest/gtest/gtestsettings.h
index 11fb144e22..fe92837607 100644
--- a/src/plugins/autotest/gtest/gtestsettings.h
+++ b/src/plugins/autotest/gtest/gtestsettings.h
@@ -5,15 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace Autotest::Internal {
-namespace Autotest {
-namespace Internal {
-
-class GTestSettings : public Utils::AspectContainer
+class GTestSettings : public Core::PagedSettings
{
public:
- GTestSettings();
+ explicit GTestSettings(Utils::Id settingsId);
Utils::IntegerAspect iterations;
Utils::IntegerAspect seed;
@@ -26,11 +23,4 @@ public:
Utils::StringAspect gtestFilter;
};
-class GTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- GTestSettingsPage(GTestSettings *settings, Utils::Id settingsId);
-};
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp
index c79ae93d1b..f91b68f0e3 100644
--- a/src/plugins/autotest/gtest/gtesttreeitem.cpp
+++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp
@@ -10,7 +10,9 @@
#include "../autotesttr.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/icon.h>
@@ -146,7 +148,7 @@ QVariant GTestTreeItem::data(int column, int role) const
ITestConfiguration *GTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
GTestConfiguration *config = nullptr;
@@ -252,7 +254,7 @@ static void collectFailedTestInfo(const GTestTreeItem *item,
QList<ITestConfiguration *> GTestTreeItem::getTestConfigurations(bool ignoreCheckState) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -291,7 +293,7 @@ QList<ITestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const
QList<ITestConfiguration *> GTestTreeItem::getFailedTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -316,7 +318,7 @@ QList<ITestConfiguration *> GTestTreeItem::getFailedTestConfigurations() const
QList<ITestConfiguration *> GTestTreeItem::getTestConfigurationsForFile(const FilePath &fileName) const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -503,7 +505,7 @@ QSet<QString> internalTargets(const TestTreeItem &item)
{
QSet<QString> result;
const auto cppMM = CppEditor::CppModelManager::instance();
- const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
+ const auto projectInfo = cppMM->projectInfo(ProjectExplorer::ProjectManager::startupProject());
if (!projectInfo)
return {};
const FilePath filePath = item.filePath();
diff --git a/src/plugins/autotest/itestparser.cpp b/src/plugins/autotest/itestparser.cpp
index d8f1d6953b..79fde2a0bb 100644
--- a/src/plugins/autotest/itestparser.cpp
+++ b/src/plugins/autotest/itestparser.cpp
@@ -24,7 +24,7 @@ CppParser::CppParser(ITestFramework *framework)
{
}
-void CppParser::init(const FilePaths &filesToParse, bool fullParse)
+void CppParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{
Q_UNUSED(filesToParse)
Q_UNUSED(fullParse)
@@ -43,8 +43,8 @@ bool CppParser::selectedForBuilding(const FilePath &fileName)
QByteArray CppParser::getFileContent(const FilePath &filePath) const
{
QByteArray fileContent;
- if (m_workingCopy.contains(filePath)) {
- fileContent = m_workingCopy.source(filePath);
+ if (const auto source = m_workingCopy.source(filePath)) {
+ fileContent = *source;
} else {
QString error;
const QTextCodec *codec = Core::EditorManager::defaultTextCodec();
diff --git a/src/plugins/autotest/itestparser.h b/src/plugins/autotest/itestparser.h
index 7e16a61b90..93233bd7a9 100644
--- a/src/plugins/autotest/itestparser.h
+++ b/src/plugins/autotest/itestparser.h
@@ -9,9 +9,9 @@
#include <cppeditor/cppworkingcopy.h>
#include <qmljs/qmljsdocument.h>
-#include <QFutureInterface>
-
QT_BEGIN_NAMESPACE
+template <class T>
+class QPromise;
class QRegularExpression;
QT_END_NAMESPACE
@@ -45,8 +45,8 @@ class ITestParser
public:
explicit ITestParser(ITestFramework *framework) : m_framework(framework) {}
virtual ~ITestParser() { }
- virtual void init(const Utils::FilePaths &filesToParse, bool fullParse) = 0;
- virtual bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ virtual void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) = 0;
+ virtual bool processDocument(QPromise<TestParseResultPtr> &futureInterface,
const Utils::FilePath &fileName) = 0;
virtual QStringList supportedExtensions() const { return {}; }
@@ -63,7 +63,7 @@ class CppParser : public ITestParser
{
public:
explicit CppParser(ITestFramework *framework);
- void init(const Utils::FilePaths &filesToParse, bool fullParse) override;
+ void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) override;
static bool selectedForBuilding(const Utils::FilePath &fileName);
QByteArray getFileContent(const Utils::FilePath &filePath) const;
void release() override;
diff --git a/src/plugins/autotest/qtest/qttest_utils.cpp b/src/plugins/autotest/qtest/qttest_utils.cpp
index 1069280bee..9bedaa64c3 100644
--- a/src/plugins/autotest/qtest/qttest_utils.cpp
+++ b/src/plugins/autotest/qtest/qttest_utils.cpp
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qttest_utils.h"
+
#include "qttesttreeitem.h"
-#include "../autotestplugin.h"
-#include "../testframeworkmanager.h"
+#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
@@ -27,7 +27,8 @@ bool isQTestMacro(const QByteArray &macro)
return valid.contains(macro);
}
-QHash<FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework, const FilePaths &files)
+QHash<FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework,
+ const QSet<FilePath> &files)
{
QHash<FilePath, TestCases> result;
TestTreeItem *rootNode = framework->rootNode();
@@ -49,7 +50,8 @@ QHash<FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework, cons
return result;
}
-QMultiHash<FilePath, FilePath> alternativeFiles(ITestFramework *framework, const FilePaths &files)
+QMultiHash<FilePath, FilePath> alternativeFiles(ITestFramework *framework,
+ const QSet<FilePath> &files)
{
QMultiHash<FilePath, FilePath> result;
TestTreeItem *rootNode = framework->rootNode();
@@ -135,7 +137,7 @@ Environment prepareBasicEnvironment(const Environment &env)
result.set("QT_FORCE_STDERR_LOGGING", "1");
result.set("QT_LOGGING_TO_CONSOLE", "1");
}
- const int timeout = AutotestPlugin::settings()->timeout;
+ const int timeout = TestSettings::instance()->timeout();
if (timeout > 5 * 60 * 1000) // Qt5.5 introduced hard limit, Qt5.6.1 added env var to raise this
result.set("QTEST_FUNCTION_TIMEOUT", QString::number(timeout));
return result;
diff --git a/src/plugins/autotest/qtest/qttest_utils.h b/src/plugins/autotest/qtest/qttest_utils.h
index 6f5a587df3..8f07734541 100644
--- a/src/plugins/autotest/qtest/qttest_utils.h
+++ b/src/plugins/autotest/qtest/qttest_utils.h
@@ -26,9 +26,9 @@ namespace QTestUtils {
bool isQTestMacro(const QByteArray &macro);
QHash<Utils::FilePath, TestCases> testCaseNamesForFiles(ITestFramework *framework,
- const Utils::FilePaths &files);
+ const QSet<Utils::FilePath> &files);
QMultiHash<Utils::FilePath, Utils::FilePath> alternativeFiles(ITestFramework *framework,
- const Utils::FilePaths &files);
+ const QSet<Utils::FilePath> &files);
QStringList filterInterfering(const QStringList &provided, QStringList *omitted, bool isQuickTest);
Utils::Environment prepareBasicEnvironment(const Utils::Environment &env);
diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp
index 8a77f305e3..5dcf59a48e 100644
--- a/src/plugins/autotest/qtest/qttestconfiguration.cpp
+++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp
@@ -2,16 +2,15 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qttestconfiguration.h"
-#include "qttestconstants.h"
+
#include "qttestoutputreader.h"
#include "qttestsettings.h"
#include "qttest_utils.h"
-#include "../autotestplugin.h"
+
#include "../itestframework.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
-#include <utils/stringutils.h>
using namespace Utils;
@@ -28,20 +27,19 @@ static QStringList quoteIfNeeded(const QStringList &testCases, bool debugMode)
});
}
-TestOutputReader *QtTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
- QtcProcess *app) const
+TestOutputReader *QtTestConfiguration::createOutputReader(Process *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()
? QtTestOutputReader::XML
: QtTestOutputReader::PlainText;
- return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), mode, TestType::QtTest);
+ return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QtTest);
}
QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
{
QStringList arguments;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments.append(QTestUtils::filterInterfering(
runnable().command.arguments().split(' ', Qt::SkipEmptyParts),
omitted, false));
diff --git a/src/plugins/autotest/qtest/qttestconfiguration.h b/src/plugins/autotest/qtest/qttestconfiguration.h
index a99c93f0b7..d41697090e 100644
--- a/src/plugins/autotest/qtest/qttestconfiguration.h
+++ b/src/plugins/autotest/qtest/qttestconfiguration.h
@@ -13,8 +13,7 @@ class QtTestConfiguration : public DebuggableTestConfiguration
public:
explicit QtTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/qtest/qttestframework.h b/src/plugins/autotest/qtest/qttestframework.h
index 4fa765ab19..c90bd3c9d8 100644
--- a/src/plugins/autotest/qtest/qttestframework.h
+++ b/src/plugins/autotest/qtest/qttestframework.h
@@ -7,8 +7,7 @@
#include "qttestsettings.h"
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
class QtTestFramework : public ITestFramework
{
@@ -24,9 +23,7 @@ private:
ITestTreeItem *createRootNode() override;
ITestSettings *testSettings() override { return &m_settings; }
- QtTestSettings m_settings;
- QtTestSettingsPage m_settingsPage{&m_settings, settingsId()};
+ QtTestSettings m_settings{settingsId()};
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp
index 073ef0e9fe..70df35e663 100644
--- a/src/plugins/autotest/qtest/qttestoutputreader.cpp
+++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp
@@ -12,8 +12,6 @@
#include <QRegularExpression>
-#include <cctype>
-
using namespace Utils;
namespace Autotest {
@@ -99,18 +97,15 @@ static QString constructBenchmarkInformation(const QString &metric, double value
else if (metric == "CPUCycles") // -perf
metricsText = "CPU cycles";
return Tr::tr("%1 %2 per iteration (total: %3, iterations: %4)")
- .arg(formatResult(value))
- .arg(metricsText)
- .arg(formatResult(value * double(iterations)))
+ .arg(formatResult(value), metricsText, formatResult(value * double(iterations)))
.arg(iterations);
}
-QtTestOutputReader::QtTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication,
+QtTestOutputReader::QtTestOutputReader(Process *testApplication,
const FilePath &buildDirectory,
const FilePath &projectFile,
OutputMode mode, TestType type)
- : TestOutputReader(futureInterface, testApplication, buildDirectory)
+ : TestOutputReader(testApplication, buildDirectory)
, m_projectFile(projectFile)
, m_mode(mode)
, m_testType(type)
@@ -177,8 +172,6 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
m_xmlReader.addData("\n");
m_xmlReader.addData(QString::fromUtf8(outputLine));
while (!m_xmlReader.atEnd()) {
- if (m_futureInterface.isCanceled())
- return;
QXmlStreamReader::TokenType token = m_xmlReader.readNext();
switch (token) {
case QXmlStreamReader::StartDocument:
@@ -277,7 +270,7 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
const QStringView currentTag = m_xmlReader.name();
if (currentTag == QStringLiteral("TestFunction")) {
sendFinishMessage(true);
- m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
+ // TODO: bump progress?
m_dataTag.clear();
m_formerTestCase = m_testCase;
m_testCase.clear();
@@ -347,9 +340,6 @@ void QtTestOutputReader::processPlainTextOutput(const QByteArray &outputLine)
static const QRegularExpression locationUnix(QT_TEST_FAIL_UNIX_REGEXP);
static const QRegularExpression locationWin(QT_TEST_FAIL_WIN_REGEXP);
- if (m_futureInterface.isCanceled())
- return;
-
const QString line = QString::fromUtf8(outputLine);
QRegularExpressionMatch match;
diff --git a/src/plugins/autotest/qtest/qttestoutputreader.h b/src/plugins/autotest/qtest/qttestoutputreader.h
index 6db9aa8781..0962c8d684 100644
--- a/src/plugins/autotest/qtest/qttestoutputreader.h
+++ b/src/plugins/autotest/qtest/qttestoutputreader.h
@@ -3,9 +3,10 @@
#pragma once
-#include "qttestconstants.h"
#include "../testoutputreader.h"
+#include "qttestconstants.h"
+
#include <QXmlStreamReader>
namespace Autotest {
@@ -22,8 +23,7 @@ public:
PlainText
};
- QtTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
+ QtTestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, OutputMode mode, TestType type);
protected:
void processOutputLine(const QByteArray &outputLine) override;
diff --git a/src/plugins/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp
index 2b13133f03..f68a87d7bf 100644
--- a/src/plugins/autotest/qtest/qttestparser.cpp
+++ b/src/plugins/autotest/qtest/qttestparser.cpp
@@ -10,6 +10,7 @@
#include <cplusplus/TypeOfExpression.h>
#include <utils/algorithm.h>
+#include <QPromise>
#include <QRegularExpressionMatchIterator>
using namespace Utils;
@@ -292,7 +293,7 @@ static bool isQObject(const CPlusPlus::Document::Ptr &declaringDoc)
|| file.endsWith("QtCore/qobject.h") || file.endsWith("kernel/qobject.h");
}
-bool QtTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool QtTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
CPlusPlus::Document::Ptr doc = document(fileName);
@@ -325,7 +326,7 @@ bool QtTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureI
data.multipleTestCases = testCase.multipleTestCases;
QtTestParseResult *parseResult
= createParseResult(testCase.name, data, projectParts.first()->projectFile);
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
reported = true;
}
}
@@ -413,7 +414,7 @@ QtTestParseResult *QtTestParser::createParseResult(
return parseResult;
}
-void QtTestParser::init(const FilePaths &filesToParse, bool fullParse)
+void QtTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{
if (!fullParse) { // in a full parse cached information might lead to wrong results
m_testCases = QTestUtils::testCaseNamesForFiles(framework(), filesToParse);
diff --git a/src/plugins/autotest/qtest/qttestparser.h b/src/plugins/autotest/qtest/qttestparser.h
index db677929ec..abd8b2b0aa 100644
--- a/src/plugins/autotest/qtest/qttestparser.h
+++ b/src/plugins/autotest/qtest/qttestparser.h
@@ -34,9 +34,9 @@ class QtTestParser : public CppParser
public:
explicit QtTestParser(ITestFramework *framework) : CppParser(framework) {}
- void init(const Utils::FilePaths &filesToParse, bool fullParse) override;
+ void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) override;
void release() override;
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
private:
diff --git a/src/plugins/autotest/qtest/qttestsettings.cpp b/src/plugins/autotest/qtest/qttestsettings.cpp
index 4be93d9937..07fac99d98 100644
--- a/src/plugins/autotest/qtest/qttestsettings.cpp
+++ b/src/plugins/autotest/qtest/qttestsettings.cpp
@@ -10,15 +10,32 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
+using namespace Layouting;
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-QtTestSettings::QtTestSettings()
+QtTestSettings::QtTestSettings(Id settingsId)
{
setSettingsGroups("Autotest", "QtTest");
- setAutoApply(false);
+ setId(settingsId);
+ setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
+ setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
+
+ setLayouter([this](QWidget *widget) {
+ Row { Form {
+ noCrashHandler, br,
+ useXMLOutput, br,
+ verboseBench, br,
+ logSignalsSlots, br,
+ limitWarnings, maxWarnings, br,
+ Group {
+ title(Tr::tr("Benchmark Metrics")),
+ Column { metrics }
+ }, br,
+ quickCheckForDerivedTests, br
+ }, st }.attachTo(widget);
+ });
registerAspect(&metrics);
metrics.setSettingsKey("Metrics");
@@ -48,8 +65,8 @@ QtTestSettings::QtTestSettings()
useXMLOutput.setDefaultValue(true);
useXMLOutput.setLabelText(Tr::tr("Use XML output"));
useXMLOutput.setToolTip(Tr::tr("XML output is recommended, because it avoids parsing issues, "
- "while plain text is more human readable.\n\n"
- "Warning: Plain text misses some information, such as duration."));
+ "while plain text is more human readable.\n\nWarning: "
+ "Plain text misses some information, such as duration."));
registerAspect(&verboseBench);
verboseBench.setSettingsKey("VerboseBench");
@@ -99,36 +116,4 @@ QString QtTestSettings::metricsTypeToOption(const MetricsType type)
return {};
}
-QtTestSettingsPage::QtTestSettingsPage(QtTestSettings *settings, Id settingsId)
-{
- setId(settingsId);
- setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
- setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- QtTestSettings &s = *settings;
- using namespace Layouting;
-
- Column col {
- s.noCrashHandler,
- s.useXMLOutput,
- s.verboseBench,
- s.logSignalsSlots,
- Row {
- s.limitWarnings, s.maxWarnings
- },
- Group {
- title(Tr::tr("Benchmark Metrics")),
- Column { s.metrics }
- },
- br,
- s.quickCheckForDerivedTests,
- };
-
- Column { Row { col, st }, st }.attachTo(widget);
- });
-}
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/qtest/qttestsettings.h b/src/plugins/autotest/qtest/qttestsettings.h
index 4bbec4332d..eafd464d7b 100644
--- a/src/plugins/autotest/qtest/qttestsettings.h
+++ b/src/plugins/autotest/qtest/qttestsettings.h
@@ -5,10 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
enum MetricsType
{
@@ -19,10 +16,10 @@ enum MetricsType
Perf
};
-class QtTestSettings : public Utils::AspectContainer
+class QtTestSettings : public Core::PagedSettings
{
public:
- QtTestSettings();
+ explicit QtTestSettings(Utils::Id settingsId);
static QString metricsTypeToOption(const MetricsType type);
@@ -36,11 +33,4 @@ public:
Utils::BoolAspect quickCheckForDerivedTests;
};
-class QtTestSettingsPage final : public Core::IOptionsPage
-{
-public:
- QtTestSettingsPage(QtTestSettings *settings, Utils::Id settingsId);
-};
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp
index 2f84637282..81f95c1775 100644
--- a/src/plugins/autotest/qtest/qttesttreeitem.cpp
+++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp
@@ -9,7 +9,9 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
using namespace Utils;
@@ -116,7 +118,7 @@ bool QtTestTreeItem::canProvideDebugConfiguration() const
ITestConfiguration *QtTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppEditor::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
@@ -195,7 +197,7 @@ static void fillTestConfigurationsFromCheckState(const TestTreeItem *item,
testConfig = new QtTestConfiguration(item->framework());
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
- testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
+ testConfig->setProject(ProjectExplorer::ProjectManager::startupProject());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
testConfigurations << testConfig;
}
@@ -229,7 +231,7 @@ static void collectFailedTestInfo(TestTreeItem *item, QList<ITestConfiguration *
QtTestConfiguration *testConfig = new QtTestConfiguration(item->framework());
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
- testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
+ testConfig->setProject(ProjectExplorer::ProjectManager::startupProject());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
testConfigs << testConfig;
}
@@ -246,7 +248,7 @@ QList<ITestConfiguration *> QtTestTreeItem::getAllTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -269,7 +271,7 @@ QList<ITestConfiguration *> QtTestTreeItem::getAllTestConfigurations() const
QList<ITestConfiguration *> QtTestTreeItem::getSelectedTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -292,7 +294,7 @@ QList<ITestConfiguration *> QtTestTreeItem::getTestConfigurationsForFile(const F
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
diff --git a/src/plugins/autotest/quick/quicktest_utils.cpp b/src/plugins/autotest/quick/quicktest_utils.cpp
index 09d2453c96..d5ffc26038 100644
--- a/src/plugins/autotest/quick/quicktest_utils.cpp
+++ b/src/plugins/autotest/quick/quicktest_utils.cpp
@@ -22,7 +22,8 @@ bool isQuickTestMacro(const QByteArray &macro)
return valid.contains(macro);
}
-QHash<FilePath, FilePath> proFilesForQmlFiles(ITestFramework *framework, const FilePaths &files)
+QHash<FilePath, FilePath> proFilesForQmlFiles(ITestFramework *framework,
+ const QSet<FilePath> &files)
{
QHash<FilePath, FilePath> result;
TestTreeItem *rootNode = framework->rootNode();
diff --git a/src/plugins/autotest/quick/quicktest_utils.h b/src/plugins/autotest/quick/quicktest_utils.h
index fb1dbb2675..731f7c6864 100644
--- a/src/plugins/autotest/quick/quicktest_utils.h
+++ b/src/plugins/autotest/quick/quicktest_utils.h
@@ -16,7 +16,7 @@ namespace QuickTestUtils {
bool isQuickTestMacro(const QByteArray &macro);
QHash<Utils::FilePath, Utils::FilePath> proFilesForQmlFiles(ITestFramework *framework,
- const Utils::FilePaths &files);
+ const QSet<Utils::FilePath> &files);
} // namespace QuickTestUtils
} // namespace Internal
diff --git a/src/plugins/autotest/quick/quicktestconfiguration.cpp b/src/plugins/autotest/quick/quicktestconfiguration.cpp
index c7cce31acf..e37b208ac4 100644
--- a/src/plugins/autotest/quick/quicktestconfiguration.cpp
+++ b/src/plugins/autotest/quick/quicktestconfiguration.cpp
@@ -2,16 +2,13 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "quicktestconfiguration.h"
-#include "../qtest/qttestconstants.h"
+
+#include "../itestframework.h"
#include "../qtest/qttestoutputreader.h"
#include "../qtest/qttestsettings.h"
#include "../qtest/qttest_utils.h"
-#include "../autotestplugin.h"
-#include "../itestframework.h"
#include "../testsettings.h"
-#include <utils/stringutils.h>
-
using namespace Utils;
namespace Autotest {
@@ -23,21 +20,19 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework)
setMixedDebugging(true);
}
-TestOutputReader *QuickTestConfiguration::createOutputReader(
- const QFutureInterface<TestResult> &fi, QtcProcess *app) const
+TestOutputReader *QuickTestConfiguration::createOutputReader(Process *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()
? QtTestOutputReader::XML
: QtTestOutputReader::PlainText;
- return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(),
- mode, TestType::QuickTest);
+ return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QuickTest);
}
QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
{
QStringList arguments;
- if (AutotestPlugin::settings()->processArgs) {
+ if (TestSettings::instance()->processArgs()) {
arguments.append(QTestUtils::filterInterfering
(runnable().command.arguments().split(' ', Qt::SkipEmptyParts),
omitted, true));
diff --git a/src/plugins/autotest/quick/quicktestconfiguration.h b/src/plugins/autotest/quick/quicktestconfiguration.h
index 84e374ebe8..33a07b0294 100644
--- a/src/plugins/autotest/quick/quicktestconfiguration.h
+++ b/src/plugins/autotest/quick/quicktestconfiguration.h
@@ -12,8 +12,7 @@ class QuickTestConfiguration : public DebuggableTestConfiguration
{
public:
explicit QuickTestConfiguration(ITestFramework *framework);
- TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const override;
+ TestOutputReader *createOutputReader(Utils::Process *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
diff --git a/src/plugins/autotest/quick/quicktestparser.cpp b/src/plugins/autotest/quick/quicktestparser.cpp
index 2c72cdf3f1..a4cf0967b6 100644
--- a/src/plugins/autotest/quick/quicktestparser.cpp
+++ b/src/plugins/autotest/quick/quicktestparser.cpp
@@ -12,14 +12,19 @@
#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/projectpart.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsdialect.h>
#include <qmljstools/qmljsmodelmanager.h>
+
#include <utils/hostosinfo.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QPromise>
+
using namespace QmlJS;
using namespace Utils;
@@ -155,10 +160,9 @@ QList<Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Fi
QStringList dirsStr({srcDir.toString()});
ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance();
// make sure even files not listed in pro file are available inside the snapshot
- QFutureInterface<void> future;
PathsAndLanguages paths;
paths.maybeInsert(srcDir, Dialect::Qml);
- ModelManagerInterface::importScan(future, ModelManagerInterface::workingCopy(), paths, qmlJsMM,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), paths, qmlJsMM,
false /*emitDocumentChanges*/, false /*onlyTheLib*/, true /*forceRescan*/ );
const Snapshot snapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
@@ -196,7 +200,7 @@ QList<Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Fi
return foundDocs;
}
-static bool checkQmlDocumentForQuickTestCode(QFutureInterface<TestParseResultPtr> &futureInterface,
+static bool checkQmlDocumentForQuickTestCode(QPromise<TestParseResultPtr> &promise,
const Document::Ptr &qmlJSDoc,
ITestFramework *framework,
const FilePath &proFile = {},
@@ -240,12 +244,12 @@ static bool checkQmlDocumentForQuickTestCode(QFutureInterface<TestParseResultPtr
parseResult->children.append(funcResult);
}
- futureInterface.reportResult(TestParseResultPtr(parseResult));
+ promise.addResult(TestParseResultPtr(parseResult));
}
return true;
}
-bool QuickTestParser::handleQtQuickTest(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool QuickTestParser::handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
CPlusPlus::Document::Ptr document,
ITestFramework *framework)
{
@@ -263,17 +267,14 @@ bool QuickTestParser::handleQtQuickTest(QFutureInterface<TestParseResultPtr> &fu
if (srcDir.isEmpty())
return false;
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return false;
const QList<Document::Ptr> qmlDocs = scanDirectoryForQuickTestQmlFiles(srcDir);
bool result = false;
for (const Document::Ptr &qmlJSDoc : qmlDocs) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
break;
- result |= checkQmlDocumentForQuickTestCode(futureInterface,
- qmlJSDoc,
- framework,
- proFile,
+ result |= checkQmlDocumentForQuickTestCode(promise, qmlJSDoc, framework, proFile,
m_checkForDerivedTests);
}
return result;
@@ -305,9 +306,8 @@ void QuickTestParser::handleDirectoryChanged(const QString &directory)
m_watchedFiles[directory] = filesAndDates;
PathsAndLanguages paths;
paths.maybeInsert(FilePath::fromString(directory), Dialect::Qml);
- QFutureInterface<void> future;
ModelManagerInterface *qmlJsMM = ModelManagerInterface::instance();
- ModelManagerInterface::importScan(future, ModelManagerInterface::workingCopy(), paths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), paths,
qmlJsMM,
true /*emitDocumentChanges*/,
false /*onlyTheLib*/,
@@ -327,8 +327,8 @@ void QuickTestParser::doUpdateWatchPaths(const QStringList &directories)
QuickTestParser::QuickTestParser(ITestFramework *framework)
: CppParser(framework)
{
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, this, [this] {
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] {
const QStringList &dirs = m_directoryWatcher.directories();
if (!dirs.isEmpty())
m_directoryWatcher.removePaths(dirs);
@@ -338,7 +338,7 @@ QuickTestParser::QuickTestParser(ITestFramework *framework)
this, &QuickTestParser::handleDirectoryChanged);
}
-void QuickTestParser::init(const FilePaths &filesToParse, bool fullParse)
+void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{
m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
if (!fullParse) {
@@ -370,7 +370,7 @@ void QuickTestParser::release()
CppParser::release();
}
-bool QuickTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
const FilePath &fileName)
{
if (fileName.endsWith(".qml")) {
@@ -378,7 +378,7 @@ bool QuickTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
if (proFile.isEmpty())
return false;
Document::Ptr qmlJSDoc = m_qmlSnapshot.document(fileName);
- return checkQmlDocumentForQuickTestCode(futureInterface,
+ return checkQmlDocumentForQuickTestCode(promise,
qmlJSDoc,
framework(),
proFile,
@@ -389,7 +389,7 @@ bool QuickTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
if (cppdoc.isNull() || !includesQtQuickTest(cppdoc, m_cppSnapshot))
return false;
- return handleQtQuickTest(futureInterface, cppdoc, framework());
+ return handleQtQuickTest(promise, cppdoc, framework());
}
FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) const
diff --git a/src/plugins/autotest/quick/quicktestparser.h b/src/plugins/autotest/quick/quicktestparser.h
index c0fbc5a3e8..dfd71333ea 100644
--- a/src/plugins/autotest/quick/quicktestparser.h
+++ b/src/plugins/autotest/quick/quicktestparser.h
@@ -24,15 +24,15 @@ class QuickTestParser : public QObject, public CppParser
Q_OBJECT
public:
explicit QuickTestParser(ITestFramework *framework);
- void init(const Utils::FilePaths &filesToParse, bool fullParse) override;
+ void init(const QSet<Utils::FilePath> &filesToParse, bool fullParse) override;
void release() override;
- bool processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override;
Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName) const;
QStringList supportedExtensions() const override { return {"qml"}; };
private:
- bool handleQtQuickTest(QFutureInterface<TestParseResultPtr> &futureInterface,
+ bool handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
CPlusPlus::Document::Ptr document,
ITestFramework *framework);
void handleDirectoryChanged(const QString &directory);
diff --git a/src/plugins/autotest/quick/quicktesttreeitem.cpp b/src/plugins/autotest/quick/quicktesttreeitem.cpp
index f721ce3bc0..88f43e1541 100644
--- a/src/plugins/autotest/quick/quicktesttreeitem.cpp
+++ b/src/plugins/autotest/quick/quicktesttreeitem.cpp
@@ -9,7 +9,9 @@
#include "../itestframework.h"
#include <cppeditor/cppmodelmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
using namespace Utils;
@@ -108,7 +110,7 @@ bool QuickTestTreeItem::canProvideDebugConfiguration() const
ITestConfiguration *QuickTestTreeItem::testConfiguration() const
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return nullptr);
QuickTestConfiguration *config = nullptr;
@@ -147,7 +149,7 @@ static QList<ITestConfiguration *> testConfigurationsFor(
const TestTreeItem *rootNode, const std::function<bool(TestTreeItem *)> &predicate)
{
QTC_ASSERT(rootNode, return {});
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || rootNode->type() != TestTreeItem::Root)
return {};
@@ -171,7 +173,7 @@ static QList<ITestConfiguration *> testConfigurationsFor(
if (it == configurationForProFiles.end()) {
auto tc = new QuickTestConfiguration(treeItem->framework());
tc->setProjectFile(treeItem->proFile());
- tc->setProject(ProjectExplorer::SessionManager::startupProject());
+ tc->setProject(ProjectExplorer::ProjectManager::startupProject());
tc->setInternalTargets(internalTargets(treeItem->proFile()));
it = configurationForProFiles.insert(treeItem->proFile(), tc);
}
@@ -206,7 +208,7 @@ QList<ITestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
{
QList<ITestConfiguration *> result;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project || type() != Root)
return result;
@@ -369,7 +371,7 @@ QSet<QString> internalTargets(const FilePath &proFile)
{
QSet<QString> result;
const auto cppMM = CppEditor::CppModelManager::instance();
- const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
+ const auto projectInfo = cppMM->projectInfo(ProjectExplorer::ProjectManager::startupProject());
if (!projectInfo)
return {};
for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectInfo->projectParts()) {
@@ -387,17 +389,19 @@ QSet<QString> internalTargets(const FilePath &proFile)
return result;
}
-void QuickTestTreeItem::markForRemovalRecursively(const FilePath &filePath)
+void QuickTestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
{
- TestTreeItem::markForRemovalRecursively(filePath);
+ TestTreeItem::markForRemovalRecursively(filePaths);
auto parser = static_cast<QuickTestParser *>(framework()->testParser());
- const FilePath proFile = parser->projectFileForMainCppFile(filePath);
- if (!proFile.isEmpty()) {
- TestTreeItem *root = framework()->rootNode();
- root->forAllChildItems([proFile](TestTreeItem *it) {
- if (it->proFile() == proFile)
- it->markForRemoval(true);
- });
+ for (const FilePath &filePath : filePaths) {
+ const FilePath proFile = parser->projectFileForMainCppFile(filePath);
+ if (!proFile.isEmpty()) {
+ TestTreeItem *root = framework()->rootNode();
+ root->forAllChildItems([proFile](TestTreeItem *it) {
+ if (it->proFile() == proFile)
+ it->markForRemoval(true);
+ });
+ }
}
}
diff --git a/src/plugins/autotest/quick/quicktesttreeitem.h b/src/plugins/autotest/quick/quicktesttreeitem.h
index adbf95fa8b..c09bd97b98 100644
--- a/src/plugins/autotest/quick/quicktesttreeitem.h
+++ b/src/plugins/autotest/quick/quicktesttreeitem.h
@@ -35,7 +35,7 @@ public:
bool removeOnSweepIfEmpty() const override;
TestTreeItem *createParentGroupNode() const override;
bool isGroupable() const override;
- void markForRemovalRecursively(const Utils::FilePath &filePath) override;
+ void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths) override;
private:
TestTreeItem *findChildByFileNameAndType(const Utils::FilePath &filePath, const QString &name,
Type tType);
diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp
index edf126f8d4..6f3d5137e4 100644
--- a/src/plugins/autotest/testcodeparser.cpp
+++ b/src/plugins/autotest/testcodeparser.cpp
@@ -8,24 +8,21 @@
#include "testtreemodel.h"
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/taskprogress.h>
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
-#include <qmljstools/qmljsmodelmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
-#include <utils/mapreduce.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <QFuture>
-#include <QFutureInterface>
#include <QLoggingCategory>
+using namespace Core;
using namespace Utils;
namespace Autotest {
@@ -37,33 +34,30 @@ using namespace ProjectExplorer;
static bool isProjectParsing()
{
- const BuildSystem *bs = SessionManager::startupBuildSystem();
+ const BuildSystem *bs = ProjectManager::startupBuildSystem();
return bs && bs->isParsing();
}
TestCodeParser::TestCodeParser()
- : m_threadPool(new QThreadPool(this))
{
// connect to ProgressManager to postpone test parsing when CppModelManager is parsing
- auto progressManager = qobject_cast<Core::ProgressManager *>(Core::ProgressManager::instance());
- connect(progressManager, &Core::ProgressManager::taskStarted,
+ ProgressManager *progressManager = ProgressManager::instance();
+ connect(progressManager, &ProgressManager::taskStarted,
this, &TestCodeParser::onTaskStarted);
- connect(progressManager, &Core::ProgressManager::allTasksFinished,
+ connect(progressManager, &ProgressManager::allTasksFinished,
this, &TestCodeParser::onAllTasksFinished);
- connect(&m_futureWatcher, &QFutureWatcher<TestParseResultPtr>::started,
- this, &TestCodeParser::parsingStarted);
- connect(&m_futureWatcher, &QFutureWatcher<TestParseResultPtr>::finished,
- this, &TestCodeParser::onFinished);
- connect(&m_futureWatcher, &QFutureWatcher<TestParseResultPtr>::resultReadyAt,
- this, [this](int index) {
- emit testParseResultReady(m_futureWatcher.resultAt(index));
- });
connect(this, &TestCodeParser::parsingFinished, this, &TestCodeParser::releaseParserInternals);
+ connect(EditorManager::instance(), &EditorManager::documentClosed, this, [this](IDocument *doc){
+ QTC_ASSERT(doc, return);
+ if (FilePath filePath = doc->filePath(); filePath.endsWith(".qml"))
+ m_qmlEditorRev.remove(filePath);
+ });
m_reparseTimer.setSingleShot(true);
connect(&m_reparseTimer, &QTimer::timeout, this, &TestCodeParser::parsePostponedFiles);
- m_threadPool->setMaxThreadCount(std::max(QThread::idealThreadCount()/4, 1));
}
+TestCodeParser::~TestCodeParser() = default;
+
void TestCodeParser::setState(State state)
{
if (m_parserState == Shutdown)
@@ -82,14 +76,14 @@ void TestCodeParser::setState(State state)
}
m_parserState = state;
- if (m_parserState == Idle && SessionManager::startupProject()) {
+ if (m_parserState == Idle && ProjectManager::startupProject()) {
if (m_postponedUpdateType == UpdateType::FullUpdate || m_dirty) {
emitUpdateTestTree();
} else if (m_postponedUpdateType == UpdateType::PartialUpdate) {
m_postponedUpdateType = UpdateType::NoUpdate;
qCDebug(LOG) << "calling scanForTests with postponed files (setState)";
if (!m_reparseTimer.isActive())
- scanForTests(Utils::toList(m_postponedFiles));
+ scanForTests(m_postponedFiles);
}
}
}
@@ -100,7 +94,7 @@ void TestCodeParser::syncTestFrameworks(const QList<ITestParser *> &parsers)
// there's a running parse
m_postponedUpdateType = UpdateType::NoUpdate;
m_postponedFiles.clear();
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
}
qCDebug(LOG) << "Setting" << parsers << "as current parsers";
m_testCodeParsers = parsers;
@@ -139,7 +133,7 @@ void TestCodeParser::updateTestTree(const QSet<ITestParser *> &parsers)
return;
}
- if (!SessionManager::startupProject())
+ if (!ProjectManager::startupProject())
return;
m_postponedUpdateType = UpdateType::NoUpdate;
@@ -158,7 +152,7 @@ void TestCodeParser::onDocumentUpdated(const FilePath &fileName, bool isQmlFile)
if (isProjectParsing() || m_codeModelParsing || m_postponedUpdateType == UpdateType::FullUpdate)
return;
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project)
return;
// Quick tests: qml files aren't necessarily listed inside project files
@@ -177,15 +171,20 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document)
{
static const QStringList ignoredSuffixes{ "qbs", "ui.qml" };
const FilePath fileName = document->fileName();
- if (!ignoredSuffixes.contains(fileName.suffix()))
- onDocumentUpdated(fileName, true);
+ int editorRevision = document->editorRevision();
+ if (editorRevision != m_qmlEditorRev.value(fileName, 0)) {
+ m_qmlEditorRev.insert(fileName, editorRevision);
+ if (!ignoredSuffixes.contains(fileName.suffix()))
+ onDocumentUpdated(fileName, true);
+ }
}
void TestCodeParser::onStartupProjectChanged(Project *project)
{
+ m_qmlEditorRev.clear();
if (m_parserState == FullParse || m_parserState == PartialParse) {
qCDebug(LOG) << "Canceling scanForTest (startup project changed)";
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
}
emit aboutToPerformFullParse();
if (project)
@@ -194,7 +193,7 @@ void TestCodeParser::onStartupProjectChanged(Project *project)
void TestCodeParser::onProjectPartsUpdated(Project *project)
{
- if (project != SessionManager::startupProject())
+ if (project != ProjectManager::startupProject())
return;
if (isProjectParsing() || m_codeModelParsing)
m_postponedUpdateType = UpdateType::FullUpdate;
@@ -205,35 +204,33 @@ void TestCodeParser::onProjectPartsUpdated(Project *project)
void TestCodeParser::aboutToShutdown()
{
qCDebug(LOG) << "Disabling (immediately) - shutting down";
- State oldState = m_parserState;
m_parserState = Shutdown;
- if (oldState == PartialParse || oldState == FullParse) {
- m_futureWatcher.cancel();
- m_futureWatcher.waitForFinished();
- }
+ m_taskTree.reset();
+ m_futureSynchronizer.waitForFinished();
}
-bool TestCodeParser::postponed(const FilePaths &fileList)
+bool TestCodeParser::postponed(const QSet<FilePath> &filePaths)
{
switch (m_parserState) {
case Idle:
- if (fileList.size() == 1) {
+ if (filePaths.size() == 1) {
if (m_reparseTimerTimedOut)
return false;
+ const FilePath filePath = *filePaths.begin();
switch (m_postponedFiles.size()) {
case 0:
- m_postponedFiles.insert(fileList.first());
+ m_postponedFiles.insert(filePath);
m_reparseTimer.setInterval(1000);
m_reparseTimer.start();
return true;
case 1:
- if (m_postponedFiles.contains(fileList.first())) {
+ if (m_postponedFiles.contains(filePath)) {
m_reparseTimer.start();
return true;
}
Q_FALLTHROUGH();
default:
- m_postponedFiles.insert(fileList.first());
+ m_postponedFiles.insert(filePath);
m_reparseTimer.stop();
m_reparseTimer.setInterval(0);
m_reparseTimerTimedOut = false;
@@ -245,18 +242,17 @@ bool TestCodeParser::postponed(const FilePaths &fileList)
case PartialParse:
case FullParse:
// parse is running, postponing a full parse
- if (fileList.isEmpty()) {
+ if (filePaths.isEmpty()) {
m_postponedFiles.clear();
m_postponedUpdateType = UpdateType::FullUpdate;
qCDebug(LOG) << "Canceling scanForTest (full parse triggered while running a scan)";
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
} else {
// partial parse triggered, but full parse is postponed already, ignoring this
if (m_postponedUpdateType == UpdateType::FullUpdate)
return true;
// partial parse triggered, postpone or add current files to already postponed partial
- for (const FilePath &file : fileList)
- m_postponedFiles.insert(file);
+ m_postponedFiles += filePaths;
m_postponedUpdateType = UpdateType::PartialUpdate;
}
return true;
@@ -266,43 +262,43 @@ bool TestCodeParser::postponed(const FilePaths &fileList)
QTC_ASSERT(false, return false); // should not happen at all
}
-static void parseFileForTests(const QList<ITestParser *> &parsers,
- QFutureInterface<TestParseResultPtr> &futureInterface,
- const FilePath &fileName)
+static void parseFileForTests(QPromise<TestParseResultPtr> &promise,
+ const QList<ITestParser *> &parsers, const FilePath &fileName)
{
for (ITestParser *parser : parsers) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
- if (parser->processDocument(futureInterface, fileName))
+ if (parser->processDocument(promise, fileName))
break;
}
}
-void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestParser *> &parsers)
+void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
+ const QList<ITestParser *> &parsers)
{
if (m_parserState == Shutdown || m_testCodeParsers.isEmpty())
return;
- if (postponed(fileList))
+ if (postponed(filePaths))
return;
+ QSet<FilePath> files = filePaths; // avoid getting cleared if m_postponedFiles have been passed
m_reparseTimer.stop();
m_reparseTimerTimedOut = false;
m_postponedFiles.clear();
- bool isFullParse = fileList.isEmpty();
- Project *project = SessionManager::startupProject();
+ const bool isFullParse = files.isEmpty();
+ Project *project = ProjectManager::startupProject();
if (!project)
return;
- FilePaths list;
if (isFullParse) {
- list = project->files(Project::SourceFiles);
- if (list.isEmpty()) {
+ files = Utils::toSet(project->files(Project::SourceFiles));
+ if (files.isEmpty()) {
// at least project file should be there, but might happen if parsing current project
// takes too long, especially when opening sessions holding multiple projects
qCDebug(LOG) << "File list empty (FullParse) - trying again in a sec";
emitUpdateTestTree();
return;
- } else if (list.size() == 1 && list.first() == project->projectFilePath()) {
+ } else if (files.size() == 1 && *files.constBegin() == project->projectFilePath()) {
qCDebug(LOG) << "File list contains only the project file.";
return;
}
@@ -310,7 +306,6 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
qCDebug(LOG) << "setting state to FullParse (scanForTests)";
m_parserState = FullParse;
} else {
- list << fileList;
qCDebug(LOG) << "setting state to PartialParse (scanForTests)";
m_parserState = PartialParse;
}
@@ -319,43 +314,39 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
TestTreeModel::instance()->updateCheckStateCache();
if (isFullParse) {
// remove qml files as they will be found automatically by the referencing cpp file
- list = Utils::filtered(list, [](const FilePath &fn) {
- return !fn.endsWith(".qml");
- });
+ files = Utils::filtered(files, [](const FilePath &fn) { return !fn.endsWith(".qml"); });
if (!parsers.isEmpty()) {
- for (ITestParser *parser : parsers) {
+ for (ITestParser *parser : parsers)
parser->framework()->rootNode()->markForRemovalRecursively(true);
- }
} else {
emit requestRemoveAllFrameworkItems();
}
} else if (!parsers.isEmpty()) {
for (ITestParser *parser: parsers) {
- for (const FilePath &filePath : std::as_const(list))
- parser->framework()->rootNode()->markForRemovalRecursively(filePath);
+ parser->framework()->rootNode()->markForRemovalRecursively(files);
}
} else {
- for (const FilePath &filePath : std::as_const(list))
- emit requestRemoval(filePath);
+ emit requestRemoval(files);
}
- QTC_ASSERT(!(isFullParse && list.isEmpty()), onFinished(); return);
+ QTC_ASSERT(!(isFullParse && files.isEmpty()), onFinished(true); return);
// use only a single parser or all current active?
const QList<ITestParser *> codeParsers = parsers.isEmpty() ? m_testCodeParsers : parsers;
qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "StartParsing";
+ m_parsingTimer.restart();
QSet<QString> extensions;
const auto cppSnapshot = CppEditor::CppModelManager::instance()->snapshot();
for (ITestParser *parser : codeParsers) {
- parser->init(list, isFullParse);
+ parser->init(files, isFullParse);
for (const QString &ext : parser->supportedExtensions())
extensions.insert(ext);
}
// We are only interested in files that have been either parsed by the c++ parser,
// or have an extension that one of the parsers is specifically interested in.
- const FilePaths filteredList
- = Utils::filtered(list, [&extensions, &cppSnapshot](const FilePath &fn) {
+ const QSet<FilePath> filteredFiles
+ = Utils::filtered(files, [&extensions, &cppSnapshot](const FilePath &fn) {
const bool isSupportedExtension = Utils::anyOf(extensions, [&fn](const QString &ext) {
return fn.suffix() == ext;
});
@@ -364,21 +355,37 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
return cppSnapshot.contains(fn);
});
- qCDebug(LOG) << "Starting scan of" << filteredList.size() << "(" << list.size() << ")"
+ qCDebug(LOG) << "Starting scan of" << filteredFiles.size() << "(" << files.size() << ")"
<< "files with" << codeParsers.size() << "parsers";
- QFuture<TestParseResultPtr> future = Utils::map(filteredList,
- [codeParsers](QFutureInterface<TestParseResultPtr> &fi, const FilePath &file) {
- parseFileForTests(codeParsers, fi, file);
- },
- MapReduceOption::Unordered,
- m_threadPool,
- QThread::LowestPriority);
- m_futureWatcher.setFuture(future);
- if (filteredList.size() > 5) {
- Core::ProgressManager::addTask(future, Tr::tr("Scanning for Tests"),
- Autotest::Constants::TASK_PARSE);
+ using namespace Tasking;
+
+ QList<TaskItem> tasks{ParallelLimit(std::max(QThread::idealThreadCount() / 4, 1))};
+ for (const FilePath &file : filteredFiles) {
+ const auto setup = [this, codeParsers, file](Async<TestParseResultPtr> &async) {
+ async.setConcurrentCallData(parseFileForTests, codeParsers, file);
+ async.setPriority(QThread::LowestPriority);
+ async.setFutureSynchronizer(&m_futureSynchronizer);
+ };
+ const auto onDone = [this](const Async<TestParseResultPtr> &async) {
+ const QList<TestParseResultPtr> results = async.results();
+ for (const TestParseResultPtr &result : results)
+ emit testParseResultReady(result);
+ };
+ tasks.append(AsyncTask<TestParseResultPtr>(setup, onDone));
+ }
+ m_taskTree.reset(new TaskTree{tasks});
+ const auto onDone = [this] { m_taskTree.release()->deleteLater(); onFinished(true); };
+ const auto onError = [this] { m_taskTree.release()->deleteLater(); onFinished(false); };
+ connect(m_taskTree.get(), &TaskTree::started, this, &TestCodeParser::parsingStarted);
+ connect(m_taskTree.get(), &TaskTree::done, this, onDone);
+ connect(m_taskTree.get(), &TaskTree::errorOccurred, this, onError);
+ if (filteredFiles.size() > 5) {
+ auto progress = new TaskProgress(m_taskTree.get());
+ progress->setDisplayName(Tr::tr("Scanning for Tests"));
+ progress->setId(Constants::TASK_PARSE);
}
+ m_taskTree->start();
}
void TestCodeParser::onTaskStarted(Id type)
@@ -390,7 +397,7 @@ void TestCodeParser::onTaskStarted(Id type)
? UpdateType::FullUpdate : UpdateType::PartialUpdate;
qCDebug(LOG) << "Canceling scan for test (CppModelParsing started)";
m_parsingHasFailed = true;
- Core::ProgressManager::cancelTasks(Constants::TASK_PARSE);
+ ProgressManager::cancelTasks(Constants::TASK_PARSE);
}
}
}
@@ -410,10 +417,9 @@ void TestCodeParser::onAllTasksFinished(Id type)
setState(Idle);
}
-void TestCodeParser::onFinished()
+void TestCodeParser::onFinished(bool success)
{
- if (m_futureWatcher.isCanceled())
- m_parsingHasFailed = true;
+ m_parsingHasFailed = !success;
switch (m_parserState) {
case PartialParse:
qCDebug(LOG) << "setting state to Idle (onFinished, PartialParse)";
@@ -433,6 +439,7 @@ void TestCodeParser::onFinished()
m_updateParsers.clear();
emit parsingFinished();
qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "ParsingFin";
+ qCDebug(LOG) << "Parsing took:" << m_parsingTimer.elapsed() << "ms";
}
m_dirty = false;
break;
@@ -457,7 +464,7 @@ void TestCodeParser::onPartialParsingFinished()
case UpdateType::PartialUpdate:
qCDebug(LOG) << "calling scanForTests with postponed files (onPartialParsingFinished)";
if (!m_reparseTimer.isActive())
- scanForTests(Utils::toList(m_postponedFiles));
+ scanForTests(m_postponedFiles);
break;
case UpdateType::NoUpdate:
m_dirty |= m_codeModelParsing;
@@ -481,7 +488,7 @@ void TestCodeParser::onPartialParsingFinished()
void TestCodeParser::parsePostponedFiles()
{
m_reparseTimerTimedOut = true;
- scanForTests(Utils::toList(m_postponedFiles));
+ scanForTests(m_postponedFiles);
}
void TestCodeParser::releaseParserInternals()
diff --git a/src/plugins/autotest/testcodeparser.h b/src/plugins/autotest/testcodeparser.h
index efd0d602e4..2dd81d93d6 100644
--- a/src/plugins/autotest/testcodeparser.h
+++ b/src/plugins/autotest/testcodeparser.h
@@ -6,10 +6,10 @@
#include "itestparser.h"
#include <qmljs/qmljsdocument.h>
+
+#include <utils/futuresynchronizer.h>
#include <utils/id.h>
-#include <QFutureWatcher>
-#include <QMap>
#include <QObject>
#include <QTimer>
@@ -18,9 +18,9 @@ class QThreadPool;
QT_END_NAMESPACE
namespace ProjectExplorer { class Project; }
+namespace Tasking { class TaskTree; }
namespace Autotest {
-
namespace Internal {
class TestCodeParser : public QObject
@@ -35,6 +35,7 @@ public:
};
TestCodeParser();
+ ~TestCodeParser();
void setState(State state);
State state() const { return m_parserState; }
@@ -48,11 +49,11 @@ public:
signals:
void aboutToPerformFullParse();
- void testParseResultReady(const TestParseResultPtr result);
+ void testParseResultReady(const TestParseResultPtr result); // TODO: pass list of results?
void parsingStarted();
void parsingFinished();
void parsingFailed();
- void requestRemoval(const Utils::FilePath &filePath);
+ void requestRemoval(const QSet<Utils::FilePath> &filePaths);
void requestRemoveAllFrameworkItems();
public:
@@ -65,15 +66,15 @@ public:
void aboutToShutdown();
private:
- bool postponed(const Utils::FilePaths &fileList);
- void scanForTests(const Utils::FilePaths &fileList = Utils::FilePaths(),
+ bool postponed(const QSet<Utils::FilePath> &fileList);
+ void scanForTests(const QSet<Utils::FilePath> &filePaths = {},
const QList<ITestParser *> &parsers = {});
// qml files must be handled slightly different
void onDocumentUpdated(const Utils::FilePath &fileName, bool isQmlFile = false);
void onTaskStarted(Utils::Id type);
void onAllTasksFinished(Utils::Id type);
- void onFinished();
+ void onFinished(bool success);
void onPartialParsingFinished();
void parsePostponedFiles();
void releaseParserInternals();
@@ -83,21 +84,21 @@ private:
bool m_parsingHasFailed = false;
bool m_codeModelParsing = false;
- enum class UpdateType {
- NoUpdate,
- PartialUpdate,
- FullUpdate
- } m_postponedUpdateType = UpdateType::NoUpdate;
+ enum class UpdateType { NoUpdate, PartialUpdate, FullUpdate };
+ UpdateType m_postponedUpdateType = UpdateType::NoUpdate;
bool m_dirty = false;
bool m_singleShotScheduled = false;
bool m_reparseTimerTimedOut = false;
QSet<Utils::FilePath> m_postponedFiles;
State m_parserState = Idle;
- QFutureWatcher<TestParseResultPtr> m_futureWatcher;
QList<ITestParser *> m_testCodeParsers; // ptrs are still owned by TestFrameworkManager
QTimer m_reparseTimer;
QSet<ITestParser *> m_updateParsers;
- QThreadPool *m_threadPool = nullptr;
+ Utils::FutureSynchronizer m_futureSynchronizer;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
+ QHash<Utils::FilePath, int> m_qmlEditorRev;
+
+ QElapsedTimer m_parsingTimer;
};
} // namespace Internal
diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp
index 764ede697d..c095e9e0f6 100644
--- a/src/plugins/autotest/testconfiguration.cpp
+++ b/src/plugins/autotest/testconfiguration.cpp
@@ -4,22 +4,19 @@
#include "testconfiguration.h"
#include "itestframework.h"
-#include "testoutputreader.h"
#include "testrunconfiguration.h"
-#include <cppeditor/cppmodelmanager.h>
-#include <cppeditor/projectinfo.h>
-
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
-#include <projectexplorer/environmentaspect.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <utils/algorithm.h>
+
#include <QLoggingCategory>
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.testconfiguration", QtWarningMsg)
@@ -29,7 +26,6 @@ using namespace Utils;
namespace Autotest {
-
ITestConfiguration::ITestConfiguration(ITestBase *testBase)
: m_testBase(testBase)
{
@@ -94,7 +90,7 @@ static FilePath ensureExeEnding(const FilePath &file)
return file.withExecutableSuffix();
}
-void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguration *rc,
+void TestConfiguration::completeTestInformation(RunConfiguration *rc,
TestRunMode runMode)
{
QTC_ASSERT(rc, return);
@@ -104,7 +100,7 @@ void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguratio
qCDebug(LOG) << "Executable has been set already - not completing configuration again.";
return;
}
- Project *startupProject = SessionManager::startupProject();
+ Project *startupProject = ProjectManager::startupProject();
if (!startupProject || startupProject != project())
return;
@@ -149,7 +145,7 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
}
qCDebug(LOG) << "Failed to complete - using 'normal' way.";
}
- Project *startupProject = SessionManager::startupProject();
+ Project *startupProject = ProjectManager::startupProject();
if (!startupProject || startupProject != project()) {
setProject(nullptr);
return;
diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h
index d57416d016..6292ef8204 100644
--- a/src/plugins/autotest/testconfiguration.h
+++ b/src/plugins/autotest/testconfiguration.h
@@ -9,11 +9,10 @@
#include <projectexplorer/runcontrol.h>
#include <utils/environment.h>
-#include <QFutureInterface>
#include <QPointer>
#include <QStringList>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Autotest {
namespace Internal {
@@ -40,8 +39,7 @@ public:
Utils::FilePath executableFilePath() const;
virtual Utils::FilePath testExecutable() const { return executableFilePath(); };
- virtual TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
- Utils::QtcProcess *app) const = 0;
+ virtual TestOutputReader *createOutputReader(Utils::Process *app) const = 0;
virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const;
ITestBase *testBase() const { return m_testBase; }
diff --git a/src/plugins/autotest/testframeworkmanager.cpp b/src/plugins/autotest/testframeworkmanager.cpp
index 91fa313593..3c6aff581d 100644
--- a/src/plugins/autotest/testframeworkmanager.cpp
+++ b/src/plugins/autotest/testframeworkmanager.cpp
@@ -3,7 +3,6 @@
#include "testframeworkmanager.h"
-#include "autotestplugin.h"
#include "testsettings.h"
#include <utils/aspects.h>
@@ -98,7 +97,7 @@ ITestTool *TestFrameworkManager::testToolForBuildSystemId(Id buildSystemId)
void TestFrameworkManager::synchronizeSettings(QSettings *s)
{
- Internal::AutotestPlugin::settings()->fromSettings(s);
+ Internal::TestSettings::instance()->fromSettings(s);
for (ITestFramework *framework : std::as_const(m_registeredFrameworks)) {
if (ITestSettings *fSettings = framework->testSettings())
fSettings->readSettings(s);
diff --git a/src/plugins/autotest/testframeworkmanager.h b/src/plugins/autotest/testframeworkmanager.h
index 3eab62adbc..73dfd96ff9 100644
--- a/src/plugins/autotest/testframeworkmanager.h
+++ b/src/plugins/autotest/testframeworkmanager.h
@@ -11,7 +11,7 @@ QT_END_NAMESPACE
namespace Autotest {
namespace Internal {
-struct TestSettings;
+class TestSettings;
}
class TestFrameworkManager final
diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp
index 100eed89ed..431f5a3af8 100644
--- a/src/plugins/autotest/testnavigationwidget.cpp
+++ b/src/plugins/autotest/testnavigationwidget.cpp
@@ -18,12 +18,15 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/itemviewfind.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <utils/algorithm.h>
#include <utils/link.h>
#include <utils/progressindicator.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -87,8 +90,8 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) :
connect(m_model, &TestTreeModel::updatedActiveFrameworks, this, [this](int numberOfActive) {
m_missingFrameworksWidget->setVisible(numberOfActive == 0);
});
- ProjectExplorer::SessionManager *sm = ProjectExplorer::SessionManager::instance();
- connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged,
+ ProjectExplorer::ProjectManager *sm = ProjectExplorer::ProjectManager::instance();
+ connect(sm, &ProjectExplorer::ProjectManager::startupProjectChanged,
this, [this](ProjectExplorer::Project * /*project*/) {
m_expandedStateCache.clear();
});
@@ -190,7 +193,7 @@ QList<QToolButton *> TestNavigationWidget::createToolButtons()
m_filterButton = new QToolButton(m_view);
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter Test Tree"));
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterButton->setPopupMode(QToolButton::InstantPopup);
m_filterMenu = new QMenu(m_filterButton);
initializeFilterMenu();
diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp
index 2f1dc75e48..cef36c90e7 100644
--- a/src/plugins/autotest/testoutputreader.cpp
+++ b/src/plugins/autotest/testoutputreader.cpp
@@ -4,17 +4,12 @@
#include "testoutputreader.h"
#include "autotesttr.h"
-#include "testresult.h"
-#include "testresultspane.h"
#include "testtreeitem.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <QDebug>
-#include <QDir>
-#include <QFileInfo>
-#include <QProcess>
+#include <QRegularExpression>
using namespace Utils;
@@ -26,11 +21,8 @@ FilePath TestOutputReader::constructSourceFilePath(const FilePath &path, const Q
return filePath.isReadableFile() ? filePath : FilePath();
}
-TestOutputReader::TestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- QtcProcess *testApplication, const FilePath &buildDirectory)
- : m_futureInterface(futureInterface)
- , m_buildDir(buildDirectory)
- , m_id(testApplication ? testApplication->commandLine().executable().toUserOutput() : QString())
+TestOutputReader::TestOutputReader(Process *testApplication, const FilePath &buildDirectory)
+ : m_buildDir(buildDirectory)
{
auto chopLineBreak = [](QByteArray line) {
if (line.endsWith('\n'))
@@ -41,6 +33,9 @@ TestOutputReader::TestOutputReader(const QFutureInterface<TestResult> &futureInt
};
if (testApplication) {
+ connect(testApplication, &Process::started, this, [this, testApplication] {
+ m_id = testApplication->commandLine().executable().toUserOutput();
+ });
testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) {
processStdOutput(chopLineBreak(line.toUtf8()));
});
diff --git a/src/plugins/autotest/testoutputreader.h b/src/plugins/autotest/testoutputreader.h
index 55c645d87c..e87c463735 100644
--- a/src/plugins/autotest/testoutputreader.h
+++ b/src/plugins/autotest/testoutputreader.h
@@ -5,11 +5,9 @@
#include "testresult.h"
-#include <QFutureInterface>
#include <QObject>
-#include <QString>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Autotest {
@@ -17,8 +15,7 @@ class TestOutputReader : public QObject
{
Q_OBJECT
public:
- TestOutputReader(const QFutureInterface<TestResult> &futureInterface,
- Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
+ TestOutputReader(Utils::Process *testApplication, const Utils::FilePath &buildDirectory);
virtual ~TestOutputReader();
void processStdOutput(const QByteArray &outputLine);
virtual void processStdError(const QByteArray &outputLine);
@@ -46,7 +43,6 @@ protected:
void sendAndResetSanitizerResult();
void reportResult(const TestResult &result);
- QFutureInterface<TestResult> m_futureInterface;
Utils::FilePath m_buildDir;
QString m_id;
QHash<ResultType, int> m_summary;
diff --git a/src/plugins/autotest/testprojectsettings.cpp b/src/plugins/autotest/testprojectsettings.cpp
index 862fc8a24f..5f745637db 100644
--- a/src/plugins/autotest/testprojectsettings.cpp
+++ b/src/plugins/autotest/testprojectsettings.cpp
@@ -7,7 +7,7 @@
#include "testframeworkmanager.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <QLoggingCategory>
diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp
index 9b55623d57..c14b6c1703 100644
--- a/src/plugins/autotest/testresultdelegate.cpp
+++ b/src/plugins/autotest/testresultdelegate.cpp
@@ -3,7 +3,6 @@
#include "testresultdelegate.h"
-#include "autotestplugin.h"
#include "testresultmodel.h"
#include "testsettings.h"
@@ -162,10 +161,10 @@ void TestResultDelegate::clearCache()
void TestResultDelegate::limitTextOutput(QString &output) const
{
- int maxLineCount = Internal::AutotestPlugin::settings()->resultDescriptionMaxSize;
+ int maxLineCount = Internal::TestSettings::instance()->resultDescriptionMaxSize();
bool limited = false;
- if (Internal::AutotestPlugin::settings()->limitResultDescription && maxLineCount > 0) {
+ if (Internal::TestSettings::instance()->limitResultDescription() && maxLineCount > 0) {
int index = -1;
int lastChar = output.size() - 1;
@@ -183,7 +182,7 @@ void TestResultDelegate::limitTextOutput(QString &output) const
}
}
- if (AutotestPlugin::settings()->limitResultOutput && output.length() > outputLimit) {
+ if (TestSettings::instance()->limitResultOutput() && output.length() > outputLimit) {
output = output.left(outputLimit);
limited = true;
}
diff --git a/src/plugins/autotest/testresultdelegate.h b/src/plugins/autotest/testresultdelegate.h
index c5a4919a71..f7d6aa1ab2 100644
--- a/src/plugins/autotest/testresultdelegate.h
+++ b/src/plugins/autotest/testresultdelegate.h
@@ -38,7 +38,6 @@ private:
public:
LayoutPositions(QStyleOptionViewItem &options, const TestResultFilterModel *filterModel)
: m_top(options.rect.top()),
- m_bottom(options.rect.bottom()),
m_left(options.rect.left()),
m_right(options.rect.right())
{
@@ -57,20 +56,15 @@ private:
int top() const { return m_top + ITEM_MARGIN; }
int left() const { return m_left + ITEM_MARGIN; }
int right() const { return m_right - ITEM_MARGIN; }
- int bottom() const { return m_bottom; }
int minimumHeight() const { return ICON_SIZE + 2 * ITEM_MARGIN; }
int iconSize() const { return ICON_SIZE; }
- int fontHeight() const { return m_fontHeight; }
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
- int typeAreaWidth() const { return m_typeAreaWidth; }
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING; }
int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); }
int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; }
int lineAreaLeft() const { return right() - m_maxLineLength; }
- QRect typeArea() const { return QRect(typeAreaLeft(), top(),
- typeAreaWidth(), m_fontHeight); }
QRect textArea() const { return QRect(textAreaLeft(), top(),
textAreaWidth(), m_fontHeight); }
QRect fileArea() const { return QRect(fileAreaLeft(), top(),
@@ -84,7 +78,6 @@ private:
int m_maxLineLength;
int m_realFileLength;
int m_top;
- int m_bottom;
int m_left;
int m_right;
int m_fontHeight;
diff --git a/src/plugins/autotest/testresultmodel.cpp b/src/plugins/autotest/testresultmodel.cpp
index 434d647410..f686692faa 100644
--- a/src/plugins/autotest/testresultmodel.cpp
+++ b/src/plugins/autotest/testresultmodel.cpp
@@ -4,7 +4,6 @@
#include "testresultmodel.h"
#include "autotesticons.h"
-#include "autotestplugin.h"
#include "testrunner.h"
#include "testsettings.h"
#include "testtreeitem.h"
@@ -274,7 +273,7 @@ void TestResultModel::addTestResult(const TestResult &testResult, bool autoExpan
TestResultItem *newItem = new TestResultItem(testResult);
TestResultItem *root = nullptr;
- if (AutotestPlugin::settings()->displayApplication) {
+ if (TestSettings::instance()->displayApplication()) {
const QString application = testResult.id();
if (!application.isEmpty()) {
root = rootItem()->findFirstLevelChild([&application](TestResultItem *child) {
diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp
index 87d44b0f2f..3dbad31ea9 100644
--- a/src/plugins/autotest/testresultspane.cpp
+++ b/src/plugins/autotest/testresultspane.cpp
@@ -197,7 +197,7 @@ void TestResultsPane::createToolButtons()
m_filterButton = new QToolButton(m_treeView);
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter Test Results"));
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterButton->setPopupMode(QToolButton::InstantPopup);
m_filterMenu = new QMenu(m_filterButton);
initializeFilterMenu();
@@ -291,7 +291,7 @@ void TestResultsPane::clearContents()
setIconBadgeNumber(0);
navigateStateChanged();
m_summaryWidget->setVisible(false);
- m_autoScroll = AutotestPlugin::settings()->autoScroll;
+ m_autoScroll = TestSettings::instance()->autoScroll();
connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection);
m_textOutput->clear();
@@ -437,7 +437,7 @@ void TestResultsPane::onRunSelectedTriggered()
void TestResultsPane::initializeFilterMenu()
{
- const bool omitIntern = AutotestPlugin::settings()->omitInternalMssg;
+ const bool omitIntern = TestSettings::instance()->omitInternalMsg();
// FilterModel has all messages enabled by default
if (omitIntern)
m_filterModel->toggleTestResultType(ResultType::MessageInternal);
@@ -553,8 +553,8 @@ void TestResultsPane::onTestRunFinished()
m_model->removeCurrentTestMessage();
disconnect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
this, &TestResultsPane::onScrollBarRangeChanged);
- if (AutotestPlugin::settings()->popupOnFinish
- && (!AutotestPlugin::settings()->popupOnFail || hasFailedTests(m_model))) {
+ if (TestSettings::instance()->popupOnFinish()
+ && (!TestSettings::instance()->popupOnFail() || hasFailedTests(m_model))) {
popup(IOutputPane::NoModeSwitch);
}
createMarks();
diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp
index cb01d0d9a2..68dddaf951 100644
--- a/src/plugins/autotest/testrunner.cpp
+++ b/src/plugins/autotest/testrunner.cpp
@@ -6,18 +6,15 @@
#include "autotestconstants.h"
#include "autotestplugin.h"
#include "autotesttr.h"
-#include "itestframework.h"
#include "testoutputreader.h"
#include "testprojectsettings.h"
#include "testresultspane.h"
#include "testrunconfiguration.h"
-#include "testsettings.h"
#include "testtreeitem.h"
#include "testtreemodel.h"
#include <coreplugin/icore.h>
-#include <coreplugin/progressmanager/futureprogress.h>
-#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/taskprogress.h>
#include <debugger/debuggerkitinformation.h>
#include <debugger/debuggerruncontrol.h>
@@ -28,30 +25,28 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/outputformat.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFormLayout>
-#include <QFuture>
-#include <QFutureInterface>
#include <QLabel>
#include <QLoggingCategory>
#include <QPointer>
-#include <QProcess>
#include <QPushButton>
-#include <QTimer>
+using namespace Core;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace Autotest {
@@ -72,14 +67,7 @@ TestRunner::TestRunner()
m_cancelTimer.setSingleShot(true);
connect(&m_cancelTimer, &QTimer::timeout, this, [this] { cancelCurrent(Timeout); });
- connect(&m_futureWatcher, &QFutureWatcher<TestResult>::finished,
- this, &TestRunner::onFinished);
- connect(this, &TestRunner::requestStopTestRun,
- &m_futureWatcher, &QFutureWatcher<TestResult>::cancel);
- connect(&m_futureWatcher, &QFutureWatcher<TestResult>::canceled, this, [this] {
- cancelCurrent(UserCanceled);
- reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user."));
- });
+ connect(this, &TestRunner::requestStopTestRun, this, [this] { cancelCurrent(UserCanceled); });
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &TestRunner::onBuildQueueFinished);
}
@@ -93,14 +81,14 @@ TestRunner::~TestRunner()
void TestRunner::runTest(TestRunMode mode, const ITestTreeItem *item)
{
- QTC_ASSERT(!m_executingTests, return);
+ QTC_ASSERT(!isTestRunning(), return);
ITestConfiguration *configuration = item->asConfiguration(mode);
if (configuration)
runTests(mode, {configuration});
}
-static QString processInformation(const QtcProcess *proc)
+static QString processInformation(const Process *proc)
{
QTC_ASSERT(proc, return {});
const CommandLine command = proc->commandLine();
@@ -144,180 +132,21 @@ static QString constructOmittedVariablesDetailsString(const EnvironmentItems &di
+ '\n' + removedVars.join('\n');
}
-bool TestRunner::currentConfigValid()
-{
- const FilePath commandFilePath = m_currentConfig->testExecutable();
- if (!commandFilePath.isEmpty())
- return true;
-
- reportResult(ResultType::MessageFatal,
- Tr::tr("Executable path is empty. (%1)").arg(m_currentConfig->displayName()));
- delete m_currentConfig;
- m_currentConfig = nullptr;
- if (m_selectedTests.isEmpty()) {
- if (m_fakeFutureInterface)
- m_fakeFutureInterface->reportFinished();
- onFinished();
- } else {
- onProcessDone();
- }
- return false;
-}
-
-void TestRunner::setUpProcessEnv()
-{
- CommandLine command = m_currentProcess->commandLine();
- if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
- TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
-
- QStringList omitted;
- command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), CommandLine::Raw);
- if (!omitted.isEmpty()) {
- const QString &details = constructOmittedDetailsString(omitted);
- reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
- }
- } else {
- TestToolConfiguration *current = static_cast<TestToolConfiguration *>(m_currentConfig);
- command.setArguments(current->commandLine().arguments());
- }
- m_currentProcess->setCommand(command);
-
- m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
- const Environment &original = m_currentConfig->environment();
- Environment environment = m_currentConfig->filteredEnvironment(original);
- const EnvironmentItems removedVariables = Utils::filtered(
- original.diff(environment), [](const EnvironmentItem &it) {
- return it.operation == EnvironmentItem::Unset;
- });
- if (!removedVariables.isEmpty()) {
- const QString &details = constructOmittedVariablesDetailsString(removedVariables)
- .arg(m_currentConfig->displayName());
- reportResult(ResultType::MessageWarn, details);
- }
- m_currentProcess->setEnvironment(environment);
-}
-
-void TestRunner::scheduleNext()
-{
- QTC_ASSERT(!m_selectedTests.isEmpty(), onFinished(); return);
- QTC_ASSERT(!m_currentConfig && !m_currentProcess, resetInternalPointers());
- QTC_ASSERT(m_fakeFutureInterface, onFinished(); return);
- QTC_ASSERT(!m_canceled, onFinished(); return);
-
- m_currentConfig = m_selectedTests.takeFirst();
-
- if (!currentConfigValid())
- return;
-
- if (!m_currentConfig->project())
- onProcessDone();
-
- m_currentProcess = new QtcProcess;
- m_currentProcess->setCommand({m_currentConfig->testExecutable(), {}});
-
- QTC_ASSERT(!m_currentOutputReader, delete m_currentOutputReader);
- m_currentOutputReader = m_currentConfig->createOutputReader(*m_fakeFutureInterface, m_currentProcess);
- QTC_ASSERT(m_currentOutputReader, onProcessDone(); return);
- connect(m_currentOutputReader, &TestOutputReader::newResult, this, &TestRunner::testResultReady);
- connect(m_currentOutputReader, &TestOutputReader::newOutputLineAvailable,
- TestResultsPane::instance(), &TestResultsPane::addOutputLine);
-
- setUpProcessEnv();
-
- connect(m_currentProcess, &QtcProcess::done, this, &TestRunner::onProcessDone);
- const int timeout = AutotestPlugin::settings()->timeout;
- m_cancelTimer.setInterval(timeout);
- m_cancelTimer.start();
-
- qCInfo(runnerLog) << "Command:" << m_currentProcess->commandLine().executable();
- qCInfo(runnerLog) << "Arguments:" << m_currentProcess->commandLine().arguments();
- qCInfo(runnerLog) << "Working directory:" << m_currentProcess->workingDirectory();
- qCDebug(runnerLog) << "Environment:" << m_currentProcess->environment().toStringList();
-
- m_currentProcess->start();
-}
-
void TestRunner::cancelCurrent(TestRunner::CancelReason reason)
{
- m_canceled = true;
-
- if (m_fakeFutureInterface)
- m_fakeFutureInterface->reportCanceled();
-
if (reason == KitChanged)
reportResult(ResultType::MessageWarn, Tr::tr("Current kit has changed. Canceling test run."));
else if (reason == Timeout)
reportResult(ResultType::MessageFatal, Tr::tr("Test case canceled due to timeout.\nMaybe raise the timeout?"));
-
- // if user or timeout cancels the current run ensure to kill the running process
- if (m_currentProcess && m_currentProcess->state() != QProcess::NotRunning) {
- m_currentProcess->kill();
- m_currentProcess->waitForFinished();
- }
-}
-
-void TestRunner::onProcessDone()
-{
- if (m_currentProcess->result() == ProcessResult::StartFailed) {
- reportResult(ResultType::MessageFatal,
- Tr::tr("Failed to start test for project \"%1\".").arg(m_currentConfig->displayName())
- + processInformation(m_currentProcess) + rcInfo(m_currentConfig));
- }
-
- if (m_executingTests && m_currentConfig) {
- QTC_CHECK(m_fakeFutureInterface);
- m_fakeFutureInterface->setProgressValue(m_fakeFutureInterface->progressValue()
- + m_currentConfig->testCaseCount());
- if (m_currentProcess && !m_fakeFutureInterface->isCanceled()) {
- if (m_currentProcess->exitStatus() == QProcess::CrashExit) {
- if (m_currentOutputReader)
- m_currentOutputReader->reportCrash();
- reportResult(ResultType::MessageFatal,
- Tr::tr("Test for project \"%1\" crashed.").arg(m_currentConfig->displayName())
- + processInformation(m_currentProcess) + rcInfo(m_currentConfig));
- } else if (m_currentOutputReader && !m_currentOutputReader->hadValidOutput()) {
- reportResult(ResultType::MessageFatal,
- Tr::tr("Test for project \"%1\" did not produce any expected output.")
- .arg(m_currentConfig->displayName()) + processInformation(m_currentProcess)
- + rcInfo(m_currentConfig));
- }
- }
- }
- if (m_currentOutputReader) {
- const int disabled = m_currentOutputReader->disabledTests();
- if (disabled > 0)
- emit hadDisabledTests(disabled);
- if (m_currentOutputReader->hasSummary())
- emit reportSummary(m_currentOutputReader->id(), m_currentOutputReader->summary());
-
- m_currentOutputReader->resetCommandlineColor();
- }
- resetInternalPointers();
-
- if (!m_fakeFutureInterface) {
- QTC_ASSERT(!m_executingTests, m_executingTests = false);
- return;
- }
- if (!m_selectedTests.isEmpty() && !m_fakeFutureInterface->isCanceled())
- scheduleNext();
- else
- m_fakeFutureInterface->reportFinished();
-}
-
-void TestRunner::resetInternalPointers()
-{
- delete m_currentOutputReader;
- if (m_currentProcess)
- m_currentProcess->deleteLater();
- delete m_currentConfig;
- m_currentOutputReader = nullptr;
- m_currentProcess = nullptr;
- m_currentConfig = nullptr;
+ else if (reason == UserCanceled)
+ reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user."));
+ m_taskTree.reset();
+ onFinished();
}
void TestRunner::runTests(TestRunMode mode, const QList<ITestConfiguration *> &selectedTests)
{
- QTC_ASSERT(!m_executingTests, return);
+ QTC_ASSERT(!isTestRunning(), return);
qDeleteAll(m_selectedTests);
m_selectedTests = selectedTests;
@@ -332,8 +161,6 @@ void TestRunner::runTests(TestRunMode mode, const QList<ITestConfiguration *> &s
return;
}
- m_executingTests = true;
- m_canceled = false;
emit testRunStarted();
// clear old log and output pane
@@ -385,7 +212,7 @@ static QString firstNonEmptyTestCaseTarget(const TestConfiguration *config)
static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
{
- const Project *project = SessionManager::startupProject();
+ const Project *project = ProjectManager::startupProject();
if (!project)
return nullptr;
const Target *target = project->activeTarget();
@@ -411,7 +238,7 @@ static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
if (runConfigurations.size() == 1)
return runConfigurations.first();
- RunConfigurationSelectionDialog dialog(buildTargetKey, Core::ICore::dialogParent());
+ RunConfigurationSelectionDialog dialog(buildTargetKey, ICore::dialogParent());
if (dialog.exec() == QDialog::Accepted) {
const QString dName = dialog.displayName();
if (dName.isEmpty())
@@ -431,7 +258,7 @@ static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
int TestRunner::precheckTestConfigurations()
{
- const bool omitWarnings = AutotestPlugin::settings()->omitRunConfigWarn;
+ const bool omitWarnings = TestSettings::instance()->omitRunConfigWarn();
int testCaseCount = 0;
for (ITestConfiguration *itc : std::as_const(m_selectedTests)) {
if (itc->testBase()->type() == ITestBase::Tool) {
@@ -467,7 +294,7 @@ int TestRunner::precheckTestConfigurations()
void TestRunner::onBuildSystemUpdated()
{
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
if (QTC_GUARD(target))
disconnect(target, &Target::buildSystemUpdated, this, &TestRunner::onBuildSystemUpdated);
if (!m_skipTargetsCheck) {
@@ -482,7 +309,7 @@ void TestRunner::runTestsHelper()
bool projectChanged = false;
for (ITestConfiguration *itc : std::as_const(m_selectedTests)) {
if (itc->testBase()->type() == ITestBase::Tool) {
- if (itc->project() != SessionManager::startupProject()) {
+ if (itc->project() != ProjectManager::startupProject()) {
projectChanged = true;
toBeRemoved.append(itc);
}
@@ -513,19 +340,135 @@ void TestRunner::runTestsHelper()
return;
}
- int testCaseCount = precheckTestConfigurations();
+ const int testCaseCount = precheckTestConfigurations();
+ Q_UNUSED(testCaseCount) // TODO: may be useful for fine-grained progress reporting, when fixed
+
+ struct TestStorage {
+ std::unique_ptr<TestOutputReader> m_outputReader;
+ };
+
+ QList<TaskItem> tasks{optional};
+
+ for (ITestConfiguration *config : m_selectedTests) {
+ QTC_ASSERT(config, continue);
+ const TreeStorage<TestStorage> storage;
+
+ const auto onGroupSetup = [this, config] {
+ if (!config->project())
+ return TaskAction::StopWithDone;
+ if (config->testExecutable().isEmpty()) {
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Executable path is empty. (%1)").arg(config->displayName()));
+ return TaskAction::StopWithDone;
+ }
+ return TaskAction::Continue;
+ };
+ const auto onSetup = [this, config, storage](Process &process) {
+ TestStorage *testStorage = storage.activeStorage();
+ QTC_ASSERT(testStorage, return);
+ testStorage->m_outputReader.reset(config->createOutputReader(&process));
+ QTC_ASSERT(testStorage->m_outputReader, return);
+ connect(testStorage->m_outputReader.get(), &TestOutputReader::newResult,
+ this, &TestRunner::testResultReady);
+ connect(testStorage->m_outputReader.get(), &TestOutputReader::newOutputLineAvailable,
+ TestResultsPane::instance(), &TestResultsPane::addOutputLine);
+
+ CommandLine command{config->testExecutable(), {}};
+ if (config->testBase()->type() == ITestBase::Framework) {
+ TestConfiguration *current = static_cast<TestConfiguration *>(config);
+ QStringList omitted;
+ command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), CommandLine::Raw);
+ if (!omitted.isEmpty()) {
+ const QString &details = constructOmittedDetailsString(omitted);
+ reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
+ }
+ } else {
+ TestToolConfiguration *current = static_cast<TestToolConfiguration *>(config);
+ command.setArguments(current->commandLine().arguments());
+ }
+ process.setCommand(command);
+
+ process.setWorkingDirectory(config->workingDirectory());
+ const Environment &original = config->environment();
+ Environment environment = config->filteredEnvironment(original);
+ const EnvironmentItems removedVariables = Utils::filtered(
+ original.diff(environment), [](const EnvironmentItem &it) {
+ return it.operation == EnvironmentItem::Unset;
+ });
+ if (!removedVariables.isEmpty()) {
+ const QString &details = constructOmittedVariablesDetailsString(removedVariables)
+ .arg(config->displayName());
+ reportResult(ResultType::MessageWarn, details);
+ }
+ process.setEnvironment(environment);
+
+ m_cancelTimer.setInterval(TestSettings::instance()->timeout());
+ m_cancelTimer.start();
+
+ qCInfo(runnerLog) << "Command:" << process.commandLine().executable();
+ qCInfo(runnerLog) << "Arguments:" << process.commandLine().arguments();
+ qCInfo(runnerLog) << "Working directory:" << process.workingDirectory();
+ qCDebug(runnerLog) << "Environment:" << process.environment().toStringList();
+ };
+ const auto onDone = [this, config, storage](const Process &process) {
+ TestStorage *testStorage = storage.activeStorage();
+ QTC_ASSERT(testStorage, return);
+ if (process.result() == ProcessResult::StartFailed) {
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Failed to start test for project \"%1\".").arg(config->displayName())
+ + processInformation(&process) + rcInfo(config));
+ }
- // Fake future interface - destruction will be handled by QFuture/QFutureWatcher
- m_fakeFutureInterface = new QFutureInterface<TestResult>(QFutureInterfaceBase::Running);
- QFuture<TestResult> future = m_fakeFutureInterface->future();
- m_fakeFutureInterface->setProgressRange(0, testCaseCount);
- m_fakeFutureInterface->setProgressValue(0);
- m_futureWatcher.setFuture(future);
+ if (process.exitStatus() == QProcess::CrashExit) {
+ if (testStorage->m_outputReader)
+ testStorage->m_outputReader->reportCrash();
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Test for project \"%1\" crashed.").arg(config->displayName())
+ + processInformation(&process) + rcInfo(config));
+ } else if (testStorage->m_outputReader && !testStorage->m_outputReader->hadValidOutput()) {
+ reportResult(ResultType::MessageFatal,
+ Tr::tr("Test for project \"%1\" did not produce any expected output.")
+ .arg(config->displayName()) + processInformation(&process)
+ + rcInfo(config));
+ }
+ if (testStorage->m_outputReader) {
+ const int disabled = testStorage->m_outputReader->disabledTests();
+ if (disabled > 0)
+ emit hadDisabledTests(disabled);
+ if (testStorage->m_outputReader->hasSummary())
+ emit reportSummary(testStorage->m_outputReader->id(), testStorage->m_outputReader->summary());
+
+ testStorage->m_outputReader->resetCommandlineColor();
+ }
+ };
+ const Group group {
+ optional,
+ Storage(storage),
+ OnGroupSetup(onGroupSetup),
+ ProcessTask(onSetup, onDone, onDone)
+ };
+ tasks.append(group);
+ }
+
+ m_taskTree.reset(new TaskTree(tasks));
+ connect(m_taskTree.get(), &TaskTree::done, this, &TestRunner::onFinished);
+ connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &TestRunner::onFinished);
+
+ auto progress = new TaskProgress(m_taskTree.get());
+ progress->setDisplayName(tr("Running Tests"));
+ progress->setAutoStopOnCancel(false);
+ progress->setHalfLifeTimePerTask(10000); // 10 seconds
+ connect(progress, &TaskProgress::canceled, this, [this, progress] {
+ // progress was a child of task tree which is going to be deleted directly. Unwind properly.
+ progress->setParent(nullptr);
+ progress->deleteLater();
+ cancelCurrent(UserCanceled);
+ });
- Core::ProgressManager::addTask(future, Tr::tr("Running Tests"), Autotest::Constants::TASK_INDEX);
- if (AutotestPlugin::settings()->popupOnStart)
+ if (TestSettings::instance()->popupOnStart())
AutotestPlugin::popupResultsPane();
- scheduleNext();
+
+ m_taskTree->start();
}
static void processOutput(TestOutputReader *outputreader, const QString &msg, OutputFormat format)
@@ -626,13 +569,8 @@ void TestRunner::debugTests()
}
}
- // We need a fake QFuture for the results. TODO: replace with QtConcurrent::run
- QFutureInterface<TestResult> *futureInterface
- = new QFutureInterface<TestResult>(QFutureInterfaceBase::Running);
- m_futureWatcher.setFuture(futureInterface->future());
-
if (useOutputProcessor) {
- TestOutputReader *outputreader = config->createOutputReader(*futureInterface, nullptr);
+ TestOutputReader *outputreader = config->createOutputReader(nullptr);
connect(outputreader, &TestOutputReader::newResult, this, &TestRunner::testResultReady);
outputreader->setId(inferior.command.executable().toString());
connect(outputreader, &TestOutputReader::newOutputLineAvailable,
@@ -641,9 +579,7 @@ void TestRunner::debugTests()
this, [outputreader](const QString &msg, OutputFormat format) {
processOutput(outputreader, msg, format);
});
-
- connect(runControl, &RunControl::stopped,
- outputreader, &QObject::deleteLater);
+ connect(runControl, &RunControl::stopped, outputreader, &QObject::deleteLater);
}
m_stopDebugConnect = connect(this, &TestRunner::requestStopTestRun,
@@ -652,13 +588,13 @@ void TestRunner::debugTests()
connect(runControl, &RunControl::stopped, this, &TestRunner::onFinished);
m_finishDebugConnect = connect(runControl, &RunControl::finished, this, &TestRunner::onFinished);
ProjectExplorerPlugin::startRunControl(runControl);
- if (useOutputProcessor && AutotestPlugin::settings()->popupOnStart)
+ if (useOutputProcessor && TestSettings::instance()->popupOnStart())
AutotestPlugin::popupResultsPane();
}
static bool executablesEmpty()
{
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
const QList<RunConfiguration *> configs = target->runConfigurations();
QTC_ASSERT(!configs.isEmpty(), return false);
if (auto execAspect = configs.first()->aspect<ExecutableAspect>())
@@ -671,7 +607,7 @@ void TestRunner::runOrDebugTests()
if (!m_skipTargetsCheck) {
if (executablesEmpty()) {
m_skipTargetsCheck = true;
- Target * target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
QTimer::singleShot(5000, this, [this, target = QPointer<Target>(target)] {
if (target) {
disconnect(target, &Target::buildSystemUpdated,
@@ -706,8 +642,7 @@ void TestRunner::buildProject(Project *project)
BuildManager *buildManager = BuildManager::instance();
m_buildConnect = connect(this, &TestRunner::requestStopTestRun,
buildManager, &BuildManager::cancel);
- connect(buildManager, &BuildManager::buildQueueFinished,
- this, &TestRunner::buildFinished);
+ connect(buildManager, &BuildManager::buildQueueFinished, this, &TestRunner::buildFinished);
BuildManager::buildProjectWithDependencies(project);
if (!BuildManager::isBuilding())
buildFinished(false);
@@ -717,37 +652,33 @@ void TestRunner::buildFinished(bool success)
{
disconnect(m_buildConnect);
BuildManager *buildManager = BuildManager::instance();
- disconnect(buildManager, &BuildManager::buildQueueFinished,
- this, &TestRunner::buildFinished);
+ disconnect(buildManager, &BuildManager::buildQueueFinished, this, &TestRunner::buildFinished);
if (success) {
- if (!m_canceled)
- runOrDebugTests();
- else if (m_executingTests)
- onFinished();
- } else {
- reportResult(ResultType::MessageFatal, Tr::tr("Build failed. Canceling test run."));
- onFinished();
+ runOrDebugTests();
+ return;
}
+ reportResult(ResultType::MessageFatal, Tr::tr("Build failed. Canceling test run."));
+ onFinished();
}
static RunAfterBuildMode runAfterBuild()
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project)
return RunAfterBuildMode::None;
if (!project->namedSettings(Constants::SK_USE_GLOBAL).isValid())
- return AutotestPlugin::settings()->runAfterBuild;
+ return TestSettings::instance()->runAfterBuildMode();
TestProjectSettings *projectSettings = AutotestPlugin::projectSettings(project);
- return projectSettings->useGlobalSettings() ? AutotestPlugin::settings()->runAfterBuild
+ return projectSettings->useGlobalSettings() ? TestSettings::instance()->runAfterBuildMode()
: projectSettings->runAfterBuild();
}
void TestRunner::onBuildQueueFinished(bool success)
{
- if (m_executingTests || !m_selectedTests.isEmpty()) // paranoia!
+ if (isTestRunning() || !m_selectedTests.isEmpty()) // paranoia!
return;
if (!success || m_runMode != TestRunMode::None)
@@ -768,17 +699,15 @@ void TestRunner::onBuildQueueFinished(bool success)
void TestRunner::onFinished()
{
- m_cancelTimer.stop();
- // if we've been canceled and we still have test configurations queued just throw them away
- qDeleteAll(m_selectedTests);
- m_selectedTests.clear();
-
+ if (m_taskTree)
+ m_taskTree.release()->deleteLater();
disconnect(m_stopDebugConnect);
disconnect(m_finishDebugConnect);
disconnect(m_targetConnect);
- m_fakeFutureInterface = nullptr;
+ qDeleteAll(m_selectedTests);
+ m_selectedTests.clear();
+ m_cancelTimer.stop();
m_runMode = TestRunMode::None;
- m_executingTests = false;
emit testRunFinished();
}
@@ -855,7 +784,7 @@ void RunConfigurationSelectionDialog::populate()
{
m_rcCombo->addItem({}, QStringList{{}, {}, {}}); // empty default
- if (auto project = SessionManager::startupProject()) {
+ if (auto project = ProjectManager::startupProject()) {
if (auto target = project->activeTarget()) {
for (RunConfiguration *rc : target->runConfigurations()) {
auto runnable = rc->runnable();
diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h
index c96af56075..25c65f8985 100644
--- a/src/plugins/autotest/testrunner.h
+++ b/src/plugins/autotest/testrunner.h
@@ -4,12 +4,11 @@
#pragma once
#include "autotest_global.h"
-#include "testresult.h"
+
+#include "autotestconstants.h"
#include <QDialog>
-#include <QFutureWatcher>
#include <QList>
-#include <QObject>
#include <QTimer>
QT_BEGIN_NAMESPACE
@@ -20,13 +19,14 @@ class QLabel;
QT_END_NAMESPACE
namespace ProjectExplorer { class Project; }
-namespace Utils { class QtcProcess; }
+namespace Tasking { class TaskTree; }
namespace Autotest {
-enum class TestRunMode;
class ITestConfiguration;
-class TestOutputReader;
+class ITestTreeItem;
+class TestResult;
+enum class ResultType;
namespace Internal {
@@ -44,7 +44,7 @@ public:
void runTests(TestRunMode mode, const QList<ITestConfiguration *> &selectedTests);
void runTest(TestRunMode mode, const ITestTreeItem *item);
- bool isTestRunning() const { return m_executingTests; }
+ bool isTestRunning() const { return m_buildConnect || m_stopDebugConnect || m_taskTree.get(); }
signals:
void testRunStarted();
@@ -61,12 +61,7 @@ private:
void onFinished();
int precheckTestConfigurations();
- bool currentConfigValid();
- void setUpProcessEnv();
- void scheduleNext();
void cancelCurrent(CancelReason reason);
- void onProcessDone();
- void resetInternalPointers();
void runTestsHelper();
void debugTests();
@@ -75,14 +70,9 @@ private:
bool postponeTestRunWithEmptyExecutable(ProjectExplorer::Project *project);
void onBuildSystemUpdated();
- QFutureWatcher<TestResult> m_futureWatcher;
- QFutureInterface<TestResult> *m_fakeFutureInterface = nullptr;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
+
QList<ITestConfiguration *> m_selectedTests;
- bool m_executingTests = false;
- bool m_canceled = false;
- ITestConfiguration *m_currentConfig = nullptr;
- Utils::QtcProcess *m_currentProcess = nullptr;
- TestOutputReader *m_currentOutputReader = nullptr;
TestRunMode m_runMode = TestRunMode::None;
// temporarily used if building before running is necessary
diff --git a/src/plugins/autotest/testsettings.cpp b/src/plugins/autotest/testsettings.cpp
index 6de76d815f..001bf7fe07 100644
--- a/src/plugins/autotest/testsettings.cpp
+++ b/src/plugins/autotest/testsettings.cpp
@@ -4,53 +4,123 @@
#include "testsettings.h"
#include "autotestconstants.h"
+#include "autotesttr.h"
#include "testframeworkmanager.h"
-#include <utils/id.h>
-
#include <QSettings>
-namespace Autotest {
-namespace Internal {
-
-static const char timeoutKey[] = "Timeout";
-static const char omitInternalKey[] = "OmitInternal";
-static const char omitRunConfigWarnKey[] = "OmitRCWarnings";
-static const char limitResultOutputKey[] = "LimitResultOutput";
-static const char limitResultDescriptionKey[] = "LimitResultDescription";
-static const char resultDescriptionMaxSizeKey[] = "ResultDescriptionMaxSize";
-static const char autoScrollKey[] = "AutoScrollResults";
-static const char processArgsKey[] = "ProcessArgs";
-static const char displayApplicationKey[] = "DisplayApp";
-static const char popupOnStartKey[] = "PopupOnStart";
-static const char popupOnFinishKey[] = "PopupOnFinish";
-static const char popupOnFailKey[] = "PopupOnFail";
-static const char runAfterBuildKey[] = "RunAfterBuild";
+namespace Autotest::Internal {
+
static const char groupSuffix[] = ".group";
constexpr int defaultTimeout = 60000;
+static TestSettings *s_instance;
+
+TestSettings *TestSettings::instance()
+{
+ return s_instance;
+}
+
TestSettings::TestSettings()
- : timeout(defaultTimeout)
{
+ s_instance = this;
+
+ setSettingsGroup(Constants::SETTINGSGROUP);
+
+ registerAspect(&timeout);
+ timeout.setSettingsKey("Timeout");
+ timeout.setDefaultValue(defaultTimeout);
+ timeout.setRange(5000, 36'000'000); // 36 Mio ms = 36'000 s = 10 h
+ timeout.setSuffix(Tr::tr(" s")); // we show seconds, but store milliseconds
+ timeout.setDisplayScaleFactor(1000);
+ timeout.setToolTip(Tr::tr("Timeout used when executing test cases. This will apply "
+ "for each test case on its own, not the whole project."));
+
+ registerAspect(&omitInternalMsg);
+ omitInternalMsg.setSettingsKey("OmitInternal");
+ omitInternalMsg.setDefaultValue(true);
+ omitInternalMsg.setLabelText(Tr::tr("Omit internal messages"));
+ omitInternalMsg.setToolTip(Tr::tr("Hides internal messages by default. "
+ "You can still enable them by using the test results filter."));
+
+ registerAspect(&omitRunConfigWarn);
+ omitRunConfigWarn.setSettingsKey("OmitRCWarnings");
+ omitRunConfigWarn.setLabelText(Tr::tr("Omit run configuration warnings"));
+ omitRunConfigWarn.setToolTip(Tr::tr("Hides warnings related to a deduced run configuration."));
+
+ registerAspect(&limitResultOutput);
+ limitResultOutput.setSettingsKey("LimitResultOutput");
+ limitResultOutput.setDefaultValue(true);
+ limitResultOutput.setLabelText(Tr::tr("Limit result output"));
+ limitResultOutput.setToolTip(Tr::tr("Limits result output to 100000 characters."));
+
+ registerAspect(&limitResultDescription);
+ limitResultDescription.setSettingsKey("LimitResultDescription");
+ limitResultDescription.setLabelText(Tr::tr("Limit result description:"));
+ limitResultDescription.setToolTip(
+ Tr::tr("Limit number of lines shown in test result tooltip and description."));
+
+ registerAspect(&resultDescriptionMaxSize);
+ resultDescriptionMaxSize.setSettingsKey("ResultDescriptionMaxSize");
+ resultDescriptionMaxSize.setDefaultValue(10);
+ resultDescriptionMaxSize.setRange(1, 100000);
+ resultDescriptionMaxSize.setEnabler(&limitResultDescription);
+
+ registerAspect(&autoScroll);
+ autoScroll.setSettingsKey("AutoScrollResults");
+ autoScroll.setDefaultValue(true);
+ autoScroll.setLabelText(Tr::tr("Automatically scroll results"));
+ autoScroll.setToolTip(Tr::tr("Automatically scrolls down when new items are added "
+ "and scrollbar is at bottom."));
+
+ registerAspect(&processArgs);
+ processArgs.setSettingsKey("ProcessArgs");
+ processArgs.setLabelText(Tr::tr("Process arguments"));
+ processArgs.setToolTip(
+ Tr::tr("Allow passing arguments specified on the respective run configuration.\n"
+ "Warning: this is an experimental feature and might lead to failing to "
+ "execute the test executable."));
+
+ registerAspect(&displayApplication);
+ displayApplication.setSettingsKey("DisplayApp");
+ displayApplication.setLabelText(Tr::tr("Group results by application"));
+
+ registerAspect(&popupOnStart);
+ popupOnStart.setSettingsKey("PopupOnStart");
+ popupOnStart.setLabelText(Tr::tr("Open results when tests start"));
+ popupOnStart.setToolTip(
+ Tr::tr("Displays test results automatically when tests are started."));
+
+ registerAspect(&popupOnFinish);
+ popupOnFinish.setSettingsKey("PopupOnFinish");
+ popupOnFinish.setDefaultValue(true);
+ popupOnFinish.setLabelText(Tr::tr("Open results when tests finish"));
+ popupOnFinish.setToolTip(
+ Tr::tr("Displays test results automatically when tests are finished."));
+
+ registerAspect(&popupOnFail);
+ popupOnFail.setSettingsKey("PopupOnFail");
+ popupOnFail.setLabelText(Tr::tr("Only for unsuccessful test runs"));
+ popupOnFail.setEnabler(&popupOnFinish);
+ popupOnFail.setToolTip(Tr::tr("Displays test results only if the test run contains "
+ "failed, fatal or unexpectedly passed tests."));
+
+ registerAspect(&runAfterBuild);
+ runAfterBuild.setSettingsKey("RunAfterBuild");
+ runAfterBuild.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
+ runAfterBuild.setToolTip(Tr::tr("Runs chosen tests automatically if a build succeeded."));
+ runAfterBuild.addOption(Tr::tr("None"));
+ runAfterBuild.addOption(Tr::tr("All"));
+ runAfterBuild.addOption(Tr::tr("Selected"));
}
void TestSettings::toSettings(QSettings *s) const
{
+ AspectContainer::writeSettings(s);
+
s->beginGroup(Constants::SETTINGSGROUP);
- s->setValue(timeoutKey, timeout);
- s->setValue(omitInternalKey, omitInternalMssg);
- s->setValue(omitRunConfigWarnKey, omitRunConfigWarn);
- s->setValue(limitResultOutputKey, limitResultOutput);
- s->setValue(limitResultDescriptionKey, limitResultDescription);
- s->setValue(resultDescriptionMaxSizeKey, resultDescriptionMaxSize);
- s->setValue(autoScrollKey, autoScroll);
- s->setValue(processArgsKey, processArgs);
- s->setValue(displayApplicationKey, displayApplication);
- s->setValue(popupOnStartKey, popupOnStart);
- s->setValue(popupOnFinishKey, popupOnFinish);
- s->setValue(popupOnFailKey, popupOnFail);
- s->setValue(runAfterBuildKey, int(runAfterBuild));
+
// store frameworks and their current active and grouping state
for (auto it = frameworks.cbegin(); it != frameworks.cend(); ++it) {
const Utils::Id &id = it.key();
@@ -65,21 +135,10 @@ void TestSettings::toSettings(QSettings *s) const
void TestSettings::fromSettings(QSettings *s)
{
+ AspectContainer::readSettings(s);
+
s->beginGroup(Constants::SETTINGSGROUP);
- timeout = s->value(timeoutKey, defaultTimeout).toInt();
- omitInternalMssg = s->value(omitInternalKey, true).toBool();
- omitRunConfigWarn = s->value(omitRunConfigWarnKey, false).toBool();
- limitResultOutput = s->value(limitResultOutputKey, true).toBool();
- limitResultDescription = s->value(limitResultDescriptionKey, false).toBool();
- resultDescriptionMaxSize = s->value(resultDescriptionMaxSizeKey, 10).toInt();
- autoScroll = s->value(autoScrollKey, true).toBool();
- processArgs = s->value(processArgsKey, false).toBool();
- displayApplication = s->value(displayApplicationKey, false).toBool();
- popupOnStart = s->value(popupOnStartKey, true).toBool();
- popupOnFinish = s->value(popupOnFinishKey, true).toBool();
- popupOnFail = s->value(popupOnFailKey, false).toBool();
- runAfterBuild = RunAfterBuildMode(s->value(runAfterBuildKey,
- int(RunAfterBuildMode::None)).toInt());
+
// try to get settings for registered frameworks
const TestFrameworks &registered = TestFrameworkManager::registeredFrameworks();
frameworks.clear();
@@ -102,5 +161,9 @@ void TestSettings::fromSettings(QSettings *s)
s->endGroup();
}
-} // namespace Internal
-} // namespace Autotest
+RunAfterBuildMode TestSettings::runAfterBuildMode() const
+{
+ return static_cast<RunAfterBuildMode>(runAfterBuild.value());
+}
+
+} // namespace Autotest::Internal
diff --git a/src/plugins/autotest/testsettings.h b/src/plugins/autotest/testsettings.h
index ecf04fabaa..b4aec87f49 100644
--- a/src/plugins/autotest/testsettings.h
+++ b/src/plugins/autotest/testsettings.h
@@ -3,18 +3,9 @@
#pragma once
-#include <QHash>
+#include <utils/aspects.h>
-namespace Utils {
-class Id;
-}
-
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
-
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
enum class RunAfterBuildMode
{
@@ -23,29 +14,39 @@ enum class RunAfterBuildMode
Selected
};
-struct TestSettings
+class NonAspectSettings
{
+public:
+ QHash<Utils::Id, bool> frameworks;
+ QHash<Utils::Id, bool> frameworksGrouping;
+ QHash<Utils::Id, bool> tools;
+};
+
+class TestSettings : public Utils::AspectContainer, public NonAspectSettings
+{
+public:
TestSettings();
+
+ static TestSettings *instance();
+
void toSettings(QSettings *s) const;
void fromSettings(QSettings *s);
- int timeout;
- bool omitInternalMssg = true;
- bool omitRunConfigWarn = false;
- bool limitResultOutput = true;
- bool limitResultDescription = false;
- int resultDescriptionMaxSize = 10;
- bool autoScroll = true;
- bool processArgs = false;
- bool displayApplication = false;
- bool popupOnStart = true;
- bool popupOnFinish = true;
- bool popupOnFail = false;
- RunAfterBuildMode runAfterBuild = RunAfterBuildMode::None;
- QHash<Utils::Id, bool> frameworks;
- QHash<Utils::Id, bool> frameworksGrouping;
- QHash<Utils::Id, bool> tools;
+ Utils::IntegerAspect timeout;
+ Utils::BoolAspect omitInternalMsg;
+ Utils::BoolAspect omitRunConfigWarn;
+ Utils::BoolAspect limitResultOutput;
+ Utils::BoolAspect limitResultDescription;
+ Utils::IntegerAspect resultDescriptionMaxSize;
+ Utils::BoolAspect autoScroll;
+ Utils::BoolAspect processArgs;
+ Utils::BoolAspect displayApplication;
+ Utils::BoolAspect popupOnStart;
+ Utils::BoolAspect popupOnFinish;
+ Utils::BoolAspect popupOnFail;
+ Utils::SelectionAspect runAfterBuild;
+
+ RunAfterBuildMode runAfterBuildMode() const;
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/testsettingspage.cpp b/src/plugins/autotest/testsettingspage.cpp
index 1cd6e2d1ae..6c9850bc2a 100644
--- a/src/plugins/autotest/testsettingspage.cpp
+++ b/src/plugins/autotest/testsettingspage.cpp
@@ -25,116 +25,33 @@
#include <QLabel>
#include <QPushButton>
#include <QSpacerItem>
-#include <QSpinBox>
#include <QTreeWidget>
-#include <QWidget>
using namespace Utils;
-namespace Autotest {
-namespace Internal {
+namespace Autotest::Internal {
-class TestSettingsWidget : public QWidget
+class TestSettingsWidget : public Core::IOptionsPageWidget
{
public:
- explicit TestSettingsWidget(QWidget *parent = nullptr);
-
- void setSettings(const TestSettings &settings);
- TestSettings settings() const;
+ TestSettingsWidget();
private:
void populateFrameworksListWidget(const QHash<Id, bool> &frameworks,
const QHash<Id, bool> &testTools);
- void testSettings(TestSettings &settings) const;
- void testToolsSettings(TestSettings &settings) const;
+ void testSettings(NonAspectSettings &settings) const;
+ void testToolsSettings(NonAspectSettings &settings) const;
void onFrameworkItemChanged();
- QCheckBox *m_omitInternalMsgCB;
- QCheckBox *m_omitRunConfigWarnCB;
- QCheckBox *m_limitResultOutputCB;
- QCheckBox *m_limitResultDescriptionCb;
- QSpinBox *m_limitResultDescriptionSpinBox;
- QCheckBox *m_openResultsOnStartCB;
- QCheckBox *m_openResultsOnFinishCB;
- QCheckBox *m_openResultsOnFailCB;
- QCheckBox *m_autoScrollCB;
- QCheckBox *m_displayAppCB;
- QCheckBox *m_processArgsCB;
- QComboBox *m_runAfterBuildCB;
- QSpinBox *m_timeoutSpin;
QTreeWidget *m_frameworkTreeWidget;
InfoLabel *m_frameworksWarn;
};
-TestSettingsWidget::TestSettingsWidget(QWidget *parent)
- : QWidget(parent)
+TestSettingsWidget::TestSettingsWidget()
{
- resize(586, 469);
-
- m_omitInternalMsgCB = new QCheckBox(Tr::tr("Omit internal messages"));
- m_omitInternalMsgCB->setChecked(true);
- m_omitInternalMsgCB->setToolTip(Tr::tr("Hides internal messages by default. "
- "You can still enable them by using the test results filter."));
-
- m_omitRunConfigWarnCB = new QCheckBox(Tr::tr("Omit run configuration warnings"));
- m_omitRunConfigWarnCB->setToolTip(Tr::tr("Hides warnings related to a deduced run configuration."));
-
- m_limitResultOutputCB = new QCheckBox(Tr::tr("Limit result output"));
- m_limitResultOutputCB->setChecked(true);
- m_limitResultOutputCB->setToolTip(Tr::tr("Limits result output to 100000 characters."));
-
- m_limitResultDescriptionCb = new QCheckBox(Tr::tr("Limit result description:"));
- m_limitResultDescriptionCb->setToolTip(
- Tr::tr("Limit number of lines shown in test result tooltip and description."));
-
- m_limitResultDescriptionSpinBox = new QSpinBox;
- m_limitResultDescriptionSpinBox->setEnabled(false);
- m_limitResultDescriptionSpinBox->setMinimum(1);
- m_limitResultDescriptionSpinBox->setMaximum(1000000);
- m_limitResultDescriptionSpinBox->setValue(10);
-
- m_openResultsOnStartCB = new QCheckBox(Tr::tr("Open results when tests start"));
- m_openResultsOnStartCB->setToolTip(
- Tr::tr("Displays test results automatically when tests are started."));
-
- m_openResultsOnFinishCB = new QCheckBox(Tr::tr("Open results when tests finish"));
- m_openResultsOnFinishCB->setChecked(true);
- m_openResultsOnFinishCB->setToolTip(
- Tr::tr("Displays test results automatically when tests are finished."));
-
- m_openResultsOnFailCB = new QCheckBox(Tr::tr("Only for unsuccessful test runs"));
- m_openResultsOnFailCB->setToolTip(
- Tr::tr("Displays test results only if the test run contains failed, fatal or unexpectedly passed tests."));
-
- m_autoScrollCB = new QCheckBox(Tr::tr("Automatically scroll results"));
- m_autoScrollCB->setChecked(true);
- m_autoScrollCB->setToolTip(Tr::tr("Automatically scrolls down when new items are added and scrollbar is at bottom."));
-
- m_displayAppCB = new QCheckBox(Tr::tr("Group results by application"));
-
- m_processArgsCB = new QCheckBox(Tr::tr("Process arguments"));
- m_processArgsCB->setToolTip(
- Tr::tr("Allow passing arguments specified on the respective run configuration.\n"
- "Warning: this is an experimental feature and might lead to failing to execute the test executable."));
-
- m_runAfterBuildCB = new QComboBox;
- m_runAfterBuildCB->setToolTip(Tr::tr("Runs chosen tests automatically if a build succeeded."));
- m_runAfterBuildCB->addItem(Tr::tr("None"));
- m_runAfterBuildCB->addItem(Tr::tr("All"));
- m_runAfterBuildCB->addItem(Tr::tr("Selected"));
-
auto timeoutLabel = new QLabel(Tr::tr("Timeout:"));
timeoutLabel->setToolTip(Tr::tr("Timeout used when executing each test case."));
- m_timeoutSpin = new QSpinBox;
- m_timeoutSpin->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- m_timeoutSpin->setRange(5, 36000);
- m_timeoutSpin->setValue(60);
- m_timeoutSpin->setSuffix(Tr::tr(" s"));
- m_timeoutSpin->setToolTip(
- Tr::tr("Timeout used when executing test cases. This will apply "
- "for each test case on its own, not the whole project."));
-
m_frameworkTreeWidget = new QTreeWidget;
m_frameworkTreeWidget->setRootIsDecorated(false);
m_frameworkTreeWidget->setHeaderHidden(false);
@@ -162,21 +79,22 @@ TestSettingsWidget::TestSettingsWidget(QWidget *parent)
onClicked([] { AutotestPlugin::clearChoiceCache(); }, this)
};
+ TestSettings &s = *TestSettings::instance();
Group generalGroup {
title(Tr::tr("General")),
Column {
- m_omitInternalMsgCB,
- m_omitRunConfigWarnCB,
- m_limitResultOutputCB,
- Row { m_limitResultDescriptionCb, m_limitResultDescriptionSpinBox, st },
- m_openResultsOnStartCB,
- m_openResultsOnFinishCB,
- Row { Space(20), m_openResultsOnFailCB },
- m_autoScrollCB,
- m_displayAppCB,
- m_processArgsCB,
- Row { Tr::tr("Automatically run"), m_runAfterBuildCB, st },
- Row { timeoutLabel, m_timeoutSpin, st },
+ s.omitInternalMsg,
+ s.omitRunConfigWarn,
+ s.limitResultOutput,
+ Row { s.limitResultDescription, s.resultDescriptionMaxSize, st },
+ s.popupOnStart,
+ s.popupOnFinish,
+ Row { Space(20), s.popupOnFail },
+ s.autoScroll,
+ s.displayApplication,
+ s.processArgs,
+ Row { Tr::tr("Automatically run"), s.runAfterBuild, st },
+ Row { timeoutLabel, s.timeout, st },
Row { resetChoicesButton, st }
}
};
@@ -199,50 +117,38 @@ TestSettingsWidget::TestSettingsWidget(QWidget *parent)
connect(m_frameworkTreeWidget, &QTreeWidget::itemChanged,
this, &TestSettingsWidget::onFrameworkItemChanged);
- connect(m_openResultsOnFinishCB, &QCheckBox::toggled,
- m_openResultsOnFailCB, &QCheckBox::setEnabled);
- connect(m_limitResultDescriptionCb, &QCheckBox::toggled,
- m_limitResultDescriptionSpinBox, &QSpinBox::setEnabled);
-}
-void TestSettingsWidget::setSettings(const TestSettings &settings)
-{
- m_timeoutSpin->setValue(settings.timeout / 1000); // we store milliseconds
- m_omitInternalMsgCB->setChecked(settings.omitInternalMssg);
- m_omitRunConfigWarnCB->setChecked(settings.omitRunConfigWarn);
- m_limitResultOutputCB->setChecked(settings.limitResultOutput);
- m_limitResultDescriptionCb->setChecked(settings.limitResultDescription);
- m_limitResultDescriptionSpinBox->setEnabled(settings.limitResultDescription);
- m_limitResultDescriptionSpinBox->setValue(settings.resultDescriptionMaxSize);
- m_autoScrollCB->setChecked(settings.autoScroll);
- m_processArgsCB->setChecked(settings.processArgs);
- m_displayAppCB->setChecked(settings.displayApplication);
- m_openResultsOnStartCB->setChecked(settings.popupOnStart);
- m_openResultsOnFinishCB->setChecked(settings.popupOnFinish);
- m_openResultsOnFailCB->setChecked(settings.popupOnFail);
- m_runAfterBuildCB->setCurrentIndex(int(settings.runAfterBuild));
- populateFrameworksListWidget(settings.frameworks, settings.tools);
-}
+ populateFrameworksListWidget(s.frameworks, s.tools);
-TestSettings TestSettingsWidget::settings() const
-{
- TestSettings result;
- result.timeout = m_timeoutSpin->value() * 1000; // we display seconds
- result.omitInternalMssg = m_omitInternalMsgCB->isChecked();
- result.omitRunConfigWarn = m_omitRunConfigWarnCB->isChecked();
- result.limitResultOutput = m_limitResultOutputCB->isChecked();
- result.limitResultDescription = m_limitResultDescriptionCb->isChecked();
- result.resultDescriptionMaxSize = m_limitResultDescriptionSpinBox->value();
- result.autoScroll = m_autoScrollCB->isChecked();
- result.processArgs = m_processArgsCB->isChecked();
- result.displayApplication = m_displayAppCB->isChecked();
- result.popupOnStart = m_openResultsOnStartCB->isChecked();
- result.popupOnFinish = m_openResultsOnFinishCB->isChecked();
- result.popupOnFail = m_openResultsOnFailCB->isChecked();
- result.runAfterBuild = RunAfterBuildMode(m_runAfterBuildCB->currentIndex());
- testSettings(result);
- testToolsSettings(result);
- return result;
+ setOnApply([this] {
+ TestSettings &s = *TestSettings::instance();
+
+ NonAspectSettings tmp;
+ testSettings(tmp);
+ testToolsSettings(tmp);
+
+ const QList<Utils::Id> changedIds = Utils::filtered(tmp.frameworksGrouping.keys(),
+ [&tmp, &s](Utils::Id id) {
+ return tmp.frameworksGrouping[id] != s.frameworksGrouping[id];
+ });
+
+ testSettings(s);
+ testToolsSettings(s);
+ s.toSettings(Core::ICore::settings());
+
+ for (ITestFramework *framework : TestFrameworkManager::registeredFrameworks()) {
+ framework->setActive(s.frameworks.value(framework->id(), false));
+ framework->setGrouping(s.frameworksGrouping.value(framework->id(), false));
+ }
+
+ for (ITestTool *testTool : TestFrameworkManager::registeredTestTools())
+ testTool->setActive(s.tools.value(testTool->id(), false));
+
+ TestTreeModel::instance()->synchronizeTestFrameworks();
+ TestTreeModel::instance()->synchronizeTestTools();
+ if (!changedIds.isEmpty())
+ TestTreeModel::instance()->rebuild(changedIds);
+ });
}
enum TestBaseInfo
@@ -283,7 +189,7 @@ void TestSettingsWidget::populateFrameworksListWidget(const QHash<Id, bool> &fra
}
}
-void TestSettingsWidget::testSettings(TestSettings &settings) const
+void TestSettingsWidget::testSettings(NonAspectSettings &settings) const
{
const QAbstractItemModel *model = m_frameworkTreeWidget->model();
QTC_ASSERT(model, return);
@@ -298,7 +204,7 @@ void TestSettingsWidget::testSettings(TestSettings &settings) const
}
}
-void TestSettingsWidget::testToolsSettings(TestSettings &settings) const
+void TestSettingsWidget::testToolsSettings(NonAspectSettings &settings) const
{
const QAbstractItemModel *model = m_frameworkTreeWidget->model();
QTC_ASSERT(model, return);
@@ -343,50 +249,16 @@ void TestSettingsWidget::onFrameworkItemChanged()
|| (mixed == (ITestBase::Framework | ITestBase::Tool)));
}
-TestSettingsPage::TestSettingsPage(TestSettings *settings)
- : m_settings(settings)
+// TestSettingsPage
+
+TestSettingsPage::TestSettingsPage()
{
setId(Constants::AUTOTEST_SETTINGS_ID);
setDisplayName(Tr::tr("General"));
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Testing"));
setCategoryIconPath(":/autotest/images/settingscategory_autotest.png");
+ setWidgetCreator([] { return new TestSettingsWidget; });
}
-QWidget *TestSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new TestSettingsWidget;
- m_widget->setSettings(*m_settings);
- }
- return m_widget;
-}
-
-void TestSettingsPage::apply()
-{
- if (!m_widget) // page was not shown at all
- return;
- const TestSettings newSettings = m_widget->settings();
- const QList<Id> changedIds = Utils::filtered(newSettings.frameworksGrouping.keys(),
- [newSettings, this](const Id &id) {
- return newSettings.frameworksGrouping[id] != m_settings->frameworksGrouping[id];
- });
- *m_settings = newSettings;
- m_settings->toSettings(Core::ICore::settings());
-
- for (ITestFramework *framework : TestFrameworkManager::registeredFrameworks()) {
- framework->setActive(m_settings->frameworks.value(framework->id(), false));
- framework->setGrouping(m_settings->frameworksGrouping.value(framework->id(), false));
- }
-
- for (ITestTool *testTool : TestFrameworkManager::registeredTestTools())
- testTool->setActive(m_settings->tools.value(testTool->id(), false));
-
- TestTreeModel::instance()->synchronizeTestFrameworks();
- TestTreeModel::instance()->synchronizeTestTools();
- if (!changedIds.isEmpty())
- TestTreeModel::instance()->rebuild(changedIds);
-}
-
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/testsettingspage.h b/src/plugins/autotest/testsettingspage.h
index 064a9504c9..03eb214097 100644
--- a/src/plugins/autotest/testsettingspage.h
+++ b/src/plugins/autotest/testsettingspage.h
@@ -5,28 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
-namespace Autotest {
-namespace Internal {
-
-struct TestSettings;
-class TestSettingsWidget;
+namespace Autotest::Internal {
class TestSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
public:
- explicit TestSettingsPage(TestSettings *settings);
-
- QWidget *widget() override;
- void apply() override;
- void finish() override { }
-
-private:
- TestSettings *m_settings;
- QPointer<TestSettingsWidget> m_widget;
+ TestSettingsPage();
};
-} // namespace Internal
-} // namespace Autotest
+} // Autotest::Internal
diff --git a/src/plugins/autotest/testtreeitem.cpp b/src/plugins/autotest/testtreeitem.cpp
index 884544e3b2..f18adb812d 100644
--- a/src/plugins/autotest/testtreeitem.cpp
+++ b/src/plugins/autotest/testtreeitem.cpp
@@ -224,11 +224,11 @@ void TestTreeItem::markForRemovalRecursively(bool mark)
childItem(row)->markForRemovalRecursively(mark);
}
-void TestTreeItem::markForRemovalRecursively(const FilePath &filepath)
+void TestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
{
- bool mark = filePath() == filepath;
- forFirstLevelChildItems([&mark, &filepath](TestTreeItem *child) {
- child->markForRemovalRecursively(filepath);
+ bool mark = filePaths.contains(filePath());
+ forFirstLevelChildItems([&mark, &filePaths](TestTreeItem *child) {
+ child->markForRemovalRecursively(filePaths);
mark &= child->markedForRemoval();
});
markForRemoval(mark);
diff --git a/src/plugins/autotest/testtreeitem.h b/src/plugins/autotest/testtreeitem.h
index e51f48b211..3737d2a360 100644
--- a/src/plugins/autotest/testtreeitem.h
+++ b/src/plugins/autotest/testtreeitem.h
@@ -115,7 +115,7 @@ public:
void setProFile(const Utils::FilePath &proFile) { m_proFile = proFile; }
void markForRemoval(bool mark);
void markForRemovalRecursively(bool mark);
- virtual void markForRemovalRecursively(const Utils::FilePath &filepath);
+ virtual void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths);
virtual bool removeOnSweepIfEmpty() const { return type() == GroupNode; }
bool markedForRemoval() const { return m_status == MarkedForRemoval; }
bool newlyAdded() const { return m_status == NewlyAdded; }
diff --git a/src/plugins/autotest/testtreemodel.cpp b/src/plugins/autotest/testtreemodel.cpp
index 8842734f0c..5e7fcb85a8 100644
--- a/src/plugins/autotest/testtreemodel.cpp
+++ b/src/plugins/autotest/testtreemodel.cpp
@@ -10,12 +10,16 @@
#include "testprojectsettings.h"
#include <cppeditor/cppmodelmanager.h>
+
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+
#include <qmljs/qmljsmodelmanagerinterface.h>
+
#include <texteditor/texteditor.h>
+
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
@@ -38,7 +42,7 @@ TestTreeModel::TestTreeModel(TestCodeParser *parser) :
connect(m_parser, &TestCodeParser::aboutToPerformFullParse, this,
&TestTreeModel::removeAllTestItems, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::testParseResultReady,
- this, &TestTreeModel::onParseResultReady, Qt::QueuedConnection);
+ this, &TestTreeModel::onParseResultReady);
connect(m_parser, &TestCodeParser::parsingFinished,
this, &TestTreeModel::sweep, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::parsingFailed,
@@ -70,8 +74,8 @@ void TestTreeModel::setupParsingConnections()
m_parser->setDirty();
m_parser->setState(TestCodeParser::Idle);
- SessionManager *sm = SessionManager::instance();
- connect(sm, &SessionManager::startupProjectChanged, this, [this, sm](Project *project) {
+ ProjectManager *sm = ProjectManager::instance();
+ connect(sm, &ProjectManager::startupProjectChanged, this, [this, sm](Project *project) {
synchronizeTestFrameworks(); // we might have project settings
m_parser->onStartupProjectChanged(project);
removeAllTestToolItems();
@@ -96,8 +100,8 @@ void TestTreeModel::setupParsingConnections()
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
connect(cppMM, &CppEditor::CppModelManager::aboutToRemoveFiles,
this, [this](const QStringList &files) {
- const FilePaths filesToRemove = FileUtils::toFilePathList(files);
- removeFiles(filesToRemove);
+ markForRemoval(transform<QSet>(files, &FilePath::fromString));
+ sweep();
}, Qt::QueuedConnection);
connect(cppMM, &CppEditor::CppModelManager::projectPartsUpdated,
m_parser, &TestCodeParser::onProjectPartsUpdated);
@@ -105,11 +109,11 @@ void TestTreeModel::setupParsingConnections()
QmlJS::ModelManagerInterface *qmlJsMM = QmlJS::ModelManagerInterface::instance();
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
- connect(qmlJsMM,
- &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
- this,
- &TestTreeModel::removeFiles,
- Qt::QueuedConnection);
+ connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
+ this, [this](const FilePaths &filePaths) {
+ markForRemoval(Utils::toSet(filePaths));
+ sweep();
+ }, Qt::QueuedConnection);
connectionsInitialized = true;
}
@@ -226,7 +230,7 @@ static QList<ITestTreeItem *> testItemsByName(TestTreeItem *root, const QString
void TestTreeModel::onTargetChanged(Target *target)
{
if (target && target->buildSystem()) {
- const Target *topLevelTarget = SessionManager::startupProject()->targets().first();
+ const Target *topLevelTarget = ProjectManager::startupProject()->targets().first();
connect(topLevelTarget->buildSystem(), &BuildSystem::testInformationUpdated,
this, &TestTreeModel::onBuildSystemTestsUpdated, Qt::UniqueConnection);
disconnect(target->project(), &Project::activeTargetChanged,
@@ -236,7 +240,7 @@ void TestTreeModel::onTargetChanged(Target *target)
void TestTreeModel::onBuildSystemTestsUpdated()
{
- const BuildSystem *bs = SessionManager::startupBuildSystem();
+ const BuildSystem *bs = ProjectManager::startupBuildSystem();
if (!bs || !bs->project())
return;
@@ -333,7 +337,7 @@ void TestTreeModel::synchronizeTestFrameworks()
void TestTreeModel::synchronizeTestTools()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
TestTools tools;
if (!project || AutotestPlugin::projectSettings(project)->useGlobalSettings()) {
tools = Utils::filtered(TestFrameworkManager::registeredTestTools(),
@@ -459,13 +463,6 @@ void TestTreeModel::clearFailedMarks()
m_failedStateCache.clear();
}
-void TestTreeModel::removeFiles(const FilePaths &files)
-{
- for (const FilePath &file : files)
- markForRemoval(file);
- sweep();
-}
-
void TestTreeModel::markAllFrameworkItemsForRemoval()
{
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
@@ -475,15 +472,12 @@ void TestTreeModel::markAllFrameworkItemsForRemoval()
}
}
-void TestTreeModel::markForRemoval(const FilePath &filePath)
+void TestTreeModel::markForRemoval(const QSet<Utils::FilePath> &filePaths)
{
- if (filePath.isEmpty())
- return;
-
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
for (int childRow = frameworkRoot->childCount() - 1; childRow >= 0; --childRow) {
TestTreeItem *child = frameworkRoot->childItem(childRow);
- child->markForRemovalRecursively(filePath);
+ child->markForRemovalRecursively(filePaths);
}
}
}
diff --git a/src/plugins/autotest/testtreemodel.h b/src/plugins/autotest/testtreemodel.h
index b8104e8b43..c48c957d99 100644
--- a/src/plugins/autotest/testtreemodel.h
+++ b/src/plugins/autotest/testtreemodel.h
@@ -66,7 +66,7 @@ public:
#endif
void markAllFrameworkItemsForRemoval();
- void markForRemoval(const Utils::FilePath &filePath);
+ void markForRemoval(const QSet<Utils::FilePath> &filePaths);
void sweep();
signals:
@@ -83,7 +83,6 @@ private:
void handleParseResult(const TestParseResult *result, TestTreeItem *rootNode);
void removeAllTestItems();
void removeAllTestToolItems();
- void removeFiles(const Utils::FilePaths &files);
bool sweepChildren(TestTreeItem *item);
void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled);
void revalidateCheckState(ITestTreeItem *item);
diff --git a/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp b/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp
index 0375ce9ac6..113212be5f 100644
--- a/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp
+++ b/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp
@@ -14,6 +14,7 @@
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
+using namespace Utils;
namespace AutotoolsProjectManager::Internal {
@@ -140,7 +141,7 @@ void AutotoolsBuildSystem::updateCppCodeModel()
if (cxxflags.isEmpty())
cxxflags = cflags;
- const QString includeFileBaseDir = projectDirectory().toString();
+ const FilePath includeFileBaseDir = projectDirectory();
rpp.setFlagsForC({kitInfo.cToolChain, cflags, includeFileBaseDir});
rpp.setFlagsForCxx({kitInfo.cxxToolChain, cxxflags, includeFileBaseDir});
diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
index 09fbd66647..e1cac6b186 100644
--- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp
+++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp
@@ -5,14 +5,16 @@
#include "autotoolsprojectmanagertr.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QDir>
#include <QFile>
#include <QFileInfo>
+using namespace Utils;
+
namespace AutotoolsProjectManager::Internal {
MakefileParser::MakefileParser(const QString &makefile) : m_makefile(makefile)
@@ -423,7 +425,7 @@ QStringList MakefileParser::parseTermsAfterAssign(const QString &line)
if (assignPos <= 0 || assignPos >= line.size())
return QStringList();
- const QStringList parts = Utils::ProcessArgs::splitArgs(line.mid(assignPos));
+ const QStringList parts = ProcessArgs::splitArgs(line.mid(assignPos), HostOsInfo::hostOs());
QStringList result;
for (int i = 0; i < parts.count(); ++i) {
const QString cur = parts.at(i);
diff --git a/src/plugins/baremetal/baremetaldebugsupport.cpp b/src/plugins/baremetal/baremetaldebugsupport.cpp
index db0c376a5a..4475be0b96 100644
--- a/src/plugins/baremetal/baremetaldebugsupport.cpp
+++ b/src/plugins/baremetal/baremetaldebugsupport.cpp
@@ -11,6 +11,7 @@
#include "idebugserverprovider.h"
#include <debugger/debuggerkitinformation.h>
+#include <debugger/debuggerruncontrol.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
@@ -23,8 +24,8 @@
#include <projectexplorer/toolchain.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace Debugger;
using namespace ProjectExplorer;
diff --git a/src/plugins/baremetal/baremetaldebugsupport.h b/src/plugins/baremetal/baremetaldebugsupport.h
index f0addb4729..ca16c1ab7e 100644
--- a/src/plugins/baremetal/baremetaldebugsupport.h
+++ b/src/plugins/baremetal/baremetaldebugsupport.h
@@ -3,12 +3,11 @@
#pragma once
-#include <debugger/debuggerruncontrol.h>
+#include <projectexplorer/runcontrol.h>
namespace BareMetal::Internal {
-class BareMetalDebugSupportFactory final
- : public ProjectExplorer::RunWorkerFactory
+class BareMetalDebugSupportFactory final : public ProjectExplorer::RunWorkerFactory
{
public:
BareMetalDebugSupportFactory();
diff --git a/src/plugins/baremetal/baremetaldevice.cpp b/src/plugins/baremetal/baremetaldevice.cpp
index 4c8d7ea8c6..d7754ebc9a 100644
--- a/src/plugins/baremetal/baremetaldevice.cpp
+++ b/src/plugins/baremetal/baremetaldevice.cpp
@@ -24,7 +24,6 @@ const char debugServerProviderIdKeyC[] = "IDebugServerProviderId";
BareMetalDevice::BareMetalDevice()
{
setDisplayType(Tr::tr("Bare Metal"));
- setDefaultDisplayName(defaultDisplayName());
setOsType(Utils::OsTypeOther);
}
diff --git a/src/plugins/baremetal/debugserverproviderssettingspage.cpp b/src/plugins/baremetal/debugserverproviderssettingspage.cpp
index 097d08033a..58af3578a9 100644
--- a/src/plugins/baremetal/debugserverproviderssettingspage.cpp
+++ b/src/plugins/baremetal/debugserverproviderssettingspage.cpp
@@ -257,8 +257,6 @@ class DebugServerProvidersSettingsWidget final : public Core::IOptionsPageWidget
public:
DebugServerProvidersSettingsWidget();
- void apply() final { m_model.apply(); }
-
void providerSelectionChanged();
void removeProvider();
void updateState();
@@ -363,6 +361,8 @@ DebugServerProvidersSettingsWidget::DebugServerProvidersSettingsWidget()
this, &DebugServerProvidersSettingsWidget::removeProvider);
updateState();
+
+ setOnApply([this] { m_model.apply(); });
}
void DebugServerProvidersSettingsWidget::providerSelectionChanged()
diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp
index 336b606d79..222d22ade9 100644
--- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp
@@ -8,6 +8,8 @@
#include <baremetal/baremetaltr.h>
#include <baremetal/debugserverprovidermanager.h>
+#include <debugger/debuggerruncontrol.h>
+
#include <projectexplorer/runconfigurationaspects.h>
#include <utils/environment.h>
diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h
index eac2ca6e68..1c23d49995 100644
--- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h
+++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h
@@ -40,7 +40,7 @@ public:
bool aboutToRun(Debugger::DebuggerRunTool *runTool,
QString &errorMessage) const final;
ProjectExplorer::RunWorker *targetRunner(
- ProjectExplorer::RunControl *runControl) const final;
+ ProjectExplorer::RunControl *runControl) const override;
bool isValid() const override;
virtual QSet<StartupMode> supportedStartupModes() const = 0;
diff --git a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h
index 2754a3d91a..98ca17451c 100644
--- a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h
+++ b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h
@@ -19,6 +19,11 @@ class GenericGdbServerProvider final : public GdbServerProvider
private:
GenericGdbServerProvider();
QSet<StartupMode> supportedStartupModes() const final;
+ ProjectExplorer::RunWorker *targetRunner(ProjectExplorer::RunControl *runControl) const final {
+ Q_UNUSED(runControl)
+ // Generic Runner assumes GDB Server already running
+ return nullptr;
+ }
friend class GenericGdbServerProviderConfigWidget;
friend class GenericGdbServerProviderFactory;
diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
index d631923788..c1ee31b710 100644
--- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp
@@ -9,8 +9,8 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QComboBox>
diff --git a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
index 334f31132c..ca7f25e56f 100644
--- a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp
@@ -9,8 +9,8 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QComboBox>
@@ -64,7 +64,7 @@ QString OpenOcdGdbServerProvider::channelString() const
// otherwise running will be stuck.
CommandLine cmd = command();
QStringList args = {"|", cmd.executable().toString()};
- for (const QString &a : ProcessArgs::splitArgs(cmd.arguments())) {
+ for (const QString &a : ProcessArgs::splitArgs(cmd.arguments(), HostOsInfo::hostOs())) {
if (a.startsWith('\"') && a.endsWith('\"'))
args << a;
else
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp b/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp
index 773ec41066..faa32fea08 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp
+++ b/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp
@@ -9,7 +9,7 @@
#include <debugger/debuggerkitinformation.h>
#include <debugger/debuggerruncontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <QFileInfo>
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
index b95c333600..a266ed7bfd 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
+++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp
@@ -14,6 +14,7 @@
#include <baremetal/debugserverprovidermanager.h>
#include <debugger/debuggerkitinformation.h>
+#include <debugger/debuggerruncontrol.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfigurationaspects.h>
@@ -357,12 +358,12 @@ UvscServerProviderRunner::UvscServerProviderRunner(ProjectExplorer::RunControl *
m_process.setCommand(runnable.command);
- connect(&m_process, &QtcProcess::started, this, [this] {
+ connect(&m_process, &Process::started, this, [this] {
ProcessHandle pid(m_process.processId());
this->runControl()->setApplicationProcessHandle(pid);
reportStarted();
});
- connect(&m_process, &QtcProcess::done, this, [this] {
+ connect(&m_process, &Process::done, this, [this] {
appendMessage(m_process.exitMessage(), NormalMessageFormat);
reportStopped();
});
diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
index d7b3bcafba..4c14ee7b7c 100644
--- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
+++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h
@@ -10,7 +10,7 @@
#include <projectexplorer/runcontrol.h> // for RunWorker
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Utils { class PathChooser; }
@@ -127,7 +127,7 @@ private:
void start() final;
void stop() final;
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
};
} // namespace Internal
diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp
index 80b5491ddf..c6653fefbb 100644
--- a/src/plugins/baremetal/iarewtoolchain.cpp
+++ b/src/plugins/baremetal/iarewtoolchain.cpp
@@ -15,8 +15,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -78,7 +78,7 @@ static Macros dumpPredefinedMacros(const FilePath &compiler, const QStringList &
const QString outpath = fakeIn.fileName() + ".tmp";
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
@@ -130,7 +130,7 @@ static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Id languageId
cmd.addArg("--preinclude");
cmd.addArg(".");
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand(cmd);
diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp
index 4c023ce306..3bb2423ab2 100644
--- a/src/plugins/baremetal/keiltoolchain.cpp
+++ b/src/plugins/baremetal/keiltoolchain.cpp
@@ -15,8 +15,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -113,7 +113,7 @@ static Macros dumpMcsPredefinedMacros(const FilePath &compiler, const Environmen
fakeIn.close();
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand({compiler, {fakeIn.fileName()}});
@@ -190,7 +190,7 @@ static Macros dumpC166PredefinedMacros(const FilePath &compiler, const Environme
fakeIn.close();
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
@@ -255,7 +255,7 @@ static Macros dumpC166PredefinedMacros(const FilePath &compiler, const Environme
static Macros dumpArmPredefinedMacros(const FilePath &compiler, const QStringList &extraArgs, const Environment &env)
{
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp
index a16372eff9..a35a468d20 100644
--- a/src/plugins/baremetal/sdcctoolchain.cpp
+++ b/src/plugins/baremetal/sdcctoolchain.cpp
@@ -15,8 +15,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -65,7 +65,7 @@ static Macros dumpPredefinedMacros(const FilePath &compiler, const Environment &
return {};
fakeIn.close();
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand({compiler, {compilerTargetFlag(abi), "-dM", "-E", fakeIn.fileName()}});
@@ -86,7 +86,7 @@ static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Environment &
if (!compiler.exists())
return {};
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setTimeoutS(10);
cpp.setCommand({compiler, {compilerTargetFlag(abi), "--print-search-dirs"}});
diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp
index 0b07bcd9ec..303b7e21c2 100644
--- a/src/plugins/bazaar/bazaarclient.cpp
+++ b/src/plugins/bazaar/bazaarclient.cpp
@@ -29,13 +29,13 @@ namespace Bazaar::Internal {
class BazaarDiffConfig : public VcsBaseEditorConfig
{
public:
- BazaarDiffConfig(BazaarSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit BazaarDiffConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("-w", Tr::tr("Ignore Whitespace")),
- &settings.diffIgnoreWhiteSpace);
+ &settings().diffIgnoreWhiteSpace);
mapSetting(addToggleButton("-B", Tr::tr("Ignore Blank Lines")),
- &settings.diffIgnoreBlankLines);
+ &settings().diffIgnoreBlankLines);
}
QStringList arguments() const override
@@ -54,18 +54,18 @@ public:
class BazaarLogConfig : public VcsBaseEditorConfig
{
public:
- BazaarLogConfig(BazaarSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ BazaarLogConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("--verbose", Tr::tr("Verbose"),
Tr::tr("Show files changed in each revision.")),
- &settings.logVerbose);
+ &settings().logVerbose);
mapSetting(addToggleButton("--forward", Tr::tr("Forward"),
Tr::tr("Show from oldest to newest.")),
- &settings.logForward);
+ &settings().logForward);
mapSetting(addToggleButton("--include-merges", Tr::tr("Include Merges"),
Tr::tr("Show merged revisions.")),
- &settings.logIncludeMerges);
+ &settings().logIncludeMerges);
const QList<ChoiceItem> logChoices = {
{Tr::tr("Detailed"), "long"},
@@ -74,18 +74,14 @@ public:
{Tr::tr("GNU Change Log"), "gnu-changelog"}
};
mapSetting(addChoices(Tr::tr("Format"), { "--log-format=%1" }, logChoices),
- &settings.logFormat);
+ &settings().logFormat);
}
};
-BazaarClient::BazaarClient(BazaarSettings *settings) : VcsBaseClient(settings)
+BazaarClient::BazaarClient() : VcsBaseClient(&Internal::settings())
{
- setDiffConfigCreator([settings](QToolBar *toolBar) {
- return new BazaarDiffConfig(*settings, toolBar);
- });
- setLogConfigCreator([settings](QToolBar *toolBar) {
- return new BazaarLogConfig(*settings, toolBar);
- });
+ setDiffConfigCreator([](QToolBar *toolBar) { return new BazaarDiffConfig(toolBar); });
+ setLogConfigCreator([](QToolBar *toolBar) { return new BazaarLogConfig(toolBar); });
}
BranchInfo BazaarClient::synchronousBranchQuery(const FilePath &repositoryRoot) const
diff --git a/src/plugins/bazaar/bazaarclient.h b/src/plugins/bazaar/bazaarclient.h
index b84484b687..5b6ce1e6d4 100644
--- a/src/plugins/bazaar/bazaarclient.h
+++ b/src/plugins/bazaar/bazaarclient.h
@@ -9,12 +9,10 @@
namespace Bazaar::Internal {
-class BazaarSettings;
-
class BazaarClient : public VcsBase::VcsBaseClient
{
public:
- explicit BazaarClient(BazaarSettings *settings);
+ BazaarClient();
BranchInfo synchronousBranchQuery(const Utils::FilePath &repositoryRoot) const;
bool synchronousUncommit(const Utils::FilePath &workingDir,
diff --git a/src/plugins/bazaar/bazaarcommitwidget.cpp b/src/plugins/bazaar/bazaarcommitwidget.cpp
index 79d0b56b5a..379fca6661 100644
--- a/src/plugins/bazaar/bazaarcommitwidget.cpp
+++ b/src/plugins/bazaar/bazaarcommitwidget.cpp
@@ -49,7 +49,7 @@ public:
emailLineEdit = new QLineEdit;
fixedBugsLineEdit = new QLineEdit;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("General Information")),
@@ -65,8 +65,9 @@ public:
Tr::tr("Email:"), emailLineEdit, br,
Tr::tr("Fixed bugs:"), fixedBugsLineEdit
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
}
QLineEdit *branchLineEdit;
diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp
index a0f980606d..d992d70eba 100644
--- a/src/plugins/bazaar/bazaarplugin.cpp
+++ b/src/plugins/bazaar/bazaarplugin.cpp
@@ -215,9 +215,8 @@ public:
void createRepositoryActions(const Core::Context &context);
// Variables
- BazaarSettings m_settings;
- BazaarClient m_client{&m_settings};
- BazaarSettingsPage m_settingPage{&m_settings};
+ BazaarSettings m_setting;
+ BazaarClient m_client;
VcsSubmitEditorFactory m_submitEditorFactory {
submitEditorParameters,
@@ -286,7 +285,7 @@ public:
dryRunBtn->setToolTip(Tr::tr("Test the outcome of removing the last committed revision, without actually removing anything."));
buttonBox->addButton(dryRunBtn, QDialogButtonBox::ApplyRole);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
keepTagsCheckBox, br,
@@ -372,7 +371,7 @@ BazaarPluginPrivate::BazaarPluginPrivate()
toolsMenu->addMenu(m_bazaarContainer);
m_menuAction = m_bazaarContainer->menu()->menuAction();
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
void BazaarPluginPrivate::createFileActions(const Context &context)
@@ -523,7 +522,7 @@ void BazaarPluginPrivate::logRepository()
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
QStringList extraOptions;
- extraOptions += "--limit=" + QString::number(m_settings.logCount.value());
+ extraOptions += "--limit=" + QString::number(settings().logCount());
m_client.log(state.topLevel(), QStringList(), extraOptions);
}
@@ -705,8 +704,8 @@ void BazaarPluginPrivate::showCommitWidget(const QList<VcsBaseClient::StatusItem
const BranchInfo branch = m_client.synchronousBranchQuery(m_submitRepository);
commitEditor->setFields(m_submitRepository, branch,
- m_settings.userName.value(),
- m_settings.userEmail.value(), status);
+ settings().userName(),
+ settings().userEmail(), status);
}
void BazaarPluginPrivate::diffFromEditorSelected(const QStringList &files)
@@ -872,7 +871,7 @@ bool BazaarPluginPrivate::managesFile(const FilePath &workingDirectory, const QS
bool BazaarPluginPrivate::isConfigured() const
{
- const FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
return !binary.isEmpty() && binary.isExecutableFile();
}
diff --git a/src/plugins/bazaar/bazaarsettings.cpp b/src/plugins/bazaar/bazaarsettings.cpp
index 7149f93251..1b2840084c 100644
--- a/src/plugins/bazaar/bazaarsettings.cpp
+++ b/src/plugins/bazaar/bazaarsettings.cpp
@@ -16,13 +16,23 @@ using namespace Utils;
namespace Bazaar::Internal {
+static BazaarSettings *theSettings;
+
+BazaarSettings &settings()
+{
+ return *theSettings;
+}
+
BazaarSettings::BazaarSettings()
{
+ theSettings = this;
+
setSettingsGroup(Constants::BAZAAR);
- setAutoApply(false);
+ setId(VcsBase::Constants::VCS_ID_BAZAAR);
+ setDisplayName(Tr::tr("Bazaar"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setDefaultValue(Constants::BAZAARDEFAULT);
binaryPath.setDisplayName(Tr::tr("Bazaar Command"));
@@ -66,42 +76,27 @@ BazaarSettings::BazaarSettings()
registerAspect(&logCount);
timeout.setLabelText(Tr::tr("Timeout:"));
timeout.setSuffix(Tr::tr("s"));
-}
-
-// BazaarSettingsPage
-
-BazaarSettingsPage::BazaarSettingsPage(BazaarSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_BAZAAR);
- setDisplayName(Tr::tr("Bazaar"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- BazaarSettings &s = *settings;
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
Column {
Group {
title(Tr::tr("Configuration")),
- Row { s.binaryPath }
+ Row { binaryPath }
},
Group {
title(Tr::tr("User")),
Form {
- s.userName,
- s.userEmail
+ userName, br,
+ userEmail
}
},
Group {
title(Tr::tr("Miscellaneous")),
- Row {
- s.logCount,
- s.timeout,
- st
- }
+ Row { logCount, timeout, st }
},
st
}.attachTo(widget);
diff --git a/src/plugins/bazaar/bazaarsettings.h b/src/plugins/bazaar/bazaarsettings.h
index a10828b42d..24643a2d4c 100644
--- a/src/plugins/bazaar/bazaarsettings.h
+++ b/src/plugins/bazaar/bazaarsettings.h
@@ -3,8 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
-
#include <vcsbase/vcsbaseclientsettings.h>
namespace Bazaar::Internal {
@@ -22,10 +20,6 @@ public:
Utils::StringAspect logFormat;
};
-class BazaarSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit BazaarSettingsPage(BazaarSettings *settings);
-};
+BazaarSettings &settings();
} // Bazaar::Internal
diff --git a/src/plugins/bazaar/pullorpushdialog.cpp b/src/plugins/bazaar/pullorpushdialog.cpp
index 10a4ce0452..4c2cd99e7c 100644
--- a/src/plugins/bazaar/pullorpushdialog.cpp
+++ b/src/plugins/bazaar/pullorpushdialog.cpp
@@ -72,7 +72,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
m_localCheckBox->setVisible(false);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Branch Location")),
diff --git a/src/plugins/beautifier/CMakeLists.txt b/src/plugins/beautifier/CMakeLists.txt
index d721f1ef20..cdfacccce1 100644
--- a/src/plugins/beautifier/CMakeLists.txt
+++ b/src/plugins/beautifier/CMakeLists.txt
@@ -19,7 +19,6 @@ add_qtc_plugin(Beautifier
configurationdialog.cpp configurationdialog.h
configurationeditor.cpp configurationeditor.h
configurationpanel.cpp configurationpanel.h
- generaloptionspage.cpp generaloptionspage.h
generalsettings.cpp generalsettings.h
uncrustify/uncrustify.cpp uncrustify/uncrustify.h
uncrustify/uncrustifyconstants.h
diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/abstractsettings.cpp
index 0b96e1579e..fa621fdebc 100644
--- a/src/plugins/beautifier/abstractsettings.cpp
+++ b/src/plugins/beautifier/abstractsettings.cpp
@@ -14,7 +14,7 @@
#include <utils/fileutils.h>
#include <utils/genericconstants.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFile>
#include <QFileInfo>
@@ -34,7 +34,7 @@ class VersionUpdater
public:
VersionUpdater()
{
- QObject::connect(&m_process, &QtcProcess::done, &m_process, [this] {
+ QObject::connect(&m_process, &Process::done, &m_process, [this] {
if (m_process.result() != ProcessResult::FinishedWithSuccess)
return;
@@ -77,7 +77,7 @@ private:
}
QRegularExpression m_versionRegExp;
- mutable QtcProcess m_process;
+ mutable Process m_process;
QVersionNumber m_versionNumber;
};
diff --git a/src/plugins/beautifier/abstractsettings.h b/src/plugins/beautifier/abstractsettings.h
index d386313e1b..25baa83885 100644
--- a/src/plugins/beautifier/abstractsettings.h
+++ b/src/plugins/beautifier/abstractsettings.h
@@ -3,6 +3,7 @@
#pragma once
+#include <utils/aspects.h>
#include <utils/filepath.h>
#include <QCoreApplication>
@@ -28,7 +29,7 @@ namespace Beautifier::Internal {
class VersionUpdater;
-class AbstractSettings : public QObject
+class AbstractSettings : public Utils::AspectContainer
{
Q_OBJECT
diff --git a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp b/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp
index 0c0735be0e..e34fe18349 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp
+++ b/src/plugins/beautifier/artisticstyle/artisticstyleoptionspage.cpp
@@ -45,8 +45,6 @@ private:
ArtisticStyleOptionsPageWidget::ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings)
: m_settings(settings)
{
- resize(817, 631);
-
m_command = new Utils::PathChooser;
m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString());
@@ -84,13 +82,14 @@ ArtisticStyleOptionsPageWidget::ArtisticStyleOptionsPageWidget(ArtisticStyleSett
Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME)));
m_command->setFilePath(m_settings->command());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_useOtherFiles,
Row { m_useSpecificConfigFile, m_specificConfigFile },
m_useHomeFile,
- Row { m_useCustomStyle, m_configurations }
+ Row { m_useCustomStyle, m_configurations },
+ noMargin,
}.attachTo(options);
Column {
diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp
index 1cff800ed1..42b91a785f 100644
--- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp
+++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp
@@ -7,8 +7,8 @@
#include <coreplugin/icore.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
-#include <utils/qtcprocess.h>
#include <QDateTime>
#include <QFile>
@@ -112,7 +112,7 @@ QString ArtisticStyleSettings::documentationFilePath() const
void ArtisticStyleSettings::createDocumentationFile() const
{
- QtcProcess process;
+ Process process;
process.setTimeoutS(2);
process.setCommand({command(), {"-h"}});
process.runBlocking();
diff --git a/src/plugins/beautifier/beautifier.qbs b/src/plugins/beautifier/beautifier.qbs
index d1160ae5f5..0f85ca29b5 100644
--- a/src/plugins/beautifier/beautifier.qbs
+++ b/src/plugins/beautifier/beautifier.qbs
@@ -25,8 +25,6 @@ QtcPlugin {
"configurationeditor.h",
"configurationpanel.cpp",
"configurationpanel.h",
- "generaloptionspage.cpp",
- "generaloptionspage.h",
"generalsettings.cpp",
"generalsettings.h",
]
diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp
index 76db40daea..b849976a05 100644
--- a/src/plugins/beautifier/beautifierplugin.cpp
+++ b/src/plugins/beautifier/beautifierplugin.cpp
@@ -5,7 +5,6 @@
#include "beautifierconstants.h"
#include "beautifiertr.h"
-#include "generaloptionspage.h"
#include "generalsettings.h"
#include "artisticstyle/artisticstyle.h"
@@ -31,8 +30,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <utils/textutils.h>
@@ -80,12 +79,6 @@ public:
&uncrustifyBeautifier,
&clangFormatBeautifier
};
-
- GeneralOptionsPage optionsPage {{
- artisticStyleBeautifier.id(),
- uncrustifyBeautifier.id(),
- clangFormatBeautifier.id()
- }};
};
static BeautifierPluginPrivate *dd = nullptr;
@@ -112,6 +105,9 @@ ExtensionSystem::IPlugin::ShutdownFlag BeautifierPlugin::aboutToShutdown()
BeautifierPluginPrivate::BeautifierPluginPrivate()
{
+ for (BeautifierAbstractTool *tool : m_tools)
+ generalSettings.autoFormatTools.addOption(tool->id());
+
updateActions();
const Core::EditorManager *editorManager = Core::EditorManager::instance();
@@ -129,14 +125,14 @@ void BeautifierPluginPrivate::updateActions(Core::IEditor *editor)
void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document)
{
- if (!generalSettings.autoFormatOnSave())
+ if (!generalSettings.autoFormatOnSave.value())
return;
- if (!isAutoFormatApplicable(document, generalSettings.autoFormatMime()))
+ if (!isAutoFormatApplicable(document, generalSettings.allowedMimeTypes()))
return;
// Check if file is contained in the current project (if wished)
- if (generalSettings.autoFormatOnlyCurrentProject()) {
+ if (generalSettings.autoFormatOnlyCurrentProject.value()) {
const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
if (!pro
|| pro->files([document](const ProjectExplorer::Node *n) {
@@ -149,7 +145,7 @@ void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document)
}
// Find tool to use by id and format file!
- const QString id = generalSettings.autoFormatTool();
+ const QString id = generalSettings.autoFormatTools.stringValue();
auto tool = std::find_if(std::begin(m_tools), std::end(m_tools),
[&id](const BeautifierAbstractTool *t){return t->id() == id;});
if (tool != std::end(m_tools)) {
diff --git a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp b/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp
index 9423aa54a8..fd79826846 100644
--- a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp
+++ b/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp
@@ -86,7 +86,7 @@ ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings *
else
useCustomizedStyle->setChecked(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
m_usePredefinedStyle, m_predefinedStyle, br,
diff --git a/src/plugins/beautifier/configurationdialog.cpp b/src/plugins/beautifier/configurationdialog.cpp
index 8b2c61e720..09e8c595f3 100644
--- a/src/plugins/beautifier/configurationdialog.cpp
+++ b/src/plugins/beautifier/configurationdialog.cpp
@@ -40,7 +40,7 @@ ConfigurationDialog::ConfigurationDialog(QWidget *parent)
m_buttonBox->setOrientation(Qt::Horizontal);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
diff --git a/src/plugins/beautifier/configurationpanel.cpp b/src/plugins/beautifier/configurationpanel.cpp
index 6aa0f5b2d9..fa0fca7139 100644
--- a/src/plugins/beautifier/configurationpanel.cpp
+++ b/src/plugins/beautifier/configurationpanel.cpp
@@ -17,8 +17,6 @@ namespace Beautifier::Internal {
ConfigurationPanel::ConfigurationPanel(QWidget *parent)
: QWidget(parent)
{
- resize(595, 23);
-
m_configurations = new QComboBox;
m_configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
@@ -26,14 +24,15 @@ ConfigurationPanel::ConfigurationPanel(QWidget *parent)
m_remove = new QPushButton(Tr::tr("Remove"));
auto add = new QPushButton(Tr::tr("Add"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_configurations,
m_edit,
m_remove,
- add
- }.attachTo(this, WithoutMargins);
+ add,
+ noMargin,
+ }.attachTo(this);
connect(add, &QPushButton::clicked, this, &ConfigurationPanel::add);
connect(m_edit, &QPushButton::clicked, this, &ConfigurationPanel::edit);
diff --git a/src/plugins/beautifier/generaloptionspage.cpp b/src/plugins/beautifier/generaloptionspage.cpp
deleted file mode 100644
index 859f323e24..0000000000
--- a/src/plugins/beautifier/generaloptionspage.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "generaloptionspage.h"
-
-#include "beautifierconstants.h"
-#include "beautifiertr.h"
-#include "generalsettings.h"
-
-#include <utils/layoutbuilder.h>
-
-#include <QApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QLabel>
-#include <QLineEdit>
-
-namespace Beautifier::Internal {
-
-class GeneralOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit GeneralOptionsPageWidget(const QStringList &toolIds);
-
-private:
- void apply() final;
-
- QCheckBox *m_autoFormat;
- QComboBox *m_autoFormatTool;
- QLineEdit *m_autoFormatMime;
- QCheckBox *m_autoFormatOnlyCurrentProject;
-};
-
-GeneralOptionsPageWidget::GeneralOptionsPageWidget(const QStringList &toolIds)
-{
- resize(817, 631);
-
- auto settings = GeneralSettings::instance();
-
- m_autoFormat = new QCheckBox(Tr::tr("Enable auto format on file save"));
- m_autoFormat->setChecked(settings->autoFormatOnSave());
-
- auto toolLabel = new QLabel(Tr::tr("Tool:"));
- toolLabel->setEnabled(false);
-
- auto mimeLabel = new QLabel(Tr::tr("Restrict to MIME types:"));
- mimeLabel->setEnabled(false);
-
- m_autoFormatMime = new QLineEdit(settings->autoFormatMimeAsString());
- m_autoFormatMime->setEnabled(false);
-
- m_autoFormatOnlyCurrentProject =
- new QCheckBox(Tr::tr("Restrict to files contained in the current project"));
- m_autoFormatOnlyCurrentProject->setEnabled(false);
- m_autoFormatOnlyCurrentProject->setChecked(settings->autoFormatOnlyCurrentProject());
-
- m_autoFormatTool = new QComboBox;
- m_autoFormatTool->setEnabled(false);
- m_autoFormatTool->addItems(toolIds);
- const int index = m_autoFormatTool->findText(settings->autoFormatTool());
- m_autoFormatTool->setCurrentIndex(qMax(index, 0));
-
- using namespace Utils::Layouting;
-
- Column {
- Group {
- title(Tr::tr("Automatic Formatting on File Save")),
- Form {
- Span(2, m_autoFormat), br,
- toolLabel, m_autoFormatTool, br,
- mimeLabel, m_autoFormatMime, br,
- Span(2, m_autoFormatOnlyCurrentProject)
- }
- },
- st
- }.attachTo(this);
-
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatTool, &QComboBox::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatMime, &QLineEdit::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, toolLabel, &QLabel::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, mimeLabel, &QLabel::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatOnlyCurrentProject, &QCheckBox::setEnabled);
-}
-
-void GeneralOptionsPageWidget::apply()
-{
- auto settings = GeneralSettings::instance();
- settings->setAutoFormatOnSave(m_autoFormat->isChecked());
- settings->setAutoFormatTool(m_autoFormatTool->currentText());
- settings->setAutoFormatMime(m_autoFormatMime->text());
- settings->setAutoFormatOnlyCurrentProject(m_autoFormatOnlyCurrentProject->isChecked());
- settings->save();
-}
-
-GeneralOptionsPage::GeneralOptionsPage(const QStringList &toolIds)
-{
- setId(Constants::OPTION_GENERAL_ID);
- setDisplayName(Tr::tr("General"));
- setCategory(Constants::OPTION_CATEGORY);
- setDisplayCategory(Tr::tr("Beautifier"));
- setWidgetCreator([toolIds] { return new GeneralOptionsPageWidget(toolIds); });
- setCategoryIconPath(":/beautifier/images/settingscategory_beautifier.png");
-}
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/generaloptionspage.h b/src/plugins/beautifier/generaloptionspage.h
deleted file mode 100644
index 885a27cb19..0000000000
--- a/src/plugins/beautifier/generaloptionspage.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace Beautifier::Internal {
-
-class GeneralOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit GeneralOptionsPage(const QStringList &toolIds);
-};
-
-} // Beautifier::Internal
diff --git a/src/plugins/beautifier/generalsettings.cpp b/src/plugins/beautifier/generalsettings.cpp
index 218f6963f3..d9ae27b3fc 100644
--- a/src/plugins/beautifier/generalsettings.cpp
+++ b/src/plugins/beautifier/generalsettings.cpp
@@ -3,114 +3,88 @@
#include "generalsettings.h"
-#include <coreplugin/icore.h>
+#include "beautifierconstants.h"
+#include "beautifiertr.h"
#include <utils/algorithm.h>
#include <utils/genericconstants.h>
-#include <utils/mimeutils.h>
+#include <utils/layoutbuilder.h>
-namespace Beautifier::Internal {
+using namespace Utils;
-const char AUTO_FORMAT_TOOL[] = "autoFormatTool";
-const char AUTO_FORMAT_MIME[] = "autoFormatMime";
-const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "autoFormatOnlyCurrentProject";
+namespace Beautifier::Internal {
static GeneralSettings *m_instance;
-GeneralSettings::GeneralSettings()
-{
- m_instance = this;
- read();
-}
-
GeneralSettings *GeneralSettings::instance()
{
return m_instance;
}
-void GeneralSettings::read()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP);
- s->beginGroup(Utils::Constants::BEAUTIFIER_GENERAL_GROUP);
- m_autoFormatOnSave = s->value(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE, false).toBool();
- m_autoFormatTool = s->value(AUTO_FORMAT_TOOL, QString()).toString();
- setAutoFormatMime(s->value(AUTO_FORMAT_MIME, "text/x-c++src;text/x-c++hdr").toString());
- m_autoFormatOnlyCurrentProject = s->value(AUTO_FORMAT_ONLY_CURRENT_PROJECT, true).toBool();
- s->endGroup();
- s->endGroup();
-}
-
-void GeneralSettings::save()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP);
- s->beginGroup(Utils::Constants::BEAUTIFIER_GENERAL_GROUP);
- s->setValue(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE, m_autoFormatOnSave);
- s->setValue(AUTO_FORMAT_TOOL, m_autoFormatTool);
- s->setValue(AUTO_FORMAT_MIME, autoFormatMimeAsString());
- s->setValue(AUTO_FORMAT_ONLY_CURRENT_PROJECT, m_autoFormatOnlyCurrentProject);
- s->endGroup();
- s->endGroup();
-}
-
-bool GeneralSettings::autoFormatOnSave() const
-{
- return m_autoFormatOnSave;
-}
-
-void GeneralSettings::setAutoFormatOnSave(bool autoFormatOnSave)
-{
- m_autoFormatOnSave = autoFormatOnSave;
-}
-
-QString GeneralSettings::autoFormatTool() const
-{
- return m_autoFormatTool;
-}
-
-void GeneralSettings::setAutoFormatTool(const QString &autoFormatTool)
-{
- m_autoFormatTool = autoFormatTool;
-}
-
-QList<Utils::MimeType> GeneralSettings::autoFormatMime() const
+GeneralSettings::GeneralSettings()
{
- return m_autoFormatMime;
-}
+ m_instance = this;
-QString GeneralSettings::autoFormatMimeAsString() const
-{
- return Utils::transform(m_autoFormatMime, &Utils::MimeType::name).join("; ");
+ setId(Constants::OPTION_GENERAL_ID);
+ setDisplayName(Tr::tr("General"));
+ setCategory(Constants::OPTION_CATEGORY);
+ setDisplayCategory(Tr::tr("Beautifier"));
+ setCategoryIconPath(":/beautifier/images/settingscategory_beautifier.png");
+ setSettingsGroups("Beautifier", "General");
+ setSettings(this);
+ setAutoApply(false);
+
+ registerAspect(&autoFormatOnSave);
+ autoFormatOnSave.setSettingsKey(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE);
+ autoFormatOnSave.setDefaultValue(false);
+ autoFormatOnSave.setLabelText(Tr::tr("Enable auto format on file save"));
+
+ registerAspect(&autoFormatOnlyCurrentProject);
+ autoFormatOnlyCurrentProject.setSettingsKey("autoFormatOnlyCurrentProject");
+ autoFormatOnlyCurrentProject.setDefaultValue(true);
+ autoFormatOnlyCurrentProject.setLabelText(Tr::tr("Restrict to files contained in the current project"));
+
+ registerAspect(&autoFormatTools);
+ autoFormatTools.setSettingsKey("autoFormatTool");
+ autoFormatTools.setLabelText(Tr::tr("Tool:"));
+ autoFormatTools.setDefaultValue(0);
+ autoFormatTools.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
+
+ registerAspect(&autoFormatMime);
+ autoFormatMime.setSettingsKey("autoFormatMime");
+ autoFormatMime.setDefaultValue("text/x-c++src;text/x-c++hdr");
+ autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:"));
+ autoFormatMime.setDisplayStyle(StringAspect::LineEditDisplay);
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ Column {
+ Group {
+ title(Tr::tr("Automatic Formatting on File Save")),
+ autoFormatOnSave.groupChecker(),
+ Form {
+ autoFormatTools, br,
+ autoFormatMime, br,
+ Span(2, autoFormatOnlyCurrentProject)
+ }
+ },
+ st
+ }.attachTo(widget);
+ });
}
-void GeneralSettings::setAutoFormatMime(const QList<Utils::MimeType> &autoFormatMime)
+QList<MimeType> GeneralSettings::allowedMimeTypes() const
{
- m_autoFormatMime = autoFormatMime;
-}
+ const QStringList stringTypes = autoFormatMime.value().split(';');
-void GeneralSettings::setAutoFormatMime(const QString &mimeList)
-{
- const QStringList stringTypes = mimeList.split(';');
- QList<Utils::MimeType> types;
- types.reserve(stringTypes.count());
+ QList<MimeType> types;
for (QString t : stringTypes) {
t = t.trimmed();
- const Utils::MimeType mime = Utils::mimeTypeForName(t);
+ const MimeType mime = Utils::mimeTypeForName(t);
if (mime.isValid())
types << mime;
}
- setAutoFormatMime(types);
-}
-
-bool GeneralSettings::autoFormatOnlyCurrentProject() const
-{
- return m_autoFormatOnlyCurrentProject;
-}
-
-void GeneralSettings::setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject)
-{
- m_autoFormatOnlyCurrentProject = autoFormatOnlyCurrentProject;
+ return types;
}
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/generalsettings.h b/src/plugins/beautifier/generalsettings.h
index b381dacb26..2c6743301b 100644
--- a/src/plugins/beautifier/generalsettings.h
+++ b/src/plugins/beautifier/generalsettings.h
@@ -5,38 +5,23 @@
#include <utils/mimeutils.h>
-#include <QList>
+#include <coreplugin/dialogs/ioptionspage.h>
namespace Beautifier::Internal {
-class GeneralSettings
+class GeneralSettings : public Core::PagedSettings
{
public:
- explicit GeneralSettings();
- static GeneralSettings *instance();
-
- void read();
- void save();
-
- bool autoFormatOnSave() const;
- void setAutoFormatOnSave(bool autoFormatOnSave);
+ GeneralSettings();
- QString autoFormatTool() const;
- void setAutoFormatTool(const QString &autoFormatTool);
-
- QList<Utils::MimeType> autoFormatMime() const;
- QString autoFormatMimeAsString() const;
- void setAutoFormatMime(const QList<Utils::MimeType> &autoFormatMime);
- void setAutoFormatMime(const QString &mimeList);
+ static GeneralSettings *instance();
- bool autoFormatOnlyCurrentProject() const;
- void setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject);
+ QList<Utils::MimeType> allowedMimeTypes() const;
-private:
- bool m_autoFormatOnSave = false;
- bool m_autoFormatOnlyCurrentProject = true;
- QString m_autoFormatTool;
- QList<Utils::MimeType> m_autoFormatMime;
+ Utils::BoolAspect autoFormatOnSave;
+ Utils::BoolAspect autoFormatOnlyCurrentProject;
+ Utils::SelectionAspect autoFormatTools;
+ Utils::StringAspect autoFormatMime;
};
} // Beautifier::Internal
diff --git a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp b/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp
index 2102dd311f..8da74a7197 100644
--- a/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp
+++ b/src/plugins/beautifier/uncrustify/uncrustifyoptionspage.cpp
@@ -45,8 +45,6 @@ private:
UncrustifyOptionsPageWidget::UncrustifyOptionsPageWidget(UncrustifySettings *settings)
: m_settings(settings)
{
- resize(817, 631);
-
m_command = new Utils::PathChooser;
m_mime = new QLineEdit(m_settings->supportedMimeTypesAsString());
@@ -86,7 +84,7 @@ UncrustifyOptionsPageWidget::UncrustifyOptionsPageWidget(UncrustifySettings *set
auto options = new QGroupBox(Tr::tr("Options"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_useOtherFiles,
diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp
index e827a8f791..a284d780b6 100644
--- a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp
+++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp
@@ -6,7 +6,7 @@
#include "../beautifierconstants.h"
#include <coreplugin/icore.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDateTime>
#include <QFile>
@@ -124,7 +124,7 @@ QString UncrustifySettings::documentationFilePath() const
void UncrustifySettings::createDocumentationFile() const
{
- QtcProcess process;
+ Process process;
process.setTimeoutS(2);
process.setCommand({command(), {"--show-config"}});
process.runBlocking();
diff --git a/src/plugins/bookmarks/bookmarkfilter.cpp b/src/plugins/bookmarks/bookmarkfilter.cpp
index 7cc9bf2a75..1cedbade48 100644
--- a/src/plugins/bookmarks/bookmarkfilter.cpp
+++ b/src/plugins/bookmarks/bookmarkfilter.cpp
@@ -19,101 +19,97 @@ BookmarkFilter::BookmarkFilter(BookmarkManager *manager)
{
setId("Bookmarks");
setDisplayName(Tr::tr("Bookmarks"));
- setDescription(Tr::tr("Matches all bookmarks. Filter by file name, by the text on the line of the "
- "bookmark, or by the bookmark's note text."));
+ setDescription(Tr::tr("Locates bookmarks. Filter by file name, by the text on the line of the "
+ "bookmark, or by the bookmark's note text."));
setPriority(Medium);
setDefaultShortcutString("b");
}
-void BookmarkFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks BookmarkFilter::matchers()
{
- m_results = {};
- if (m_manager->rowCount() == 0)
- return;
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=] { storage->reportOutput(match(storage->input())); };
+ return {{Sync(onSetup), storage}};
+}
- auto match = [this](const QString &name, BookmarkManager::Roles role) {
+LocatorFilterEntries BookmarkFilter::match(const QString &input) const
+{
+ if (m_manager->rowCount() == 0)
+ return {};
+ const auto match = [this](const QString &name, BookmarkManager::Roles role) {
return m_manager->match(m_manager->index(0, 0), role, name, -1,
Qt::MatchContains | Qt::MatchWrap);
};
- int colonIndex = entry.lastIndexOf(':');
+ const int colonIndex = input.lastIndexOf(':');
QModelIndexList fileNameLineNumberMatches;
if (colonIndex >= 0) {
// Filter by "fileName:lineNumber" pattern
- const QString fileName = entry.left(colonIndex);
- const QString lineNumber = entry.mid(colonIndex + 1);
+ const QString fileName = input.left(colonIndex);
+ const QString lineNumber = input.mid(colonIndex + 1);
fileNameLineNumberMatches = match(fileName, BookmarkManager::Filename);
- fileNameLineNumberMatches =
- Utils::filtered(fileNameLineNumberMatches, [lineNumber](const QModelIndex &index) {
- return index.data(BookmarkManager::LineNumber).toString().contains(lineNumber);
- });
+ fileNameLineNumberMatches = Utils::filtered(
+ fileNameLineNumberMatches, [lineNumber](const QModelIndex &index) {
+ return index.data(BookmarkManager::LineNumber).toString().contains(lineNumber);
+ });
}
-
const QModelIndexList matches = filteredUnique(fileNameLineNumberMatches
- + match(entry, BookmarkManager::Filename)
- + match(entry, BookmarkManager::LineNumber)
- + match(entry, BookmarkManager::Note)
- + match(entry, BookmarkManager::LineText));
-
+ + match(input, BookmarkManager::Filename)
+ + match(input, BookmarkManager::LineNumber)
+ + match(input, BookmarkManager::Note)
+ + match(input, BookmarkManager::LineText));
+ LocatorFilterEntries entries;
for (const QModelIndex &idx : matches) {
const Bookmark *bookmark = m_manager->bookmarkForIndex(idx);
const QString filename = bookmark->filePath().fileName();
- LocatorFilterEntry filterEntry(this,
- QString("%1:%2").arg(filename).arg(bookmark->lineNumber()),
- QVariant::fromValue(idx));
+ LocatorFilterEntry entry;
+ entry.displayName = QString("%1:%2").arg(filename).arg(bookmark->lineNumber());
+ // TODO: according to QModelIndex docs, we shouldn't store model indexes:
+ // Model indexes should be used immediately and then discarded.
+ // You should not rely on indexes to remain valid after calling model functions
+ // that change the structure of the model or delete items.
+ entry.acceptor = [manager = m_manager, idx] {
+ if (Bookmark *bookmark = manager->bookmarkForIndex(idx))
+ manager->gotoBookmark(bookmark);
+ return AcceptResult();
+ };
if (!bookmark->note().isEmpty())
- filterEntry.extraInfo = bookmark->note();
+ entry.extraInfo = bookmark->note();
else if (!bookmark->lineText().isEmpty())
- filterEntry.extraInfo = bookmark->lineText();
+ entry.extraInfo = bookmark->lineText();
else
- filterEntry.extraInfo = bookmark->filePath().toString();
- int highlightIndex = filterEntry.displayName.indexOf(entry, 0, Qt::CaseInsensitive);
+ entry.extraInfo = bookmark->filePath().toString();
+ int highlightIndex = entry.displayName.indexOf(input, 0, Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo = {highlightIndex, int(entry.length())};
+ entry.highlightInfo = {highlightIndex, int(input.length())};
} else {
- highlightIndex = filterEntry.extraInfo.indexOf(entry, 0, Qt::CaseInsensitive);
+ highlightIndex = entry.extraInfo.indexOf(input, 0, Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo = {highlightIndex, int(entry.length()),
- LocatorFilterEntry::HighlightInfo::ExtraInfo};
+ entry.highlightInfo = {highlightIndex, int(input.length()),
+ LocatorFilterEntry::HighlightInfo::ExtraInfo};
} else if (colonIndex >= 0) {
- const QString fileName = entry.left(colonIndex);
- const QString lineNumber = entry.mid(colonIndex + 1);
- highlightIndex = filterEntry.displayName.indexOf(fileName, 0, Qt::CaseInsensitive);
+ const QString fileName = input.left(colonIndex);
+ const QString lineNumber = input.mid(colonIndex + 1);
+ highlightIndex = entry.displayName.indexOf(fileName, 0,
+ Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo = {highlightIndex, int(fileName.length())};
- highlightIndex = filterEntry.displayName.indexOf(
+ entry.highlightInfo = {highlightIndex, int(fileName.length())};
+ highlightIndex = entry.displayName.indexOf(
lineNumber, highlightIndex, Qt::CaseInsensitive);
if (highlightIndex >= 0) {
- filterEntry.highlightInfo.startsDisplay += highlightIndex;
- filterEntry.highlightInfo.lengthsDisplay += lineNumber.length();
+ entry.highlightInfo.startsDisplay += highlightIndex;
+ entry.highlightInfo.lengthsDisplay += lineNumber.length();
}
}
}
}
-
- filterEntry.displayIcon = bookmark->icon();
- m_results.append(filterEntry);
- }
-}
-
-QList<LocatorFilterEntry> BookmarkFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
-{
- Q_UNUSED(future)
- Q_UNUSED(entry)
- return m_results;
-}
-
-void BookmarkFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- if (const Bookmark *bookmark = m_manager->bookmarkForIndex(
- selection.internalData.toModelIndex())) {
- m_manager->gotoBookmark(bookmark);
+ entry.displayIcon = bookmark->icon();
+ entries.append(entry);
}
+ return entries;
}
} // Bookmarks::Internal
diff --git a/src/plugins/bookmarks/bookmarkfilter.h b/src/plugins/bookmarks/bookmarkfilter.h
index 0c69e2b6d4..f85eff9b7d 100644
--- a/src/plugins/bookmarks/bookmarkfilter.h
+++ b/src/plugins/bookmarks/bookmarkfilter.h
@@ -12,16 +12,13 @@ class BookmarkManager;
class BookmarkFilter : public Core::ILocatorFilter
{
public:
- explicit BookmarkFilter(BookmarkManager *manager);
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
+ BookmarkFilter(BookmarkManager *manager);
private:
+ Core::LocatorMatcherTasks matchers() final;
+ Core::LocatorFilterEntries match(const QString &input) const;
+
BookmarkManager *m_manager = nullptr; // not owned
- QList<Core::LocatorFilterEntry> m_results;
};
} // Bookmarks::Internal
diff --git a/src/plugins/bookmarks/bookmarkmanager.cpp b/src/plugins/bookmarks/bookmarkmanager.cpp
index e7c5a4e388..77bb0f4151 100644
--- a/src/plugins/bookmarks/bookmarkmanager.cpp
+++ b/src/plugins/bookmarks/bookmarkmanager.cpp
@@ -12,8 +12,10 @@
#include <coreplugin/idocument.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
+
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
+
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
#include <utils/icon.h>
@@ -29,7 +31,6 @@
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
-#include <QFileInfo>
#include <QFormLayout>
#include <QLineEdit>
#include <QMenu>
@@ -256,11 +257,13 @@ void BookmarkView::keyPressEvent(QKeyEvent *event)
void BookmarkView::removeAll()
{
- if (CheckableMessageBox::doNotAskAgainQuestion(this,
- Tr::tr("Remove All Bookmarks"),
- Tr::tr("Are you sure you want to remove all bookmarks from all files in the current session?"),
- ICore::settings(),
- QLatin1String("RemoveAllBookmarks")) != QDialogButtonBox::Yes)
+ if (CheckableMessageBox::question(this,
+ Tr::tr("Remove All Bookmarks"),
+ Tr::tr("Are you sure you want to remove all bookmarks from "
+ "all files in the current session?"),
+ ICore::settings(),
+ QLatin1String("RemoveAllBookmarks"))
+ != QMessageBox::Yes)
return;
// The performance of this function could be greatly improved.
diff --git a/src/plugins/bookmarks/bookmarksplugin.cpp b/src/plugins/bookmarks/bookmarksplugin.cpp
index 2f25571b53..7473876ec6 100644
--- a/src/plugins/bookmarks/bookmarksplugin.cpp
+++ b/src/plugins/bookmarks/bookmarksplugin.cpp
@@ -128,14 +128,16 @@ BookmarksPluginPrivate::BookmarksPluginPrivate()
mbm->addAction(cmd);
connect(&m_toggleAction, &QAction::triggered, this, [this] {
- BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
- if (editor && !editor->document()->isTemporary())
+ IEditor *editor = EditorManager::currentEditor();
+ auto widget = TextEditorWidget::fromEditor(editor);
+ if (widget && editor && !editor->document()->isTemporary())
m_bookmarkManager.toggleBookmark(editor->document()->filePath(), editor->currentLine());
});
connect(&m_editAction, &QAction::triggered, this, [this] {
- BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
- if (editor && !editor->document()->isTemporary()) {
+ IEditor *editor = EditorManager::currentEditor();
+ auto widget = TextEditorWidget::fromEditor(editor);
+ if (widget && editor && !editor->document()->isTemporary()) {
const FilePath filePath = editor->document()->filePath();
const int line = editor->currentLine();
if (!m_bookmarkManager.hasBookmarkInPosition(filePath, line))
diff --git a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
index 3375e10e02..869b2d84fc 100644
--- a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
+++ b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp
@@ -8,7 +8,7 @@
#include "../qdbutils.h"
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFile>
#include <QTimer>
@@ -118,7 +118,7 @@ void QdbWatcher::forkHostServer()
showMessage(message, true);
return;
}
- if (Utils::QtcProcess::startDetached({qdbFilePath, {"server"}}))
+ if (Utils::Process::startDetached({qdbFilePath, {"server"}}))
showMessage(Tr::tr("QDB host server started."), false);
else
showMessage(Tr::tr("Could not start QDB host server in %1").arg(qdbFilePath.toString()), true);
diff --git a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp
index f115685794..3abb700df1 100644
--- a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp
+++ b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp
@@ -7,7 +7,7 @@
#include "qdbtr.h"
#include <projectexplorer/deploymentdataview.h>
-#include "projectexplorer/devicesupport/idevice.h"
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp
index 90568f4264..43783279d6 100644
--- a/src/plugins/boot2qt/qdbdevice.cpp
+++ b/src/plugins/boot2qt/qdbdevice.cpp
@@ -15,8 +15,8 @@
#include <remotelinux/linuxprocessinterface.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <QFormLayout>
@@ -30,11 +30,10 @@ using namespace Utils;
namespace Qdb::Internal {
-class QdbProcessImpl : public LinuxProcessInterface
+class QdbProcessImpl : public SshProcessInterface
{
public:
- QdbProcessImpl(const LinuxDevice *linuxDevice)
- : LinuxProcessInterface(linuxDevice) {}
+ QdbProcessImpl(const IDevice::ConstPtr &device) : SshProcessInterface(device) {}
~QdbProcessImpl() { killIfRunning(); }
private:
@@ -51,7 +50,7 @@ class DeviceApplicationObserver : public QObject
public:
DeviceApplicationObserver(const IDevice::ConstPtr &device, const CommandLine &command)
{
- connect(&m_appRunner, &QtcProcess::done, this, &DeviceApplicationObserver::handleDone);
+ connect(&m_appRunner, &Process::done, this, &DeviceApplicationObserver::handleDone);
QTC_ASSERT(device, return);
m_deviceName = device->displayName();
@@ -95,7 +94,7 @@ private:
deleteLater();
}
- QtcProcess m_appRunner;
+ Process m_appRunner;
QString m_deviceName;
};
@@ -105,6 +104,7 @@ private:
QdbDevice::QdbDevice()
{
setDisplayType(Tr::tr("Boot2Qt Device"));
+ setType(Constants::QdbLinuxOsType);
addDeviceAction({Tr::tr("Reboot Device"), [](const IDevice::Ptr &device, QWidget *) {
(void) new DeviceApplicationObserver(device, {device->filePath("reboot"), {}});
@@ -124,7 +124,7 @@ ProjectExplorer::IDeviceWidget *QdbDevice::createWidget()
ProcessInterface *QdbDevice::createProcessInterface() const
{
- return new QdbProcessImpl(this);
+ return new QdbProcessImpl(sharedFromThis());
}
void QdbDevice::setSerialNumber(const QString &serial)
@@ -248,6 +248,7 @@ QdbLinuxDeviceFactory::QdbLinuxDeviceFactory()
{
setDisplayName(Tr::tr("Boot2Qt Device"));
setCombinedIcon(":/qdb/images/qdbdevicesmall.png", ":/qdb/images/qdbdevice.png");
+ setQuickCreationAllowed(true);
setConstructionFunction(&QdbDevice::create);
setCreator([] {
QdbDeviceWizard wizard(Core::ICore::dialogParent());
diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
index 97529dfa9f..d6e198538f 100644
--- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
+++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp
@@ -13,7 +13,7 @@
#include <debugger/debuggerruncontrol.h>
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/url.h>
using namespace Debugger;
@@ -34,13 +34,13 @@ public:
{
setId("QdbDebuggeeRunner");
- connect(&m_launcher, &QtcProcess::started, this, &RunWorker::reportStarted);
- connect(&m_launcher, &QtcProcess::done, this, &RunWorker::reportStopped);
+ connect(&m_launcher, &Process::started, this, &RunWorker::reportStarted);
+ connect(&m_launcher, &Process::done, this, &RunWorker::reportStopped);
- connect(&m_launcher, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_launcher, &Process::readyReadStandardOutput, this, [this] {
appendMessage(m_launcher.readAllStandardOutput(), StdOutFormat);
});
- connect(&m_launcher, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_launcher, &Process::readyReadStandardError, this, [this] {
appendMessage(m_launcher.readAllStandardError(), StdErrFormat);
});
@@ -112,7 +112,7 @@ private:
bool m_useGdbServer;
bool m_useQmlServer;
QmlDebug::QmlDebugServicesPreset m_qmlServices;
- QtcProcess m_launcher;
+ Process m_launcher;
};
// QdbDeviceRunSupport
diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
index 70c1c40acb..ae5806deb9 100644
--- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
+++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp
@@ -14,27 +14,35 @@
#include <remotelinux/abstractremotelinuxdeploystep.h>
#include <utils/commandline.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace Qdb::Internal {
-// QdbMakeDefaultAppService
-
-class QdbMakeDefaultAppService : public RemoteLinux::AbstractRemoteLinuxDeployService
+class QdbMakeDefaultAppStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
{
public:
- void setMakeDefault(bool makeDefault) { m_makeDefault = makeDefault; }
+ QdbMakeDefaultAppStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ auto selection = addAspect<SelectionAspect>();
+ selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault");
+ selection->addOption(Tr::tr("Set this application to start by default"));
+ selection->addOption(Tr::tr("Reset default application"));
-private:
- bool isDeploymentNecessary() const final { return true; }
+ setInternalInitializer([this, selection] {
+ m_makeDefault = selection->value() == 0;
+ return isDeploymentPossible();
+ });
+ }
+private:
Group deployRecipe() final
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
QString remoteExe;
if (RunConfiguration *rc = target()->activeRunConfiguration()) {
if (auto exeAspect = rc->aspect<ExecutableAspect>())
@@ -46,50 +54,26 @@ private:
else
cmd.addArg("--remove-default");
process.setCommand(cmd);
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardError, this, [this, proc] {
- emit stdErrData(proc->readAllStandardError());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardError, this, [this, proc] {
+ handleStdErrData(proc->readAllStandardError());
});
};
- const auto doneHandler = [this](const QtcProcess &) {
+ const auto doneHandler = [this](const Process &) {
if (m_makeDefault)
- emit progressMessage(Tr::tr("Application set as the default one."));
+ addProgressMessage(Tr::tr("Application set as the default one."));
else
- emit progressMessage(Tr::tr("Reset the default application."));
+ addProgressMessage(Tr::tr("Reset the default application."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
- emit errorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
+ const auto errorHandler = [this](const Process &process) {
+ addErrorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
};
- return Group { Process(setupHandler, doneHandler, errorHandler) };
+ return Group { ProcessTask(setupHandler, doneHandler, errorHandler) };
}
bool m_makeDefault = true;
};
-// QdbMakeDefaultAppStep
-
-class QdbMakeDefaultAppStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
-{
-public:
- QdbMakeDefaultAppStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new QdbMakeDefaultAppService;
- setDeployService(service);
-
- auto selection = addAspect<SelectionAspect>();
- selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault");
- selection->addOption(Tr::tr("Set this application to start by default"));
- selection->addOption(Tr::tr("Reset default application"));
-
- setInternalInitializer([service, selection] {
- service->setMakeDefault(selection->value() == 0);
- return service->isDeploymentPossible();
- });
- }
-};
-
-
// QdbMakeDefaultAppStepFactory
QdbMakeDefaultAppStepFactory::QdbMakeDefaultAppStepFactory()
diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp
index a73646b6c8..8c806cb158 100644
--- a/src/plugins/boot2qt/qdbplugin.cpp
+++ b/src/plugins/boot2qt/qdbplugin.cpp
@@ -27,22 +27,18 @@
#include <qtsupport/qtversionfactory.h>
-#include <remotelinux/genericdirectuploadstep.h>
-#include <remotelinux/makeinstallstep.h>
-#include <remotelinux/rsyncdeploystep.h>
#include <remotelinux/remotelinux_constants.h>
-#include <utils/hostosinfo.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <QAction>
using namespace ProjectExplorer;
using namespace Utils;
-namespace Qdb {
-namespace Internal {
+namespace Qdb::Internal {
static FilePath flashWizardFilePath()
{
@@ -53,9 +49,9 @@ static void startFlashingWizard()
{
const FilePath filePath = flashWizardFilePath();
if (HostOsInfo::isWindowsHost()) {
- if (QtcProcess::startDetached({"explorer.exe", {filePath.toUserOutput()}}))
+ if (Process::startDetached({"explorer.exe", {filePath.toUserOutput()}}))
return;
- } else if (QtcProcess::startDetached({filePath, {}})) {
+ } else if (Process::startDetached({filePath, {}})) {
return;
}
const QString message = Tr::tr("Flash wizard \"%1\" failed to start.");
@@ -100,14 +96,12 @@ void registerFlashAction(QObject *parentForAction)
toolsContainer->addAction(flashCommand, flashActionId);
}
-template <class Step>
-class QdbDeployStepFactory : public ProjectExplorer::BuildStepFactory
+class QdbDeployStepFactory : public BuildStepFactory
{
public:
- explicit QdbDeployStepFactory(Id id)
+ explicit QdbDeployStepFactory(Id existingStepId)
{
- registerStep<Step>(id);
- setDisplayName(Step::displayName());
+ cloneStepCreator(existingStepId);
setSupportedConfiguration(Constants::QdbDeployConfigurationId);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
}
@@ -125,12 +119,9 @@ public:
QdbStopApplicationStepFactory m_stopApplicationStepFactory;
QdbMakeDefaultAppStepFactory m_makeDefaultAppStepFactory;
- QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep>
- m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
- QdbDeployStepFactory<RemoteLinux::RsyncDeployStep>
- m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId};
- QdbDeployStepFactory<RemoteLinux::MakeInstallStep>
- m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
+ QdbDeployStepFactory m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
+ QdbDeployStepFactory m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId};
+ QdbDeployStepFactory m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
const QList<Id> supportedRunConfigs {
m_runConfigFactory.runConfigurationId(),
@@ -180,5 +171,4 @@ void QdbPluginPrivate::setupDeviceDetection()
m_deviceDetector.start();
}
-} // Internal
-} // Qdb
+} // Qdb::Internal
diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp
index 60d891beaf..5fef60268f 100644
--- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp
+++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp
@@ -13,78 +13,67 @@
#include <remotelinux/abstractremotelinuxdeploystep.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace Qdb::Internal {
-// QdbStopApplicationService
+// QdbStopApplicationStep
-class QdbStopApplicationService : public RemoteLinux::AbstractRemoteLinuxDeployService
+class QdbStopApplicationStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
{
-private:
- bool isDeploymentNecessary() const final { return true; }
+public:
+ QdbStopApplicationStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+
+ setInternalInitializer([this] { return isDeploymentPossible(); });
+ }
+
Group deployRecipe() final;
};
-Group QdbStopApplicationService::deployRecipe()
+Group QdbStopApplicationStep::deployRecipe()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
const auto device = DeviceKitAspect::device(target()->kit());
if (!device) {
- emit errorMessage(Tr::tr("No device to stop the application on."));
+ addErrorMessage(Tr::tr("No device to stop the application on."));
return TaskAction::StopWithError;
}
QTC_CHECK(device);
process.setCommand({device->filePath(Constants::AppcontrollerFilepath), {"--stop"}});
process.setWorkingDirectory("/usr/bin");
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [this, proc] {
- emit stdOutData(proc->readAllStandardOutput());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
+ handleStdOutData(proc->readAllStandardOutput());
});
return TaskAction::Continue;
};
- const auto doneHandler = [this](const QtcProcess &) {
- emit progressMessage(Tr::tr("Stopped the running application."));
+ const auto doneHandler = [this](const Process &) {
+ addProgressMessage(Tr::tr("Stopped the running application."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
const QString errorOutput = process.cleanedStdErr();
const QString failureMessage = Tr::tr("Could not check and possibly stop running application.");
if (process.exitStatus() == QProcess::CrashExit) {
- emit errorMessage(failureMessage);
+ addErrorMessage(failureMessage);
} else if (process.result() != ProcessResult::FinishedWithSuccess) {
- emit stdErrData(process.errorString());
+ handleStdErrData(process.errorString());
} else if (errorOutput.contains("Could not connect: Connection refused")) {
- emit progressMessage(Tr::tr("Checked that there is no running application."));
+ addProgressMessage(Tr::tr("Checked that there is no running application."));
} else if (!errorOutput.isEmpty()) {
- emit stdErrData(errorOutput);
- emit errorMessage(failureMessage);
+ handleStdErrData(errorOutput);
+ addErrorMessage(failureMessage);
}
};
- return Group { Process(setupHandler, doneHandler, errorHandler) };
+ return Group { ProcessTask(setupHandler, doneHandler, errorHandler) };
}
-// QdbStopApplicationStep
-
-class QdbStopApplicationStep final : public RemoteLinux::AbstractRemoteLinuxDeployStep
-{
-public:
- QdbStopApplicationStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new QdbStopApplicationService;
- setDeployService(service);
-
- setWidgetExpandedByDefault(false);
-
- setInternalInitializer([service] { return service->isDeploymentPossible(); });
- }
-};
-
-
// QdbStopApplicationStepFactory
QdbStopApplicationStepFactory::QdbStopApplicationStepFactory()
diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt
index ace3907171..243a330efa 100644
--- a/src/plugins/clangcodemodel/CMakeLists.txt
+++ b/src/plugins/clangcodemodel/CMakeLists.txt
@@ -51,7 +51,6 @@ extend_qtc_plugin(ClangCodeModel
CONDITION WITH_TESTS
SOURCES
test/activationsequenceprocessortest.cpp test/activationsequenceprocessortest.h
- test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h
test/clangdtests.cpp test/clangdtests.h
test/clangfixittest.cpp test/clangfixittest.h
test/data/clangtestdata.qrc
diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs
index 45a6abc347..903a81147a 100644
--- a/src/plugins/clangcodemodel/clangcodemodel.qbs
+++ b/src/plugins/clangcodemodel/clangcodemodel.qbs
@@ -94,15 +94,11 @@ QtcPlugin {
}
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "test/"
files: [
"activationsequenceprocessortest.cpp",
"activationsequenceprocessortest.h",
- "clangbatchfileprocessor.cpp",
- "clangbatchfileprocessor.h",
"clangdtests.cpp",
"clangdtests.h",
"clangfixittest.cpp",
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
index 2a3772afb2..9b5ec42523 100644
--- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp
@@ -10,7 +10,6 @@
#ifdef WITH_TESTS
# include "test/activationsequenceprocessortest.h"
-# include "test/clangbatchfileprocessor.h"
# include "test/clangdtests.h"
# include "test/clangfixittest.h"
#endif
@@ -28,15 +27,16 @@
#include <projectexplorer/projectpanelfactory.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <texteditor/textmark.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
using namespace Core;
@@ -49,7 +49,7 @@ void ClangCodeModelPlugin::generateCompilationDB()
{
using namespace CppEditor;
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
if (!target)
return;
@@ -61,7 +61,7 @@ void ClangCodeModelPlugin::generateCompilationDB()
baseDir = TemporaryDirectory::masterDirectoryFilePath();
QFuture<GenerateCompilationDbResult> task
- = Utils::runAsync(&Internal::generateCompilationDB, ProjectInfoList{projectInfo},
+ = Utils::asyncRun(&Internal::generateCompilationDB, ProjectInfoList{projectInfo},
baseDir, CompilationDbPurpose::Project,
warningsConfigForProject(target->project()),
globalClangOptions(),
@@ -78,15 +78,8 @@ ClangCodeModelPlugin::~ClangCodeModelPlugin()
void ClangCodeModelPlugin::initialize()
{
TaskHub::addCategory(Constants::TASK_CATEGORY_DIAGNOSTICS, Tr::tr("Clang Code Model"));
-
- connect(ProjectExplorerPlugin::instance(),
- &ProjectExplorerPlugin::finishedInitialization,
- this,
- &ClangCodeModelPlugin::maybeHandleBatchFileAndExit);
-
CppEditor::CppModelManager::instance()->activateClangCodeModel(
- std::make_unique<ClangModelManagerSupport>());
-
+ std::make_unique<ClangModelManagerSupport>());
createCompilationDBAction();
#ifdef WITH_TESTS
@@ -109,7 +102,7 @@ void ClangCodeModelPlugin::createCompilationDBAction()
Tr::tr("Generate Compilation Database"),
Tr::tr("Generate Compilation Database for \"%1\""),
ParameterAction::AlwaysEnabled, this);
- Project *startupProject = SessionManager::startupProject();
+ Project *startupProject = ProjectManager::startupProject();
if (startupProject)
m_generateCompilationDBAction->setParameter(startupProject->displayName());
Command *command = ActionManager::registerAction(m_generateCompilationDBAction,
@@ -136,7 +129,7 @@ void ClangCodeModelPlugin::createCompilationDBAction()
"Generator is already running.");
return;
}
- Project * const project = SessionManager::startupProject();
+ Project * const project = ProjectManager::startupProject();
if (!project) {
MessageManager::writeDisrupting("Cannot generate compilation database: "
"No active project.");
@@ -154,21 +147,21 @@ void ClangCodeModelPlugin::createCompilationDBAction()
});
connect(CppEditor::CppModelManager::instance(), &CppEditor::CppModelManager::projectPartsUpdated,
this, [this](Project *project) {
- if (project != SessionManager::startupProject())
+ if (project != ProjectManager::startupProject())
return;
m_generateCompilationDBAction->setParameter(project->displayName());
});
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, [this](Project *project) {
m_generateCompilationDBAction->setParameter(project ? project->displayName() : "");
});
- connect(SessionManager::instance(), &SessionManager::projectDisplayNameChanged,
+ connect(ProjectManager::instance(), &ProjectManager::projectDisplayNameChanged,
this, [this](Project *project) {
- if (project != SessionManager::startupProject())
+ if (project != ProjectManager::startupProject())
return;
m_generateCompilationDBAction->setParameter(project->displayName());
});
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this](Project *project) {
project->registerGenerator(Constants::GENERATE_COMPILATION_DB,
m_generateCompilationDBAction->text(),
@@ -176,16 +169,4 @@ void ClangCodeModelPlugin::createCompilationDBAction()
});
}
-// For e.g. creation of profile-guided optimization builds.
-void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const
-{
-#ifdef WITH_TESTS
- const QString batchFilePath = qtcEnvironmentVariable("QTC_CLANG_BATCH");
- if (!batchFilePath.isEmpty() && QTC_GUARD(QFileInfo::exists(batchFilePath))) {
- const bool runSucceeded = runClangBatchFile(batchFilePath);
- QCoreApplication::exit(!runSucceeded);
- }
-#endif
-}
-
-} // ClangCodeModel::Internal
+} // namespace ClangCodeModel::Internal
diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h
index 664cf113aa..d0456257f2 100644
--- a/src/plugins/clangcodemodel/clangcodemodelplugin.h
+++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h
@@ -23,8 +23,6 @@ public:
void initialize() override;
private:
- void maybeHandleBatchFileAndExit() const;
-
void generateCompilationDB();
void createCompilationDBAction();
diff --git a/src/plugins/clangcodemodel/clangdast.cpp b/src/plugins/clangcodemodel/clangdast.cpp
index 6b97146dd5..01f12d1177 100644
--- a/src/plugins/clangcodemodel/clangdast.cpp
+++ b/src/plugins/clangcodemodel/clangdast.cpp
@@ -172,6 +172,20 @@ bool ClangdAstNode::hasChildWithRole(const QString &role) const
[&role](const ClangdAstNode &c) { return c.role() == role; });
}
+bool ClangdAstNode::hasChild(const std::function<bool(const ClangdAstNode &)> &predicate,
+ bool recursive) const
+{
+ std::function<bool(const ClangdAstNode &)> fullPredicate = predicate;
+ if (recursive) {
+ fullPredicate = [&predicate](const ClangdAstNode &n) {
+ if (predicate(n))
+ return true;
+ return n.hasChild(predicate, true);
+ };
+ }
+ return Utils::contains(children().value_or(QList<ClangdAstNode>()), fullPredicate);
+}
+
QString ClangdAstNode::operatorString() const
{
if (kind() == "BinaryOperator")
diff --git a/src/plugins/clangcodemodel/clangdast.h b/src/plugins/clangcodemodel/clangdast.h
index 7eb24e9f21..cdb87c7be0 100644
--- a/src/plugins/clangcodemodel/clangdast.h
+++ b/src/plugins/clangcodemodel/clangdast.h
@@ -77,6 +77,7 @@ public:
bool childContainsRange(int index, const LanguageServerProtocol::Range &range) const;
bool hasChildWithRole(const QString &role) const;
+ bool hasChild(const std::function<bool(const ClangdAstNode &child)> &predicate, bool recursive) const;
QString operatorString() const;
enum class FileStatus { Ours, Foreign, Mixed, Unknown };
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index 53a5b967aa..0082ff6cef 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -9,12 +9,10 @@
#include "clangdcompletion.h"
#include "clangdfindreferences.h"
#include "clangdfollowsymbol.h"
-#include "clangdlocatorfilters.h"
#include "clangdmemoryusagewidget.h"
#include "clangdquickfixes.h"
#include "clangdsemantichighlighting.h"
#include "clangdswitchdecldef.h"
-#include "clangmodelmanagersupport.h"
#include "clangtextmark.h"
#include "clangutils.h"
#include "tasktimers.h"
@@ -38,6 +36,7 @@
#include <languageclient/languageclienthoverhandler.h>
#include <languageclient/languageclientinterface.h>
#include <languageclient/languageclientmanager.h>
+#include <languageclient/languageclientoutline.h>
#include <languageclient/languageclientsymbolsupport.h>
#include <languageclient/languageclientutils.h>
#include <languageclient/progressmanager.h>
@@ -48,7 +47,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <texteditor/codeassist/assistinterface.h>
@@ -57,10 +56,11 @@
#include <texteditor/codeassist/textdocumentmanipulatorinterface.h>
#include <texteditor/texteditor.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/itemviews.h>
-#include <utils/runextensions.h>
+#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -128,6 +128,27 @@ public:
: Request("textDocument/symbolInfo", params) {}
};
+class ClangdOutlineItem : public LanguageClientOutlineItem
+{
+ using LanguageClientOutlineItem::LanguageClientOutlineItem;
+private:
+ QVariant data(int column, int role) const override
+ {
+ switch (role) {
+ case Qt::DisplayRole:
+ return ClangdClient::displayNameFromDocumentSymbol(
+ static_cast<SymbolKind>(type()), name(), detail());
+ case Qt::ForegroundRole:
+ if ((detail().endsWith("class") || detail().endsWith("struct"))
+ && range().end() == selectionRange().end()) {
+ return creatorTheme()->color(Theme::TextColorDisabled);
+ }
+ break;
+ }
+ return LanguageClientOutlineItem::data(column, role);
+ }
+};
+
void setupClangdConfigFile()
{
const Utils::FilePath targetConfigFile = CppEditor::ClangdSettings::clangdUserConfigFilePath();
@@ -192,7 +213,7 @@ static BaseClientInterface *clientInterface(Project *project, const Utils::FileP
if (settings.clangdVersion() >= QVersionNumber(16))
cmd.addArg("--rename-file-limit=0");
if (!jsonDbDir.isEmpty())
- cmd.addArg("--compile-commands-dir=" + jsonDbDir.onDevice(clangdExePath).path());
+ cmd.addArg("--compile-commands-dir=" + clangdExePath.withNewMappedPath(jsonDbDir).path());
if (clangdLogServer().isDebugEnabled())
cmd.addArgs({"--log=verbose", "--pretty"});
cmd.addArg("--use-dirty-headers");
@@ -427,7 +448,6 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
});
setCurrentProject(project);
setDocumentChangeUpdateThreshold(d->settings.documentUpdateThreshold);
- setSymbolStringifier(displayNameFromDocumentSymbol);
setSemanticTokensHandler([this](TextDocument *doc, const QList<ExpandedSemanticToken> &tokens,
int version, bool force) {
d->handleSemanticTokens(doc, tokens, version, force);
@@ -450,12 +470,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c
}
});
- connect(this, &Client::initialized, this, [this] {
- auto currentDocumentFilter = static_cast<ClangdCurrentDocumentFilter *>(
- CppEditor::CppModelManager::instance()->currentDocumentFilter());
- currentDocumentFilter->updateCurrentClient();
- d->openedExtraFiles.clear();
- });
+ connect(this, &Client::initialized, this, [this] { d->openedExtraFiles.clear(); });
start();
}
@@ -698,6 +713,12 @@ DiagnosticManager *ClangdClient::createDiagnosticManager()
return diagnosticManager;
}
+LanguageClientOutlineItem *ClangdClient::createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol)
+{
+ return new ClangdOutlineItem(this, symbol);
+}
+
bool ClangdClient::referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate)
{
@@ -710,7 +731,7 @@ bool ClangdClient::fileBelongsToProject(const Utils::FilePath &filePath) const
{
if (CppEditor::ClangdSettings::instance().granularity()
== CppEditor::ClangdSettings::Granularity::Session) {
- return SessionManager::projectForFile(filePath);
+ return ProjectManager::projectForFile(filePath);
}
return Client::fileBelongsToProject(filePath);
}
@@ -1477,7 +1498,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
clangdVersion = q->versionNumber(),
this] {
try {
- return Utils::runAsync(doSemanticHighlighting, filePath, tokens, text, ast, doc,
+ return Utils::asyncRun(doSemanticHighlighting, filePath, tokens, text, ast, doc,
rev, clangdVersion, highlightingTimer);
} catch (const std::exception &e) {
qWarning() << "caught" << e.what() << "in main highlighting thread";
diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h
index 67416d7379..27ec66a301 100644
--- a/src/plugins/clangcodemodel/clangdclient.h
+++ b/src/plugins/clangcodemodel/clangdclient.h
@@ -3,12 +3,12 @@
#pragma once
-#include <coreplugin/find/searchresultitem.h>
#include <cppeditor/baseeditordocumentparser.h>
#include <cppeditor/cppcodemodelsettings.h>
#include <cppeditor/cursorineditor.h>
#include <languageclient/client.h>
#include <utils/link.h>
+#include <utils/searchresultitem.h>
#include <QVersionNumber>
@@ -119,7 +119,7 @@ public:
signals:
void indexingFinished();
- void foundReferences(const QList<Core::SearchResultItem> &items);
+ void foundReferences(const Utils::SearchResultItems &items);
void findUsagesDone();
void helpItemGathered(const Core::HelpItem &helpItem);
void highlightingResultsReady(const TextEditor::HighlightingResults &results,
@@ -137,6 +137,8 @@ private:
const CustomInspectorTabs createCustomInspectorTabs() override;
TextEditor::RefactoringChangesData *createRefactoringChangesBackend() const override;
LanguageClient::DiagnosticManager *createDiagnosticManager() override;
+ LanguageClient::LanguageClientOutlineItem *createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol) override;
bool referencesShadowFile(const TextEditor::TextDocument *doc,
const Utils::FilePath &candidate) override;
bool fileBelongsToProject(const Utils::FilePath &filePath) const override;
diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp
index 8a2d3faaef..b44fd41a82 100644
--- a/src/plugins/clangcodemodel/clangdfindreferences.cpp
+++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp
@@ -21,9 +21,10 @@
#include <languageserverprotocol/lsptypes.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/basefilefind.h>
@@ -71,7 +72,7 @@ public:
const Position linkAsPosition;
const QPointer<SearchResult> search;
const LinkHandler callback;
- QList<SearchResultItem> declDefItems;
+ SearchResultItems declDefItems;
bool openedExtraFileForLink = false;
bool declHasUsedTag = false;
bool recursiveCallDetected = false;
@@ -88,7 +89,7 @@ public:
const SearchResult *search,
const ReplacementData &replacementData,
const QString &newSymbolName,
- const QList<SearchResultItem> &checkedItems,
+ const SearchResultItems &checkedItems,
bool preserveCase);
void handleFindUsagesResult(const QList<Location> &locations);
void finishSearch();
@@ -142,7 +143,7 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d
d->search->setAdditionalReplaceWidget(renameFilesCheckBox);
const auto renameHandler =
[search = d->search](const QString &newSymbolName,
- const QList<SearchResultItem> &checkedItems,
+ const SearchResultItems &checkedItems,
bool preserveCase) {
const auto replacementData = search->userData().value<ReplacementData>();
Private::handleRenameRequest(search, replacementData, newSymbolName, checkedItems,
@@ -150,7 +151,7 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d
};
connect(d->search, &SearchResult::replaceButtonClicked, renameHandler);
}
- connect(d->search, &SearchResult::activated, [](const SearchResultItem& item) {
+ connect(d->search, &SearchResult::activated, [](const SearchResultItem &item) {
EditorManager::openEditorAtSearchResult(item);
});
if (d->search->isInteractive())
@@ -242,7 +243,7 @@ void ClangdFindReferences::Private::handleRenameRequest(
const SearchResult *search,
const ReplacementData &replacementData,
const QString &newSymbolName,
- const QList<SearchResultItem> &checkedItems,
+ const SearchResultItems &checkedItems,
bool preserveCase)
{
const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems,
@@ -390,7 +391,7 @@ static Usage::Tags getUsageType(const ClangdAstPath &path, const QString &search
void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file,
const ReferencesFileData &fileData)
{
- QList<SearchResultItem> items;
+ SearchResultItems items;
qCDebug(clangdLog) << file << "has valid AST:" << fileData.ast.isValid();
const auto expectedDeclTypes = [this]() -> QStringList {
if (checkUnusedData)
@@ -451,7 +452,7 @@ void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file
item.setContainingFunctionName(getContainingFunction(astPath, range).detail());
if (search->supportsReplace()) {
- const bool fileInSession = SessionManager::projectForFile(file);
+ const bool fileInSession = ProjectManager::projectForFile(file);
item.setSelectForReplacement(fileInSession);
if (fileInSession && file.baseName().compare(replacementData->oldSymbolName,
Qt::CaseInsensitive) == 0) {
@@ -594,6 +595,19 @@ static Usage::Tags getUsageType(const ClangdAstPath &path, const QString &search
tags |= Usage::Tag::Operator;
}
}
+ if (pathIt->kind() == "CXXMethod") {
+ const ClangdAstNode &classNode = *std::next(pathIt);
+ if (classNode.hasChild([&](const ClangdAstNode &n) {
+ if (n.kind() != "StaticAssert")
+ return false;
+ return n.hasChild([&](const ClangdAstNode &n) {
+ return n.arcanaContains("Q_PROPERTY"); }, true)
+ && n.hasChild([&](const ClangdAstNode &n) {
+ return n.arcanaContains(" " + searchTerm); }, true);
+ }, false)) {
+ tags |= Usage::Tag::MocInvokable;
+ }
+ }
return tags;
}
if (pathIt->kind() == "MemberInitializer")
diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h
index d904db31f4..e110b35543 100644
--- a/src/plugins/clangcodemodel/clangdfindreferences.h
+++ b/src/plugins/clangcodemodel/clangdfindreferences.h
@@ -3,9 +3,9 @@
#pragma once
-#include <coreplugin/find/searchresultitem.h>
#include <cppeditor/cursorineditor.h>
#include <utils/link.h>
+#include <utils/searchresultitem.h>
#include <QObject>
@@ -35,7 +35,7 @@ public:
~ClangdFindReferences();
signals:
- void foundReferences(const QList<Core::SearchResultItem> &items);
+ void foundReferences(const Utils::SearchResultItems &items);
void done();
private:
diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
index 725d961591..f783a98499 100644
--- a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
+++ b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp
@@ -9,290 +9,205 @@
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppeditortr.h>
#include <cppeditor/cpplocatorfilter.h>
-#include <cppeditor/cppmodelmanager.h>
-#include <cppeditor/indexitem.h>
-#include <languageclient/languageclientutils.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <languageclient/currentdocumentsymbolsrequest.h>
#include <languageclient/locatorfilter.h>
-#include <projectexplorer/session.h>
-#include <utils/link.h>
-#include <set>
-#include <tuple>
+#include <utils/algorithm.h>
+#include <utils/async.h>
+
+#include <QHash>
+using namespace Core;
using namespace LanguageClient;
using namespace LanguageServerProtocol;
+using namespace ProjectExplorer;
+using namespace TextEditor;
using namespace Utils;
-namespace ClangCodeModel {
-namespace Internal {
+namespace ClangCodeModel::Internal {
const int MaxResultCount = 10000;
-class CppLocatorFilter : public CppEditor::CppLocatorFilter
-{
-public:
- CppLocatorFilter()
- : CppEditor::CppLocatorFilter(CppEditor::CppModelManager::instance()->locatorData())
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- }
-};
-
-class LspWorkspaceFilter : public WorkspaceLocatorFilter
-{
-public:
- LspWorkspaceFilter()
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- setMaxResultCount(MaxResultCount);
- }
-};
-
-
-class CppClassesFilter : public CppEditor::CppClassesFilter
-{
-public:
- CppClassesFilter()
- : CppEditor::CppClassesFilter(CppEditor::CppModelManager::instance()->locatorData())
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- }
-};
-
-class LspClassesFilter : public WorkspaceClassLocatorFilter
-{
-public:
- LspClassesFilter() {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- setMaxResultCount(MaxResultCount);
- }
-};
-
-class CppFunctionsFilter : public CppEditor::CppFunctionsFilter
-{
-public:
- CppFunctionsFilter()
- : CppEditor::CppFunctionsFilter(CppEditor::CppModelManager::instance()->locatorData())
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- }
-};
-
-class LspFunctionsFilter : public WorkspaceMethodLocatorFilter
-{
-public:
- LspFunctionsFilter()
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- setMaxResultCount(MaxResultCount);
- }
-};
-
-
-ClangGlobalSymbolFilter::ClangGlobalSymbolFilter()
- : ClangGlobalSymbolFilter(new CppLocatorFilter, new LspWorkspaceFilter)
-{
-}
-
-ClangGlobalSymbolFilter::ClangGlobalSymbolFilter(ILocatorFilter *cppFilter,
- WorkspaceLocatorFilter *lspFilter)
- : m_cppFilter(cppFilter), m_lspFilter(lspFilter)
+ClangdAllSymbolsFilter::ClangdAllSymbolsFilter()
{
setId(CppEditor::Constants::LOCATOR_FILTER_ID);
setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::LOCATOR_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::LOCATOR_FILTER_DESCRIPTION));
setDefaultShortcutString(":");
- setDefaultIncludedByDefault(false);
}
-ClangGlobalSymbolFilter::~ClangGlobalSymbolFilter()
+LocatorMatcherTasks ClangdAllSymbolsFilter::matchers()
{
- delete m_cppFilter;
- delete m_lspFilter;
+ return CppEditor::cppMatchers(MatcherType::AllSymbols)
+ + LanguageClient::languageClientMatchers(MatcherType::AllSymbols,
+ ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount);
}
-void ClangGlobalSymbolFilter::prepareSearch(const QString &entry)
+ClangdClassesFilter::ClangdClassesFilter()
{
- m_cppFilter->prepareSearch(entry);
- QList<Client *> clients;
- for (ProjectExplorer::Project * const project : ProjectExplorer::SessionManager::projects()) {
- if (Client * const client = ClangModelManagerSupport::clientForProject(project))
- clients << client;
- }
- if (!clients.isEmpty())
- m_lspFilter->prepareSearch(entry, clients);
+ setId(CppEditor::Constants::CLASSES_FILTER_ID);
+ setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CLASSES_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::CLASSES_FILTER_DESCRIPTION));
+ setDefaultShortcutString("c");
}
-QList<Core::LocatorFilterEntry> ClangGlobalSymbolFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+LocatorMatcherTasks ClangdClassesFilter::matchers()
{
- QList<Core::LocatorFilterEntry> matches = m_cppFilter->matchesFor(future, entry);
- const QList<Core::LocatorFilterEntry> lspMatches = m_lspFilter->matchesFor(future, entry);
- if (!lspMatches.isEmpty()) {
- std::set<std::tuple<FilePath, int, int>> locations;
- for (const auto &entry : std::as_const(matches)) {
- const CppEditor::IndexItem::Ptr item
- = qvariant_cast<CppEditor::IndexItem::Ptr>(entry.internalData);
- locations.insert(std::make_tuple(item->filePath(), item->line(), item->column()));
- }
- for (const auto &entry : lspMatches) {
- if (!entry.internalData.canConvert<Link>())
- continue;
- const auto link = qvariant_cast<Link>(entry.internalData);
- if (locations.find(std::make_tuple(link.targetFilePath, link.targetLine,
- link.targetColumn)) == locations.cend()) {
- matches << entry; // TODO: Insert sorted?
- }
- }
- }
-
- return matches;
+ return CppEditor::cppMatchers(MatcherType::Classes)
+ + LanguageClient::languageClientMatchers(MatcherType::Classes,
+ ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount);
}
-void ClangGlobalSymbolFilter::accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+ClangdFunctionsFilter::ClangdFunctionsFilter()
{
- if (qvariant_cast<CppEditor::IndexItem::Ptr>(selection.internalData))
- m_cppFilter->accept(selection, newText, selectionStart, selectionLength);
- else
- m_lspFilter->accept(selection, newText, selectionStart, selectionLength);
+ setId(CppEditor::Constants::FUNCTIONS_FILTER_ID);
+ setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::FUNCTIONS_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::FUNCTIONS_FILTER_DESCRIPTION));
+ setDefaultShortcutString("m");
}
-
-ClangClassesFilter::ClangClassesFilter()
- : ClangGlobalSymbolFilter(new CppClassesFilter, new LspClassesFilter)
+LocatorMatcherTasks ClangdFunctionsFilter::matchers()
{
- setId(CppEditor::Constants::CLASSES_FILTER_ID);
- setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CLASSES_FILTER_DISPLAY_NAME));
- setDefaultShortcutString("c");
- setDefaultIncludedByDefault(false);
+ return CppEditor::cppMatchers(MatcherType::Functions)
+ + LanguageClient::languageClientMatchers(MatcherType::Functions,
+ ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount);
}
-ClangFunctionsFilter::ClangFunctionsFilter()
- : ClangGlobalSymbolFilter(new CppFunctionsFilter, new LspFunctionsFilter)
+ClangdCurrentDocumentFilter::ClangdCurrentDocumentFilter()
{
- setId(CppEditor::Constants::FUNCTIONS_FILTER_ID);
- setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::FUNCTIONS_FILTER_DISPLAY_NAME));
- setDefaultShortcutString("m");
- setDefaultIncludedByDefault(false);
+ setId(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_ID);
+ setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
+ setDescription(::CppEditor::Tr::tr(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DESCRIPTION));
+ setDefaultShortcutString(".");
+ setPriority(High);
+ setEnabled(false);
+ connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
+ this, [this](const IEditor *editor) { setEnabled(editor); });
}
-class LspCurrentDocumentFilter : public DocumentLocatorFilter
+static void filterCurrentResults(QPromise<void> &promise, const LocatorStorage &storage,
+ const CurrentDocumentSymbolsData &currentSymbolsData,
+ const QString &contents)
{
-public:
- LspCurrentDocumentFilter()
- {
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
- forceUse();
- }
-
-private:
- Core::LocatorFilterEntry generateLocatorEntry(const DocumentSymbol &info,
- const Core::LocatorFilterEntry &parent) override
+ Q_UNUSED(promise)
+ struct Entry
{
- Core::LocatorFilterEntry entry;
- entry.filter = this;
+ LocatorFilterEntry entry;
+ DocumentSymbol symbol;
+ };
+ QList<Entry> docEntries;
+
+ const auto docSymbolModifier = [&docEntries](LocatorFilterEntry &entry,
+ const DocumentSymbol &info,
+ const LocatorFilterEntry &parent) {
entry.displayName = ClangdClient::displayNameFromDocumentSymbol(
- static_cast<SymbolKind>(info.kind()), info.name(),
- info.detail().value_or(QString()));
- const Position &pos = info.range().start();
- entry.internalData = QVariant::fromValue(LineColumn(pos.line(), pos.character()));
+ static_cast<SymbolKind>(info.kind()), info.name(),
+ info.detail().value_or(QString()));
entry.extraInfo = parent.extraInfo;
if (!entry.extraInfo.isEmpty())
entry.extraInfo.append("::");
entry.extraInfo.append(parent.displayName);
// TODO: Can we extend clangd to send visibility information?
- entry.displayIcon = LanguageClient::symbolIcon(info.kind());
-
+ docEntries.append({entry, info});
return entry;
+ };
+ // TODO: Pass promise into currentSymbols
+ const LocatorFilterEntries allMatches = LanguageClient::currentDocumentSymbols(storage.input(),
+ currentSymbolsData, docSymbolModifier);
+ if (docEntries.isEmpty()) {
+ storage.reportOutput(allMatches);
+ return; // SymbolInformation case
}
-};
-
-class ClangdCurrentDocumentFilter::Private
-{
-public:
- ~Private() { delete cppFilter; }
-
- Core::ILocatorFilter * const cppFilter
- = CppEditor::CppModelManager::createAuxiliaryCurrentDocumentFilter();
- LspCurrentDocumentFilter lspFilter;
- Core::ILocatorFilter *activeFilter = nullptr;
-};
+ QTC_CHECK(docEntries.size() == allMatches.size());
+ QHash<QString, QList<Entry>> possibleDuplicates;
+ for (const Entry &e : std::as_const(docEntries))
+ possibleDuplicates[e.entry.displayName + e.entry.extraInfo] << e;
+ const QTextDocument doc(contents);
+ for (auto it = possibleDuplicates.cbegin(); it != possibleDuplicates.cend(); ++it) {
+ const QList<Entry> &duplicates = it.value();
+ if (duplicates.size() == 1)
+ continue;
+ QList<Entry> declarations;
+ QList<Entry> definitions;
+ for (const Entry &candidate : duplicates) {
+ const DocumentSymbol symbol = candidate.symbol;
+ const SymbolKind kind = static_cast<SymbolKind>(symbol.kind());
+ if (kind != SymbolKind::Class && kind != SymbolKind::Function)
+ break;
+ const Range range = symbol.range();
+ const Range selectionRange = symbol.selectionRange();
+ if (kind == SymbolKind::Class) {
+ if (range.end() == selectionRange.end())
+ declarations << candidate;
+ else
+ definitions << candidate;
+ continue;
+ }
+ const int startPos = selectionRange.end().toPositionInDocument(&doc);
+ const int endPos = range.end().toPositionInDocument(&doc);
+ const QString functionBody = contents.mid(startPos, endPos - startPos);
+
+ // Hacky, but I don't see anything better.
+ if (functionBody.contains('{') && functionBody.contains('}'))
+ definitions << candidate;
+ else
+ declarations << candidate;
+ }
+ if (definitions.size() == 1
+ && declarations.size() + definitions.size() == duplicates.size()) {
+ for (const Entry &decl : std::as_const(declarations)) {
+ Utils::erase(docEntries, [&decl](const Entry &e) {
+ return e.symbol == decl.symbol;
+ });
+ }
+ }
+ }
+ storage.reportOutput(Utils::transform(docEntries,
+ [](const Entry &entry) { return entry.entry; }));
+}
-ClangdCurrentDocumentFilter::ClangdCurrentDocumentFilter() : d(new Private)
+LocatorMatcherTask currentDocumentMatcher()
{
- setId(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_ID);
- setDisplayName(::CppEditor::Tr::tr(CppEditor::Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(".");
- setPriority(High);
- setDefaultIncludedByDefault(false);
- setEnabled(false);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
- this, [this](const Core::IEditor *editor) { setEnabled(editor); });
-}
+ using namespace Tasking;
-ClangdCurrentDocumentFilter::~ClangdCurrentDocumentFilter() { delete d; }
+ TreeStorage<LocatorStorage> storage;
+ TreeStorage<CurrentDocumentSymbolsData> resultStorage;
-void ClangdCurrentDocumentFilter::updateCurrentClient()
-{
- d->lspFilter.updateCurrentClient();
-}
+ const auto onQuerySetup = [=](CurrentDocumentSymbolsRequest &request) {
+ Q_UNUSED(request)
+ };
+ const auto onQueryDone = [resultStorage](const CurrentDocumentSymbolsRequest &request) {
+ *resultStorage = request.currentDocumentSymbolsData();
+ };
-void ClangdCurrentDocumentFilter::prepareSearch(const QString &entry)
-{
- const auto doc = TextEditor::TextDocument::currentTextDocument();
- QTC_ASSERT(doc, return);
- if (const ClangdClient * const client = ClangModelManagerSupport::clientForFile
- (doc->filePath()); client && client->reachable()) {
- d->activeFilter = &d->lspFilter;
- } else {
- d->activeFilter = d->cppFilter;
- }
- d->activeFilter->prepareSearch(entry);
-}
+ const auto onFilterSetup = [=](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage,
+ TextDocument::currentTextDocument()->plainText());
+ };
-QList<Core::LocatorFilterEntry> ClangdCurrentDocumentFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-{
- QTC_ASSERT(d->activeFilter, return {});
- return d->activeFilter->matchesFor(future, entry);
+ const Group root {
+ Storage(resultStorage),
+ CurrentDocumentSymbolsRequestTask(onQuerySetup, onQueryDone),
+ AsyncTask<void>(onFilterSetup)
+ };
+ return {root, storage};
}
-void ClangdCurrentDocumentFilter::accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+LocatorMatcherTasks ClangdCurrentDocumentFilter::matchers()
{
- QTC_ASSERT(d->activeFilter, return);
- d->activeFilter->accept(selection, newText, selectionStart, selectionLength);
+ const auto doc = TextDocument::currentTextDocument();
+ QTC_ASSERT(doc, return {});
+ if (const ClangdClient *client = ClangModelManagerSupport::clientForFile(doc->filePath());
+ client && client->reachable()) {
+ return {currentDocumentMatcher()};
+ }
+ return CppEditor::cppMatchers(MatcherType::CurrentDocumentSymbols);
}
-} // namespace Internal
-} // namespace ClangCodeModel
+} // namespace ClangCodeModel::Internal
diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.h b/src/plugins/clangcodemodel/clangdlocatorfilters.h
index f7deacc760..1aba669417 100644
--- a/src/plugins/clangcodemodel/clangdlocatorfilters.h
+++ b/src/plugins/clangcodemodel/clangdlocatorfilters.h
@@ -5,60 +5,42 @@
#include <coreplugin/locator/ilocatorfilter.h>
-namespace LanguageClient { class WorkspaceLocatorFilter; }
+namespace ClangCodeModel::Internal {
-namespace ClangCodeModel {
-namespace Internal {
-
-class ClangGlobalSymbolFilter : public Core::ILocatorFilter
+class ClangdAllSymbolsFilter : public Core::ILocatorFilter
{
public:
- ClangGlobalSymbolFilter();
- ClangGlobalSymbolFilter(Core::ILocatorFilter *cppFilter,
- LanguageClient::WorkspaceLocatorFilter *lspFilter);
- ~ClangGlobalSymbolFilter() override;
+ ClangdAllSymbolsFilter();
private:
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
-
- Core::ILocatorFilter * const m_cppFilter;
- LanguageClient::WorkspaceLocatorFilter * const m_lspFilter;
+ Core::LocatorMatcherTasks matchers() final;
};
-class ClangClassesFilter : public ClangGlobalSymbolFilter
+class ClangdClassesFilter : public Core::ILocatorFilter
{
public:
- ClangClassesFilter();
+ ClangdClassesFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class ClangFunctionsFilter : public ClangGlobalSymbolFilter
+class ClangdFunctionsFilter : public Core::ILocatorFilter
{
public:
- ClangFunctionsFilter();
+ ClangdFunctionsFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
class ClangdCurrentDocumentFilter : public Core::ILocatorFilter
{
public:
ClangdCurrentDocumentFilter();
- ~ClangdCurrentDocumentFilter() override;
-
- void updateCurrentClient();
private:
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
-
- class Private;
- Private * const d;
+ Core::LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace ClangCodeModel
+} // namespace ClangCodeModel::Internal
diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
index cff8253de6..30ffaa780e 100644
--- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
+++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
@@ -112,7 +112,7 @@ static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const
class ExtraHighlightingResultsCollector
{
public:
- ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
+ ExtraHighlightingResultsCollector(QPromise<HighlightingResult> &promise,
HighlightingResults &results,
const Utils::FilePath &filePath, const ClangdAstNode &ast,
const QTextDocument *doc, const QString &docContent,
@@ -131,7 +131,7 @@ private:
void collectFromNode(const ClangdAstNode &node);
void visitNode(const ClangdAstNode&node);
- QFutureInterface<HighlightingResult> &m_future;
+ QPromise<HighlightingResult> &m_promise;
HighlightingResults &m_results;
const Utils::FilePath m_filePath;
const ClangdAstNode &m_ast;
@@ -142,7 +142,7 @@ private:
};
void doSemanticHighlighting(
- QFutureInterface<HighlightingResult> &future,
+ QPromise<HighlightingResult> &promise,
const Utils::FilePath &filePath,
const QList<ExpandedSemanticToken> &tokens,
const QString &docContents,
@@ -153,10 +153,8 @@ void doSemanticHighlighting(
const TaskTimer &taskTimer)
{
ThreadedSubtaskTimer t("highlighting", taskTimer);
- if (future.isCanceled()) {
- future.reportFinished();
+ if (promise.isCanceled())
return;
- }
const QTextDocument doc(docContents);
const auto tokenRange = [&doc](const ExpandedSemanticToken &token) {
@@ -399,13 +397,13 @@ void doSemanticHighlighting(
};
auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, safeToResult);
const QList<BlockRange> ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
- ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents,
+ ExtraHighlightingResultsCollector(promise, results, filePath, ast, &doc, docContents,
clangdVersion).collect();
Utils::erase(results, [](const HighlightingResult &res) {
// QTCREATORBUG-28639
return res.textStyles.mainStyle == C_TEXT && res.textStyles.mixinStyles.empty();
});
- if (!future.isCanceled()) {
+ if (!promise.isCanceled()) {
qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results";
QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] {
if (textDocument && textDocument->document()->revision() == docRevision)
@@ -423,16 +421,16 @@ void doSemanticHighlighting(
if (ClangdClient * const client = ClangModelManagerSupport::clientForFile(filePath))
client->setVirtualRanges(filePath, virtualRanges, docRevision);
}, Qt::QueuedConnection);
- future.reportResults(QVector<HighlightingResult>(results.cbegin(), results.cend()));
+ for (const HighlightingResult &r : results)
+ promise.addResult(r);
}
- future.reportFinished();
}
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
- QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
+ QPromise<HighlightingResult> &promise, HighlightingResults &results,
const Utils::FilePath &filePath, const ClangdAstNode &ast, const QTextDocument *doc,
const QString &docContent, const QVersionNumber &clangdVersion)
- : m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
+ : m_promise(promise), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
m_docContent(docContent), m_clangdVersion(clangdVersion.majorVersion())
{
}
@@ -916,7 +914,7 @@ void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &nod
void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node)
{
- if (m_future.isCanceled())
+ if (m_promise.isCanceled())
return;
const ClangdAstNode::FileStatus prevFileStatus = m_currentFileStatus;
m_currentFileStatus = node.fileStatus(m_filePath);
diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.h b/src/plugins/clangcodemodel/clangdsemantichighlighting.h
index 10bc4b8d09..a7f667d459 100644
--- a/src/plugins/clangcodemodel/clangdsemantichighlighting.h
+++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.h
@@ -3,11 +3,15 @@
#pragma once
-#include <QFutureInterface>
#include <QLoggingCategory>
#include <QPointer>
#include <QVersionNumber>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+QT_END_NAMESPACE
+
namespace LanguageClient { class ExpandedSemanticToken; }
namespace TextEditor {
class HighlightingResult;
@@ -21,7 +25,7 @@ class TaskTimer;
Q_DECLARE_LOGGING_CATEGORY(clangdLogHighlight);
void doSemanticHighlighting(
- QFutureInterface<TextEditor::HighlightingResult> &future,
+ QPromise<TextEditor::HighlightingResult> &promise,
const Utils::FilePath &filePath,
const QList<LanguageClient::ExpandedSemanticToken> &tokens,
const QString &docContents,
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
index 248e97349f..1dd66db3cd 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -27,12 +27,14 @@
#include <cppeditor/editordocumenthandle.h>
#include <languageclient/languageclientmanager.h>
+#include <languageclient/locatorfilter.h>
#include <texteditor/quickfix.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/session.h>
@@ -40,9 +42,9 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/infobar.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QApplication>
#include <QLabel>
@@ -51,22 +53,24 @@
#include <QTimer>
#include <QtDebug>
+using namespace Core;
using namespace CppEditor;
using namespace LanguageClient;
+using namespace ProjectExplorer;
using namespace Utils;
namespace ClangCodeModel::Internal {
-static CppEditor::CppModelManager *cppModelManager()
+static CppModelManager *cppModelManager()
{
- return CppEditor::CppModelManager::instance();
+ return CppModelManager::instance();
}
-static ProjectExplorer::Project *fallbackProject()
+static Project *fallbackProject()
{
- if (ProjectExplorer::Project * const p = ProjectExplorer::ProjectTree::currentProject())
+ if (Project * const p = ProjectTree::currentProject())
return p;
- return ProjectExplorer::SessionManager::startupProject();
+ return ProjectManager::startupProject();
}
static bool sessionModeEnabled()
@@ -76,18 +80,17 @@ static bool sessionModeEnabled()
static const QList<TextEditor::TextDocument *> allCppDocuments()
{
- const auto isCppDocument = Utils::equal(&Core::IDocument::id,
- Utils::Id(CppEditor::Constants::CPPEDITOR_ID));
- const QList<Core::IDocument *> documents
- = Utils::filtered(Core::DocumentModel::openedDocuments(), isCppDocument);
+ const auto isCppDocument = Utils::equal(&IDocument::id, Id(CppEditor::Constants::CPPEDITOR_ID));
+ const QList<IDocument *> documents = Utils::filtered(DocumentModel::openedDocuments(),
+ isCppDocument);
return Utils::qobject_container_cast<TextEditor::TextDocument *>(documents);
}
-static const QList<ProjectExplorer::Project *> projectsForClient(const Client *client)
+static const QList<Project *> projectsForClient(const Client *client)
{
- QList<ProjectExplorer::Project *> projects;
+ QList<Project *> projects;
if (sessionModeEnabled()) {
- for (ProjectExplorer::Project * const p : ProjectExplorer::SessionManager::projects()) {
+ for (Project * const p : ProjectManager::projects()) {
if (ClangdProjectSettings(p).settings().useClangd)
projects << p;
}
@@ -99,7 +102,7 @@ static const QList<ProjectExplorer::Project *> projectsForClient(const Client *c
static bool fileIsProjectBuildArtifact(const Client *client, const FilePath &filePath)
{
- for (const ProjectExplorer::Project * const p : projectsForClient(client)) {
+ for (const Project * const p : projectsForClient(client)) {
if (const auto t = p->activeTarget()) {
if (const auto bc = t->activeBuildConfiguration()) {
if (filePath.isChildOf(bc->buildDirectory()))
@@ -144,15 +147,15 @@ static void checkSystemForClangdSuitability()
"You can enable/disable and fine-tune clangd <a href=\"dummy\">here</a>."));
label->setWordWrap(true);
QObject::connect(label, &QLabel::linkActivated, [] {
- Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CLANGD_SETTINGS_ID);
+ ICore::showOptionsDialog(CppEditor::Constants::CPP_CLANGD_SETTINGS_ID);
});
return label;
});
info.addCustomButton(Tr::tr("Enable Anyway"), [clangdWarningSetting] {
ClangdSettings::setUseClangdAndSave(true);
- Core::ICore::infoBar()->removeInfo(clangdWarningSetting);
+ ICore::infoBar()->removeInfo(clangdWarningSetting);
});
- Core::ICore::infoBar()->addInfo(info);
+ ICore::infoBar()->addInfo(info);
}
static void updateParserConfig(ClangdClient *client)
@@ -170,8 +173,8 @@ static void updateParserConfig(ClangdClient *client)
static bool projectIsParsing(const ClangdClient *client)
{
- for (const ProjectExplorer::Project * const p : projectsForClient(client)) {
- const ProjectExplorer::BuildSystem * const bs = p && p->activeTarget()
+ for (const Project * const p : projectsForClient(client)) {
+ const BuildSystem * const bs = p && p->activeTarget()
? p->activeTarget()->buildSystem() : nullptr;
if (bs && (bs->isParsing() || bs->isWaitingForParse()))
return true;
@@ -179,7 +182,6 @@ static bool projectIsParsing(const ClangdClient *client)
return false;
}
-
ClangModelManagerSupport::ClangModelManagerSupport()
: m_clientRestartTimer(new QTimer(this))
{
@@ -200,24 +202,37 @@ ClangModelManagerSupport::ClangModelManagerSupport()
setupClangdConfigFile();
checkSystemForClangdSuitability();
cppModelManager()->setCurrentDocumentFilter(std::make_unique<ClangdCurrentDocumentFilter>());
- cppModelManager()->setLocatorFilter(std::make_unique<ClangGlobalSymbolFilter>());
- cppModelManager()->setClassesFilter(std::make_unique<ClangClassesFilter>());
- cppModelManager()->setFunctionsFilter(std::make_unique<ClangFunctionsFilter>());
+ cppModelManager()->setLocatorFilter(std::make_unique<ClangdAllSymbolsFilter>());
+ cppModelManager()->setClassesFilter(std::make_unique<ClangdClassesFilter>());
+ cppModelManager()->setFunctionsFilter(std::make_unique<ClangdFunctionsFilter>());
+ // Setup matchers
+ LocatorMatcher::addMatcherCreator(MatcherType::AllSymbols, [] {
+ return LanguageClient::languageClientMatchers(
+ MatcherType::AllSymbols, clientsForOpenProjects(), 10000);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Classes, [] {
+ return LanguageClient::languageClientMatchers(
+ MatcherType::Classes, clientsForOpenProjects(), 10000);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Functions, [] {
+ return LanguageClient::languageClientMatchers(
+ MatcherType::Functions, clientsForOpenProjects(), 10000);
+ });
- Core::EditorManager *editorManager = Core::EditorManager::instance();
- connect(editorManager, &Core::EditorManager::editorOpened,
+ EditorManager *editorManager = EditorManager::instance();
+ connect(editorManager, &EditorManager::editorOpened,
this, &ClangModelManagerSupport::onEditorOpened);
- connect(editorManager, &Core::EditorManager::currentEditorChanged,
+ connect(editorManager, &EditorManager::currentEditorChanged,
this, &ClangModelManagerSupport::onCurrentEditorChanged);
- CppEditor::CppModelManager *modelManager = cppModelManager();
- connect(modelManager, &CppEditor::CppModelManager::abstractEditorSupportContentsUpdated,
+ CppModelManager *modelManager = cppModelManager();
+ connect(modelManager, &CppModelManager::abstractEditorSupportContentsUpdated,
this, &ClangModelManagerSupport::onAbstractEditorSupportContentsUpdated);
- connect(modelManager, &CppEditor::CppModelManager::abstractEditorSupportRemoved,
+ connect(modelManager, &CppModelManager::abstractEditorSupportRemoved,
this, &ClangModelManagerSupport::onAbstractEditorSupportRemoved);
- connect(modelManager, &CppEditor::CppModelManager::projectPartsUpdated,
+ connect(modelManager, &CppModelManager::projectPartsUpdated,
this, &ClangModelManagerSupport::onProjectPartsUpdated);
- connect(modelManager, &CppEditor::CppModelManager::projectPartsRemoved,
+ connect(modelManager, &CppModelManager::projectPartsRemoved,
this, &ClangModelManagerSupport::onProjectPartsRemoved);
connect(modelManager, &CppModelManager::fallbackProjectPartUpdated, this, [this] {
if (sessionModeEnabled())
@@ -228,26 +243,23 @@ ClangModelManagerSupport::ClangModelManagerSupport()
}
});
- auto *sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::projectRemoved,
- this, [this] {
+ auto projectManager = ProjectManager::instance();
+ connect(projectManager, &ProjectManager::projectRemoved, this, [this] {
if (!sessionModeEnabled())
claimNonProjectSources(clientForProject(fallbackProject()));
});
- connect(sessionManager, &ProjectExplorer::SessionManager::sessionLoaded,
- this, [this] {
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded, this, [this] {
if (sessionModeEnabled())
onClangdSettingsChanged();
});
- CppEditor::ClangdSettings::setDefaultClangdPath(Core::ICore::clangdExecutable(CLANG_BINDIR));
- connect(&CppEditor::ClangdSettings::instance(), &CppEditor::ClangdSettings::changed,
+ ClangdSettings::setDefaultClangdPath(ICore::clangdExecutable(CLANG_BINDIR));
+ connect(&ClangdSettings::instance(), &ClangdSettings::changed,
this, &ClangModelManagerSupport::onClangdSettingsChanged);
- if (CppEditor::ClangdSettings::instance().useClangd())
+ if (ClangdSettings::instance().useClangd())
new ClangdClient(nullptr, {});
- m_generatorSynchronizer.setCancelOnWait(true);
new ClangdQuickFixFactory(); // memory managed by CppEditor::g_cppQuickFixFactories
}
@@ -256,9 +268,9 @@ ClangModelManagerSupport::~ClangModelManagerSupport()
m_generatorSynchronizer.waitForFinished();
}
-void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,
- const LinkHandler &processLinkCallback, bool resolveTarget,
- bool inNextSplit)
+void ClangModelManagerSupport::followSymbol(const CursorInEditor &data,
+ const LinkHandler &processLinkCallback,
+ bool resolveTarget, bool inNextSplit)
{
if (ClangdClient * const client = clientForFile(data.filePath());
client && client->isFullyIndexed()) {
@@ -271,7 +283,7 @@ void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &dat
CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::followSymbolToType(const CppEditor::CursorInEditor &data,
+void ClangModelManagerSupport::followSymbolToType(const CursorInEditor &data,
const LinkHandler &processLinkCallback,
bool inNextSplit)
{
@@ -284,8 +296,8 @@ void ClangModelManagerSupport::followSymbolToType(const CppEditor::CursorInEdito
CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::switchDeclDef(const CppEditor::CursorInEditor &data,
- const LinkHandler &processLinkCallback)
+void ClangModelManagerSupport::switchDeclDef(const CursorInEditor &data,
+ const LinkHandler &processLinkCallback)
{
if (ClangdClient * const client = clientForFile(data.filePath());
client && client->isFullyIndexed()) {
@@ -297,9 +309,9 @@ void ClangModelManagerSupport::switchDeclDef(const CppEditor::CursorInEditor &da
CppModelManager::switchDeclDef(data, processLinkCallback, CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEditor &data,
- const CppEditor::ProjectPart *projectPart,
- RenameCallback &&renameSymbolsCallback)
+void ClangModelManagerSupport::startLocalRenaming(const CursorInEditor &data,
+ const ProjectPart *projectPart,
+ RenameCallback &&renameSymbolsCallback)
{
if (ClangdClient * const client = clientForFile(data.filePath());
client && client->reachable()) {
@@ -312,7 +324,7 @@ void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEdito
std::move(renameSymbolsCallback), CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::globalRename(const CppEditor::CursorInEditor &cursor,
+void ClangModelManagerSupport::globalRename(const CursorInEditor &cursor,
const QString &replacement,
const std::function<void()> &callback)
{
@@ -326,7 +338,7 @@ void ClangModelManagerSupport::globalRename(const CppEditor::CursorInEditor &cur
CppModelManager::globalRename(cursor, replacement, callback, CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &cursor) const
+void ClangModelManagerSupport::findUsages(const CursorInEditor &cursor) const
{
if (ClangdClient * const client = clientForFile(cursor.filePath());
client && client->isFullyIndexed()) {
@@ -346,11 +358,10 @@ void ClangModelManagerSupport::switchHeaderSource(const FilePath &filePath, bool
CppModelManager::switchHeaderSource(inNextSplit, CppModelManager::Backend::Builtin);
}
-void ClangModelManagerSupport::checkUnused(const Link &link, Core::SearchResult *search,
+void ClangModelManagerSupport::checkUnused(const Link &link, SearchResult *search,
const LinkHandler &callback)
{
- if (const ProjectExplorer::Project * const project
- = ProjectExplorer::SessionManager::projectForFile(link.targetFilePath)) {
+ if (const Project * const project = ProjectManager::projectForFile(link.targetFilePath)) {
if (ClangdClient * const client = clientWithProject(project);
client && client->isFullyIndexed()) {
client->checkUnused(link, search, callback);
@@ -367,7 +378,7 @@ bool ClangModelManagerSupport::usesClangd(const TextEditor::TextDocument *docume
return clientForFile(document->filePath());
}
-CppEditor::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor(
+BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument)
{
const auto processor = new ClangEditorDocumentProcessor(baseTextDocument);
@@ -381,10 +392,10 @@ CppEditor::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDo
return processor;
}
-void ClangModelManagerSupport::onCurrentEditorChanged(Core::IEditor *editor)
+void ClangModelManagerSupport::onCurrentEditorChanged(IEditor *editor)
{
// Update task hub issues for current CppEditorDocument
- ProjectExplorer::TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS);
+ TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS);
if (!editor || !editor->document() || !cppModelManager()->isCppEditor(editor))
return;
@@ -407,28 +418,25 @@ void ClangModelManagerSupport::connectToWidgetsMarkContextMenuRequested(QWidget
}
}
-static FilePath getJsonDbDir(const ProjectExplorer::Project *project)
+static FilePath getJsonDbDir(const Project *project)
{
static const QString dirName(".qtc_clangd");
if (!project) {
const QString sessionDirName = FileUtils::fileSystemFriendlyName(
- ProjectExplorer::SessionManager::activeSession());
- return Core::ICore::userResourcePath() / dirName / sessionDirName; // TODO: Make configurable?
+ SessionManager::activeSession());
+ return ICore::userResourcePath() / dirName / sessionDirName; // TODO: Make configurable?
}
- if (const ProjectExplorer::Target * const target = project->activeTarget()) {
- if (const ProjectExplorer::BuildConfiguration * const bc
- = target->activeBuildConfiguration()) {
+ if (const Target * const target = project->activeTarget()) {
+ if (const BuildConfiguration * const bc = target->activeBuildConfiguration())
return bc->buildDirectory() / dirName;
- }
}
return {};
}
-static bool isProjectDataUpToDate(
- ProjectExplorer::Project *project, ProjectInfoList projectInfo,
- const FilePath &jsonDbDir)
+static bool isProjectDataUpToDate(Project *project, ProjectInfoList projectInfo,
+ const FilePath &jsonDbDir)
{
- if (project && !ProjectExplorer::SessionManager::hasProject(project))
+ if (project && !ProjectManager::hasProject(project))
return false;
const ClangdSettings settings(ClangdProjectSettings(project).settings());
if (!settings.useClangd())
@@ -457,7 +465,7 @@ static bool isProjectDataUpToDate(
return true;
}
-void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *project)
+void ClangModelManagerSupport::updateLanguageClient(Project *project)
{
const ClangdSettings settings(ClangdProjectSettings(project).settings());
if (!settings.useClangd())
@@ -483,12 +491,12 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
return;
const GenerateCompilationDbResult result = generatorWatcher->result();
if (!result.error.isEmpty()) {
- Core::MessageManager::writeDisrupting(
+ MessageManager::writeDisrupting(
Tr::tr("Cannot use clangd: Failed to generate compilation database:\n%1")
.arg(result.error));
return;
}
- Utils::Id previousId;
+ Id previousId;
if (Client * const oldClient = clientForProject(project)) {
previousId = oldClient->id();
LanguageClientManager::shutdownClient(oldClient);
@@ -524,7 +532,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
hasDocuments = true;
continue;
}
- const Project * const docProject = SessionManager::projectForFile(doc->filePath());
+ const Project * const docProject = ProjectManager::projectForFile(doc->filePath());
if (currentClient && currentClient->project()
&& currentClient->project() != project
&& currentClient->project() == docProject) {
@@ -564,8 +572,8 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
ProjectNode *rootNode = nullptr;
if (project)
rootNode = project->rootProjectNode();
- else if (SessionManager::startupProject())
- rootNode = SessionManager::startupProject()->rootProjectNode();
+ else if (ProjectManager::startupProject())
+ rootNode = ProjectManager::startupProject()->rootProjectNode();
if (!rootNode)
return;
const Node * const cxxNode = rootNode->findNode([](Node *n) {
@@ -583,7 +591,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
});
const FilePath includeDir = settings.clangdIncludePath();
- auto future = Utils::runAsync(&Internal::generateCompilationDB, projectInfo,
+ auto future = Utils::asyncRun(&Internal::generateCompilationDB, projectInfo,
jsonDbDir, CompilationDbPurpose::CodeModel,
warningsConfigForProject(project),
globalClangOptions(), includeDir);
@@ -591,18 +599,28 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
m_generatorSynchronizer.addFuture(future);
}
-ClangdClient *ClangModelManagerSupport::clientForProject(const ProjectExplorer::Project *project)
+QList<Client *> ClangModelManagerSupport::clientsForOpenProjects()
+{
+ QSet<Client *> clients;
+ const QList<Project *> projects = ProjectManager::projects();
+ for (Project *project : projects) {
+ if (Client *client = ClangModelManagerSupport::clientForProject(project))
+ clients << client;
+ }
+ return clients.values();
+}
+
+ClangdClient *ClangModelManagerSupport::clientForProject(const Project *project)
{
if (sessionModeEnabled())
project = nullptr;
return clientWithProject(project);
}
-ClangdClient *ClangModelManagerSupport::clientWithProject(const ProjectExplorer::Project *project)
+ClangdClient *ClangModelManagerSupport::clientWithProject(const Project *project)
{
const QList<Client *> clients = Utils::filtered(
- LanguageClientManager::clientsForProject(project),
- [](const LanguageClient::Client *c) {
+ LanguageClientManager::clientsForProject(project), [](const Client *c) {
return qobject_cast<const ClangdClient *>(c)
&& c->state() != Client::ShutdownRequested
&& c->state() != Client::Shutdown;
@@ -640,7 +658,7 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client)
}
if (!ClangdSettings::instance().sizeIsOkay(doc->filePath()))
continue;
- if (ProjectExplorer::SessionManager::projectForFile(doc->filePath()))
+ if (ProjectManager::projectForFile(doc->filePath()))
continue;
if (client->project() && !ProjectFile::isHeader(doc->filePath()))
continue;
@@ -656,7 +674,7 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client)
// workflow, e.g. a git branch switch will hit at least one open file.
void ClangModelManagerSupport::watchForExternalChanges()
{
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedExternally,
+ connect(DocumentManager::instance(), &DocumentManager::filesChangedExternally,
this, [this](const QSet<FilePath> &files) {
if (!LanguageClientManager::hasClients<ClangdClient>())
return;
@@ -664,8 +682,7 @@ void ClangModelManagerSupport::watchForExternalChanges()
const ProjectFile::Kind kind = ProjectFile::classify(file.toString());
if (!ProjectFile::isSource(kind) && !ProjectFile::isHeader(kind))
continue;
- ProjectExplorer::Project * const project
- = ProjectExplorer::SessionManager::projectForFile(file);
+ Project * const project = ProjectManager::projectForFile(file);
if (!project)
continue;
@@ -684,14 +701,13 @@ void ClangModelManagerSupport::watchForExternalChanges()
// restart clangd for reliable re-parsing and re-indexing.
void ClangModelManagerSupport::watchForInternalChanges()
{
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
+ connect(DocumentManager::instance(), &DocumentManager::filesChangedInternally,
this, [this](const FilePaths &filePaths) {
for (const FilePath &fp : filePaths) {
const ProjectFile::Kind kind = ProjectFile::classify(fp.toString());
if (!ProjectFile::isSource(kind) && !ProjectFile::isHeader(kind))
continue;
- ProjectExplorer::Project * const project
- = ProjectExplorer::SessionManager::projectForFile(fp);
+ Project * const project = ProjectManager::projectForFile(fp);
if (!project)
continue;
if (ClangdClient * const client = clientForProject(project);
@@ -717,18 +733,17 @@ void ClangModelManagerSupport::scheduleClientRestart(ClangdClient *client)
m_clientRestartTimer->start();
}
-void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
+void ClangModelManagerSupport::onEditorOpened(IEditor *editor)
{
QTC_ASSERT(editor, return);
- Core::IDocument *document = editor->document();
+ IDocument *document = editor->document();
QTC_ASSERT(document, return);
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (textDocument && cppModelManager()->isCppEditor(editor)) {
connectToWidgetsMarkContextMenuRequested(editor->widget());
- ProjectExplorer::Project * project
- = ProjectExplorer::SessionManager::projectForFile(document->filePath());
+ Project * project = ProjectManager::projectForFile(document->filePath());
const ClangdSettings settings(ClangdProjectSettings(project).settings());
if (!settings.sizeIsOkay(textDocument->filePath()))
return;
@@ -823,17 +838,17 @@ static ClangEditorDocumentProcessors clangProcessors()
return result;
}
-void ClangModelManagerSupport::onProjectPartsUpdated(ProjectExplorer::Project *project)
+void ClangModelManagerSupport::onProjectPartsUpdated(Project *project)
{
QTC_ASSERT(project, return);
updateLanguageClient(project);
QStringList projectPartIds;
- const CppEditor::ProjectInfo::ConstPtr projectInfo = cppModelManager()->projectInfo(project);
+ const ProjectInfo::ConstPtr projectInfo = cppModelManager()->projectInfo(project);
QTC_ASSERT(projectInfo, return);
- for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectInfo->projectParts())
+ for (const ProjectPart::ConstPtr &projectPart : projectInfo->projectParts())
projectPartIds.append(projectPart->id());
onProjectPartsRemoved(projectPartIds);
}
@@ -848,9 +863,8 @@ void ClangModelManagerSupport::onClangdSettingsChanged()
{
const bool sessionMode = sessionModeEnabled();
- for (ProjectExplorer::Project * const project : ProjectExplorer::SessionManager::projects()) {
- const CppEditor::ClangdSettings settings(
- CppEditor::ClangdProjectSettings(project).settings());
+ for (Project * const project : ProjectManager::projects()) {
+ const ClangdSettings settings(ClangdProjectSettings(project).settings());
ClangdClient * const client = clientWithProject(project);
if (sessionMode) {
if (client && client->project())
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
index a901b4407e..140239550d 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h
@@ -24,6 +24,7 @@ QT_END_NAMESPACE
namespace Core { class IEditor; }
namespace CppEditor { class RefactoringEngineInterface; }
+namespace LanguageClient { class Client; }
namespace TextEditor { class TextEditorWidget; }
namespace ClangCodeModel {
@@ -45,6 +46,7 @@ public:
TextEditor::TextDocument *baseTextDocument) override;
bool usesClangd(const TextEditor::TextDocument *document) const override;
+ static QList<LanguageClient::Client *> clientsForOpenProjects();
static ClangdClient *clientForProject(const ProjectExplorer::Project *project);
static ClangdClient *clientForFile(const Utils::FilePath &file);
diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp
index a38aeb6287..570e6a2e95 100644
--- a/src/plugins/clangcodemodel/clangtextmark.cpp
+++ b/src/plugins/clangcodemodel/clangtextmark.cpp
@@ -98,15 +98,12 @@ ClangDiagnosticConfig diagnosticConfig()
return warningsConfigForProject(project);
}
-bool isDiagnosticConfigChangable(Project *project, const ClangDiagnostic &diagnostic)
+static bool isDiagnosticConfigChangable(Project *project)
{
if (!project)
return false;
- const ClangDiagnosticConfig config = diagnosticConfig();
- if (config.clangTidyMode() == ClangDiagnosticConfig::TidyMode::UseConfigFile
- && diagnosticType(diagnostic) == DiagnosticType::Tidy) {
+ if (diagnosticConfig().useBuildSystemWarnings())
return false;
- }
return true;
}
@@ -308,7 +305,7 @@ ClangdTextMark::ClangdTextMark(const FilePath &filePath,
// Remove diagnostic warning action
Project *project = projectForCurrentEditor();
- if (project && isDiagnosticConfigChangable(project, diag)) {
+ if (project && isDiagnosticConfigChangable(project)) {
action = new QAction();
action->setIcon(Icons::BROKEN.icon());
action->setToolTip(Tr::tr("Disable Diagnostic in Current Project"));
diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
deleted file mode 100644
index 1f0219df99..0000000000
--- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
+++ /dev/null
@@ -1,729 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "clangbatchfileprocessor.h"
-
-#include <clangcodemodel/clangeditordocumentprocessor.h>
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/icore.h>
-#include <cppeditor/cpptoolsreuse.h>
-#include <cppeditor/cpptoolstestcase.h>
-#include <cppeditor/modelmanagertesthelper.h>
-#include <cppeditor/projectinfo.h>
-#include <projectexplorer/projectexplorer.h>
-#include <texteditor/codeassist/assistinterface.h>
-#include <texteditor/codeassist/assistproposalitem.h>
-#include <texteditor/codeassist/completionassistprovider.h>
-#include <texteditor/codeassist/genericproposalmodel.h>
-#include <texteditor/codeassist/iassistprocessor.h>
-#include <texteditor/codeassist/iassistproposal.h>
-#include <texteditor/textdocument.h>
-#include <texteditor/texteditor.h>
-
-#include <utils/environment.h>
-#include <utils/executeondestruction.h>
-#include <utils/qtcassert.h>
-
-#include <QDebug>
-#include <QElapsedTimer>
-#include <QFileInfo>
-#include <QLoggingCategory>
-#include <QSharedPointer>
-#include <QString>
-#include <QThread>
-
-using namespace ProjectExplorer;
-
-namespace ClangCodeModel {
-namespace Internal {
-
-static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg);
-
-static int timeOutFromEnvironmentVariable()
-{
- bool isConversionOk = false;
- const int intervalAsInt = Utils::qtcEnvironmentVariableIntValue("QTC_CLANG_BATCH_TIMEOUT",
- &isConversionOk);
- if (!isConversionOk) {
- qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000.");
- return 30000;
- }
-
- return intervalAsInt;
-}
-
-int timeOutInMs()
-{
- static int timeOut = timeOutFromEnvironmentVariable();
- return timeOut;
-}
-
-namespace {
-
-class BatchFileLineTokenizer
-{
-public:
- BatchFileLineTokenizer(const QString &line);
-
- QString nextToken();
-
-private:
- const QChar *advanceToTokenBegin();
- const QChar *advanceToTokenEnd();
-
- bool atEnd() const;
- bool atWhiteSpace() const;
- bool atQuotationMark() const;
-
-private:
- bool m_isWithinQuotation = false;
- QString m_line;
- const QChar *m_currentChar;
-};
-
-BatchFileLineTokenizer::BatchFileLineTokenizer(const QString &line)
- : m_line(line)
- , m_currentChar(m_line.unicode())
-{
-}
-
-QString BatchFileLineTokenizer::nextToken()
-{
- if (const QChar *tokenBegin = advanceToTokenBegin()) {
- if (const QChar *tokenEnd = advanceToTokenEnd()) {
- const int length = tokenEnd - tokenBegin;
- return QString(tokenBegin, length);
- }
- }
-
- return QString();
-}
-
-const QChar *BatchFileLineTokenizer::advanceToTokenBegin()
-{
- m_isWithinQuotation = false;
-
- forever {
- if (atEnd())
- return nullptr;
-
- if (atQuotationMark()) {
- m_isWithinQuotation = true;
- ++m_currentChar;
- return m_currentChar;
- }
-
- if (!atWhiteSpace())
- return m_currentChar;
-
- ++m_currentChar;
- }
-}
-
-const QChar *BatchFileLineTokenizer::advanceToTokenEnd()
-{
- forever {
- if (m_isWithinQuotation) {
- if (atEnd()) {
- qWarning("ClangBatchFileProcessor: error: unfinished quotation.");
- return nullptr;
- }
-
- if (atQuotationMark())
- return m_currentChar++;
-
- } else if (atWhiteSpace() || atEnd()) {
- return m_currentChar;
- }
-
- ++m_currentChar;
- }
-}
-
-bool BatchFileLineTokenizer::atEnd() const
-{
- return *m_currentChar == QLatin1Char('\0');
-}
-
-bool BatchFileLineTokenizer::atWhiteSpace() const
-{
- return *m_currentChar == ' '
- || *m_currentChar == '\t'
- || *m_currentChar == '\n';
-}
-
-bool BatchFileLineTokenizer::atQuotationMark() const
-{
- return *m_currentChar == '"';
-}
-
-struct CommandContext {
- QString filePath;
- int lineNumber = -1;
-};
-
-class Command
-{
-public:
- using Ptr = QSharedPointer<Command>;
-
-public:
- Command(const CommandContext &context) : m_commandContext(context) {}
- virtual ~Command() = default;
-
- const CommandContext &context() const { return m_commandContext; }
- virtual bool run() { return true; }
-
-private:
- const CommandContext m_commandContext;
-};
-
-class OpenProjectCommand : public Command
-{
-public:
- OpenProjectCommand(const CommandContext &context,
- const QString &projectFilePath);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- QString m_projectFilePath;
-};
-
-OpenProjectCommand::OpenProjectCommand(const CommandContext &context,
- const QString &projectFilePath)
- : Command(context)
- , m_projectFilePath(projectFilePath)
-{
-}
-
-bool OpenProjectCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "OpenProjectCommand" << m_projectFilePath;
-
- const ProjectExplorerPlugin::OpenProjectResult openProjectSucceeded
- = ProjectExplorerPlugin::openProject(Utils::FilePath::fromString(m_projectFilePath));
- QTC_ASSERT(openProjectSucceeded, return false);
-
- Project *project = openProjectSucceeded.project();
- project->configureAsExampleProject(nullptr);
-
- return CppEditor::Tests::TestCase::waitUntilProjectIsFullyOpened(project, timeOutInMs());
-}
-
-Command::Ptr OpenProjectCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString projectFilePath = arguments.nextToken();
- if (projectFilePath.isEmpty()) {
- qWarning("%s:%d: error: No project file path given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- const QString absoluteProjectFilePath = QFileInfo(projectFilePath).absoluteFilePath();
-
- return Command::Ptr(new OpenProjectCommand(context, absoluteProjectFilePath));
-}
-
-class OpenDocumentCommand : public Command
-{
-public:
- OpenDocumentCommand(const CommandContext &context,
- const QString &documentFilePath);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context);
-
-private:
- Utils::FilePath m_documentFilePath;
-};
-
-OpenDocumentCommand::OpenDocumentCommand(const CommandContext &context,
- const QString &documentFilePath)
- : Command(context)
- , m_documentFilePath(Utils::FilePath::fromString(documentFilePath))
-{
-}
-
-class WaitForUpdatedCodeWarnings : public QObject
-{
-public:
- WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor);
-
- bool wait(int timeOutInMs) const;
-
-private:
- void onCodeWarningsUpdated() { m_gotResults = true; }
-
-private:
-
- bool m_gotResults = false;
-};
-
-WaitForUpdatedCodeWarnings::WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor)
-{
- connect(processor,
- &ClangEditorDocumentProcessor::codeWarningsUpdated,
- this, &WaitForUpdatedCodeWarnings::onCodeWarningsUpdated);
-}
-
-bool WaitForUpdatedCodeWarnings::wait(int timeOutInMs) const
-{
- QElapsedTimer time;
- time.start();
-
- forever {
- if (time.elapsed() > timeOutInMs) {
- qWarning("WaitForUpdatedCodeWarnings: timeout of %d ms reached.", timeOutInMs);
- return false;
- }
-
- if (m_gotResults)
- return true;
-
- QCoreApplication::processEvents();
- QThread::msleep(20);
- }
-}
-
-bool OpenDocumentCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "OpenDocumentCommand" << m_documentFilePath;
-
- const bool openEditorSucceeded = Core::EditorManager::openEditor(m_documentFilePath);
- QTC_ASSERT(openEditorSucceeded, return false);
-
- auto *processor = ClangEditorDocumentProcessor::get(m_documentFilePath);
- QTC_ASSERT(processor, return false);
-
- WaitForUpdatedCodeWarnings waiter(processor);
- return waiter.wait(timeOutInMs());
-}
-
-Command::Ptr OpenDocumentCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString documentFilePath = arguments.nextToken();
- if (documentFilePath.isEmpty()) {
- qWarning("%s:%d: error: No document file path given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- const QString absoluteDocumentFilePath = QFileInfo(documentFilePath).absoluteFilePath();
-
- return Command::Ptr(new OpenDocumentCommand(context, absoluteDocumentFilePath));
-}
-
-class CloseAllDocuments : public Command
-{
-public:
- CloseAllDocuments(const CommandContext &context);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context);
-};
-
-CloseAllDocuments::CloseAllDocuments(const CommandContext &context)
- : Command(context)
-{
-}
-
-bool CloseAllDocuments::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "CloseAllDocuments";
-
- return Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false);
-}
-
-Command::Ptr CloseAllDocuments::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString argument = arguments.nextToken();
- if (!argument.isEmpty()) {
- qWarning("%s:%d: error: Unexpected argument.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new CloseAllDocuments(context));
-}
-
-class InsertTextCommand : public Command
-{
-public:
- // line and column are 1-based
- InsertTextCommand(const CommandContext &context, const QString &text);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- const QString m_textToInsert;
-};
-
-InsertTextCommand::InsertTextCommand(const CommandContext &context, const QString &text)
- : Command(context)
- , m_textToInsert(text)
-{
-}
-
-TextEditor::BaseTextEditor *currentTextEditor()
-{
- return qobject_cast<TextEditor::BaseTextEditor*>(Core::EditorManager::currentEditor());
-}
-
-bool InsertTextCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "InsertTextCommand" << m_textToInsert;
-
- TextEditor::BaseTextEditor *editor = currentTextEditor();
- QTC_ASSERT(editor, return false);
- const Utils::FilePath documentFilePath = editor->document()->filePath();
- auto processor = ClangEditorDocumentProcessor::get(documentFilePath);
- QTC_ASSERT(processor, return false);
-
- editor->insert(m_textToInsert);
-
- WaitForUpdatedCodeWarnings waiter(processor);
- return waiter.wait(timeOutInMs());
-}
-
-Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString textToInsert = arguments.nextToken();
- if (textToInsert.isEmpty()) {
- qWarning("%s:%d: error: No text to insert given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new InsertTextCommand(context, textToInsert));
-}
-
-class SetCursorCommand : public Command
-{
-public:
- // line and column are 1-based
- SetCursorCommand(const CommandContext &context, int line, int column);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- int m_line;
- int m_column;
-};
-
-SetCursorCommand::SetCursorCommand(const CommandContext &context, int line, int column)
- : Command(context)
- , m_line(line)
- , m_column(column)
-{
-}
-
-bool SetCursorCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "SetCursorCommand" << m_line << m_column;
-
- TextEditor::BaseTextEditor *editor = currentTextEditor();
- QTC_ASSERT(editor, return false);
-
- editor->gotoLine(m_line, m_column - 1);
-
- return true;
-}
-
-Command::Ptr SetCursorCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- // Process line
- const QString line = arguments.nextToken();
- if (line.isEmpty()) {
- qWarning("%s:%d: error: No line number given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
- bool converted = false;
- const int lineNumber = line.toInt(&converted);
- if (!converted) {
- qWarning("%s:%d: error: Invalid line number.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- // Process column
- const QString column = arguments.nextToken();
- if (column.isEmpty()) {
- qWarning("%s:%d: error: No column number given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
- converted = false;
- const int columnNumber = column.toInt(&converted);
- if (!converted) {
- qWarning("%s:%d: error: Invalid column number.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new SetCursorCommand(context, lineNumber, columnNumber));
-}
-
-class ProcessEventsCommand : public Command
-{
-public:
- ProcessEventsCommand(const CommandContext &context, int durationInMs);
-
- bool run() override;
-
- static Command::Ptr parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context);
-
-private:
- int m_durationInMs;
-};
-
-ProcessEventsCommand::ProcessEventsCommand(const CommandContext &context,
- int durationInMs)
- : Command(context)
- , m_durationInMs(durationInMs)
-{
-}
-
-bool ProcessEventsCommand::run()
-{
- qCDebug(debug) << "line" << context().lineNumber << "ProcessEventsCommand" << m_durationInMs;
-
- QElapsedTimer time;
- time.start();
-
- forever {
- if (time.elapsed() > m_durationInMs)
- return true;
-
- QCoreApplication::processEvents();
- QThread::msleep(20);
- }
-}
-
-Command::Ptr ProcessEventsCommand::parse(BatchFileLineTokenizer &arguments,
- const CommandContext &context)
-{
- const QString durationInMsText = arguments.nextToken();
- if (durationInMsText.isEmpty()) {
- qWarning("%s:%d: error: No duration given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- bool converted = false;
- const int durationInMs = durationInMsText.toInt(&converted);
- if (!converted) {
- qWarning("%s:%d: error: Invalid duration given.",
- qPrintable(context.filePath),
- context.lineNumber);
- return Command::Ptr();
- }
-
- return Command::Ptr(new ProcessEventsCommand(context, durationInMs));
-}
-
-class BatchFileReader
-{
-public:
- BatchFileReader(const QString &filePath);
-
- bool isFilePathValid() const;
-
- QString read() const;
-
-private:
- const QString m_batchFilePath;
-};
-
-BatchFileReader::BatchFileReader(const QString &filePath)
- : m_batchFilePath(filePath)
-{
-}
-
-bool BatchFileReader::isFilePathValid() const
-{
- QFileInfo fileInfo(m_batchFilePath);
-
- return !m_batchFilePath.isEmpty()
- && fileInfo.isFile()
- && fileInfo.isReadable();
-}
-
-QString BatchFileReader::read() const
-{
- QFile file(m_batchFilePath);
- QTC_CHECK(file.open(QFile::ReadOnly | QFile::Text));
-
- return QString::fromLocal8Bit(file.readAll());
-}
-
-class BatchFileParser
-{
-public:
- BatchFileParser(const QString &filePath,
- const QString &commands);
-
- bool parse();
- QVector<Command::Ptr> commands() const;
-
-private:
- bool advanceLine();
- QString currentLine() const;
- bool parseLine(const QString &line);
-
-private:
- using ParseFunction = Command::Ptr (*)(BatchFileLineTokenizer &, const CommandContext &);
- using CommandToParseFunction = QHash<QString, ParseFunction>;
- CommandToParseFunction m_commandParsers;
-
- int m_currentLineIndex = -1;
- CommandContext m_context;
- QStringList m_lines;
- QVector<Command::Ptr> m_commands;
-};
-
-BatchFileParser::BatchFileParser(const QString &filePath,
- const QString &commands)
- : m_lines(commands.split('\n'))
-{
- m_context.filePath = filePath;
-
- m_commandParsers.insert("openProject", &OpenProjectCommand::parse);
- m_commandParsers.insert("openDocument", &OpenDocumentCommand::parse);
- m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse);
- m_commandParsers.insert("setCursor", &SetCursorCommand::parse);
- m_commandParsers.insert("insertText", &InsertTextCommand::parse);
- m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse);
-}
-
-bool BatchFileParser::parse()
-{
- while (advanceLine()) {
- const QString line = currentLine().trimmed();
- if (line.isEmpty() || line.startsWith('#'))
- continue;
-
- if (!parseLine(line))
- return false;
- }
-
- return true;
-}
-
-QVector<Command::Ptr> BatchFileParser::commands() const
-{
- return m_commands;
-}
-
-bool BatchFileParser::advanceLine()
-{
- ++m_currentLineIndex;
- m_context.lineNumber = m_currentLineIndex + 1;
- return m_currentLineIndex < m_lines.size();
-}
-
-QString BatchFileParser::currentLine() const
-{
- return m_lines[m_currentLineIndex];
-}
-
-bool BatchFileParser::parseLine(const QString &line)
-{
- BatchFileLineTokenizer tokenizer(line);
- QString command = tokenizer.nextToken();
- QTC_CHECK(!command.isEmpty());
-
- if (const ParseFunction parseFunction = m_commandParsers.value(command)) {
- if (Command::Ptr cmd = parseFunction(tokenizer, m_context)) {
- m_commands.append(cmd);
- return true;
- }
-
- return false;
- }
-
- qWarning("%s:%d: error: Unknown command \"%s\".",
- qPrintable(m_context.filePath),
- m_context.lineNumber,
- qPrintable(command));
-
- return false;
-}
-
-} // anonymous namespace
-
-static QString applySubstitutions(const QString &filePath, const QString &text)
-{
- const QString dirPath = QFileInfo(filePath).absolutePath();
-
- QString result = text;
- result.replace("${PWD}", dirPath);
-
- return result;
-}
-
-bool runClangBatchFile(const QString &filePath)
-{
- qWarning("ClangBatchFileProcessor: Running \"%s\".", qPrintable(filePath));
-
- BatchFileReader reader(filePath);
- QTC_ASSERT(reader.isFilePathValid(), return false);
- const QString fileContent = reader.read();
- const QString fileContentWithSubstitutionsApplied = applySubstitutions(filePath, fileContent);
-
- BatchFileParser parser(filePath, fileContentWithSubstitutionsApplied);
- QTC_ASSERT(parser.parse(), return false);
- const QVector<Command::Ptr> commands = parser.commands();
-
- Utils::ExecuteOnDestruction closeAllEditors([] {
- qWarning("ClangBatchFileProcessor: Finished, closing all documents.");
- QTC_CHECK(Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false));
- });
-
- for (const Command::Ptr &command : commands) {
- const bool runSucceeded = command->run();
- QCoreApplication::processEvents(); // Update GUI
-
- if (!runSucceeded) {
- const CommandContext context = command->context();
- qWarning("%s:%d: Failed to run.",
- qPrintable(context.filePath),
- context.lineNumber);
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace Internal
-} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
deleted file mode 100644
index 942269b364..0000000000
--- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QString>
-
-namespace ClangCodeModel {
-namespace Internal {
-
-int timeOutInMs();
-
-bool runClangBatchFile(const QString &filePath);
-
-} // namespace Internal
-} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp
index c60a45f1df..e66515365b 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.cpp
+++ b/src/plugins/clangcodemodel/test/clangdtests.cpp
@@ -3,7 +3,6 @@
#include "clangdtests.h"
-#include "clangbatchfileprocessor.h"
#include "../clangdclient.h"
#include "../clangmodelmanagersupport.h"
@@ -44,6 +43,7 @@ using namespace CppEditor::Tests;
using namespace LanguageClient;
using namespace ProjectExplorer;
using namespace TextEditor;
+using namespace Utils;
namespace ClangCodeModel {
namespace Internal {
@@ -69,6 +69,27 @@ static QString qrcPath(const QString &relativeFilePath)
return ":/unittests/ClangCodeModel/" + relativeFilePath;
}
+static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg);
+
+static int timeOutFromEnvironmentVariable()
+{
+ bool isConversionOk = false;
+ const int intervalAsInt = Utils::qtcEnvironmentVariableIntValue("QTC_CLANG_BATCH_TIMEOUT",
+ &isConversionOk);
+ if (!isConversionOk) {
+ qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000.");
+ return 30000;
+ }
+
+ return intervalAsInt;
+}
+
+int timeOutInMs()
+{
+ static int timeOut = timeOutFromEnvironmentVariable();
+ return timeOut;
+}
+
ClangdTest::~ClangdTest()
{
EditorManager::closeAllEditors(false);
@@ -77,7 +98,7 @@ ClangdTest::~ClangdTest()
delete m_projectDir;
}
-Utils::FilePath ClangdTest::filePath(const QString &fileName) const
+FilePath ClangdTest::filePath(const QString &fileName) const
{
return m_projectDir->absolutePath(fileName);
}
@@ -119,7 +140,7 @@ void ClangdTest::initTestCase()
{
const QString clangdFromEnv = Utils::qtcEnvironmentVariable("QTC_CLANGD");
if (!clangdFromEnv.isEmpty())
- CppEditor::ClangdSettings::setClangdFilePath(Utils::FilePath::fromString(clangdFromEnv));
+ CppEditor::ClangdSettings::setClangdFilePath(FilePath::fromString(clangdFromEnv));
const auto clangd = CppEditor::ClangdSettings::instance().clangdFilePath();
if (clangd.isEmpty() || !clangd.exists())
QSKIP("clangd binary not found");
@@ -170,7 +191,7 @@ void ClangdTestFindReferences::initTestCase()
ClangdTest::initTestCase();
CppEditor::codeModelSettings()->setCategorizeFindReferences(true);
connect(client(), &ClangdClient::foundReferences, this,
- [this](const QList<SearchResultItem> &results) {
+ [this](const SearchResultItems &results) {
if (results.isEmpty())
return;
if (results.first().path().first().endsWith("defs.h"))
@@ -184,8 +205,7 @@ void ClangdTestFindReferences::test_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<int>("pos");
- using ItemList = QList<SearchResultItem>;
- QTest::addColumn<ItemList>("expectedResults");
+ QTest::addColumn<SearchResultItems>("expectedResults");
static const auto makeItem = [](int line, int column, Usage::Tags tags) {
SearchResultItem item;
@@ -194,7 +214,7 @@ void ClangdTestFindReferences::test_data()
return item;
};
- QTest::newRow("struct member") << "defs.h" << 55 << ItemList{
+ QTest::newRow("struct member") << "defs.h" << 55 << SearchResultItems{
makeItem(2, 17, Usage::Tag::Read), makeItem(3, 15, Usage::Tag::Declaration),
makeItem(6, 17, Usage::Tag::WritableRef), makeItem(8, 11, Usage::Tag::WritableRef),
makeItem(9, 13, Usage::Tag::WritableRef), makeItem(10, 12, Usage::Tag::WritableRef),
@@ -211,23 +231,23 @@ void ClangdTestFindReferences::test_data()
makeItem(56, 7, Usage::Tag::Write), makeItem(56, 25, Usage::Tags()),
makeItem(58, 13, Usage::Tag::Read), makeItem(58, 25, Usage::Tag::Read),
makeItem(59, 7, Usage::Tag::Write), makeItem(59, 24, Usage::Tag::Read)};
- QTest::newRow("constructor member initialization") << "defs.h" << 68 << ItemList{
+ QTest::newRow("constructor member initialization") << "defs.h" << 68 << SearchResultItems{
makeItem(2, 10, Usage::Tag::Write), makeItem(4, 8, Usage::Tag::Declaration)};
- QTest::newRow("direct member initialization") << "defs.h" << 101 << ItemList{
+ QTest::newRow("direct member initialization") << "defs.h" << 101 << SearchResultItems{
makeItem(5, 21, Initialization), makeItem(45, 16, Usage::Tag::Read)};
- ItemList pureVirtualRefs{makeItem(17, 17, Usage::Tag::Declaration),
+ SearchResultItems pureVirtualRefs{makeItem(17, 17, Usage::Tag::Declaration),
makeItem(21, 9, {Usage::Tag::Declaration, Usage::Tag::Override})};
QTest::newRow("pure virtual declaration") << "defs.h" << 420 << pureVirtualRefs;
- QTest::newRow("pointer variable") << "main.cpp" << 52 << ItemList{
+ QTest::newRow("pointer variable") << "main.cpp" << 52 << SearchResultItems{
makeItem(6, 10, Initialization), makeItem(8, 4, Usage::Tag::Write),
makeItem(10, 4, Usage::Tag::Write), makeItem(24, 5, Usage::Tag::Write),
makeItem(25, 11, Usage::Tag::WritableRef), makeItem(26, 11, Usage::Tag::Read),
makeItem(27, 10, Usage::Tag::WritableRef), makeItem(28, 10, Usage::Tag::Read),
makeItem(29, 11, Usage::Tag::Read), makeItem(30, 15, Usage::Tag::WritableRef),
makeItem(31, 22, Usage::Tag::Read)};
- QTest::newRow("struct variable") << "main.cpp" << 39 << ItemList{
+ QTest::newRow("struct variable") << "main.cpp" << 39 << SearchResultItems{
makeItem(5, 7, Usage::Tag::Declaration), makeItem(6, 15, Usage::Tag::WritableRef),
makeItem(8, 9, Usage::Tag::WritableRef), makeItem(9, 11, Usage::Tag::WritableRef),
makeItem(11, 4, Usage::Tag::Write), makeItem(11, 11, Usage::Tag::WritableRef),
@@ -248,7 +268,7 @@ void ClangdTestFindReferences::test_data()
// Some of these are conceptually questionable, as S is a type and thus we cannot "read from"
// or "write to" it. But it probably matches the intuitive user expectation.
- QTest::newRow("struct type") << "defs.h" << 7 << ItemList{
+ QTest::newRow("struct type") << "defs.h" << 7 << SearchResultItems{
makeItem(1, 7, Usage::Tag::Declaration),
makeItem(2, 4, (Usage::Tags{Usage::Tag::Declaration, Usage::Tag::ConstructorDestructor})),
makeItem(20, 19, Usage::Tags()), makeItem(10, 9, Usage::Tag::WritableRef),
@@ -260,24 +280,24 @@ void ClangdTestFindReferences::test_data()
makeItem(58, 10, Usage::Tag::Read), makeItem(58, 22, Usage::Tag::Read),
makeItem(59, 4, Usage::Tag::Write), makeItem(59, 21, Usage::Tag::Read)};
- QTest::newRow("struct type 2") << "defs.h" << 450 << ItemList{
+ QTest::newRow("struct type 2") << "defs.h" << 450 << SearchResultItems{
makeItem(20, 7, Usage::Tag::Declaration), makeItem(5, 4, Usage::Tags()),
makeItem(13, 21, Usage::Tags()), makeItem(32, 8, Usage::Tags())};
- QTest::newRow("constructor") << "defs.h" << 627 << ItemList{
+ QTest::newRow("constructor") << "defs.h" << 627 << SearchResultItems{
makeItem(31, 4, (Usage::Tags{Usage::Tag::Declaration, Usage::Tag::ConstructorDestructor})),
makeItem(36, 7, Usage::Tag::ConstructorDestructor)};
- QTest::newRow("subclass") << "defs.h" << 450 << ItemList{
+ QTest::newRow("subclass") << "defs.h" << 450 << SearchResultItems{
makeItem(20, 7, Usage::Tag::Declaration), makeItem(5, 4, Usage::Tags()),
makeItem(13, 21, Usage::Tags()), makeItem(32, 8, Usage::Tags())};
- QTest::newRow("array variable") << "main.cpp" << 1134 << ItemList{
+ QTest::newRow("array variable") << "main.cpp" << 1134 << SearchResultItems{
makeItem(57, 8, Usage::Tag::Declaration), makeItem(58, 4, Usage::Tag::Write),
makeItem(59, 15, Usage::Tag::Read)};
- QTest::newRow("free function") << "defs.h" << 510 << ItemList{
+ QTest::newRow("free function") << "defs.h" << 510 << SearchResultItems{
makeItem(24, 5, Usage::Tag::Declaration), makeItem(19, 4, Usage::Tags()),
makeItem(25, 4, Usage::Tags()), makeItem(60, 26, Usage::Tag::Read)};
- QTest::newRow("member function") << "defs.h" << 192 << ItemList{
+ QTest::newRow("member function") << "defs.h" << 192 << SearchResultItems{
makeItem(9, 12, Usage::Tag::Declaration), makeItem(40, 8, Usage::Tags())};
}
@@ -288,7 +308,7 @@ void ClangdTestFindReferences::test()
{
QFETCH(QString, fileName);
QFETCH(int, pos);
- QFETCH(QList<SearchResultItem>, expectedResults);
+ QFETCH(SearchResultItems, expectedResults);
TextEditor::TextDocument * const doc = document(fileName);
QVERIFY(doc);
@@ -388,13 +408,13 @@ void ClangdTestFollowSymbol::test()
timer.setSingleShot(true);
QEventLoop loop;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- Utils::Link actualLink;
- const auto handler = [&actualLink, &loop](const Utils::Link &l) {
+ Link actualLink;
+ const auto handler = [&actualLink, &loop](const Link &l) {
actualLink = l;
loop.quit();
};
QTextCursor cursor(doc->document());
- const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
+ const int pos = Text::positionInText(doc->document(), sourceLine, sourceColumn);
cursor.setPosition(pos);
client()->followSymbol(doc, cursor, nullptr, handler, true,
goToType ? FollowTo::SymbolType : FollowTo::SymbolDef, false);
@@ -500,15 +520,14 @@ void ClangdTestLocalReferences::test()
QEventLoop loop;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
QList<Range> actualRanges;
- const auto handler = [&actualRanges, &loop](const QString &symbol,
- const Utils::Links &links, int) {
- for (const Utils::Link &link : links)
+ const auto handler = [&actualRanges, &loop](const QString &symbol, const Links &links, int) {
+ for (const Link &link : links)
actualRanges << Range(link.targetLine, link.targetColumn, symbol.length());
loop.quit();
};
QTextCursor cursor(doc->document());
- const int pos = Utils::Text::positionInText(doc->document(), sourceLine, sourceColumn);
+ const int pos = Text::positionInText(doc->document(), sourceLine, sourceColumn);
cursor.setPosition(pos);
client()->findLocalUsages(doc, cursor, std::move(handler));
timer.start(10000);
@@ -639,7 +658,7 @@ void ClangdTestTooltips::test()
connect(client(), &ClangdClient::helpItemGathered, &loop, handler);
QTextCursor cursor(doc->document());
- const int pos = Utils::Text::positionInText(doc->document(), line, column);
+ const int pos = Text::positionInText(doc->document(), line, column);
cursor.setPosition(pos);
editor->editorWidget()->processTooltipRequest(cursor);
@@ -1296,11 +1315,11 @@ void ClangdTestHighlighting::test()
const TextEditor::TextDocument * const doc = document("highlighting.cpp");
QVERIFY(doc);
- const int startPos = Utils::Text::positionInText(doc->document(), firstLine, startColumn);
- const int endPos = Utils::Text::positionInText(doc->document(), lastLine, endColumn);
+ const int startPos = Text::positionInText(doc->document(), firstLine, startColumn);
+ const int endPos = Text::positionInText(doc->document(), lastLine, endColumn);
const auto lessThan = [=](const TextEditor::HighlightingResult &r, int) {
- return Utils::Text::positionInText(doc->document(), r.line, r.column) < startPos;
+ return Text::positionInText(doc->document(), r.line, r.column) < startPos;
};
const auto findResults = [=] {
TextEditor::HighlightingResults results;
@@ -1308,7 +1327,7 @@ void ClangdTestHighlighting::test()
if (it == m_results.cend())
return results;
while (it != m_results.cend()) {
- const int resultEndPos = Utils::Text::positionInText(doc->document(), it->line,
+ const int resultEndPos = Text::positionInText(doc->document(), it->line,
it->column) + it->length;
if (resultEndPos > endPos)
break;
@@ -1419,7 +1438,7 @@ public:
{
const int pos = currentPosition();
QPair<int, int> lineAndColumn;
- Utils::Text::convertPosition(m_doc, pos, &lineAndColumn.first, &lineAndColumn.second);
+ Text::convertPosition(m_doc, pos, &lineAndColumn.first, &lineAndColumn.second);
return lineAndColumn;
}
@@ -1844,7 +1863,7 @@ void ClangdTestCompletion::startCollectingHighlightingInfo()
{
m_documentsWithHighlighting.clear();
connect(client(), &ClangdClient::highlightingResultsReady, this,
- [this](const HighlightingResults &, const Utils::FilePath &file) {
+ [this](const HighlightingResults &, const FilePath &file) {
m_documentsWithHighlighting.insert(file);
});
}
@@ -1861,7 +1880,7 @@ void ClangdTestCompletion::getProposal(const QString &fileName,
if (cursorPos)
*cursorPos = pos;
int line, column;
- Utils::Text::convertPosition(doc->document(), pos, &line, &column);
+ Text::convertPosition(doc->document(), pos, &line, &column);
const auto editor = qobject_cast<BaseTextEditor *>(
EditorManager::openEditorAt({doc->filePath(), line, column - 1}));
QVERIFY(editor);
diff --git a/src/plugins/clangcodemodel/test/clangdtests.h b/src/plugins/clangcodemodel/test/clangdtests.h
index 2a8f33579c..90f4eca45a 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.h
+++ b/src/plugins/clangcodemodel/test/clangdtests.h
@@ -4,11 +4,11 @@
#pragma once
#include <cppeditor/cpptoolstestcase.h>
-#include <coreplugin/find/searchresultitem.h>
#include <texteditor/blockrange.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/semantichighlighter.h>
#include <utils/fileutils.h>
+#include <utils/searchresultitem.h>
#include <QHash>
#include <QObject>
@@ -74,7 +74,7 @@ private slots:
void test();
private:
- QList<Core::SearchResultItem> m_actualResults;
+ Utils::SearchResultItems m_actualResults;
};
class ClangdTestFollowSymbol : public ClangdTest
diff --git a/src/plugins/clangformat/CMakeLists.txt b/src/plugins/clangformat/CMakeLists.txt
index 79767a2ffe..7c49e9ad47 100644
--- a/src/plugins/clangformat/CMakeLists.txt
+++ b/src/plugins/clangformat/CMakeLists.txt
@@ -39,4 +39,5 @@ extend_qtc_plugin(ClangFormat
tests/clangformat-test.h
DEFINES
TESTDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/data"
+ EXPLICIT_MOC tests/clangformat-test.h
)
diff --git a/src/plugins/clangformat/clangformat.qbs b/src/plugins/clangformat/clangformat.qbs
index bd1b967763..057035cd1a 100644
--- a/src/plugins/clangformat/clangformat.qbs
+++ b/src/plugins/clangformat/clangformat.qbs
@@ -54,10 +54,8 @@ QtcPlugin {
"clangformatutils.cpp",
]
- Group {
- name: "Tests"
+ QtcTestFiles {
prefix: "tests/"
- condition: qtc.testsEnabled
cpp.defines: outer.concat('TESTDATA_DIR="' + sourceDirectory + "/tests/data" + '"')
files: [
"clangformat-test.cpp",
diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp
index 45e55495a5..5138e03510 100644
--- a/src/plugins/clangformat/clangformatbaseindenter.cpp
+++ b/src/plugins/clangformat/clangformatbaseindenter.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/icodestylepreferences.h>
#include <texteditor/texteditorsettings.h>
@@ -346,6 +346,17 @@ bool isInsideDummyTextInLine(const QString &originalLine, const QString &modifie
|| !modifiedLine.startsWith(originalLine));
}
+static Utils::Text::Position utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset)
+{
+ Utils::Text::Position position;
+ position.line = static_cast<int>(std::count(utf8Buffer.begin(),
+ utf8Buffer.begin() + utf8Offset, '\n')) + 1;
+ const int startOfLineOffset = utf8Offset ? (utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1)
+ : 0;
+ position.column = QString::fromUtf8(utf8Buffer.mid(startOfLineOffset,
+ utf8Offset - startOfLineOffset)).length();
+ return position;
+}
Utils::Text::Replacements utf16Replacements(const QTextDocument *doc,
const QByteArray &utf8Buffer,
const clang::tooling::Replacements &replacements)
@@ -354,9 +365,8 @@ Utils::Text::Replacements utf16Replacements(const QTextDocument *doc,
convertedReplacements.reserve(replacements.size());
for (const clang::tooling::Replacement &replacement : replacements) {
- Utils::LineColumn lineColUtf16 = Utils::Text::utf16LineColumn(utf8Buffer,
- static_cast<int>(
- replacement.getOffset()));
+ Utils::Text::Position lineColUtf16 = utf16LineColumn(
+ utf8Buffer, static_cast<int>(replacement.getOffset()));
if (!lineColUtf16.isValid())
continue;
@@ -364,14 +374,13 @@ Utils::Text::Replacements utf16Replacements(const QTextDocument *doc,
const QString bufferLineText
= Utils::Text::utf16LineTextInUtf8Buffer(utf8Buffer,
static_cast<int>(replacement.getOffset()));
- if (isInsideDummyTextInLine(lineText, bufferLineText, lineColUtf16.column))
+ if (isInsideDummyTextInLine(lineText, bufferLineText, lineColUtf16.column + 1))
continue;
- lineColUtf16.column = std::min(lineColUtf16.column, int(lineText.length()) + 1);
-
+ lineColUtf16.column = std::min(lineColUtf16.column, int(lineText.length()));
const int utf16Offset = Utils::Text::positionInText(doc,
lineColUtf16.line,
- lineColUtf16.column);
+ lineColUtf16.column + 1);
const int utf16Length = QString::fromUtf8(
utf8Buffer.mid(static_cast<int>(replacement.getOffset()),
static_cast<int>(replacement.getLength())))
@@ -741,7 +750,7 @@ void ClangFormatBaseIndenter::autoIndent(const QTextCursor &cursor,
clang::format::FormatStyle overrideStyle(const Utils::FilePath &fileName)
{
const ProjectExplorer::Project *projectForFile
- = ProjectExplorer::SessionManager::projectForFile(fileName);
+ = ProjectExplorer::ProjectManager::projectForFile(fileName);
const TextEditor::ICodeStylePreferences *preferences
= projectForFile
diff --git a/src/plugins/clangformat/clangformatchecks.cpp b/src/plugins/clangformat/clangformatchecks.cpp
index 1ec64e7a37..6027e600d7 100644
--- a/src/plugins/clangformat/clangformatchecks.cpp
+++ b/src/plugins/clangformat/clangformatchecks.cpp
@@ -15,8 +15,6 @@
#include <QPushButton>
#include <QWidget>
-using namespace Utils;
-
using namespace ClangFormat;
ClangFormatChecks::ClangFormatChecks(QWidget *parent)
diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp
index dc5d1b3dc0..e2f9272e14 100644
--- a/src/plugins/clangformat/clangformatconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatconfigwidget.cpp
@@ -82,7 +82,6 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc
d->project = project;
d->config = std::make_unique<ClangFormatFile>(filePathToCurrentSettings(codeStyle->currentPreferences()));
- resize(489, 305);
d->fallbackConfig = new QLabel(Tr::tr("Clang-Format Style"));
d->checksScrollArea = new QScrollArea();
d->checksWidget = new ClangFormatChecks();
diff --git a/src/plugins/clangformat/clangformatfile.h b/src/plugins/clangformat/clangformatfile.h
index 6e2267befc..dcd0e0d6c1 100644
--- a/src/plugins/clangformat/clangformatfile.h
+++ b/src/plugins/clangformat/clangformatfile.h
@@ -3,7 +3,8 @@
#pragma once
-#include "utils/filepath.h"
+#include <utils/filepath.h>
+
#include <clang/Format/Format.h>
namespace CppEditor { class CppCodeStyleSettings; }
diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
index 5579e594e1..af8a0846b1 100644
--- a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
@@ -31,35 +31,37 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(
, m_project(project)
, m_codeStyle(codeStyle)
{
- resize(489, 305);
-
m_projectHasClangFormat = new QLabel(this);
m_formattingModeLabel = new QLabel(Tr::tr("Formatting mode:"));
m_indentingOrFormatting = new QComboBox(this);
m_formatWhileTyping = new QCheckBox(Tr::tr("Format while typing"));
m_formatOnSave = new QCheckBox(Tr::tr("Format edited code on file save"));
- m_overrideDefault = new QCheckBox(Tr::tr("Override Clang Format configuration file"));
+ m_overrideDefault = new QCheckBox(Tr::tr("Override .clang-format file"));
m_useGlobalSettings = new QCheckBox(Tr::tr("Use global settings"));
m_useGlobalSettings->hide();
m_overrideDefaultFile = ClangFormatSettings::instance().overrideDefaultFile();
using namespace Layouting;
+ QWidget *globalSettingsGroupBoxWidget = nullptr;
+
Group globalSettingsGroupBox {
+ bindTo(&globalSettingsGroupBoxWidget),
title(Tr::tr("ClangFormat settings:")),
- Column {
- m_useGlobalSettings,
- Row { m_formattingModeLabel, m_indentingOrFormatting, st },
- m_formatWhileTyping,
- m_formatOnSave,
- m_projectHasClangFormat,
- m_overrideDefault
+ Column {
+ m_useGlobalSettings,
+ Row { m_formattingModeLabel, m_indentingOrFormatting, st },
+ m_formatWhileTyping,
+ m_formatOnSave,
+ m_projectHasClangFormat,
+ m_overrideDefault
}
};
Column {
- globalSettingsGroupBox
- }.attachTo(this, Layouting::WithoutMargins);
+ globalSettingsGroupBox,
+ noMargin
+ }.attachTo(this);
initCheckBoxes();
initIndentationOrFormattingCombobox();
@@ -74,7 +76,8 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(
m_useGlobalSettings->show();
return;
}
- globalSettingsGroupBox.widget->show();
+
+ globalSettingsGroupBoxWidget->show();
}
ClangFormatGlobalConfigWidget::~ClangFormatGlobalConfigWidget() = default;
@@ -174,8 +177,15 @@ void ClangFormatGlobalConfigWidget::initOverrideCheckBox()
connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged,
this, setEnableOverrideCheckBox);
- m_overrideDefault->setToolTip(
- Tr::tr("Override Clang Format configuration file with the chosen configuration."));
+ m_overrideDefault->setToolTip(Tr::tr(
+ "When this option is enabled, ClangFormat will use a\n"
+ "user-specified configuration from the widget below,\n"
+ "instead of the project .clang-format file. You can\n"
+ "customize the formatting options for your code by\n"
+ "adjusting the settings in the widget. Note that any\n"
+ "changes made there will only affect the current\n"
+ "configuration, and will not modify the project\n"
+ ".clang-format file."));
m_overrideDefault->setChecked(getProjectOverriddenSettings(m_project));
setTemporarilyReadOnly();
diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp
index 94e0fafd8e..67d5171b01 100644
--- a/src/plugins/clangformat/clangformatindenter.cpp
+++ b/src/plugins/clangformat/clangformatindenter.cpp
@@ -15,7 +15,7 @@
#include <utils/genericconstants.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/tabsettings.h>
#include <texteditor/textdocumentlayout.h>
diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp
index 95632db7a0..9304599d99 100644
--- a/src/plugins/clangformat/clangformatutils.cpp
+++ b/src/plugins/clangformat/clangformatutils.cpp
@@ -15,7 +15,7 @@
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/qtcassert.h>
@@ -210,7 +210,7 @@ bool getProjectOverriddenSettings(const ProjectExplorer::Project *project)
bool getCurrentOverriddenSettings(const Utils::FilePath &filePath)
{
- const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
filePath);
return getProjectUseGlobalSettings(project)
@@ -232,7 +232,7 @@ ClangFormatSettings::Mode getProjectIndentationOrFormattingSettings(
ClangFormatSettings::Mode getCurrentIndentationOrFormattingSettings(const Utils::FilePath &filePath)
{
- const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
filePath);
return getProjectUseGlobalSettings(project)
@@ -263,7 +263,7 @@ Utils::FilePath configForFile(const Utils::FilePath &fileName)
return findConfig(fileName);
const ProjectExplorer::Project *projectForFile
- = ProjectExplorer::SessionManager::projectForFile(fileName);
+ = ProjectExplorer::ProjectManager::projectForFile(fileName);
const TextEditor::ICodeStylePreferences *preferences
= projectForFile
diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp
index 109f23875c..343849458a 100644
--- a/src/plugins/clangtools/clangtool.cpp
+++ b/src/plugins/clangtools/clangtool.cpp
@@ -34,7 +34,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorericons.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
@@ -342,7 +342,7 @@ static FileInfos sortedFileInfos(const QVector<CppEditor::ProjectPart::ConstPtr>
static RunSettings runSettings()
{
- if (Project *project = SessionManager::startupProject()) {
+ if (Project *project = ProjectManager::startupProject()) {
const auto projectSettings = ClangToolsProjectSettings::getSettings(project);
if (!projectSettings->useGlobalSettings())
return projectSettings->runSettings();
@@ -599,19 +599,19 @@ static bool continueDespiteReleaseBuild(const QString &toolName)
"<p>%2</p>"
"</body></html>")
.arg(problem, question);
- return CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
- title,
- message,
- ICore::settings(),
- "ClangToolsCorrectModeWarning")
- == QDialogButtonBox::Yes;
+ return CheckableMessageBox::question(ICore::dialogParent(),
+ title,
+ message,
+ ICore::settings(),
+ "ClangToolsCorrectModeWarning")
+ == QMessageBox::Yes;
}
void ClangTool::startTool(ClangTool::FileSelection fileSelection,
const RunSettings &runSettings,
const CppEditor::ClangDiagnosticConfig &diagnosticConfig)
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
QTC_ASSERT(project, return);
QTC_ASSERT(project->activeTarget(), return);
@@ -858,19 +858,19 @@ static CheckResult canAnalyze()
{
const ClangDiagnosticConfig config = diagnosticConfig(runSettings().diagnosticConfigId());
- if (config.isEnabled(ClangToolType::Tidy)
+ if (toolEnabled(ClangToolType::Tidy, config, runSettings())
&& !toolExecutable(ClangToolType::Tidy).isExecutableFile()) {
return {CheckResult::InvalidTidyExecutable,
Tr::tr("Set a valid Clang-Tidy executable.")};
}
- if (config.isEnabled(ClangToolType::Clazy)
+ if (toolEnabled(ClangToolType::Clazy, config, runSettings())
&& !toolExecutable(ClangToolType::Clazy).isExecutableFile()) {
return {CheckResult::InvalidClazyExecutable,
Tr::tr("Set a valid Clazy-Standalone executable.")};
}
- if (Project *project = SessionManager::startupProject()) {
+ if (Project *project = ProjectManager::startupProject()) {
if (!canAnalyzeProject(project)) {
return {CheckResult::ProjectNotSuitable,
Tr::tr("Project \"%1\" is not a C/C++ project.")
diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp
index e583a6269d..be6f416d2e 100644
--- a/src/plugins/clangtools/clangtoolruncontrol.cpp
+++ b/src/plugins/clangtools/clangtoolruncontrol.cpp
@@ -21,14 +21,14 @@
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
-#include <utils/tasktree.h>
#include <QLoggingCategory>
using namespace CppEditor;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg)
@@ -183,10 +183,13 @@ void ClangToolRunWorker::start()
m_filesAnalyzed.clear();
m_filesNotAnalyzed.clear();
- using namespace Tasking;
QList<TaskItem> tasks{ParallelLimit(qMax(1, m_runSettings.parallelJobs()))};
for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) {
- const AnalyzeInputData input{tool, m_diagnosticConfig, m_temporaryDir.path(),
+ if (!m_diagnosticConfig.isEnabled(tool)
+ && !m_runSettings.hasConfigFileForSourceFile(unit.file)) {
+ continue;
+ }
+ const AnalyzeInputData input{tool, m_runSettings, m_diagnosticConfig, m_temporaryDir.path(),
m_environment, unit};
const auto setupHandler = [this, unit, tool] {
const QString filePath = unit.file.toUserOutput();
diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h
index 19ad9189af..e6204da4ff 100644
--- a/src/plugins/clangtools/clangtoolruncontrol.h
+++ b/src/plugins/clangtools/clangtoolruncontrol.h
@@ -15,7 +15,7 @@
#include <QElapsedTimer>
#include <QSet>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace ClangTools {
namespace Internal {
@@ -67,7 +67,7 @@ private:
QString m_targetTriple;
Utils::Id m_toolChainType;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
QSet<Utils::FilePath> m_projectFiles;
QSet<Utils::FilePath> m_filesAnalyzed;
QSet<Utils::FilePath> m_filesNotAnalyzed;
diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp
index f5c955c50e..9d27e9f49b 100644
--- a/src/plugins/clangtools/clangtoolrunner.cpp
+++ b/src/plugins/clangtools/clangtoolrunner.cpp
@@ -11,8 +11,8 @@
#include <cppeditor/clangdiagnosticconfigsmodel.h>
#include <cppeditor/cpptoolsreuse.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
@@ -52,21 +52,22 @@ static bool isClMode(const QStringList &options)
return options.contains("--driver-mode=cl");
}
-static QStringList checksArguments(ClangToolType tool,
- const ClangDiagnosticConfig &diagnosticConfig)
+static QStringList checksArguments(const AnalyzeInputData &input)
{
- if (tool == ClangToolType::Tidy) {
- const ClangDiagnosticConfig::TidyMode tidyMode = diagnosticConfig.clangTidyMode();
- // The argument "-config={}" stops stating/evaluating the .clang-tidy file.
- if (tidyMode == ClangDiagnosticConfig::TidyMode::UseDefaultChecks)
+ if (input.tool == ClangToolType::Tidy) {
+ if (input.runSettings.hasConfigFileForSourceFile(input.unit.file))
+ return {"--warnings-as-errors=-*", "-checks=-clang-diagnostic-*"};
+ switch (input.config.clangTidyMode()) {
+ case ClangDiagnosticConfig::TidyMode::UseDefaultChecks:
+ // The argument "-config={}" stops stating/evaluating the .clang-tidy file.
return {"-config={}", "-checks=-clang-diagnostic-*"};
- if (tidyMode == ClangDiagnosticConfig::TidyMode::UseCustomChecks)
- return {"-config=" + diagnosticConfig.clangTidyChecksAsJson()};
- return {"--warnings-as-errors=-*", "-checks=-clang-diagnostic-*"};
+ case ClangDiagnosticConfig::TidyMode::UseCustomChecks:
+ return {"-config=" + input.config.clangTidyChecksAsJson()};
+ }
}
- const QString clazyChecks = diagnosticConfig.checks(ClangToolType::Clazy);
+ const QString clazyChecks = input.config.checks(ClangToolType::Clazy);
if (!clazyChecks.isEmpty())
- return {"-checks=" + diagnosticConfig.checks(ClangToolType::Clazy)};
+ return {"-checks=" + input.config.checks(ClangToolType::Clazy)};
return {};
}
@@ -111,11 +112,10 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
};
const TreeStorage<ClangToolStorage> storage;
- const auto mainToolArguments = [=](const ClangToolStorage *data)
- {
+ const auto mainToolArguments = [=](const ClangToolStorage &data) {
QStringList result;
- result << "-export-fixes=" + data->outputFilePath.nativePath();
- if (!input.overlayFilePath.isEmpty() && isVFSOverlaySupported(data->executable))
+ result << "-export-fixes=" + data.outputFilePath.nativePath();
+ if (!input.overlayFilePath.isEmpty() && isVFSOverlaySupported(data.executable))
result << "--vfsoverlay=" + input.overlayFilePath;
result << input.unit.file.nativePath();
return result;
@@ -141,46 +141,45 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
return TaskAction::Continue;
};
- const auto onProcessSetup = [=](QtcProcess &process) {
+ const auto onProcessSetup = [=](Process &process) {
process.setEnvironment(input.environment);
process.setUseCtrlCStub(true);
process.setLowPriority();
process.setWorkingDirectory(input.outputDirPath); // Current clang-cl puts log file into working dir.
- const ClangToolStorage *data = storage.activeStorage();
+ const ClangToolStorage &data = *storage;
- const QStringList args = checksArguments(input.tool, input.config)
+ const QStringList args = checksArguments(input)
+ mainToolArguments(data)
+ QStringList{"--"}
+ clangArguments(input.config, input.unit.arguments);
- const CommandLine commandLine = {data->executable, args};
+ const CommandLine commandLine = {data.executable, args};
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput();
process.setCommand(commandLine);
};
- const auto onProcessDone = [=](const QtcProcess &process) {
+ const auto onProcessDone = [=](const Process &process) {
qCDebug(LOG).noquote() << "Output:\n" << process.cleanedStdOut();
if (!outputHandler)
return;
- const ClangToolStorage *data = storage.activeStorage();
- outputHandler({true, input.unit.file, data->outputFilePath, input.tool});
+ outputHandler({true, input.unit.file, storage->outputFilePath, input.tool});
};
- const auto onProcessError = [=](const QtcProcess &process) {
+ const auto onProcessError = [=](const Process &process) {
if (!outputHandler)
return;
const QString details = Tr::tr("Command line: %1\nProcess Error: %2\nOutput:\n%3")
.arg(process.commandLine().toUserOutput())
.arg(process.error())
.arg(process.cleanedStdOut());
- const ClangToolStorage *data = storage.activeStorage();
+ const ClangToolStorage &data = *storage;
QString message;
if (process.result() == ProcessResult::StartFailed)
- message = Tr::tr("An error occurred with the %1 process.").arg(data->name);
+ message = Tr::tr("An error occurred with the %1 process.").arg(data.name);
else if (process.result() == ProcessResult::FinishedWithError)
- message = Tr::tr("%1 finished with exit code: %2.").arg(data->name).arg(process.exitCode());
+ message = Tr::tr("%1 finished with exit code: %2.").arg(data.name).arg(process.exitCode());
else
- message = Tr::tr("%1 crashed.").arg(data->name);
- outputHandler({false, input.unit.file, data->outputFilePath, input.tool, message, details});
+ message = Tr::tr("%1 crashed.").arg(data.name);
+ outputHandler({false, input.unit.file, data.outputFilePath, input.tool, message, details});
};
const Group group {
@@ -188,7 +187,7 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
OnGroupSetup(onGroupSetup),
Group {
optional,
- Process(onProcessSetup, onProcessDone, onProcessError)
+ ProcessTask(onProcessSetup, onProcessDone, onProcessError)
}
};
return group;
diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h
index 104e3ab90b..3a0f59f341 100644
--- a/src/plugins/clangtools/clangtoolrunner.h
+++ b/src/plugins/clangtools/clangtoolrunner.h
@@ -4,12 +4,13 @@
#pragma once
#include "clangfileinfo.h"
+#include "clangtoolssettings.h"
#include <cppeditor/clangdiagnosticconfig.h>
#include <utils/environment.h>
-namespace Utils::Tasking { class TaskItem; }
+namespace Tasking { class TaskItem; }
namespace ClangTools {
namespace Internal {
@@ -28,6 +29,7 @@ using AnalyzeUnits = QList<AnalyzeUnit>;
struct AnalyzeInputData
{
CppEditor::ClangToolType tool = CppEditor::ClangToolType::Tidy;
+ RunSettings runSettings;
CppEditor::ClangDiagnosticConfig config;
Utils::FilePath outputDirPath;
Utils::Environment environment;
@@ -48,9 +50,9 @@ struct AnalyzeOutputData
using AnalyzeSetupHandler = std::function<bool()>;
using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>;
-Utils::Tasking::TaskItem clangToolTask(const AnalyzeInputData &input,
- const AnalyzeSetupHandler &setupHandler,
- const AnalyzeOutputHandler &outputHandler);
+Tasking::TaskItem clangToolTask(const AnalyzeInputData &input,
+ const AnalyzeSetupHandler &setupHandler,
+ const AnalyzeOutputHandler &outputHandler);
} // namespace Internal
} // namespace ClangTools
diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs
index 9c86d4bb11..ad8543bf61 100644
--- a/src/plugins/clangtools/clangtools.qbs
+++ b/src/plugins/clangtools/clangtools.qbs
@@ -74,9 +74,7 @@ QtcPlugin {
"virtualfilesystemoverlay.h",
]
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"clangtoolspreconfiguredsessiontests.cpp",
"clangtoolspreconfiguredsessiontests.h",
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
index 39329c79f1..e0b797a1e0 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
@@ -10,14 +10,14 @@
#include "diagnosticmark.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/textmark.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-#include <QFileInfo>
#include <QLoggingCategory>
#include <tuple>
@@ -484,8 +484,8 @@ DiagnosticFilterModel::DiagnosticFilterModel(QObject *parent)
{
// So that when a user closes and re-opens a project and *then* clicks "Suppress",
// we enter that information into the project settings.
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::projectAdded, this,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectAdded, this,
[this](ProjectExplorer::Project *project) {
if (!m_project && project->projectDirectory() == m_lastProjectDirectory)
setProject(project);
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
index a516ef3066..ed264b603d 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
@@ -313,9 +313,8 @@ bool DiagnosticView::disableChecksEnabled() const
if (!activeConfig.id().isValid())
return false;
- // If all selected diagnostics come from clang-tidy and the active config is controlled
- // by a .clang-tidy file, then we do not offer the action.
- if (activeConfig.clangTidyMode() != ClangDiagnosticConfig::TidyMode::UseConfigFile)
+ // If all selected diagnostics come from clang-tidy and we prefer a .clang-tidy file, then we do not offer the action.
+ if (!settings->runSettings().preferConfigFile())
return true;
return Utils::anyOf(indexes, [this](const QModelIndex &index) {
return model()->data(index).toString().startsWith("clazy-");
diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp
index 6bd68d8710..35086be7aa 100644
--- a/src/plugins/clangtools/clangtoolsplugin.cpp
+++ b/src/plugins/clangtools/clangtoolsplugin.cpp
@@ -19,6 +19,7 @@
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -171,7 +172,7 @@ void ClangToolsPlugin::registerAnalyzeActions()
button->setPopupMode(QToolButton::InstantPopup);
button->setIcon(icon);
button->setToolTip(Tr::tr("Analyze File..."));
- button->setProperty("noArrow", true);
+ button->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
widget->toolBar()->addWidget(button);
const auto toolsMenu = new QMenu(widget);
button->setMenu(toolsMenu);
diff --git a/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp b/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp
index 5041dc4dac..1a87580efc 100644
--- a/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp
+++ b/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp
@@ -5,15 +5,17 @@
#include "clangtool.h"
#include "clangtoolsdiagnostic.h"
-#include "clangtoolsutils.h"
#include <coreplugin/icore.h>
+
#include <cppeditor/compileroptionsbuilder.h>
#include <cppeditor/projectinfo.h>
+
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
@@ -55,8 +57,8 @@ public:
WaitForParsedProjects(const FilePaths &projects)
: m_projectsToWaitFor(projects)
{
- connect(SessionManager::instance(),
- &ProjectExplorer::SessionManager::projectFinishedParsing,
+ connect(ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectFinishedParsing,
this, &WaitForParsedProjects::onProjectFinishedParsing);
}
@@ -87,7 +89,7 @@ void PreconfiguredSessionTests::initTestCase()
QSKIP("Session must not be already active.");
// Load session
- const FilePaths projects = SessionManager::projectsForSessionName(preconfiguredSessionName);
+ const FilePaths projects = ProjectManager::projectsForSessionName(preconfiguredSessionName);
WaitForParsedProjects waitForParsedProjects(projects);
QVERIFY(SessionManager::loadSession(preconfiguredSessionName));
QVERIFY(waitForParsedProjects.wait());
@@ -102,7 +104,7 @@ void PreconfiguredSessionTests::testPreconfiguredSession()
for (ClangTool * const tool : {ClangTidyTool::instance(), ClazyTool::instance()}) {
tool->startTool(ClangTool::FileSelectionType::AllFiles);
- QSignalSpy waitUntilAnalyzerFinished(tool, SIGNAL(finished(bool)));
+ QSignalSpy waitUntilAnalyzerFinished(tool, &ClangTool::finished);
QVERIFY(waitUntilAnalyzerFinished.wait(30000));
const QList<QVariant> arguments = waitUntilAnalyzerFinished.takeFirst();
const bool analyzerFinishedSuccessfully = arguments.first().toBool();
@@ -175,7 +177,7 @@ void PreconfiguredSessionTests::testPreconfiguredSession_data()
bool hasAddedTestData = false;
- for (Project *project : validProjects(SessionManager::projects())) {
+ for (Project *project : validProjects(ProjectManager::projects())) {
for (Target *target : validTargets(project)) {
hasAddedTestData = true;
QTest::newRow(dataTagName(project, target)) << project << target;
@@ -189,17 +191,17 @@ void PreconfiguredSessionTests::testPreconfiguredSession_data()
bool PreconfiguredSessionTests::switchToProjectAndTarget(Project *project,
Target *target)
{
- Project * const activeProject = SessionManager::startupProject();
+ Project * const activeProject = ProjectManager::startupProject();
if (project == activeProject && target == activeProject->activeTarget())
return true; // OK, desired project/target already active.
if (project != activeProject)
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
if (target != project->activeTarget()) {
- QSignalSpy spyFinishedParsing(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::projectFinishedParsing);
- SessionManager::setActiveTarget(project, target, ProjectExplorer::SetActive::NoCascade);
+ QSignalSpy spyFinishedParsing(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::projectFinishedParsing);
+ project->setActiveTarget(target, ProjectExplorer::SetActive::NoCascade);
QTC_ASSERT(spyFinishedParsing.wait(30000), return false);
const QVariant projectArgument = spyFinishedParsing.takeFirst().constFirst();
diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.cpp b/src/plugins/clangtools/clangtoolsprojectsettings.cpp
index cde1731acb..581a8ca57b 100644
--- a/src/plugins/clangtools/clangtoolsprojectsettings.cpp
+++ b/src/plugins/clangtools/clangtoolsprojectsettings.cpp
@@ -4,7 +4,7 @@
#include "clangtoolsprojectsettings.h"
#include "clangtoolsdiagnostic.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp b/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp
index 5fe1487259..8e1ac057f9 100644
--- a/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp
+++ b/src/plugins/clangtools/clangtoolsprojectsettingswidget.cpp
@@ -68,7 +68,7 @@ ClangToolsProjectSettingsWidget::ClangToolsProjectSettingsWidget(ProjectExplorer
m_removeSelectedButton = new QPushButton(Tr::tr("Remove Selected"), this);
m_removeAllButton = new QPushButton(Tr::tr("Remove All"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { m_restoreGlobal, st, gotoClangTidyModeLabel, gotoClazyModeLabel },
@@ -84,8 +84,9 @@ ClangToolsProjectSettingsWidget::ClangToolsProjectSettingsWidget(ProjectExplorer
st
}
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
setUseGlobalSettings(m_projectSettings->useGlobalSettings());
onGlobalCustomChanged(useGlobalSettings());
diff --git a/src/plugins/clangtools/clangtoolssettings.cpp b/src/plugins/clangtools/clangtoolssettings.cpp
index eeb24d1457..286b076f95 100644
--- a/src/plugins/clangtools/clangtoolssettings.cpp
+++ b/src/plugins/clangtools/clangtoolssettings.cpp
@@ -27,6 +27,7 @@ const char clangTidyExecutableKey[] = "ClangTidyExecutable";
const char clazyStandaloneExecutableKey[] = "ClazyStandaloneExecutable";
const char parallelJobsKey[] = "ParallelJobs";
+const char preferConfigFileKey[] = "PreferConfigFile";
const char buildBeforeAnalysisKey[] = "BuildBeforeAnalysis";
const char analyzeOpenFilesKey[] = "AnalyzeOpenFiles";
const char oldDiagnosticConfigIdKey[] = "diagnosticConfigId";
@@ -46,6 +47,7 @@ void RunSettings::fromMap(const QVariantMap &map, const QString &prefix)
{
m_diagnosticConfigId = Id::fromSetting(map.value(prefix + diagnosticConfigIdKey));
m_parallelJobs = map.value(prefix + parallelJobsKey).toInt();
+ m_preferConfigFile = map.value(prefix + preferConfigFileKey).toBool();
m_buildBeforeAnalysis = map.value(prefix + buildBeforeAnalysisKey).toBool();
m_analyzeOpenFiles = map.value(prefix + analyzeOpenFilesKey).toBool();
}
@@ -54,6 +56,7 @@ void RunSettings::toMap(QVariantMap &map, const QString &prefix) const
{
map.insert(prefix + diagnosticConfigIdKey, m_diagnosticConfigId.toSetting());
map.insert(prefix + parallelJobsKey, m_parallelJobs);
+ map.insert(prefix + preferConfigFileKey, m_preferConfigFile);
map.insert(prefix + buildBeforeAnalysisKey, m_buildBeforeAnalysis);
map.insert(prefix + analyzeOpenFilesKey, m_analyzeOpenFiles);
}
@@ -69,10 +72,23 @@ bool RunSettings::operator==(const RunSettings &other) const
{
return m_diagnosticConfigId == other.m_diagnosticConfigId
&& m_parallelJobs == other.m_parallelJobs
+ && m_preferConfigFile == other.m_preferConfigFile
&& m_buildBeforeAnalysis == other.m_buildBeforeAnalysis
&& m_analyzeOpenFiles == other.m_analyzeOpenFiles;
}
+bool RunSettings::hasConfigFileForSourceFile(const Utils::FilePath &sourceFile) const
+{
+ if (!preferConfigFile())
+ return false;
+ for (FilePath parentDir = sourceFile.parentDir(); !parentDir.isEmpty();
+ parentDir = parentDir.parentDir()) {
+ if (parentDir.resolvePath(QLatin1String(".clang-tidy")).isReadableFile())
+ return true;
+ }
+ return false;
+}
+
ClangToolsSettings::ClangToolsSettings()
{
readSettings();
@@ -139,6 +155,7 @@ void ClangToolsSettings::readSettings()
QVariantMap defaults;
defaults.insert(diagnosticConfigIdKey, defaultDiagnosticId().toSetting());
defaults.insert(parallelJobsKey, m_runSettings.parallelJobs());
+ defaults.insert(preferConfigFileKey, m_runSettings.preferConfigFile());
defaults.insert(buildBeforeAnalysisKey, m_runSettings.buildBeforeAnalysis());
defaults.insert(analyzeOpenFilesKey, m_runSettings.analyzeOpenFiles());
map = defaults;
diff --git a/src/plugins/clangtools/clangtoolssettings.h b/src/plugins/clangtools/clangtoolssettings.h
index 3ecbfcc30a..45a2cb032e 100644
--- a/src/plugins/clangtools/clangtoolssettings.h
+++ b/src/plugins/clangtools/clangtoolssettings.h
@@ -31,6 +31,9 @@ public:
Utils::Id diagnosticConfigId() const;
void setDiagnosticConfigId(const Utils::Id &id) { m_diagnosticConfigId = id; }
+ bool preferConfigFile() const { return m_preferConfigFile; }
+ void setPreferConfigFile(bool yesno) { m_preferConfigFile = yesno; }
+
bool buildBeforeAnalysis() const { return m_buildBeforeAnalysis; }
void setBuildBeforeAnalysis(bool yesno) { m_buildBeforeAnalysis = yesno; }
@@ -42,9 +45,12 @@ public:
bool operator==(const RunSettings &other) const;
+ bool hasConfigFileForSourceFile(const Utils::FilePath &sourceFile) const;
+
private:
Utils::Id m_diagnosticConfigId;
int m_parallelJobs = -1;
+ bool m_preferConfigFile = true;
bool m_buildBeforeAnalysis = true;
bool m_analyzeOpenFiles = true;
};
diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp
index c8650c0b77..0672d43054 100644
--- a/src/plugins/clangtools/clangtoolsunittests.cpp
+++ b/src/plugins/clangtools/clangtoolsunittests.cpp
@@ -26,7 +26,6 @@
#include <utils/executeondestruction.h>
#include <utils/fileutils.h>
-#include <QEventLoop>
#include <QSignalSpy>
#include <QTimer>
#include <QtTest>
diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp
index 01dcb614b5..a2cdd5fc7b 100644
--- a/src/plugins/clangtools/clangtoolsutils.cpp
+++ b/src/plugins/clangtools/clangtoolsutils.cpp
@@ -18,8 +18,8 @@
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <cppeditor/clangdiagnosticconfigsmodel.h>
@@ -138,12 +138,11 @@ QString hintAboutBuildBeforeAnalysis()
void showHintAboutBuildBeforeAnalysis()
{
- Utils::CheckableMessageBox::doNotShowAgainInformation(
- Core::ICore::dialogParent(),
- Tr::tr("Info About Build the Project Before Analysis"),
- hintAboutBuildBeforeAnalysis(),
- Core::ICore::settings(),
- "ClangToolsDisablingBuildBeforeAnalysisHint");
+ Utils::CheckableMessageBox::information(Core::ICore::dialogParent(),
+ Tr::tr("Info About Build the Project Before Analysis"),
+ hintAboutBuildBeforeAnalysis(),
+ Core::ICore::settings(),
+ "ClangToolsDisablingBuildBeforeAnalysisHint");
}
FilePath fullPath(const FilePath &executable)
@@ -211,7 +210,7 @@ bool isVFSOverlaySupported(const FilePath &executable)
static QMap<FilePath, bool> vfsCapabilities;
auto it = vfsCapabilities.find(executable);
if (it == vfsCapabilities.end()) {
- QtcProcess p;
+ Process p;
p.setCommand({executable, {"--help"}});
p.runBlocking();
it = vfsCapabilities.insert(executable, p.allOutput().contains("vfsoverlay"));
@@ -284,7 +283,7 @@ static QStringList extraOptions(const QString &envVar)
if (!qtcEnvironmentVariableIsSet(envVar))
return QStringList();
QString arguments = qtcEnvironmentVariable(envVar);
- return Utils::ProcessArgs::splitArgs(arguments);
+ return ProcessArgs::splitArgs(arguments, HostOsInfo::hostOs());
}
QStringList extraClangToolsPrependOptions()
@@ -294,7 +293,7 @@ QStringList extraClangToolsPrependOptions()
static const QStringList options = extraOptions(csaPrependOptions)
+ extraOptions(toolsPrependOptions);
if (!options.isEmpty())
- qWarning() << "ClangTools options are prepended with " << options.toVector();
+ qWarning() << "ClangTools options are prepended with " << options;
return options;
}
@@ -305,7 +304,7 @@ QStringList extraClangToolsAppendOptions()
static const QStringList options = extraOptions(csaAppendOptions)
+ extraOptions(toolsAppendOptions);
if (!options.isEmpty())
- qWarning() << "ClangTools options are appended with " << options.toVector();
+ qWarning() << "ClangTools options are appended with " << options;
return options;
}
@@ -343,5 +342,13 @@ QString clazyDocUrl(const QString &check)
return QString::fromLatin1(urlTemplate).arg(versionString, check);
}
+bool toolEnabled(CppEditor::ClangToolType type, const ClangDiagnosticConfig &config,
+ const RunSettings &runSettings)
+{
+ if (type == ClangToolType::Tidy && runSettings.preferConfigFile())
+ return true;
+ return config.isEnabled(type);
+}
+
} // namespace Internal
} // namespace ClangTools
diff --git a/src/plugins/clangtools/clangtoolsutils.h b/src/plugins/clangtools/clangtoolsutils.h
index 6f4f2871a0..36bcc75ff2 100644
--- a/src/plugins/clangtools/clangtoolsutils.h
+++ b/src/plugins/clangtools/clangtoolsutils.h
@@ -17,6 +17,7 @@ namespace Utils { class FilePath; }
namespace ClangTools {
namespace Internal {
+class RunSettings;
QString clangTidyDocUrl(const QString &check);
QString clazyDocUrl(const QString &check);
@@ -47,6 +48,8 @@ Utils::FilePath toolShippedExecutable(CppEditor::ClangToolType tool);
Utils::FilePath toolExecutable(CppEditor::ClangToolType tool);
Utils::FilePath toolFallbackExecutable(CppEditor::ClangToolType tool);
QString clangToolName(CppEditor::ClangToolType tool);
+bool toolEnabled(CppEditor::ClangToolType type, const CppEditor::ClangDiagnosticConfig &config,
+ const RunSettings &runSettings);
bool isVFSOverlaySupported(const Utils::FilePath &executable);
diff --git a/src/plugins/clangtools/diagnosticconfigswidget.cpp b/src/plugins/clangtools/diagnosticconfigswidget.cpp
index 1d160befa2..57480bf8cd 100644
--- a/src/plugins/clangtools/diagnosticconfigswidget.cpp
+++ b/src/plugins/clangtools/diagnosticconfigswidget.cpp
@@ -14,8 +14,8 @@
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cpptoolsreuse.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/selectablefilesmodel.h>
-#include <projectexplorer/session.h>
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
@@ -56,7 +56,6 @@ QString removeClazyCheck(const QString &checks, const QString &check);
class TidyChecksWidget : public QWidget
{
public:
- QComboBox *tidyMode;
QPushButton *plainTextEditButton;
FancyLineEdit *filterLineEdit;
QTreeView *checksPrefixesTree;
@@ -65,10 +64,6 @@ public:
TidyChecksWidget()
{
- tidyMode = new QComboBox;
- tidyMode->addItem(Tr::tr("Select Checks"));
- tidyMode->addItem(Tr::tr("Use .clang-tidy config file"));
-
plainTextEditButton = new QPushButton(Tr::tr("Edit Checks as String..."));
filterLineEdit = new FancyLineEdit;
@@ -95,16 +90,18 @@ public:
using namespace Layouting;
Column {
- checksPrefixesTree
- }.attachTo(checksPage, WithoutMargins);
+ checksPrefixesTree,
+ noMargin
+ }.attachTo(checksPage);
Column {
invalidExecutableLabel,
st,
- }.attachTo(invalidExecutablePage, WithoutMargins);
+ noMargin
+ }.attachTo(invalidExecutablePage);
Column {
- Row { tidyMode, plainTextEditButton, filterLineEdit },
+ Row { plainTextEditButton, filterLineEdit },
stackedWidget,
}.attachTo(this);
}
@@ -174,13 +171,15 @@ public:
Column {
label,
- Row { groupBox, checksGroupBox }
- }.attachTo(checksPage, WithoutMargins);
+ Row { groupBox, checksGroupBox },
+ noMargin
+ }.attachTo(checksPage);
Column {
invalidExecutableLabel,
- st
- }.attachTo(invalidExecutablePage, WithoutMargins);
+ st,
+ noMargin
+ }.attachTo(invalidExecutablePage);
Column {
enableLowerLevelsCheckBox,
@@ -1107,32 +1106,18 @@ void DiagnosticConfigsWidget::syncClangTidyWidgets(const ClangDiagnosticConfig &
disconnectClangTidyItemChanged();
- const ClangDiagnosticConfig::TidyMode tidyMode = config.clangTidyMode();
- switch (tidyMode) {
- case ClangDiagnosticConfig::TidyMode::UseConfigFile:
- m_tidyChecks->tidyMode->setCurrentIndex(1);
+ if (m_tidyInfo.supportedChecks.isEmpty()) {
m_tidyChecks->plainTextEditButton->setVisible(false);
m_tidyChecks->filterLineEdit->setVisible(false);
- m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::EmptyPage);
- break;
- case ClangDiagnosticConfig::TidyMode::UseCustomChecks:
- case ClangDiagnosticConfig::TidyMode::UseDefaultChecks:
- m_tidyChecks->tidyMode->setCurrentIndex(0);
- if (m_tidyInfo.supportedChecks.isEmpty()) {
- m_tidyChecks->plainTextEditButton->setVisible(false);
- m_tidyChecks->filterLineEdit->setVisible(false);
- m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::InvalidExecutablePage);
- } else {
- m_tidyChecks->plainTextEditButton->setVisible(true);
- m_tidyChecks->filterLineEdit->setVisible(true);
- m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::ChecksPage);
- syncTidyChecksToTree(config);
- }
- break;
+ m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::InvalidExecutablePage);
+ } else {
+ m_tidyChecks->plainTextEditButton->setVisible(true);
+ m_tidyChecks->filterLineEdit->setVisible(true);
+ m_tidyChecks->stackedWidget->setCurrentIndex(TidyPages::ChecksPage);
+ syncTidyChecksToTree(config);
}
const bool enabled = !config.isReadOnly();
- m_tidyChecks->tidyMode->setEnabled(enabled);
m_tidyChecks->plainTextEditButton->setText(enabled ? Tr::tr("Edit Checks as String...")
: Tr::tr("View Checks as String..."));
m_tidyTreeModel->setEnabled(enabled);
@@ -1189,16 +1174,12 @@ void DiagnosticConfigsWidget::syncExtraWidgets(const ClangDiagnosticConfig &conf
void DiagnosticConfigsWidget::connectClangTidyItemChanged()
{
- connect(m_tidyChecks->tidyMode, &QComboBox::currentIndexChanged,
- this, &DiagnosticConfigsWidget::onClangTidyModeChanged);
connect(m_tidyTreeModel.get(), &TidyChecksTreeModel::dataChanged,
this, &DiagnosticConfigsWidget::onClangTidyTreeChanged);
}
void DiagnosticConfigsWidget::disconnectClangTidyItemChanged()
{
- disconnect(m_tidyChecks->tidyMode, &QComboBox::currentIndexChanged,
- this, &DiagnosticConfigsWidget::onClangTidyModeChanged);
disconnect(m_tidyTreeModel.get(), &TidyChecksTreeModel::dataChanged,
this, &DiagnosticConfigsWidget::onClangTidyTreeChanged);
}
@@ -1215,18 +1196,6 @@ void DiagnosticConfigsWidget::disconnectClazyItemChanged()
this, &DiagnosticConfigsWidget::onClazyTreeChanged);
}
-void DiagnosticConfigsWidget::onClangTidyModeChanged(int index)
-{
- const ClangDiagnosticConfig::TidyMode tidyMode
- = index == 0 ? ClangDiagnosticConfig::TidyMode::UseCustomChecks
- : ClangDiagnosticConfig::TidyMode::UseConfigFile;
-
- ClangDiagnosticConfig config = currentConfig();
- config.setClangTidyMode(tidyMode);
- updateConfig(config);
- syncClangTidyWidgets(config);
-}
-
void DiagnosticConfigsWidget::onClangTidyTreeChanged()
{
ClangDiagnosticConfig config = currentConfig();
@@ -1294,7 +1263,7 @@ void disableChecks(const QList<Diagnostic> &diagnostics)
Utils::Id activeConfigId = settings->runSettings().diagnosticConfigId();
ClangToolsProjectSettings::ClangToolsProjectSettingsPtr projectSettings;
- if (ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ if (ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
diagnostics.first().location.filePath)) {
projectSettings = ClangToolsProjectSettings::getSettings(project);
if (!projectSettings->useGlobalSettings())
@@ -1330,7 +1299,7 @@ void disableChecks(const QList<Diagnostic> &diagnostics)
}
config.setChecks(ClangToolType::Clazy,
removeClazyCheck(config.checks(ClangToolType::Clazy), diag.name));
- } else if (config.clangTidyMode() != ClangDiagnosticConfig::TidyMode::UseConfigFile) {
+ } else if (!settings->runSettings().preferConfigFile()){
if (config.clangTidyMode() == ClangDiagnosticConfig::TidyMode::UseDefaultChecks) {
config.setClangTidyMode(ClangDiagnosticConfig::TidyMode::UseCustomChecks);
const ClangTidyInfo tidyInfo(toolExecutable(ClangToolType::Tidy));
diff --git a/src/plugins/clangtools/diagnosticconfigswidget.h b/src/plugins/clangtools/diagnosticconfigswidget.h
index 7a3b892760..04af842f0e 100644
--- a/src/plugins/clangtools/diagnosticconfigswidget.h
+++ b/src/plugins/clangtools/diagnosticconfigswidget.h
@@ -41,7 +41,6 @@ private:
void syncClazyWidgets(const CppEditor::ClangDiagnosticConfig &config);
void syncClazyChecksGroupBox();
- void onClangTidyModeChanged(int index);
void onClangTidyTreeChanged();
void onClazyTreeChanged();
diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp
index 81a7e8f544..178022a91a 100644
--- a/src/plugins/clangtools/documentclangtoolrunner.cpp
+++ b/src/plugins/clangtools/documentclangtoolrunner.cpp
@@ -18,14 +18,15 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildtargettype.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <solutions/tasking/tasktree.h>
+
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <QLoggingCategory>
#include <QScopeGuard>
@@ -35,6 +36,7 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.cftr", QtWarningMsg)
using namespace Core;
using namespace CppEditor;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace ClangTools {
@@ -97,8 +99,8 @@ void DocumentClangToolRunner::scheduleRun()
static Project *findProject(const FilePath &file)
{
- Project *project = SessionManager::projectForFile(file);
- return project ? project : SessionManager::startupProject();
+ Project *project = ProjectManager::projectForFile(file);
+ return project ? project : ProjectManager::startupProject();
}
static VirtualFileSystemOverlay &vfso()
@@ -188,17 +190,20 @@ void DocumentClangToolRunner::run()
vfso().update();
const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId());
const Environment env = projectBuildEnvironment(project);
- using namespace Tasking;
QList<TaskItem> tasks{parallel};
- const auto addClangTool = [this, &config, &env, &tasks](ClangToolType tool) {
- if (!config.isEnabled(tool))
+ const auto addClangTool = [this, &runSettings, &config, &env, &tasks](ClangToolType tool) {
+ if (!toolEnabled(tool, config, runSettings))
+ return;
+ if (!config.isEnabled(tool) && !runSettings.hasConfigFileForSourceFile(m_fileInfo.file))
return;
const FilePath executable = toolExecutable(tool);
+ if (executable.isEmpty() || !executable.isExecutableFile())
+ return;
const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable);
- if (!executable.isExecutableFile() || includeDir.isEmpty() || clangVersion.isEmpty())
+ if (includeDir.isEmpty() || clangVersion.isEmpty())
return;
const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion);
- const AnalyzeInputData input{tool, config, m_temporaryDir.path(), env, unit,
+ const AnalyzeInputData input{tool, runSettings, config, m_temporaryDir.path(), env, unit,
vfso().overlayFilePath().toString()};
const auto setupHandler = [this, executable] {
return !m_document->isModified() || isVFSOverlaySupported(executable);
diff --git a/src/plugins/clangtools/documentclangtoolrunner.h b/src/plugins/clangtools/documentclangtoolrunner.h
index 94111abb37..e80a9632dc 100644
--- a/src/plugins/clangtools/documentclangtoolrunner.h
+++ b/src/plugins/clangtools/documentclangtoolrunner.h
@@ -14,8 +14,8 @@
#include <QTimer>
namespace Core { class IDocument; }
+namespace Tasking { class TaskTree; }
namespace TextEditor { class TextEditorWidget; }
-namespace Utils { class TaskTree; }
namespace ClangTools {
namespace Internal {
@@ -51,7 +51,7 @@ private:
QList<QPointer<TextEditor::TextEditorWidget>> m_editorsWithMarkers;
SuppressedDiagnosticsList m_suppressed;
Utils::FilePath m_lastProjectDirectory;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
};
} // namespace Internal
diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp
index 1e280a6889..22898b5734 100644
--- a/src/plugins/clangtools/executableinfo.cpp
+++ b/src/plugins/clangtools/executableinfo.cpp
@@ -7,7 +7,7 @@
#include <coreplugin/messagemanager.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
#include <QFileInfo>
@@ -26,7 +26,7 @@ static QString runExecutable(const Utils::CommandLine &commandLine, QueryFailMod
if (commandLine.executable().isEmpty() || !commandLine.executable().toFileInfo().isExecutable())
return {};
- QtcProcess cpp;
+ Process cpp;
Environment env = Environment::systemEnvironment();
env.setupEnglishOutput();
cpp.setEnvironment(env);
diff --git a/src/plugins/clangtools/filterdialog.cpp b/src/plugins/clangtools/filterdialog.cpp
index 4c59b44b55..bcb81346d0 100644
--- a/src/plugins/clangtools/filterdialog.cpp
+++ b/src/plugins/clangtools/filterdialog.cpp
@@ -86,7 +86,7 @@ FilterDialog::FilterDialog(const Checks &checks, QWidget *parent)
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
m_view->setIndentation(0);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Select the diagnostics to display."),
diff --git a/src/plugins/clangtools/runsettingswidget.cpp b/src/plugins/clangtools/runsettingswidget.cpp
index 0e7a34959f..cd0dc6a1ee 100644
--- a/src/plugins/clangtools/runsettingswidget.cpp
+++ b/src/plugins/clangtools/runsettingswidget.cpp
@@ -28,14 +28,10 @@ namespace ClangTools::Internal {
RunSettingsWidget::RunSettingsWidget(QWidget *parent)
: QWidget(parent)
{
- resize(383, 125);
-
m_diagnosticWidget = new ClangDiagnosticConfigsSelectionWidget;
-
+ m_preferConfigFile = new QCheckBox(Tr::tr("Prefer .clang-tidy file, if present"));
m_buildBeforeAnalysis = new QCheckBox(Tr::tr("Build the project before analysis"));
-
m_analyzeOpenFiles = new QCheckBox(Tr::tr("Analyze open files"));
-
m_parallelJobsSpinBox = new QSpinBox;
m_parallelJobsSpinBox->setRange(1, 32);
@@ -47,12 +43,14 @@ RunSettingsWidget::RunSettingsWidget(QWidget *parent)
title(Tr::tr("Run Options")),
Column {
m_diagnosticWidget,
+ m_preferConfigFile,
m_buildBeforeAnalysis,
m_analyzeOpenFiles,
Row { Tr::tr("Parallel jobs:"), m_parallelJobsSpinBox, st },
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
}
RunSettingsWidget::~RunSettingsWidget() = default;
@@ -99,6 +97,9 @@ void RunSettingsWidget::fromSettings(const RunSettings &s)
connect(m_diagnosticWidget, &ClangDiagnosticConfigsSelectionWidget::changed,
this, &RunSettingsWidget::changed);
+ m_preferConfigFile->setChecked(s.preferConfigFile());
+ connect(m_preferConfigFile, &QCheckBox::toggled, this, &RunSettingsWidget::changed);
+
disconnect(m_buildBeforeAnalysis, 0, 0, 0);
m_buildBeforeAnalysis->setToolTip(hintAboutBuildBeforeAnalysis());
m_buildBeforeAnalysis->setCheckState(s.buildBeforeAnalysis() ? Qt::Checked : Qt::Unchecked);
@@ -115,13 +116,13 @@ void RunSettingsWidget::fromSettings(const RunSettings &s)
connect(m_parallelJobsSpinBox, &QSpinBox::valueChanged, this, &RunSettingsWidget::changed);
m_analyzeOpenFiles->setChecked(s.analyzeOpenFiles());
connect(m_analyzeOpenFiles, &QCheckBox::toggled, this, &RunSettingsWidget::changed);
-
}
RunSettings RunSettingsWidget::toSettings() const
{
RunSettings s;
s.setDiagnosticConfigId(m_diagnosticWidget->currentConfigId());
+ s.setPreferConfigFile(m_preferConfigFile->isChecked());
s.setBuildBeforeAnalysis(m_buildBeforeAnalysis->checkState() == Qt::CheckState::Checked);
s.setParallelJobs(m_parallelJobsSpinBox->value());
s.setAnalyzeOpenFiles(m_analyzeOpenFiles->checkState() == Qt::CheckState::Checked);
diff --git a/src/plugins/clangtools/runsettingswidget.h b/src/plugins/clangtools/runsettingswidget.h
index 532bbd4ff8..0d2e4b645a 100644
--- a/src/plugins/clangtools/runsettingswidget.h
+++ b/src/plugins/clangtools/runsettingswidget.h
@@ -37,6 +37,7 @@ signals:
private:
CppEditor::ClangDiagnosticConfigsSelectionWidget *m_diagnosticWidget;
+ QCheckBox *m_preferConfigFile;
QCheckBox *m_buildBeforeAnalysis;
QCheckBox *m_analyzeOpenFiles;
QSpinBox *m_parallelJobsSpinBox;
diff --git a/src/plugins/clangtools/settingswidget.cpp b/src/plugins/clangtools/settingswidget.cpp
index 76f915dd99..80aad561b0 100644
--- a/src/plugins/clangtools/settingswidget.cpp
+++ b/src/plugins/clangtools/settingswidget.cpp
@@ -34,8 +34,6 @@ SettingsWidget::SettingsWidget()
{
m_instance = this;
- resize(400, 300);
-
auto createPathChooser = [this](ClangToolType tool)
{
const QString placeHolderText = toolShippedExecutable(tool).toUserOutput();
@@ -53,6 +51,7 @@ SettingsWidget::SettingsWidget()
pathChooser->setHistoryCompleter(tool == ClangToolType::Tidy
? QString("ClangTools.ClangTidyExecutable.History")
: QString("ClangTools.ClazyStandaloneExecutable.History"));
+ pathChooser->setCommandVersionArguments({"--version"});
return pathChooser;
};
m_clangTidyPathChooser = createPathChooser(ClangToolType::Tidy);
diff --git a/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt b/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt
index 30efc99691..98f9833492 100644
--- a/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt
+++ b/src/plugins/clangtools/unit-tests/exported-diagnostics/CMakeLists.txt
@@ -11,7 +11,7 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-find_package(Qt5 COMPONENTS Widgets REQUIRED)
+find_package(Qt6 COMPONENTS Widgets REQUIRED)
add_executable(clangtools
main.cpp
diff --git a/src/plugins/classview/classviewmanager.cpp b/src/plugins/classview/classviewmanager.cpp
index a98cec7044..7f4eeb955c 100644
--- a/src/plugins/classview/classviewmanager.cpp
+++ b/src/plugins/classview/classviewmanager.cpp
@@ -8,8 +8,11 @@
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppmodelmanager.h>
+
#include <coreplugin/progressmanager/progressmanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/texteditor.h>
#include <QThread>
@@ -90,7 +93,7 @@ void ManagerPrivate::resetParser()
cancelScheduledUpdate();
QHash<FilePath, QPair<QString, FilePaths>> projectData;
- for (const Project *project : SessionManager::projects()) {
+ for (const Project *project : ProjectManager::projects()) {
projectData.insert(project->projectFilePath(),
{project->displayName(), project->files(Project::SourceFiles)});
}
@@ -201,8 +204,8 @@ void Manager::initialize()
d->m_timer.setSingleShot(true);
// connections to enable/disable navi widget factory
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::projectAdded,
+ ProjectManager *sessionManager = ProjectManager::instance();
+ connect(sessionManager, &ProjectManager::projectAdded,
this, [this](Project *project) {
const FilePath projectPath = project->projectFilePath();
const QString projectName = project->displayName();
@@ -211,7 +214,7 @@ void Manager::initialize()
d->m_parser->addProject(projectPath, projectName, projectFiles);
}, Qt::QueuedConnection);
});
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
this, [this](Project *project) {
const FilePath projectPath = project->projectFilePath();
QMetaObject::invokeMethod(d->m_parser, [this, projectPath]() {
diff --git a/src/plugins/classview/classviewparsertreeitem.cpp b/src/plugins/classview/classviewparsertreeitem.cpp
index 1d97d0c013..787b63b3c8 100644
--- a/src/plugins/classview/classviewparsertreeitem.cpp
+++ b/src/plugins/classview/classviewparsertreeitem.cpp
@@ -7,9 +7,11 @@
#include <cplusplus/Icons.h>
#include <cplusplus/Overview.h>
+
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <QDebug>
#include <QHash>
@@ -261,7 +263,7 @@ bool ParserTreeItem::canFetchMore(QStandardItem *item) const
*/
void ParserTreeItem::fetchMore(QStandardItem *item) const
{
- using ProjectExplorer::SessionManager;
+ using ProjectExplorer::ProjectManager;
if (!item)
return;
@@ -283,7 +285,7 @@ void ParserTreeItem::fetchMore(QStandardItem *item) const
// icon
const Utils::FilePath &filePath = ptr->projectFilePath();
if (!filePath.isEmpty()) {
- ProjectExplorer::Project *project = SessionManager::projectForFile(filePath);
+ ProjectExplorer::Project *project = ProjectManager::projectForFile(filePath);
if (project)
add->setIcon(project->containerNode()->icon());
}
diff --git a/src/plugins/clearcase/checkoutdialog.cpp b/src/plugins/clearcase/checkoutdialog.cpp
index 8c3a5b8204..df8c43a4e1 100644
--- a/src/plugins/clearcase/checkoutdialog.cpp
+++ b/src/plugins/clearcase/checkoutdialog.cpp
@@ -48,7 +48,7 @@ CheckOutDialog::CheckOutDialog(const QString &fileName, bool isUcm, bool showCom
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
lblFileName,
@@ -67,7 +67,7 @@ CheckOutDialog::CheckOutDialog(const QString &fileName, bool isUcm, bool showCom
m_actSelector = new ActivitySelector(this);
m_verticalLayout->insertWidget(0, m_actSelector);
- m_verticalLayout->insertWidget(1, Utils::Layouting::createHr());
+ m_verticalLayout->insertWidget(1, Layouting::createHr());
}
if (!showComment)
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index de884452be..aa5c598c84 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -27,16 +27,16 @@
#include <texteditor/textdocument.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/hostosinfo.h>
#include <utils/infobar.h>
#include <utils/layoutbuilder.h>
#include <utils/parameteraction.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <vcsbase/basevcseditorfactory.h>
@@ -57,7 +57,6 @@
#include <QDir>
#include <QFileInfo>
#include <QFuture>
-#include <QFutureInterface>
#include <QInputDialog>
#include <QLabel>
#include <QList>
@@ -259,7 +258,7 @@ private:
CommandResult runCleartool(const FilePath &workingDir, const QStringList &arguments,
VcsBase::RunFlags flags = VcsBase::RunFlags::None,
QTextCodec *codec = nullptr, int timeoutMultiplier = 1) const;
- static void sync(QFutureInterface<void> &future, QStringList files);
+ static void sync(QPromise<void> &promise, QStringList files);
void history(const FilePath &workingDir,
const QStringList &file = {},
@@ -488,7 +487,7 @@ FileStatus::Status ClearCasePluginPrivate::getFileStatus(const QString &fileName
/// "cleartool pwv" returns the values for "set view" and "working directory view", also for
/// snapshot views.
///
-/// \returns The ClearCase topLevel/VOB directory for this directory
+/// Returns the ClearCase topLevel/VOB directory for this directory.
QString ClearCasePluginPrivate::ccManagesDirectory(const FilePath &directory) const
{
const CommandResult result = runCleartoolProc(directory, {"pwv"});
@@ -595,7 +594,7 @@ ClearCasePluginPrivate::ClearCasePluginPrivate()
m_settings.fromSettings(ICore::settings());
// update view name when changing active project
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &ClearCasePluginPrivate::projectChanged);
const QString description = QLatin1String("ClearCase");
@@ -1575,7 +1574,7 @@ CommandResult ClearCasePluginPrivate::runCleartoolProc(const FilePath &workingDi
if (m_settings.ccBinaryPath.isEmpty())
return CommandResult(ProcessResult::StartFailed, Tr::tr("No ClearCase executable specified."));
- QtcProcess process;
+ Process process;
Environment env = Environment::systemEnvironment();
VcsBase::setProcessEnvironment(&env);
process.setEnvironment(env);
@@ -1657,7 +1656,7 @@ bool ClearCasePluginPrivate::vcsOpen(const FilePath &workingDir, const QString &
if (!m_settings.disableIndexer &&
(fi.isWritable() || vcsStatus(absPath).status == FileStatus::Unknown))
- runAsync(sync, QStringList(absPath)).waitForFinished();
+ Utils::asyncRun(sync, QStringList(absPath)).waitForFinished();
if (vcsStatus(absPath).status == FileStatus::CheckedOut) {
QMessageBox::information(ICore::dialogParent(), Tr::tr("ClearCase Checkout"),
Tr::tr("File is already checked out."));
@@ -2124,12 +2123,13 @@ void ClearCasePluginPrivate::updateIndex()
{
QTC_ASSERT(currentState().hasTopLevel(), return);
ProgressManager::cancelTasks(ClearCase::Constants::TASK_INDEX);
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project)
return;
m_checkInAllAction->setEnabled(false);
m_statusMap->clear();
- QFuture<void> result = runAsync(sync, transform(project->files(Project::SourceFiles), &FilePath::toString));
+ QFuture<void> result = Utils::asyncRun(sync, transform(project->files(Project::SourceFiles),
+ &FilePath::toString));
if (!m_settings.disableIndexer)
ProgressManager::addTask(result, Tr::tr("Updating ClearCase Index"), ClearCase::Constants::TASK_INDEX);
}
@@ -2232,7 +2232,7 @@ void ClearCasePluginPrivate::diffGraphical(const QString &file1, const QString &
args << file1;
if (!pred)
args << file2;
- QtcProcess::startDetached({m_settings.ccBinaryPath, args}, m_topLevel);
+ Process::startDetached({m_settings.ccBinaryPath, args}, m_topLevel);
}
QString ClearCasePluginPrivate::runExtDiff(const FilePath &workingDir, const QStringList &arguments,
@@ -2242,7 +2242,7 @@ QString ClearCasePluginPrivate::runExtDiff(const FilePath &workingDir, const QSt
diff.addArgs(m_settings.diffArgs.split(' ', Qt::SkipEmptyParts));
diff.addArgs(arguments);
- QtcProcess process;
+ Process process;
process.setTimeoutS(timeOutS);
process.setWorkingDirectory(workingDir);
process.setCodec(outputCodec ? outputCodec : QTextCodec::codecForName("UTF-8"));
@@ -2261,7 +2261,7 @@ void ClearCasePluginPrivate::syncSlot()
FilePath topLevel = state.topLevel();
if (topLevel != state.currentProjectTopLevel())
return;
- runAsync(sync, QStringList());
+ Utils::asyncRun(sync, QStringList()); // TODO: make use of returned QFuture
}
void ClearCasePluginPrivate::closing()
@@ -2271,12 +2271,12 @@ void ClearCasePluginPrivate::closing()
disconnect(qApp, &QApplication::applicationStateChanged, nullptr, nullptr);
}
-void ClearCasePluginPrivate::sync(QFutureInterface<void> &future, QStringList files)
+void ClearCasePluginPrivate::sync(QPromise<void> &promise, QStringList files)
{
ClearCasePluginPrivate *plugin = ClearCasePluginPrivate::instance();
ClearCaseSync ccSync(plugin->m_statusMap);
connect(&ccSync, &ClearCaseSync::updateStreamAndView, plugin, &ClearCasePluginPrivate::updateStreamAndView);
- ccSync.run(future, files);
+ ccSync.run(promise, files);
}
QString ClearCasePluginPrivate::displayName() const
diff --git a/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp b/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp
index 44c3fd1b44..8708e566f3 100644
--- a/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp
+++ b/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp
@@ -69,7 +69,7 @@ void ClearCaseSubmitEditorWidget::addActivitySelector(bool isUcm)
m_actSelector = new ActivitySelector;
m_verticalLayout->insertWidget(0, m_actSelector);
- m_verticalLayout->insertWidget(1, Utils::Layouting::createHr());
+ m_verticalLayout->insertWidget(1, Layouting::createHr());
}
QString ClearCaseSubmitEditorWidget::commitName() const
diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp
index 388ce1a8c1..8476d1f04a 100644
--- a/src/plugins/clearcase/clearcasesync.cpp
+++ b/src/plugins/clearcase/clearcasesync.cpp
@@ -6,12 +6,13 @@
#include "clearcasesettings.h"
#include <QDir>
-#include <QFutureInterface>
#include <QRegularExpression>
#include <QStringList>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+
+#include <QPromise>
#ifdef WITH_TESTS
#include <QTest>
@@ -22,13 +23,12 @@ using namespace Utils;
namespace ClearCase::Internal {
-static void runProcess(QFutureInterface<void> &future,
- const ClearCaseSettings &settings,
+static void runProcess(QPromise<void> &promise, const ClearCaseSettings &settings,
const QStringList &args,
std::function<void(const QString &buffer, int processed)> processLine)
{
const QString viewRoot = ClearCasePlugin::viewData().root;
- QtcProcess process;
+ Process process;
process.setWorkingDirectory(FilePath::fromString(viewRoot));
process.setCommand({settings.ccBinaryPath, args});
process.start();
@@ -37,7 +37,7 @@ static void runProcess(QFutureInterface<void> &future,
int processed = 0;
QString buffer;
- while (process.waitForReadyRead() && !future.isCanceled()) {
+ while (process.waitForReadyRead() && !promise.isCanceled()) {
buffer += QString::fromLocal8Bit(process.readAllRawStandardOutput());
int index = buffer.indexOf('\n');
while (index != -1) {
@@ -135,7 +135,7 @@ void ClearCaseSync::updateStatusForNotManagedFiles(const QStringList &files)
}
}
-void ClearCaseSync::syncSnapshotView(QFutureInterface<void> &future, QStringList &files,
+void ClearCaseSync::syncSnapshotView(QPromise<void> &promise, QStringList &files,
const ClearCaseSettings &settings)
{
const QString view = ClearCasePlugin::viewData().name;
@@ -167,18 +167,18 @@ void ClearCaseSync::syncSnapshotView(QFutureInterface<void> &future, QStringList
// adding 1 for initial sync in which total is not accurate, to prevent finishing
// (we don't want it to become green)
- future.setProgressRange(0, totalFileCount + 1);
+ promise.setProgressRange(0, totalFileCount + 1);
int totalProcessed = 0;
- runProcess(future, settings, args, [&](const QString &buffer, int processed) {
+ runProcess(promise, settings, args, [&](const QString &buffer, int processed) {
processCleartoolLsLine(viewRootDir, buffer);
- future.setProgressValue(qMin(totalFileCount, processed));
+ promise.setProgressValue(qMin(totalFileCount, processed));
totalProcessed = processed;
});
- if (!future.isCanceled()) {
+ if (!promise.isCanceled()) {
updateStatusForNotManagedFiles(files);
- future.setProgressValue(totalFileCount + 1);
+ promise.setProgressValue(totalFileCount + 1);
if (!hot)
updateTotalFilesCount(view, settings, totalProcessed);
}
@@ -193,21 +193,20 @@ void ClearCaseSync::processCleartoolLscheckoutLine(const QString &buffer)
///
/// Update the file status for dynamic views.
///
-void ClearCaseSync::syncDynamicView(QFutureInterface<void> &future,
- const ClearCaseSettings& settings)
+void ClearCaseSync::syncDynamicView(QPromise<void> &promise, const ClearCaseSettings& settings)
{
// Always invalidate status for all files
invalidateStatusAllFiles();
const QStringList args({"lscheckout", "-avobs", "-me", "-cview", "-s"});
- runProcess(future, settings, args, [&](const QString &buffer, int processed) {
+ runProcess(promise, settings, args, [&](const QString &buffer, int processed) {
processCleartoolLscheckoutLine(buffer);
- future.setProgressValue(processed);
+ promise.setProgressValue(processed);
});
}
-void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files)
+void ClearCaseSync::run(QPromise<void> &promise, QStringList &files)
{
ClearCaseSettings settings = ClearCasePlugin::settings();
if (settings.disableIndexer)
@@ -225,9 +224,9 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files)
emit updateStreamAndView();
if (ClearCasePlugin::viewData().isDynamic)
- syncDynamicView(future, settings);
+ syncDynamicView(promise, settings);
else
- syncSnapshotView(future, files, settings);
+ syncSnapshotView(promise, files, settings);
}
#ifdef WITH_TESTS
diff --git a/src/plugins/clearcase/clearcasesync.h b/src/plugins/clearcase/clearcasesync.h
index a008dabb60..4b69985b0c 100644
--- a/src/plugins/clearcase/clearcasesync.h
+++ b/src/plugins/clearcase/clearcasesync.h
@@ -8,7 +8,7 @@
QT_BEGIN_NAMESPACE
class QDir;
template <typename T>
-class QFutureInterface;
+class QPromise;
QT_END_NAMESPACE
namespace ClearCase::Internal {
@@ -18,7 +18,7 @@ class ClearCaseSync : public QObject
Q_OBJECT
public:
explicit ClearCaseSync(QSharedPointer<StatusMap> statusMap);
- void run(QFutureInterface<void> &future, QStringList &files);
+ void run(QPromise<void> &promise, QStringList &files);
QStringList updateStatusHotFiles(const QString &viewRoot, int &total);
void invalidateStatus(const QDir &viewRootDir, const QStringList &files);
@@ -28,9 +28,8 @@ public:
const int processed);
void updateStatusForNotManagedFiles(const QStringList &files);
- void syncDynamicView(QFutureInterface<void> &future,
- const ClearCaseSettings &settings);
- void syncSnapshotView(QFutureInterface<void> &future, QStringList &files,
+ void syncDynamicView(QPromise<void> &promise, const ClearCaseSettings &settings);
+ void syncSnapshotView(QPromise<void> &promise, QStringList &files,
const ClearCaseSettings &settings);
void processCleartoolLscheckoutLine(const QString &buffer);
diff --git a/src/plugins/clearcase/settingspage.cpp b/src/plugins/clearcase/settingspage.cpp
index bcbab9f16a..1ff8e48264 100644
--- a/src/plugins/clearcase/settingspage.cpp
+++ b/src/plugins/clearcase/settingspage.cpp
@@ -50,8 +50,6 @@ private:
SettingsPageWidget::SettingsPageWidget()
{
- resize(512, 589);
-
commandPathChooser = new PathChooser;
commandPathChooser->setPromptDialogTitle(Tr::tr("ClearCase Command"));
commandPathChooser->setExpectedKind(PathChooser::ExistingCommand);
@@ -109,8 +107,8 @@ SettingsPageWidget::SettingsPageWidget()
using namespace Layouting;
Form {
- Tr::tr("Arg&uments:"), diffArgsEdit
- }.attachTo(diffWidget, WithoutMargins);
+ Tr::tr("Arg&uments:"), diffArgsEdit, noMargin
+ }.attachTo(diffWidget);
Column {
Group {
diff --git a/src/plugins/clearcase/versionselector.cpp b/src/plugins/clearcase/versionselector.cpp
index 2c0d46a6a3..4d3dd16059 100644
--- a/src/plugins/clearcase/versionselector.cpp
+++ b/src/plugins/clearcase/versionselector.cpp
@@ -51,7 +51,7 @@ VersionSelector::VersionSelector(const QString &fileName, const QString &message
"the changes (not supported by the plugin)")
+ "</b></p></body></html>");
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
headerLabel,
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt b/src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt
new file mode 100644
index 0000000000..515e403a2d
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/Copyright.txt
@@ -0,0 +1,136 @@
+CMake - Cross Platform Makefile Generator
+Copyright 2000-2023 Kitware, Inc. and Contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Kitware, Inc. nor the names of Contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------------------------------------------------------------------------------
+
+The following individuals and institutions are among the Contributors:
+
+* Aaron C. Meadows <cmake@shadowguarddev.com>
+* Adriaan de Groot <groot@kde.org>
+* Aleksey Avdeev <solo@altlinux.ru>
+* Alexander Neundorf <neundorf@kde.org>
+* Alexander Smorkalov <alexander.smorkalov@itseez.com>
+* Alexey Sokolov <sokolov@google.com>
+* Alex Merry <alex.merry@kde.org>
+* Alex Turbov <i.zaufi@gmail.com>
+* Andreas Pakulat <apaku@gmx.de>
+* Andreas Schneider <asn@cryptomilk.org>
+* André Rigland Brodtkorb <Andre.Brodtkorb@ifi.uio.no>
+* Axel Huebl, Helmholtz-Zentrum Dresden - Rossendorf
+* Benjamin Eikel
+* Bjoern Ricks <bjoern.ricks@gmail.com>
+* Brad Hards <bradh@kde.org>
+* Christopher Harvey
+* Christoph Grüninger <foss@grueninger.de>
+* Clement Creusot <creusot@cs.york.ac.uk>
+* Daniel Blezek <blezek@gmail.com>
+* Daniel Pfeifer <daniel@pfeifer-mail.de>
+* Dawid Wróbel <me@dawidwrobel.com>
+* Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+* Eran Ifrah <eran.ifrah@gmail.com>
+* Esben Mose Hansen, Ange Optimization ApS
+* Geoffrey Viola <geoffrey.viola@asirobots.com>
+* Google Inc
+* Gregor Jasny
+* Helio Chissini de Castro <helio@kde.org>
+* Ilya Lavrenov <ilya.lavrenov@itseez.com>
+* Insight Software Consortium <insightsoftwareconsortium.org>
+* Intel Corporation <www.intel.com>
+* Jan Woetzel
+* Jordan Williams <jordan@jwillikers.com>
+* Julien Schueller
+* Kelly Thompson <kgt@lanl.gov>
+* Konstantin Podsvirov <konstantin@podsvirov.pro>
+* Laurent Montel <montel@kde.org>
+* Mario Bensi <mbensi@ipsquad.net>
+* Martin Gräßlin <mgraesslin@kde.org>
+* Mathieu Malaterre <mathieu.malaterre@gmail.com>
+* Matthaeus G. Chajdas
+* Matthias Kretz <kretz@kde.org>
+* Matthias Maennich <matthias@maennich.net>
+* Michael Hirsch, Ph.D. <www.scivision.co>
+* Michael Stürmer
+* Miguel A. Figueroa-Villanueva
+* Mike Durso <rbprogrammer@gmail.com>
+* Mike Jackson
+* Mike McQuaid <mike@mikemcquaid.com>
+* Nicolas Bock <nicolasbock@gmail.com>
+* Nicolas Despres <nicolas.despres@gmail.com>
+* Nikita Krupen'ko <krnekit@gmail.com>
+* NVIDIA Corporation <www.nvidia.com>
+* OpenGamma Ltd. <opengamma.com>
+* Patrick Stotko <stotko@cs.uni-bonn.de>
+* Per Øyvind Karlsen <peroyvind@mandriva.org>
+* Peter Collingbourne <peter@pcc.me.uk>
+* Petr Gotthard <gotthard@honeywell.com>
+* Philip Lowman <philip@yhbt.com>
+* Philippe Proulx <pproulx@efficios.com>
+* Raffi Enficiaud, Max Planck Society
+* Raumfeld <raumfeld.com>
+* Roger Leigh <rleigh@codelibre.net>
+* Rolf Eike Beer <eike@sf-mail.de>
+* Roman Donchenko <roman.donchenko@itseez.com>
+* Roman Kharitonov <roman.kharitonov@itseez.com>
+* Ruslan Baratov
+* Sebastian Holtermann <sebholt@xwmw.org>
+* Stephen Kelly <steveire@gmail.com>
+* Sylvain Joubert <joubert.sy@gmail.com>
+* The Qt Company Ltd.
+* Thomas Sondergaard <ts@medical-insight.com>
+* Tobias Hunger <tobias.hunger@qt.io>
+* Todd Gamblin <tgamblin@llnl.gov>
+* Tristan Carel
+* University of Dundee
+* Vadim Zhukov
+* Will Dicharry <wdicharry@stellarscience.com>
+
+See version control history for details of individual contributions.
+
+The above copyright and license notice applies to distributions of
+CMake in source and binary form. Third-party software packages supplied
+with CMake under compatible licenses provide their own copyright notices
+documented in corresponding subdirectories or source files.
+
+------------------------------------------------------------------------------
+
+CMake was initially developed by Kitware with the following sponsorship:
+
+ * National Library of Medicine at the National Institutes of Health
+ as part of the Insight Segmentation and Registration Toolkit (ITK).
+
+ * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
+ Visualization Initiative.
+
+ * National Alliance for Medical Image Computing (NAMIC) is funded by the
+ National Institutes of Health through the NIH Roadmap for Medical Research,
+ Grant U54 EB005149.
+
+ * Kitware, Inc.
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md b/src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md
new file mode 100644
index 0000000000..92a4d869a6
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/README.md
@@ -0,0 +1,4 @@
+Files taken from the CMake repository https://gitlab.kitware.com/cmake/cmake.git
+
+624461526f4707a2406ebbd40245a605b6bd41fa (tag: v3.26.3)
+
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx
new file mode 100644
index 0000000000..e3ffd23cc4
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.cxx
@@ -0,0 +1,241 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#define cmListFileCache_cxx
+#include "cmListFileCache.h"
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "cmListFileLexer.h"
+
+struct cmListFileParser
+{
+ cmListFileParser(cmListFile* lf, std::string &error);
+ ~cmListFileParser();
+ cmListFileParser(const cmListFileParser&) = delete;
+ cmListFileParser& operator=(const cmListFileParser&) = delete;
+ void IssueError(std::string const& text) const;
+ bool ParseFile(const char* filename);
+ bool ParseString(const std::string &str, const std::string &virtual_filename);
+ bool Parse();
+ bool ParseFunction(const char* name, long line);
+ bool AddArgument(cmListFileLexer_Token* token,
+ cmListFileArgument::Delimiter delim);
+ cmListFile* ListFile;
+ cmListFileLexer* Lexer;
+ std::string FunctionName;
+ long FunctionLine;
+ long FunctionLineEnd;
+ std::vector<cmListFileArgument> FunctionArguments;
+ std::string &Error;
+ enum
+ {
+ SeparationOkay,
+ SeparationWarning,
+ SeparationError
+ } Separation;
+};
+
+cmListFileParser::cmListFileParser(cmListFile *lf, std::string &error)
+ : ListFile(lf)
+ , Lexer(cmListFileLexer_New())
+ , Error(error)
+{
+}
+
+cmListFileParser::~cmListFileParser()
+{
+ cmListFileLexer_Delete(this->Lexer);
+}
+
+void cmListFileParser::IssueError(const std::string& text) const
+{
+ Error += text;
+ Error += "\n";
+}
+
+bool cmListFileParser::ParseString(const std::string &str,
+ const std::string &/*virtual_filename*/)
+{
+ if (!cmListFileLexer_SetString(this->Lexer, str.c_str(), (int)str.size())) {
+ this->IssueError("cmListFileCache: cannot allocate buffer.");
+ return false;
+ }
+
+ return this->Parse();
+}
+
+bool cmListFileParser::Parse()
+{
+ // Use a simple recursive-descent parser to process the token
+ // stream.
+ bool haveNewline = true;
+ while (cmListFileLexer_Token* token = cmListFileLexer_Scan(this->Lexer)) {
+ if (token->type == cmListFileLexer_Token_Space) {
+ } else if (token->type == cmListFileLexer_Token_Newline) {
+ haveNewline = true;
+ } else if (token->type == cmListFileLexer_Token_CommentBracket
+ || token->type == cmListFileLexer_Token_CommentLine) {
+ haveNewline = false;
+ } else if (token->type == cmListFileLexer_Token_Identifier) {
+ if (haveNewline) {
+ haveNewline = false;
+ if (this->ParseFunction(token->text, token->line)) {
+ this->ListFile->Functions.emplace_back(
+ std::move(this->FunctionName), this->FunctionLine,
+ this->FunctionLineEnd, std::move(this->FunctionArguments));
+ } else {
+ return false;
+ }
+ } else {
+ std::ostringstream error;
+ error << "Parse error. Expected a newline, got "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+ } else {
+ std::ostringstream error;
+ error << "Parse error. Expected a command name, got "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool cmListFile::ParseString(const std::string &str, const std::string& virtual_filename, std::string &error)
+{
+ bool parseError = false;
+
+ {
+ cmListFileParser parser(this, error);
+ parseError = !parser.ParseString(str, virtual_filename);
+ }
+
+ return !parseError;
+}
+
+bool cmListFileParser::ParseFunction(const char* name, long line)
+{
+ // Ininitialize a new function call.
+ this->FunctionName = name;
+ this->FunctionLine = line;
+
+ // Command name has already been parsed. Read the left paren.
+ cmListFileLexer_Token* token;
+ while ((token = cmListFileLexer_Scan(this->Lexer)) &&
+ token->type == cmListFileLexer_Token_Space) {
+ }
+ if (!token) {
+ std::ostringstream error;
+ /* clang-format off */
+ error << "Unexpected end of file.\n"
+ << "Parse error. Function missing opening \"(\".";
+ /* clang-format on */
+ this->IssueError(error.str());
+ return false;
+ }
+ if (token->type != cmListFileLexer_Token_ParenLeft) {
+ std::ostringstream error;
+ error << "Parse error. Expected \"(\", got "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+
+ // Arguments.
+ unsigned long parenDepth = 0;
+ this->Separation = SeparationOkay;
+ while ((token = cmListFileLexer_Scan(this->Lexer))) {
+ if (token->type == cmListFileLexer_Token_Space ||
+ token->type == cmListFileLexer_Token_Newline) {
+ this->Separation = SeparationOkay;
+ continue;
+ }
+ if (token->type == cmListFileLexer_Token_ParenLeft) {
+ parenDepth++;
+ this->Separation = SeparationOkay;
+ if (!this->AddArgument(token, cmListFileArgument::Unquoted)) {
+ return false;
+ }
+ } else if (token->type == cmListFileLexer_Token_ParenRight) {
+ if (parenDepth == 0) {
+ this->FunctionLineEnd = token->line;
+ return true;
+ }
+ parenDepth--;
+ this->Separation = SeparationOkay;
+ if (!this->AddArgument(token, cmListFileArgument::Unquoted)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_Identifier ||
+ token->type == cmListFileLexer_Token_ArgumentUnquoted) {
+ if (!this->AddArgument(token, cmListFileArgument::Unquoted)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_ArgumentQuoted) {
+ if (!this->AddArgument(token, cmListFileArgument::Quoted)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_CommentLine) {
+ if (!this->AddArgument(token, cmListFileArgument::Comment)) {
+ return false;
+ }
+ this->Separation = SeparationWarning;
+ } else if (token->type == cmListFileLexer_Token_ArgumentBracket) {
+ if (!this->AddArgument(token, cmListFileArgument::Bracket)) {
+ return false;
+ }
+ this->Separation = SeparationError;
+ } else if (token->type == cmListFileLexer_Token_CommentBracket) {
+ this->Separation = SeparationError;
+ } else {
+ // Error.
+ std::ostringstream error;
+ error << "Parse error. Function missing ending \")\". "
+ << "Instead found "
+ << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
+ << " with text \"" << token->text << "\".";
+ this->IssueError(error.str());
+ return false;
+ }
+ }
+
+ std::ostringstream error;
+ error << "Parse error. Function missing ending \")\". "
+ << "End of file reached.";
+ IssueError(error.str());
+ return false;
+}
+
+bool cmListFileParser::AddArgument(cmListFileLexer_Token* token,
+ cmListFileArgument::Delimiter delim)
+{
+ this->FunctionArguments.emplace_back(token->text, delim, token->line, token->column);
+ if (this->Separation == SeparationOkay) {
+ return true;
+ }
+ bool isError = (this->Separation == SeparationError ||
+ delim == cmListFileArgument::Bracket);
+ std::ostringstream m;
+
+ m << "Syntax " << (isError ? "Error" : "Warning") << " in cmake code at "
+ << "column " << token->column << "\n"
+ << "Argument not separated from preceding token by whitespace.";
+ /* clang-format on */
+ if (isError) {
+ IssueError(m.str());
+ return false;
+ }
+ return true;
+}
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h
new file mode 100644
index 0000000000..caffba4b85
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileCache.h
@@ -0,0 +1,115 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <algorithm>
+#include <iosfwd>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+/** \class cmListFileCache
+ * \brief A class to cache list file contents.
+ *
+ * cmListFileCache is a class used to cache the contents of parsed
+ * cmake list files.
+ */
+
+struct cmListFileArgument
+{
+ enum Delimiter
+ {
+ Unquoted,
+ Quoted,
+ Bracket,
+ Comment
+ };
+ cmListFileArgument() = default;
+ cmListFileArgument(std::string v, Delimiter d, long line, long column)
+ : Value(std::move(v))
+ , Delim(d)
+ , Line(line)
+ , Column(column)
+ {
+ }
+ bool operator==(const cmListFileArgument& r) const
+ {
+ return (this->Value == r.Value) && (this->Delim == r.Delim);
+ }
+ bool operator!=(const cmListFileArgument& r) const { return !(*this == r); }
+ std::string Value;
+ Delimiter Delim = Unquoted;
+ long Line = 0;
+ long Column = 0;
+};
+
+class cmListFileFunction
+{
+public:
+ cmListFileFunction(std::string name, long line, long lineEnd,
+ std::vector<cmListFileArgument> args)
+ : Impl{ std::make_shared<Implementation>(std::move(name), line, lineEnd,
+ std::move(args)) }
+ {
+ }
+
+ std::string const& OriginalName() const noexcept
+ {
+ return this->Impl->OriginalName;
+ }
+
+ std::string const& LowerCaseName() const noexcept
+ {
+ return this->Impl->LowerCaseName;
+ }
+
+ long Line() const noexcept { return this->Impl->Line; }
+ long LineEnd() const noexcept { return this->Impl->LineEnd; }
+
+ std::vector<cmListFileArgument> const& Arguments() const noexcept
+ {
+ return this->Impl->Arguments;
+ }
+
+private:
+ struct Implementation
+ {
+ Implementation(std::string name, long line, long lineEnd,
+ std::vector<cmListFileArgument> args)
+ : OriginalName{ std::move(name) }
+ , LowerCaseName{ tolower(this->OriginalName) }
+ , Line{ line }
+ , LineEnd{ lineEnd }
+ , Arguments{ std::move(args) }
+ {
+ }
+
+ // taken from yaml-cpp
+ static bool IsLower(char ch) { return 'a' <= ch && ch <= 'z'; }
+ static bool IsUpper(char ch) { return 'A' <= ch && ch <= 'Z'; }
+ static char ToLower(char ch) { return IsUpper(ch) ? ch + 'a' - 'A' : ch; }
+
+ std::string tolower(const std::string& str)
+ {
+ std::string s(str);
+ std::transform(s.begin(), s.end(), s.begin(), ToLower);
+ return s;
+ }
+
+ std::string OriginalName;
+ std::string LowerCaseName;
+ long Line = 0;
+ long LineEnd = 0;
+ std::vector<cmListFileArgument> Arguments;
+ };
+
+ std::shared_ptr<Implementation const> Impl;
+};
+
+struct cmListFile
+{
+ bool ParseString(const std::string &str, const std::string &virtual_filename, std::string &error);
+
+ std::vector<cmListFileFunction> Functions;
+};
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.c b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.c
new file mode 100644
index 0000000000..fae62886a7
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.c
@@ -0,0 +1,2835 @@
+#include "cmStandardLexer.h"
+
+#define FLEXINT_H 1
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner created by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yy_create_buffer
+#define cmListFileLexer_yy_create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer cmListFileLexer_yy_create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define cmListFileLexer_yy_delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer cmListFileLexer_yy_delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define cmListFileLexer_yy_scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer cmListFileLexer_yy_scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define cmListFileLexer_yy_scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string cmListFileLexer_yy_scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define cmListFileLexer_yy_scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes cmListFileLexer_yy_scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define cmListFileLexer_yy_init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer cmListFileLexer_yy_init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define cmListFileLexer_yy_flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer cmListFileLexer_yy_flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define cmListFileLexer_yy_load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state cmListFileLexer_yy_load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define cmListFileLexer_yy_switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer cmListFileLexer_yy_switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define cmListFileLexer_yypush_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state cmListFileLexer_yypush_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define cmListFileLexer_yypop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state cmListFileLexer_yypop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define cmListFileLexer_yyensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack cmListFileLexer_yyensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define cmListFileLexer_yylex_ALREADY_DEFINED
+#else
+#define yylex cmListFileLexer_yylex
+#endif
+
+#ifdef yyrestart
+#define cmListFileLexer_yyrestart_ALREADY_DEFINED
+#else
+#define yyrestart cmListFileLexer_yyrestart
+#endif
+
+#ifdef yylex_init
+#define cmListFileLexer_yylex_init_ALREADY_DEFINED
+#else
+#define yylex_init cmListFileLexer_yylex_init
+#endif
+
+#ifdef yylex_init_extra
+#define cmListFileLexer_yylex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra cmListFileLexer_yylex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define cmListFileLexer_yylex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy cmListFileLexer_yylex_destroy
+#endif
+
+#ifdef yyget_debug
+#define cmListFileLexer_yyget_debug_ALREADY_DEFINED
+#else
+#define yyget_debug cmListFileLexer_yyget_debug
+#endif
+
+#ifdef yyset_debug
+#define cmListFileLexer_yyset_debug_ALREADY_DEFINED
+#else
+#define yyset_debug cmListFileLexer_yyset_debug
+#endif
+
+#ifdef yyget_extra
+#define cmListFileLexer_yyget_extra_ALREADY_DEFINED
+#else
+#define yyget_extra cmListFileLexer_yyget_extra
+#endif
+
+#ifdef yyset_extra
+#define cmListFileLexer_yyset_extra_ALREADY_DEFINED
+#else
+#define yyset_extra cmListFileLexer_yyset_extra
+#endif
+
+#ifdef yyget_in
+#define cmListFileLexer_yyget_in_ALREADY_DEFINED
+#else
+#define yyget_in cmListFileLexer_yyget_in
+#endif
+
+#ifdef yyset_in
+#define cmListFileLexer_yyset_in_ALREADY_DEFINED
+#else
+#define yyset_in cmListFileLexer_yyset_in
+#endif
+
+#ifdef yyget_out
+#define cmListFileLexer_yyget_out_ALREADY_DEFINED
+#else
+#define yyget_out cmListFileLexer_yyget_out
+#endif
+
+#ifdef yyset_out
+#define cmListFileLexer_yyset_out_ALREADY_DEFINED
+#else
+#define yyset_out cmListFileLexer_yyset_out
+#endif
+
+#ifdef yyget_leng
+#define cmListFileLexer_yyget_leng_ALREADY_DEFINED
+#else
+#define yyget_leng cmListFileLexer_yyget_leng
+#endif
+
+#ifdef yyget_text
+#define cmListFileLexer_yyget_text_ALREADY_DEFINED
+#else
+#define yyget_text cmListFileLexer_yyget_text
+#endif
+
+#ifdef yyget_lineno
+#define cmListFileLexer_yyget_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno cmListFileLexer_yyget_lineno
+#endif
+
+#ifdef yyset_lineno
+#define cmListFileLexer_yyset_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno cmListFileLexer_yyset_lineno
+#endif
+
+#ifdef yyget_column
+#define cmListFileLexer_yyget_column_ALREADY_DEFINED
+#else
+#define yyget_column cmListFileLexer_yyget_column
+#endif
+
+#ifdef yyset_column
+#define cmListFileLexer_yyset_column_ALREADY_DEFINED
+#else
+#define yyset_column cmListFileLexer_yyset_column
+#endif
+
+#ifdef yywrap
+#define cmListFileLexer_yywrap_ALREADY_DEFINED
+#else
+#define yywrap cmListFileLexer_yywrap
+#endif
+
+#ifdef yyalloc
+#define cmListFileLexer_yyalloc_ALREADY_DEFINED
+#else
+#define yyalloc cmListFileLexer_yyalloc
+#endif
+
+#ifdef yyrealloc
+#define cmListFileLexer_yyrealloc_ALREADY_DEFINED
+#else
+#define yyrealloc cmListFileLexer_yyrealloc
+#endif
+
+#ifdef yyfree
+#define cmListFileLexer_yyfree_ALREADY_DEFINED
+#else
+#define yyfree cmListFileLexer_yyfree
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an
+ * integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin , yyscanner )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE yylex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-yylineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ int yyl;\
+ for ( yyl = n; yyl < yyleng; ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+ #define YY_LINENO_REWIND_TO(dst) \
+ do {\
+ const char *p;\
+ for ( p = yy_cp-1; p >= (dst); --p)\
+ if ( *p == '\n' )\
+ --yylineno;\
+ }while(0)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+static void yyensure_buffer_stack ( yyscan_t yyscanner );
+static void yy_load_buffer_state ( yyscan_t yyscanner );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define cmListFileLexer_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+typedef flex_uint8_t YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state ( yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner);
+static int yy_get_next_buffer ( yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+#define YY_NUM_RULES 24
+#define YY_END_OF_BUFFER 25
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static const flex_int16_t yy_accept[79] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4,
+ 25, 13, 22, 1, 16, 3, 13, 5, 6, 7,
+ 15, 23, 23, 17, 19, 20, 21, 24, 10, 11,
+ 8, 12, 9, 4, 13, 0, 13, 0, 22, 0,
+ 0, 7, 13, 0, 13, 0, 2, 0, 13, 17,
+ 0, 18, 10, 8, 4, 0, 14, 0, 0, 0,
+ 0, 14, 0, 0, 14, 0, 0, 0, 2, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ } ;
+
+static const YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 5, 6, 7, 1, 1, 1, 8,
+ 9, 1, 1, 1, 1, 1, 1, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 1, 1, 1,
+ 11, 1, 1, 1, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 13, 14, 15, 1, 12, 1, 12, 12, 12, 12,
+
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static const YY_CHAR yy_meta[17] =
+ { 0,
+ 1, 1, 2, 3, 4, 3, 1, 3, 5, 6,
+ 1, 6, 1, 1, 7, 2
+ } ;
+
+static const flex_int16_t yy_base[97] =
+ { 0,
+ 0, 0, 14, 28, 42, 56, 70, 84, 18, 19,
+ 68, 100, 16, 298, 298, 54, 58, 298, 298, 13,
+ 115, 0, 298, 51, 298, 298, 21, 298, 0, 298,
+ 53, 298, 298, 0, 0, 126, 55, 0, 25, 25,
+ 53, 0, 0, 136, 53, 0, 57, 0, 0, 42,
+ 50, 298, 0, 43, 0, 146, 160, 45, 172, 43,
+ 26, 0, 42, 177, 0, 42, 188, 40, 298, 40,
+ 0, 38, 37, 34, 32, 31, 23, 298, 197, 204,
+ 211, 218, 225, 232, 239, 245, 252, 259, 262, 268,
+ 275, 278, 280, 286, 289, 291
+
+ } ;
+
+static const flex_int16_t yy_def[97] =
+ { 0,
+ 78, 1, 79, 79, 80, 80, 81, 81, 82, 82,
+ 78, 78, 78, 78, 78, 78, 12, 78, 78, 12,
+ 78, 83, 78, 84, 78, 78, 84, 78, 85, 78,
+ 78, 78, 78, 86, 12, 87, 12, 88, 78, 78,
+ 89, 20, 12, 90, 12, 21, 78, 91, 12, 84,
+ 84, 78, 85, 78, 86, 87, 78, 56, 87, 92,
+ 78, 57, 89, 90, 57, 64, 90, 93, 78, 57,
+ 94, 95, 92, 96, 93, 95, 96, 0, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78
+
+ } ;
+
+static const flex_int16_t yy_nxt[315] =
+ { 0,
+ 12, 13, 14, 13, 15, 16, 17, 18, 19, 12,
+ 12, 20, 21, 22, 12, 23, 25, 39, 26, 39,
+ 14, 14, 42, 52, 42, 50, 39, 27, 39, 28,
+ 25, 64, 26, 28, 28, 61, 61, 47, 47, 56,
+ 65, 27, 64, 28, 30, 57, 56, 60, 65, 74,
+ 62, 57, 72, 54, 50, 51, 31, 28, 30, 69,
+ 68, 62, 60, 54, 51, 41, 40, 78, 78, 78,
+ 31, 28, 30, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 33, 28, 30, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78, 33, 28,
+
+ 35, 78, 78, 78, 36, 78, 37, 78, 78, 35,
+ 35, 35, 35, 38, 35, 43, 78, 78, 78, 44,
+ 78, 45, 78, 78, 43, 46, 43, 47, 48, 43,
+ 57, 78, 58, 78, 78, 78, 78, 78, 78, 59,
+ 65, 78, 66, 78, 78, 78, 78, 78, 78, 67,
+ 57, 78, 58, 78, 78, 78, 78, 78, 78, 59,
+ 57, 78, 78, 78, 36, 78, 70, 78, 78, 57,
+ 57, 57, 57, 71, 57, 56, 78, 56, 78, 56,
+ 56, 65, 78, 66, 78, 78, 78, 78, 78, 78,
+ 67, 64, 78, 64, 78, 64, 64, 24, 24, 24,
+
+ 24, 24, 24, 24, 29, 29, 29, 29, 29, 29,
+ 29, 32, 32, 32, 32, 32, 32, 32, 34, 34,
+ 34, 34, 34, 34, 34, 49, 78, 49, 49, 49,
+ 49, 49, 50, 78, 50, 78, 50, 50, 50, 53,
+ 78, 53, 53, 53, 53, 55, 78, 55, 55, 55,
+ 55, 55, 56, 78, 78, 56, 78, 56, 56, 35,
+ 78, 35, 35, 35, 35, 35, 63, 63, 64, 78,
+ 78, 64, 78, 64, 64, 43, 78, 43, 43, 43,
+ 43, 43, 73, 73, 75, 75, 57, 78, 57, 57,
+ 57, 57, 57, 76, 76, 77, 77, 11, 78, 78,
+
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78
+ } ;
+
+static const flex_int16_t yy_chk[315] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 13, 3, 13,
+ 9, 10, 20, 27, 20, 27, 39, 3, 39, 3,
+ 4, 77, 4, 9, 10, 40, 61, 40, 61, 76,
+ 75, 4, 74, 4, 5, 73, 72, 70, 68, 66,
+ 63, 60, 58, 54, 51, 50, 5, 5, 6, 47,
+ 45, 41, 37, 31, 24, 17, 16, 11, 0, 0,
+ 6, 6, 7, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 8, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 8,
+
+ 12, 0, 0, 0, 12, 0, 12, 0, 0, 12,
+ 12, 12, 12, 12, 12, 21, 0, 0, 0, 21,
+ 0, 21, 0, 0, 21, 21, 21, 21, 21, 21,
+ 36, 0, 36, 0, 0, 0, 0, 0, 0, 36,
+ 44, 0, 44, 0, 0, 0, 0, 0, 0, 44,
+ 56, 0, 56, 0, 0, 0, 0, 0, 0, 56,
+ 57, 0, 0, 0, 57, 0, 57, 0, 0, 57,
+ 57, 57, 57, 57, 57, 59, 0, 59, 0, 59,
+ 59, 64, 0, 64, 0, 0, 0, 0, 0, 0,
+ 64, 67, 0, 67, 0, 67, 67, 79, 79, 79,
+
+ 79, 79, 79, 79, 80, 80, 80, 80, 80, 80,
+ 80, 81, 81, 81, 81, 81, 81, 81, 82, 82,
+ 82, 82, 82, 82, 82, 83, 0, 83, 83, 83,
+ 83, 83, 84, 0, 84, 0, 84, 84, 84, 85,
+ 0, 85, 85, 85, 85, 86, 0, 86, 86, 86,
+ 86, 86, 87, 0, 0, 87, 0, 87, 87, 88,
+ 0, 88, 88, 88, 88, 88, 89, 89, 90, 0,
+ 0, 90, 0, 90, 90, 91, 0, 91, 91, 91,
+ 91, 91, 92, 92, 93, 93, 94, 0, 94, 94,
+ 94, 94, 94, 95, 95, 96, 96, 78, 78, 78,
+
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static const flex_int32_t yy_rule_can_match_eol[25] =
+ { 0,
+1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, };
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+/*
+
+This file must be translated to C and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+ flex --nounistd -DFLEXINT_H --noline -ocmListFileLexer.c cmListFileLexer.in.l
+
+Modify cmListFileLexer.c:
+ - remove trailing whitespace: sed -i 's/\s*$//' cmListFileLexer.c
+ - remove blank lines at end of file: sed -i '${/^$/d;}' cmListFileLexer.c
+ - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmListFileLexer.c
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+/* Setup the proper cmListFileLexer_yylex declaration. */
+#define YY_EXTRA_TYPE cmListFileLexer*
+#define YY_DECL int cmListFileLexer_yylex (yyscan_t yyscanner, cmListFileLexer* lexer)
+
+#include "cmListFileLexer.h"
+
+/*--------------------------------------------------------------------------*/
+struct cmListFileLexer_s
+{
+ cmListFileLexer_Token token;
+ int bracket;
+ int comment;
+ int line;
+ int column;
+ int size;
+ FILE* file;
+ size_t cr;
+ char* string_buffer;
+ char* string_position;
+ int string_left;
+ yyscan_t scanner;
+};
+
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length);
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length);
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize);
+static void cmListFileLexerInit(cmListFileLexer* lexer);
+static void cmListFileLexerDestroy(cmListFileLexer* lexer);
+
+/* Replace the lexer input function. */
+#undef YY_INPUT
+#define YY_INPUT(buf, result, max_size) \
+ do { result = cmListFileLexerInput(cmListFileLexer_yyget_extra(yyscanner), buf, max_size); } while (0)
+
+/*--------------------------------------------------------------------------*/
+
+#define INITIAL 0
+#define STRING 1
+#define BRACKET 2
+#define BRACKETEND 3
+#define COMMENT 4
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals ( yyscan_t yyscanner );
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
+
+ int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+
+ static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner);
+
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput ( yyscan_t yyscanner );
+#else
+static int input ( yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ yy_state_type yy_current_state;
+ char *yy_cp, *yy_bp;
+ int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+ }
+
+ yy_load_buffer_state( yyscanner );
+ }
+
+ {
+
+ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 79 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 298 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ int yyl;
+ for ( yyl = 0; yyl < yyleng; ++yyl )
+ if ( yytext[yyl] == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_Newline;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+ const char* bracket = yytext;
+ lexer->comment = yytext[0] == '#';
+ if (lexer->comment) {
+ lexer->token.type = cmListFileLexer_Token_CommentBracket;
+ bracket += 1;
+ } else {
+ lexer->token.type = cmListFileLexer_Token_ArgumentBracket;
+ }
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->bracket = strchr(bracket+1, '[') - bracket;
+ if (yytext[yyleng-1] == '\n') {
+ ++lexer->line;
+ lexer->column = 1;
+ } else {
+ lexer->column += yyleng;
+ }
+ BEGIN(BRACKET);
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+ BEGIN(COMMENT);
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_CommentLine;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ParenLeft;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ParenRight;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_Identifier;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+{
+ /* Handle ]]====]=======]*/
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ if (yyleng == lexer->bracket) {
+ BEGIN(BRACKETEND);
+ }
+}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+ /* Erase the partial bracket from the token. */
+ lexer->token.length -= lexer->bracket;
+ lexer->token.text[lexer->token.length] = 0;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case 11:
+/* rule 11 can match eol */
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(BRACKET);
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ BEGIN(BRACKET);
+}
+ YY_BREAK
+case YY_STATE_EOF(BRACKET):
+case YY_STATE_EOF(BRACKETEND):
+{
+ lexer->token.type = cmListFileLexer_Token_BadBracket;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_ArgumentQuoted;
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->column += yyleng;
+ BEGIN(STRING);
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case 18:
+/* rule 18 can match eol */
+YY_RULE_SETUP
+{
+ /* Continuation: text is not part of string */
+ ++lexer->line;
+ lexer->column = 1;
+}
+ YY_BREAK
+case 19:
+/* rule 19 can match eol */
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+{
+ lexer->column += yyleng;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+{
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+ lexer->token.type = cmListFileLexer_Token_BadString;
+ BEGIN(INITIAL);
+ return 1;
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_Space;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+{
+ lexer->token.type = cmListFileLexer_Token_BadCharacter;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMENT):
+{
+ lexer->token.type = cmListFileLexer_Token_None;
+ cmListFileLexerSetToken(lexer, 0, 0);
+ return 0;
+}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+ECHO;
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap( yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ char *source = yyg->yytext_ptr;
+ int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1);
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc( (void *) b->yy_ch_buf,
+ (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = NULL;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin , yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ /* "- 2" to take care of EOB's */
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ yy_state_type yy_current_state;
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 16);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 79 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ char *yy_cp = yyg->yy_c_buf_p;
+
+ YY_CHAR yy_c = 16;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 79 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ yy_is_jam = (yy_current_state == 78);
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+
+ static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
+{
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ int number_to_move = yyg->yy_n_chars + 2;
+ char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ if ( c == '\n' ){
+ --yylineno;
+ }
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin , yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( yyscanner ) )
+ return 0;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ if ( c == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+ }
+
+ yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner);
+ yy_load_buffer_state( yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file , yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree( (void *) b->yy_ch_buf , yyscanner );
+
+ yyfree( (void *) b , yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_flush_buffer( b , yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return NULL;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b , yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner)
+{
+
+ return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = (yy_size_t) (_yybytes_len + 2);
+ buf = (char *) yyalloc( n , yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n , yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param _line_number line number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int _line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+ yylineno = _line_number;
+}
+
+/** Set the current column.
+ * @param _column_no column number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int _column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+ yycolumn = _column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * _in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = _in_str ;
+}
+
+void yyset_out (FILE * _out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = _out_str ;
+}
+
+int yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void yyset_debug (int _bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = _bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+int yylex_init(yyscan_t* ptr_yy_globals)
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals )
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = NULL;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = NULL;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = NULL;
+ yyout = NULL;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(yyg->yy_buffer_stack , yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ yyfree( yyg->yy_start_stack , yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s , yyscan_t yyscanner)
+{
+ int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ return malloc(size);
+}
+
+void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return realloc(ptr, size);
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ /* Set the token line and column number. */
+ lexer->token.line = lexer->line;
+ lexer->token.column = lexer->column;
+
+ /* Use the same buffer if possible. */
+ if (lexer->token.text) {
+ if (text && length < lexer->size) {
+ strcpy(lexer->token.text, text);
+ lexer->token.length = length;
+ return;
+ }
+ free(lexer->token.text);
+ lexer->token.text = 0;
+ lexer->size = 0;
+ }
+
+ /* Need to extend the buffer. */
+ if (text) {
+ lexer->token.text = strdup(text);
+ lexer->token.length = length;
+ lexer->size = length + 1;
+ } else {
+ lexer->token.length = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ char* temp;
+ int newSize;
+
+ /* If the appended text will fit in the buffer, do not reallocate. */
+ newSize = lexer->token.length + length + 1;
+ if (lexer->token.text && newSize <= lexer->size) {
+ strcpy(lexer->token.text + lexer->token.length, text);
+ lexer->token.length += length;
+ return;
+ }
+
+ /* We need to extend the buffer. */
+ temp = malloc(newSize);
+ if (lexer->token.text) {
+ memcpy(temp, lexer->token.text, lexer->token.length);
+ free(lexer->token.text);
+ }
+ memcpy(temp + lexer->token.length, text, length);
+ temp[lexer->token.length + length] = 0;
+ lexer->token.text = temp;
+ lexer->token.length += length;
+ lexer->size = newSize;
+}
+
+/*--------------------------------------------------------------------------*/
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize)
+{
+ if (lexer) {
+ if (lexer->file) {
+ /* Convert CRLF -> LF explicitly. The C FILE "t"ext mode
+ does not convert newlines on all platforms. Move any
+ trailing CR to the start of the buffer for the next read. */
+ size_t cr = lexer->cr;
+ size_t n;
+ buffer[0] = '\r';
+ n = fread(buffer + cr, 1, bufferSize - cr, lexer->file);
+ if (n) {
+ char* o = buffer;
+ const char* i = buffer;
+ const char* e;
+ n += cr;
+ cr = (buffer[n - 1] == '\r') ? 1 : 0;
+ e = buffer + n - cr;
+ while (i != e) {
+ if (i[0] == '\r' && i[1] == '\n') {
+ ++i;
+ }
+ *o++ = *i++;
+ }
+ n = o - buffer;
+ } else {
+ n = cr;
+ cr = 0;
+ }
+ lexer->cr = cr;
+ return n;
+ } else if (lexer->string_left) {
+ int length = lexer->string_left;
+ if ((int)bufferSize < length) {
+ length = (int)bufferSize;
+ }
+ memcpy(buffer, lexer->string_position, length);
+ lexer->string_position += length;
+ lexer->string_left -= length;
+ return length;
+ }
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerInit(cmListFileLexer* lexer)
+{
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_init(&lexer->scanner);
+ cmListFileLexer_yyset_extra(lexer, lexer->scanner);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerDestroy(cmListFileLexer* lexer)
+{
+ cmListFileLexerSetToken(lexer, 0, 0);
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_destroy(lexer->scanner);
+ if (lexer->file) {
+ fclose(lexer->file);
+ lexer->file = 0;
+ }
+ if (lexer->string_buffer) {
+ lexer->string_buffer = 0;
+ lexer->string_left = 0;
+ lexer->string_position = 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer* cmListFileLexer_New(void)
+{
+ cmListFileLexer* lexer = (cmListFileLexer*)malloc(sizeof(cmListFileLexer));
+ if (!lexer) {
+ return 0;
+ }
+ memset(lexer, 0, sizeof(*lexer));
+ lexer->line = 1;
+ lexer->column = 1;
+ return lexer;
+}
+
+/*--------------------------------------------------------------------------*/
+void cmListFileLexer_Delete(cmListFileLexer* lexer)
+{
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ free(lexer);
+}
+
+/*--------------------------------------------------------------------------*/
+static cmListFileLexer_BOM cmListFileLexer_ReadBOM(FILE* f)
+{
+ unsigned char b[2];
+ if (fread(b, 1, 2, f) == 2) {
+ if (b[0] == 0xEF && b[1] == 0xBB) {
+ if (fread(b, 1, 1, f) == 1 && b[0] == 0xBF) {
+ return cmListFileLexer_BOM_UTF8;
+ }
+ } else if (b[0] == 0xFE && b[1] == 0xFF) {
+ /* UTF-16 BE */
+ return cmListFileLexer_BOM_UTF16BE;
+ } else if (b[0] == 0 && b[1] == 0) {
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0xFE && b[1] == 0xFF) {
+ return cmListFileLexer_BOM_UTF32BE;
+ }
+ } else if (b[0] == 0xFF && b[1] == 0xFE) {
+ fpos_t p;
+ fgetpos(f, &p);
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0 && b[1] == 0) {
+ return cmListFileLexer_BOM_UTF32LE;
+ }
+ if (fsetpos(f, &p) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_UTF16LE;
+ }
+ }
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_None;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetFileName(cmListFileLexer* lexer, const char* name,
+ cmListFileLexer_BOM* bom)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (name) {
+ lexer->file = fopen(name, "rb");
+ if (lexer->file) {
+ if (bom) {
+ *bom = cmListFileLexer_ReadBOM(lexer->file);
+ }
+ } else {
+ result = 0;
+ }
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetString(cmListFileLexer* lexer, const char* text, int length)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (text) {
+ lexer->string_buffer = (char *) text;
+ lexer->string_position = lexer->string_buffer;
+ lexer->string_left = length;
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer)
+{
+ if (!lexer->file && !lexer->string_buffer) {
+ return 0;
+ }
+ if (cmListFileLexer_yylex(lexer->scanner, lexer)) {
+ return &lexer->token;
+ } else {
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentLine(cmListFileLexer* lexer)
+{
+ return lexer->line;
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentColumn(cmListFileLexer* lexer)
+{
+ return lexer->column;
+}
+
+/*--------------------------------------------------------------------------*/
+const char* cmListFileLexer_GetTypeAsString(cmListFileLexer* lexer,
+ cmListFileLexer_Type type)
+{
+ (void)lexer;
+ switch (type) {
+ case cmListFileLexer_Token_None:
+ return "nothing";
+ case cmListFileLexer_Token_Space:
+ return "space";
+ case cmListFileLexer_Token_Newline:
+ return "newline";
+ case cmListFileLexer_Token_Identifier:
+ return "identifier";
+ case cmListFileLexer_Token_ParenLeft:
+ return "left paren";
+ case cmListFileLexer_Token_ParenRight:
+ return "right paren";
+ case cmListFileLexer_Token_ArgumentUnquoted:
+ return "unquoted argument";
+ case cmListFileLexer_Token_ArgumentQuoted:
+ return "quoted argument";
+ case cmListFileLexer_Token_ArgumentBracket:
+ return "bracket argument";
+ case cmListFileLexer_Token_CommentBracket:
+ return "bracket comment";
+ case cmListFileLexer_Token_BadCharacter:
+ return "bad character";
+ case cmListFileLexer_Token_BadBracket:
+ return "unterminated bracket";
+ case cmListFileLexer_Token_BadString:
+ return "unterminated string";
+ case cmListFileLexer_Token_CommentLine:
+ return "line comment";
+ }
+ return "unknown token";
+}
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h
new file mode 100644
index 0000000000..199530f16c
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.h
@@ -0,0 +1,69 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef enum cmListFileLexer_Type_e
+{
+ cmListFileLexer_Token_None,
+ cmListFileLexer_Token_Space,
+ cmListFileLexer_Token_Newline,
+ cmListFileLexer_Token_Identifier,
+ cmListFileLexer_Token_ParenLeft,
+ cmListFileLexer_Token_ParenRight,
+ cmListFileLexer_Token_ArgumentUnquoted,
+ cmListFileLexer_Token_ArgumentQuoted,
+ cmListFileLexer_Token_ArgumentBracket,
+ cmListFileLexer_Token_CommentBracket,
+ cmListFileLexer_Token_BadCharacter,
+ cmListFileLexer_Token_BadBracket,
+ cmListFileLexer_Token_BadString,
+ cmListFileLexer_Token_CommentLine
+} cmListFileLexer_Type;
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef struct cmListFileLexer_Token_s cmListFileLexer_Token;
+struct cmListFileLexer_Token_s
+{
+ cmListFileLexer_Type type;
+ char* text;
+ int length;
+ int line;
+ int column;
+};
+
+enum cmListFileLexer_BOM_e
+{
+ cmListFileLexer_BOM_None,
+ cmListFileLexer_BOM_Broken,
+ cmListFileLexer_BOM_UTF8,
+ cmListFileLexer_BOM_UTF16BE,
+ cmListFileLexer_BOM_UTF16LE,
+ cmListFileLexer_BOM_UTF32BE,
+ cmListFileLexer_BOM_UTF32LE
+};
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef enum cmListFileLexer_BOM_e cmListFileLexer_BOM;
+
+/* NOLINTNEXTLINE(modernize-use-using) */
+typedef struct cmListFileLexer_s cmListFileLexer;
+
+cmListFileLexer* cmListFileLexer_New(void);
+int cmListFileLexer_SetFileName(cmListFileLexer*, const char*,
+ cmListFileLexer_BOM* bom);
+int cmListFileLexer_SetString(cmListFileLexer*, const char*, int length);
+cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer*);
+long cmListFileLexer_GetCurrentLine(cmListFileLexer*);
+long cmListFileLexer_GetCurrentColumn(cmListFileLexer*);
+const char* cmListFileLexer_GetTypeAsString(cmListFileLexer*,
+ cmListFileLexer_Type);
+void cmListFileLexer_Delete(cmListFileLexer*);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l
new file mode 100644
index 0000000000..2cf9f8cf2c
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmListFileLexer.in.l
@@ -0,0 +1,548 @@
+%{
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+/*
+
+This file must be translated to C and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+ flex --nounistd -DFLEXINT_H --noline -ocmListFileLexer.c cmListFileLexer.in.l
+
+Modify cmListFileLexer.c:
+ - remove trailing whitespace: sed -i 's/\s*$//' cmListFileLexer.c
+ - remove blank lines at end of file: sed -i '${/^$/d;}' cmListFileLexer.c
+ - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmListFileLexer.c
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+/* Setup the proper cmListFileLexer_yylex declaration. */
+#define YY_EXTRA_TYPE cmListFileLexer*
+#define YY_DECL int cmListFileLexer_yylex (yyscan_t yyscanner, cmListFileLexer* lexer)
+
+#include "cmListFileLexer.h"
+
+/*--------------------------------------------------------------------------*/
+struct cmListFileLexer_s
+{
+ cmListFileLexer_Token token;
+ int bracket;
+ int comment;
+ int line;
+ int column;
+ int size;
+ FILE* file;
+ size_t cr;
+ char* string_buffer;
+ char* string_position;
+ int string_left;
+ yyscan_t scanner;
+};
+
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length);
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length);
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize);
+static void cmListFileLexerInit(cmListFileLexer* lexer);
+static void cmListFileLexerDestroy(cmListFileLexer* lexer);
+
+/* Replace the lexer input function. */
+#undef YY_INPUT
+#define YY_INPUT(buf, result, max_size) \
+ do { result = cmListFileLexerInput(cmListFileLexer_yyget_extra(yyscanner), buf, max_size); } while (0)
+
+/*--------------------------------------------------------------------------*/
+%}
+
+%option prefix="cmListFileLexer_yy"
+
+%option reentrant
+%option yylineno
+%option noyywrap
+%pointer
+%x STRING
+%x BRACKET
+%x BRACKETEND
+%x COMMENT
+
+MAKEVAR \$\([A-Za-z0-9_]*\)
+UNQUOTED ([^ \0\t\r\n\(\)#\\\"[=]|\\[^\0\n])
+LEGACY {MAKEVAR}|{UNQUOTED}|\"({MAKEVAR}|{UNQUOTED}|[ \t[=])*\"
+
+%%
+
+<INITIAL,COMMENT>\n {
+ lexer->token.type = cmListFileLexer_Token_Newline;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+#?\[=*\[\n? {
+ const char* bracket = yytext;
+ lexer->comment = yytext[0] == '#';
+ if (lexer->comment) {
+ lexer->token.type = cmListFileLexer_Token_CommentBracket;
+ bracket += 1;
+ } else {
+ lexer->token.type = cmListFileLexer_Token_ArgumentBracket;
+ }
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->bracket = strchr(bracket+1, '[') - bracket;
+ if (yytext[yyleng-1] == '\n') {
+ ++lexer->line;
+ lexer->column = 1;
+ } else {
+ lexer->column += yyleng;
+ }
+ BEGIN(BRACKET);
+}
+
+# {
+ lexer->column += yyleng;
+ BEGIN(COMMENT);
+}
+
+<COMMENT>[^\0\n]* {
+ lexer->token.type = cmListFileLexer_Token_CommentLine;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\( {
+ lexer->token.type = cmListFileLexer_Token_ParenLeft;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\) {
+ lexer->token.type = cmListFileLexer_Token_ParenRight;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+[A-Za-z_][A-Za-z0-9_]* {
+ lexer->token.type = cmListFileLexer_Token_Identifier;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+<BRACKET>\]=* {
+ /* Handle ]]====]=======]*/
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ if (yyleng == lexer->bracket) {
+ BEGIN(BRACKETEND);
+ }
+}
+
+<BRACKETEND>\] {
+ lexer->column += yyleng;
+ /* Erase the partial bracket from the token. */
+ lexer->token.length -= lexer->bracket;
+ lexer->token.text[lexer->token.length] = 0;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+<BRACKET>([^]\0\n])+ {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+
+<BRACKET,BRACKETEND>\n {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+ BEGIN(BRACKET);
+}
+
+<BRACKET,BRACKETEND>[^\0\n] {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ BEGIN(BRACKET);
+}
+
+<BRACKET,BRACKETEND><<EOF>> {
+ lexer->token.type = cmListFileLexer_Token_BadBracket;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+({UNQUOTED}|=|\[=*{UNQUOTED})({UNQUOTED}|[[=])* {
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+({MAKEVAR}|{UNQUOTED}|=|\[=*{LEGACY})({LEGACY}|[[=])* {
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\[ {
+ lexer->token.type = cmListFileLexer_Token_ArgumentUnquoted;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+\" {
+ lexer->token.type = cmListFileLexer_Token_ArgumentQuoted;
+ cmListFileLexerSetToken(lexer, "", 0);
+ lexer->column += yyleng;
+ BEGIN(STRING);
+}
+
+<STRING>([^\\\0\n\"]|\\[^\0\n])+ {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+
+<STRING>\\\n {
+ /* Continuation: text is not part of string */
+ ++lexer->line;
+ lexer->column = 1;
+}
+
+<STRING>\n {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ ++lexer->line;
+ lexer->column = 1;
+}
+
+<STRING>\" {
+ lexer->column += yyleng;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+<STRING>[^\0\n] {
+ cmListFileLexerAppend(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+}
+
+<STRING><<EOF>> {
+ lexer->token.type = cmListFileLexer_Token_BadString;
+ BEGIN(INITIAL);
+ return 1;
+}
+
+[ \t\r]+ {
+ lexer->token.type = cmListFileLexer_Token_Space;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+. {
+ lexer->token.type = cmListFileLexer_Token_BadCharacter;
+ cmListFileLexerSetToken(lexer, yytext, yyleng);
+ lexer->column += yyleng;
+ return 1;
+}
+
+<<EOF>> {
+ lexer->token.type = cmListFileLexer_Token_None;
+ cmListFileLexerSetToken(lexer, 0, 0);
+ return 0;
+}
+
+%%
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerSetToken(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ /* Set the token line and column number. */
+ lexer->token.line = lexer->line;
+ lexer->token.column = lexer->column;
+
+ /* Use the same buffer if possible. */
+ if (lexer->token.text) {
+ if (text && length < lexer->size) {
+ strcpy(lexer->token.text, text);
+ lexer->token.length = length;
+ return;
+ }
+ free(lexer->token.text);
+ lexer->token.text = 0;
+ lexer->size = 0;
+ }
+
+ /* Need to extend the buffer. */
+ if (text) {
+ lexer->token.text = strdup(text);
+ lexer->token.length = length;
+ lexer->size = length + 1;
+ } else {
+ lexer->token.length = 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerAppend(cmListFileLexer* lexer, const char* text,
+ int length)
+{
+ char* temp;
+ int newSize;
+
+ /* If the appended text will fit in the buffer, do not reallocate. */
+ newSize = lexer->token.length + length + 1;
+ if (lexer->token.text && newSize <= lexer->size) {
+ strcpy(lexer->token.text + lexer->token.length, text);
+ lexer->token.length += length;
+ return;
+ }
+
+ /* We need to extend the buffer. */
+ temp = malloc(newSize);
+ if (lexer->token.text) {
+ memcpy(temp, lexer->token.text, lexer->token.length);
+ free(lexer->token.text);
+ }
+ memcpy(temp + lexer->token.length, text, length);
+ temp[lexer->token.length + length] = 0;
+ lexer->token.text = temp;
+ lexer->token.length += length;
+ lexer->size = newSize;
+}
+
+/*--------------------------------------------------------------------------*/
+static int cmListFileLexerInput(cmListFileLexer* lexer, char* buffer,
+ size_t bufferSize)
+{
+ if (lexer) {
+ if (lexer->file) {
+ /* Convert CRLF -> LF explicitly. The C FILE "t"ext mode
+ does not convert newlines on all platforms. Move any
+ trailing CR to the start of the buffer for the next read. */
+ size_t cr = lexer->cr;
+ size_t n;
+ buffer[0] = '\r';
+ n = fread(buffer + cr, 1, bufferSize - cr, lexer->file);
+ if (n) {
+ char* o = buffer;
+ const char* i = buffer;
+ const char* e;
+ n += cr;
+ cr = (buffer[n - 1] == '\r') ? 1 : 0;
+ e = buffer + n - cr;
+ while (i != e) {
+ if (i[0] == '\r' && i[1] == '\n') {
+ ++i;
+ }
+ *o++ = *i++;
+ }
+ n = o - buffer;
+ } else {
+ n = cr;
+ cr = 0;
+ }
+ lexer->cr = cr;
+ return n;
+ } else if (lexer->string_left) {
+ int length = lexer->string_left;
+ if ((int)bufferSize < length) {
+ length = (int)bufferSize;
+ }
+ memcpy(buffer, lexer->string_position, length);
+ lexer->string_position += length;
+ lexer->string_left -= length;
+ return length;
+ }
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerInit(cmListFileLexer* lexer)
+{
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_init(&lexer->scanner);
+ cmListFileLexer_yyset_extra(lexer, lexer->scanner);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static void cmListFileLexerDestroy(cmListFileLexer* lexer)
+{
+ cmListFileLexerSetToken(lexer, 0, 0);
+ if (lexer->file || lexer->string_buffer) {
+ cmListFileLexer_yylex_destroy(lexer->scanner);
+ if (lexer->file) {
+ fclose(lexer->file);
+ lexer->file = 0;
+ }
+ if (lexer->string_buffer) {
+ lexer->string_buffer = 0;
+ lexer->string_left = 0;
+ lexer->string_position = 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer* cmListFileLexer_New(void)
+{
+ cmListFileLexer* lexer = (cmListFileLexer*)malloc(sizeof(cmListFileLexer));
+ if (!lexer) {
+ return 0;
+ }
+ memset(lexer, 0, sizeof(*lexer));
+ lexer->line = 1;
+ lexer->column = 1;
+ return lexer;
+}
+
+/*--------------------------------------------------------------------------*/
+void cmListFileLexer_Delete(cmListFileLexer* lexer)
+{
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ free(lexer);
+}
+
+/*--------------------------------------------------------------------------*/
+static cmListFileLexer_BOM cmListFileLexer_ReadBOM(FILE* f)
+{
+ unsigned char b[2];
+ if (fread(b, 1, 2, f) == 2) {
+ if (b[0] == 0xEF && b[1] == 0xBB) {
+ if (fread(b, 1, 1, f) == 1 && b[0] == 0xBF) {
+ return cmListFileLexer_BOM_UTF8;
+ }
+ } else if (b[0] == 0xFE && b[1] == 0xFF) {
+ /* UTF-16 BE */
+ return cmListFileLexer_BOM_UTF16BE;
+ } else if (b[0] == 0 && b[1] == 0) {
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0xFE && b[1] == 0xFF) {
+ return cmListFileLexer_BOM_UTF32BE;
+ }
+ } else if (b[0] == 0xFF && b[1] == 0xFE) {
+ fpos_t p;
+ fgetpos(f, &p);
+ if (fread(b, 1, 2, f) == 2 && b[0] == 0 && b[1] == 0) {
+ return cmListFileLexer_BOM_UTF32LE;
+ }
+ if (fsetpos(f, &p) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_UTF16LE;
+ }
+ }
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ return cmListFileLexer_BOM_Broken;
+ }
+ return cmListFileLexer_BOM_None;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetFileName(cmListFileLexer* lexer, const char* name,
+ cmListFileLexer_BOM* bom)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (name) {
+ lexer->file = fopen(name, "rb");
+ if (lexer->file) {
+ if (bom) {
+ *bom = cmListFileLexer_ReadBOM(lexer->file);
+ }
+ } else {
+ result = 0;
+ }
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+int cmListFileLexer_SetString(cmListFileLexer* lexer, const char* text, int length)
+{
+ int result = 1;
+ cmListFileLexerDestroy(lexer);
+ if (text) {
+ lexer->string_buffer = (char *) text;
+ lexer->string_position = lexer->string_buffer;
+ lexer->string_left = length;
+ }
+ cmListFileLexerInit(lexer);
+ return result;
+}
+
+/*--------------------------------------------------------------------------*/
+cmListFileLexer_Token* cmListFileLexer_Scan(cmListFileLexer* lexer)
+{
+ if (!lexer->file && !lexer->string_buffer) {
+ return 0;
+ }
+ if (cmListFileLexer_yylex(lexer->scanner, lexer)) {
+ return &lexer->token;
+ } else {
+ cmListFileLexer_SetFileName(lexer, 0, 0);
+ return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentLine(cmListFileLexer* lexer)
+{
+ return lexer->line;
+}
+
+/*--------------------------------------------------------------------------*/
+long cmListFileLexer_GetCurrentColumn(cmListFileLexer* lexer)
+{
+ return lexer->column;
+}
+
+/*--------------------------------------------------------------------------*/
+const char* cmListFileLexer_GetTypeAsString(cmListFileLexer* lexer,
+ cmListFileLexer_Type type)
+{
+ (void)lexer;
+ switch (type) {
+ case cmListFileLexer_Token_None:
+ return "nothing";
+ case cmListFileLexer_Token_Space:
+ return "space";
+ case cmListFileLexer_Token_Newline:
+ return "newline";
+ case cmListFileLexer_Token_Identifier:
+ return "identifier";
+ case cmListFileLexer_Token_ParenLeft:
+ return "left paren";
+ case cmListFileLexer_Token_ParenRight:
+ return "right paren";
+ case cmListFileLexer_Token_ArgumentUnquoted:
+ return "unquoted argument";
+ case cmListFileLexer_Token_ArgumentQuoted:
+ return "quoted argument";
+ case cmListFileLexer_Token_ArgumentBracket:
+ return "bracket argument";
+ case cmListFileLexer_Token_CommentBracket:
+ return "bracket comment";
+ case cmListFileLexer_Token_BadCharacter:
+ return "bad character";
+ case cmListFileLexer_Token_BadBracket:
+ return "unterminated bracket";
+ case cmListFileLexer_Token_BadString:
+ return "unterminated string";
+ case cmListFileLexer_Token_CommentLine:
+ return "line comment";
+ }
+ return "unknown token";
+}
diff --git a/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h
new file mode 100644
index 0000000000..0808861342
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/3rdparty/cmake/cmStandardLexer.h
@@ -0,0 +1,88 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#if defined(__linux)
+/* Needed for glibc < 2.12 */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _XOPEN_SOURCE 600
+#endif
+#if !defined(_POSIX_C_SOURCE) && !defined(_WIN32) && !defined(__sun) && \
+ !defined(__OpenBSD__)
+/* POSIX APIs are needed */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _POSIX_C_SOURCE 200809L
+#endif
+#if defined(__sun) && defined(__GNUC__) && !defined(__cplusplus)
+/* C sources: for fileno and strdup */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _XOPEN_SOURCE 600
+#endif
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+/* For isascii */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+# define _XOPEN_SOURCE 700
+#endif
+
+/* Disable some warnings. */
+#if defined(_MSC_VER)
+# pragma warning(disable : 4018)
+# pragma warning(disable : 4127)
+# pragma warning(disable : 4131)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4251)
+# pragma warning(disable : 4267)
+# pragma warning(disable : 4305)
+# pragma warning(disable : 4309)
+# pragma warning(disable : 4706)
+# pragma warning(disable : 4786)
+# pragma warning(disable : 4996)
+#endif
+
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
+# pragma GCC diagnostic ignored "-Wconversion"
+# pragma GCC diagnostic ignored "-Wsign-compare"
+# endif
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 403
+# pragma GCC diagnostic ignored "-Wsign-conversion"
+# endif
+#endif
+
+#if defined(__LCC__)
+# pragma diag_suppress 1873 /* comparison between signed and unsigned */
+#endif
+
+#if defined(__NVCOMPILER)
+# pragma diag_suppress 111 /* statement is unreachable */
+# pragma diag_suppress 550 /* variable set but never used */
+#endif
+
+/* Make sure isatty is available. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# include <io.h>
+# if defined(_MSC_VER)
+# define isatty _isatty
+# endif
+#else
+# include <unistd.h> // IWYU pragma: export
+#endif
+
+/* Make sure malloc and free are available on QNX. */
+#ifdef __QNX__
+# include <malloc.h>
+#endif
+
+/* Disable features we do not need. */
+#define YY_NEVER_INTERACTIVE 1
+#define YY_NO_INPUT 1
+#define YY_NO_UNPUT 1
+#define ECHO
+
+#include <stdint.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt
index 683a2a407a..3d9dc4849e 100644
--- a/src/plugins/cmakeprojectmanager/CMakeLists.txt
+++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt
@@ -2,6 +2,7 @@ add_qtc_plugin(CMakeProjectManager
PLUGIN_CLASS CMakeProjectPlugin
DEPENDS QmlJS
PLUGIN_DEPENDS Core CppEditor ProjectExplorer TextEditor QtSupport
+ INCLUDES 3dparty/cmake
SOURCES
builddirparameters.cpp builddirparameters.h
cmake_global.h
@@ -15,8 +16,6 @@ add_qtc_plugin(CMakeProjectManager
cmakeeditor.cpp cmakeeditor.h
cmakefilecompletionassist.cpp cmakefilecompletionassist.h
cmakeformatter.cpp cmakeformatter.h
- cmakeformatteroptionspage.cpp cmakeformatteroptionspage.h
- cmakeformattersettings.cpp cmakeformattersettings.h
cmakeindenter.cpp cmakeindenter.h
cmakeinstallstep.cpp cmakeinstallstep.h
cmakekitinformation.cpp cmakekitinformation.h
@@ -44,4 +43,7 @@ add_qtc_plugin(CMakeProjectManager
presetsparser.cpp presetsparser.h
presetsmacros.cpp presetsmacros.h
projecttreehelper.cpp projecttreehelper.h
+ 3rdparty/cmake/cmListFileCache.cxx
+ 3rdparty/cmake/cmListFileLexer.c
+ 3rdparty/cmake/cmListFileCache.h
)
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
index 1ae95c1807..c13cf8709c 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
@@ -41,7 +41,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorertr.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
@@ -335,8 +335,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
Column {
Form {
- buildDirAspect,
- bc->aspect<BuildTypeAspect>(),
+ buildDirAspect, br,
+ bc->aspect<BuildTypeAspect>(), br,
qmlDebugAspect
},
m_warningMessageLabel,
@@ -347,19 +347,21 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
Column {
cmakeConfiguration,
Row {
- bc->aspect<InitialCMakeArgumentsAspect>(),
+ bc->aspect<InitialCMakeArgumentsAspect>(), br,
bc->aspect<AdditionalCMakeOptionsAspect>()
},
m_reconfigureButton,
}
},
configureEnvironmentAspectWidget
- }
- }.attachTo(details, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(details);
Column {
m_configureDetailsWidget,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
updateAdvancedCheckBox();
setError(m_buildSystem->error());
@@ -588,23 +590,18 @@ void CMakeBuildSettingsWidget::batchEditConfiguration()
void CMakeBuildSettingsWidget::reconfigureWithInitialParameters()
{
auto settings = CMakeSpecificSettings::instance();
- bool doNotAsk = !settings->askBeforeReConfigureInitialParams.value();
- if (!doNotAsk) {
- QDialogButtonBox::StandardButton reply = CheckableMessageBox::question(
- Core::ICore::dialogParent(),
- Tr::tr("Re-configure with Initial Parameters"),
- Tr::tr("Clear CMake configuration and configure with initial parameters?"),
- Tr::tr("Do not ask again"),
- &doNotAsk,
- QDialogButtonBox::Yes | QDialogButtonBox::No,
- QDialogButtonBox::Yes);
-
- settings->askBeforeReConfigureInitialParams.setValue(!doNotAsk);
- settings->writeSettings(Core::ICore::settings());
-
- if (reply != QDialogButtonBox::Yes) {
- return;
- }
+ QMessageBox::StandardButton reply = CheckableMessageBox::question(
+ Core::ICore::dialogParent(),
+ Tr::tr("Re-configure with Initial Parameters"),
+ Tr::tr("Clear CMake configuration and configure with initial parameters?"),
+ settings->askBeforeReConfigureInitialParams,
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::Yes);
+
+ settings->writeSettings(Core::ICore::settings());
+
+ if (reply != QMessageBox::Yes) {
+ return;
}
m_buildSystem->clearCMakeCache();
@@ -643,7 +640,7 @@ void CMakeBuildSettingsWidget::updateInitialCMakeArguments()
// As the user would expect to have e.g. "--preset" from "Initial Configuration"
// to "Current Configuration" as additional parameters
m_buildSystem->setAdditionalCMakeArguments(ProcessArgs::splitArgs(
- bc->aspect<InitialCMakeArgumentsAspect>()->value()));
+ bc->aspect<InitialCMakeArgumentsAspect>()->value(), HostOsInfo::hostOs()));
}
void CMakeBuildSettingsWidget::kitCMakeConfiguration()
@@ -663,17 +660,19 @@ void CMakeBuildSettingsWidget::kitCMakeConfiguration()
CMakeGeneratorKitAspect generatorAspect;
CMakeConfigurationKitAspect configurationKitAspect;
- auto layout = new QGridLayout(dialog);
-
+ Layouting::Grid grid;
KitAspectWidget *widget = kitAspect.createConfigWidget(m_buildSystem->kit());
widget->setParent(dialog);
- widget->addToLayoutWithLabel(layout->parentWidget());
+ widget->addToLayoutWithLabel(grid, dialog);
widget = generatorAspect.createConfigWidget(m_buildSystem->kit());
widget->setParent(dialog);
- widget->addToLayoutWithLabel(layout->parentWidget());
+ widget->addToLayoutWithLabel(grid, dialog);
widget = configurationKitAspect.createConfigWidget(m_buildSystem->kit());
widget->setParent(dialog);
- widget->addToLayoutWithLabel(layout->parentWidget());
+ widget->addToLayoutWithLabel(grid, dialog);
+ grid.attachTo(dialog);
+
+ auto layout = qobject_cast<QGridLayout *>(dialog->layout());
layout->setColumnStretch(1, 1);
@@ -701,7 +700,7 @@ void CMakeBuildSettingsWidget::updateConfigureDetailsWidgetsSummary(
const FilePath buildDirectory = bc ? bc->buildDirectory() : ".";
cmd.addArgs({"-S", m_buildSystem->projectDirectory().path()});
- cmd.addArgs({"-B", buildDirectory.onDevice(cmd.executable()).path()});
+ cmd.addArgs({"-B", buildDirectory.path()});
cmd.addArgs(configurationArguments);
params.setCommandLine(cmd);
@@ -826,9 +825,10 @@ void CMakeBuildSettingsWidget::updateFromKit()
// Then the additional parameters
const QStringList additionalKitCMake = ProcessArgs::splitArgs(
- CMakeConfigurationKitAspect::additionalConfiguration(k));
+ CMakeConfigurationKitAspect::additionalConfiguration(k), HostOsInfo::hostOs());
const QStringList additionalInitialCMake = ProcessArgs::splitArgs(
- m_buildSystem->buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->value());
+ m_buildSystem->buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->value(),
+ HostOsInfo::hostOs());
QStringList mergedArgumentList;
std::set_union(additionalInitialCMake.begin(),
@@ -1137,7 +1137,7 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildT
// Package manager auto setup
if (Internal::CMakeSpecificSettings::instance()->packageManagerAutoSetup.value()) {
cmd.addArg(QString("-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH="
- "%{buildDir}/%1/auto-setup.cmake")
+ "%{BuildConfig:BuildDirectory:NativeFilePath}/%1/auto-setup.cmake")
.arg(Constants::PACKAGE_MANAGER_DIR));
}
@@ -1177,12 +1177,6 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
initialArguments.removeIf(
[presetArgument](const QString &item) { return item == presetArgument; });
- // Remove the -DQTC_KIT_DEFAULT_CONFIG_HASH argument
- const QString presetHashArgument
- = CMakeConfigurationKitAspect::kitDefaultConfigHashItem(k).toArgument();
- initialArguments.removeIf(
- [presetHashArgument](const QString &item) { return item == presetHashArgument; });
-
PresetsDetails::ConfigurePreset configurePreset
= Utils::findOrDefault(project->presetsData().configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
@@ -1743,7 +1737,8 @@ void CMakeBuildSystem::setInitialCMakeArguments(const QStringList &args)
QStringList CMakeBuildSystem::additionalCMakeArguments() const
{
- return ProcessArgs::splitArgs(buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value());
+ return ProcessArgs::splitArgs(buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value(),
+ HostOsInfo::hostOs());
}
void CMakeBuildSystem::setAdditionalCMakeArguments(const QStringList &args)
@@ -1766,7 +1761,8 @@ void CMakeBuildSystem::filterConfigArgumentsFromAdditionalCMakeArguments()
// which is already part of the CMake variables and should not be also
// in the addtional CMake options
const QStringList arguments = ProcessArgs::splitArgs(
- buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value());
+ buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value(),
+ HostOsInfo::hostOs());
QStringList unknownOptions;
const CMakeConfig config = CMakeConfig::fromArguments(arguments, unknownOptions);
@@ -2162,7 +2158,7 @@ const QStringList InitialCMakeArgumentsAspect::allValues() const
return ci.toArgument(nullptr);
});
- initialCMakeArguments.append(ProcessArgs::splitArgs(value()));
+ initialCMakeArguments.append(ProcessArgs::splitArgs(value(), HostOsInfo::hostOs()));
return initialCMakeArguments;
}
@@ -2281,6 +2277,7 @@ public:
ConfigureEnvironmentAspect::ConfigureEnvironmentAspect(ProjectExplorer::Target *target)
{
setIsLocal(true);
+ setAllowPrintOnRun(false);
setConfigWidgetCreator(
[this, target] { return new ConfigureEnvironmentAspectWidget(this, target); });
addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {});
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
index ccb21ccfb3..d4e50a0c27 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp
@@ -31,6 +31,7 @@
#include <utils/layoutbuilder.h>
#include <QListWidget>
+#include <QRandomGenerator>
#include <QRegularExpression>
#include <QTreeView>
#include <QCheckBox>
@@ -44,6 +45,8 @@ namespace CMakeProjectManager::Internal {
const char BUILD_TARGETS_KEY[] = "CMakeProjectManager.MakeStep.BuildTargets";
const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.CMakeArguments";
const char TOOL_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.AdditionalArguments";
+const char USE_STAGING_KEY[] = "CMakeProjectManager.MakeStep.UseStaging";
+const char STAGING_DIR_KEY[] = "CMakeProjectManager.MakeStep.StagingDir";
const char IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY[] =
"CMakeProjectManager.MakeStep.iOSAutomaticProvisioningUpdates";
const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearSystemEnvironment";
@@ -156,7 +159,25 @@ Qt::ItemFlags CMakeTargetItem::flags(int) const
// CMakeBuildStep
-CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
+static QString initialStagingDir()
+{
+ // Avoid actual file accesses.
+ auto rg = QRandomGenerator::global();
+ const qulonglong rand = rg->generate64();
+ char buf[sizeof(rand)];
+ memcpy(&buf, &rand, sizeof(rand));
+ const QByteArray ba = QByteArray(buf, sizeof(buf)).toHex();
+ return QString::fromUtf8("/tmp/Qt-Creator-staging-" + ba);
+}
+
+static bool buildAndRunOnSameDevice(Kit *kit)
+{
+ IDeviceConstPtr runDevice = DeviceKitAspect::device(kit);
+ IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit);
+ return runDevice->id() == buildDevice->id();
+}
+
+CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) :
CMakeAbstractProcessStep(bsl, id)
{
m_cmakeArguments = addAspect<StringAspect>();
@@ -169,6 +190,17 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
m_toolArguments->setLabelText(Tr::tr("Tool arguments:"));
m_toolArguments->setDisplayStyle(StringAspect::LineEditDisplay);
+ m_useStaging = addAspect<BoolAspect>();
+ m_useStaging->setSettingsKey(USE_STAGING_KEY);
+ m_useStaging->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
+ m_useStaging->setDefaultValue(!buildAndRunOnSameDevice(kit()));
+
+ m_stagingDir = addAspect<StringAspect>();
+ m_stagingDir->setSettingsKey(STAGING_DIR_KEY);
+ m_stagingDir->setLabelText(Tr::tr("Staging directory:"));
+ m_stagingDir->setDisplayStyle(StringAspect::PathChooserDisplay);
+ m_stagingDir->setDefaultValue(initialStagingDir());
+
Kit *kit = buildConfiguration()->kit();
if (CMakeBuildConfiguration::isIos(kit)) {
m_useiOSAutomaticProvisioningUpdates = addAspect<BoolAspect>();
@@ -199,6 +231,9 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
if (!env.expandedValueForKey("NINJA_STATUS").startsWith(ninjaProgressString))
env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] ");
env.modify(m_userEnvironmentChanges);
+
+ if (m_useStaging)
+ env.set("DESTDIR", currentStagingDir());
});
connect(target(), &Target::parsingFinished, this, [this](bool success) {
@@ -210,7 +245,6 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
this, &CMakeBuildStep::updateBuildTargetsModel);
}
-
QVariantMap CMakeBuildStep::toMap() const
{
QVariantMap map(CMakeAbstractProcessStep::toMap());
@@ -375,15 +409,13 @@ void CMakeBuildStep::setBuildTargets(const QStringList &buildTargets)
CommandLine CMakeBuildStep::cmakeCommand() const
{
- CommandLine cmd;
- if (CMakeTool *tool = CMakeKitAspect::cmakeTool(kit()))
- cmd.setExecutable(tool->cmakeExecutable());
+ CommandLine cmd{cmakeExecutable()};
FilePath buildDirectory = ".";
if (buildConfiguration())
buildDirectory = buildConfiguration()->buildDirectory();
- cmd.addArgs({"--build", buildDirectory.onDevice(cmd.executable()).path()});
+ cmd.addArgs({"--build", buildDirectory.path()});
cmd.addArg("--target");
cmd.addArgs(Utils::transform(m_buildTargets, [this](const QString &s) {
@@ -406,6 +438,9 @@ CommandLine CMakeBuildStep::cmakeCommand() const
if (!m_cmakeArguments->value().isEmpty())
cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw);
+ if (m_useStaging->value())
+ cmd.addArg("install");
+
bool toolArgumentsSpecified = false;
if (!m_toolArguments->value().isEmpty()) {
cmd.addArg("--");
@@ -466,6 +501,12 @@ QWidget *CMakeBuildStep::createConfigWidget()
QString summaryText = param.summary(displayName());
+ m_stagingDir->setEnabled(m_useStaging->value());
+ if (m_useStaging->value()) {
+ summaryText.append(" " + Tr::tr("and stage at %2 for %3")
+ .arg(currentStagingDir(), currentInstallPrefix()));
+ }
+
if (!m_buildPreset.isEmpty()) {
const CMakeProject *cp = static_cast<const CMakeProject *>(project());
@@ -519,28 +560,32 @@ QWidget *CMakeBuildStep::createConfigWidget()
envWidget->setBaseEnvironmentText(baseEnvironmentText());
});
- builder.addRow(clearBox);
- builder.addRow(envWidget);
+ builder.addRow({clearBox});
+ builder.addRow({envWidget});
};
Layouting::Form builder;
- builder.addRow(m_cmakeArguments);
- builder.addRow(m_toolArguments);
+ builder.addRow({m_cmakeArguments});
+ builder.addRow({m_toolArguments});
+ builder.addRow({Tr::tr("Stage for installation:"), Layouting::Row{m_useStaging, m_stagingDir}});
if (m_useiOSAutomaticProvisioningUpdates)
- builder.addRow(m_useiOSAutomaticProvisioningUpdates);
+ builder.addRow({m_useiOSAutomaticProvisioningUpdates});
builder.addRow({new QLabel(Tr::tr("Targets:")), frame});
if (!isCleanStep() && !m_buildPreset.isEmpty())
createAndAddEnvironmentWidgets(builder);
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ builder.addItem(Layouting::noMargin);
+ auto widget = builder.emerge();
updateDetails();
connect(m_cmakeArguments, &StringAspect::changed, this, updateDetails);
connect(m_toolArguments, &StringAspect::changed, this, updateDetails);
+ connect(m_useStaging, &BoolAspect::changed, this, updateDetails);
+ connect(m_stagingDir, &StringAspect::changed, this, updateDetails);
if (m_useiOSAutomaticProvisioningUpdates)
connect(m_useiOSAutomaticProvisioningUpdates, &BoolAspect::changed, this, updateDetails);
@@ -683,8 +728,62 @@ QString CMakeBuildStep::baseEnvironmentText() const
return Tr::tr("System Environment");
}
+QString CMakeBuildStep::currentInstallPrefix() const
+{
+ auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem());
+ QTC_ASSERT(bs, return {});
+ const CMakeConfig config = bs->configurationFromCMake();
+ return QString::fromUtf8(config.valueOf("CMAKE_INSTALL_PREFIX"));
+}
+
+QString CMakeBuildStep::currentStagingDir() const
+{
+ return m_stagingDir->filePath().path();
+}
+
+FilePath CMakeBuildStep::cmakeExecutable() const
+{
+ CMakeTool *tool = CMakeKitAspect::cmakeTool(kit());
+ return tool ? tool->cmakeExecutable() : FilePath();
+}
+
+void CMakeBuildStep::updateDeploymentData()
+{
+ if (!m_useStaging->value())
+ return;
+
+ QString install = currentInstallPrefix();
+ QString stagingDir = currentStagingDir();
+ FilePath rootDir = cmakeExecutable().withNewPath(stagingDir);
+ Q_UNUSED(install);
+
+ DeploymentData deploymentData;
+ deploymentData.setLocalInstallRoot(rootDir);
+
+ const int startPos = rootDir.path().length();
+
+ const auto appFileNames = transform<QSet<QString>>(buildSystem()->applicationTargets(),
+ [](const BuildTargetInfo &appTarget) { return appTarget.targetFilePath.fileName(); });
+
+ auto handleFile = [&appFileNames, startPos, &deploymentData](const FilePath &filePath) {
+ const DeployableFile::Type type = appFileNames.contains(filePath.fileName())
+ ? DeployableFile::TypeExecutable
+ : DeployableFile::TypeNormal;
+ const QString targetDir = filePath.parentDir().path().mid(startPos);
+ deploymentData.addFile(filePath, targetDir, type);
+ return IterationPolicy::Continue;
+ };
+
+ rootDir.iterateDirectory(handleFile,
+ {{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories});
+
+ buildSystem()->setDeploymentData(deploymentData);
+}
+
void CMakeBuildStep::finish(ProcessResult result)
{
+ updateDeploymentData();
+
emit progress(100, {});
AbstractProcessStep::finish(result);
}
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h
index 96d53919e1..973b00e8f3 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h
+++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h
@@ -85,6 +85,10 @@ private:
void doRun() override;
QWidget *createConfigWidget() override;
+ Utils::FilePath cmakeExecutable() const;
+ QString currentInstallPrefix() const;
+ QString currentStagingDir() const;
+
QString defaultBuildTarget() const;
bool isCleanStep() const;
@@ -94,6 +98,7 @@ private:
void handleBuildTargetsChanges(bool success);
void recreateBuildTargetsModel();
void updateBuildTargetsModel();
+ void updateDeploymentData();
QMetaObject::Connection m_runTrigger;
@@ -102,6 +107,8 @@ private:
Utils::StringAspect *m_cmakeArguments = nullptr;
Utils::StringAspect *m_toolArguments = nullptr;
Utils::BoolAspect *m_useiOSAutomaticProvisioningUpdates = nullptr;
+ Utils::BoolAspect *m_useStaging = nullptr;
+ Utils::StringAspect *m_stagingDir = nullptr;
QString m_allTarget = "all";
QString m_installTarget = "install";
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
index 726414dafc..aaad00ed4b 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp
@@ -17,6 +17,8 @@
#include <android/androidconstants.h>
#include <coreplugin/icore.h>
+#include <coreplugin/documentmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
@@ -30,7 +32,11 @@
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
+#include <texteditor/texteditor.h>
+#include <texteditor/textdocument.h>
+
#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <qmljstools/qmljstoolsconstants.h>
#include <qtsupport/qtcppkitinfo.h>
#include <qtsupport/qtkitinformation.h>
@@ -40,8 +46,8 @@
#include <utils/checkablemessagebox.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QClipboard>
#include <QGuiApplication>
@@ -51,74 +57,11 @@
#include <QLoggingCategory>
using namespace ProjectExplorer;
+using namespace TextEditor;
using namespace Utils;
namespace CMakeProjectManager::Internal {
-static void copySourcePathsToClipboard(const FilePaths &srcPaths, const ProjectNode *node)
-{
- QClipboard *clip = QGuiApplication::clipboard();
-
- QString data = Utils::transform(srcPaths, [projDir = node->filePath()](const FilePath &path) {
- return path.relativePathFrom(projDir).cleanPath().toString();
- }).join(" ");
- clip->setText(data);
-}
-
-static void noAutoAdditionNotify(const FilePaths &filePaths, const ProjectNode *node)
-{
- const FilePaths srcPaths = Utils::filtered(filePaths, [](const FilePath &file) {
- const auto mimeType = Utils::mimeTypeForFile(file).name();
- return mimeType == CppEditor::Constants::C_SOURCE_MIMETYPE ||
- mimeType == CppEditor::Constants::C_HEADER_MIMETYPE ||
- mimeType == CppEditor::Constants::CPP_SOURCE_MIMETYPE ||
- mimeType == CppEditor::Constants::CPP_HEADER_MIMETYPE ||
- mimeType == ProjectExplorer::Constants::FORM_MIMETYPE ||
- mimeType == ProjectExplorer::Constants::RESOURCE_MIMETYPE ||
- mimeType == ProjectExplorer::Constants::SCXML_MIMETYPE;
- });
-
- if (!srcPaths.empty()) {
- auto settings = CMakeSpecificSettings::instance();
- switch (settings->afterAddFileSetting.value()) {
- case AskUser: {
- bool checkValue{false};
- QDialogButtonBox::StandardButton reply = CheckableMessageBox::question(
- Core::ICore::dialogParent(),
- Tr::tr("Copy to Clipboard?"),
- Tr::tr("Files are not automatically added to the "
- "CMakeLists.txt file of the CMake project."
- "\nCopy the path to the source files to the clipboard?"),
- "Remember My Choice",
- &checkValue,
- QDialogButtonBox::Yes | QDialogButtonBox::No,
- QDialogButtonBox::Yes);
- if (checkValue) {
- if (QDialogButtonBox::Yes == reply)
- settings->afterAddFileSetting.setValue(CopyFilePath);
- else if (QDialogButtonBox::No == reply)
- settings->afterAddFileSetting.setValue(NeverCopyFilePath);
-
- settings->writeSettings(Core::ICore::settings());
- }
-
- if (QDialogButtonBox::Yes == reply)
- copySourcePathsToClipboard(srcPaths, node);
-
- break;
- }
-
- case CopyFilePath: {
- copySourcePathsToClipboard(srcPaths, node);
- break;
- }
-
- case NeverCopyFilePath:
- break;
- }
- }
-}
-
static Q_LOGGING_CATEGORY(cmakeBuildSystemLog, "qtc.cmake.buildsystem", QtWarningMsg);
// --------------------------------------------------------------------
@@ -258,29 +201,436 @@ void CMakeBuildSystem::triggerParsing()
bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
{
if (dynamic_cast<CMakeTargetNode *>(context))
- return action == ProjectAction::AddNewFile;
-
- if (dynamic_cast<CMakeListsNode *>(context))
- return action == ProjectAction::AddNewFile;
+ return action == ProjectAction::AddNewFile || action == ProjectAction::AddExistingFile
+ || action == ProjectAction::AddExistingDirectory || action == ProjectAction::Rename
+ || action == ProjectAction::RemoveFile;
return BuildSystem::supportsAction(context, action, node);
}
-bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+static QString newFilesForFunction(const std::string &cmakeFunction,
+ const FilePaths &filePaths,
+ const FilePath &projDir)
{
- if (auto n = dynamic_cast<CMakeProjectNode *>(context)) {
- noAutoAdditionNotify(filePaths, n);
- return true; // Return always true as autoadd is not supported!
+ auto relativeFilePaths = [projDir](const FilePaths &filePaths) {
+ return Utils::transform(filePaths, [projDir](const FilePath &path) {
+ return path.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+ });
+ };
+
+ if (cmakeFunction == "qt_add_qml_module" || cmakeFunction == "qt6_add_qml_module") {
+ FilePaths sourceFiles;
+ FilePaths resourceFiles;
+ FilePaths qmlFiles;
+
+ for (const auto &file : filePaths) {
+ const auto mimeType = Utils::mimeTypeForFile(file);
+ if (mimeType.matchesName(CppEditor::Constants::CPP_SOURCE_MIMETYPE)
+ || mimeType.matchesName(CppEditor::Constants::CPP_HEADER_MIMETYPE)
+ || mimeType.matchesName(CppEditor::Constants::OBJECTIVE_C_SOURCE_MIMETYPE)
+ || mimeType.matchesName(CppEditor::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)) {
+ sourceFiles << file;
+ } else if (mimeType.matchesName(QmlJSTools::Constants::QML_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::QMLUI_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::QMLPROJECT_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::JS_MIMETYPE)
+ || mimeType.matchesName(QmlJSTools::Constants::JSON_MIMETYPE)) {
+ qmlFiles << file;
+ } else {
+ resourceFiles << file;
+ }
+ }
+
+ QStringList result;
+ if (!sourceFiles.isEmpty())
+ result << QString("SOURCES %1").arg(relativeFilePaths(sourceFiles).join(" "));
+ if (!resourceFiles.isEmpty())
+ result << QString("RESOURCES %1").arg(relativeFilePaths(resourceFiles).join(" "));
+ if (!qmlFiles.isEmpty())
+ result << QString("QML_FILES %1").arg(relativeFilePaths(qmlFiles).join(" "));
+
+ return result.join("\n");
}
+ return relativeFilePaths(filePaths).join(" ");
+}
+
+bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
+{
if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
- noAutoAdditionNotify(filePaths, n);
- return true; // Return always true as autoadd is not supported!
+ const QString targetName = n->buildKey();
+ auto target = Utils::findOrDefault(buildTargets(),
+ [targetName](const CMakeBuildTarget &target) {
+ return target.title == targetName;
+ });
+
+ if (target.backtrace.isEmpty()) {
+ *notAdded = filePaths;
+ return false;
+ }
+ const FilePath targetCMakeFile = target.backtrace.last().path;
+ const int targetDefinitionLine = target.backtrace.last().line;
+
+ // Have a fresh look at the CMake file, not relying on a cached value
+ expected_str<QByteArray> fileContent = targetCMakeFile.fileContents();
+ cmListFile cmakeListFile;
+ std::string errorString;
+ if (fileContent) {
+ fileContent = fileContent->replace("\r\n", "\n");
+ if (!cmakeListFile.ParseString(fileContent->toStdString(),
+ targetCMakeFile.fileName().toStdString(),
+ errorString)) {
+ *notAdded = filePaths;
+ return false;
+ }
+ }
+
+ auto function = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [targetDefinitionLine](const auto &func) {
+ return func.Line() == targetDefinitionLine;
+ });
+
+ if (function == cmakeListFile.Functions.end()) {
+ *notAdded = filePaths;
+ return false;
+ }
+
+ // Special case: when qt_add_executable and qt_add_qml_module use the same target name
+ // then qt_add_qml_module function should be used
+ const std::string target_name = targetName.toStdString();
+ auto add_qml_module_func
+ = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return (func.LowerCaseName() == "qt_add_qml_module"
+ || func.LowerCaseName() == "qt6_add_qml_module")
+ && func.Arguments().front().Value == target_name;
+ });
+ if (add_qml_module_func != cmakeListFile.Functions.end())
+ function = add_qml_module_func;
+
+ const QString newSourceFiles = newFilesForFunction(function->LowerCaseName(),
+ filePaths,
+ n->filePath().canonicalPath());
+
+ static QSet<std::string> knownFunctions{"add_executable",
+ "add_library",
+ "qt_add_executable",
+ "qt_add_library",
+ "qt6_add_executable",
+ "qt6_add_library",
+ "qt_add_qml_module",
+ "qt6_add_qml_module"};
+
+ int line = 0;
+ int column = 0;
+ QString snippet;
+
+ auto afterFunctionLastArgument = [&line, &column, &snippet, newSourceFiles](const auto &f) {
+ auto lastArgument = f->Arguments().back();
+
+ line = lastArgument.Line;
+ column = lastArgument.Column + static_cast<int>(lastArgument.Value.size()) - 1;
+ snippet = QString("\n%1").arg(newSourceFiles);
+ };
+
+ if (knownFunctions.contains(function->LowerCaseName())) {
+ afterFunctionLastArgument(function);
+ } else {
+ auto targetSourcesFunc = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return func.LowerCaseName()
+ == "target_sources"
+ && func.Arguments().front().Value
+ == target_name;
+ });
+
+ if (targetSourcesFunc == cmakeListFile.Functions.end()) {
+ line = function->LineEnd() + 1;
+ column = 0;
+ snippet = QString("\ntarget_sources(%1\n PRIVATE\n %2\n)\n")
+ .arg(targetName)
+ .arg(newSourceFiles);
+ } else {
+ afterFunctionLastArgument(targetSourcesFunc);
+ }
+ }
+
+ BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditorAt({targetCMakeFile, line, column},
+ Constants::CMAKE_EDITOR_ID,
+ Core::EditorManager::DoNotMakeVisible));
+ if (!editor) {
+ *notAdded = filePaths;
+ return false;
+ }
+
+ editor->insert(snippet);
+ editor->editorWidget()->autoIndent();
+ if (!Core::DocumentManager::saveDocument(editor->document()))
+ return false;
+
+ return true;
}
return BuildSystem::addFiles(context, filePaths, notAdded);
}
+std::optional<CMakeBuildSystem::ProjectFileArgumentPosition>
+CMakeBuildSystem::projectFileArgumentPosition(const QString &targetName, const QString &fileName)
+{
+ auto target = Utils::findOrDefault(buildTargets(), [targetName](const CMakeBuildTarget &target) {
+ return target.title == targetName;
+ });
+
+ if (target.backtrace.isEmpty())
+ return std::nullopt;
+
+ const FilePath targetCMakeFile = target.backtrace.last().path;
+
+ // Have a fresh look at the CMake file, not relying on a cached value
+ expected_str<QByteArray> fileContent = targetCMakeFile.fileContents();
+ cmListFile cmakeListFile;
+ std::string errorString;
+ if (fileContent) {
+ fileContent = fileContent->replace("\r\n", "\n");
+ if (!cmakeListFile.ParseString(fileContent->toStdString(),
+ targetCMakeFile.fileName().toStdString(),
+ errorString))
+ return std::nullopt;
+ }
+
+ const int targetDefinitionLine = target.backtrace.last().line;
+
+ auto function = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [targetDefinitionLine](const auto &func) {
+ return func.Line() == targetDefinitionLine;
+ });
+
+ const std::string target_name = targetName.toStdString();
+ auto targetSourcesFunc = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return func.LowerCaseName() == "target_sources"
+ && func.Arguments().size() > 1
+ && func.Arguments().front().Value
+ == target_name;
+ });
+ auto addQmlModuleFunc = std::find_if(cmakeListFile.Functions.begin(),
+ cmakeListFile.Functions.end(),
+ [target_name](const auto &func) {
+ return (func.LowerCaseName() == "qt_add_qml_module"
+ || func.LowerCaseName() == "qt6_add_qml_module")
+ && func.Arguments().size() > 1
+ && func.Arguments().front().Value
+ == target_name;
+ });
+
+ for (const auto &func : {function, targetSourcesFunc, addQmlModuleFunc}) {
+ if (func == cmakeListFile.Functions.end())
+ continue;
+ auto filePathArgument
+ = Utils::findOrDefault(func->Arguments(),
+ [file_name = fileName.toStdString()](const auto &arg) {
+ return arg.Delim != cmListFileArgument::Comment
+ && arg.Value == file_name;
+ });
+
+ if (!filePathArgument.Value.empty()) {
+ return ProjectFileArgumentPosition{filePathArgument, targetCMakeFile, fileName};
+ } else {
+ // Check if the filename is part of globbing variable result
+ const auto globFunctions = std::get<0>(
+ Utils::partition(cmakeListFile.Functions, [](const auto &f) {
+ return f.LowerCaseName() == "file" && f.Arguments().size() > 2
+ && (f.Arguments().front().Value == "GLOB"
+ || f.Arguments().front().Value == "GLOB_RECURSE");
+ }));
+
+ const auto globVariables = Utils::transform<QSet>(globFunctions, [](const auto &func) {
+ return std::string("${") + func.Arguments()[1].Value + "}";
+ });
+
+ const auto haveGlobbing = Utils::anyOf(func->Arguments(),
+ [globVariables](const auto &arg) {
+ return globVariables.contains(arg.Value)
+ && arg.Delim
+ != cmListFileArgument::Comment;
+ });
+
+ if (haveGlobbing) {
+ return ProjectFileArgumentPosition{filePathArgument,
+ targetCMakeFile,
+ fileName,
+ true};
+ }
+
+ // Check if the filename is part of a variable set by the user
+ const auto setFunctions = std::get<0>(
+ Utils::partition(cmakeListFile.Functions, [](const auto &f) {
+ return f.LowerCaseName() == "set" && f.Arguments().size() > 1;
+ }));
+
+ for (const auto &arg : func->Arguments()) {
+ if (arg.Delim == cmListFileArgument::Comment)
+ continue;
+
+ auto matchedFunctions = Utils::filtered(setFunctions, [arg](const auto &f) {
+ return arg.Value == std::string("${") + f.Arguments()[0].Value + "}";
+ });
+
+ for (const auto &f : matchedFunctions) {
+ filePathArgument
+ = Utils::findOrDefault(f.Arguments(),
+ [file_name = fileName.toStdString()](
+ const auto &arg) {
+ return arg.Delim != cmListFileArgument::Comment
+ && arg.Value == file_name;
+ });
+
+ if (!filePathArgument.Value.empty()) {
+ return ProjectFileArgumentPosition{filePathArgument,
+ targetCMakeFile,
+ fileName};
+ }
+ }
+ }
+ }
+ }
+
+ return std::nullopt;
+}
+
+RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
+ const FilePaths &filePaths,
+ FilePaths *notRemoved)
+{
+ FilePaths badFiles;
+ if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
+ const FilePath projDir = n->filePath().canonicalPath();
+ const QString targetName = n->buildKey();
+
+ for (const auto &file : filePaths) {
+ const QString fileName
+ = file.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+
+ auto filePos = projectFileArgumentPosition(targetName, fileName);
+ if (filePos) {
+ if (!filePos.value().cmakeFile.exists()) {
+ badFiles << file;
+ continue;
+ }
+
+ BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditorAt({filePos.value().cmakeFile,
+ static_cast<int>(filePos.value().argumentPosition.Line),
+ static_cast<int>(filePos.value().argumentPosition.Column
+ - 1)},
+ Constants::CMAKE_EDITOR_ID,
+ Core::EditorManager::DoNotMakeVisible));
+ if (!editor) {
+ badFiles << file;
+ continue;
+ }
+
+ if (!filePos.value().fromGlobbing)
+ editor->replace(filePos.value().relativeFileName.length(), "");
+
+ editor->editorWidget()->autoIndent();
+ if (!Core::DocumentManager::saveDocument(editor->document())) {
+ badFiles << file;
+ continue;
+ }
+ } else {
+ badFiles << file;
+ }
+ }
+
+ if (notRemoved && !badFiles.isEmpty())
+ *notRemoved = badFiles;
+
+ return badFiles.isEmpty() ? RemovedFilesFromProject::Ok : RemovedFilesFromProject::Error;
+ }
+
+ return RemovedFilesFromProject::Error;
+}
+
+bool CMakeBuildSystem::canRenameFile(Node *context,
+ const FilePath &oldFilePath,
+ const FilePath &newFilePath)
+{
+ // "canRenameFile" will cause an actual rename after the function call.
+ // This will make the a sequence like
+ // canonicalPath().relativePathFrom(projDir).cleanPath().toString()
+ // to fail if the file doesn't exist on disk
+ // therefore cache the results for the subsequent "renameFile" call
+ // where oldFilePath has already been renamed as newFilePath.
+
+ if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
+ const FilePath projDir = n->filePath().canonicalPath();
+ const QString oldRelPathName
+ = oldFilePath.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+
+ const QString targetName = n->buildKey();
+
+ const QString key
+ = QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}
+ .join(";");
+
+ auto filePos = projectFileArgumentPosition(targetName, oldRelPathName);
+ if (!filePos)
+ return false;
+
+ m_filesToBeRenamed.insert(key, filePos.value());
+ return true;
+ }
+ return false;
+}
+
+bool CMakeBuildSystem::renameFile(Node *context,
+ const FilePath &oldFilePath,
+ const FilePath &newFilePath)
+{
+ if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
+ const FilePath projDir = n->filePath().canonicalPath();
+ const QString newRelPathName
+ = newFilePath.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
+
+ const QString targetName = n->buildKey();
+ const QString key
+ = QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}.join(
+ ";");
+
+ auto fileToRename = m_filesToBeRenamed.take(key);
+ if (!fileToRename.cmakeFile.exists())
+ return false;
+
+ BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
+ Core::EditorManager::openEditorAt({fileToRename.cmakeFile,
+ static_cast<int>(fileToRename.argumentPosition.Line),
+ static_cast<int>(fileToRename.argumentPosition.Column
+ - 1)},
+ Constants::CMAKE_EDITOR_ID,
+ Core::EditorManager::DoNotMakeVisible));
+ if (!editor)
+ return false;
+
+ if (!fileToRename.fromGlobbing)
+ editor->replace(fileToRename.relativeFileName.length(), newRelPathName);
+
+ editor->editorWidget()->autoIndent();
+ if (!Core::DocumentManager::saveDocument(editor->document()))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
FilePaths CMakeBuildSystem::filesGeneratedFrom(const FilePath &sourceFile) const
{
FilePath project = projectDirectory();
@@ -522,13 +872,22 @@ void CMakeBuildSystem::checkAndReportError(QString &errorMessage)
}
}
+static QSet<FilePath> projectFilesToWatch(const QSet<CMakeFileInfo> &cmakeFiles)
+{
+ return Utils::transform(Utils::filtered(cmakeFiles,
+ [](const CMakeFileInfo &info) {
+ return !info.isGenerated;
+ }),
+ [](const CMakeFileInfo &info) { return info.path; });
+}
+
void CMakeBuildSystem::updateProjectData()
{
qCDebug(cmakeBuildSystemLog) << "Updating CMake project data";
QTC_ASSERT(m_treeScanner.isFinished() && !m_reader.isParsing(), return );
- buildConfiguration()->project()->setExtraProjectFiles(m_reader.projectFilesToWatch());
+ buildConfiguration()->project()->setExtraProjectFiles(projectFilesToWatch(m_cmakeFiles));
CMakeConfig patchedConfig = configurationFromCMake();
{
@@ -601,7 +960,7 @@ void CMakeBuildSystem::updateProjectData()
for (RawProjectPart &rpp : rpps) {
rpp.setQtVersion(
kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt.
- const QString includeFileBaseDir = buildConfiguration()->buildDirectory().toString();
+ const FilePath includeFileBaseDir = buildConfiguration()->buildDirectory();
QStringList cxxFlags = rpp.flagsForCxx.commandLineFlags;
QStringList cFlags = rpp.flagsForC.commandLineFlags;
addTargetFlagForIos(cxxFlags, cFlags, this, [this] {
@@ -735,6 +1094,8 @@ void CMakeBuildSystem::handleParsingSucceeded(bool restoredFromBackup)
return result;
});
m_buildTargets += m_reader.takeBuildTargets(errorMessage);
+ m_cmakeFiles = m_reader.takeCMakeFileInfos(errorMessage);
+
checkAndReportError(errorMessage);
}
@@ -747,7 +1108,11 @@ void CMakeBuildSystem::handleParsingSucceeded(bool restoredFromBackup)
m_ctestPath = tool->cmakeExecutable().withNewPath(m_reader.ctestPath());
setApplicationTargets(appTargets());
- setDeploymentData(deploymentData());
+
+ // Note: This is practically always wrong and resulting in an empty view.
+ // Setting the real data is triggered from a successful run of a
+ // MakeInstallStep.
+ setDeploymentData(deploymentDataFromFile());
QTC_ASSERT(m_waitingForParse, return );
m_waitingForParse = false;
@@ -901,11 +1266,11 @@ void CMakeBuildSystem::runCTest()
QTC_ASSERT(parameters.isValid(), return);
ensureBuildDirectory(parameters);
- m_ctestProcess.reset(new QtcProcess);
+ m_ctestProcess.reset(new Process);
m_ctestProcess->setEnvironment(buildConfiguration()->environment());
m_ctestProcess->setWorkingDirectory(parameters.buildDirectory);
m_ctestProcess->setCommand({m_ctestPath, { "-N", "--show-only=json-v1"}});
- connect(m_ctestProcess.get(), &QtcProcess::done, this, [this] {
+ connect(m_ctestProcess.get(), &Process::done, this, [this] {
if (m_ctestProcess->result() == ProcessResult::FinishedWithSuccess) {
const QJsonDocument json
= QJsonDocument::fromJson(m_ctestProcess->readAllRawStandardOutput());
@@ -1064,7 +1429,7 @@ CommandLine CMakeBuildSystem::commandLineForTests(const QList<QString> &tests,
return {m_ctestPath, args};
}
-DeploymentData CMakeBuildSystem::deploymentData() const
+DeploymentData CMakeBuildSystem::deploymentDataFromFile() const
{
DeploymentData result;
@@ -1226,7 +1591,6 @@ void CMakeBuildSystem::updateInitialCMakeExpandableVars()
"CMAKE_CXX_COMPILER",
"QT_QMAKE_EXECUTABLE",
"QT_HOST_PATH",
- "CMAKE_PROJECT_INCLUDE_BEFORE",
"CMAKE_TOOLCHAIN_FILE"
};
for (const auto &var : singlePathList) {
@@ -1299,7 +1663,7 @@ MakeInstallCommand CMakeBuildSystem::makeInstallCommand(const FilePath &installR
buildDirectory = bc->buildDirectory();
cmd.command.addArg("--build");
- cmd.command.addArg(buildDirectory.onDevice(cmd.command.executable()).path());
+ cmd.command.addArg(buildDirectory.path());
cmd.command.addArg("--target");
cmd.command.addArg(installTarget);
@@ -1380,12 +1744,12 @@ void CMakeBuildSystem::runGenerator(Id id)
optionsAspect && !optionsAspect->value().isEmpty()) {
cmdLine.addArgs(optionsAspect->value(), CommandLine::Raw);
}
- const auto proc = new QtcProcess(this);
- connect(proc, &QtcProcess::done, proc, &QtcProcess::deleteLater);
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [proc] {
+ const auto proc = new Process(this);
+ connect(proc, &Process::done, proc, &Process::deleteLater);
+ connect(proc, &Process::readyReadStandardOutput, this, [proc] {
Core::MessageManager::writeFlashing(QString::fromLocal8Bit(proc->readAllRawStandardOutput()));
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [proc] {
+ connect(proc, &Process::readyReadStandardError, this, [proc] {
Core::MessageManager::writeDisrupting(QString::fromLocal8Bit(proc->readAllRawStandardError()));
});
proc->setWorkingDirectory(outDir);
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
index 802921f04b..4770b4d84b 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
+++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h
@@ -18,7 +18,7 @@ namespace ProjectExplorer {
class ExtraCompiler;
class FolderNode;
}
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace CMakeProjectManager {
@@ -48,6 +48,18 @@ public:
bool addFiles(ProjectExplorer::Node *context,
const Utils::FilePaths &filePaths, Utils::FilePaths *) final;
+ ProjectExplorer::RemovedFilesFromProject removeFiles(ProjectExplorer::Node *context,
+ const Utils::FilePaths &filePaths,
+ Utils::FilePaths *notRemoved
+ = nullptr) final;
+
+ bool canRenameFile(ProjectExplorer::Node *context,
+ const Utils::FilePath &oldFilePath,
+ const Utils::FilePath &newFilePath) final;
+ bool renameFile(ProjectExplorer::Node *context,
+ const Utils::FilePath &oldFilePath,
+ const Utils::FilePath &newFilePath) final;
+
Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const final;
QString name() const final { return QLatin1String("cmake"); }
@@ -67,7 +79,7 @@ public:
const QList<ProjectExplorer::BuildTargetInfo> appTargets() const;
QStringList buildTargetTitles() const;
const QList<CMakeBuildTarget> &buildTargets() const;
- ProjectExplorer::DeploymentData deploymentData() const;
+ ProjectExplorer::DeploymentData deploymentDataFromFile() const;
CMakeBuildConfiguration *cmakeBuildConfiguration() const;
@@ -185,6 +197,16 @@ private:
void runCTest();
+ struct ProjectFileArgumentPosition
+ {
+ cmListFileArgument argumentPosition;
+ Utils::FilePath cmakeFile;
+ QString relativeFileName;
+ bool fromGlobbing = false;
+ };
+ std::optional<ProjectFileArgumentPosition> projectFileArgumentPosition(
+ const QString &targetName, const QString &fileName);
+
ProjectExplorer::TreeScanner m_treeScanner;
std::shared_ptr<ProjectExplorer::FolderNode> m_allFiles;
QHash<QString, bool> m_mimeBinaryCache;
@@ -199,6 +221,9 @@ private:
CppEditor::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
QList<CMakeBuildTarget> m_buildTargets;
+ QSet<CMakeFileInfo> m_cmakeFiles;
+
+ QHash<QString, ProjectFileArgumentPosition> m_filesToBeRenamed;
// Parsing state:
BuildDirParameters m_parameters;
@@ -208,7 +233,7 @@ private:
// CTest integration
Utils::FilePath m_ctestPath;
- std::unique_ptr<Utils::QtcProcess> m_ctestProcess;
+ std::unique_ptr<Utils::Process> m_ctestProcess;
QList<ProjectExplorer::TestCaseInfo> m_testNames;
CMakeConfig m_configurationFromCMake;
diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp
index 325a6012a5..01fbeaa232 100644
--- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp
@@ -8,8 +8,9 @@
#include "cmaketool.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+
#include <texteditor/codeassist/assistinterface.h>
#include <QFileInfo>
@@ -39,7 +40,7 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
Keywords kw;
const Utils::FilePath &filePath = interface()->filePath();
if (!filePath.isEmpty() && filePath.toFileInfo().isFile()) {
- Project *p = SessionManager::projectForFile(filePath);
+ Project *p = ProjectManager::projectForFile(filePath);
if (p && p->activeTarget()) {
CMakeTool *cmake = CMakeKitAspect::cmakeTool(p->activeTarget()->kit());
if (cmake && cmake->isValid())
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
index d9aa8b2c84..778e04563d 100644
--- a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp
@@ -4,7 +4,6 @@
#include "cmakeformatter.h"
-#include "cmakeformattersettings.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
@@ -12,6 +11,7 @@
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
+#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
@@ -20,54 +20,188 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
+#include <texteditor/command.h>
#include <texteditor/formattexteditor.h>
#include <texteditor/texteditor.h>
-#include <QAction>
-#include <QVersionNumber>
+#include <utils/algorithm.h>
+#include <utils/genericconstants.h>
+#include <utils/layoutbuilder.h>
+#include <utils/mimeutils.h>
+#include <QMenu>
+
+using namespace Core;
using namespace TextEditor;
+using namespace Utils;
-namespace CMakeProjectManager {
-namespace Internal {
+namespace CMakeProjectManager::Internal {
-void CMakeFormatter::updateActions(Core::IEditor *editor)
+class CMakeFormatterPrivate : public PagedSettings
{
- const bool enabled = editor && CMakeFormatterSettings::instance()->isApplicable(editor->document());
- m_formatFile->setEnabled(enabled);
-}
-
-void CMakeFormatter::formatFile()
+public:
+ CMakeFormatterPrivate()
+ {
+ setSettingsGroups(Constants::CMAKEFORMATTER_SETTINGS_GROUP,
+ Constants::CMAKEFORMATTER_GENERAL_GROUP);
+
+ setId(Constants::Settings::FORMATTER_ID);
+ setDisplayName(Tr::tr("Formatter"));
+ setDisplayCategory("CMake");
+ setCategory(Constants::Settings::CATEGORY);
+
+ registerAspect(&command);
+ command.setSettingsKey("autoFormatCommand");
+ command.setDisplayStyle(StringAspect::PathChooserDisplay);
+ command.setDefaultValue("cmake-format");
+ command.setExpectedKind(PathChooser::ExistingCommand);
+
+ registerAspect(&autoFormatOnSave);
+ autoFormatOnSave.setSettingsKey("autoFormatOnSave");
+ autoFormatOnSave.setLabelText(Tr::tr("Enable auto format on file save"));
+
+ registerAspect(&autoFormatOnlyCurrentProject);
+ autoFormatOnlyCurrentProject.setSettingsKey("autoFormatOnlyCurrentProject");
+ autoFormatOnlyCurrentProject.setDefaultValue(true);
+ autoFormatOnlyCurrentProject.setLabelText(Tr::tr("Restrict to files contained in the current project"));
+
+ registerAspect(&autoFormatMime);
+ autoFormatMime.setSettingsKey("autoFormatMime");
+ autoFormatMime.setDefaultValue("text/x-cmake");
+ autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:"));
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ Column {
+ Row { Tr::tr("CMakeFormat command:"), command },
+ Space(10),
+ Group {
+ title(Tr::tr("Automatic Formatting on File Save")),
+ autoFormatOnSave.groupChecker(),
+ Form {
+ autoFormatMime, br,
+ Span(2, autoFormatOnlyCurrentProject)
+ }
+ },
+ st
+ }.attachTo(widget);
+ });
+
+ ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);
+ menu->menu()->setTitle(Tr::tr("CMakeFormatter"));
+ menu->setOnAllDisabledBehavior(ActionContainer::Show);
+ ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
+
+ Core::Command *cmd = ActionManager::registerAction(&formatFile, Constants::CMAKEFORMATTER_ACTION_ID);
+ connect(&formatFile, &QAction::triggered, this, [this] {
+ TextEditor::formatCurrentFile(formatCommand());
+ });
+
+ ActionManager::actionContainer(Constants::CMAKEFORMATTER_MENU_ID)->addAction(cmd);
+
+ auto updateActions = [this] {
+ auto editor = EditorManager::currentEditor();
+ formatFile.setEnabled(editor && isApplicable(editor->document()));
+ };
+
+ connect(&autoFormatMime, &Utils::StringAspect::changed,
+ this, updateActions);
+ connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
+ this, updateActions);
+ connect(EditorManager::instance(), &EditorManager::aboutToSave,
+ this, &CMakeFormatterPrivate::applyIfNecessary);
+
+ readSettings();
+ }
+
+ bool isApplicable(const IDocument *document) const;
+
+ void applyIfNecessary(IDocument *document) const;
+
+ TextEditor::Command formatCommand() const
+ {
+ TextEditor::Command cmd;
+ cmd.setExecutable(command.filePath());
+ cmd.setProcessing(TextEditor::Command::FileProcessing);
+ cmd.addOption("--in-place");
+ cmd.addOption("%file");
+ return cmd;
+ }
+
+ StringAspect command;
+ BoolAspect autoFormatOnSave;
+ BoolAspect autoFormatOnlyCurrentProject;
+ StringAspect autoFormatMime;
+
+ QAction formatFile{Tr::tr("Format &Current File")};
+};
+
+bool CMakeFormatterPrivate::isApplicable(const IDocument *document) const
{
- formatCurrentFile(command());
+ if (!document)
+ return false;
+
+ if (autoFormatMime.value().isEmpty())
+ return true;
+
+ const QStringList allowedMimeTypes = autoFormatMime.value().split(';');
+ const MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
+
+ return anyOf(allowedMimeTypes, [&documentMimeType](const QString &mime) {
+ return documentMimeType.inherits(mime);
+ });
}
-Command CMakeFormatter::command() const
+void CMakeFormatterPrivate::applyIfNecessary(IDocument *document) const
{
- Command command;
- command.setExecutable(CMakeFormatterSettings::instance()->command());
- command.setProcessing(Command::FileProcessing);
- command.addOption("--in-place");
- command.addOption("%file");
- return command;
+ if (!autoFormatOnSave.value())
+ return;
+
+ if (!document)
+ return;
+
+ if (!isApplicable(document))
+ return;
+
+ // Check if file is contained in the current project (if wished)
+ if (autoFormatOnlyCurrentProject.value()) {
+ const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
+ if (!pro || pro->files([document](const ProjectExplorer::Node *n) {
+ return ProjectExplorer::Project::SourceFiles(n)
+ && n->filePath() == document->filePath();
+ }).isEmpty()) {
+ return;
+ }
+ }
+
+ TextEditor::Command command = formatCommand();
+ if (!command.isValid())
+ return;
+
+ const QList<IEditor *> editors = DocumentModel::editorsForDocument(document);
+ if (editors.isEmpty())
+ return;
+
+ IEditor *currentEditor = EditorManager::currentEditor();
+ IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first();
+ if (auto widget = TextEditorWidget::fromEditor(editor))
+ TextEditor::formatEditor(widget, command);
}
-bool CMakeFormatter::isApplicable(const Core::IDocument *document) const
+// CMakeFormatter
+
+CMakeFormatter::CMakeFormatter()
+ : d(new CMakeFormatterPrivate)
+{}
+
+CMakeFormatter::~CMakeFormatter()
{
- return CMakeFormatterSettings::instance()->isApplicable(document);
+ delete d;
}
-void CMakeFormatter::initialize()
+void CMakeFormatter::applyIfNecessary(IDocument *document) const
{
- m_formatFile = new QAction(Tr::tr("Format &Current File"), this);
- Core::Command *cmd = Core::ActionManager::registerAction(m_formatFile, Constants::CMAKEFORMATTER_ACTION_ID);
- connect(m_formatFile, &QAction::triggered, this, &CMakeFormatter::formatFile);
-
- Core::ActionManager::actionContainer(Constants::CMAKEFORMATTER_MENU_ID)->addAction(cmd);
-
- connect(CMakeFormatterSettings::instance(), &CMakeFormatterSettings::supportedMimeTypesChanged,
- [this] { updateActions(Core::EditorManager::currentEditor()); });
+ d->applyIfNecessary(document);
}
-} // namespace Internal
-} // namespace CMakeProjectManager
+} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.h b/src/plugins/cmakeprojectmanager/cmakeformatter.h
index f3fc3a28c5..9727c245a5 100644
--- a/src/plugins/cmakeprojectmanager/cmakeformatter.h
+++ b/src/plugins/cmakeprojectmanager/cmakeformatter.h
@@ -4,35 +4,20 @@
#pragma once
-#include <texteditor/command.h>
+namespace Core { class IDocument; }
-#include "cmakeformatteroptionspage.h"
+namespace CMakeProjectManager::Internal {
-namespace Core {
-class IDocument;
-class IEditor;
-}
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-class CMakeFormatter : public QObject
+class CMakeFormatter
{
- Q_OBJECT
-
public:
- void updateActions(Core::IEditor *editor);
- TextEditor::Command command() const;
- bool isApplicable(const Core::IDocument *document) const;
+ CMakeFormatter();
+ ~CMakeFormatter();
- void initialize();
+ void applyIfNecessary(Core::IDocument *document) const;
private:
- void formatFile();
-
- QAction *m_formatFile = nullptr;
- CMakeFormatterOptionsPage m_page;
+ class CMakeFormatterPrivate *d = nullptr;
};
-} // namespace Internal
-} // namespace CMakeProjectManager
+} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp b/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp
deleted file mode 100644
index a82e7be07a..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakeformatteroptionspage.h"
-
-#include "cmakeprojectconstants.h"
-#include "cmakeprojectmanagertr.h"
-#include "cmakeformattersettings.h"
-
-#include <utils/layoutbuilder.h>
-#include <utils/pathchooser.h>
-
-#include <QApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QLabel>
-#include <QLineEdit>
-
-namespace CMakeProjectManager::Internal {
-
-class CMakeFormatterOptionsPageWidget : public Core::IOptionsPageWidget
-{
-public:
- explicit CMakeFormatterOptionsPageWidget();
-
-private:
- void apply() final;
-
- Utils::PathChooser *m_command;
- QCheckBox *m_autoFormat;
- QLineEdit *m_autoFormatMime;
- QCheckBox *m_autoFormatOnlyCurrentProject;
-};
-
-CMakeFormatterOptionsPageWidget::CMakeFormatterOptionsPageWidget()
-{
- resize(817, 631);
-
- auto settings = CMakeFormatterSettings::instance();
-
- m_autoFormat = new QCheckBox(Tr::tr("Enable auto format on file save"));
- m_autoFormat->setChecked(settings->autoFormatOnSave());
-
- auto mimeLabel = new QLabel(Tr::tr("Restrict to MIME types:"));
- mimeLabel->setEnabled(false);
-
- m_autoFormatMime = new QLineEdit(settings->autoFormatMimeAsString());
- m_autoFormatMime->setEnabled(m_autoFormat->isChecked());
-
- m_autoFormatOnlyCurrentProject =
- new QCheckBox(Tr::tr("Restrict to files contained in the current project"));
- m_autoFormatOnlyCurrentProject->setEnabled(m_autoFormat->isChecked());
- m_autoFormatOnlyCurrentProject->setChecked(settings->autoFormatOnlyCurrentProject());
-
- m_command = new Utils::PathChooser;
- m_command->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_command->setCommandVersionArguments({"--version"});
- m_command->setPromptDialogTitle(Tr::tr("%1 Command").arg(Tr::tr("Formatter")));
- m_command->setFilePath(settings->command());
-
- using namespace Utils::Layouting;
-
- Column {
- Group {
- title(Tr::tr("Automatic Formatting on File Save")),
- Form {
- Tr::tr("CMakeFormat command:"), m_command, br,
- Span(2, m_autoFormat), br,
- mimeLabel, m_autoFormatMime, br,
- Span(2, m_autoFormatOnlyCurrentProject)
- }
- },
- st
- }.attachTo(this);
-
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatMime, &QLineEdit::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, mimeLabel, &QLabel::setEnabled);
- connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatOnlyCurrentProject, &QCheckBox::setEnabled);
-}
-
-void CMakeFormatterOptionsPageWidget::apply()
-{
- auto settings = CMakeFormatterSettings::instance();
- settings->setCommand(m_command->filePath().toString());
- settings->setAutoFormatOnSave(m_autoFormat->isChecked());
- settings->setAutoFormatMime(m_autoFormatMime->text());
- settings->setAutoFormatOnlyCurrentProject(m_autoFormatOnlyCurrentProject->isChecked());
- settings->save();
-}
-
-CMakeFormatterOptionsPage::CMakeFormatterOptionsPage()
-{
- setId(Constants::Settings::FORMATTER_ID);
- setDisplayName(Tr::tr("Formatter"));
- setDisplayCategory("CMake");
- setCategory(Constants::Settings::CATEGORY);
- setWidgetCreator([] { return new CMakeFormatterOptionsPageWidget; });
-}
-
-} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h b/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h
deleted file mode 100644
index 08cfac3590..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformatteroptionspage.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-
-namespace CMakeProjectManager::Internal {
-
-class CMakeFormatterOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit CMakeFormatterOptionsPage();
-};
-
-} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp b/src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp
deleted file mode 100644
index 4bb2218340..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformattersettings.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cmakeformattersettings.h"
-#include "cmakeprojectconstants.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/idocument.h>
-#include <utils/algorithm.h>
-#include <utils/genericconstants.h>
-#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-namespace {
-const char AUTO_FORMAT_COMMAND[] = "autoFormatCommand";
-const char AUTO_FORMAT_MIME[] = "autoFormatMime";
-const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "autoFormatOnlyCurrentProject";
-const char AUTO_FORMAT_ON_SAVE[] = "autoFormatOnSave";
-}
-
-CMakeFormatterSettings::CMakeFormatterSettings(QObject* parent)
- : QObject(parent)
-{
- read();
-}
-
-CMakeFormatterSettings *CMakeFormatterSettings::instance()
-{
- static CMakeFormatterSettings m_instance;
- return &m_instance;
-}
-
-void CMakeFormatterSettings::read()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Constants::CMAKEFORMATTER_SETTINGS_GROUP);
- s->beginGroup(Constants::CMAKEFORMATTER_GENERAL_GROUP);
- setCommand(s->value(AUTO_FORMAT_COMMAND, QString("cmake-format")).toString());
- m_autoFormatOnSave = s->value(AUTO_FORMAT_ON_SAVE, false).toBool();
- setAutoFormatMime(s->value(AUTO_FORMAT_MIME, QString("text/x-cmake")).toString());
- m_autoFormatOnlyCurrentProject = s->value(AUTO_FORMAT_ONLY_CURRENT_PROJECT, true).toBool();
- s->endGroup();
- s->endGroup();
-}
-
-void CMakeFormatterSettings::save()
-{
- QSettings *s = Core::ICore::settings();
- s->beginGroup(Constants::CMAKEFORMATTER_SETTINGS_GROUP);
- s->beginGroup(Constants::CMAKEFORMATTER_GENERAL_GROUP);
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_COMMAND, m_command, QString("cmake-format"));
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_ON_SAVE, m_autoFormatOnSave, false);
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_MIME, autoFormatMimeAsString(), QString("text/x-cmake"));
- Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_ONLY_CURRENT_PROJECT, m_autoFormatOnlyCurrentProject, true);
- s->endGroup();
- s->endGroup();
-}
-
-Utils::FilePath CMakeFormatterSettings::command() const
-{
- return Utils::FilePath::fromString(m_command);
-}
-
-void CMakeFormatterSettings::setCommand(const QString &cmd)
-{
- if (cmd == m_command)
- return;
-
- m_command = cmd;
-}
-
-bool CMakeFormatterSettings::autoFormatOnSave() const
-{
- return m_autoFormatOnSave;
-}
-
-void CMakeFormatterSettings::setAutoFormatOnSave(bool autoFormatOnSave)
-{
- m_autoFormatOnSave = autoFormatOnSave;
-}
-
-QStringList CMakeFormatterSettings::autoFormatMime() const
-{
- return m_autoFormatMime;
-}
-
-QString CMakeFormatterSettings::autoFormatMimeAsString() const
-{
- return m_autoFormatMime.join("; ");
-}
-
-void CMakeFormatterSettings::setAutoFormatMime(const QStringList &autoFormatMime)
-{
- if (m_autoFormatMime == autoFormatMime)
- return;
-
- m_autoFormatMime = autoFormatMime;
- emit supportedMimeTypesChanged();
-}
-
-void CMakeFormatterSettings::setAutoFormatMime(const QString &mimeList)
-{
- setAutoFormatMime(mimeList.split(';'));
-}
-
-bool CMakeFormatterSettings::autoFormatOnlyCurrentProject() const
-{
- return m_autoFormatOnlyCurrentProject;
-}
-
-void CMakeFormatterSettings::setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject)
-{
- m_autoFormatOnlyCurrentProject = autoFormatOnlyCurrentProject;
-}
-
-bool CMakeFormatterSettings::isApplicable(const Core::IDocument *document) const
-{
- if (!document)
- return false;
-
- if (m_autoFormatMime.isEmpty())
- return true;
-
- const Utils::MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
- return Utils::anyOf(m_autoFormatMime, [&documentMimeType](const QString &mime) {
- return documentMimeType.inherits(mime);
- });
-}
-
-} // namespace Internal
-} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeformattersettings.h b/src/plugins/cmakeprojectmanager/cmakeformattersettings.h
deleted file mode 100644
index ea8a3ba162..0000000000
--- a/src/plugins/cmakeprojectmanager/cmakeformattersettings.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2016 Lorenz Haas
-// Copyright (C) 2022 Xavier BESSON
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QList>
-#include <QObject>
-#include <QVersionNumber>
-
-namespace Core { class IDocument; }
-namespace Utils { class FilePath; }
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-class VersionUpdater;
-
-class CMakeFormatterSettings : public QObject
-{
- Q_OBJECT
-public:
- explicit CMakeFormatterSettings(QObject* parent = nullptr);
- static CMakeFormatterSettings *instance();
-
- void read();
- void save();
-
- Utils::FilePath command() const;
- void setCommand(const QString &cmd);
-
- bool autoFormatOnSave() const;
- void setAutoFormatOnSave(bool autoFormatOnSave);
-
- QStringList autoFormatMime() const;
- QString autoFormatMimeAsString() const;
- void setAutoFormatMime(const QStringList &autoFormatMime);
- void setAutoFormatMime(const QString &mimeList);
-
- bool autoFormatOnlyCurrentProject() const;
- void setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject);
-
- bool isApplicable(const Core::IDocument *document) const;
-
-signals:
- void supportedMimeTypesChanged();
-
-private:
- QString m_command;
-
- bool m_autoFormatOnSave = false;
- bool m_autoFormatOnlyCurrentProject = true;
- QString m_autoFormatTool;
- QStringList m_autoFormatMime;
-};
-
-} // namespace Internal
-} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp b/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp
index 6e6a49523f..07d3c6fd47 100644
--- a/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp
@@ -74,7 +74,7 @@ CommandLine CMakeInstallStep::cmakeCommand() const
if (buildConfiguration())
buildDirectory = buildConfiguration()->buildDirectory();
- cmd.addArgs({"--install", buildDirectory.onDevice(cmd.executable()).path()});
+ cmd.addArgs({"--install", buildDirectory.path()});
auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem());
if (bs && bs->isMultiConfigReader()) {
@@ -93,6 +93,7 @@ void CMakeInstallStep::finish(ProcessResult result)
emit progress(100, {});
AbstractProcessStep::finish(result);
}
+
QWidget *CMakeInstallStep::createConfigWidget()
{
auto updateDetails = [this] {
@@ -105,10 +106,8 @@ QWidget *CMakeInstallStep::createConfigWidget()
setDisplayName(Tr::tr("Install", "ConfigWidget display name."));
- Layouting::Form builder;
- builder.addRow(m_cmakeArguments);
-
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ using namespace Layouting;
+ auto widget = Form { m_cmakeArguments, noMargin }.emerge();
updateDetails();
diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp
index 347c8e794c..ee7cd5f1c1 100644
--- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp
@@ -38,7 +38,6 @@
#include <utils/variablechooser.h>
#include <QComboBox>
-#include <QCryptographicHash>
#include <QDialog>
#include <QDialogButtonBox>
#include <QGridLayout>
@@ -49,7 +48,6 @@
using namespace ProjectExplorer;
using namespace Utils;
-using namespace Utils::Layouting;
namespace CMakeProjectManager {
@@ -105,7 +103,7 @@ private:
// KitAspectWidget interface
void makeReadOnly() override { m_comboBox->setEnabled(false); }
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -350,11 +348,11 @@ private:
// KitAspectWidget interface
void makeReadOnly() override { m_changeButton->setEnabled(false); }
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_label);
- builder.addItem(m_label);
- builder.addItem(m_changeButton);
+ parent.addItem(m_label);
+ parent.addItem(m_changeButton);
}
void refresh() override
@@ -874,7 +872,6 @@ const char CMAKE_CXX_TOOLCHAIN_KEY[] = "CMAKE_CXX_COMPILER";
const char CMAKE_QMAKE_KEY[] = "QT_QMAKE_EXECUTABLE";
const char CMAKE_PREFIX_PATH_KEY[] = "CMAKE_PREFIX_PATH";
const char QTC_CMAKE_PRESET_KEY[] = "QTC_CMAKE_PRESET";
-const char QTC_KIT_DEFAULT_CONFIG_HASH[] = "QTC_KIT_DEFAULT_CONFIG_HASH";
class CMakeConfigurationKitAspectWidget final : public KitAspectWidget
{
@@ -892,11 +889,11 @@ public:
private:
// KitAspectWidget interface
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_summaryLabel);
- builder.addItem(m_summaryLabel);
- builder.addItem(m_manageButton);
+ parent.addItem(m_summaryLabel);
+ parent.addItem(m_manageButton);
}
void makeReadOnly() override
@@ -1135,51 +1132,6 @@ CMakeConfigItem CMakeConfigurationKitAspect::cmakePresetConfigItem(const Project
});
}
-void CMakeConfigurationKitAspect::setKitDefaultConfigHash(ProjectExplorer::Kit *k)
-{
- const CMakeConfig defaultConfigExpanded
- = Utils::transform(defaultConfiguration(k).toList(), [k](const CMakeConfigItem &item) {
- CMakeConfigItem expanded(item);
- expanded.value = item.expandedValue(k).toUtf8();
- return expanded;
- });
- const CMakeTool *const tool = CMakeKitAspect::cmakeTool(k);
- const QByteArray kitHash = computeDefaultConfigHash(defaultConfigExpanded,
- tool ? tool->cmakeExecutable()
- : FilePath());
-
- CMakeConfig config = configuration(k);
- config.append(CMakeConfigItem(QTC_KIT_DEFAULT_CONFIG_HASH, CMakeConfigItem::INTERNAL, kitHash));
-
- setConfiguration(k, config);
-}
-
-CMakeConfigItem CMakeConfigurationKitAspect::kitDefaultConfigHashItem(const ProjectExplorer::Kit *k)
-{
- const CMakeConfig config = configuration(k);
- return Utils::findOrDefault(config, [](const CMakeConfigItem &item) {
- return item.key == QTC_KIT_DEFAULT_CONFIG_HASH;
- });
-}
-
-QByteArray CMakeConfigurationKitAspect::computeDefaultConfigHash(const CMakeConfig &config,
- const FilePath &cmakeBinary)
-{
- const CMakeConfig defaultConfig = defaultConfiguration(nullptr);
- const QByteArray configValues = std::accumulate(defaultConfig.begin(),
- defaultConfig.end(),
- QByteArray(),
- [config](QByteArray &sum,
- const CMakeConfigItem &item) {
- return sum += config.valueOf(item.key);
- });
- return QCryptographicHash::hash(cmakeBinary.caseSensitivity() == Qt::CaseInsensitive
- ? configValues.toLower()
- : configValues,
- QCryptographicHash::Md5)
- .toHex();
-}
-
QVariant CMakeConfigurationKitAspect::defaultValue(const Kit *k) const
{
// FIXME: Convert preload scripts
@@ -1209,16 +1161,15 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
FilePath tcCxxPath;
for (const CMakeConfigItem &i : config) {
// Do not use expand(QByteArray) as we cannot be sure the input is latin1
- const FilePath expandedValue
- = FilePath::fromString(k->macroExpander()->expand(QString::fromUtf8(i.value)));
+ const QString expandedValue = k->macroExpander()->expand(QString::fromUtf8(i.value));
if (i.key == CMAKE_QMAKE_KEY)
- qmakePath = expandedValue.onDevice(cmake->cmakeExecutable());
+ qmakePath = cmake->cmakeExecutable().withNewPath(expandedValue);
else if (i.key == CMAKE_C_TOOLCHAIN_KEY)
- tcCPath = expandedValue.onDevice(cmake->cmakeExecutable());
+ tcCPath = cmake->cmakeExecutable().withNewPath(expandedValue);
else if (i.key == CMAKE_CXX_TOOLCHAIN_KEY)
- tcCxxPath = expandedValue.onDevice(cmake->cmakeExecutable());
+ tcCxxPath = cmake->cmakeExecutable().withNewPath(expandedValue);
else if (i.key == CMAKE_PREFIX_PATH_KEY)
- qtInstallDirs = CMakeConfigItem::cmakeSplitValue(expandedValue.path());
+ qtInstallDirs = CMakeConfigItem::cmakeSplitValue(expandedValue);
}
Tasks result;
@@ -1259,7 +1210,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
if (!tcC || !tcC->isValid()) {
addWarning(Tr::tr("CMake configuration has a path to a C compiler set, "
"even though the kit has no valid tool chain."));
- } else if (tcCPath != tcC->compilerCommand() && tcCPath != tcC->compilerCommand().onDevice(tcCPath)) {
+ } else if (tcCPath != tcC->compilerCommand() && tcCPath != tcCPath.withNewMappedPath(tcC->compilerCommand())) {
addWarning(Tr::tr("CMake configuration has a path to a C compiler set "
"that does not match the compiler path "
"configured in the tool chain of the kit."));
@@ -1275,7 +1226,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
if (!tcCxx || !tcCxx->isValid()) {
addWarning(Tr::tr("CMake configuration has a path to a C++ compiler set, "
"even though the kit has no valid tool chain."));
- } else if (tcCxxPath != tcCxx->compilerCommand() && tcCxxPath != tcCxx->compilerCommand().onDevice(tcCxxPath)) {
+ } else if (tcCxxPath != tcCxx->compilerCommand() && tcCxxPath != tcCxxPath.withNewMappedPath(tcCxx->compilerCommand())) {
addWarning(Tr::tr("CMake configuration has a path to a C++ compiler set "
"that does not match the compiler path "
"configured in the tool chain of the kit."));
diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.h b/src/plugins/cmakeprojectmanager/cmakekitinformation.h
index bf056f99e2..3b85235d91 100644
--- a/src/plugins/cmakeprojectmanager/cmakekitinformation.h
+++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.h
@@ -91,11 +91,6 @@ public:
static void setCMakePreset(ProjectExplorer::Kit *k, const QString &presetName);
static CMakeConfigItem cmakePresetConfigItem(const ProjectExplorer::Kit *k);
- static void setKitDefaultConfigHash(ProjectExplorer::Kit *k);
- static CMakeConfigItem kitDefaultConfigHashItem(const ProjectExplorer::Kit *k);
- static QByteArray computeDefaultConfigHash(const CMakeConfig &config,
- const Utils::FilePath &cmakeBinary);
-
// KitAspect interface
ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final;
void setup(ProjectExplorer::Kit *k) final;
diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
index 0eab601200..8d63ee0fa9 100644
--- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
@@ -8,114 +8,94 @@
#include "cmakeproject.h"
#include "cmakeprojectmanagertr.h"
-#include <coreplugin/editormanager/editormanager.h>
-
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/buildsteplist.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
+using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
namespace CMakeProjectManager::Internal {
-// --------------------------------------------------------------------
-// CMakeTargetLocatorFilter:
-// --------------------------------------------------------------------
+using BuildAcceptor = std::function<void(const FilePath &, const QString &)>;
-CMakeTargetLocatorFilter::CMakeTargetLocatorFilter()
+static LocatorMatcherTasks cmakeMatchers(const BuildAcceptor &acceptor)
{
- connect(SessionManager::instance(), &SessionManager::projectAdded,
- this, &CMakeTargetLocatorFilter::projectListUpdated);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
- this, &CMakeTargetLocatorFilter::projectListUpdated);
+ using namespace Tasking;
- // Initialize the filter
- projectListUpdated();
-}
+ TreeStorage<LocatorStorage> storage;
-void CMakeTargetLocatorFilter::prepareSearch(const QString &entry)
-{
- m_result.clear();
- const QList<Project *> projects = SessionManager::projects();
- for (Project *p : projects) {
- auto cmakeProject = qobject_cast<const CMakeProject *>(p);
- if (!cmakeProject || !cmakeProject->activeTarget())
- continue;
- auto bs = qobject_cast<CMakeBuildSystem *>(cmakeProject->activeTarget()->buildSystem());
- if (!bs)
- continue;
-
- const QList<CMakeBuildTarget> buildTargets = bs->buildTargets();
- for (const CMakeBuildTarget &target : buildTargets) {
- if (CMakeBuildSystem::filteredOutTarget(target))
+ const auto onSetup = [storage, acceptor] {
+ const QString input = storage->input();
+ const QList<Project *> projects = ProjectManager::projects();
+ LocatorFilterEntries entries;
+ for (Project *project : projects) {
+ const auto cmakeProject = qobject_cast<const CMakeProject *>(project);
+ if (!cmakeProject || !cmakeProject->activeTarget())
+ continue;
+ const auto bs = qobject_cast<CMakeBuildSystem *>(
+ cmakeProject->activeTarget()->buildSystem());
+ if (!bs)
continue;
- const int index = target.title.indexOf(entry, 0, Qt::CaseInsensitive);
- if (index >= 0) {
- const FilePath path = target.backtrace.isEmpty() ? cmakeProject->projectFilePath()
- : target.backtrace.last().path;
- const int line = target.backtrace.isEmpty() ? -1 : target.backtrace.last().line;
-
- QVariantMap extraData;
- extraData.insert("project", cmakeProject->projectFilePath().toString());
- extraData.insert("line", line);
- extraData.insert("file", path.toString());
-
- Core::LocatorFilterEntry filterEntry(this, target.title, extraData);
- filterEntry.extraInfo = path.shortNativePath();
- filterEntry.highlightInfo = {index, int(entry.length())};
- filterEntry.filePath = path;
-
- m_result.append(filterEntry);
+
+ const QList<CMakeBuildTarget> buildTargets = bs->buildTargets();
+ for (const CMakeBuildTarget &target : buildTargets) {
+ if (CMakeBuildSystem::filteredOutTarget(target))
+ continue;
+ const int index = target.title.indexOf(input, 0, Qt::CaseInsensitive);
+ if (index >= 0) {
+ const FilePath path = target.backtrace.isEmpty()
+ ? cmakeProject->projectFilePath()
+ : target.backtrace.last().path;
+ const int line = target.backtrace.isEmpty() ? 0 : target.backtrace.last().line;
+ const FilePath projectPath = cmakeProject->projectFilePath();
+ const QString displayName = target.title;
+ LocatorFilterEntry entry;
+ entry.displayName = displayName;
+ if (acceptor) {
+ entry.acceptor = [projectPath, displayName, acceptor] {
+ acceptor(projectPath, displayName);
+ return AcceptResult();
+ };
+ }
+ entry.linkForEditor = {path, line};
+ entry.extraInfo = path.shortNativePath();
+ entry.highlightInfo = {index, int(input.length())};
+ entry.filePath = cmakeProject->projectFilePath();
+ entries.append(entry);
+ }
}
}
- }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
-QList<Core::LocatorFilterEntry> CMakeTargetLocatorFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+void setupFilter(ILocatorFilter *filter)
{
- Q_UNUSED(future)
- Q_UNUSED(entry)
- return m_result;
-}
-
-void CMakeTargetLocatorFilter::projectListUpdated()
-{
- // Enable the filter if there's at least one CMake project
- setEnabled(Utils::contains(SessionManager::projects(), [](Project *p) { return qobject_cast<CMakeProject *>(p); }));
+ const auto projectListUpdated = [filter] {
+ filter->setEnabled(Utils::contains(ProjectManager::projects(),
+ [](Project *p) { return qobject_cast<CMakeProject *>(p); }));
+ };
+ QObject::connect(ProjectManager::instance(), &ProjectManager::projectAdded,
+ filter, projectListUpdated);
+ QObject::connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
+ filter, projectListUpdated);
}
// --------------------------------------------------------------------
// BuildCMakeTargetLocatorFilter:
// --------------------------------------------------------------------
-BuildCMakeTargetLocatorFilter::BuildCMakeTargetLocatorFilter()
+static void buildAcceptor(const FilePath &projectPath, const QString &displayName)
{
- setId("Build CMake target");
- setDisplayName(Tr::tr("Build CMake target"));
- setDescription(Tr::tr("Builds a target of any open CMake project."));
- setDefaultShortcutString("cm");
- setPriority(High);
-}
-
-void BuildCMakeTargetLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
-
- const QVariantMap extraData = selection.internalData.toMap();
- const FilePath projectPath = FilePath::fromString(extraData.value("project").toString());
-
// Get the project containing the target selected
const auto cmakeProject = qobject_cast<CMakeProject *>(
- Utils::findOrDefault(SessionManager::projects(), [projectPath](Project *p) {
+ Utils::findOrDefault(ProjectManager::projects(), [projectPath](Project *p) {
return p->projectFilePath() == projectPath;
}));
if (!cmakeProject || !cmakeProject->activeTarget()
@@ -126,53 +106,53 @@ void BuildCMakeTargetLocatorFilter::accept(const Core::LocatorFilterEntry &selec
BuildManager::cancel();
// Find the make step
- BuildStepList *buildStepList =
- cmakeProject->activeTarget()->activeBuildConfiguration()->buildSteps();
- auto buildStep = buildStepList->firstOfType<CMakeBuildStep>();
+ const BuildStepList *buildStepList =
+ cmakeProject->activeTarget()->activeBuildConfiguration()->buildSteps();
+ const auto buildStep = buildStepList->firstOfType<CMakeBuildStep>();
if (!buildStep)
return;
// Change the make step to build only the given target
- QStringList oldTargets = buildStep->buildTargets();
- buildStep->setBuildTargets({selection.displayName});
+ const QStringList oldTargets = buildStep->buildTargets();
+ buildStep->setBuildTargets({displayName});
// Build
BuildManager::buildProjectWithDependencies(cmakeProject);
buildStep->setBuildTargets(oldTargets);
}
+CMakeBuildTargetFilter::CMakeBuildTargetFilter()
+{
+ setId("Build CMake target");
+ setDisplayName(Tr::tr("Build CMake Target"));
+ setDescription(Tr::tr("Builds a target of any open CMake project."));
+ setDefaultShortcutString("cm");
+ setPriority(High);
+ setupFilter(this);
+}
+
+Core::LocatorMatcherTasks CMakeBuildTargetFilter::matchers()
+{
+ return cmakeMatchers(&buildAcceptor);
+}
+
// --------------------------------------------------------------------
// OpenCMakeTargetLocatorFilter:
// --------------------------------------------------------------------
-OpenCMakeTargetLocatorFilter::OpenCMakeTargetLocatorFilter()
+CMakeOpenTargetFilter::CMakeOpenTargetFilter()
{
setId("Open CMake target definition");
- setDisplayName(Tr::tr("Open CMake target"));
- setDescription(Tr::tr("Jumps to the definition of a target of any open CMake project."));
+ setDisplayName(Tr::tr("Open CMake Target"));
+ setDescription(Tr::tr("Locates the definition of a target of any open CMake project."));
setDefaultShortcutString("cmo");
setPriority(Medium);
+ setupFilter(this);
}
-void OpenCMakeTargetLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+Core::LocatorMatcherTasks CMakeOpenTargetFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
-
- const QVariantMap extraData = selection.internalData.toMap();
- const int line = extraData.value("line").toInt();
- const auto file = FilePath::fromVariant(extraData.value("file"));
-
- if (line >= 0)
- Core::EditorManager::openEditorAt({file, line},
- {},
- Core::EditorManager::AllowExternalEditor);
- else
- Core::EditorManager::openEditor(file, {}, Core::EditorManager::AllowExternalEditor);
+ return cmakeMatchers({});
}
-} // CMakeProjectManager::Internal
+} // namespace CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h
index 0a74eeafeb..73e0a9fe4a 100644
--- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h
+++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.h
@@ -7,41 +7,22 @@
namespace CMakeProjectManager::Internal {
-class CMakeTargetLocatorFilter : public Core::ILocatorFilter
+class CMakeBuildTargetFilter : Core::ILocatorFilter
{
public:
- CMakeTargetLocatorFilter();
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) final;
+ CMakeBuildTargetFilter();
private:
- void projectListUpdated();
-
- QList<Core::LocatorFilterEntry> m_result;
-};
-
-class BuildCMakeTargetLocatorFilter : CMakeTargetLocatorFilter
-{
-public:
- BuildCMakeTargetLocatorFilter();
-
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+ Core::LocatorMatcherTasks matchers() final;
};
-class OpenCMakeTargetLocatorFilter : CMakeTargetLocatorFilter
+class CMakeOpenTargetFilter : Core::ILocatorFilter
{
public:
- OpenCMakeTargetLocatorFilter();
+ CMakeOpenTargetFilter();
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-} // CMakeProjectManager::Internal
+} // namespace CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
index 7a14143a4b..1455cbf88b 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp
@@ -14,8 +14,8 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
using namespace Core;
@@ -38,7 +38,7 @@ CMakeProcess::~CMakeProcess()
m_parser.flush();
}
-static const int failedToStartExitCode = 0xFF; // See QtcProcessPrivate::handleDone() impl
+static const int failedToStartExitCode = 0xFF; // See ProcessPrivate::handleDone() impl
void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &arguments)
{
@@ -67,8 +67,8 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
return;
}
- const FilePath sourceDirectory = parameters.sourceDirectory.onDevice(cmakeExecutable);
- const FilePath buildDirectory = parameters.buildDirectory.onDevice(cmakeExecutable);
+ const FilePath sourceDirectory = cmakeExecutable.withNewMappedPath(parameters.sourceDirectory);
+ const FilePath buildDirectory = parameters.buildDirectory;
if (!buildDirectory.exists()) {
const QString msg = ::CMakeProjectManager::Tr::tr(
@@ -106,7 +106,7 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
// Always use the sourceDir: If we are triggered because the build directory is getting deleted
// then we are racing against CMakeCache.txt also getting deleted.
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setWorkingDirectory(buildDirectory);
m_process->setEnvironment(parameters.environment);
@@ -120,7 +120,7 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
BuildSystem::appendBuildSystemOutput(stripTrailingNewline(s));
});
- connect(m_process.get(), &QtcProcess::done, this, [this] {
+ connect(m_process.get(), &Process::done, this, [this] {
handleProcessDone(m_process->resultData());
});
diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.h b/src/plugins/cmakeprojectmanager/cmakeprocess.h
index d13b0efabe..bb365d337c 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprocess.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprocess.h
@@ -13,7 +13,7 @@
namespace Utils {
class ProcessResultData;
-class QtcProcess;
+class Process;
}
namespace CMakeProjectManager::Internal {
@@ -37,7 +37,7 @@ signals:
private:
void handleProcessDone(const Utils::ProcessResultData &resultData);
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
Utils::OutputFormatter m_parser;
QElapsedTimer m_elapsed;
};
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
index ae6e26f9e3..610fd86ec2 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
@@ -7,16 +7,17 @@
#include "cmakeprojectconstants.h"
#include "cmakeprojectimporter.h"
#include "cmakeprojectmanagertr.h"
-#include "cmaketool.h"
#include <coreplugin/icontext.h>
#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
+#include <qtsupport/qtkitinformation.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -34,7 +35,11 @@ CMakeProject::CMakeProject(const FilePath &fileName)
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
setDisplayName(projectDirectory().fileName());
setCanBuildProducts();
- setHasMakeInstallEquivalent(true);
+
+ // This only influences whether 'Install into temporary host directory'
+ // will show up by default enabled in some remote deploy configurations.
+ // We rely on staging via the actual cmake build step.
+ setHasMakeInstallEquivalent(false);
readPresets();
}
@@ -62,7 +67,7 @@ Tasks CMakeProject::projectIssues(const Kit *k) const
ProjectImporter *CMakeProject::projectImporter() const
{
if (!m_projectImporter)
- m_projectImporter = new CMakeProjectImporter(projectFilePath(), m_presetsData);
+ m_projectImporter = new CMakeProjectImporter(projectFilePath(), this);
return m_projectImporter;
}
@@ -88,6 +93,14 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP
result.version = cmakePresetsData.version;
result.cmakeMinimimRequired = cmakePresetsData.cmakeMinimimRequired;
+ result.include = cmakePresetsData.include;
+ if (result.include) {
+ if (cmakeUserPresetsData.include)
+ result.include->append(cmakeUserPresetsData.include.value());
+ } else {
+ result.include = cmakeUserPresetsData.include;
+ }
+
auto combinePresetsInternal = [](auto &presetsHash,
auto &presets,
auto &userPresets,
@@ -280,4 +293,28 @@ ProjectExplorer::DeploymentKnowledge CMakeProject::deploymentKnowledge() const
: DeploymentKnowledge::Bad;
}
+void CMakeProject::configureAsExampleProject(ProjectExplorer::Kit *kit)
+{
+ QList<BuildInfo> infoList;
+ const QList<Kit *> kits(kit != nullptr ? QList<Kit *>({kit}) : KitManager::kits());
+ for (Kit *k : kits) {
+ if (QtSupport::QtKitAspect::qtVersion(k) != nullptr) {
+ if (auto factory = BuildConfigurationFactory::find(k, projectFilePath()))
+ infoList << factory->allAvailableSetups(k, projectFilePath());
+ }
+ }
+ setup(infoList);
+}
+
+void CMakeProjectManager::CMakeProject::setOldPresetKits(
+ const QList<ProjectExplorer::Kit *> &presetKits) const
+{
+ m_oldPresetKits = presetKits;
+}
+
+QList<Kit *> CMakeProject::oldPresetKits() const
+{
+ return m_oldPresetKits;
+}
+
} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h
index 2885b9eb4b..6540dd4964 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.h
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.h
@@ -31,16 +31,22 @@ public:
Internal::PresetsData presetsData() const;
void readPresets();
+ void setOldPresetKits(const QList<ProjectExplorer::Kit *> &presetKits) const;
+ QList<ProjectExplorer::Kit *> oldPresetKits() const;
+
protected:
bool setupTarget(ProjectExplorer::Target *t) final;
private:
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
+ void configureAsExampleProject(ProjectExplorer::Kit *kit) override;
+
Internal::PresetsData combinePresets(Internal::PresetsData &cmakePresetsData,
Internal::PresetsData &cmakeUserPresetsData);
void setupBuildPresets(Internal::PresetsData &presetsData);
mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr;
+ mutable QList<ProjectExplorer::Kit*> m_oldPresetKits;
ProjectExplorer::Tasks m_issues;
Internal::PresetsData m_presetsData;
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
index cbaa648726..183e950136 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h
@@ -17,6 +17,7 @@ const char BUILD_FILE_CONTEXT_MENU[] = "CMakeProject.BuildFileContextMenu";
const char BUILD_FILE[] = "CMakeProject.BuildFile";
const char CMAKE_HOME_DIR[] = "CMakeProject.HomeDirectory";
const char QML_DEBUG_SETTING[] = "CMakeProject.EnableQmlDebugging";
+const char RELOAD_CMAKE_PRESETS[] = "CMakeProject.ReloadCMakePresets";
const char CMAKEFORMATTER_SETTINGS_GROUP[] = "CMakeFormatter";
const char CMAKEFORMATTER_GENERAL_GROUP[] = "General";
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
index dc752f0a9f..029182a053 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp
@@ -5,6 +5,7 @@
#include "cmakebuildconfiguration.h"
#include "cmakekitinformation.h"
+#include "cmakeproject.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmaketoolmanager.h"
@@ -15,13 +16,14 @@
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
#include <projectexplorer/toolchainmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -46,7 +48,6 @@ struct DirectoryData
QString cmakePresetDisplayname;
QString cmakePreset;
- QByteArray cmakePresetDefaultConfigHash;
// Kit Stuff
FilePath cmakeBinary;
@@ -93,9 +94,9 @@ static QString uniqueCMakeToolDisplayName(CMakeTool &tool)
// CMakeProjectImporter
-CMakeProjectImporter::CMakeProjectImporter(const FilePath &path, const PresetsData &presetsData)
+CMakeProjectImporter::CMakeProjectImporter(const FilePath &path, const CMakeProject *project)
: QtProjectImporter(path)
- , m_presetsData(presetsData)
+ , m_project(project)
, m_presetsTempDir("qtc-cmake-presets-XXXXXXXX")
{
useTemporaryKitAspect(CMakeKitAspect::id(),
@@ -120,7 +121,7 @@ FilePaths CMakeProjectImporter::importCandidates()
candidates << scanDirectory(shadowBuildDirectory.absolutePath(), QString());
}
- for (const auto &configPreset : m_presetsData.configurePresets) {
+ for (const auto &configPreset : m_project->presetsData().configurePresets) {
if (configPreset.hidden.value())
continue;
@@ -153,6 +154,21 @@ FilePaths CMakeProjectImporter::importCandidates()
return finalists;
}
+Target *CMakeProjectImporter::preferredTarget(const QList<Target *> &possibleTargets)
+{
+ for (Kit *kit : m_project->oldPresetKits()) {
+ const bool haveKit = Utils::contains(possibleTargets, [kit](const auto &target) {
+ return target->kit() == kit;
+ });
+
+ if (!haveKit)
+ KitManager::deregisterKit(kit);
+ }
+ m_project->setOldPresetKits({});
+
+ return ProjectImporter::preferredTarget(possibleTargets);
+}
+
static CMakeConfig configurationFromPresetProbe(
const FilePath &importPath,
const FilePath &sourceDirectory,
@@ -164,7 +180,7 @@ static CMakeConfig configurationFromPresetProbe(
"project(preset-probe)\n"
"\n"));
- QtcProcess cmake;
+ Process cmake;
cmake.setTimeoutS(30);
cmake.setDisableUnixTerminal();
@@ -247,81 +263,107 @@ static CMakeConfig configurationFromPresetProbe(
return config;
}
-static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
+struct QMakeAndCMakePrefixPath
+{
+ FilePath qmakePath;
+ QString cmakePrefixPath; // can be a semicolon-separated list
+};
+
+static QMakeAndCMakePrefixPath qtInfoFromCMakeCache(const CMakeConfig &config,
+ const Environment &env)
{
// Qt4 way to define things (more convenient for us, so try this first;-)
const FilePath qmake = config.filePathValueOf("QT_QMAKE_EXECUTABLE");
qCDebug(cmInputLog) << "QT_QMAKE_EXECUTABLE=" << qmake.toUserOutput();
- if (!qmake.isEmpty())
- return qmake;
// Check Qt5 settings: oh, the horror!
- const FilePath qtCMakeDir = [config] {
- FilePath tmp = config.filePathValueOf("Qt5Core_DIR");
- if (tmp.isEmpty())
- tmp = config.filePathValueOf("Qt6Core_DIR");
+ const FilePath qtCMakeDir = [config, env] {
+ FilePath tmp;
+ // Check the CMake "<package-name>_DIR" variable
+ for (const auto &var : {"Qt6", "Qt6Core", "Qt5", "Qt5Core"}) {
+ tmp = config.filePathValueOf(QByteArray(var) + "_DIR");
+ if (!tmp.isEmpty())
+ break;
+ }
return tmp;
}();
qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput();
const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath());
qCInfo(cmInputLog) << "QtXCore_DIR (canonical)=" << canQtCMakeDir.toUserOutput();
- QString prefixPath;
- if (!qtCMakeDir.isEmpty()) {
- prefixPath = canQtCMakeDir.parentDir().parentDir().parentDir().toString(); // Up 3 levels...
- } else {
- prefixPath = config.stringValueOf("CMAKE_PREFIX_PATH");
- }
+
+ const QString prefixPath = [qtCMakeDir, canQtCMakeDir, config, env] {
+ QString result;
+ if (!qtCMakeDir.isEmpty()) {
+ result = canQtCMakeDir.parentDir().parentDir().parentDir().path(); // Up 3 levels...
+ } else {
+ // Check the CMAKE_PREFIX_PATH and "<package-name>_ROOT" CMake or environment variables
+ // This can be a single value or a semicolon-separated list
+ for (const auto &var : {"CMAKE_PREFIX_PATH", "Qt6_ROOT", "Qt5_ROOT"}) {
+ result = config.stringValueOf(var);
+ if (result.isEmpty())
+ result = env.value(QString::fromUtf8(var));
+ if (!result.isEmpty())
+ break;
+ }
+ }
+ return result;
+ }();
qCDebug(cmInputLog) << "PrefixPath:" << prefixPath;
+ if (!qmake.isEmpty() && !prefixPath.isEmpty())
+ return {qmake, prefixPath};
+
FilePath toolchainFile = config.filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"));
if (prefixPath.isEmpty() && toolchainFile.isEmpty())
- return FilePath();
+ return {qmake, QString()};
// Run a CMake project that would do qmake probing
TemporaryDirectory qtcQMakeProbeDir("qtc-cmake-qmake-probe-XXXXXXXX");
- QFile cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt").toString());
- if (!cmakeListTxt.open(QIODevice::WriteOnly)) {
- return FilePath();
- }
- // FIXME replace by raw string when gcc 8+ is minimum
- cmakeListTxt.write(QByteArray(
-"cmake_minimum_required(VERSION 3.15)\n"
-"\n"
-"project(qmake-probe LANGUAGES NONE)\n"
-"\n"
-"# Bypass Qt6's usage of find_dependency, which would require compiler\n"
-"# and source code probing, which slows things unnecessarily\n"
-"file(WRITE \"${CMAKE_SOURCE_DIR}/CMakeFindDependencyMacro.cmake\"\n"
-"[=["
-" macro(find_dependency dep)\n"
-" endmacro()\n"
-"]=])\n"
-"set(CMAKE_MODULE_PATH \"${CMAKE_SOURCE_DIR}\")\n"
-"\n"
-"find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)\n"
-"find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)\n"
-"\n"
-"if (CMAKE_CROSSCOMPILING)\n"
-" find_program(qmake_binary\n"
-" NAMES qmake qmake.bat\n"
-" PATHS \"${Qt${QT_VERSION_MAJOR}_DIR}/../../../bin\"\n"
-" NO_DEFAULT_PATH)\n"
-" file(WRITE \"${CMAKE_SOURCE_DIR}/qmake-location.txt\" \"${qmake_binary}\")\n"
-"else()\n"
-" file(GENERATE\n"
-" OUTPUT \"${CMAKE_SOURCE_DIR}/qmake-location.txt\"\n"
-" CONTENT \"$<TARGET_PROPERTY:Qt${QT_VERSION_MAJOR}::qmake,IMPORTED_LOCATION>\")\n"
-"endif()\n"
-));
- cmakeListTxt.close();
-
- QtcProcess cmake;
+ FilePath cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt"));
+
+ cmakeListTxt.writeFileContents(QByteArray(R"(
+ cmake_minimum_required(VERSION 3.15)
+
+ project(qmake-probe LANGUAGES NONE)
+
+ # Bypass Qt6's usage of find_dependency, which would require compiler
+ # and source code probing, which slows things unnecessarily
+ file(WRITE "${CMAKE_SOURCE_DIR}/CMakeFindDependencyMacro.cmake"
+ [=[
+ macro(find_dependency dep)
+ endmacro()
+ ]=])
+ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
+
+ find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
+
+ if (CMAKE_CROSSCOMPILING)
+ find_program(qmake_binary
+ NAMES qmake qmake.bat
+ PATHS "${Qt${QT_VERSION_MAJOR}_DIR}/../../../bin"
+ NO_DEFAULT_PATH)
+ file(WRITE "${CMAKE_SOURCE_DIR}/qmake-location.txt" "${qmake_binary}")
+ else()
+ file(GENERATE
+ OUTPUT "${CMAKE_SOURCE_DIR}/qmake-location.txt"
+ CONTENT "$<TARGET_PROPERTY:Qt${QT_VERSION_MAJOR}::qmake,IMPORTED_LOCATION>")
+ endif()
+
+ # Remove a Qt CMake hack that adds lib/cmake at the end of every path in CMAKE_PREFIX_PATH
+ list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH)
+ list(TRANSFORM CMAKE_PREFIX_PATH REPLACE "/lib/cmake$" "")
+ file(WRITE "${CMAKE_SOURCE_DIR}/cmake-prefix-path.txt" "${CMAKE_PREFIX_PATH}")
+ )"));
+
+ Process cmake;
cmake.setTimeoutS(5);
cmake.setDisableUnixTerminal();
- Environment env = Environment::systemEnvironment();
- env.setupEnglishOutput();
- cmake.setEnvironment(env);
+
+ Environment cmakeEnv(env);
+ cmakeEnv.setupEnglishOutput();
+ cmake.setEnvironment(cmakeEnv);
cmake.setTimeOutMessageBoxEnabled(false);
QString cmakeGenerator = config.stringValueOf(QByteArray("CMAKE_GENERATOR"));
@@ -365,14 +407,17 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
cmake.setCommand({cmakeExecutable, args});
cmake.runBlocking();
- QFile qmakeLocationTxt(qtcQMakeProbeDir.filePath("qmake-location.txt").path());
- if (!qmakeLocationTxt.open(QIODevice::ReadOnly)) {
- return FilePath();
- }
- FilePath qmakeLocation = FilePath::fromUtf8(qmakeLocationTxt.readLine().constData());
+ const FilePath qmakeLocationTxt = qtcQMakeProbeDir.filePath("qmake-location.txt");
+ const FilePath qmakeLocation = FilePath::fromUtf8(
+ qmakeLocationTxt.fileContents().value_or(QByteArray()));
qCDebug(cmInputLog) << "qmake location: " << qmakeLocation.toUserOutput();
- return qmakeLocation;
+ const FilePath prefixPathTxt = qtcQMakeProbeDir.filePath("cmake-prefix-path.txt");
+ const QString resultedPrefixPath = QString::fromUtf8(
+ prefixPathTxt.fileContents().value_or(QByteArray()));
+ qCDebug(cmInputLog) << "PrefixPath [after qmake probe]: " << resultedPrefixPath;
+
+ return {qmakeLocation, resultedPrefixPath};
}
static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfig &config)
@@ -590,7 +635,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
const QString presetName = importPath.fileName();
PresetsDetails::ConfigurePreset configurePreset
- = Utils::findOrDefault(m_presetsData.configurePresets,
+ = Utils::findOrDefault(m_project->presetsData().configurePresets,
[presetName](const PresetsDetails::ConfigurePreset &preset) {
return preset.name == presetName;
});
@@ -687,19 +732,21 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
configurePreset.generator.value().toUtf8());
}
- const FilePath qmake = qmakeFromCMakeCache(config);
+ const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
+ if (!cmakePrefixPath.isEmpty() && config.valueOf("CMAKE_PREFIX_PATH").isEmpty())
+ config << CMakeConfigItem("CMAKE_PREFIX_PATH",
+ CMakeConfigItem::PATH,
+ cmakePrefixPath.toUtf8());
+
// ToolChains:
data->toolChains = extractToolChainsFromCache(config);
// Update QT_QMAKE_EXECUTABLE and CMAKE_C|XX_COMPILER config values
updateConfigWithDirectoryData(config, data);
- data->cmakePresetDefaultConfigHash
- = CMakeConfigurationKitAspect::computeDefaultConfigHash(config, data->cmakeBinary);
-
QByteArrayList buildConfigurationTypes = {cache.valueOf("CMAKE_BUILD_TYPE")};
if (buildConfigurationTypes.front().isEmpty()) {
buildConfigurationTypes.clear();
@@ -747,6 +794,8 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
buildConfigurationTypes = buildConfigurationTypesString.split(';');
}
+ const Environment env = projectDirectory().deviceEnvironment();
+
for (auto const &buildType: std::as_const(buildConfigurationTypes)) {
auto data = std::make_unique<DirectoryData>();
@@ -778,7 +827,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
data->sysroot = config.filePathValueOf("CMAKE_SYSROOT");
// Qt:
- const FilePath qmake = qmakeFromCMakeCache(config);
+ const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
@@ -826,36 +875,50 @@ bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const
if (data->qt.qt && QtSupport::QtKitAspect::qtVersionId(k) != data->qt.qt->uniqueId())
return false;
+ const bool compilersMatch = [k, data] {
+ const QList<Id> allLanguages = ToolChainManager::allLanguages();
+ for (const ToolChainDescription &tcd : data->toolChains) {
+ if (!Utils::contains(allLanguages,
+ [&tcd](const Id &language) { return language == tcd.language; }))
+ continue;
+ ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
+ if ((!tc || !tc->matchesCompilerCommand(tcd.compilerPath))) {
+ return false;
+ }
+ }
+ return true;
+ }();
+ const bool noCompilers = [k, data] {
+ const QList<Id> allLanguages = ToolChainManager::allLanguages();
+ for (const ToolChainDescription &tcd : data->toolChains) {
+ if (!Utils::contains(allLanguages,
+ [&tcd](const Id &language) { return language == tcd.language; }))
+ continue;
+ ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
+ if (tc && tc->matchesCompilerCommand(tcd.compilerPath)) {
+ return false;
+ }
+ }
+ return true;
+ }();
+
bool haveCMakePreset = false;
if (!data->cmakePreset.isEmpty()) {
const auto presetConfigItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(k);
- const auto kitConfigHashItem = CMakeConfigurationKitAspect::kitDefaultConfigHashItem(k);
const QString presetName = presetConfigItem.expandedValue(k);
- const bool haveSameKitConfigHash = kitConfigHashItem.isNull()
- ? true
- : data->cmakePresetDefaultConfigHash
- == kitConfigHashItem.value;
-
- if (data->cmakePreset != presetName || !haveSameKitConfigHash)
+ if (data->cmakePreset != presetName)
return false;
ensureBuildDirectory(*data, k);
haveCMakePreset = true;
}
- const QList<Id> allLanguages = ToolChainManager::allLanguages();
- for (const ToolChainDescription &tcd : data->toolChains) {
- if (!Utils::contains(allLanguages, [&tcd](const Id& language) {return language == tcd.language;}))
- continue;
- ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
- if ((!tc || !tc->matchesCompilerCommand(tcd.compilerPath)) && !haveCMakePreset) {
- return false;
- }
- }
+ if (!compilersMatch && !(haveCMakePreset && noCompilers))
+ return false;
qCDebug(cmInputLog) << k->displayName()
- << "matches directoryData for" << data->buildDirectory.toUserOutput();
+ << "matches directoryData for" << data->buildDirectory.toUserOutput();
return true;
}
@@ -894,7 +957,6 @@ Kit *CMakeProjectImporter::createKit(void *directoryData) const
QString("%1 (CMake preset)").arg(data->cmakePresetDisplayname));
CMakeConfigurationKitAspect::setCMakePreset(k, data->cmakePreset);
- CMakeConfigurationKitAspect::setKitDefaultConfigHash(k);
}
if (!data->cmakePreset.isEmpty())
ensureBuildDirectory(*data, k);
@@ -1022,8 +1084,9 @@ void CMakeProjectPlugin::testCMakeProjectImporterQt()
config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8()));
}
- FilePath realQmake = qmakeFromCMakeCache(config);
- QCOMPARE(realQmake.toString(), expectedQmake);
+ auto [realQmake, cmakePrefixPath] = qtInfoFromCMakeCache(config,
+ Environment::systemEnvironment());
+ QCOMPARE(realQmake.path(), expectedQmake);
}
void CMakeProjectPlugin::testCMakeProjectImporterToolChain_data()
{
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h
index 7c7716dc8b..350b0b77d6 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.h
@@ -4,12 +4,14 @@
#pragma once
#include "presetsparser.h"
-#include "utils/temporarydirectory.h"
#include <qtsupport/qtprojectimporter.h>
+#include <utils/temporarydirectory.h>
+
namespace CMakeProjectManager {
+class CMakeProject;
class CMakeTool;
namespace Internal {
@@ -19,9 +21,11 @@ struct DirectoryData;
class CMakeProjectImporter : public QtSupport::QtProjectImporter
{
public:
- CMakeProjectImporter(const Utils::FilePath &path, const Internal::PresetsData &presetsData);
+ CMakeProjectImporter(const Utils::FilePath &path,
+ const CMakeProjectManager::CMakeProject *project);
Utils::FilePaths importCandidates() final;
+ ProjectExplorer::Target *preferredTarget(const QList<ProjectExplorer::Target *> &possibleTargets) final;
private:
QList<void *> examineDirectory(const Utils::FilePath &importPath,
@@ -43,7 +47,7 @@ private:
void ensureBuildDirectory(DirectoryData &data, const ProjectExplorer::Kit *k) const;
- Internal::PresetsData m_presetsData;
+ const CMakeProject *m_project;
Utils::TemporaryDirectory m_presetsTempDir;
};
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
index e05e155cf6..a1950b6bcc 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp
@@ -9,6 +9,7 @@
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmakeprojectnodes.h"
+#include "cmakespecificsettings.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -16,14 +17,19 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <coreplugin/modemanager.h>
+
#include <cppeditor/cpptoolsreuse.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
+#include <utils/checkablemessagebox.h>
+#include <utils/utilsicons.h>
#include <utils/parameteraction.h>
#include <QAction>
@@ -40,6 +46,8 @@ CMakeManager::CMakeManager()
, m_clearCMakeCacheAction(new QAction(QIcon(), Tr::tr("Clear CMake Configuration"), this))
, m_runCMakeActionContextMenu(new QAction(QIcon(), Tr::tr("Run CMake"), this))
, m_rescanProjectAction(new QAction(QIcon(), Tr::tr("Rescan Project"), this))
+ , m_reloadCMakePresetsAction(
+ new QAction(Utils::Icons::RELOAD_TOOLBAR.icon(), Tr::tr("Reload CMake Presets"), this))
{
Core::ActionContainer *mbuild =
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
@@ -59,7 +67,7 @@ CMakeManager::CMakeManager()
command->setAttribute(Core::Command::CA_Hide);
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
connect(m_runCMakeAction, &QAction::triggered, this, [this] {
- runCMake(SessionManager::startupBuildSystem());
+ runCMake(ProjectManager::startupBuildSystem());
});
command = Core::ActionManager::registerAction(m_clearCMakeCacheAction,
@@ -68,7 +76,7 @@ CMakeManager::CMakeManager()
command->setAttribute(Core::Command::CA_Hide);
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
connect(m_clearCMakeCacheAction, &QAction::triggered, this, [this] {
- clearCMakeCache(SessionManager::startupBuildSystem());
+ clearCMakeCache(ProjectManager::startupBuildSystem());
});
command = Core::ActionManager::registerAction(m_runCMakeActionContextMenu,
@@ -99,6 +107,15 @@ CMakeManager::CMakeManager()
rescanProject(ProjectTree::currentBuildSystem());
});
+ command = Core::ActionManager::registerAction(m_reloadCMakePresetsAction,
+ Constants::RELOAD_CMAKE_PRESETS,
+ globalContext);
+ command->setAttribute(Core::Command::CA_Hide);
+ mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
+ connect(m_reloadCMakePresetsAction, &QAction::triggered, this, [this] {
+ reloadCMakePresets();
+ });
+
m_buildFileAction = new Utils::ParameterAction(Tr::tr("Build File"),
Tr::tr("Build File \"%1\""),
Utils::ParameterAction::AlwaysEnabled,
@@ -111,7 +128,7 @@ CMakeManager::CMakeManager()
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD);
connect(m_buildFileAction, &QAction::triggered, this, [this] { buildFile(); });
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged, this, [this] {
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, [this] {
updateCmakeActions(ProjectTree::currentNode());
});
connect(BuildManager::instance(), &BuildManager::buildStateChanged, this, [this] {
@@ -127,12 +144,22 @@ CMakeManager::CMakeManager()
void CMakeManager::updateCmakeActions(Node *node)
{
- auto project = qobject_cast<CMakeProject *>(SessionManager::startupProject());
+ auto project = qobject_cast<CMakeProject *>(ProjectManager::startupProject());
const bool visible = project && !BuildManager::isBuilding(project);
m_runCMakeAction->setVisible(visible);
m_runCMakeActionContextMenu->setEnabled(visible);
m_clearCMakeCacheAction->setVisible(visible);
m_rescanProjectAction->setVisible(visible);
+
+ const bool reloadPresetsVisible = [project] {
+ if (!project)
+ return false;
+ const FilePath presetsPath = project->projectFilePath().parentDir().pathAppended(
+ "CMakePresets.json");
+ return presetsPath.exists();
+ }();
+ m_reloadCMakePresetsAction->setVisible(reloadPresetsVisible);
+
enableBuildFileMenus(node);
}
@@ -202,6 +229,62 @@ void CMakeManager::enableBuildFileMenus(Node *node)
}
}
+void CMakeManager::reloadCMakePresets()
+{
+ auto settings = CMakeSpecificSettings::instance();
+
+ QMessageBox::StandardButton clickedButton
+ = CheckableMessageBox::question(Core::ICore::dialogParent(),
+ Tr::tr("Reload CMake Presets"),
+ Tr::tr("Re-generates the CMake presets kits. The manual "
+ "CMake project modifications will be lost."),
+ settings->askBeforePresetsReload,
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Yes,
+ QMessageBox::Yes,
+ {
+ {QMessageBox::Yes, Tr::tr("Reload")},
+ });
+
+ if (clickedButton == QMessageBox::Cancel)
+ return;
+
+ CMakeProject *project = static_cast<CMakeProject *>(ProjectTree::currentProject());
+ if (!project)
+ return;
+
+ const QSet<QString> oldPresets = Utils::transform<QSet>(project->presetsData().configurePresets,
+ [](const auto &preset) {
+ return preset.name;
+ });
+ project->readPresets();
+
+ QList<Kit*> oldKits;
+ for (const auto &target : project->targets()) {
+ const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem(
+ target->kit());
+
+ if (BuildManager::isBuilding(target))
+ BuildManager::cancel();
+
+ // Only clear the CMake configuration for preset kits. Any manual kit configuration
+ // will get the chance to get imported afterwards in the Kit selection wizard
+ CMakeBuildSystem *bs = static_cast<CMakeBuildSystem *>(target->buildSystem());
+ if (!presetItem.isNull() && bs)
+ bs->clearCMakeCache();
+
+ if (!presetItem.isNull() && oldPresets.contains(QString::fromUtf8(presetItem.value)))
+ oldKits << target->kit();
+
+ project->removeTarget(target);
+ }
+
+ project->setOldPresetKits(oldKits);
+
+ Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION);
+ Core::ModeManager::setFocusToCurrentMode();
+}
+
void CMakeManager::buildFile(Node *node)
{
if (!node) {
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
index 3a52709291..c47d724c9b 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h
@@ -30,12 +30,14 @@ private:
void buildFile(ProjectExplorer::Node *node = nullptr);
void updateBuildFileAction();
void enableBuildFileMenus(ProjectExplorer::Node *node);
+ void reloadCMakePresets();
QAction *m_runCMakeAction;
QAction *m_clearCMakeCacheAction;
QAction *m_runCMakeActionContextMenu;
QAction *m_rescanProjectAction;
QAction *m_buildFileContextMenu;
+ QAction *m_reloadCMakePresetsAction;
Utils::ParameterAction *m_buildFileAction;
};
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
index 89e449ae40..79d8c219f2 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
@@ -35,10 +35,6 @@ QtcPlugin {
"cmakefilecompletionassist.h",
"cmakeformatter.cpp",
"cmakeformatter.h",
- "cmakeformatteroptionspage.cpp",
- "cmakeformatteroptionspage.h",
- "cmakeformattersettings.cpp",
- "cmakeformattersettings.h",
"cmakeinstallstep.cpp",
"cmakeinstallstep.h",
"cmakekitinformation.h",
@@ -93,4 +89,18 @@ QtcPlugin {
"projecttreehelper.cpp",
"projecttreehelper.h"
]
+
+ Group {
+ name: "3rdparty"
+ cpp.includePaths: base.concat("3rdparty/cmake")
+
+ prefix: "3rdparty/cmake/"
+ files: [
+ "cmListFileCache.cxx",
+ "cmListFileCache.h",
+ "cmListFileLexer.c",
+ "cmListFileLexer.h",
+ "cmStandardLexer.h",
+ ]
+ }
}
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
index 9c0d687ca6..b26f9a70b5 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
@@ -30,6 +30,15 @@ CMakeInputsNode::CMakeInputsNode(const FilePath &cmakeLists) :
setListInProject(false);
}
+CMakePresetsNode::CMakePresetsNode(const FilePath &projectPath) :
+ ProjectExplorer::ProjectNode(projectPath)
+{
+ setPriority(Node::DefaultPriority - 9);
+ setDisplayName(Tr::tr("CMake Presets"));
+ setIcon(DirectoryIcon(ProjectExplorer::Constants::FILEOVERLAY_PRODUCT));
+ setListInProject(false);
+}
+
CMakeListsNode::CMakeListsNode(const FilePath &cmakeListPath) :
ProjectExplorer::ProjectNode(cmakeListPath)
{
@@ -198,6 +207,10 @@ void CMakeTargetNode::setTargetInformation(const QList<FilePath> &artifacts, con
m_tooltip += Tr::tr("Build artifacts:") + "<br>" + tmp.join("<br>");
m_artifact = artifacts.first();
}
+ if (type == "EXECUTABLE")
+ setProductType(ProductType::App);
+ else if (type == "SHARED_LIBRARY" || type == "STATIC_LIBRARY")
+ setProductType(ProductType::Lib);
}
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h
index 5d248020ab..94cde6dfef 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h
@@ -15,6 +15,12 @@ public:
CMakeInputsNode(const Utils::FilePath &cmakeLists);
};
+class CMakePresetsNode : public ProjectExplorer::ProjectNode
+{
+public:
+ CMakePresetsNode(const Utils::FilePath &projectPath);
+};
+
class CMakeListsNode : public ProjectExplorer::ProjectNode
{
public:
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
index bb0270d16e..39e3ec2926 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp
@@ -8,7 +8,6 @@
#include "cmakebuildsystem.h"
#include "cmakeeditor.h"
#include "cmakeformatter.h"
-#include "cmakeformattersettings.h"
#include "cmakeinstallstep.h"
#include "cmakekitinformation.h"
#include "cmakelocatorfilter.h"
@@ -23,7 +22,6 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
-#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
@@ -44,27 +42,9 @@ using namespace Utils;
namespace CMakeProjectManager::Internal {
-bool isAutoFormatApplicable(const Core::IDocument *document, const QStringList &allowedMimeTypes)
-{
- if (!document)
- return false;
-
- if (allowedMimeTypes.isEmpty())
- return true;
-
- const Utils::MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
- return Utils::anyOf(allowedMimeTypes, [&documentMimeType](const QString &mime) {
- return documentMimeType.inherits(mime);
- });
-}
-
class CMakeProjectPluginPrivate : public QObject
{
public:
- CMakeProjectPluginPrivate();
- void updateActions(Core::IEditor *editor = nullptr);
- void autoFormatOnSave(Core::IDocument *document);
-
CMakeToolManager cmakeToolManager; // have that before the first CMakeKitAspect
ParameterAction buildTargetContextAction{
@@ -74,15 +54,15 @@ public:
};
CMakeSettingsPage settingsPage;
- CMakeSpecificSettingsPage specificSettings;
+ CMakeSpecificSettings specificSettings;
CMakeManager manager;
CMakeBuildStepFactory buildStepFactory;
CMakeBuildConfigurationFactory buildConfigFactory;
CMakeEditorFactory editorFactor;
CMakeInstallStepFactory installStepFactory;
- BuildCMakeTargetLocatorFilter buildCMakeTargetLocatorFilter;
- OpenCMakeTargetLocatorFilter openCMakeTargetLocationFilter;
+ CMakeBuildTargetFilter cMakeBuildTargetFilter;
+ CMakeOpenTargetFilter cMakeOpenTargetFilter;
CMakeKitAspect cmakeKitAspect;
CMakeGeneratorKitAspect cmakeGeneratorKitAspect;
@@ -91,53 +71,6 @@ public:
CMakeFormatter cmakeFormatter;
};
-CMakeProjectPluginPrivate::CMakeProjectPluginPrivate()
-{
- const Core::EditorManager *editorManager = Core::EditorManager::instance();
- QObject::connect(editorManager, &Core::EditorManager::currentEditorChanged,
- this, &CMakeProjectPluginPrivate::updateActions);
- QObject::connect(editorManager, &Core::EditorManager::aboutToSave,
- this, &CMakeProjectPluginPrivate::autoFormatOnSave);
-}
-
-void CMakeProjectPluginPrivate::updateActions(Core::IEditor *editor)
-{
- cmakeFormatter.updateActions(editor);
-}
-
-void CMakeProjectPluginPrivate::autoFormatOnSave(Core::IDocument *document)
-{
- if (!CMakeFormatterSettings::instance()->autoFormatOnSave())
- return;
-
- if (!isAutoFormatApplicable(document, CMakeFormatterSettings::instance()->autoFormatMime()))
- return;
-
- // Check if file is contained in the current project (if wished)
- if (CMakeFormatterSettings::instance()->autoFormatOnlyCurrentProject()) {
- const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
- if (!pro || pro->files([document](const ProjectExplorer::Node *n) {
- return ProjectExplorer::Project::SourceFiles(n)
- && n->filePath() == document->filePath();
- }).isEmpty()) {
- return;
- }
- }
-
- if (!cmakeFormatter.isApplicable(document))
- return;
- const TextEditor::Command command = cmakeFormatter.command();
- if (!command.isValid())
- return;
- const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(document);
- if (editors.isEmpty())
- return;
- IEditor *currentEditor = EditorManager::currentEditor();
- IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first();
- if (auto widget = TextEditor::TextEditorWidget::fromEditor(editor))
- TextEditor::formatEditor(widget, command);
-}
-
CMakeProjectPlugin::~CMakeProjectPlugin()
{
delete d;
@@ -146,7 +79,6 @@ CMakeProjectPlugin::~CMakeProjectPlugin()
void CMakeProjectPlugin::initialize()
{
d = new CMakeProjectPluginPrivate;
- CMakeSpecificSettings::instance()->readSettings(ICore::settings());
const Context projectContext{CMakeProjectManager::Constants::CMAKE_PROJECT_ID};
@@ -179,14 +111,6 @@ void CMakeProjectPlugin::initialize()
bs->buildCMakeTarget(targetNode ? targetNode->displayName() : QString());
}
});
-
- Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);
- menu->menu()->setTitle(Tr::tr("CMakeFormatter"));
- menu->setOnAllDisabledBehavior(Core::ActionContainer::Show);
- Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
-
- d->cmakeFormatter.initialize();
- d->updateActions();
}
void CMakeProjectPlugin::extensionsInitialized()
diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
index b29351d175..7875f97ada 100644
--- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp
@@ -15,8 +15,36 @@ using namespace Utils;
namespace CMakeProjectManager::Internal {
+static CMakeSpecificSettings *theSettings;
+
+CMakeSpecificSettings *CMakeSpecificSettings::instance()
+{
+ return theSettings;
+}
+
CMakeSpecificSettings::CMakeSpecificSettings()
{
+ theSettings = this;
+
+ setId(Constants::Settings::GENERAL_ID);
+ setDisplayName(::CMakeProjectManager::Tr::tr("General"));
+ setDisplayCategory("CMake");
+ setCategory(Constants::Settings::CATEGORY);
+ setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY);
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ Column {
+ autorunCMake,
+ packageManagerAutoSetup,
+ askBeforeReConfigureInitialParams,
+ askBeforePresetsReload,
+ showSourceSubFolders,
+ showAdvancedOptionsByDefault,
+ st
+ }.attachTo(widget);
+ });
+
// TODO: fixup of QTCREATORBUG-26289 , remove in Qt Creator 7 or so
Core::ICore::settings()->remove("CMakeSpecificSettings/NinjaPath");
@@ -30,16 +58,6 @@ CMakeSpecificSettings::CMakeSpecificSettings()
autorunCMake.setToolTip(::CMakeProjectManager::Tr::tr(
"Automatically run CMake after changes to CMake project files."));
- registerAspect(&afterAddFileSetting);
- afterAddFileSetting.setSettingsKey("ProjectPopupSetting");
- afterAddFileSetting.setDefaultValue(AfterAddFileAction::AskUser);
- afterAddFileSetting.addOption(::CMakeProjectManager::Tr::tr("Ask about copying file paths"));
- afterAddFileSetting.addOption(::CMakeProjectManager::Tr::tr("Do not copy file paths"));
- afterAddFileSetting.addOption(::CMakeProjectManager::Tr::tr("Copy file paths"));
- afterAddFileSetting.setToolTip(::CMakeProjectManager::Tr::tr("Determines whether file paths are copied "
- "to the clipboard for pasting to the CMakeLists.txt file when you "
- "add new files to CMake projects."));
-
registerAspect(&ninjaPath);
ninjaPath.setSettingsKey("NinjaPath");
// never save this to the settings:
@@ -60,6 +78,11 @@ CMakeSpecificSettings::CMakeSpecificSettings()
askBeforeReConfigureInitialParams.setLabelText(::CMakeProjectManager::Tr::tr("Ask before re-configuring with "
"initial parameters"));
+ registerAspect(&askBeforePresetsReload);
+ askBeforePresetsReload.setSettingsKey("AskBeforePresetsReload");
+ askBeforePresetsReload.setDefaultValue(true);
+ askBeforePresetsReload.setLabelText(::CMakeProjectManager::Tr::tr("Ask before reloading CMake Presets"));
+
registerAspect(&showSourceSubFolders);
showSourceSubFolders.setSettingsKey("ShowSourceSubFolders");
showSourceSubFolders.setDefaultValue(true);
@@ -71,42 +94,8 @@ CMakeSpecificSettings::CMakeSpecificSettings()
showAdvancedOptionsByDefault.setDefaultValue(false);
showAdvancedOptionsByDefault.setLabelText(
::CMakeProjectManager::Tr::tr("Show advanced options by default"));
-}
-
-CMakeSpecificSettings *CMakeSpecificSettings::instance()
-{
- static CMakeSpecificSettings theSettings;
- return &theSettings;
-}
-
-// CMakeSpecificSettingsPage
-CMakeSpecificSettingsPage::CMakeSpecificSettingsPage()
-{
- CMakeSpecificSettings *settings = CMakeSpecificSettings::instance();
- setId(Constants::Settings::GENERAL_ID);
- setDisplayName(::CMakeProjectManager::Tr::tr("General"));
- setDisplayCategory("CMake");
- setCategory(Constants::Settings::CATEGORY);
- setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- CMakeSpecificSettings &s = *settings;
- using namespace Layouting;
- Column {
- Group {
- title(::CMakeProjectManager::Tr::tr("Adding Files")),
- Column { s.afterAddFileSetting }
- },
- s.autorunCMake,
- s.packageManagerAutoSetup,
- s.askBeforeReConfigureInitialParams,
- s.showSourceSubFolders,
- s.showAdvancedOptionsByDefault,
- st
- }.attachTo(widget);
- });
+ readSettings();
}
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
index d0b19bee42..472945e1a5 100644
--- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
+++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h
@@ -5,17 +5,9 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace CMakeProjectManager::Internal {
-enum AfterAddFileAction : int {
- AskUser,
- CopyFilePath,
- NeverCopyFilePath
-};
-
-class CMakeSpecificSettings final : public Utils::AspectContainer
+class CMakeSpecificSettings final : public Core::PagedSettings
{
public:
CMakeSpecificSettings();
@@ -23,18 +15,12 @@ public:
static CMakeSpecificSettings *instance();
Utils::BoolAspect autorunCMake;
- Utils::SelectionAspect afterAddFileSetting;
Utils::StringAspect ninjaPath;
Utils::BoolAspect packageManagerAutoSetup;
Utils::BoolAspect askBeforeReConfigureInitialParams;
+ Utils::BoolAspect askBeforePresetsReload;
Utils::BoolAspect showSourceSubFolders;
Utils::BoolAspect showAdvancedOptionsByDefault;
};
-class CMakeSpecificSettingsPage final : public Core::IOptionsPage
-{
-public:
- CMakeSpecificSettingsPage();
-};
-
} // CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index 184811884d..9239ea5417 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -10,8 +10,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QJsonDocument>
@@ -162,7 +162,7 @@ bool CMakeTool::isValid() const
return m_introspection->m_didRun && !m_introspection->m_fileApis.isEmpty();
}
-void CMakeTool::runCMake(QtcProcess &cmake, const QStringList &args, int timeoutS) const
+void CMakeTool::runCMake(Process &cmake, const QStringList &args, int timeoutS) const
{
const FilePath executable = cmakeExecutable();
cmake.setTimeoutS(timeoutS);
@@ -248,7 +248,7 @@ TextEditor::Keywords CMakeTool::keywords()
return {};
if (m_introspection->m_functions.isEmpty() && m_introspection->m_didRun) {
- QtcProcess proc;
+ Process proc;
runCMake(proc, {"--help-command-list"}, 5);
if (proc.result() == ProcessResult::FinishedWithSuccess)
m_introspection->m_functions = proc.cleanedStdOut().split('\n');
@@ -492,7 +492,7 @@ QStringList CMakeTool::parseVariableOutput(const QString &output)
void CMakeTool::fetchFromCapabilities() const
{
- QtcProcess cmake;
+ Process cmake;
runCMake(cmake, {"-E", "capabilities"});
if (cmake.result() == ProcessResult::FinishedWithSuccess) {
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h
index 54628910cc..31b9e01627 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.h
+++ b/src/plugins/cmakeprojectmanager/cmaketool.h
@@ -12,7 +12,7 @@
#include <optional>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace CMakeProjectManager {
@@ -97,7 +97,7 @@ public:
private:
void readInformation() const;
- void runCMake(Utils::QtcProcess &proc, const QStringList &args, int timeoutS = 1) const;
+ void runCMake(Utils::Process &proc, const QStringList &args, int timeoutS = 1) const;
void parseFunctionDetailsOutput(const QString &output);
QStringList parseVariableOutput(const QString &output);
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
index cfce81f005..3b1c2e4546 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
@@ -139,11 +139,10 @@ mergeTools(std::vector<std::unique_ptr<CMakeTool>> &sdkTools,
// CMakeToolSettingsAccessor:
// --------------------------------------------------------------------
-CMakeToolSettingsAccessor::CMakeToolSettingsAccessor() :
- UpgradingSettingsAccessor("QtCreatorCMakeTools",
- Tr::tr("CMake"),
- Core::Constants::IDE_DISPLAY_NAME)
+CMakeToolSettingsAccessor::CMakeToolSettingsAccessor()
{
+ setDocType("QtCreatorCMakeTools");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
setBaseFilePath(Core::ICore::userResourcePath(CMAKE_TOOL_FILENAME));
addVersionUpgrader(std::make_unique<CMakeToolSettingsUpgraderV0>());
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index 13a1510ebd..1ef7172168 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -3,6 +3,7 @@
#include "fileapidataextractor.h"
+#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h"
@@ -13,8 +14,8 @@
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <projectexplorer/projecttree.h>
@@ -27,6 +28,8 @@ using namespace CMakeProjectManager::Internal::FileApiDetails;
namespace CMakeProjectManager::Internal {
+static Q_LOGGING_CATEGORY(cmakeLogger, "qtc.cmake.fileApiExtractor", QtWarningMsg);
+
// --------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------
@@ -53,7 +56,24 @@ CMakeFileResult extractCMakeFilesData(const std::vector<CMakeFileInfo> &cmakefil
const int oldCount = result.cmakeFiles.count();
CMakeFileInfo absolute(info);
absolute.path = sfn;
+
+ const auto mimeType = Utils::mimeTypeForFile(info.path);
+ if (mimeType.matchesName(Constants::CMAKE_MIMETYPE)
+ || mimeType.matchesName(Constants::CMAKE_PROJECT_MIMETYPE)) {
+ expected_str<QByteArray> fileContent = sfn.fileContents();
+ std::string errorString;
+ if (fileContent) {
+ fileContent = fileContent->replace("\r\n", "\n");
+ if (!absolute.cmakeListFile.ParseString(fileContent->toStdString(),
+ sfn.fileName().toStdString(),
+ errorString))
+ qCWarning(cmakeLogger)
+ << "Failed to parse:" << sfn.path() << QString::fromLatin1(errorString);
+ }
+ }
+
result.cmakeFiles.insert(absolute);
+
if (oldCount < result.cmakeFiles.count()) {
if (info.isCMake && !info.isCMakeListsDotTxt) {
// Skip files that cmake considers to be part of the installation -- but include
@@ -245,7 +265,7 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
continue;
// CMake sometimes mixes several shell-escaped pieces into one fragment. Disentangle that again:
- const QStringList parts = ProcessArgs::splitArgs(f.fragment);
+ const QStringList parts = ProcessArgs::splitArgs(f.fragment, HostOsInfo::hostOs());
for (QString part : parts) {
// Library search paths that are added with target_link_directories are added as
// -LIBPATH:... (Windows/MSVC), or
@@ -264,7 +284,7 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
continue;
const FilePath buildDir = haveLibrariesRelativeToBuildDirectory ? buildDirectory : currentBuildDir;
- FilePath tmp = buildDir.resolvePath(FilePath::fromUserInput(part).onDevice(buildDir));
+ FilePath tmp = buildDir.resolvePath(part);
if (f.role == "libraries")
tmp = tmp.parentDir();
@@ -306,7 +326,7 @@ static QStringList splitFragments(const QStringList &fragments)
{
QStringList result;
for (const QString &f : fragments) {
- result += ProcessArgs::splitArgs(f);
+ result += ProcessArgs::splitArgs(f, HostOsInfo::hostOs());
}
return result;
}
@@ -497,11 +517,8 @@ FolderNode *createSourceGroupNode(const QString &sourceGroupName,
const QStringList parts = sourceGroupName.split("\\");
for (const QString &p : parts) {
- FolderNode *existingNode = Utils::findOrDefault(currentNode->folderNodes(),
- [&p](const FolderNode *fn) {
- return fn->displayName() == p;
- });
-
+ FolderNode *existingNode = currentNode->findChildFolderNode(
+ [&p](const FolderNode *fn) { return fn->displayName() == p; });
if (!existingNode) {
auto node = createCMakeVFolder(sourceDirectory, Node::DefaultFolderPriority + 5, p);
node->setListInProject(false);
@@ -605,6 +622,28 @@ void addCompileGroups(ProjectNode *targetRoot,
std::move(otherFileNodes));
}
+static void addGeneratedFilesNode(ProjectNode *targetRoot, const FilePath &topLevelBuildDir,
+ const TargetDetails &td)
+{
+ if (td.artifacts.isEmpty())
+ return;
+ FileType type = FileType::Unknown;
+ if (td.type == "EXECUTABLE")
+ type = FileType::App;
+ else if (td.type == "SHARED_LIBRARY" || td.type == "STATIC_LIBRARY")
+ type = FileType::Lib;
+ if (type == FileType::Unknown)
+ return;
+ std::vector<std::unique_ptr<FileNode>> nodes;
+ const FilePath buildDir = topLevelBuildDir.resolvePath(td.buildDir);
+ for (const FilePath &artifact : td.artifacts) {
+ nodes.emplace_back(new FileNode(buildDir.resolvePath(artifact), type));
+ type = FileType::Unknown;
+ nodes.back()->setIsGenerated(true);
+ }
+ addCMakeVFolder(targetRoot, buildDir, 10, Tr::tr("<Generated Files>"), std::move(nodes));
+}
+
void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
const Configuration &config,
const std::vector<TargetDetails> &targetDetails,
@@ -635,6 +674,7 @@ void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cm
tNode->setBuildDirectory(directoryBuildDir(config, buildDir, t.directory));
addCompileGroups(tNode, sourceDir, dir, tNode->buildDirectory(), td);
+ addGeneratedFilesNode(tNode, buildDir, td);
}
}
@@ -671,6 +711,9 @@ std::unique_ptr<CMakeProjectNode> generateRootProjectNode(
std::move(data.cmakeNodesBuild),
std::move(data.cmakeNodesOther));
+
+ addCMakePresets(result.get(), sourceDirectory);
+
data.cmakeNodesSource.clear(); // Remove all the nullptr in the vector...
data.cmakeNodesBuild.clear(); // Remove all the nullptr in the vector...
data.cmakeNodesOther.clear(); // Remove all the nullptr in the vector...
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
index 04f4ecea43..e4ed3aa778 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.h
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
@@ -5,6 +5,7 @@
#include "cmakebuildtarget.h"
#include "cmakeprojectnodes.h"
+#include "3rdparty/cmake/cmListFileCache.h"
#include <projectexplorer/rawprojectpart.h>
@@ -32,6 +33,7 @@ public:
bool isCMakeListsDotTxt = false;
bool isExternal = false;
bool isGenerated = false;
+ cmListFile cmakeListFile;
};
class FileApiQtcData
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.cpp b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
index 0f25f27580..e4dca5c9b2 100644
--- a/src/plugins/cmakeprojectmanager/fileapiparser.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
@@ -16,6 +16,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
+#include <QPromise>
using namespace Utils;
@@ -825,7 +826,7 @@ static QStringList uniqueTargetFiles(const Configuration &config)
return files;
}
-FileApiData FileApiParser::parseData(QFutureInterface<std::shared_ptr<FileApiQtcData>> &fi,
+FileApiData FileApiParser::parseData(QPromise<std::shared_ptr<FileApiQtcData>> &promise,
const FilePath &replyFilePath,
const QString &cmakeBuildType,
QString &errorMessage)
@@ -836,8 +837,8 @@ FileApiData FileApiParser::parseData(QFutureInterface<std::shared_ptr<FileApiQtc
FileApiData result;
- const auto cancelCheck = [&fi, &errorMessage] {
- if (fi.isCanceled()) {
+ const auto cancelCheck = [&promise, &errorMessage] {
+ if (promise.isCanceled()) {
errorMessage = Tr::tr("CMake parsing was canceled.");
return true;
}
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.h b/src/plugins/cmakeprojectmanager/fileapiparser.h
index 14ac55873b..1d7c5d5ab3 100644
--- a/src/plugins/cmakeprojectmanager/fileapiparser.h
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.h
@@ -14,13 +14,17 @@
#include <utils/fileutils.h>
#include <QDir>
-#include <QFutureInterface>
#include <QString>
#include <QVector>
#include <QVersionNumber>
#include <vector>
+QT_BEGIN_NAMESPACE
+template <typename Ret>
+class QPromise;
+QT_END_NAMESPACE
+
namespace CMakeProjectManager::Internal {
namespace FileApiDetails {
@@ -218,7 +222,7 @@ public:
class FileApiParser
{
public:
- static FileApiData parseData(QFutureInterface<std::shared_ptr<FileApiQtcData>> &fi,
+ static FileApiData parseData(QPromise<std::shared_ptr<FileApiQtcData>> &promise,
const Utils::FilePath &replyFilePath,
const QString &cmakeBuildType,
QString &errorMessage);
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp
index c6ee73d6ab..fd507f7bf7 100644
--- a/src/plugins/cmakeprojectmanager/fileapireader.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp
@@ -5,7 +5,6 @@
#include "cmakeprocess.h"
#include "cmakeprojectmanagertr.h"
-#include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h"
#include "fileapidataextractor.h"
#include "fileapiparser.h"
@@ -15,8 +14,8 @@
#include <projectexplorer/projectexplorer.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QLoggingCategory>
@@ -166,20 +165,19 @@ bool FileApiReader::isParsing() const
return m_isParsing;
}
-QSet<FilePath> FileApiReader::projectFilesToWatch() const
-{
- return Utils::transform(
- Utils::filtered(m_cmakeFiles,
- [](const CMakeFileInfo &info) { return !info.isGenerated; }),
- [](const CMakeFileInfo &info) { return info.path;});
-}
-
QList<CMakeBuildTarget> FileApiReader::takeBuildTargets(QString &errorMessage){
Q_UNUSED(errorMessage)
return std::exchange(m_buildTargets, {});
}
+QSet<CMakeFileInfo> FileApiReader::takeCMakeFileInfos(QString &errorMessage)
+{
+ Q_UNUSED(errorMessage)
+
+ return std::exchange(m_cmakeFiles, {});
+}
+
CMakeConfig FileApiReader::takeParsedConfiguration(QString &errorMessage)
{
if (m_lastCMakeExitCode != 0)
@@ -235,11 +233,11 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac
m_lastReplyTimestamp = replyFilePath.lastModified();
- m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
+ m_future = Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
[replyFilePath, sourceDirectory, buildDirectory, cmakeBuildType](
- QFutureInterface<std::shared_ptr<FileApiQtcData>> &fi) {
+ QPromise<std::shared_ptr<FileApiQtcData>> &promise) {
auto result = std::make_shared<FileApiQtcData>();
- FileApiData data = FileApiParser::parseData(fi,
+ FileApiData data = FileApiParser::parseData(promise,
replyFilePath,
cmakeBuildType,
result->errorMessage);
@@ -248,7 +246,7 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac
else
qWarning() << result->errorMessage;
- fi.reportResult(result);
+ promise.addResult(result);
});
onResultReady(m_future.value(),
this,
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h
index c854ce2040..115d22ea71 100644
--- a/src/plugins/cmakeprojectmanager/fileapireader.h
+++ b/src/plugins/cmakeprojectmanager/fileapireader.h
@@ -44,8 +44,8 @@ public:
bool isParsing() const;
- QSet<Utils::FilePath> projectFilesToWatch() const;
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage);
+ QSet<CMakeFileInfo> takeCMakeFileInfos(QString &errorMessage);
CMakeConfig takeParsedConfiguration(QString &errorMessage);
QString ctestPath() const;
ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage);
diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.cpp b/src/plugins/cmakeprojectmanager/presetsmacros.cpp
index af5524216f..c96b0eed7a 100644
--- a/src/plugins/cmakeprojectmanager/presetsmacros.cpp
+++ b/src/plugins/cmakeprojectmanager/presetsmacros.cpp
@@ -10,6 +10,8 @@
#include <utils/hostosinfo.h>
#include <utils/osspecificaspects.h>
+using namespace Utils;
+
namespace CMakeProjectManager::Internal::CMakePresets::Macros {
static QString getHostSystemName(Utils::OsType osType)
@@ -107,32 +109,26 @@ static QString expandMacroEnv(const QString &macroPrefix,
return result;
}
-static Utils::Environment getEnvCombined(const std::optional<Utils::Environment> &optPresetEnv,
- const Utils::Environment &env)
+static Environment getEnvCombined(const std::optional<Environment> &optPresetEnv,
+ const Environment &env)
{
- Utils::Environment result = env;
-
- if (!optPresetEnv)
- return result;
+ Environment result = env;
- Utils::Environment presetEnv = optPresetEnv.value();
- for (auto it = presetEnv.constBegin(); it != presetEnv.constEnd(); ++it) {
- result.set(it.key().name, it.value().first);
+ if (optPresetEnv) {
+ optPresetEnv->forEachEntry([&result](const QString &key, const QString &value, bool) {
+ result.set(key, value);
+ });
}
return result;
}
template<class PresetType>
-void expand(const PresetType &preset,
- Utils::Environment &env,
- const Utils::FilePath &sourceDirectory)
+void expand(const PresetType &preset, Environment &env, const FilePath &sourceDirectory)
{
- const Utils::Environment presetEnv = getEnvCombined(preset.environment, env);
- for (auto it = presetEnv.constBegin(); it != presetEnv.constEnd(); ++it) {
- const QString key = it.key().name;
- QString value = it.value().first;
-
+ const Environment presetEnv = getEnvCombined(preset.environment, env);
+ presetEnv.forEachEntry([&](const QString &key, const QString &value_, bool) {
+ QString value = value_;
expandAllButEnv(preset, sourceDirectory, value);
value = expandMacroEnv("env", value, [presetEnv](const QString &macroName) {
return presetEnv.value(macroName);
@@ -141,7 +137,7 @@ void expand(const PresetType &preset,
QString sep;
bool append = true;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
- sep = Utils::OsSpecificAspects::pathListSeparator(env.osType());
+ sep = OsSpecificAspects::pathListSeparator(env.osType());
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
append = false;
@@ -159,20 +155,15 @@ void expand(const PresetType &preset,
env.appendOrSet(key, value, sep);
else
env.prependOrSet(key, value, sep);
- }
+ });
}
template<class PresetType>
-void expand(const PresetType &preset,
- Utils::EnvironmentItems &envItems,
- const Utils::FilePath &sourceDirectory)
+void expand(const PresetType &preset, EnvironmentItems &envItems, const FilePath &sourceDirectory)
{
- const Utils::Environment presetEnv = preset.environment ? preset.environment.value()
- : Utils::Environment();
- for (auto it = presetEnv.constBegin(); it != presetEnv.constEnd(); ++it) {
- const QString key = it.key().name;
- QString value = it.value().first;
-
+ const Environment presetEnv = preset.environment ? *preset.environment : Environment();
+ presetEnv.forEachEntry([&](const QString &key, const QString &value_, bool) {
+ QString value = value_;
expandAllButEnv(preset, sourceDirectory, value);
value = expandMacroEnv("env", value, [presetEnv](const QString &macroName) {
if (presetEnv.hasKey(macroName))
@@ -180,12 +171,12 @@ void expand(const PresetType &preset,
return QString("${%1}").arg(macroName);
});
- auto operation = Utils::EnvironmentItem::Operation::SetEnabled;
+ auto operation = EnvironmentItem::Operation::SetEnabled;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
- operation = Utils::EnvironmentItem::Operation::Append;
+ operation = EnvironmentItem::Operation::Append;
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
- operation = Utils::EnvironmentItem::Operation::Prepend;
+ operation = EnvironmentItem::Operation::Prepend;
value.replace("$penv{PATH}", "", Qt::CaseInsensitive);
}
@@ -197,7 +188,7 @@ void expand(const PresetType &preset,
expandAllButEnv(preset, sourceDirectory, value);
envItems.emplace_back(Utils::EnvironmentItem(key, value, operation));
- }
+ });
}
template<class PresetType>
diff --git a/src/plugins/cmakeprojectmanager/presetsparser.cpp b/src/plugins/cmakeprojectmanager/presetsparser.cpp
index 5ebd17fd63..f3be87f7ea 100644
--- a/src/plugins/cmakeprojectmanager/presetsparser.cpp
+++ b/src/plugins/cmakeprojectmanager/presetsparser.cpp
@@ -494,16 +494,6 @@ static QHash<QString, QString> merge(const QHash<QString, QString> &first,
return result;
}
-static Utils::Environment merge(const Utils::Environment &first, const Utils::Environment &second)
-{
- Utils::Environment result = first;
- for (auto it = second.constBegin(); it != second.constEnd(); ++it) {
- result.set(it.key().name, it.value().first);
- }
-
- return result;
-}
-
static CMakeConfig merge(const CMakeConfig &first, const CMakeConfig &second)
{
return Utils::setUnionMerge<CMakeConfig>(
@@ -561,7 +551,7 @@ void PresetsDetails::ConfigurePreset::inheritFrom(const ConfigurePreset &other)
if (!environment && other.environment)
environment = other.environment;
else if (environment && other.environment)
- environment = merge(other.environment.value(), environment.value());
+ environment = environment.value().appliedToEnvironment(other.environment.value());
if (!warnings && other.warnings)
warnings = other.warnings;
@@ -587,7 +577,7 @@ void PresetsDetails::BuildPreset::inheritFrom(const BuildPreset &other)
if (!environment && other.environment)
environment = other.environment;
else if (environment && other.environment)
- environment = merge(other.environment.value(), environment.value());
+ environment = environment.value().appliedToEnvironment(other.environment.value());
if (!configurePreset && other.configurePreset)
configurePreset = other.configurePreset;
diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
index 3fb89d9fec..7b9efb07b9 100644
--- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
+++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
@@ -3,9 +3,11 @@
#include "projecttreehelper.h"
+#include "cmakeproject.h"
#include "cmakeprojectmanagertr.h"
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/fsengine/fileiconprovider.h>
@@ -42,8 +44,7 @@ void addCMakeVFolder(FolderNode *base,
base->addNode(std::move(newFolder));
}
folder->addNestedNodes(std::move(files));
- for (FolderNode *fn : folder->folderNodes())
- fn->compress();
+ folder->forEachFolderNode([] (FolderNode *fn) { fn->compress(); });
}
std::vector<std::unique_ptr<FileNode>> &&removeKnownNodes(
@@ -89,6 +90,34 @@ void addCMakeInputs(FolderNode *root,
root->addNode(std::move(cmakeVFolder));
}
+void addCMakePresets(FolderNode *root, const Utils::FilePath &sourceDir)
+{
+ QStringList presetFileNames;
+ presetFileNames << "CMakePresets.json";
+ presetFileNames << "CMakeUserPresets.json";
+
+ const CMakeProject *cp = static_cast<const CMakeProject *>(
+ ProjectManager::projectForFile(sourceDir.pathAppended("CMakeLists.txt")));
+
+ if (cp && cp->presetsData().include)
+ presetFileNames.append(cp->presetsData().include.value());
+
+ std::vector<std::unique_ptr<FileNode>> presets;
+ for (const auto &fileName : presetFileNames) {
+ Utils::FilePath file = sourceDir.pathAppended(fileName);
+ if (file.exists())
+ presets.push_back(std::make_unique<FileNode>(file, Node::fileTypeForFileName(file)));
+ }
+
+ if (presets.empty())
+ return;
+
+ std::unique_ptr<ProjectNode> cmakeVFolder = std::make_unique<CMakePresetsNode>(root->filePath());
+ addCMakeVFolder(cmakeVFolder.get(), sourceDir, 1000, QString(), std::move(presets));
+
+ root->addNode(std::move(cmakeVFolder));
+}
+
QHash<Utils::FilePath, ProjectNode *> addCMakeLists(
CMakeProjectNode *root, std::vector<std::unique_ptr<FileNode>> &&cmakeLists)
{
diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.h b/src/plugins/cmakeprojectmanager/projecttreehelper.h
index 66cde4aea9..34a8723759 100644
--- a/src/plugins/cmakeprojectmanager/projecttreehelper.h
+++ b/src/plugins/cmakeprojectmanager/projecttreehelper.h
@@ -30,6 +30,8 @@ void addCMakeInputs(ProjectExplorer::FolderNode *root,
std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&buildInputs,
std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&rootInputs);
+void addCMakePresets(ProjectExplorer::FolderNode *root, const Utils::FilePath &sourceDir);
+
QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> addCMakeLists(
CMakeProjectNode *root, std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&cmakeLists);
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp
index 051552d16c..ad614e7e23 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp
@@ -148,8 +148,8 @@ void addDriverModeFlagIfNeeded(const ToolChain *toolchain,
RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile,
Kit *kit,
ProjectExplorer::KitInfo &kitInfo,
- const QString &workingDir,
- const Utils::FilePath &fileName,
+ const FilePath &workingDir,
+ const FilePath &filePath,
QStringList flags)
{
HeaderPaths headerPaths;
@@ -157,7 +157,7 @@ RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile,
CppEditor::ProjectFile::Kind fileKind = CppEditor::ProjectFile::Unclassified;
const QStringList originalFlags = flags;
- filteredFlags(fileName.fileName(),
+ filteredFlags(filePath,
workingDir,
flags,
headerPaths,
@@ -166,10 +166,12 @@ RawProjectPart makeRawProjectPart(const Utils::FilePath &projectFile,
kitInfo.sysRootPath);
RawProjectPart rpp;
+
rpp.setProjectFileLocation(projectFile.toString());
- rpp.setBuildSystemTarget(workingDir);
- rpp.setDisplayName(fileName.fileName());
- rpp.setFiles({fileName.toString()});
+ rpp.setBuildSystemTarget(workingDir.path());
+ rpp.setDisplayName(filePath.fileName());
+ rpp.setFiles({filePath.toFSPathString()});
+
rpp.setHeaderPaths(headerPaths);
rpp.setMacros(macros);
@@ -221,13 +223,10 @@ FolderNode *addChildFolderNode(FolderNode *parent, const QString &childName)
FolderNode *addOrGetChildFolderNode(FolderNode *parent, const QString &childName)
{
- for (FolderNode *folder : parent->folderNodes()) {
- if (folder->filePath().fileName() == childName) {
- return folder;
- }
- }
-
- return addChildFolderNode(parent, childName);
+ FolderNode *fn = parent->findChildFolderNode([&](FolderNode *folder) {
+ return folder->filePath().fileName() == childName;
+ });
+ return fn ? fn : addChildFolderNode(parent, childName);
}
// Return the node for folderPath.
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs
index 9c12b05a3a..ce7e4e35ac 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs
@@ -21,9 +21,7 @@ QtcPlugin {
"compilationdbparser.h",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"compilationdatabasetests.cpp",
"compilationdatabasetests.h",
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp
index fc1da70ca7..13f03c0ed3 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp
@@ -15,7 +15,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/parameteraction.h>
@@ -72,7 +72,7 @@ void CompilationDatabaseProjectManagerPlugin::initialize()
d->changeRootAction.setEnabled(currentProject);
};
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, onProjectChanged);
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp
index 6fff494764..f8b2574771 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp
@@ -92,7 +92,9 @@ public:
QStringList getFilteredFlags()
{
- filteredFlags(fileName, workingDir, flags, headerPaths, macros, fileKind, sysRoot);
+ filteredFlags(FilePath::fromString(fileName),
+ FilePath::fromString(workingDir),
+ flags, headerPaths, macros, fileKind, sysRoot);
return flags;
}
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp
index df8e08a68f..be0ab87e54 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp
@@ -22,15 +22,6 @@ using namespace Utils;
namespace CompilationDatabaseProjectManager {
namespace Internal {
-static QString updatedPathFlag(const QString &pathStr, const QString &workingDir)
-{
- QString result = pathStr;
- if (QDir(pathStr).isRelative())
- result = workingDir + "/" + pathStr;
-
- return result;
-}
-
static CppEditor::ProjectFile::Kind fileKindFromString(QString flag)
{
using namespace CppEditor;
@@ -86,13 +77,13 @@ QStringList filterFromFileName(const QStringList &flags, QString fileName)
return result;
}
-void filteredFlags(const QString &fileName,
- const QString &workingDir,
+void filteredFlags(const FilePath &filePath,
+ const FilePath &workingDir,
QStringList &flags,
HeaderPaths &headerPaths,
Macros &macros,
CppEditor::ProjectFile::Kind &fileKind,
- Utils::FilePath &sysRoot)
+ FilePath &sysRoot)
{
if (flags.empty())
return;
@@ -113,7 +104,7 @@ void filteredFlags(const QString &fileName,
}
if (includePathType) {
- const QString pathStr = updatedPathFlag(flag, workingDir);
+ const QString pathStr = workingDir.resolvePath(flag).toString();
headerPaths.append({pathStr, includePathType.value()});
includePathType.reset();
continue;
@@ -145,14 +136,14 @@ void filteredFlags(const QString &fileName,
continue;
}
- const QStringList userIncludeFlags{"-I", "/I"};
- const QStringList systemIncludeFlags{"-isystem", "-imsvc", "/imsvc"};
+ const QStringList userIncludeFlags{"-I", "-iquote", "/I"};
+ const QStringList systemIncludeFlags{"-isystem", "-idirafter", "-imsvc", "/imsvc"};
const QStringList allIncludeFlags = QStringList(userIncludeFlags) << systemIncludeFlags;
const QString includeOpt = Utils::findOrDefault(allIncludeFlags, [flag](const QString &opt) {
return flag.startsWith(opt) && flag != opt;
});
if (!includeOpt.isEmpty()) {
- const QString pathStr = updatedPathFlag(flag.mid(includeOpt.length()), workingDir);
+ const QString pathStr = workingDir.resolvePath(flag.mid(includeOpt.length())).toString();
headerPaths.append({pathStr, userIncludeFlags.contains(includeOpt)
? HeaderPathType::User : HeaderPathType::System});
continue;
@@ -182,14 +173,14 @@ void filteredFlags(const QString &fileName,
if (flag.startsWith("--sysroot=")) {
if (sysRoot.isEmpty())
- sysRoot = FilePath::fromUserInput(updatedPathFlag(flag.mid(10), workingDir));
+ sysRoot = workingDir.resolvePath(flag.mid(10));
continue;
}
if ((flag.startsWith("-std=") || flag.startsWith("/std:"))
&& fileKind == CppEditor::ProjectFile::Unclassified) {
const bool cpp = (flag.contains("c++") || flag.contains("gnu++"));
- if (CppEditor::ProjectFile::isHeader(CppEditor::ProjectFile::classify(fileName)))
+ if (CppEditor::ProjectFile::isHeader(CppEditor::ProjectFile::classify(filePath.path())))
fileKind = cpp ? CppEditor::ProjectFile::CXXHeader : CppEditor::ProjectFile::CHeader;
else
fileKind = cpp ? CppEditor::ProjectFile::CXXSource : CppEditor::ProjectFile::CSource;
@@ -203,7 +194,7 @@ void filteredFlags(const QString &fileName,
}
if (fileKind == CppEditor::ProjectFile::Unclassified)
- fileKind = CppEditor::ProjectFile::classify(fileName);
+ fileKind = CppEditor::ProjectFile::classify(filePath.path());
flags = filtered;
}
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h
index 79130799b4..ec024443d8 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h
@@ -4,10 +4,8 @@
#pragma once
#include <cppeditor/cppprojectfile.h>
-#include <utils/filepath.h>
#include <QHash>
-#include <QStringList>
namespace ProjectExplorer {
class HeaderPath;
@@ -21,7 +19,7 @@ class DbEntry {
public:
QStringList flags;
Utils::FilePath fileName;
- QString workingDir;
+ Utils::FilePath workingDir;
};
class DbContents {
@@ -35,8 +33,8 @@ using MimeBinaryCache = QHash<QString, bool>;
QStringList filterFromFileName(const QStringList &flags, QString baseName);
-void filteredFlags(const QString &fileName,
- const QString &workingDir,
+void filteredFlags(const Utils::FilePath &filePath,
+ const Utils::FilePath &workingDir,
QStringList &flags,
QVector<ProjectExplorer::HeaderPath> &headerPaths,
QVector<ProjectExplorer::Macro> &macros,
diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
index 251e9685c4..ccbead4c4c 100644
--- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
+++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp
@@ -8,8 +8,8 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/treescanner.h>
+#include <utils/async.h>
#include <utils/mimeutils.h>
-#include <utils/runextensions.h>
#include <QCryptographicHash>
#include <QDir>
@@ -95,7 +95,7 @@ void CompilationDbParser::start()
}
// Thread 2: Parse the project file.
- const QFuture<DbContents> future = runAsync(&CompilationDbParser::parseProject, this);
+ const QFuture<DbContents> future = Utils::asyncRun(&CompilationDbParser::parseProject, this);
Core::ProgressManager::addTask(future,
Tr::tr("Parse \"%1\" project").arg(m_projectName),
"CompilationDatabase.Parse");
@@ -182,7 +182,7 @@ std::vector<DbEntry> CompilationDbParser::readJsonObjects() const
const Utils::FilePath filePath = jsonObjectFilePath(object);
const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache),
filePath.fileName());
- result.push_back({flags, filePath, object["directory"].toString()});
+ result.push_back({flags, filePath, FilePath::fromUserInput(object["directory"].toString())});
objectStart = m_projectFileContents.indexOf('{', objectEnd + 1);
objectEnd = m_projectFileContents.indexOf('}', objectStart + 1);
diff --git a/src/plugins/conan/conaninstallstep.cpp b/src/plugins/conan/conaninstallstep.cpp
index d0c2ef5bac..c1957cc976 100644
--- a/src/plugins/conan/conaninstallstep.cpp
+++ b/src/plugins/conan/conaninstallstep.cpp
@@ -68,7 +68,7 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id)
const QString buildType = bt == BuildConfiguration::Release ? QString("Release")
: QString("Debug");
- CommandLine cmd(ConanPlugin::conanSettings()->conanFilePath.filePath());
+ CommandLine cmd(ConanPlugin::conanSettings()->conanFilePath());
cmd.addArgs({"install", "-s", "build_type=" + buildType});
if (buildMissing->value())
cmd.addArg("--build=missing");
diff --git a/src/plugins/conan/conanplugin.cpp b/src/plugins/conan/conanplugin.cpp
index a1a78acca3..3844b2983b 100644
--- a/src/plugins/conan/conanplugin.cpp
+++ b/src/plugins/conan/conanplugin.cpp
@@ -13,8 +13,8 @@
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
using namespace Core;
@@ -39,7 +39,7 @@ void ConanPlugin::initialize()
d = new ConanPluginPrivate;
conanSettings()->readSettings(ICore::settings());
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &ConanPlugin::projectAdded);
}
@@ -48,12 +48,12 @@ static void connectTarget(Project *project, Target *target)
if (!ConanPlugin::conanFilePath(project).isEmpty()) {
const QList<BuildConfiguration *> buildConfigurations = target->buildConfigurations();
for (BuildConfiguration *buildConfiguration : buildConfigurations)
- buildConfiguration->buildSteps()->appendStep(Constants::INSTALL_STEP);
+ buildConfiguration->buildSteps()->insertStep(0, Constants::INSTALL_STEP);
}
QObject::connect(target, &Target::addedBuildConfiguration,
target, [project] (BuildConfiguration *buildConfiguration) {
if (!ConanPlugin::conanFilePath(project).isEmpty())
- buildConfiguration->buildSteps()->appendStep(Constants::INSTALL_STEP);
+ buildConfiguration->buildSteps()->insertStep(0, Constants::INSTALL_STEP);
});
}
diff --git a/src/plugins/conan/conansettings.cpp b/src/plugins/conan/conansettings.cpp
index 6a608b99f8..83e8f38d53 100644
--- a/src/plugins/conan/conansettings.cpp
+++ b/src/plugins/conan/conansettings.cpp
@@ -16,7 +16,6 @@ ConanSettings::ConanSettings()
registerAspect(&conanFilePath);
conanFilePath.setSettingsKey("ConanFilePath");
- conanFilePath.setDisplayStyle(StringAspect::PathChooserDisplay);
conanFilePath.setExpectedKind(PathChooser::ExistingCommand);
conanFilePath.setDefaultValue(HostOsInfo::withExecutableSuffix("conan"));
}
diff --git a/src/plugins/conan/conansettings.h b/src/plugins/conan/conansettings.h
index 93e6190839..69a836dcfe 100644
--- a/src/plugins/conan/conansettings.h
+++ b/src/plugins/conan/conansettings.h
@@ -12,7 +12,7 @@ class ConanSettings : public Utils::AspectContainer
public:
ConanSettings();
- Utils::StringAspect conanFilePath;
+ Utils::FilePathAspect conanFilePath;
};
} // Conan::Internal
diff --git a/src/plugins/copilot/CMakeLists.txt b/src/plugins/copilot/CMakeLists.txt
new file mode 100644
index 0000000000..b718ec1269
--- /dev/null
+++ b/src/plugins/copilot/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_qtc_plugin(Copilot
+ PLUGIN_DEPENDS Core LanguageClient
+ SOURCES
+ authwidget.cpp authwidget.h
+ copilot.qrc
+ copilotclient.cpp copilotclient.h
+ copilothoverhandler.cpp copilothoverhandler.h
+ copilotoptionspage.cpp copilotoptionspage.h
+ copilotplugin.cpp copilotplugin.h
+ copilotsettings.cpp copilotsettings.h
+ copilotsuggestion.cpp copilotsuggestion.h
+ requests/checkstatus.h
+ requests/getcompletions.h
+ requests/signinconfirm.h
+ requests/signininitiate.h
+ requests/signout.h
+)
diff --git a/src/plugins/copilot/Copilot.json.in b/src/plugins/copilot/Copilot.json.in
new file mode 100644
index 0000000000..55ff6f6bf4
--- /dev/null
+++ b/src/plugins/copilot/Copilot.json.in
@@ -0,0 +1,19 @@
+{
+ \"Name\" : \"Copilot\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Experimental\" : true,
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
+ ],
+ \"Description\" : \"Copilot support\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/copilot/authwidget.cpp b/src/plugins/copilot/authwidget.cpp
new file mode 100644
index 0000000000..ed2870e85e
--- /dev/null
+++ b/src/plugins/copilot/authwidget.cpp
@@ -0,0 +1,155 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "authwidget.h"
+
+#include "copilotclient.h"
+#include "copilottr.h"
+
+#include <utils/layoutbuilder.h>
+#include <utils/stringutils.h>
+
+#include <languageclient/languageclientmanager.h>
+
+#include <QDesktopServices>
+#include <QMessageBox>
+
+using namespace LanguageClient;
+using namespace Copilot::Internal;
+
+namespace Copilot {
+
+AuthWidget::AuthWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ using namespace Layouting;
+
+ m_button = new QPushButton(Tr::tr("Sign in"));
+ m_button->setEnabled(false);
+ m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small);
+ m_progressIndicator->setVisible(false);
+ m_statusLabel = new QLabel();
+ m_statusLabel->setVisible(false);
+
+ // clang-format off
+ Column {
+ Row {
+ m_button, m_progressIndicator, st
+ },
+ m_statusLabel
+ }.attachTo(this);
+ // clang-format on
+
+ connect(m_button, &QPushButton::clicked, this, [this]() {
+ if (m_status == Status::SignedIn)
+ signOut();
+ else if (m_status == Status::SignedOut)
+ signIn();
+ });
+}
+
+AuthWidget::~AuthWidget()
+{
+ if (m_client)
+ LanguageClientManager::shutdownClient(m_client);
+}
+
+void AuthWidget::setState(const QString &buttonText, bool working)
+{
+ m_button->setText(buttonText);
+ m_button->setVisible(true);
+ m_progressIndicator->setVisible(working);
+ m_statusLabel->setVisible(!m_statusLabel->text().isEmpty());
+ m_button->setEnabled(!working);
+}
+
+void AuthWidget::checkStatus()
+{
+ QTC_ASSERT(m_client && m_client->reachable(), return);
+
+ setState("Checking status ...", true);
+
+ m_client->requestCheckStatus(false, [this](const CheckStatusRequest::Response &response) {
+ if (response.error()) {
+ setState("failed: " + response.error()->message(), false);
+ return;
+ }
+ const CheckStatusResponse result = *response.result();
+
+ if (result.user().isEmpty()) {
+ setState("Sign in", false);
+ m_status = Status::SignedOut;
+ return;
+ }
+
+ setState("Sign out " + result.user(), false);
+ m_status = Status::SignedIn;
+ });
+}
+
+void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent)
+{
+ LanguageClientManager::shutdownClient(m_client);
+ m_client = nullptr;
+ setState(Tr::tr("Sign in"), true);
+ if (!nodeJs.exists() || !agent.exists())
+ return;
+
+ m_client = new CopilotClient(nodeJs, agent);
+ connect(m_client, &Client::initialized, this, &AuthWidget::checkStatus);
+}
+
+void AuthWidget::signIn()
+{
+ qCritical() << "Not implemented";
+ QTC_ASSERT(m_client && m_client->reachable(), return);
+
+ setState("Signing in ...", true);
+
+ m_client->requestSignInInitiate([this](const SignInInitiateRequest::Response &response) {
+ QTC_ASSERT(!response.error(), return);
+
+ Utils::setClipboardAndSelection(response.result()->userCode());
+
+ QDesktopServices::openUrl(QUrl(response.result()->verificationUri()));
+
+ m_statusLabel->setText(Tr::tr("A browser window will open, enter the code %1 when "
+ "asked.\nThe code has been copied to your clipboard.")
+ .arg(response.result()->userCode()));
+ m_statusLabel->setVisible(true);
+
+ m_client
+ ->requestSignInConfirm(response.result()->userCode(),
+ [this](const SignInConfirmRequest::Response &response) {
+ m_statusLabel->setText("");
+
+ if (response.error()) {
+ QMessageBox::critical(this,
+ Tr::tr("Login failed"),
+ Tr::tr(
+ "The login request failed: ")
+ + response.error()->message());
+ setState("Sign in", false);
+ return;
+ }
+
+ setState("Sign Out " + response.result()->user(), false);
+ });
+ });
+}
+
+void AuthWidget::signOut()
+{
+ QTC_ASSERT(m_client && m_client->reachable(), return);
+
+ setState("Signing out ...", true);
+
+ m_client->requestSignOut([this](const SignOutRequest::Response &response) {
+ QTC_ASSERT(!response.error(), return);
+ QTC_ASSERT(response.result()->status() == "NotSignedIn", return);
+
+ checkStatus();
+ });
+}
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/authwidget.h b/src/plugins/copilot/authwidget.h
new file mode 100644
index 0000000000..acb18810fe
--- /dev/null
+++ b/src/plugins/copilot/authwidget.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "copilotclient.h"
+
+#include <utils/progressindicator.h>
+
+#include <QLabel>
+#include <QPushButton>
+#include <QWidget>
+
+namespace LanguageClient {
+class Client;
+}
+
+namespace Copilot {
+
+class AuthWidget : public QWidget
+{
+ Q_OBJECT
+
+ enum class Status { SignedIn, SignedOut, Unknown };
+
+public:
+ explicit AuthWidget(QWidget *parent = nullptr);
+ ~AuthWidget() override;
+
+ void updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent);
+
+private:
+ void setState(const QString &buttonText, bool working);
+ void checkStatus();
+
+
+ void signIn();
+ void signOut();
+
+private:
+ Status m_status = Status::Unknown;
+ QPushButton *m_button = nullptr;
+ QLabel *m_statusLabel = nullptr;
+ Utils::ProgressIndicator *m_progressIndicator = nullptr;
+ Internal::CopilotClient *m_client = nullptr;
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilot.qbs b/src/plugins/copilot/copilot.qbs
new file mode 100644
index 0000000000..51d7febbd6
--- /dev/null
+++ b/src/plugins/copilot/copilot.qbs
@@ -0,0 +1,33 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "Copilot"
+
+ Depends { name: "Core" }
+ Depends { name: "LanguageClient" }
+ Depends { name: "TextEditor" }
+ Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] }
+
+ files: [
+ "authwidget.cpp",
+ "authwidget.h",
+ "copilot.qrc",
+ "copilotclient.cpp",
+ "copilotclient.h",
+ "copilothoverhandler.cpp",
+ "copilothoverhandler.h",
+ "copilotoptionspage.cpp",
+ "copilotoptionspage.h",
+ "copilotplugin.cpp",
+ "copilotplugin.h",
+ "copilotsettings.cpp",
+ "copilotsettings.h",
+ "copilotsuggestion.cpp",
+ "copilotsuggestion.h",
+ "requests/checkstatus.h",
+ "requests/getcompletions.h",
+ "requests/signinconfirm.h",
+ "requests/signininitiate.h",
+ "requests/signout.h",
+ ]
+}
diff --git a/src/plugins/copilot/copilot.qrc b/src/plugins/copilot/copilot.qrc
new file mode 100644
index 0000000000..2071a38101
--- /dev/null
+++ b/src/plugins/copilot/copilot.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/copilot">
+ <file>images/settingscategory_copilot.png</file>
+ <file>images/settingscategory_copilot@2x.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp
new file mode 100644
index 0000000000..946e5c1e3e
--- /dev/null
+++ b/src/plugins/copilot/copilotclient.cpp
@@ -0,0 +1,217 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilotclient.h"
+#include "copilotsettings.h"
+#include "copilotsuggestion.h"
+
+#include <languageclient/languageclientinterface.h>
+#include <languageclient/languageclientmanager.h>
+#include <languageclient/languageclientsettings.h>
+
+#include <coreplugin/editormanager/editormanager.h>
+
+#include <utils/filepath.h>
+
+#include <texteditor/textdocumentlayout.h>
+#include <texteditor/texteditor.h>
+
+#include <languageserverprotocol/lsptypes.h>
+
+#include <QTimer>
+
+using namespace LanguageServerProtocol;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace Copilot::Internal {
+
+static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath,
+ const FilePath &distPath)
+{
+ CommandLine cmd{nodePath, {distPath.toFSPathString()}};
+
+ const auto interface = new LanguageClient::StdIOClientInterface;
+ interface->setCommandLine(cmd);
+ return interface;
+}
+
+CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
+ : LanguageClient::Client(clientInterface(nodePath, distPath))
+{
+ setName("Copilot");
+ LanguageClient::LanguageFilter langFilter;
+
+ langFilter.filePattern = {"*"};
+
+ setSupportedLanguage(langFilter);
+ start();
+
+ auto openDoc = [this](Core::IDocument *document) {
+ if (auto *textDocument = qobject_cast<TextDocument *>(document))
+ openDocument(textDocument);
+ };
+
+ connect(Core::EditorManager::instance(), &Core::EditorManager::documentOpened, this, openDoc);
+ connect(Core::EditorManager::instance(),
+ &Core::EditorManager::documentClosed,
+ this,
+ [this](Core::IDocument *document) {
+ if (auto textDocument = qobject_cast<TextDocument *>(document))
+ closeDocument(textDocument);
+ });
+
+ for (Core::IDocument *doc : Core::DocumentModel::openedDocuments())
+ openDoc(doc);
+}
+
+CopilotClient::~CopilotClient()
+{
+ for (Core::IEditor *editor : Core::DocumentModel::editorsForOpenedDocuments()) {
+ if (auto textEditor = qobject_cast<BaseTextEditor *>(editor))
+ textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler);
+ }
+}
+
+void CopilotClient::openDocument(TextDocument *document)
+{
+ Client::openDocument(document);
+ connect(document,
+ &TextDocument::contentsChangedWithPosition,
+ this,
+ [this, document](int position, int charsRemoved, int charsAdded) {
+ Q_UNUSED(charsRemoved)
+ if (!CopilotSettings::instance().autoComplete.value())
+ return;
+ auto textEditor = BaseTextEditor::currentTextEditor();
+ if (!textEditor || textEditor->document() != document)
+ return;
+ TextEditorWidget *widget = textEditor->editorWidget();
+ if (widget->multiTextCursor().hasMultipleCursors())
+ return;
+ const int cursorPosition = widget->textCursor().position();
+ if (cursorPosition < position || cursorPosition > position + charsAdded)
+ return;
+ scheduleRequest(widget);
+ });
+}
+
+void CopilotClient::scheduleRequest(TextEditorWidget *editor)
+{
+ cancelRunningRequest(editor);
+
+ if (!m_scheduledRequests.contains(editor)) {
+ auto timer = new QTimer(this);
+ timer->setSingleShot(true);
+ connect(timer, &QTimer::timeout, this, [this, editor]() {
+ if (m_scheduledRequests[editor].cursorPosition == editor->textCursor().position())
+ requestCompletions(editor);
+ });
+ connect(editor, &TextEditorWidget::destroyed, this, [this, editor]() {
+ delete m_scheduledRequests.take(editor).timer;
+ cancelRunningRequest(editor);
+ });
+ connect(editor, &TextEditorWidget::cursorPositionChanged, this, [this, editor] {
+ cancelRunningRequest(editor);
+ });
+ m_scheduledRequests.insert(editor, {editor->textCursor().position(), timer});
+ } else {
+ m_scheduledRequests[editor].cursorPosition = editor->textCursor().position();
+ }
+ m_scheduledRequests[editor].timer->start(500);
+}
+
+void CopilotClient::requestCompletions(TextEditorWidget *editor)
+{
+ Utils::MultiTextCursor cursor = editor->multiTextCursor();
+ if (cursor.hasMultipleCursors() || cursor.hasSelection() || editor->suggestionVisible())
+ return;
+
+ const Utils::FilePath filePath = editor->textDocument()->filePath();
+ GetCompletionRequest request{
+ {TextDocumentIdentifier(hostPathToServerUri(filePath)),
+ documentVersion(filePath),
+ Position(cursor.mainCursor())}};
+ request.setResponseCallback([this, editor = QPointer<TextEditorWidget>(editor)](
+ const GetCompletionRequest::Response &response) {
+ QTC_ASSERT(editor, return);
+ handleCompletions(response, editor);
+ });
+ m_runningRequests[editor] = request;
+ sendMessage(request);
+}
+
+void CopilotClient::handleCompletions(const GetCompletionRequest::Response &response,
+ TextEditorWidget *editor)
+{
+ if (response.error())
+ log(*response.error());
+
+ int requestPosition = -1;
+ if (const auto requestParams = m_runningRequests.take(editor).params())
+ requestPosition = requestParams->position().toPositionInDocument(editor->document());
+
+ const Utils::MultiTextCursor cursors = editor->multiTextCursor();
+ if (cursors.hasMultipleCursors())
+ return;
+
+ if (cursors.hasSelection() || cursors.mainCursor().position() != requestPosition)
+ return;
+
+ if (const std::optional<GetCompletionResponse> result = response.result()) {
+ QList<Completion> completions = result->completions().toListOrEmpty();
+ if (completions.isEmpty())
+ return;
+ editor->insertSuggestion(
+ std::make_unique<CopilotSuggestion>(completions, editor->document()));
+ editor->addHoverHandler(&m_hoverHandler);
+ }
+}
+
+void CopilotClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor)
+{
+ auto it = m_runningRequests.find(editor);
+ if (it == m_runningRequests.end())
+ return;
+ cancelRequest(it->id());
+ m_runningRequests.erase(it);
+}
+
+void CopilotClient::requestCheckStatus(
+ bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback)
+{
+ CheckStatusRequest request{localChecksOnly};
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+void CopilotClient::requestSignOut(
+ std::function<void(const SignOutRequest::Response &response)> callback)
+{
+ SignOutRequest request;
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+void CopilotClient::requestSignInInitiate(
+ std::function<void(const SignInInitiateRequest::Response &response)> callback)
+{
+ SignInInitiateRequest request;
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+void CopilotClient::requestSignInConfirm(
+ const QString &userCode,
+ std::function<void(const SignInConfirmRequest::Response &response)> callback)
+{
+ SignInConfirmRequest request(userCode);
+ request.setResponseCallback(callback);
+
+ sendMessage(request);
+}
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilotclient.h b/src/plugins/copilot/copilotclient.h
new file mode 100644
index 0000000000..bd48710a18
--- /dev/null
+++ b/src/plugins/copilot/copilotclient.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "copilothoverhandler.h"
+#include "requests/checkstatus.h"
+#include "requests/getcompletions.h"
+#include "requests/signinconfirm.h"
+#include "requests/signininitiate.h"
+#include "requests/signout.h"
+
+#include <languageclient/client.h>
+
+#include <utils/filepath.h>
+
+#include <QHash>
+#include <QTemporaryDir>
+
+namespace Copilot::Internal {
+
+class CopilotClient : public LanguageClient::Client
+{
+public:
+ CopilotClient(const Utils::FilePath &nodePath, const Utils::FilePath &distPath);
+ ~CopilotClient() override;
+
+ void openDocument(TextEditor::TextDocument *document) override;
+
+ void scheduleRequest(TextEditor::TextEditorWidget *editor);
+ void requestCompletions(TextEditor::TextEditorWidget *editor);
+ void handleCompletions(const GetCompletionRequest::Response &response,
+ TextEditor::TextEditorWidget *editor);
+ void cancelRunningRequest(TextEditor::TextEditorWidget *editor);
+
+ void requestCheckStatus(
+ bool localChecksOnly,
+ std::function<void(const CheckStatusRequest::Response &response)> callback);
+
+ void requestSignOut(std::function<void(const SignOutRequest::Response &response)> callback);
+
+ void requestSignInInitiate(
+ std::function<void(const SignInInitiateRequest::Response &response)> callback);
+
+ void requestSignInConfirm(
+ const QString &userCode,
+ std::function<void(const SignInConfirmRequest::Response &response)> callback);
+
+private:
+ QMap<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
+ struct ScheduleData
+ {
+ int cursorPosition = -1;
+ QTimer *timer = nullptr;
+ };
+ QMap<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
+ CopilotHoverHandler m_hoverHandler;
+};
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilothoverhandler.cpp b/src/plugins/copilot/copilothoverhandler.cpp
new file mode 100644
index 0000000000..b252cedc77
--- /dev/null
+++ b/src/plugins/copilot/copilothoverhandler.cpp
@@ -0,0 +1,154 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilothoverhandler.h"
+
+#include "copilotclient.h"
+#include "copilotsuggestion.h"
+#include "copilottr.h"
+
+#include <texteditor/textdocument.h>
+#include <texteditor/textdocumentlayout.h>
+#include <texteditor/texteditor.h>
+
+#include <utils/tooltip/tooltip.h>
+#include <utils/utilsicons.h>
+
+#include <QPushButton>
+#include <QToolBar>
+#include <QToolButton>
+
+using namespace TextEditor;
+using namespace LanguageServerProtocol;
+using namespace Utils;
+
+namespace Copilot::Internal {
+
+class CopilotCompletionToolTip : public QToolBar
+{
+public:
+ CopilotCompletionToolTip(QList<Completion> completions,
+ int currentCompletion,
+ TextEditorWidget *editor)
+ : m_numberLabel(new QLabel)
+ , m_completions(completions)
+ , m_currentCompletion(std::max(0, std::min<int>(currentCompletion, completions.size() - 1)))
+ , m_editor(editor)
+ {
+ auto prev = addAction(Utils::Icons::PREV_TOOLBAR.icon(),
+ Tr::tr("Select Previous Copilot Suggestion"));
+ prev->setEnabled(m_completions.size() > 1);
+ addWidget(m_numberLabel);
+ auto next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(),
+ Tr::tr("Select Next Copilot Suggestion"));
+ next->setEnabled(m_completions.size() > 1);
+
+ auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
+ auto applyWord = addAction(
+ Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
+
+ connect(prev, &QAction::triggered, this, &CopilotCompletionToolTip::selectPrevious);
+ connect(next, &QAction::triggered, this, &CopilotCompletionToolTip::selectNext);
+ connect(apply, &QAction::triggered, this, &CopilotCompletionToolTip::apply);
+ connect(applyWord, &QAction::triggered, this, &CopilotCompletionToolTip::applyWord);
+
+ updateLabels();
+ }
+
+private:
+ void updateLabels()
+ {
+ m_numberLabel->setText(Tr::tr("%1 of %2")
+ .arg(m_currentCompletion + 1)
+ .arg(m_completions.count()));
+ }
+
+ void selectPrevious()
+ {
+ --m_currentCompletion;
+ if (m_currentCompletion < 0)
+ m_currentCompletion = m_completions.size() - 1;
+ setCurrentCompletion();
+ }
+
+ void selectNext()
+ {
+ ++m_currentCompletion;
+ if (m_currentCompletion >= m_completions.size())
+ m_currentCompletion = 0;
+ setCurrentCompletion();
+ }
+
+ void setCurrentCompletion()
+ {
+ updateLabels();
+ if (TextSuggestion *suggestion = m_editor->currentSuggestion())
+ suggestion->reset();
+ m_editor->insertSuggestion(std::make_unique<CopilotSuggestion>(m_completions,
+ m_editor->document(),
+ m_currentCompletion));
+ }
+
+ void apply()
+ {
+ if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
+ if (!suggestion->apply())
+ return;
+ }
+ ToolTip::hide();
+ }
+
+ void applyWord()
+ {
+ if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
+ if (!suggestion->applyWord(m_editor))
+ return;
+ }
+ ToolTip::hide();
+ }
+
+ QLabel *m_numberLabel;
+ QList<Completion> m_completions;
+ int m_currentCompletion = 0;
+ TextEditorWidget *m_editor;
+};
+
+void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
+ int pos,
+ ReportPriority report)
+{
+ auto reportNone = qScopeGuard([&] { report(Priority_None); });
+ if (!editorWidget->suggestionVisible())
+ return;
+
+ QTextCursor cursor(editorWidget->document());
+ cursor.setPosition(pos);
+ m_block = cursor.block();
+ auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block));
+
+ if (!suggestion)
+ return;
+
+ const QList<Completion> completions = suggestion->completions();
+ if (completions.isEmpty())
+ return;
+
+ reportNone.dismiss();
+ report(Priority_Suggestion);
+}
+
+void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
+{
+ auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block));
+
+ if (!suggestion)
+ return;
+
+ auto tooltipWidget = new CopilotCompletionToolTip(suggestion->completions(),
+ suggestion->currentCompletion(),
+ editorWidget);
+ const qreal deltay = 2 * editorWidget->textDocument()->fontSettings().lineSpacing();
+ ToolTip::show(point - QPoint{0, int(deltay)}, tooltipWidget, editorWidget);
+}
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilothoverhandler.h b/src/plugins/copilot/copilothoverhandler.h
new file mode 100644
index 0000000000..1c48e75d5b
--- /dev/null
+++ b/src/plugins/copilot/copilothoverhandler.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "requests/getcompletions.h"
+
+#include <texteditor/basehoverhandler.h>
+
+#include <QTextBlock>
+
+#pragma once
+
+namespace TextEditor { class TextSuggestion; }
+namespace Copilot::Internal {
+
+class CopilotClient;
+
+class CopilotHoverHandler final : public TextEditor::BaseHoverHandler
+{
+public:
+ CopilotHoverHandler() = default;
+
+protected:
+ void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
+ int pos,
+ ReportPriority report) final;
+ void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final;
+
+private:
+ QTextBlock m_block;
+};
+
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilotoptionspage.cpp b/src/plugins/copilot/copilotoptionspage.cpp
new file mode 100644
index 0000000000..d0f2bf654a
--- /dev/null
+++ b/src/plugins/copilot/copilotoptionspage.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilotoptionspage.h"
+
+#include "authwidget.h"
+#include "copilotsettings.h"
+#include "copilottr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+
+using namespace Utils;
+using namespace LanguageClient;
+
+namespace Copilot {
+
+class CopilotOptionsPageWidget : public Core::IOptionsPageWidget
+{
+public:
+ CopilotOptionsPageWidget()
+ {
+ using namespace Layouting;
+
+ auto authWidget = new AuthWidget();
+
+ QLabel *helpLabel = new QLabel();
+ helpLabel->setTextFormat(Qt::MarkdownText);
+ helpLabel->setWordWrap(true);
+ helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
+ | Qt::LinksAccessibleByKeyboard);
+ helpLabel->setOpenExternalLinks(true);
+
+ // clang-format off
+ helpLabel->setText(Tr::tr(R"(
+The Copilot plugin requires node.js and the Copilot neovim plugin.
+If you install the neovim plugin as described in the
+[README.md](https://github.com/github/copilot.vim),
+the plugin will find the agent.js file automatically.
+
+Otherwise you need to specify the path to the
+[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)
+file from the Copilot neovim plugin.
+ )", "Markdown text for the copilot instruction label"));
+
+ Column {
+ authWidget, br,
+ CopilotSettings::instance().nodeJsPath, br,
+ CopilotSettings::instance().distPath, br,
+ CopilotSettings::instance().autoComplete, br,
+ helpLabel, br,
+ st
+ }.attachTo(this);
+ // clang-format on
+
+ auto updateAuthWidget = [authWidget]() {
+ authWidget->updateClient(
+ FilePath::fromUserInput(
+ CopilotSettings::instance().nodeJsPath.volatileValue().toString()),
+ FilePath::fromUserInput(
+ CopilotSettings::instance().distPath.volatileValue().toString()));
+ };
+
+ connect(CopilotSettings::instance().nodeJsPath.pathChooser(),
+ &PathChooser::textChanged,
+ authWidget,
+ updateAuthWidget);
+ connect(CopilotSettings::instance().distPath.pathChooser(),
+ &PathChooser::textChanged,
+ authWidget,
+ updateAuthWidget);
+ updateAuthWidget();
+
+ setOnApply([] {
+ CopilotSettings::instance().apply();
+ CopilotSettings::instance().writeSettings(Core::ICore::settings());
+ });
+ }
+};
+
+CopilotOptionsPage::CopilotOptionsPage()
+{
+ setId("Copilot.General");
+ setDisplayName("Copilot");
+ setCategory("ZY.Copilot");
+ setDisplayCategory("Copilot");
+ setCategoryIconPath(":/copilot/images/settingscategory_copilot.png");
+ setWidgetCreator([] { return new CopilotOptionsPageWidget; });
+}
+
+CopilotOptionsPage &CopilotOptionsPage::instance()
+{
+ static CopilotOptionsPage settingsPage;
+ return settingsPage;
+}
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotoptionspage.h b/src/plugins/copilot/copilotoptionspage.h
new file mode 100644
index 0000000000..103e975b63
--- /dev/null
+++ b/src/plugins/copilot/copilotoptionspage.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Copilot {
+
+class CopilotOptionsPage : public Core::IOptionsPage
+{
+public:
+ CopilotOptionsPage();
+
+ static CopilotOptionsPage &instance();
+};
+
+} // Copilot
diff --git a/src/plugins/copilot/copilotplugin.cpp b/src/plugins/copilot/copilotplugin.cpp
new file mode 100644
index 0000000000..61753b29c3
--- /dev/null
+++ b/src/plugins/copilot/copilotplugin.cpp
@@ -0,0 +1,64 @@
+// 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 "copilotplugin.h"
+
+#include "copilotclient.h"
+#include "copilotoptionspage.h"
+#include "copilotsettings.h"
+#include "copilottr.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+
+#include <languageclient/languageclientmanager.h>
+
+#include <texteditor/texteditor.h>
+
+#include <QAction>
+
+using namespace Utils;
+using namespace Core;
+
+namespace Copilot {
+namespace Internal {
+
+void CopilotPlugin::initialize()
+{
+ CopilotSettings::instance().readSettings(ICore::settings());
+
+ restartClient();
+
+ connect(&CopilotSettings::instance(),
+ &CopilotSettings::applied,
+ this,
+ &CopilotPlugin::restartClient);
+
+ QAction *action = new QAction(this);
+ action->setText(Tr::tr("Request Copilot Suggestion"));
+ action->setToolTip(Tr::tr("Request Copilot Suggestion at the current editors cursor position."));
+
+ QObject::connect(action, &QAction::triggered, this, [this] {
+ if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
+ if (m_client->reachable())
+ m_client->requestCompletions(editor);
+ }
+ });
+ ActionManager::registerAction(action, "Copilot.RequestSuggestion");
+}
+
+void CopilotPlugin::extensionsInitialized()
+{
+ (void)CopilotOptionsPage::instance();
+}
+
+void CopilotPlugin::restartClient()
+{
+ LanguageClient::LanguageClientManager::shutdownClient(m_client);
+ m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(),
+ CopilotSettings::instance().distPath());
+}
+
+} // namespace Internal
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotplugin.h b/src/plugins/copilot/copilotplugin.h
new file mode 100644
index 0000000000..09a6caecb2
--- /dev/null
+++ b/src/plugins/copilot/copilotplugin.h
@@ -0,0 +1,30 @@
+// 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 "copilotclient.h"
+
+#include <extensionsystem/iplugin.h>
+
+#include <QPointer>
+
+namespace Copilot {
+namespace Internal {
+
+class CopilotPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Copilot.json")
+
+public:
+ void initialize() override;
+ void extensionsInitialized() override;
+ void restartClient();
+
+private:
+ QPointer<CopilotClient> m_client;
+};
+
+} // namespace Internal
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp
new file mode 100644
index 0000000000..202da82488
--- /dev/null
+++ b/src/plugins/copilot/copilotsettings.cpp
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "copilotsettings.h"
+#include "copilottr.h"
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+
+using namespace Utils;
+
+namespace Copilot {
+
+CopilotSettings &CopilotSettings::instance()
+{
+ static CopilotSettings settings;
+ return settings;
+}
+
+CopilotSettings::CopilotSettings()
+{
+ setAutoApply(false);
+
+ const FilePath nodeFromPath = FilePath("node").searchInPath();
+
+ const FilePaths searchDirs
+ = {FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
+ FilePath::fromUserInput(
+ "~/.config/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
+ FilePath::fromUserInput(
+ "~/vimfiles/pack/github/start/copilot.vim/copilot/dist/agent.js"),
+ FilePath::fromUserInput(
+ "~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js")};
+
+ const FilePath distFromVim = Utils::findOrDefault(searchDirs, [](const FilePath &fp) {
+ return fp.exists();
+ });
+
+ registerAspect(&nodeJsPath);
+ nodeJsPath.setExpectedKind(PathChooser::ExistingCommand);
+ nodeJsPath.setDefaultFilePath(nodeFromPath);
+ nodeJsPath.setSettingsKey("Copilot.NodeJsPath");
+ nodeJsPath.setLabelText(Tr::tr("Node.js path:"));
+ nodeJsPath.setHistoryCompleter("Copilot.NodePath.History");
+ nodeJsPath.setDisplayName(Tr::tr("Node.js Path"));
+ nodeJsPath.setToolTip(
+ Tr::tr("Select path to node.js executable. See https://nodejs.org/de/download/"
+ "for installation instructions."));
+
+ registerAspect(&distPath);
+ distPath.setExpectedKind(PathChooser::File);
+ distPath.setDefaultFilePath(distFromVim);
+ distPath.setSettingsKey("Copilot.DistPath");
+ distPath.setLabelText(Tr::tr("Path to agent.js:"));
+ distPath.setHistoryCompleter("Copilot.DistPath.History");
+ distPath.setDisplayName(Tr::tr("Agent.js path"));
+ distPath.setToolTip(Tr::tr(
+ "Select path to agent.js in copilot neovim plugin. See "
+ "https://github.com/github/copilot.vim#getting-started for installation instructions."));
+
+ registerAspect(&autoComplete);
+ autoComplete.setDisplayName(Tr::tr("Auto Complete"));
+ autoComplete.setLabelText(Tr::tr("Request completions automatically"));
+ autoComplete.setDefaultValue(true);
+ autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor "
+ "position after changes to the document"));
+}
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotsettings.h b/src/plugins/copilot/copilotsettings.h
new file mode 100644
index 0000000000..2337094876
--- /dev/null
+++ b/src/plugins/copilot/copilotsettings.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/aspects.h>
+
+namespace Copilot {
+
+class CopilotSettings : public Utils::AspectContainer
+{
+public:
+ CopilotSettings();
+
+ static CopilotSettings &instance();
+
+ Utils::FilePathAspect nodeJsPath;
+ Utils::FilePathAspect distPath;
+ Utils::BoolAspect autoComplete;
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/copilotsuggestion.cpp b/src/plugins/copilot/copilotsuggestion.cpp
new file mode 100644
index 0000000000..28da286f1f
--- /dev/null
+++ b/src/plugins/copilot/copilotsuggestion.cpp
@@ -0,0 +1,79 @@
+// 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 "copilotsuggestion.h"
+
+#include <texteditor/texteditor.h>
+
+#include <utils/stringutils.h>
+
+using namespace Utils;
+using namespace TextEditor;
+using namespace LanguageServerProtocol;
+
+namespace Copilot::Internal {
+
+CopilotSuggestion::CopilotSuggestion(const QList<Completion> &completions,
+ QTextDocument *origin,
+ int currentCompletion)
+ : m_completions(completions)
+ , m_currentCompletion(currentCompletion)
+{
+ const Completion completion = completions.value(currentCompletion);
+ const Position start = completion.range().start();
+ const Position end = completion.range().end();
+ QString text = start.toTextCursor(origin).block().text();
+ int length = text.length() - start.character();
+ if (start.line() == end.line())
+ length = end.character() - start.character();
+ text.replace(start.character(), length, completion.text());
+ document()->setPlainText(text);
+ m_start = completion.position().toTextCursor(origin);
+ m_start.setKeepPositionOnInsert(true);
+ setCurrentPosition(m_start.position());
+}
+
+bool CopilotSuggestion::apply()
+{
+ reset();
+ const Completion completion = m_completions.value(m_currentCompletion);
+ QTextCursor cursor = completion.range().toSelection(m_start.document());
+ cursor.insertText(completion.text());
+ return true;
+}
+
+bool CopilotSuggestion::applyWord(TextEditorWidget *widget)
+{
+ const Completion completion = m_completions.value(m_currentCompletion);
+ const QTextCursor cursor = completion.range().toSelection(m_start.document());
+ QTextCursor currentCursor = widget->textCursor();
+ const QString text = completion.text();
+ const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
+ + (cursor.selectionEnd() - cursor.selectionStart());
+ const int next = endOfNextWord(text, startPos);
+
+ if (next == -1)
+ return apply();
+
+ // TODO: Allow adding more than one line
+ QString subText = text.mid(startPos, next - startPos);
+ subText = subText.left(subText.indexOf('\n'));
+ if (subText.isEmpty())
+ return false;
+
+ currentCursor.insertText(subText);
+ return false;
+}
+
+void CopilotSuggestion::reset()
+{
+ m_start.removeSelectedText();
+}
+
+int CopilotSuggestion::position()
+{
+ return m_start.position();
+}
+
+} // namespace Copilot::Internal
+
diff --git a/src/plugins/copilot/copilotsuggestion.h b/src/plugins/copilot/copilotsuggestion.h
new file mode 100644
index 0000000000..719016236a
--- /dev/null
+++ b/src/plugins/copilot/copilotsuggestion.h
@@ -0,0 +1,32 @@
+// 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 "requests/getcompletions.h"
+
+#include <texteditor/textdocumentlayout.h>
+#include <texteditor/texteditor.h>
+
+namespace Copilot::Internal {
+
+class CopilotSuggestion final : public TextEditor::TextSuggestion
+{
+public:
+ CopilotSuggestion(const QList<Completion> &completions,
+ QTextDocument *origin,
+ int currentCompletion = 0);
+
+ bool apply() final;
+ bool applyWord(TextEditor::TextEditorWidget *widget) final;
+ void reset() final;
+ int position() final;
+
+ const QList<Completion> &completions() const { return m_completions; }
+ int currentCompletion() const { return m_currentCompletion; }
+
+private:
+ QList<Completion> m_completions;
+ int m_currentCompletion = 0;
+ QTextCursor m_start;
+};
+} // namespace Copilot::Internal
diff --git a/src/plugins/copilot/copilottr.h b/src/plugins/copilot/copilottr.h
new file mode 100644
index 0000000000..42fef818db
--- /dev/null
+++ b/src/plugins/copilot/copilottr.h
@@ -0,0 +1,15 @@
+// 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 <QCoreApplication>
+
+namespace Copilot {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::Copilot)
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/images/settingscategory_copilot.png b/src/plugins/copilot/images/settingscategory_copilot.png
new file mode 100644
index 0000000000..674fece4c5
--- /dev/null
+++ b/src/plugins/copilot/images/settingscategory_copilot.png
Binary files differ
diff --git a/src/plugins/copilot/images/settingscategory_copilot@2x.png b/src/plugins/copilot/images/settingscategory_copilot@2x.png
new file mode 100644
index 0000000000..a2d45562f7
--- /dev/null
+++ b/src/plugins/copilot/images/settingscategory_copilot@2x.png
Binary files differ
diff --git a/src/plugins/copilot/requests/checkstatus.h b/src/plugins/copilot/requests/checkstatus.h
new file mode 100644
index 0000000000..20e77eb145
--- /dev/null
+++ b/src/plugins/copilot/requests/checkstatus.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+class CheckStatusParams : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t optionsKey[] = u"options";
+ static constexpr char16_t localChecksOnlyKey[] = u"options";
+
+public:
+ using JsonObject::JsonObject;
+
+ CheckStatusParams(bool localChecksOnly = false) { setLocalChecksOnly(localChecksOnly); }
+
+ void setLocalChecksOnly(bool localChecksOnly)
+ {
+ QJsonObject options;
+ options.insert(localChecksOnlyKey, localChecksOnly);
+ setOptions(options);
+ }
+
+ void setOptions(QJsonObject options) { insert(optionsKey, options); }
+};
+
+class CheckStatusResponse : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t userKey[] = u"user";
+ static constexpr char16_t statusKey[] = u"status";
+
+public:
+ using JsonObject::JsonObject;
+
+ QString status() const { return typedValue<QString>(statusKey); }
+ QString user() const { return typedValue<QString>(userKey); }
+};
+
+class CheckStatusRequest
+ : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, CheckStatusParams>
+{
+public:
+ explicit CheckStatusRequest(const CheckStatusParams &params)
+ : Request(methodName, params)
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "checkStatus";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/getcompletions.h b/src/plugins/copilot/requests/getcompletions.h
new file mode 100644
index 0000000000..d602fea97d
--- /dev/null
+++ b/src/plugins/copilot/requests/getcompletions.h
@@ -0,0 +1,119 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <languageserverprotocol/jsonkeys.h>
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+class Completion : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t displayTextKey[] = u"displayText";
+ static constexpr char16_t uuidKey[] = u"uuid";
+
+public:
+ using JsonObject::JsonObject;
+
+ QString displayText() const { return typedValue<QString>(displayTextKey); }
+ LanguageServerProtocol::Position position() const
+ {
+ return typedValue<LanguageServerProtocol::Position>(LanguageServerProtocol::positionKey);
+ }
+ LanguageServerProtocol::Range range() const
+ {
+ return typedValue<LanguageServerProtocol::Range>(LanguageServerProtocol::rangeKey);
+ }
+ QString text() const { return typedValue<QString>(LanguageServerProtocol::textKey); }
+ QString uuid() const { return typedValue<QString>(uuidKey); }
+
+ bool isValid() const override
+ {
+ return contains(LanguageServerProtocol::textKey)
+ && contains(LanguageServerProtocol::rangeKey)
+ && contains(LanguageServerProtocol::positionKey);
+ }
+};
+
+class GetCompletionParams : public LanguageServerProtocol::JsonObject
+{
+public:
+ static constexpr char16_t docKey[] = u"doc";
+
+ GetCompletionParams(const LanguageServerProtocol::TextDocumentIdentifier &document,
+ int version,
+ const LanguageServerProtocol::Position &position)
+ {
+ setTextDocument(document);
+ setVersion(version);
+ setPosition(position);
+ }
+ using JsonObject::JsonObject;
+
+ // The text document.
+ LanguageServerProtocol::TextDocumentIdentifier textDocument() const
+ {
+ return typedValue<LanguageServerProtocol::TextDocumentIdentifier>(docKey);
+ }
+ void setTextDocument(const LanguageServerProtocol::TextDocumentIdentifier &id)
+ {
+ insert(docKey, id);
+ }
+
+ // The position inside the text document.
+ LanguageServerProtocol::Position position() const
+ {
+ return LanguageServerProtocol::fromJsonValue<LanguageServerProtocol::Position>(
+ value(docKey).toObject().value(LanguageServerProtocol::positionKey));
+ }
+ void setPosition(const LanguageServerProtocol::Position &position)
+ {
+ QJsonObject result = value(docKey).toObject();
+ result[LanguageServerProtocol::positionKey] = (QJsonObject) position;
+ insert(docKey, result);
+ }
+
+ // The version
+ int version() const { return typedValue<int>(LanguageServerProtocol::versionKey); }
+ void setVersion(int version)
+ {
+ QJsonObject result = value(docKey).toObject();
+ result[LanguageServerProtocol::versionKey] = version;
+ insert(docKey, result);
+ }
+
+ bool isValid() const override
+ {
+ return contains(docKey)
+ && value(docKey).toObject().contains(LanguageServerProtocol::positionKey)
+ && value(docKey).toObject().contains(LanguageServerProtocol::versionKey);
+ }
+};
+
+class GetCompletionResponse : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t completionKey[] = u"completions";
+
+public:
+ using JsonObject::JsonObject;
+
+ LanguageServerProtocol::LanguageClientArray<Completion> completions() const
+ {
+ return clientArray<Completion>(completionKey);
+ }
+};
+
+class GetCompletionRequest
+ : public LanguageServerProtocol::Request<GetCompletionResponse, std::nullptr_t, GetCompletionParams>
+{
+public:
+ explicit GetCompletionRequest(const GetCompletionParams &params = {})
+ : Request(methodName, params)
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "getCompletionsCycling";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/signinconfirm.h b/src/plugins/copilot/requests/signinconfirm.h
new file mode 100644
index 0000000000..64f4ce7d53
--- /dev/null
+++ b/src/plugins/copilot/requests/signinconfirm.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "checkstatus.h"
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+class SignInConfirmParams : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t userCodeKey[] = u"userCode";
+
+public:
+ using JsonObject::JsonObject;
+
+ SignInConfirmParams(const QString &userCode) { setUserCode(userCode); }
+
+ void setUserCode(const QString &userCode) { insert(userCodeKey, userCode); }
+};
+
+class SignInConfirmRequest
+ : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SignInConfirmParams>
+{
+public:
+ explicit SignInConfirmRequest(const QString &userCode)
+ : Request(methodName, {userCode})
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "signInConfirm";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/signininitiate.h b/src/plugins/copilot/requests/signininitiate.h
new file mode 100644
index 0000000000..005205e6e0
--- /dev/null
+++ b/src/plugins/copilot/requests/signininitiate.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+using SignInInitiateParams = LanguageServerProtocol::JsonObject;
+
+class SignInInitiateResponse : public LanguageServerProtocol::JsonObject
+{
+ static constexpr char16_t verificationUriKey[] = u"verificationUri";
+ static constexpr char16_t userCodeKey[] = u"userCode";
+
+public:
+ using JsonObject::JsonObject;
+
+public:
+ QString verificationUri() const { return typedValue<QString>(verificationUriKey); }
+ QString userCode() const { return typedValue<QString>(userCodeKey); }
+};
+
+class SignInInitiateRequest : public LanguageServerProtocol::Request<SignInInitiateResponse,
+ std::nullptr_t,
+ SignInInitiateParams>
+{
+public:
+ explicit SignInInitiateRequest()
+ : Request(methodName, {})
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "signInInitiate";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/copilot/requests/signout.h b/src/plugins/copilot/requests/signout.h
new file mode 100644
index 0000000000..944c10d414
--- /dev/null
+++ b/src/plugins/copilot/requests/signout.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "checkstatus.h"
+
+#include <languageserverprotocol/jsonrpcmessages.h>
+#include <languageserverprotocol/lsptypes.h>
+
+namespace Copilot {
+
+using SignOutParams = LanguageServerProtocol::JsonObject;
+
+class SignOutRequest
+ : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SignOutParams>
+{
+public:
+ explicit SignOutRequest()
+ : Request(methodName, {})
+ {}
+ using Request::Request;
+ constexpr static const char methodName[] = "signOut";
+};
+
+} // namespace Copilot
diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt
index 8a680a0f7b..1507b345c9 100644
--- a/src/plugins/coreplugin/CMakeLists.txt
+++ b/src/plugins/coreplugin/CMakeLists.txt
@@ -72,8 +72,6 @@ add_qtc_plugin(Core
find/ifindsupport.cpp find/ifindsupport.h
find/itemviewfind.cpp find/itemviewfind.h
find/optionspopup.cpp find/optionspopup.h
- find/searchresultcolor.h
- find/searchresultitem.h
find/searchresulttreeitemdelegate.cpp find/searchresulttreeitemdelegate.h
find/searchresulttreeitemroles.h
find/searchresulttreeitems.cpp find/searchresulttreeitems.h
@@ -101,7 +99,6 @@ add_qtc_plugin(Core
iwelcomepage.cpp iwelcomepage.h
iwizardfactory.cpp iwizardfactory.h
jsexpander.cpp jsexpander.h
- locator/basefilefilter.cpp locator/basefilefilter.h
locator/commandlocator.cpp locator/commandlocator.h
locator/directoryfilter.cpp locator/directoryfilter.h
locator/executefilter.cpp locator/executefilter.h
@@ -113,7 +110,6 @@ add_qtc_plugin(Core
locator/locatorconstants.h
locator/locatorfiltersfilter.cpp locator/locatorfiltersfilter.h
locator/locatormanager.cpp locator/locatormanager.h
- locator/locatorsearchutils.cpp locator/locatorsearchutils.h
locator/locatorsettingspage.cpp locator/locatorsettingspage.h
locator/locatorwidget.cpp locator/locatorwidget.h
locator/opendocumentsfilter.cpp locator/opendocumentsfilter.h
@@ -150,7 +146,6 @@ add_qtc_plugin(Core
sidebar.cpp sidebar.h
sidebarwidget.cpp sidebarwidget.h
statusbarmanager.cpp statusbarmanager.h
- styleanimator.cpp styleanimator.h
systemsettings.cpp systemsettings.h
textdocument.cpp textdocument.h
themechooser.cpp themechooser.h
diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.cpp b/src/plugins/coreplugin/actionmanager/actionmanager.cpp
index 3d0a200283..54e58dc217 100644
--- a/src/plugins/coreplugin/actionmanager/actionmanager.cpp
+++ b/src/plugins/coreplugin/actionmanager/actionmanager.cpp
@@ -34,8 +34,6 @@ namespace Core::Internal {
class PresentationModeHandler : public QObject
{
- Q_OBJECT
-
public:
void connectCommand(Command *command);
@@ -520,5 +518,3 @@ void ActionManagerPrivate::saveSettings()
saveSettings(j.value());
}
}
-
-#include "actionmanager.moc"
diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.h b/src/plugins/coreplugin/actionmanager/commandmappings.h
index 67c864a0bf..a672526550 100644
--- a/src/plugins/coreplugin/actionmanager/commandmappings.h
+++ b/src/plugins/coreplugin/actionmanager/commandmappings.h
@@ -12,8 +12,6 @@ class QTreeWidget;
class QTreeWidgetItem;
QT_END_NAMESPACE
-namespace Utils { class FancyLineEdit; }
-
namespace Core {
namespace Internal { class CommandMappingsPrivate; }
diff --git a/src/plugins/coreplugin/actionsfilter.cpp b/src/plugins/coreplugin/actionsfilter.cpp
index 927e7da633..01772ac252 100644
--- a/src/plugins/coreplugin/actionsfilter.cpp
+++ b/src/plugins/coreplugin/actionsfilter.cpp
@@ -10,9 +10,11 @@
#include "icore.h"
#include "locator/locatormanager.h"
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fuzzymatcher.h>
-#include <utils/mapreduce.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -21,8 +23,11 @@
#include <QMenuBar>
#include <QPointer>
#include <QRegularExpression>
+#include <QtConcurrent>
#include <QTextDocument>
+using namespace Utils;
+
static const char lastTriggeredC[] = "LastTriggeredActions";
QT_BEGIN_NAMESPACE
@@ -34,6 +39,13 @@ QT_END_NAMESPACE
namespace Core::Internal {
+static const QList<QAction *> menuBarActions()
+{
+ QMenuBar *menuBar = Core::ActionManager::actionContainer(Constants::MENU_BAR)->menuBar();
+ QTC_ASSERT(menuBar, return {});
+ return menuBar->actions();
+}
+
ActionsFilter::ActionsFilter()
{
setId("Actions from the menu");
@@ -50,29 +62,21 @@ ActionsFilter::ActionsFilter()
});
}
-static const QList<QAction *> menuBarActions()
-{
- QMenuBar *menuBar = Core::ActionManager::actionContainer(Constants::MENU_BAR)->menuBar();
- QTC_ASSERT(menuBar, return {});
- return menuBar->actions();
-}
-
-QList<LocatorFilterEntry> ActionsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const LocatorFilterEntries &entries)
{
using Highlight = LocatorFilterEntry::HighlightInfo;
- if (entry.simplified().isEmpty())
- return m_entries;
-
- const QRegularExpression regExp = createRegExp(entry, Qt::CaseInsensitive, true);
-
+ using MatchLevel = ILocatorFilter::MatchLevel;
+ const QString input = storage.input();
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(input, Qt::CaseInsensitive,
+ true);
using FilterResult = std::pair<MatchLevel, LocatorFilterEntry>;
const auto filter = [&](const LocatorFilterEntry &filterEntry) -> std::optional<FilterResult> {
- if (future.isCanceled())
+ if (promise.isCanceled())
return {};
Highlight highlight;
- const auto withHighlight = [&](LocatorFilterEntry result) {
+ const auto withHighlight = [&highlight](LocatorFilterEntry result) {
result.highlightInfo = highlight;
return result;
};
@@ -89,17 +93,18 @@ QList<LocatorFilterEntry> ActionsFilter::matchesFor(QFutureInterface<LocatorFilt
if (first == Highlight::DisplayName) {
const QRegularExpressionMatch displayMatch = regExp.match(filterEntry.displayName);
if (displayMatch.hasMatch())
- highlight = highlightInfo(displayMatch);
+ highlight = ILocatorFilter::highlightInfo(displayMatch);
const QRegularExpressionMatch extraMatch = regExp.match(filterEntry.extraInfo);
if (extraMatch.hasMatch()) {
- Highlight extraHighlight = highlightInfo(extraMatch, Highlight::ExtraInfo);
+ Highlight extraHighlight = ILocatorFilter::highlightInfo(extraMatch,
+ Highlight::ExtraInfo);
highlight.startsExtraInfo = extraHighlight.startsExtraInfo;
highlight.lengthsExtraInfo = extraHighlight.lengthsExtraInfo;
}
- if (filterEntry.displayName.startsWith(entry, Qt::CaseInsensitive))
+ if (filterEntry.displayName.startsWith(input, Qt::CaseInsensitive))
return FilterResult{MatchLevel::Best, withHighlight(filterEntry)};
- if (filterEntry.displayName.contains(entry, Qt::CaseInsensitive))
+ if (filterEntry.displayName.contains(input, Qt::CaseInsensitive))
return FilterResult{MatchLevel::Better, withHighlight(filterEntry)};
if (displayMatch.hasMatch())
return FilterResult{MatchLevel::Good, withHighlight(filterEntry)};
@@ -154,29 +159,53 @@ QList<LocatorFilterEntry> ActionsFilter::matchesFor(QFutureInterface<LocatorFilt
return {};
};
- QMap<MatchLevel, QList<LocatorFilterEntry>> filtered;
- const QList<std::optional<FilterResult>> filterResults = Utils::map(std::as_const(m_entries), filter)
- .results();
+ QMap<MatchLevel, LocatorFilterEntries> filtered;
+ const QList<std::optional<FilterResult>> filterResults
+ = QtConcurrent::blockingMapped(entries, filter);
+ if (promise.isCanceled())
+ return;
for (const std::optional<FilterResult> &filterResult : filterResults) {
+ if (promise.isCanceled())
+ return;
if (filterResult)
filtered[filterResult->first] << filterResult->second;
}
+ storage.reportOutput(std::accumulate(std::begin(filtered), std::end(filtered),
+ LocatorFilterEntries()));
+}
- QList<LocatorFilterEntry> result;
- for (const QList<LocatorFilterEntry> &sublist : std::as_const(filtered))
- result << sublist;
- return result;
+LocatorMatcherTasks ActionsFilter::matchers()
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [this, storage](Async<void> &async) {
+ m_entries.clear();
+ m_indexes.clear();
+ QList<const QMenu *> processedMenus;
+ collectEntriesForLastTriggered();
+ for (QAction* action : menuBarActions())
+ collectEntriesForAction(action, {}, processedMenus);
+ collectEntriesForCommands();
+ if (storage->input().simplified().isEmpty()) {
+ storage->reportOutput(m_entries);
+ return TaskAction::StopWithDone;
+ }
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, m_entries);
+ return TaskAction::Continue;
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
-void ActionsFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+LocatorFilterEntry::Acceptor ActionsFilter::acceptor(const ActionFilterEntryData &data) const
{
static const int maxHistorySize = 30;
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- auto data = selection.internalData.value<ActionFilterEntryData>();
- if (data.action) {
+ return [this, data] {
+ if (!data.action)
+ return AcceptResult();
m_lastTriggered.removeAll(data);
m_lastTriggered.prepend(data);
QMetaObject::invokeMethod(data.action, [action = data.action] {
@@ -185,7 +214,8 @@ void ActionsFilter::accept(const LocatorFilterEntry &selection, QString *newText
}, Qt::QueuedConnection);
if (m_lastTriggered.size() > maxHistorySize)
m_lastTriggered.resize(maxHistorySize);
- }
+ return AcceptResult();
+ };
}
static QString actionText(QAction *action)
@@ -214,8 +244,10 @@ void ActionsFilter::collectEntriesForAction(QAction *action,
collectEntriesForAction(menuAction, menuPath, processedMenus);
}
} else if (!text.isEmpty()) {
- const ActionFilterEntryData data{action, {}};
- LocatorFilterEntry filterEntry(this, text, QVariant::fromValue(data), action->icon());
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = text;
+ filterEntry.acceptor = acceptor({action, {}});
+ filterEntry.displayIcon = action->icon();
filterEntry.extraInfo = path.join(" > ");
updateEntry(action, filterEntry);
}
@@ -241,8 +273,10 @@ void ActionsFilter::collectEntriesForCommands()
const QString identifier = command->id().toString();
const QStringList path = identifier.split(QLatin1Char('.'));
- const ActionFilterEntryData data{action, command->id()};
- LocatorFilterEntry filterEntry(this, text, QVariant::fromValue(data), action->icon());
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = text;
+ filterEntry.acceptor = acceptor({action, command->id()});
+ filterEntry.displayIcon = action->icon();
filterEntry.displayExtra = command->keySequence().toString(QKeySequence::NativeText);
if (path.size() >= 2)
filterEntry.extraInfo = path.mid(0, path.size() - 1).join(" > ");
@@ -259,15 +293,17 @@ void ActionsFilter::collectEntriesForLastTriggered()
}
if (!data.action || !m_enabledActions.contains(data.action))
continue;
- const QString text = actionText(data.action);
- LocatorFilterEntry filterEntry(this, text, QVariant::fromValue(data), data.action->icon());
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = actionText(data.action);
+ filterEntry.acceptor = acceptor(data);
+ filterEntry.displayIcon = data.action->icon();
updateEntry(data.action, filterEntry);
}
}
void ActionsFilter::updateEntry(const QPointer<QAction> action, const LocatorFilterEntry &entry)
{
- auto index = m_indexes.value(action, -1);
+ const int index = m_indexes.value(action, -1);
if (index < 0) {
m_indexes[action] = m_entries.size();
m_entries << entry;
@@ -311,18 +347,6 @@ void ActionsFilter::updateEnabledActionCache()
}
}
-void Core::Internal::ActionsFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- m_entries.clear();
- m_indexes.clear();
- QList<const QMenu *> processedMenus;
- collectEntriesForLastTriggered();
- for (QAction* action : menuBarActions())
- collectEntriesForAction(action, QStringList(), processedMenus);
- collectEntriesForCommands();
-}
-
void ActionsFilter::saveState(QJsonObject &object) const
{
QJsonArray commands;
@@ -336,9 +360,10 @@ void ActionsFilter::saveState(QJsonObject &object) const
void ActionsFilter::restoreState(const QJsonObject &object)
{
m_lastTriggered.clear();
- for (const QJsonValue &command : object.value(lastTriggeredC).toArray()) {
+ const QJsonArray commands = object.value(lastTriggeredC).toArray();
+ for (const QJsonValue &command : commands) {
if (command.isString())
- m_lastTriggered.append({nullptr, Utils::Id::fromString(command.toString())});
+ m_lastTriggered.append({nullptr, Id::fromString(command.toString())});
}
}
diff --git a/src/plugins/coreplugin/actionsfilter.h b/src/plugins/coreplugin/actionsfilter.h
index 50a993ad01..692dea95d2 100644
--- a/src/plugins/coreplugin/actionsfilter.h
+++ b/src/plugins/coreplugin/actionsfilter.h
@@ -14,8 +14,7 @@ class QAction;
class QMenu;
QT_END_NAMESPACE
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class ActionFilterEntryData
{
@@ -30,19 +29,14 @@ public:
class ActionsFilter : public ILocatorFilter
{
- Q_OBJECT
public:
ActionsFilter();
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
- void prepareSearch(const QString &entry) override;
-
private:
+ LocatorMatcherTasks matchers() final;
void saveState(QJsonObject &object) const override;
void restoreState(const QJsonObject &object) override;
+ LocatorFilterEntry::Acceptor acceptor(const ActionFilterEntryData &data) const;
void collectEntriesForAction(QAction *action,
const QStringList &path,
QList<const QMenu *> &processedMenus);
@@ -51,11 +45,10 @@ private:
void updateEntry(const QPointer<QAction> action, const LocatorFilterEntry &entry);
void updateEnabledActionCache();
- QList<LocatorFilterEntry> m_entries;
+ LocatorFilterEntries m_entries;
QMap<QPointer<QAction>, int> m_indexes;
QSet<QPointer<QAction>> m_enabledActions;
mutable QList<ActionFilterEntryData> m_lastTriggered;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h
index e61e3345be..0b4831e51b 100644
--- a/src/plugins/coreplugin/coreconstants.h
+++ b/src/plugins/coreplugin/coreconstants.h
@@ -215,5 +215,7 @@ const int MODEBAR_ICON_SIZE = 34;
const int MODEBAR_ICONSONLY_BUTTON_SIZE = MODEBAR_ICON_SIZE + 4;
const int DEFAULT_MAX_CHAR_COUNT = 10000000;
+const char SETTINGS_MENU_HIDE_TOOLS[] = "Menu/HideTools";
+
} // namespace Constants
} // namespace Core
diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp
index 043bdba74d..bf8e09b67b 100644
--- a/src/plugins/coreplugin/coreplugin.cpp
+++ b/src/plugins/coreplugin/coreplugin.cpp
@@ -6,7 +6,6 @@
#include "designmode.h"
#include "editmode.h"
#include "foldernavigationwidget.h"
-#include "helpmanager.h"
#include "icore.h"
#include "idocument.h"
#include "iwizardfactory.h"
@@ -36,6 +35,7 @@
#include <utils/pathchooser.h>
#include <utils/savefile.h>
#include <utils/stringutils.h>
+#include <utils/textutils.h>
#include <utils/theme/theme.h>
#include <utils/theme/theme_p.h>
@@ -71,7 +71,7 @@ void CorePlugin::setupSystemEnvironment()
CorePlugin::CorePlugin()
{
qRegisterMetaType<Id>();
- qRegisterMetaType<Core::Search::TextPosition>();
+ qRegisterMetaType<Utils::Text::Position>();
qRegisterMetaType<Utils::CommandLine>();
qRegisterMetaType<Utils::FilePath>();
qRegisterMetaType<Utils::Environment>();
@@ -471,8 +471,7 @@ QString CorePlugin::msgCrashpadInformation()
ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown()
{
Find::aboutToShutdown();
- ExtensionSystem::IPlugin::ShutdownFlag shutdownFlag = m_locator->aboutToShutdown(
- [this] { emit asynchronousShutdownFinished(); });
+ m_locator->aboutToShutdown();
m_mainWindow->aboutToShutdown();
- return shutdownFlag;
+ return SynchronousShutdown;
}
diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index 7153c5415b..3f535be8a6 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -52,7 +52,7 @@ public:
static QString msgCrashpadInformation();
public slots:
- void fileOpenRequest(const QString&);
+ void fileOpenRequest(const QString &);
#if defined(WITH_TESTS)
private slots:
diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs
index be30292174..2467567f01 100644
--- a/src/plugins/coreplugin/coreplugin.qbs
+++ b/src/plugins/coreplugin/coreplugin.qbs
@@ -157,8 +157,6 @@ Project {
"sidebarwidget.h",
"statusbarmanager.cpp",
"statusbarmanager.h",
- "styleanimator.cpp",
- "styleanimator.h",
"systemsettings.cpp",
"systemsettings.h",
"textdocument.cpp",
@@ -271,9 +269,7 @@ Project {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"testdatadir.cpp",
"testdatadir.h",
@@ -310,8 +306,6 @@ Project {
"itemviewfind.h",
"optionspopup.cpp",
"optionspopup.h",
- "searchresultcolor.h",
- "searchresultitem.h",
"searchresulttreeitemdelegate.cpp",
"searchresulttreeitemdelegate.h",
"searchresulttreeitemroles.h",
@@ -333,8 +327,6 @@ Project {
name: "Locator"
prefix: "locator/"
files: [
- "basefilefilter.cpp",
- "basefilefilter.h",
"commandlocator.cpp",
"commandlocator.h",
"directoryfilter.cpp",
@@ -356,8 +348,6 @@ Project {
"locatormanager.h",
"locator.cpp",
"locator.h",
- "locatorsearchutils.cpp",
- "locatorsearchutils.h",
"locatorsettingspage.cpp",
"locatorsettingspage.h",
"locatorwidget.cpp",
diff --git a/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp b/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp
index 7750cfcc17..09a4bf4592 100644
--- a/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/addtovcsdialog.cpp
@@ -22,7 +22,7 @@ AddToVcsDialog::AddToVcsDialog(QWidget *parent,
const QString &vcsDisplayName)
: QDialog(parent)
{
- using namespace Utils::Layouting;
+ using namespace Layouting;
resize(363, 375);
setMinimumSize({200, 200});
@@ -33,7 +33,7 @@ AddToVcsDialog::AddToVcsDialog(QWidget *parent,
filesListWidget->setSelectionMode(QAbstractItemView::NoSelection);
filesListWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
- QWidget *scrollAreaWidgetContents = Column{filesListWidget}.emerge(WithoutMargins);
+ QWidget *scrollAreaWidgetContents = Column{filesListWidget, noMargin}.emerge();
scrollAreaWidgetContents->setGeometry({0, 0, 341, 300});
auto scrollArea = new QScrollArea;
diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
index 270ed9f681..59237aad79 100644
--- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
+++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp
@@ -18,8 +18,8 @@
#include <utils/layoutbuilder.h>
#include <utils/macroexpander.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
@@ -564,11 +564,11 @@ ExternalToolConfig::ExternalToolConfig()
Tr::tr("Environment:"), m_environmentLabel, environmentButton, br,
empty, m_modifiesDocumentCheckbox, br,
inputLabel, m_inputText
- }.attachTo(m_infoWidget, WithMargins);
+ }.attachTo(m_infoWidget);
Column {
- m_infoWidget
- }.attachTo(scrollAreaWidgetContents, WithoutMargins);
+ m_infoWidget, noMargin
+ }.attachTo(scrollAreaWidgetContents);
Row {
Column {
diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.cpp b/src/plugins/coreplugin/dialogs/ioptionspage.cpp
index 451859c527..bb9b80a913 100644
--- a/src/plugins/coreplugin/dialogs/ioptionspage.cpp
+++ b/src/plugins/coreplugin/dialogs/ioptionspage.cpp
@@ -9,6 +9,7 @@
#include <coreplugin/icore.h>
#include <utils/aspects.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -135,9 +136,6 @@ QWidget *IOptionsPage::widget()
if (!m_widget) {
if (m_widgetCreator) {
m_widget = m_widgetCreator();
- } else if (m_layouter) {
- m_widget = new QWidget;
- m_layouter(m_widget);
} else {
QTC_CHECK(false);
}
@@ -156,9 +154,10 @@ QWidget *IOptionsPage::widget()
void IOptionsPage::apply()
{
- if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget)) {
+ if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget))
widget->apply();
- } else if (m_settings) {
+
+ if (m_settings) {
if (m_settings->isDirty()) {
m_settings->apply();
m_settings->writeSettings(ICore::settings());
@@ -179,7 +178,8 @@ void IOptionsPage::finish()
{
if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget))
widget->finish();
- else if (m_settings)
+
+ if (m_settings)
m_settings->finish();
delete m_widget;
@@ -201,7 +201,11 @@ void IOptionsPage::setSettings(AspectContainer *settings)
void IOptionsPage::setLayouter(const std::function<void(QWidget *w)> &layouter)
{
- m_layouter = layouter;
+ m_widgetCreator = [layouter] {
+ auto widget = new IOptionsPageWidget;
+ layouter(widget);
+ return widget;
+ };
}
/*!
@@ -240,8 +244,7 @@ static QList<IOptionsPage *> g_optionsPages;
Constructs an options page with the given \a parent and registers it
at the global options page pool if \a registerGlobally is \c true.
*/
-IOptionsPage::IOptionsPage(QObject *parent, bool registerGlobally)
- : QObject(parent)
+IOptionsPage::IOptionsPage(bool registerGlobally)
{
if (registerGlobally)
g_optionsPages.append(this);
@@ -278,8 +281,7 @@ bool IOptionsPage::matches(const QRegularExpression &regexp) const
static QList<IOptionsPageProvider *> g_optionsPagesProviders;
-IOptionsPageProvider::IOptionsPageProvider(QObject *parent)
- : QObject(parent)
+IOptionsPageProvider::IOptionsPageProvider()
{
g_optionsPagesProviders.append(this);
}
@@ -299,4 +301,17 @@ QIcon IOptionsPageProvider::categoryIcon() const
return m_categoryIcon.icon();
}
+// PagedSettings
+
+PagedSettings::PagedSettings()
+{
+ setSettings(this);
+ setAutoApply(false);
+}
+
+void PagedSettings::readSettings()
+{
+ return AspectContainer::readSettings(Core::ICore::settings());
+}
+
} // Core
diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h
index 300ecda4a0..96d9b6824a 100644
--- a/src/plugins/coreplugin/dialogs/ioptionspage.h
+++ b/src/plugins/coreplugin/dialogs/ioptionspage.h
@@ -5,6 +5,7 @@
#include <coreplugin/core_global.h>
+#include <utils/aspects.h>
#include <utils/icon.h>
#include <utils/id.h>
@@ -15,6 +16,8 @@
#include <functional>
+namespace Layouting { class LayoutItem; };
+
namespace Utils { class AspectContainer; };
namespace Core {
@@ -22,18 +25,28 @@ namespace Core {
class CORE_EXPORT IOptionsPageWidget : public QWidget
{
Q_OBJECT
+
public:
- virtual void apply() = 0;
- virtual void finish() {}
+ void setOnApply(const std::function<void ()> &func) { m_onApply = func; }
+ void setOnFinish(const std::function<void ()> &func) { m_onFinish = func; }
+
+protected:
+ friend class IOptionsPage;
+ virtual void apply() { if (m_onApply) m_onApply(); }
+ virtual void finish() { if (m_onFinish) m_onFinish(); }
+
+private:
+ std::function<void()> m_onApply;
+ std::function<void()> m_onFinish;
};
-class CORE_EXPORT IOptionsPage : public QObject
+class CORE_EXPORT IOptionsPage
{
- Q_OBJECT
+ Q_DISABLE_COPY_MOVE(IOptionsPage)
public:
- IOptionsPage(QObject *parent = nullptr, bool registerGlobally = true);
- ~IOptionsPage() override;
+ explicit IOptionsPage(bool registerGlobally = true);
+ virtual ~IOptionsPage();
static const QList<IOptionsPage *> allOptionsPages();
@@ -78,7 +91,6 @@ private:
mutable QStringList m_keywords;
Utils::AspectContainer *m_settings = nullptr;
- std::function<void(QWidget *w)> m_layouter;
};
/*
@@ -89,13 +101,13 @@ private:
before the options pages get available.)
*/
-class CORE_EXPORT IOptionsPageProvider : public QObject
+class CORE_EXPORT IOptionsPageProvider
{
- Q_OBJECT
+ Q_DISABLE_COPY_MOVE(IOptionsPageProvider);
public:
- IOptionsPageProvider(QObject *parent = nullptr);
- ~IOptionsPageProvider() override;
+ IOptionsPageProvider();
+ virtual ~IOptionsPageProvider();
static const QList<IOptionsPageProvider *> allOptionsPagesProviders();
@@ -116,4 +128,13 @@ protected:
Utils::Icon m_categoryIcon;
};
+class CORE_EXPORT PagedSettings : public Utils::AspectContainer, public IOptionsPage
+{
+public:
+ PagedSettings();
+
+ using AspectContainer::readSettings; // FIXME: Remove.
+ void readSettings(); // Intentionally hides AspectContainer::readSettings()
+};
+
} // namespace Core
diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.cpp b/src/plugins/coreplugin/dialogs/openwithdialog.cpp
index ddec97928b..ff8db72ddb 100644
--- a/src/plugins/coreplugin/dialogs/openwithdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/openwithdialog.cpp
@@ -25,7 +25,7 @@ OpenWithDialog::OpenWithDialog(const Utils::FilePath &filePath, QWidget *parent)
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
// clang-format off
Column {
Tr::tr("Open file \"%1\" with:").arg(filePath.fileName()),
diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
index 97d35aca82..88431454fe 100644
--- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp
@@ -422,8 +422,12 @@ void ReadOnlyFilesDialogPrivate::initDialog(const FilePaths &filePaths)
using namespace Layouting;
- QWidget *setAllWidget = Row{Tr::tr("Select all, if possible: "), m_setAll, st}.emerge(
- WithoutMargins);
+ QWidget *setAllWidget = Row {
+ Tr::tr("Select all, if possible: "),
+ m_setAll,
+ st,
+ noMargin
+ }.emerge();
// clang-format off
Column {
diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp
index 87953b3ab9..3b9034ddd7 100644
--- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp
@@ -59,7 +59,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, const QList<IDocument *> &item
m_saveBeforeBuildCheckBox->setVisible(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
// clang-format off
Column {
m_msgLabel,
diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp
index 5b760f8ff1..f429994edd 100644
--- a/src/plugins/coreplugin/dialogs/settingsdialog.cpp
+++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp
@@ -237,15 +237,33 @@ protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
+const char SETTING_HIDE_OPTION_CATEGORIES[] = "HideOptionCategories";
+
+static bool categoryVisible(const Id &id)
+{
+ static QStringList list
+ = Core::ICore::settings()->value(SETTING_HIDE_OPTION_CATEGORIES).toStringList();
+
+ if (anyOf(list, [id](const QString &str) { return id.toString().contains(str); }))
+ return false;
+
+ return true;
+}
+
bool CategoryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
+ const CategoryModel *cm = static_cast<CategoryModel *>(sourceModel());
+ const Category *category = cm->categories().at(sourceRow);
+
+ if (!categoryVisible(category->id))
+ return false;
+
// Regular contents check, then check page-filter.
if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent))
return true;
const QRegularExpression regex = filterRegularExpression();
- const CategoryModel *cm = static_cast<CategoryModel*>(sourceModel());
- const Category *category = cm->categories().at(sourceRow);
+
for (const IOptionsPage *page : category->pages) {
if (page->displayCategory().contains(regex) || page->displayName().contains(regex)
|| page->matches(regex))
@@ -313,14 +331,26 @@ public:
class SmartScrollArea : public QScrollArea
{
public:
- explicit SmartScrollArea(QWidget *parent)
- : QScrollArea(parent)
+ explicit SmartScrollArea(QWidget *parent, IOptionsPage *page)
+ : QScrollArea(parent), m_page(page)
{
setFrameStyle(QFrame::NoFrame | QFrame::Plain);
viewport()->setAutoFillBackground(false);
setWidgetResizable(true);
}
+
private:
+ void showEvent(QShowEvent *event) final
+ {
+ if (!widget()) {
+ QWidget *inner = m_page->widget();
+ setWidget(inner);
+ inner->setAutoFillBackground(false);
+ }
+
+ QScrollArea::showEvent(event);
+ }
+
void resizeEvent(QResizeEvent *event) final
{
QWidget *inner = widget();
@@ -369,6 +399,8 @@ private:
return 0;
return list.first()->sizeHint().width();
}
+
+ IOptionsPage *m_page = nullptr;
};
// ----------- SettingsDialog
@@ -586,14 +618,8 @@ void SettingsDialog::ensureCategoryWidget(Category *category)
m_model.ensurePages(category);
auto tabWidget = new QTabWidget;
tabWidget->tabBar()->setObjectName("qc_settings_main_tabbar"); // easier lookup in Squish
- for (IOptionsPage *page : std::as_const(category->pages)) {
- QWidget *widget = page->widget();
- ICore::setupScreenShooter(page->displayName(), widget);
- auto ssa = new SmartScrollArea(this);
- ssa->setWidget(widget);
- widget->setAutoFillBackground(false);
- tabWidget->addTab(ssa, page->displayName());
- }
+ for (IOptionsPage *page : std::as_const(category->pages))
+ tabWidget->addTab(new SmartScrollArea(this, page), page->displayName());
connect(tabWidget, &QTabWidget::currentChanged,
this, &SettingsDialog::currentTabChanged);
diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
index 598cf92d99..c846fb9dae 100644
--- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
+++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp
@@ -268,10 +268,8 @@ ShortcutSettingsWidget::ShortcutSettingsWidget()
this, &ShortcutSettingsWidget::initialize);
connect(this, &ShortcutSettingsWidget::currentCommandChanged,
this, &ShortcutSettingsWidget::handleCurrentCommandChanged);
- connect(this,
- &ShortcutSettingsWidget::resetRequested,
- this,
- &ShortcutSettingsWidget::resetToDefault);
+ connect(this, &ShortcutSettingsWidget::resetRequested,
+ this, &ShortcutSettingsWidget::resetToDefault);
m_shortcutBox = new QGroupBox(Tr::tr("Shortcut"), this);
m_shortcutBox->setEnabled(false);
@@ -287,37 +285,12 @@ ShortcutSettingsWidget::~ShortcutSettingsWidget()
qDeleteAll(m_scitems);
}
-ShortcutSettings::ShortcutSettings()
-{
- setId(Constants::SETTINGS_ID_SHORTCUTS);
- setDisplayName(Tr::tr("Keyboard"));
- setCategory(Constants::SETTINGS_CATEGORY_CORE);
-}
-
-QWidget *ShortcutSettings::widget()
-{
- if (!m_widget)
- m_widget = new ShortcutSettingsWidget();
- return m_widget;
-}
-
void ShortcutSettingsWidget::apply()
{
for (const ShortcutItem *item : std::as_const(m_scitems))
item->m_cmd->setKeySequences(item->m_keys);
}
-void ShortcutSettings::apply()
-{
- QTC_ASSERT(m_widget, return);
- m_widget->apply();
-}
-
-void ShortcutSettings::finish()
-{
- delete m_widget;
-}
-
ShortcutItem *shortcutItem(QTreeWidgetItem *treeItem)
{
if (!treeItem)
@@ -706,5 +679,31 @@ void ShortcutInput::setConflictChecker(const ShortcutInput::ConflictChecker &fun
m_conflictChecker = fun;
}
+// ShortcutSettingsPageWidget
+
+class ShortcutSettingsPageWidget : public IOptionsPageWidget
+{
+public:
+ ShortcutSettingsPageWidget()
+ {
+ auto inner = new ShortcutSettingsWidget;
+ auto vbox = new QVBoxLayout(this);
+ vbox->addWidget(inner);
+ vbox->setContentsMargins(0, 0, 0, 0);
+
+ setOnApply([inner] { inner->apply(); });
+ }
+};
+
+// ShortcutSettings
+
+ShortcutSettings::ShortcutSettings()
+{
+ setId(Constants::SETTINGS_ID_SHORTCUTS);
+ setDisplayName(Tr::tr("Keyboard"));
+ setCategory(Constants::SETTINGS_CATEGORY_CORE);
+ setWidgetCreator([] { return new ShortcutSettingsPageWidget; });
+}
+
} // namespace Internal
} // namespace Core
diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.h b/src/plugins/coreplugin/dialogs/shortcutsettings.h
index d7edfcad29..49297b3d50 100644
--- a/src/plugins/coreplugin/dialogs/shortcutsettings.h
+++ b/src/plugins/coreplugin/dialogs/shortcutsettings.h
@@ -14,19 +14,17 @@
#include <array>
QT_BEGIN_NAMESPACE
-class QGroupBox;
class QLabel;
QT_END_NAMESPACE
+namespace Utils { class FancyLineEdit; }
+
namespace Core {
class Command;
namespace Internal {
-class ActionManagerPrivate;
-class ShortcutSettingsWidget;
-
struct ShortcutItem
{
Command *m_cmd;
@@ -90,13 +88,6 @@ class ShortcutSettings final : public IOptionsPage
{
public:
ShortcutSettings();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<ShortcutSettingsWidget> m_widget;
};
} // namespace Internal
diff --git a/src/plugins/coreplugin/editormanager/documentmodel.cpp b/src/plugins/coreplugin/editormanager/documentmodel.cpp
index 04a877ec49..876b22b128 100644
--- a/src/plugins/coreplugin/editormanager/documentmodel.cpp
+++ b/src/plugins/coreplugin/editormanager/documentmodel.cpp
@@ -11,6 +11,8 @@
#include <utils/algorithm.h>
#include <utils/dropsupport.h>
+#include <utils/filepath.h>
+#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -121,63 +123,55 @@ DocumentModel::Entry *DocumentModelPrivate::addEntry(DocumentModel::Entry *entry
bool DocumentModelPrivate::disambiguateDisplayNames(DocumentModel::Entry *entry)
{
const QString displayName = entry->plainDisplayName();
- int minIdx = -1, maxIdx = -1;
- QList<DynamicEntry> dups;
+ QList<DocumentModel::Entry *> dups;
+ FilePaths paths;
+ int minIdx = m_entries.count();
+ int maxIdx = 0;
- for (int i = 0, total = m_entries.count(); i < total; ++i) {
+ for (int i = 0; i < m_entries.count(); ++i) {
DocumentModel::Entry *e = m_entries.at(i);
if (e == entry || e->plainDisplayName() == displayName) {
- e->document->setUniqueDisplayName(QString());
- dups += DynamicEntry(e);
- maxIdx = i;
- if (minIdx < 0)
+ if (minIdx > i)
minIdx = i;
+ if (maxIdx < i)
+ maxIdx = i;
+ dups += e;
+ if (!e->filePath().isEmpty())
+ paths += e->filePath();
}
}
- const int dupsCount = dups.count();
- if (dupsCount == 0)
+ const auto triggerDataChanged = [this](int minIdx, int maxIdx) {
+ const QModelIndex idxMin = index(minIdx + 1 /*<no document>*/, 0);
+ const QModelIndex idxMax = index(maxIdx + 1 /*<no document>*/, 0);
+ if (idxMin.isValid() && idxMax.isValid())
+ emit dataChanged(idxMin, idxMax);
+ };
+
+ if (dups.count() == 1) {
+ dups.at(0)->document->setUniqueDisplayName({});
+ triggerDataChanged(minIdx, maxIdx);
return false;
+ }
- if (dupsCount > 1) {
- int serial = 0;
- int count = 0;
- // increase uniqueness unless no dups are left
- forever {
- bool seenDups = false;
- for (int i = 0; i < dupsCount - 1; ++i) {
- DynamicEntry &e = dups[i];
- const Utils::FilePath myFileName = e->document->filePath();
- if (e->document->isTemporary() || myFileName.isEmpty() || count > 10) {
- // path-less entry, append number
- e.setNumberedName(++serial);
- continue;
- }
- for (int j = i + 1; j < dupsCount; ++j) {
- DynamicEntry &e2 = dups[j];
- if (e->displayName().compare(e2->displayName(), Utils::HostOsInfo::fileNameCaseSensitivity()) == 0) {
- const Utils::FilePath otherFileName = e2->document->filePath();
- if (otherFileName.isEmpty())
- continue;
- seenDups = true;
- e2.disambiguate();
- if (j > maxIdx)
- maxIdx = j;
- }
- }
- if (seenDups) {
- e.disambiguate();
- ++count;
- break;
- }
- }
- if (!seenDups)
- break;
+ const FilePath commonAncestor = FileUtils::commonPath(paths);
+
+ int countWithoutFilePath = 0;
+ for (DocumentModel::Entry *e : std::as_const(dups)) {
+ const FilePath path = e->filePath();
+ if (path.isEmpty()) {
+ e->document->setUniqueDisplayName(QStringLiteral("%1 (%2)")
+ .arg(e->document->displayName())
+ .arg(++countWithoutFilePath));
+ continue;
+ }
+ const QString uniqueDisplayName = path.relativeChildPath(commonAncestor).toString();
+ if (uniqueDisplayName != "" && e->document->uniqueDisplayName() != uniqueDisplayName) {
+ e->document->setUniqueDisplayName(uniqueDisplayName);
}
}
-
- emit dataChanged(index(minIdx + 1, 0), index(maxIdx + 1, 0));
+ triggerDataChanged(minIdx, maxIdx);
return true;
}
@@ -483,30 +477,6 @@ void DocumentModelPrivate::removeAllSuspendedEntries(PinnedFileRemovalPolicy pin
}
}
-DocumentModelPrivate::DynamicEntry::DynamicEntry(DocumentModel::Entry *e) :
- entry(e),
- pathComponents(0)
-{
-}
-
-DocumentModel::Entry *DocumentModelPrivate::DynamicEntry::operator->() const
-{
- return entry;
-}
-
-void DocumentModelPrivate::DynamicEntry::disambiguate()
-{
- const QString display = entry->filePath().fileNameWithPathComponents(++pathComponents);
- entry->document->setUniqueDisplayName(display);
-}
-
-void DocumentModelPrivate::DynamicEntry::setNumberedName(int number)
-{
- entry->document->setUniqueDisplayName(QStringLiteral("%1 (%2)")
- .arg(entry->document->displayName())
- .arg(number));
-}
-
} // Internal
DocumentModel::Entry::Entry() :
diff --git a/src/plugins/coreplugin/editormanager/documentmodel_p.h b/src/plugins/coreplugin/editormanager/documentmodel_p.h
index 8e3efa98b0..6b99d3fe43 100644
--- a/src/plugins/coreplugin/editormanager/documentmodel_p.h
+++ b/src/plugins/coreplugin/editormanager/documentmodel_p.h
@@ -61,18 +61,6 @@ public:
void itemChanged(IDocument *document);
- class DynamicEntry
- {
- public:
- DocumentModel::Entry *entry;
- int pathComponents;
-
- DynamicEntry(DocumentModel::Entry *e);
- DocumentModel::Entry *operator->() const;
- void disambiguate();
- void setNumberedName(int number);
- };
-
QList<DocumentModel::Entry *> m_entries;
QMap<IDocument *, QList<IEditor *> > m_editors;
QHash<Utils::FilePath, DocumentModel::Entry *> m_entryByFixedPath;
diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp
index 2953a833b7..107ce249e1 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.cpp
+++ b/src/plugins/coreplugin/editormanager/editormanager.cpp
@@ -24,10 +24,10 @@
#include "../editormanager/ieditorfactory_p.h"
#include "../editormanager/iexternaleditor.h"
#include "../fileutils.h"
-#include "../find/searchresultitem.h"
#include "../findplaceholder.h"
#include "../icore.h"
#include "../iversioncontrol.h"
+#include "../locator/ilocatorfilter.h"
#include "../modemanager.h"
#include "../outputpane.h"
#include "../outputpanemanager.h"
@@ -51,6 +51,7 @@
#include <utils/mimeutils.h>
#include <utils/overridecursor.h>
#include <utils/qtcassert.h>
+#include <utils/searchresultitem.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
@@ -751,17 +752,13 @@ bool EditorManagerPrivate::skipOpeningBigTextFile(const FilePath &filePath)
.arg(filePath.fileName())
.arg(fileSizeInMB, 0, 'f', 2);
- CheckableMessageBox messageBox(ICore::dialogParent());
- messageBox.setWindowTitle(title);
- messageBox.setText(text);
- messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No);
- messageBox.setDefaultButton(QDialogButtonBox::No);
- messageBox.setIcon(QMessageBox::Question);
- messageBox.setCheckBoxVisible(true);
- messageBox.setCheckBoxText(CheckableMessageBox::msgDoNotAskAgain());
- messageBox.exec();
- setWarnBeforeOpeningBigFilesEnabled(!messageBox.isChecked());
- return messageBox.clickedStandardButton() != QDialogButtonBox::Yes;
+ bool askAgain = true;
+ auto decider = CheckableMessageBox::make_decider(askAgain);
+
+ QMessageBox::StandardButton clickedButton
+ = CheckableMessageBox::question(ICore::dialogParent(), title, text, decider);
+ setWarnBeforeOpeningBigFilesEnabled(askAgain);
+ return clickedButton != QMessageBox::Yes;
}
return false;
@@ -3157,6 +3154,17 @@ IEditor *EditorManager::openEditorAt(const Link &link,
newEditor);
}
+IEditor *EditorManager::openEditor(const LocatorFilterEntry &entry)
+{
+ const OpenEditorFlags defaultFlags = EditorManager::AllowExternalEditor;
+ if (entry.linkForEditor)
+ return EditorManager::openEditorAt(*entry.linkForEditor, {}, defaultFlags);
+ else if (!entry.filePath.isEmpty())
+ return EditorManager::openEditor(entry.filePath, {}, defaultFlags);
+ return nullptr;
+}
+
+
/*!
Opens the document at the position of the search result \a item using the
editor type \a editorId and the specified \a flags.
@@ -3180,7 +3188,7 @@ void EditorManager::openEditorAtSearchResult(const SearchResultItem &item,
openEditor(FilePath::fromUserInput(item.lineText()), editorId, flags, newEditor);
return;
}
- const Search::TextPosition position = item.mainRange().begin;
+ const Text::Position position = item.mainRange().begin;
openEditorAt({FilePath::fromUserInput(path.first()), position.line, position.column},
editorId, flags, newEditor);
}
diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h
index c1c4c4644c..09218f42c3 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.h
+++ b/src/plugins/coreplugin/editormanager/editormanager.h
@@ -9,8 +9,8 @@
#include "documentmodel.h"
#include "ieditor.h"
-#include "utils/link.h"
-#include "utils/textfileformat.h"
+#include <utils/link.h>
+#include <utils/textfileformat.h>
#include <QFileDialog>
#include <QList>
@@ -18,21 +18,18 @@
#include <functional>
-QT_FORWARD_DECLARE_CLASS(QMenu)
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
-namespace Utils {
-class MimeType;
-}
+namespace Utils { class SearchResultItem; }
namespace Core {
class IDocument;
-class SearchResultItem;
+class LocatorFilterEntry;
-namespace Internal {
-class EditorManagerPrivate;
-class MainWindow;
-} // namespace Internal
+namespace Internal { class MainWindow; }
class CORE_EXPORT EditorManagerPlaceHolder final : public QWidget
{
@@ -76,8 +73,9 @@ public:
Utils::Id editorId = {},
OpenEditorFlags flags = NoFlags,
bool *newEditor = nullptr);
+ static IEditor *openEditor(const LocatorFilterEntry &entry);
- static void openEditorAtSearchResult(const SearchResultItem &item,
+ static void openEditorAtSearchResult(const Utils::SearchResultItem &item,
Utils::Id editorId = {},
OpenEditorFlags flags = NoFlags,
bool *newEditor = nullptr);
diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp
index fadfe4cae6..ee4d912d85 100644
--- a/src/plugins/coreplugin/editortoolbar.cpp
+++ b/src/plugins/coreplugin/editortoolbar.cpp
@@ -16,6 +16,7 @@
#include <utils/fsengine/fileiconprovider.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QApplication>
@@ -109,7 +110,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
d->m_lockButton->setEnabled(false);
- d->m_dragHandle->setProperty("noArrow", true);
+ d->m_dragHandle->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
d->m_dragHandle->setToolTip(Tr::tr("Drag to drag documents between splits"));
d->m_dragHandle->installEventFilter(this);
d->m_dragHandleMenu = new QMenu(d->m_dragHandle);
@@ -118,9 +119,9 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
connect(d->m_goBackAction, &QAction::triggered, this, &EditorToolBar::goBackClicked);
connect(d->m_goForwardAction, &QAction::triggered, this, &EditorToolBar::goForwardClicked);
- d->m_editorList->setProperty("hideicon", true);
- d->m_editorList->setProperty("notelideasterisk", true);
- d->m_editorList->setProperty("elidemode", Qt::ElideMiddle);
+ d->m_editorList->setProperty(Utils::StyleHelper::C_HIDE_ICON, true);
+ d->m_editorList->setProperty(Utils::StyleHelper::C_NOT_ELIDE_ASTERISK, true);
+ d->m_editorList->setProperty(Utils::StyleHelper::C_ELIDE_MODE, Qt::ElideMiddle);
d->m_editorList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
d->m_editorList->setMinimumContentsLength(20);
d->m_editorList->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
@@ -130,7 +131,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
d->m_closeEditorButton->setIcon(Utils::Icons::CLOSE_TOOLBAR.icon());
d->m_closeEditorButton->setEnabled(false);
- d->m_closeEditorButton->setProperty("showborder", true);
+ d->m_closeEditorButton->setProperty(Utils::StyleHelper::C_SHOW_BORDER, true);
d->m_toolBarPlaceholder->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
@@ -141,7 +142,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) :
d->m_splitButton->setIcon(Utils::Icons::SPLIT_HORIZONTAL_TOOLBAR.icon());
d->m_splitButton->setToolTip(Tr::tr("Split"));
d->m_splitButton->setPopupMode(QToolButton::InstantPopup);
- d->m_splitButton->setProperty("noArrow", true);
+ d->m_splitButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
auto splitMenu = new QMenu(d->m_splitButton);
splitMenu->addAction(d->m_horizontalSplitAction);
splitMenu->addAction(d->m_verticalSplitAction);
diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp
index 0c0b1d8926..6cafa6ff88 100644
--- a/src/plugins/coreplugin/externaltool.cpp
+++ b/src/plugins/coreplugin/externaltool.cpp
@@ -16,8 +16,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QDateTime>
@@ -623,11 +623,11 @@ void ExternalToolRunner::run()
DocumentManager::expectFileChange(m_expectedFilePath);
}
}
- m_process = new QtcProcess(this);
- connect(m_process, &QtcProcess::done, this, &ExternalToolRunner::done);
- connect(m_process, &QtcProcess::readyReadStandardOutput,
+ m_process = new Process(this);
+ connect(m_process, &Process::done, this, &ExternalToolRunner::done);
+ connect(m_process, &Process::readyReadStandardOutput,
this, &ExternalToolRunner::readStandardOutput);
- connect(m_process, &QtcProcess::readyReadStandardError,
+ connect(m_process, &Process::readyReadStandardError,
this, &ExternalToolRunner::readStandardError);
if (!m_resolvedWorkingDirectory.isEmpty())
m_process->setWorkingDirectory(m_resolvedWorkingDirectory);
diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h
index 8a32d24165..d92d6099bc 100644
--- a/src/plugins/coreplugin/externaltool.h
+++ b/src/plugins/coreplugin/externaltool.h
@@ -14,7 +14,7 @@
#include <QTextCodec>
#include <QMetaType>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Core {
@@ -127,7 +127,7 @@ private:
QString m_resolvedInput;
Utils::FilePath m_resolvedWorkingDirectory;
Utils::Environment m_resolvedEnvironment;
- Utils::QtcProcess *m_process;
+ Utils::Process *m_process;
QTextCodec *m_outputCodec;
QTextCodec::ConverterState m_outputCodecState;
QTextCodec::ConverterState m_errorCodecState;
diff --git a/src/plugins/coreplugin/fancyactionbar.cpp b/src/plugins/coreplugin/fancyactionbar.cpp
index 55904bf149..a07dd71897 100644
--- a/src/plugins/coreplugin/fancyactionbar.cpp
+++ b/src/plugins/coreplugin/fancyactionbar.cpp
@@ -192,10 +192,7 @@ void FancyToolButton::paintEvent(QPaintEvent *event)
const int textFlags = Qt::AlignVCenter | Qt::AlignHCenter;
const QString projectName = defaultAction()->property("heading").toString();
- if (!projectName.isNull())
- centerRect.adjust(0, lineHeight + 4, 0, 0);
-
- centerRect.adjust(0, 0, 0, -lineHeight * 2 - 4);
+ centerRect.adjust(0, lineHeight + 4, 0, -lineHeight * 2 - 4);
iconRect.moveCenter(centerRect.center());
StyleHelper::drawIconWithShadow(icon(), iconRect, &painter, iconMode);
@@ -294,12 +291,12 @@ QSize FancyToolButton::sizeHint() const
boldFont.setBold(true);
const QFontMetrics fm(boldFont);
const qreal lineHeight = fm.height();
- const QString projectName = defaultAction()->property("heading").toString();
- buttonSize += QSizeF(0, 10);
- if (!projectName.isEmpty())
- buttonSize += QSizeF(0, lineHeight + 2);
-
- buttonSize += QSizeF(0, lineHeight * 2 + 2);
+ const int extraHeight = 10 // Spacing between top and projectName
+ + lineHeight // projectName height
+ + 2 // Spacing between projectName and icon
+ + lineHeight * 2 // configurationName height (2 lines)
+ + 2; // Spacing between configurationName and bottom
+ buttonSize.rheight() += extraHeight;
}
return buttonSize.toSize();
}
diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp
index 6c78348897..0fe0ccd887 100644
--- a/src/plugins/coreplugin/fileutils.cpp
+++ b/src/plugins/coreplugin/fileutils.cpp
@@ -17,8 +17,9 @@
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/terminalcommand.h>
+#include <utils/terminalhooks.h>
#include <utils/textfileformat.h>
#include <utils/unixutils.h>
@@ -67,15 +68,16 @@ void FileUtils::showInGraphicalShell(QWidget *parent, const FilePath &pathIn)
if (!pathIn.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
- QtcProcess::startDetached({explorer, param});
+ Process::startDetached({explorer, param});
} else if (HostOsInfo::isMacHost()) {
- QtcProcess::startDetached({"/usr/bin/open", {"-R", fileInfo.canonicalFilePath()}});
+ Process::startDetached({"/usr/bin/open", {"-R", fileInfo.canonicalFilePath()}});
} else {
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
const QString app = UnixUtils::fileBrowser(ICore::settings());
QStringList browserArgs = ProcessArgs::splitArgs(
- UnixUtils::substituteFileBrowserParameters(app, folder));
+ UnixUtils::substituteFileBrowserParameters(app, folder),
+ HostOsInfo::hostOs());
QString error;
if (browserArgs.isEmpty()) {
error = Tr::tr("The command for file browser is not set.");
@@ -104,8 +106,7 @@ void FileUtils::showInFileSystemView(const FilePath &path)
void FileUtils::openTerminal(const FilePath &path, const Environment &env)
{
- QTC_ASSERT(DeviceFileHooks::instance().openTerminal, return);
- DeviceFileHooks::instance().openTerminal(path, env);
+ Terminal::Hooks::instance().openTerminal({std::nullopt, path, env});
}
QString FileUtils::msgFindInDirectory()
@@ -169,8 +170,11 @@ bool FileUtils::renameFile(const FilePath &orgFilePath, const FilePath &newFileP
if (orgFilePath == newFilePath)
return false;
- FilePath dir = orgFilePath.absolutePath();
+ const FilePath dir = orgFilePath.absolutePath();
IVersionControl *vc = VcsManager::findVersionControlForDirectory(dir);
+ const FilePath newDir = newFilePath.absolutePath();
+ if (newDir != dir && !newDir.ensureWritableDir())
+ return false;
bool result = false;
if (vc && vc->supportsOperation(IVersionControl::MoveOperation))
diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp
index 11c51fe26e..96a99f6eba 100644
--- a/src/plugins/coreplugin/find/findtoolbar.cpp
+++ b/src/plugins/coreplugin/find/findtoolbar.cpp
@@ -133,7 +133,6 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
auto verticalLayout_3 = new QVBoxLayout();
verticalLayout_3->setSpacing(0);
verticalLayout_3->addWidget(m_advancedButton);
- verticalLayout_3->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
auto gridLayout = new QGridLayout();
gridLayout->setHorizontalSpacing(3);
@@ -147,8 +146,8 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
m_findEdit,
findButtonsWidget,
br,
- Column { m_replaceLabel, st }.setSpacing(0),
- Column { m_replaceEdit, st }.setSpacing(0),
+ Column { spacing(0), m_replaceLabel, st },
+ Column { spacing(0), m_replaceEdit, st },
gridLayout,
}.attachTo(this);
@@ -159,7 +158,7 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
mainLayout->setColumnStretch(1, 10);
setFocusProxy(m_findEdit);
- setProperty("topBorder", true);
+ setProperty(StyleHelper::C_TOP_BORDER, true);
setSingleRow(false);
QWidget::setTabOrder(m_findEdit, m_replaceEdit);
diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp
index 1ab13cdc7c..0125671d34 100644
--- a/src/plugins/coreplugin/find/findtoolwindow.cpp
+++ b/src/plugins/coreplugin/find/findtoolwindow.cpp
@@ -109,7 +109,8 @@ FindToolWindow::FindToolWindow(QWidget *parent)
m_wholeWords,
m_regExp,
st,
- }.attachTo(m_optionsWidget, WithoutMargins);
+ noMargin
+ }.attachTo(m_optionsWidget);
Grid {
label, m_filterList, br,
diff --git a/src/plugins/coreplugin/find/itemviewfind.cpp b/src/plugins/coreplugin/find/itemviewfind.cpp
index c1c6e84f93..8bba18ae69 100644
--- a/src/plugins/coreplugin/find/itemviewfind.cpp
+++ b/src/plugins/coreplugin/find/itemviewfind.cpp
@@ -203,11 +203,18 @@ IFindSupport::Result ItemViewFind::find(const QString &searchTxt,
index, d->m_role).toString();
if (d->m_view->model()->flags(index) & Qt::ItemIsSelectable
&& (index.row() != currentRow || index.parent() != currentIndex.parent())
- && text.indexOf(searchExpr) != -1)
+ && text.indexOf(searchExpr) != -1) {
resultIndex = index;
+ break;
+ }
}
index = followingIndex(index, backward, &stepWrapped);
- } while (!resultIndex.isValid() && index.isValid() && index != currentIndex);
+ if (index == currentIndex) { // we're back where we started
+ if (d->m_view->model()->data(index, d->m_role).toString().indexOf(searchExpr) != -1)
+ resultIndex = index;
+ break;
+ }
+ } while (index.isValid());
if (resultIndex.isValid()) {
d->m_view->setCurrentIndex(resultIndex);
diff --git a/src/plugins/coreplugin/find/searchresultcolor.h b/src/plugins/coreplugin/find/searchresultcolor.h
deleted file mode 100644
index a6bc46b4e1..0000000000
--- a/src/plugins/coreplugin/find/searchresultcolor.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "../core_global.h"
-
-#include <QColor>
-#include <QHash>
-
-namespace Core {
-
-class CORE_EXPORT SearchResultColor
-{
-public:
- enum class Style { Default, Alt1, Alt2 };
-
- SearchResultColor() = default;
- SearchResultColor(const QColor &textBg, const QColor &textFg,
- const QColor &highlightBg, const QColor &highlightFg,
- const QColor &functionBg, const QColor &functionFg
- )
- : textBackground(textBg), textForeground(textFg),
- highlightBackground(highlightBg), highlightForeground(highlightFg),
- containingFunctionBackground(functionBg),containingFunctionForeground(functionFg)
- {
- if (!highlightBackground.isValid())
- highlightBackground = textBackground;
- if (!highlightForeground.isValid())
- highlightForeground = textForeground;
- if (!containingFunctionBackground.isValid())
- containingFunctionBackground = textBackground;
- if (!containingFunctionForeground.isValid())
- containingFunctionForeground = textForeground;
- }
-
- friend auto qHash(SearchResultColor::Style style)
- {
- return QT_PREPEND_NAMESPACE(qHash(int(style)));
- }
-
- QColor textBackground;
- QColor textForeground;
- QColor highlightBackground;
- QColor highlightForeground;
- QColor containingFunctionBackground;
- QColor containingFunctionForeground;
-};
-
-using SearchResultColors = QHash<SearchResultColor::Style, SearchResultColor>;
-
-} // namespace Core
diff --git a/src/plugins/coreplugin/find/searchresultitem.h b/src/plugins/coreplugin/find/searchresultitem.h
deleted file mode 100644
index d33df65f0a..0000000000
--- a/src/plugins/coreplugin/find/searchresultitem.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "searchresultcolor.h"
-
-#include <utils/filepath.h>
-#include <utils/hostosinfo.h>
-
-#include <QIcon>
-#include <QStringList>
-#include <QVariant>
-
-#include <optional>
-
-namespace Core {
-
-namespace Search {
-
-class TextPosition
-{
-public:
- TextPosition() = default;
- TextPosition(int line, int column) : line(line), column(column) {}
-
- int line = -1; // (0 or -1 for no line number)
- int column = -1; // 0-based starting position for a mark (-1 for no mark)
-
- bool operator<(const TextPosition &other)
- { return line < other.line || (line == other.line && column < other.column); }
-};
-
-class TextRange
-{
-public:
- TextRange() = default;
- TextRange(TextPosition begin, TextPosition end) : begin(begin), end(end) {}
-
- QString mid(const QString &text) const { return text.mid(begin.column, length(text)); }
-
- int length(const QString &text) const
- {
- if (begin.line == end.line)
- return end.column - begin.column;
-
- const int lineCount = end.line - begin.line;
- int index = text.indexOf(QChar::LineFeed);
- int currentLine = 1;
- while (index > 0 && currentLine < lineCount) {
- ++index;
- index = text.indexOf(QChar::LineFeed, index);
- ++currentLine;
- }
-
- if (index < 0)
- return 0;
-
- return index - begin.column + end.column;
- }
-
- TextPosition begin;
- TextPosition end;
-
- bool operator<(const TextRange &other)
- { return begin < other.begin; }
-};
-
-} // namespace Search
-
-class CORE_EXPORT SearchResultItem
-{
-public:
- QStringList path() const { return m_path; }
- void setPath(const QStringList &path) { m_path = path; }
- void setFilePath(const Utils::FilePath &filePath)
- {
- m_path = QStringList{filePath.toUserOutput()};
- }
-
- QString lineText() const { return m_lineText; }
- void setLineText(const QString &text) { m_lineText = text; }
-
- QIcon icon() const { return m_icon; }
- void setIcon(const QIcon &icon) { m_icon = icon; }
-
- QVariant userData() const { return m_userData; }
- void setUserData(const QVariant &userData) { m_userData = userData; }
-
- Search::TextRange mainRange() const { return m_mainRange; }
- void setMainRange(const Search::TextRange &mainRange) { m_mainRange = mainRange; }
- void setMainRange(int line, int column, int length)
- {
- m_mainRange = {};
- m_mainRange.begin.line = line;
- m_mainRange.begin.column = column;
- m_mainRange.end.line = m_mainRange.begin.line;
- m_mainRange.end.column = m_mainRange.begin.column + length;
- }
-
- bool useTextEditorFont() const { return m_useTextEditorFont; }
- void setUseTextEditorFont(bool useTextEditorFont) { m_useTextEditorFont = useTextEditorFont; }
-
- SearchResultColor::Style style() const { return m_style; }
- void setStyle(SearchResultColor::Style style) { m_style = style; }
-
- bool selectForReplacement() const { return m_selectForReplacement; }
- void setSelectForReplacement(bool select) { m_selectForReplacement = select; }
-
- std::optional<QString> containingFunctionName() const { return m_containingFunctionName; }
-
- void setContainingFunctionName(std::optional<QString> containingFunctionName)
- {
- m_containingFunctionName = std::move(containingFunctionName);
- }
-
-private:
- QStringList m_path; // hierarchy to the parent item of this item
- QString m_lineText; // text to show for the item itself
- QIcon m_icon; // icon to show in front of the item (by be null icon to hide)
- QVariant m_userData; // user data for identification of the item
- Search::TextRange m_mainRange;
- bool m_useTextEditorFont = false;
- bool m_selectForReplacement = true;
- SearchResultColor::Style m_style = SearchResultColor::Style::Default;
- std::optional<QString> m_containingFunctionName;
-};
-
-} // namespace Core
-
-Q_DECLARE_METATYPE(Core::SearchResultItem)
-Q_DECLARE_METATYPE(Core::Search::TextPosition)
diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.cpp b/src/plugins/coreplugin/find/searchresulttreeitems.cpp
index 79ccc2429d..9aa8a765db 100644
--- a/src/plugins/coreplugin/find/searchresulttreeitems.cpp
+++ b/src/plugins/coreplugin/find/searchresulttreeitems.cpp
@@ -3,10 +3,12 @@
#include "searchresulttreeitems.h"
+#include <utils/searchresultitem.h>
+
namespace Core {
namespace Internal {
-SearchResultTreeItem::SearchResultTreeItem(const SearchResultItem &item,
+SearchResultTreeItem::SearchResultTreeItem(const Utils::SearchResultItem &item,
SearchResultTreeItem *parent)
: item(item),
m_parent(parent),
@@ -79,7 +81,8 @@ int SearchResultTreeItem::insertionIndex(const QString &text, SearchResultTreeIt
return insertionPosition - m_children.begin();
}
-int SearchResultTreeItem::insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const
+int SearchResultTreeItem::insertionIndex(const Utils::SearchResultItem &item,
+ SearchResultTreeItem **existingItem) const
{
return insertionIndex(item.lineText(), existingItem);
}
@@ -89,13 +92,13 @@ void SearchResultTreeItem::insertChild(int index, SearchResultTreeItem *child)
m_children.insert(index, child);
}
-void SearchResultTreeItem::insertChild(int index, const SearchResultItem &item)
+void SearchResultTreeItem::insertChild(int index, const Utils::SearchResultItem &item)
{
auto child = new SearchResultTreeItem(item, this);
insertChild(index, child);
}
-void SearchResultTreeItem::appendChild(const SearchResultItem &item)
+void SearchResultTreeItem::appendChild(const Utils::SearchResultItem &item)
{
insertChild(m_children.count(), item);
}
diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.h b/src/plugins/coreplugin/find/searchresulttreeitems.h
index 4d4b353039..dbbc27d886 100644
--- a/src/plugins/coreplugin/find/searchresulttreeitems.h
+++ b/src/plugins/coreplugin/find/searchresulttreeitems.h
@@ -5,8 +5,7 @@
#include "searchresultwindow.h"
-#include <QString>
-#include <QList>
+#include <utils/searchresultitem.h>
namespace Core {
namespace Internal {
@@ -14,7 +13,7 @@ namespace Internal {
class SearchResultTreeItem
{
public:
- explicit SearchResultTreeItem(const SearchResultItem &item = SearchResultItem(),
+ explicit SearchResultTreeItem(const Utils::SearchResultItem &item = {},
SearchResultTreeItem *parent = nullptr);
virtual ~SearchResultTreeItem();
@@ -22,10 +21,10 @@ public:
SearchResultTreeItem *parent() const;
SearchResultTreeItem *childAt(int index) const;
int insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const;
- int insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const;
+ int insertionIndex(const Utils::SearchResultItem &item, SearchResultTreeItem **existingItem) const;
void insertChild(int index, SearchResultTreeItem *child);
- void insertChild(int index, const SearchResultItem &item);
- void appendChild(const SearchResultItem &item);
+ void insertChild(int index, const Utils::SearchResultItem &item);
+ void appendChild(const Utils::SearchResultItem &item);
int childrenCount() const;
int rowOfItem() const;
void clearChildren();
@@ -36,7 +35,7 @@ public:
bool isGenerated() const { return m_isGenerated; }
void setGenerated(bool value) { m_isGenerated = value; }
- SearchResultItem item;
+ Utils::SearchResultItem item;
private:
SearchResultTreeItem *m_parent;
diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.cpp b/src/plugins/coreplugin/find/searchresulttreemodel.cpp
index cf00cf755a..b6d620b80b 100644
--- a/src/plugins/coreplugin/find/searchresulttreemodel.cpp
+++ b/src/plugins/coreplugin/find/searchresulttreemodel.cpp
@@ -6,12 +6,15 @@
#include "searchresulttreeitemroles.h"
#include <utils/algorithm.h>
+#include <utils/searchresultitem.h>
#include <QApplication>
#include <QFont>
#include <QFontMetrics>
#include <QDebug>
+using namespace Utils;
+
namespace Core {
namespace Internal {
@@ -38,7 +41,7 @@ public:
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
- QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ QList<QModelIndex> addResults(const SearchResultItems &items, SearchResult::AddMode mode);
static SearchResultTreeItem *treeItemAtIndex(const QModelIndex &idx);
@@ -51,7 +54,7 @@ public slots:
private:
QModelIndex index(SearchResultTreeItem *item) const;
- void addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void addResultsToCurrentParent(const SearchResultItems &items, SearchResult::AddMode mode);
QSet<SearchResultTreeItem *> addPath(const QStringList &path);
QVariant data(const SearchResultTreeItem *row, int role) const;
bool setCheckState(const QModelIndex &idx, Qt::CheckState checkState, bool firstCall = true);
@@ -319,9 +322,13 @@ QVariant SearchResultTreeModel::data(const SearchResultTreeItem *row, int role)
case ItemDataRoles::ResultBeginColumnNumberRole:
result = row->item.mainRange().begin.column;
break;
- case ItemDataRoles::SearchTermLengthRole:
- result = row->item.mainRange().length(row->item.lineText());
+ case ItemDataRoles::SearchTermLengthRole:{
+ Text::Range range = row->item.mainRange();
+ range.end.line -= range.begin.line - 1;
+ range.begin.line = 1;
+ result = range.length(row->item.lineText());
break;
+ }
case ItemDataRoles::ContainingFunctionNameRole:
result = row->item.containingFunctionName().value_or(QString{});
break;
@@ -382,7 +389,8 @@ QSet<SearchResultTreeItem *> SearchResultTreeModel::addPath(const QStringList &p
return pathNodes;
}
-void SearchResultTreeModel::addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+void SearchResultTreeModel::addResultsToCurrentParent(const SearchResultItems &items,
+ SearchResult::AddMode mode)
{
if (!m_currentParent)
return;
@@ -433,12 +441,12 @@ static bool lessThanByPath(const SearchResultItem &a, const SearchResultItem &b)
* Adds the search result to the list of results, creating nodes for the path when
* necessary.
*/
-QList<QModelIndex> SearchResultTreeModel::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+QList<QModelIndex> SearchResultTreeModel::addResults(const SearchResultItems &items, SearchResult::AddMode mode)
{
QSet<SearchResultTreeItem *> pathNodes;
- QList<SearchResultItem> sortedItems = items;
+ SearchResultItems sortedItems = items;
std::stable_sort(sortedItems.begin(), sortedItems.end(), lessThanByPath);
- QList<SearchResultItem> itemSet;
+ SearchResultItems itemSet;
for (const SearchResultItem &item : sortedItems) {
m_editorFontIsUsed |= item.useTextEditorFont();
if (!m_currentParent || (m_currentPath != item.path())) {
@@ -577,7 +585,7 @@ void SearchResultFilterModel::setTextEditorFont(const QFont &font, const SearchR
sourceModel()->setTextEditorFont(font, colors);
}
-QList<QModelIndex> SearchResultFilterModel::addResults(const QList<SearchResultItem> &items,
+QList<QModelIndex> SearchResultFilterModel::addResults(const SearchResultItems &items,
SearchResult::AddMode mode)
{
QList<QModelIndex> sourceIndexes = sourceModel()->addResults(items, mode);
diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.h b/src/plugins/coreplugin/find/searchresulttreemodel.h
index 6727fc56b4..716bcccca3 100644
--- a/src/plugins/coreplugin/find/searchresulttreemodel.h
+++ b/src/plugins/coreplugin/find/searchresulttreemodel.h
@@ -4,7 +4,6 @@
#pragma once
#include "searchresultwindow.h"
-#include "searchresultcolor.h"
#include <QFont>
#include <QSortFilterProxyModel>
@@ -25,8 +24,9 @@ public:
void setFilter(SearchResultFilter *filter);
void setShowReplaceUI(bool show);
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
- QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
+ QList<QModelIndex> addResults(const Utils::SearchResultItems &items,
+ SearchResult::AddMode mode);
void clear();
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false,
bool *wrapped = nullptr) const;
diff --git a/src/plugins/coreplugin/find/searchresulttreeview.cpp b/src/plugins/coreplugin/find/searchresulttreeview.cpp
index e5ac5b4719..d8026ccd84 100644
--- a/src/plugins/coreplugin/find/searchresulttreeview.cpp
+++ b/src/plugins/coreplugin/find/searchresulttreeview.cpp
@@ -7,11 +7,14 @@
#include "searchresulttreeitemdelegate.h"
#include <utils/qtcassert.h>
+#include <utils/searchresultitem.h>
#include <QHeaderView>
#include <QKeyEvent>
#include <QVBoxLayout>
+using namespace Utils;
+
namespace Core {
namespace Internal {
@@ -31,7 +34,7 @@ public:
};
SearchResultTreeView::SearchResultTreeView(QWidget *parent)
- : Utils::TreeView(parent)
+ : TreeView(parent)
, m_model(new SearchResultFilterModel(this))
, m_autoExpandResults(false)
{
@@ -70,7 +73,7 @@ void SearchResultTreeView::clear()
m_model->clear();
}
-void SearchResultTreeView::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+void SearchResultTreeView::addResults(const SearchResultItems &items, SearchResult::AddMode mode)
{
const QList<QModelIndex> addedParents = m_model->addResults(items, mode);
if (m_autoExpandResults && !addedParents.isEmpty()) {
diff --git a/src/plugins/coreplugin/find/searchresulttreeview.h b/src/plugins/coreplugin/find/searchresulttreeview.h
index 9ddbb06cd1..06996fefe0 100644
--- a/src/plugins/coreplugin/find/searchresulttreeview.h
+++ b/src/plugins/coreplugin/find/searchresulttreeview.h
@@ -6,9 +6,9 @@
#include "searchresultwindow.h"
#include <utils/itemviews.h>
+#include <utils/searchresultitem.h>
namespace Core {
-class SearchResultColor;
namespace Internal {
@@ -22,11 +22,11 @@ public:
explicit SearchResultTreeView(QWidget *parent = nullptr);
void setAutoExpandResults(bool expand);
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
void setTabWidth(int tabWidth);
SearchResultFilterModel *model() const;
- void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void addResults(const Utils::SearchResultItems &items, SearchResult::AddMode mode);
void setFilter(SearchResultFilter *filter);
bool hasFilter() const;
void showFilterWidget(QWidget *parent);
@@ -35,7 +35,7 @@ public:
bool event(QEvent *e) override;
signals:
- void jumpToSearchResult(const SearchResultItem &item);
+ void jumpToSearchResult(const Utils::SearchResultItem &item);
void filterInvalidated();
void filterChanged();
diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp
index 36344ed804..ec2ab2c0f6 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.cpp
+++ b/src/plugins/coreplugin/find/searchresultwidget.cpp
@@ -93,7 +93,7 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
topLayout->addWidget(m_topReplaceWidget);
m_messageWidget = new QFrame;
- pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::CanceledSearchTextColor));
+ pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::TextColorError));
m_messageWidget->setPalette(pal);
if (creatorTheme()->flag(Theme::DrawSearchResultWidgetFrame)) {
m_messageWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
@@ -226,7 +226,7 @@ void SearchResultWidget::setAdditionalReplaceWidget(QWidget *widget)
m_additionalReplaceWidget = widget;
}
-void SearchResultWidget::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+void SearchResultWidget::addResults(const SearchResultItems &items, SearchResult::AddMode mode)
{
bool firstItems = (m_count == 0);
m_count += items.size();
@@ -496,9 +496,9 @@ void SearchResultWidget::searchAgain()
emit searchAgainRequested();
}
-QList<SearchResultItem> SearchResultWidget::checkedItems() const
+SearchResultItems SearchResultWidget::checkedItems() const
{
- QList<SearchResultItem> result;
+ SearchResultItems result;
SearchResultFilterModel *model = m_searchResultTreeView->model();
const int fileCount = model->rowCount();
for (int i = 0; i < fileCount; ++i) {
diff --git a/src/plugins/coreplugin/find/searchresultwidget.h b/src/plugins/coreplugin/find/searchresultwidget.h
index f10bd5dd67..6722af579e 100644
--- a/src/plugins/coreplugin/find/searchresultwidget.h
+++ b/src/plugins/coreplugin/find/searchresultwidget.h
@@ -6,6 +6,7 @@
#include "searchresultwindow.h"
#include <utils/infobar.h>
+#include <utils/searchresultitem.h>
#include <QWidget>
@@ -33,9 +34,10 @@ public:
QWidget *additionalReplaceWidget() const;
void setAdditionalReplaceWidget(QWidget *widget);
- void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ void addResults(const Utils::SearchResultItems &items, SearchResult::AddMode mode);
int count() const;
+ bool isSearching() const { return m_searching; }
void setSupportsReplace(bool replaceSupported, const QString &group);
bool supportsReplace() const;
@@ -51,7 +53,7 @@ public:
void notifyVisibilityChanged(bool visible);
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
void setTabWidth(int tabWidth);
void setAutoExpandResults(bool expand);
@@ -75,8 +77,9 @@ public slots:
void sendRequestPopup();
signals:
- void activated(const Core::SearchResultItem &item);
- void replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase);
+ void activated(const Utils::SearchResultItem &item);
+ void replaceButtonClicked(const QString &replaceText,
+ const Utils::SearchResultItems &checkedItems, bool preserveCase);
void replaceTextChanged(const QString &replaceText);
void searchAgainRequested();
void canceled();
@@ -90,7 +93,7 @@ signals:
void navigateStateChanged();
private:
- void handleJumpToSearchResult(const SearchResultItem &item);
+ void handleJumpToSearchResult(const Utils::SearchResultItem &item);
void handleReplaceButton();
void doReplace();
void cancel();
@@ -100,7 +103,7 @@ private:
void continueAfterSizeWarning();
void cancelAfterSizeWarning();
- QList<SearchResultItem> checkedItems() const;
+ Utils::SearchResultItems checkedItems() const;
void updateMatchesFoundLabel();
SearchResultTreeView *m_searchResultTreeView = nullptr;
diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp
index 3b2ea6e8ae..534c9e13bb 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.cpp
+++ b/src/plugins/coreplugin/find/searchresultwindow.cpp
@@ -8,11 +8,11 @@
#include "../actionmanager/actionmanager.h"
#include "../actionmanager/command.h"
#include "../coreplugintr.h"
-#include "../icontext.h"
#include "../icore.h"
#include <utils/qtcassert.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -28,6 +28,9 @@
static const char SETTINGSKEYSECTIONNAME[] = "SearchResults";
static const char SETTINGSKEYEXPANDRESULTS[] = "ExpandResults";
+
+// Note that this is a soft limit: If all searches are still running, none of them will be
+// removed when a new one is started.
static const int MAX_SEARCH_HISTORY = 12;
namespace Core {
@@ -38,24 +41,6 @@ namespace Core {
\internal
*/
-/*!
- \class Core::Search::TextPosition
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::Search::TextRange
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::SearchResultItem
- \inmodule QtCreator
- \internal
-*/
-
namespace Internal {
class InternalScrollArea : public QScrollArea
@@ -90,6 +75,7 @@ namespace Internal {
void popupRequested(SearchResultWidget *widget, bool focus);
void handleExpandCollapseToolButton(bool checked);
void updateFilterButton();
+ int indexOfSearchToEvict() const;
QList<QWidget *> toolBarWidgets();
SearchResultWindow *q;
@@ -107,7 +93,7 @@ namespace Internal {
QList<SearchResult *> m_searchResults;
int m_currentIndex;
QFont m_font;
- SearchResultColors m_colors;
+ Utils::SearchResultColors m_colors;
int m_tabWidth;
};
@@ -257,14 +243,14 @@ using namespace Core::Internal;
*/
/*!
- \fn void Core::SearchResult::activated(const Core::SearchResultItem &item)
+ \fn void Core::SearchResult::activated(const Utils::SearchResultItem &item)
Indicates that the user activated the search result \a item by
double-clicking it, for example.
*/
/*!
\fn void Core::SearchResult::replaceButtonClicked(const QString &replaceText,
- const QList<Core::SearchResultItem> &checkedItems,
+ const Utils::SearchResultItems &checkedItems,
bool preserveCase)
Indicates that the user initiated a text replace by selecting
@@ -473,11 +459,15 @@ SearchResult *SearchResultWindow::startNewSearch(const QString &label,
// temporarily set the index to the last but one existing
d->m_currentIndex = d->m_recentSearchesBox->count() - 2;
}
- d->m_searchResultWidgets.last()->notifyVisibilityChanged(false);
- // widget first, because that might send interesting signals to SearchResult
- delete d->m_searchResultWidgets.takeLast();
- delete d->m_searchResults.takeLast();
- d->m_recentSearchesBox->removeItem(d->m_recentSearchesBox->count() - 1);
+ if (const int toRemoveIndex = d->indexOfSearchToEvict(); toRemoveIndex != -1) {
+ SearchResultWidget * const widgetToRemove
+ = d->m_searchResultWidgets.takeAt(toRemoveIndex);
+ widgetToRemove->notifyVisibilityChanged(false);
+ // widget first, because that might send interesting signals to SearchResult
+ delete widgetToRemove;
+ delete d->m_searchResults.takeAt(toRemoveIndex);
+ d->m_recentSearchesBox->removeItem(toRemoveIndex + 1);
+ }
}
d->m_recentSearchesBox->insertItem(1, Tr::tr("%1 %2").arg(label, searchTerm));
}
@@ -570,7 +560,8 @@ void SearchResultWindow::setFocus()
/*!
\internal
*/
-void SearchResultWindow::setTextEditorFont(const QFont &font, const SearchResultColors &colors)
+void SearchResultWindow::setTextEditorFont(const QFont &font,
+ const Utils::SearchResultColors &colors)
{
d->m_font = font;
d->m_colors = colors;
@@ -616,13 +607,22 @@ void SearchResultWindowPrivate::updateFilterButton()
&& m_searchResultWidgets.at(visibleSearchIndex())->hasFilter());
}
+int SearchResultWindowPrivate::indexOfSearchToEvict() const
+{
+ for (int i = m_searchResultWidgets.size() - 1; i >= 0; --i) {
+ if (!m_searchResultWidgets.at(i)->isSearching())
+ return i;
+ }
+ return -1;
+}
+
QList<QWidget *> SearchResultWindowPrivate::toolBarWidgets()
{
if (!m_historyLabel)
m_historyLabel = new QLabel(Tr::tr("History:"));
if (!m_recentSearchesBox) {
m_recentSearchesBox = new QComboBox;
- m_recentSearchesBox->setProperty("drawleftborder", true);
+ m_recentSearchesBox->setProperty(Utils::StyleHelper::C_DRAW_LEFT_BORDER, true);
m_recentSearchesBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
m_recentSearchesBox->addItem(Tr::tr("New Search"));
connect(m_recentSearchesBox, &QComboBox::activated,
@@ -821,7 +821,7 @@ void SearchResult::setAdditionalReplaceWidget(QWidget *widget)
\sa addResults()
*/
-void SearchResult::addResult(const SearchResultItem &item)
+void SearchResult::addResult(const Utils::SearchResultItem &item)
{
m_widget->addResults({item}, AddOrdered);
}
@@ -832,7 +832,7 @@ void SearchResult::addResult(const SearchResultItem &item)
\sa addResult()
*/
-void SearchResult::addResults(const QList<SearchResultItem> &items, AddMode mode)
+void SearchResult::addResults(const Utils::SearchResultItems &items, AddMode mode)
{
m_widget->addResults(items, mode);
emit countChanged(m_widget->count());
diff --git a/src/plugins/coreplugin/find/searchresultwindow.h b/src/plugins/coreplugin/find/searchresultwindow.h
index da1bcf67e4..ae61192681 100644
--- a/src/plugins/coreplugin/find/searchresultwindow.h
+++ b/src/plugins/coreplugin/find/searchresultwindow.h
@@ -3,11 +3,10 @@
#pragma once
-#include "searchresultcolor.h"
-#include "searchresultitem.h"
-
#include <coreplugin/ioutputpane.h>
+#include <utils/searchresultitem.h>
+
#include <QVariant>
#include <QStringList>
#include <QIcon>
@@ -19,9 +18,10 @@ class QFont;
QT_END_NAMESPACE
namespace Core {
+
namespace Internal {
- class SearchResultWindowPrivate;
- class SearchResultWidget;
+class SearchResultWindowPrivate;
+class SearchResultWidget;
}
class SearchResultWindow;
@@ -31,7 +31,7 @@ class CORE_EXPORT SearchResultFilter : public QObject
public:
virtual QWidget *createWidget() = 0;
- virtual bool matches(const SearchResultItem &item) const = 0;
+ virtual bool matches(const Utils::SearchResultItem &item) const = 0;
signals:
void filterChanged();
@@ -59,8 +59,8 @@ public:
bool isInteractive() const { return !m_finishedHandler; }
public slots:
- void addResult(const SearchResultItem &item);
- void addResults(const QList<SearchResultItem> &items, AddMode mode);
+ void addResult(const Utils::SearchResultItem &item);
+ void addResults(const Utils::SearchResultItems &items, AddMode mode);
void setFilter(SearchResultFilter *filter); // Takes ownership
void finishSearch(bool canceled, const QString &reason = {});
void setTextToReplace(const QString &textToReplace);
@@ -70,8 +70,9 @@ public slots:
void popup();
signals:
- void activated(const Core::SearchResultItem &item);
- void replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase);
+ void activated(const Utils::SearchResultItem &item);
+ void replaceButtonClicked(const QString &replaceText,
+ const Utils::SearchResultItems &checkedItems, bool preserveCase);
void replaceTextChanged(const QString &replaceText);
void canceled();
void paused(bool paused);
@@ -125,7 +126,7 @@ public:
void goToPrev() override;
bool canNavigate() const override;
- void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
+ void setTextEditorFont(const QFont &font, const Utils::SearchResultColors &colors);
void setTabWidth(int width);
void openNewSearchPanel();
diff --git a/src/plugins/coreplugin/foldernavigationwidget.cpp b/src/plugins/coreplugin/foldernavigationwidget.cpp
index fc7b86ab17..7460f427b5 100644
--- a/src/plugins/coreplugin/foldernavigationwidget.cpp
+++ b/src/plugins/coreplugin/foldernavigationwidget.cpp
@@ -32,6 +32,7 @@
#include <utils/removefiledialog.h>
#include <utils/stringutils.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -818,7 +819,7 @@ Core::NavigationView FolderNavigationWidgetFactory::createWidget()
filter->setIcon(Utils::Icons::FILTER.icon());
filter->setToolTip(Tr::tr("Options"));
filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
+ filter->setProperty(StyleHelper::C_NO_ARROW, true);
auto filterMenu = new QMenu(filter);
filterMenu->addAction(fnw->m_filterHiddenFilesAction);
filterMenu->addAction(fnw->m_showBreadCrumbsAction);
diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp
index ae5139b95c..e97797c9fc 100644
--- a/src/plugins/coreplugin/generalsettings.cpp
+++ b/src/plugins/coreplugin/generalsettings.cpp
@@ -9,6 +9,8 @@
#include <coreplugin/dialogs/restartdialog.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/hostosinfo.h>
@@ -17,6 +19,7 @@
#include <utils/qtcolorbutton.h>
#include <utils/stylehelper.h>
+#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
@@ -38,6 +41,7 @@ namespace Internal {
const char settingsKeyDPI[] = "Core/EnableHighDpiScaling";
const char settingsKeyShortcutsInContextMenu[] = "General/ShowShortcutsInContextMenu";
const char settingsKeyCodecForLocale[] = "General/OverrideCodecForLocale";
+const char settingsKeyToolbarStyle[] = "General/ToolbarStyle";
class GeneralSettingsWidget final : public IOptionsPageWidget
{
@@ -57,6 +61,7 @@ public:
void fillCodecBox() const;
static QByteArray codecForLocale();
static void setCodecForLocale(const QByteArray&);
+ void fillToolbarSyleBox() const;
GeneralSettings *q;
QComboBox *m_languageBox;
@@ -65,6 +70,7 @@ public:
QtColorButton *m_colorButton;
ThemeChooser *m_themeChooser;
QPushButton *m_resetWarningsButton;
+ QComboBox *m_toolbarStyleBox;
};
GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
@@ -75,6 +81,7 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
, m_colorButton(new QtColorButton)
, m_themeChooser(new ThemeChooser)
, m_resetWarningsButton(new QPushButton)
+ , m_toolbarStyleBox(new QComboBox)
{
m_languageBox->setObjectName("languageBox");
m_languageBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
@@ -92,6 +99,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
"Show Again\" (for example, missing highlighter).",
nullptr));
+ m_toolbarStyleBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
auto resetColorButton = new QPushButton(Tr::tr("Reset"));
resetColorButton->setToolTip(Tr::tr("Reset to default.", "Color"));
@@ -114,12 +123,14 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q)
}
form.addRow({empty, m_showShortcutsInContextMenus});
- form.addRow(Row{m_resetWarningsButton, st});
+ form.addRow({Row{m_resetWarningsButton, st}});
form.addRow({Tr::tr("Text codec for tools:"), m_codecBox, st});
+ form.addRow({Tr::tr("Toolbar Style:"), m_toolbarStyleBox, st});
Column{Group{title(Tr::tr("User Interface")), form}}.attachTo(this);
fillLanguageBox();
fillCodecBox();
+ fillToolbarSyleBox();
m_colorButton->setColor(StyleHelper::requestedBaseColor());
m_resetWarningsButton->setEnabled(canResetWarnings());
@@ -188,6 +199,15 @@ void GeneralSettingsWidget::apply()
// Apply the new base color if accepted
StyleHelper::setBaseColor(m_colorButton->color());
m_themeChooser->apply();
+ if (const auto newStyle = m_toolbarStyleBox->currentData().value<StyleHelper::ToolbarStyle>();
+ newStyle != StyleHelper::toolbarStyle()) {
+ ICore::settings()->setValueWithDefault(settingsKeyToolbarStyle, int(newStyle),
+ int(StyleHelper::defaultToolbarStyle));
+ StyleHelper::setToolbarStyle(newStyle);
+ QStyle *applicationStyle = QApplication::style();
+ for (QWidget *widget : QApplication::allWidgets())
+ applicationStyle->polish(widget);
+ }
}
bool GeneralSettings::showShortcutsInContextMenu()
@@ -268,6 +288,24 @@ void GeneralSettingsWidget::setCodecForLocale(const QByteArray &codec)
QTextCodec::setCodecForLocale(QTextCodec::codecForName(codec));
}
+StyleHelper::ToolbarStyle toolbarStylefromSettings()
+{
+ if (!ExtensionSystem::PluginManager::instance()) // May happen in tests
+ return StyleHelper::defaultToolbarStyle;
+
+ return StyleHelper::ToolbarStyle(
+ ICore::settings()->value(settingsKeyToolbarStyle,
+ StyleHelper::defaultToolbarStyle).toInt());
+}
+
+void GeneralSettingsWidget::fillToolbarSyleBox() const
+{
+ m_toolbarStyleBox->addItem(Tr::tr("Compact"), StyleHelper::ToolbarStyleCompact);
+ m_toolbarStyleBox->addItem(Tr::tr("Relaxed"), StyleHelper::ToolbarStyleRelaxed);
+ const int curId = m_toolbarStyleBox->findData(toolbarStylefromSettings());
+ m_toolbarStyleBox->setCurrentIndex(curId);
+}
+
void GeneralSettings::setShowShortcutsInContextMenu(bool show)
{
ICore::settings()->setValueWithDefault(settingsKeyShortcutsInContextMenu,
@@ -276,6 +314,11 @@ void GeneralSettings::setShowShortcutsInContextMenu(bool show)
QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus, !show);
}
+void GeneralSettings::applyToolbarStyleFromSettings()
+{
+ StyleHelper::setToolbarStyle(toolbarStylefromSettings());
+}
+
GeneralSettings::GeneralSettings()
{
setId(Constants::SETTINGS_ID_INTERFACE);
diff --git a/src/plugins/coreplugin/generalsettings.h b/src/plugins/coreplugin/generalsettings.h
index 78d1dab270..4587443eac 100644
--- a/src/plugins/coreplugin/generalsettings.h
+++ b/src/plugins/coreplugin/generalsettings.h
@@ -16,6 +16,8 @@ public:
static bool showShortcutsInContextMenu();
void setShowShortcutsInContextMenu(bool show);
+ static void applyToolbarStyleFromSettings();
+
private:
friend class GeneralSettingsWidget;
bool m_defaultShowShortcutsInContextMenu;
diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp
index a7cf3debe3..fc6ec2bf92 100644
--- a/src/plugins/coreplugin/icore.cpp
+++ b/src/plugins/coreplugin/icore.cpp
@@ -513,7 +513,7 @@ FilePath ICore::libexecPath(const QString &rel)
FilePath ICore::crashReportsPath()
{
if (Utils::HostOsInfo::isMacHost())
- return libexecPath("crashpad_reports/completed");
+ return Core::ICore::userResourcePath("crashpad_reports/completed");
else
return libexecPath("crashpad_reports/reports");
}
diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp
deleted file mode 100644
index 9ce27cc052..0000000000
--- a/src/plugins/coreplugin/locator/basefilefilter.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "basefilefilter.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <utils/algorithm.h>
-#include <utils/filepath.h>
-#include <utils/linecolumn.h>
-#include <utils/link.h>
-#include <utils/qtcassert.h>
-
-#include <QDir>
-#include <QRegularExpression>
-
-using namespace Utils;
-
-namespace Core {
-namespace Internal {
-
-class Data
-{
-public:
- void clear()
- {
- iterator.clear();
- previousResultPaths.clear();
- previousEntry.clear();
- }
-
- QSharedPointer<BaseFileFilter::Iterator> iterator;
- FilePaths previousResultPaths;
- bool forceNewSearchList;
- QString previousEntry;
-};
-
-class BaseFileFilterPrivate
-{
-public:
- Data m_data;
- Data m_current;
-};
-
-} // Internal
-
-/*!
- \class Core::BaseFileFilter
- \inheaderfile coreplugin/locator/basefilefilter.h
- \inmodule QtCreator
-
- \brief The BaseFileFilter class is a base class for locator filter classes.
-*/
-
-/*!
- \class Core::BaseFileFilter::Iterator
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::BaseFileFilter::ListIterator
- \inmodule QtCreator
- \internal
-*/
-
-BaseFileFilter::Iterator::~Iterator() = default;
-
-/*!
- \internal
-*/
-BaseFileFilter::BaseFileFilter()
- : d(new Internal::BaseFileFilterPrivate)
-{
- d->m_data.forceNewSearchList = true;
- setFileIterator(new ListIterator({}));
-}
-
-/*!
- \internal
-*/
-BaseFileFilter::~BaseFileFilter()
-{
- delete d;
-}
-
-/*!
- \reimp
-*/
-void BaseFileFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- d->m_current = d->m_data;
- d->m_data.forceNewSearchList = false;
-}
-
-ILocatorFilter::MatchLevel BaseFileFilter::matchLevelFor(const QRegularExpressionMatch &match,
- const QString &matchText)
-{
- const int consecutivePos = match.capturedStart(1);
- if (consecutivePos == 0)
- return MatchLevel::Best;
- if (consecutivePos > 0) {
- const QChar prevChar = matchText.at(consecutivePos - 1);
- if (prevChar == '_' || prevChar == '.')
- return MatchLevel::Better;
- }
- if (match.capturedStart() == 0)
- return MatchLevel::Good;
- return MatchLevel::Normal;
-}
-
-/*!
- \reimp
-*/
-QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &origEntry)
-{
- QList<LocatorFilterEntry> entries[int(MatchLevel::Count)];
- // If search string contains spaces, treat them as wildcard '*' and search in full path
- const QString entry = QDir::fromNativeSeparators(origEntry).replace(' ', '*');
- const Link link = Link::fromString(entry, true);
-
- const QRegularExpression regexp = createRegExp(link.targetFilePath.toString());
- if (!regexp.isValid()) {
- d->m_current.clear(); // free memory
- return {};
- }
- auto containsPathSeparator = [](const QString &candidate) {
- return candidate.contains('/') || candidate.contains('*');
- };
-
- const bool hasPathSeparator = containsPathSeparator(link.targetFilePath.toString());
- const bool containsPreviousEntry = !d->m_current.previousEntry.isEmpty()
- && link.targetFilePath.toString().contains(d->m_current.previousEntry);
- const bool pathSeparatorAdded = !containsPathSeparator(d->m_current.previousEntry)
- && hasPathSeparator;
- const bool searchInPreviousResults = !d->m_current.forceNewSearchList && containsPreviousEntry
- && !pathSeparatorAdded;
- if (searchInPreviousResults)
- d->m_current.iterator.reset(new ListIterator(d->m_current.previousResultPaths));
-
- QTC_ASSERT(d->m_current.iterator.data(), return QList<LocatorFilterEntry>());
- d->m_current.previousResultPaths.clear();
- d->m_current.previousEntry = link.targetFilePath.toString();
- d->m_current.iterator->toFront();
- bool canceled = false;
- while (d->m_current.iterator->hasNext()) {
- if (future.isCanceled()) {
- canceled = true;
- break;
- }
-
- d->m_current.iterator->next();
- FilePath path = d->m_current.iterator->filePath();
- QString matchText = hasPathSeparator ? path.toString() : path.fileName();
- QRegularExpressionMatch match = regexp.match(matchText);
-
- if (match.hasMatch()) {
- LocatorFilterEntry filterEntry(this, path.fileName());
- filterEntry.filePath = path;
- filterEntry.extraInfo = path.shortNativePath();
- filterEntry.linkForEditor = Link(path, link.targetLine, link.targetColumn);
- const MatchLevel matchLevel = matchLevelFor(match, matchText);
- if (hasPathSeparator) {
- match = regexp.match(filterEntry.extraInfo);
- filterEntry.highlightInfo =
- highlightInfo(match, LocatorFilterEntry::HighlightInfo::ExtraInfo);
- } else {
- filterEntry.highlightInfo = highlightInfo(match);
- }
-
- entries[int(matchLevel)].append(filterEntry);
- d->m_current.previousResultPaths.append(path);
- }
- }
-
- if (canceled) {
- // we keep the old list of previous search results if this search was canceled
- // so a later search without forceNewSearchList will use that previous list instead of an
- // incomplete list of a canceled search
- d->m_current.clear(); // free memory
- } else {
- d->m_current.iterator.clear();
- QMetaObject::invokeMethod(this, &BaseFileFilter::updatePreviousResultData,
- Qt::QueuedConnection);
- }
-
- for (auto &entry : entries) {
- if (entry.size() < 1000)
- Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
- }
-
- return std::accumulate(std::begin(entries), std::end(entries), QList<LocatorFilterEntry>());
-}
-
-/*!
- \reimp
-*/
-void BaseFileFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- openEditorAt(selection);
-}
-
-void BaseFileFilter::openEditorAt(const LocatorFilterEntry &entry)
-{
- if (entry.linkForEditor) {
- EditorManager::openEditorAt(*entry.linkForEditor, {}, EditorManager::AllowExternalEditor);
- return;
- }
- EditorManager::openEditor(entry.filePath, {}, EditorManager::AllowExternalEditor);
-}
-
-/*!
- Takes ownership of the \a iterator. The previously set iterator might not be deleted until
- a currently running search is finished.
-*/
-
-void BaseFileFilter::setFileIterator(BaseFileFilter::Iterator *iterator)
-{
- d->m_data.clear();
- d->m_data.forceNewSearchList = true;
- d->m_data.iterator.reset(iterator);
-}
-
-/*!
- Returns the file iterator.
-*/
-QSharedPointer<BaseFileFilter::Iterator> BaseFileFilter::fileIterator()
-{
- return d->m_data.iterator;
-}
-
-void BaseFileFilter::updatePreviousResultData()
-{
- if (d->m_data.forceNewSearchList) // in the meantime the iterator was reset / cache invalidated
- return; // do not update with the new result list etc
- d->m_data.previousEntry = d->m_current.previousEntry;
- d->m_data.previousResultPaths = d->m_current.previousResultPaths;
- // forceNewSearchList was already reset in prepareSearch
-}
-
-BaseFileFilter::ListIterator::ListIterator(const FilePaths &filePaths)
-{
- m_filePaths = filePaths;
- toFront();
-}
-
-void BaseFileFilter::ListIterator::toFront()
-{
- m_pathPosition = m_filePaths.constBegin() - 1;
-}
-
-bool BaseFileFilter::ListIterator::hasNext() const
-{
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return false);
- return m_pathPosition + 1 != m_filePaths.constEnd();
-}
-
-FilePath BaseFileFilter::ListIterator::next()
-{
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return {});
- ++m_pathPosition;
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return {});
- return *m_pathPosition;
-}
-
-FilePath BaseFileFilter::ListIterator::filePath() const
-{
- QTC_ASSERT(m_pathPosition != m_filePaths.constEnd(), return {});
- return *m_pathPosition;
-}
-
-} // Core
diff --git a/src/plugins/coreplugin/locator/basefilefilter.h b/src/plugins/coreplugin/locator/basefilefilter.h
deleted file mode 100644
index 710cd21edf..0000000000
--- a/src/plugins/coreplugin/locator/basefilefilter.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "ilocatorfilter.h"
-
-#include <utils/filepath.h>
-
-#include <QSharedPointer>
-
-namespace Core {
-
-namespace Internal { class BaseFileFilterPrivate; }
-
-class CORE_EXPORT BaseFileFilter : public ILocatorFilter
-{
- Q_OBJECT
-
-public:
- class CORE_EXPORT Iterator {
- public:
- virtual ~Iterator();
- virtual void toFront() = 0;
- virtual bool hasNext() const = 0;
- virtual Utils::FilePath next() = 0;
- virtual Utils::FilePath filePath() const = 0;
- };
-
- class CORE_EXPORT ListIterator final : public Iterator {
- public:
- ListIterator(const Utils::FilePaths &filePaths);
-
- void toFront() override;
- bool hasNext() const override;
- Utils::FilePath next() override;
- Utils::FilePath filePath() const override;
-
- private:
- Utils::FilePaths m_filePaths;
- Utils::FilePaths::const_iterator m_pathPosition;
- };
-
- BaseFileFilter();
- ~BaseFileFilter() override;
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- static void openEditorAt(const LocatorFilterEntry &entry);
-
-protected:
- void setFileIterator(Iterator *iterator);
- QSharedPointer<Iterator> fileIterator();
-
-private:
- static MatchLevel matchLevelFor(const QRegularExpressionMatch &match,
- const QString &matchText);
- void updatePreviousResultData();
-
- Internal::BaseFileFilterPrivate *d = nullptr;
-};
-
-} // namespace Core
diff --git a/src/plugins/coreplugin/locator/commandlocator.cpp b/src/plugins/coreplugin/locator/commandlocator.cpp
index de2989bc17..e960b4df85 100644
--- a/src/plugins/coreplugin/locator/commandlocator.cpp
+++ b/src/plugins/coreplugin/locator/commandlocator.cpp
@@ -5,7 +5,6 @@
#include <coreplugin/actionmanager/command.h>
-#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <QAction>
@@ -14,97 +13,58 @@ using namespace Utils;
namespace Core {
-struct CommandLocatorPrivate
-{
- QList<Command *> commands;
- QList<QPair<int, QString>> commandsData;
-};
-
-/*!
- \class Core::CommandLocator
- \inmodule QtCreator
- \internal
-*/
-
-CommandLocator::CommandLocator(Id id,
- const QString &displayName,
- const QString &shortCutString,
- QObject *parent) :
- ILocatorFilter(parent),
- d(new CommandLocatorPrivate)
+CommandLocator::CommandLocator(Id id, const QString &displayName, const QString &shortCutString,
+ QObject *parent)
+ : ILocatorFilter(parent)
{
setId(id);
setDisplayName(displayName);
setDefaultShortcutString(shortCutString);
}
-CommandLocator::~CommandLocator()
-{
- delete d;
-}
-
-void CommandLocator::appendCommand(Command *cmd)
-{
- d->commands.push_back(cmd);
-}
-
-void CommandLocator::prepareSearch(const QString &entry)
+LocatorMatcherTasks CommandLocator::matchers()
{
- Q_UNUSED(entry)
- d->commandsData = {};
- const int count = d->commands.size();
- // Get active, enabled actions matching text, store in list.
- // Reference via index in extraInfo.
- for (int i = 0; i < count; ++i) {
- Command *command = d->commands.at(i);
- if (!command->isActive())
- continue;
- QAction *action = command->action();
- if (action && action->isEnabled())
- d->commandsData.append({i, action->text()});
- }
-}
-
-QList<LocatorFilterEntry> CommandLocator::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
-{
- QList<LocatorFilterEntry> goodEntries;
- QList<LocatorFilterEntry> betterEntries;
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
- for (const auto &pair : std::as_const(d->commandsData)) {
- if (future.isCanceled())
- break;
-
- const QString text = Utils::stripAccelerator(pair.second);
- const int index = text.indexOf(entry, 0, entryCaseSensitivity);
- if (index >= 0) {
- LocatorFilterEntry filterEntry(this, text, QVariant(pair.first));
- filterEntry.highlightInfo = {index, int(entry.length())};
-
- if (index == 0)
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, commands = m_commands] {
+ const QString input = storage->input();
+ const Qt::CaseSensitivity inputCaseSensitivity = caseSensitivity(input);
+ LocatorFilterEntries goodEntries;
+ LocatorFilterEntries betterEntries;
+ for (Command *command : commands) {
+ if (!command->isActive())
+ continue;
+
+ QAction *action = command->action();
+ if (!action || !action->isEnabled())
+ continue;
+
+ const QString text = Utils::stripAccelerator(action->text());
+ const int index = text.indexOf(input, 0, inputCaseSensitivity);
+ if (index >= 0) {
+ LocatorFilterEntry entry;
+ entry.displayName = text;
+ entry.acceptor = [actionPointer = QPointer(action)] {
+ if (actionPointer) {
+ QMetaObject::invokeMethod(actionPointer, [actionPointer] {
+ if (actionPointer && actionPointer->isEnabled())
+ actionPointer->trigger();
+ }, Qt::QueuedConnection);
+ }
+ return AcceptResult();
+ };
+ entry.highlightInfo = {index, int(input.length())};
+ if (index == 0)
+ betterEntries.append(entry);
+ else
+ goodEntries.append(entry);
+ }
}
- }
- betterEntries.append(goodEntries);
- return betterEntries;
-}
-
-void CommandLocator::accept(const LocatorFilterEntry &entry,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- // Retrieve action via index.
- const int index = entry.internalData.toInt();
- QTC_ASSERT(index >= 0 && index < d->commands.size(), return);
- QAction *action = d->commands.at(index)->action();
- // avoid nested stack trace and blocking locator by delayed triggering
- QMetaObject::invokeMethod(action, [action] {
- if (action->isEnabled())
- action->trigger();
- }, Qt::QueuedConnection);
+ storage->reportOutput(betterEntries + goodEntries);
+ };
+ return {{Sync(onSetup), storage}};
}
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/commandlocator.h b/src/plugins/coreplugin/locator/commandlocator.h
index 8199661bed..8e7d1ead03 100644
--- a/src/plugins/coreplugin/locator/commandlocator.h
+++ b/src/plugins/coreplugin/locator/commandlocator.h
@@ -10,27 +10,18 @@ namespace Core {
/* Command locators: Provides completion for a set of
* Core::Command's by sub-string of their action's text. */
class Command;
-struct CommandLocatorPrivate;
class CORE_EXPORT CommandLocator : public ILocatorFilter
{
- Q_OBJECT
-
public:
- CommandLocator(Utils::Id id, const QString &displayName,
- const QString &shortCutString, QObject *parent = nullptr);
- ~CommandLocator() override;
-
- void appendCommand(Command *cmd);
-
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+ CommandLocator(Utils::Id id, const QString &displayName, const QString &shortCutString,
+ QObject *parent = nullptr);
+ void appendCommand(Command *cmd) { m_commands.push_back(cmd); }
private:
- CommandLocatorPrivate *d = nullptr;
+ LocatorMatcherTasks matchers() final;
+
+ QList<Command *> m_commands;
};
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp
index 19a9f098e6..6100181ff4 100644
--- a/src/plugins/coreplugin/locator/directoryfilter.cpp
+++ b/src/plugins/coreplugin/locator/directoryfilter.cpp
@@ -7,12 +7,12 @@
#include "../coreplugintr.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/filesearch.h>
#include <utils/layoutbuilder.h>
#include <QCheckBox>
-#include <QCoreApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QJsonArray>
@@ -46,6 +46,28 @@ static QString defaultDisplayName()
return Tr::tr("Generic Directory Filter");
}
+static void refresh(QPromise<FilePaths> &promise, const FilePaths &directories,
+ const QStringList &filters, const QStringList &exclusionFilters,
+ const QString &displayName)
+{
+ SubDirFileIterator subDirIterator(directories, filters, exclusionFilters);
+ promise.setProgressRange(0, subDirIterator.maxProgress());
+ FilePaths files;
+ const auto end = subDirIterator.end();
+ for (auto it = subDirIterator.begin(); it != end; ++it) {
+ if (promise.isCanceled()) {
+ promise.setProgressValueAndText(subDirIterator.currentProgress(),
+ Tr::tr("%1 filter update: canceled").arg(displayName));
+ return;
+ }
+ files << (*it).filePath;
+ promise.setProgressValueAndText(subDirIterator.currentProgress(),
+ Tr::tr("%1 filter update: %n files", nullptr, files.size()).arg(displayName));
+ }
+ promise.setProgressValue(subDirIterator.maxProgress());
+ promise.addResult(files);
+}
+
DirectoryFilter::DirectoryFilter(Id id)
: m_filters(kFiltersDefault)
, m_exclusionFilters(kExclusionFiltersDefault)
@@ -53,15 +75,34 @@ DirectoryFilter::DirectoryFilter(Id id)
setId(id);
setDefaultIncludedByDefault(true);
setDisplayName(defaultDisplayName());
- setDescription(Tr::tr("Matches all files from a custom set of directories. Append \"+<number>\" "
+ setDescription(Tr::tr("Locates files from a custom set of directories. Append \"+<number>\" "
"or \":<number>\" to jump to the given line number. Append another "
"\"+<number>\" or \":<number>\" to jump to the column number as well."));
+
+ using namespace Tasking;
+ const auto groupSetup = [this] {
+ if (!m_directories.isEmpty())
+ return TaskAction::Continue; // Async task will run
+ m_cache.setFilePaths({});
+ return TaskAction::StopWithDone; // Group stops, skips async task
+ };
+ const auto asyncSetup = [this](Async<FilePaths> &async) {
+ async.setConcurrentCallData(&refresh, m_directories, m_filters, m_exclusionFilters,
+ displayName());
+ };
+ const auto asyncDone = [this](const Async<FilePaths> &async) {
+ if (async.isResultAvailable())
+ m_cache.setFilePaths(async.result());
+ };
+ const Group root {
+ OnGroupSetup(groupSetup),
+ AsyncTask<FilePaths>(asyncSetup, asyncDone)
+ };
+ setRefreshRecipe(root);
}
void DirectoryFilter::saveState(QJsonObject &object) const
{
- QMutexLocker locker(&m_lock); // m_files is modified in other thread
-
if (displayName() != defaultDisplayName())
object.insert(kDisplayNameKey, displayName());
if (!m_directories.isEmpty()) {
@@ -71,10 +112,11 @@ void DirectoryFilter::saveState(QJsonObject &object) const
}
if (m_filters != kFiltersDefault)
object.insert(kFiltersKey, QJsonArray::fromStringList(m_filters));
- if (!m_files.isEmpty())
- object.insert(kFilesKey,
- QJsonArray::fromStringList(
- Utils::transform(m_files, &Utils::FilePath::toString)));
+ const std::optional<FilePaths> files = m_cache.filePaths();
+ if (files) {
+ object.insert(kFilesKey, QJsonArray::fromStringList(
+ Utils::transform(*files, &FilePath::toString)));
+ }
if (m_exclusionFilters != kExclusionFiltersDefault)
object.insert(kExclusionFiltersKey, QJsonArray::fromStringList(m_exclusionFilters));
}
@@ -92,12 +134,14 @@ static FilePaths toFilePaths(const QJsonArray &array)
void DirectoryFilter::restoreState(const QJsonObject &object)
{
- QMutexLocker locker(&m_lock);
setDisplayName(object.value(kDisplayNameKey).toString(defaultDisplayName()));
m_directories = toFilePaths(object.value(kDirectoriesKey).toArray());
m_filters = toStringList(
object.value(kFiltersKey).toArray(QJsonArray::fromStringList(kFiltersDefault)));
- m_files = FileUtils::toFilePathList(toStringList(object.value(kFilesKey).toArray()));
+ if (object.contains(kFilesKey)) {
+ m_cache.setFilePaths(FileUtils::toFilePathList(
+ toStringList(object.value(kFilesKey).toArray())));
+ }
m_exclusionFilters = toStringList(
object.value(kExclusionFiltersKey)
.toArray(QJsonArray::fromStringList(kExclusionFiltersDefault)));
@@ -107,8 +151,6 @@ void DirectoryFilter::restoreState(const QByteArray &state)
{
if (isOldSetting(state)) {
// TODO read old settings, remove some time after Qt Creator 4.15
- QMutexLocker locker(&m_lock);
-
QString name;
QStringList directories;
QString shortcut;
@@ -122,7 +164,7 @@ void DirectoryFilter::restoreState(const QByteArray &state)
in >> shortcut;
in >> defaultFilter;
in >> files;
- m_files = FileUtils::toFilePathList(files);
+ m_cache.setFilePaths(FileUtils::toFilePathList(files));
if (!in.atEnd()) // Qt Creator 4.3 and later
in >> m_exclusionFilters;
else
@@ -136,12 +178,9 @@ void DirectoryFilter::restoreState(const QByteArray &state)
setDisplayName(name);
setShortcutString(shortcut);
setIncludedByDefault(defaultFilter);
-
- locker.unlock();
} else {
ILocatorFilter::restoreState(state);
}
- updateFileIterator();
}
class DirectoryFilterOptions : public QDialog
@@ -263,8 +302,6 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
&DirectoryFilter::updateOptionButtons,
Qt::DirectConnection);
m_dialog->directoryList->clear();
- // Note: assuming we only change m_directories in the Gui thread,
- // we don't need to protect it here with mutex
m_dialog->directoryList->addItems(Utils::transform(m_directories, &FilePath::toString));
m_dialog->nameLabel->setVisible(m_isCustomFilter);
m_dialog->nameEdit->setVisible(m_isCustomFilter);
@@ -276,14 +313,10 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
m_dialog->filePatternLabel->setText(Utils::msgFilePatternLabel());
m_dialog->filePatternLabel->setBuddy(m_dialog->filePattern);
m_dialog->filePattern->setToolTip(Utils::msgFilePatternToolTip());
- // Note: assuming we only change m_filters in the Gui thread,
- // we don't need to protect it here with mutex
m_dialog->filePattern->setText(Utils::transform(m_filters, &QDir::toNativeSeparators).join(','));
m_dialog->exclusionPatternLabel->setText(Utils::msgExclusionPatternLabel());
m_dialog->exclusionPatternLabel->setBuddy(m_dialog->exclusionPattern);
m_dialog->exclusionPattern->setToolTip(Utils::msgFilePatternToolTip());
- // Note: assuming we only change m_exclusionFilters in the Gui thread,
- // we don't need to protect it here with mutex
m_dialog->exclusionPattern->setText(
Utils::transform(m_exclusionFilters, &QDir::toNativeSeparators).join(','));
m_dialog->shortcutEdit->setText(shortcutString());
@@ -291,7 +324,6 @@ bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
updateOptionButtons();
dialog.adjustSize();
if (dialog.exec() == QDialog::Accepted) {
- QMutexLocker locker(&m_lock);
bool directoriesChanged = false;
const FilePaths oldDirectories = m_directories;
const QStringList oldFilters = m_filters;
@@ -351,55 +383,6 @@ void DirectoryFilter::updateOptionButtons()
m_dialog->removeButton->setEnabled(haveSelectedItem);
}
-void DirectoryFilter::updateFileIterator()
-{
- QMutexLocker locker(&m_lock);
- setFileIterator(new BaseFileFilter::ListIterator(m_files));
-}
-
-void DirectoryFilter::refresh(QFutureInterface<void> &future)
-{
- FilePaths directories;
- QStringList filters, exclusionFilters;
- {
- QMutexLocker locker(&m_lock);
- if (m_directories.isEmpty()) {
- m_files.clear();
- QMetaObject::invokeMethod(this, &DirectoryFilter::updateFileIterator,
- Qt::QueuedConnection);
- future.setProgressRange(0, 1);
- future.setProgressValueAndText(1, Tr::tr("%1 filter update: 0 files").arg(displayName()));
- return;
- }
- directories = m_directories;
- filters = m_filters;
- exclusionFilters = m_exclusionFilters;
- }
- Utils::SubDirFileIterator subDirIterator(directories, filters, exclusionFilters);
- future.setProgressRange(0, subDirIterator.maxProgress());
- Utils::FilePaths filesFound;
- auto end = subDirIterator.end();
- for (auto it = subDirIterator.begin(); it != end; ++it) {
- if (future.isCanceled())
- break;
- filesFound << (*it).filePath;
- if (future.isProgressUpdateNeeded()
- || future.progressValue() == 0 /*workaround for regression in Qt*/) {
- future.setProgressValueAndText(subDirIterator.currentProgress(),
- Tr::tr("%1 filter update: %n files", nullptr, filesFound.size()).arg(displayName()));
- }
- }
-
- if (!future.isCanceled()) {
- QMutexLocker locker(&m_lock);
- m_files = filesFound;
- QMetaObject::invokeMethod(this, &DirectoryFilter::updateFileIterator, Qt::QueuedConnection);
- future.setProgressValue(subDirIterator.maxProgress());
- } else {
- future.setProgressValueAndText(subDirIterator.currentProgress(), Tr::tr("%1 filter update: canceled").arg(displayName()));
- }
-}
-
void DirectoryFilter::setIsCustomFilter(bool value)
{
m_isCustomFilter = value;
@@ -409,10 +392,7 @@ void DirectoryFilter::setDirectories(const FilePaths &directories)
{
if (directories == m_directories)
return;
- {
- QMutexLocker locker(&m_lock);
- m_directories = directories;
- }
+ m_directories = directories;
Internal::Locator::instance()->refresh({this});
}
@@ -428,20 +408,13 @@ void DirectoryFilter::removeDirectory(const FilePath &directory)
setDirectories(directories);
}
-FilePaths DirectoryFilter::directories() const
-{
- return m_directories;
-}
-
void DirectoryFilter::setFilters(const QStringList &filters)
{
- QMutexLocker locker(&m_lock);
m_filters = filters;
}
void DirectoryFilter::setExclusionFilters(const QStringList &exclusionFilters)
{
- QMutexLocker locker(&m_lock);
m_exclusionFilters = exclusionFilters;
}
diff --git a/src/plugins/coreplugin/locator/directoryfilter.h b/src/plugins/coreplugin/locator/directoryfilter.h
index 7fb88ce112..485377d2a1 100644
--- a/src/plugins/coreplugin/locator/directoryfilter.h
+++ b/src/plugins/coreplugin/locator/directoryfilter.h
@@ -3,45 +3,36 @@
#pragma once
-#include "basefilefilter.h"
+#include "ilocatorfilter.h"
#include <coreplugin/core_global.h>
-#include <QString>
-#include <QByteArray>
-#include <QFutureInterface>
-#include <QMutex>
-
namespace Core {
-class CORE_EXPORT DirectoryFilter : public BaseFileFilter
+class CORE_EXPORT DirectoryFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
DirectoryFilter(Utils::Id id);
void restoreState(const QByteArray &state) override;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
- void refresh(QFutureInterface<void> &future) override;
+protected:
void setIsCustomFilter(bool value);
- void setDirectories(const Utils::FilePaths &directories);
void addDirectory(const Utils::FilePath &directory);
void removeDirectory(const Utils::FilePath &directory);
- Utils::FilePaths directories() const;
void setFilters(const QStringList &filters);
void setExclusionFilters(const QStringList &exclusionFilters);
-protected:
void saveState(QJsonObject &object) const override;
void restoreState(const QJsonObject &object) override;
private:
+ LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
+ void setDirectories(const Utils::FilePaths &directories);
void handleAddDirectory();
void handleEditDirectory();
void handleRemoveDirectory();
void updateOptionButtons();
- void updateFileIterator();
Utils::FilePaths m_directories;
QStringList m_filters;
@@ -49,9 +40,8 @@ private:
// Our config dialog, uses in addDirectory and editDirectory
// to give their dialogs the right parent
class DirectoryFilterOptions *m_dialog = nullptr;
- mutable QMutex m_lock;
- Utils::FilePaths m_files;
bool m_isCustomFilter = true;
+ LocatorFileCache m_cache;
};
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp
index 6be23fff3f..2a405c369e 100644
--- a/src/plugins/coreplugin/locator/executefilter.cpp
+++ b/src/plugins/coreplugin/locator/executefilter.cpp
@@ -11,8 +11,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QJsonArray>
#include <QJsonObject>
@@ -31,7 +31,6 @@ ExecuteFilter::ExecuteFilter()
"environment variable if needed. Note that the command is run directly, not in a shell."));
setDefaultShortcutString("!");
setPriority(High);
- setDefaultIncludedByDefault(false);
}
ExecuteFilter::~ExecuteFilter()
@@ -39,77 +38,78 @@ ExecuteFilter::~ExecuteFilter()
removeProcess();
}
-QList<LocatorFilterEntry> ExecuteFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
+LocatorMatcherTasks ExecuteFilter::matchers()
{
- QList<LocatorFilterEntry> value;
- if (!entry.isEmpty()) // avoid empty entry
- value.append(LocatorFilterEntry(this, entry));
- QList<LocatorFilterEntry> others;
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
- for (const QString &cmd : std::as_const(m_commandHistory)) {
- if (future.isCanceled())
- break;
- if (cmd == entry) // avoid repeated entry
- continue;
- LocatorFilterEntry filterEntry(this, cmd);
- const int index = cmd.indexOf(entry, 0, entryCaseSensitivity);
- if (index >= 0) {
- filterEntry.highlightInfo = {index, int(entry.length())};
- value.append(filterEntry);
- } else {
- others.append(filterEntry);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=] {
+ const QString input = storage->input();
+ LocatorFilterEntries entries;
+ if (!input.isEmpty()) { // avoid empty entry
+ LocatorFilterEntry entry;
+ entry.displayName = input;
+ entry.acceptor = [this, input] { acceptCommand(input); return AcceptResult(); };
+ entries.append(entry);
}
- }
- value.append(others);
- return value;
+ LocatorFilterEntries others;
+ const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(input);
+ for (const QString &cmd : std::as_const(m_commandHistory)) {
+ if (cmd == input) // avoid repeated entry
+ continue;
+ LocatorFilterEntry entry;
+ entry.displayName = cmd;
+ entry.acceptor = [this, cmd] { acceptCommand(cmd); return AcceptResult(); };
+ const int index = cmd.indexOf(input, 0, entryCaseSensitivity);
+ if (index >= 0) {
+ entry.highlightInfo = {index, int(input.length())};
+ entries.append(entry);
+ } else {
+ others.append(entry);
+ }
+ }
+ storage->reportOutput(entries + others);
+ };
+ return {{Sync(onSetup), storage}};
}
-void ExecuteFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
+void ExecuteFilter::acceptCommand(const QString &cmd)
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- auto p = const_cast<ExecuteFilter *>(this);
-
- const QString value = selection.displayName.trimmed();
-
- const int index = m_commandHistory.indexOf(value);
+ const QString displayName = cmd.trimmed();
+ const int index = m_commandHistory.indexOf(displayName);
if (index != -1 && index != 0)
- p->m_commandHistory.removeAt(index);
+ m_commandHistory.removeAt(index);
if (index != 0)
- p->m_commandHistory.prepend(value);
+ m_commandHistory.prepend(displayName);
static const int maxHistory = 100;
- while (p->m_commandHistory.size() > maxHistory)
- p->m_commandHistory.removeLast();
+ while (m_commandHistory.size() > maxHistory)
+ m_commandHistory.removeLast();
bool found;
- QString workingDirectory = Utils::globalMacroExpander()->value("CurrentDocument:Path", &found);
+ QString workingDirectory = globalMacroExpander()->value("CurrentDocument:Path", &found);
if (!found || workingDirectory.isEmpty())
- workingDirectory = Utils::globalMacroExpander()->value("CurrentDocument:Project:Path", &found);
-
- ExecuteData d;
- d.command = CommandLine::fromUserInput(value, Utils::globalMacroExpander());
- d.workingDirectory = FilePath::fromString(workingDirectory);
+ workingDirectory = globalMacroExpander()->value("CurrentDocument:Project:Path", &found);
+ const ExecuteData data{CommandLine::fromUserInput(displayName, globalMacroExpander()),
+ FilePath::fromString(workingDirectory)};
if (m_process) {
- const QString info(Tr::tr("Previous command is still running (\"%1\").\nDo you want to kill it?")
- .arg(p->headCommand()));
- int r = QMessageBox::question(ICore::dialogParent(), Tr::tr("Kill Previous Process?"), info,
- QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
- QMessageBox::Yes);
- if (r == QMessageBox::Cancel)
+ const QString info(Tr::tr("Previous command is still running (\"%1\").\n"
+ "Do you want to kill it?").arg(headCommand()));
+ const auto result = QMessageBox::question(ICore::dialogParent(),
+ Tr::tr("Kill Previous Process?"), info,
+ QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+ QMessageBox::Yes);
+ if (result == QMessageBox::Cancel)
return;
- if (r == QMessageBox::No) {
- p->m_taskQueue.enqueue(d);
+ if (result == QMessageBox::No) {
+ m_taskQueue.append(data);
return;
}
- p->removeProcess();
+ removeProcess();
}
-
- p->m_taskQueue.enqueue(d);
- p->runHeadCommand();
+ m_taskQueue.append(data);
+ runHeadCommand();
}
void ExecuteFilter::done()
@@ -122,7 +122,7 @@ void ExecuteFilter::done()
runHeadCommand();
}
-void ExecuteFilter::readStandardOutput()
+void ExecuteFilter::readStdOutput()
{
QTC_ASSERT(m_process, return);
const QByteArray data = m_process->readAllRawStandardOutput();
@@ -130,7 +130,7 @@ void ExecuteFilter::readStandardOutput()
QTextCodec::codecForLocale()->toUnicode(data.constData(), data.size(), &m_stdoutState));
}
-void ExecuteFilter::readStandardError()
+void ExecuteFilter::readStdError()
{
QTC_ASSERT(m_process, return);
const QByteArray data = m_process->readAllRawStandardError();
@@ -141,11 +141,11 @@ void ExecuteFilter::readStandardError()
void ExecuteFilter::runHeadCommand()
{
if (!m_taskQueue.isEmpty()) {
- const ExecuteData &d = m_taskQueue.head();
+ const ExecuteData &d = m_taskQueue.first();
if (d.command.executable().isEmpty()) {
- MessageManager::writeDisrupting(
- Tr::tr("Could not find executable for \"%1\".").arg(d.command.executable().toUserOutput()));
- m_taskQueue.dequeue();
+ MessageManager::writeDisrupting(Tr::tr("Could not find executable for \"%1\".")
+ .arg(d.command.executable().toUserOutput()));
+ m_taskQueue.removeFirst();
runHeadCommand();
return;
}
@@ -163,11 +163,11 @@ void ExecuteFilter::createProcess()
if (m_process)
return;
- m_process = new Utils::QtcProcess;
- m_process->setEnvironment(Utils::Environment::systemEnvironment());
- connect(m_process, &QtcProcess::done, this, &ExecuteFilter::done);
- connect(m_process, &QtcProcess::readyReadStandardOutput, this, &ExecuteFilter::readStandardOutput);
- connect(m_process, &QtcProcess::readyReadStandardError, this, &ExecuteFilter::readStandardError);
+ m_process = new Process;
+ m_process->setEnvironment(Environment::systemEnvironment());
+ connect(m_process, &Process::done, this, &ExecuteFilter::done);
+ connect(m_process, &Process::readyReadStandardOutput, this, &ExecuteFilter::readStdOutput);
+ connect(m_process, &Process::readyReadStandardError, this, &ExecuteFilter::readStdError);
}
void ExecuteFilter::removeProcess()
@@ -175,7 +175,7 @@ void ExecuteFilter::removeProcess()
if (!m_process)
return;
- m_taskQueue.dequeue();
+ m_taskQueue.removeFirst();
m_process->deleteLater();
m_process = nullptr;
}
@@ -198,8 +198,8 @@ QString ExecuteFilter::headCommand() const
{
if (m_taskQueue.isEmpty())
return QString();
- const ExecuteData &data = m_taskQueue.head();
+ const ExecuteData &data = m_taskQueue.first();
return data.command.toUserOutput();
}
-} // Core::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/executefilter.h b/src/plugins/coreplugin/locator/executefilter.h
index 0d499e8860..2e51d320d3 100644
--- a/src/plugins/coreplugin/locator/executefilter.h
+++ b/src/plugins/coreplugin/locator/executefilter.h
@@ -7,19 +7,15 @@
#include <utils/commandline.h>
-#include <QQueue>
#include <QStringList>
#include <QTextCodec>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class ExecuteFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
struct ExecuteData
{
Utils::CommandLine command;
@@ -29,15 +25,13 @@ class ExecuteFilter : public Core::ILocatorFilter
public:
ExecuteFilter();
~ExecuteFilter() override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
private:
+ LocatorMatcherTasks matchers() final;
+ void acceptCommand(const QString &cmd);
void done();
- void readStandardOutput();
- void readStandardError();
+ void readStdOutput();
+ void readStdError();
void runHeadCommand();
void createProcess();
@@ -48,12 +42,11 @@ private:
QString headCommand() const;
- QQueue<ExecuteData> m_taskQueue;
+ QList<ExecuteData> m_taskQueue;
QStringList m_commandHistory;
- Utils::QtcProcess *m_process = nullptr;
+ Utils::Process *m_process = nullptr;
QTextCodec::ConverterState m_stdoutState;
QTextCodec::ConverterState m_stderrState;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/externaltoolsfilter.cpp b/src/plugins/coreplugin/locator/externaltoolsfilter.cpp
index 96ce58e20b..36fc78cba9 100644
--- a/src/plugins/coreplugin/locator/externaltoolsfilter.cpp
+++ b/src/plugins/coreplugin/locator/externaltoolsfilter.cpp
@@ -4,6 +4,7 @@
#include "externaltoolsfilter.h"
#include "../coreconstants.h"
+#include "../coreplugin.h"
#include "../coreplugintr.h"
#include "../externaltool.h"
#include "../externaltoolmanager.h"
@@ -24,64 +25,62 @@ ExternalToolsFilter::ExternalToolsFilter()
setPriority(Medium);
}
-QList<LocatorFilterEntry> ExternalToolsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &,
- const QString &)
+LocatorMatcherTasks ExternalToolsFilter::matchers()
{
- return m_results;
-}
+ using namespace Tasking;
-void ExternalToolsFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
+ TreeStorage<LocatorStorage> storage;
- if (!selection.internalData.isValid()) {
- ICore::showOptionsDialog(Constants::SETTINGS_ID_TOOLS);
- return;
- }
+ const auto onSetup = [storage] {
+ const QString input = storage->input();
- auto tool = selection.internalData.value<ExternalTool *>();
- QTC_ASSERT(tool, return);
+ LocatorFilterEntries bestEntries;
+ LocatorFilterEntries betterEntries;
+ LocatorFilterEntries goodEntries;
+ const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(input);
+ const QMap<QString, ExternalTool *> externalToolsById = ExternalToolManager::toolsById();
+ for (ExternalTool *tool : externalToolsById) {
+ int index = tool->displayName().indexOf(input, 0, entryCaseSensitivity);
+ LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
+ if (index < 0) {
+ index = tool->description().indexOf(input, 0, entryCaseSensitivity);
+ hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
+ }
- auto runner = new ExternalToolRunner(tool);
- if (runner->hasError())
- MessageManager::writeFlashing(runner->errorString());
-}
+ if (index >= 0) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = tool->displayName();
+ filterEntry.acceptor = [tool] {
+ auto runner = new ExternalToolRunner(tool);
+ if (runner->hasError())
+ MessageManager::writeFlashing(runner->errorString());
+ return AcceptResult();
+ };
+ filterEntry.extraInfo = tool->description();
+ filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, input.length(), hDataType);
-void ExternalToolsFilter::prepareSearch(const QString &entry)
-{
- QList<LocatorFilterEntry> bestEntries;
- QList<LocatorFilterEntry> betterEntries;
- QList<LocatorFilterEntry> goodEntries;
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
- const QMap<QString, ExternalTool *> externalToolsById = ExternalToolManager::toolsById();
- for (ExternalTool *tool : externalToolsById) {
- int index = tool->displayName().indexOf(entry, 0, entryCaseSensitivity);
- LocatorFilterEntry::HighlightInfo::DataType hDataType = LocatorFilterEntry::HighlightInfo::DisplayName;
- if (index < 0) {
- index = tool->description().indexOf(entry, 0, entryCaseSensitivity);
- hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
+ if (filterEntry.displayName.startsWith(input, entryCaseSensitivity))
+ bestEntries.append(filterEntry);
+ else if (filterEntry.displayName.contains(input, entryCaseSensitivity))
+ betterEntries.append(filterEntry);
+ else
+ goodEntries.append(filterEntry);
+ }
}
+ LocatorFilterEntry configEntry;
+ configEntry.displayName = "Configure External Tool...";
+ configEntry.extraInfo = "Opens External Tool settings";
+ configEntry.acceptor = [] {
+ QMetaObject::invokeMethod(CorePlugin::instance(), [] {
+ ICore::showOptionsDialog(Constants::SETTINGS_ID_TOOLS);
+ }, Qt::QueuedConnection);
+ return AcceptResult();
+ };
- if (index >= 0) {
- LocatorFilterEntry filterEntry(this, tool->displayName(), QVariant::fromValue(tool));
- filterEntry.extraInfo = tool->description();
- filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, entry.length(), hDataType);
-
- if (filterEntry.displayName.startsWith(entry, entryCaseSensitivity))
- bestEntries.append(filterEntry);
- else if (filterEntry.displayName.contains(entry, entryCaseSensitivity))
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
- }
- }
- LocatorFilterEntry configEntry(this, "Configure External Tool...", {});
- configEntry.extraInfo = "Opens External Tool settings";
- m_results = {};
- m_results << bestEntries << betterEntries << goodEntries << configEntry;
+ storage->reportOutput(bestEntries + betterEntries + goodEntries
+ + LocatorFilterEntries{configEntry});
+ };
+ return {{Sync(onSetup), storage}};
}
} // Core::Internal
diff --git a/src/plugins/coreplugin/locator/externaltoolsfilter.h b/src/plugins/coreplugin/locator/externaltoolsfilter.h
index 74d5214aa3..bc8fca6e70 100644
--- a/src/plugins/coreplugin/locator/externaltoolsfilter.h
+++ b/src/plugins/coreplugin/locator/externaltoolsfilter.h
@@ -5,24 +5,15 @@
#include "ilocatorfilter.h"
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class ExternalToolsFilter : public ILocatorFilter
{
- Q_OBJECT
public:
ExternalToolsFilter();
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void prepareSearch(const QString &entry) override;
-
private:
- QList<LocatorFilterEntry> m_results;
+ LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp
index 17c54c748d..32ad0a2a2b 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.cpp
+++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp
@@ -3,19 +3,25 @@
#include "filesystemfilter.h"
-#include "basefilefilter.h"
#include "../coreplugintr.h"
#include "../documentmanager.h"
#include "../editormanager/editormanager.h"
#include "../icore.h"
#include "../vcsmanager.h"
+#include "locatormanager.h"
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/checkablemessagebox.h>
#include <utils/environment.h>
#include <utils/filepath.h>
+#include <utils/fsengine/fileiconprovider.h>
#include <utils/layoutbuilder.h>
#include <utils/link.h>
+#include <QApplication>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
@@ -25,26 +31,85 @@
#include <QLineEdit>
#include <QPushButton>
#include <QRegularExpression>
+#include <QStyle>
using namespace Utils;
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
+
+Q_GLOBAL_STATIC(QIcon, sDeviceRootIcon);
+
+static const char kAlwaysCreate[] = "Locator/FileSystemFilter/AlwaysCreate";
-ILocatorFilter::MatchLevel FileSystemFilter::matchLevelFor(const QRegularExpressionMatch &match,
- const QString &matchText)
+static ILocatorFilter::MatchLevel matchLevelFor(const QRegularExpressionMatch &match,
+ const QString &matchText)
{
const int consecutivePos = match.capturedStart(1);
if (consecutivePos == 0)
- return MatchLevel::Best;
+ return ILocatorFilter::MatchLevel::Best;
if (consecutivePos > 0) {
const QChar prevChar = matchText.at(consecutivePos - 1);
if (prevChar == '_' || prevChar == '.')
- return MatchLevel::Better;
+ return ILocatorFilter::MatchLevel::Better;
}
if (match.capturedStart() == 0)
- return MatchLevel::Good;
- return MatchLevel::Normal;
+ return ILocatorFilter::MatchLevel::Good;
+ return ILocatorFilter::MatchLevel::Normal;
+}
+
+static bool askForCreating(const QString &title, const FilePath &filePath)
+{
+ QMessageBox::StandardButton selected
+ = CheckableMessageBox::question(ICore::dialogParent(),
+ title,
+ Tr::tr("Create \"%1\"?").arg(filePath.shortNativePath()),
+ ICore::settings(),
+ kAlwaysCreate,
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Cancel,
+ QMessageBox::Yes,
+ {{QMessageBox::Yes, Tr::tr("Create")}},
+ Tr::tr("Always create"));
+ return selected == QMessageBox::Yes;
+}
+
+static void createAndOpenFile(const FilePath &filePath)
+{
+ if (!filePath.exists()) {
+ if (askForCreating(Tr::tr("Create File"), filePath)) {
+ QFile file(filePath.toFSPathString());
+ file.open(QFile::WriteOnly);
+ file.close();
+ VcsManager::promptToAdd(filePath.absolutePath(), {filePath});
+ }
+ }
+ if (filePath.exists())
+ EditorManager::openEditor(filePath);
+}
+
+static bool createDirectory(const FilePath &filePath)
+{
+ if (!filePath.exists()) {
+ if (askForCreating(Tr::tr("Create Directory"), filePath))
+ filePath.createDir();
+ }
+ if (filePath.exists())
+ return true;
+ return false;
+}
+
+static FilePaths deviceRoots()
+{
+ const QString rootPath = FilePath::specialRootPath();
+ const QStringList roots = QDir(rootPath).entryList();
+ FilePaths devices;
+ for (const QString &root : roots) {
+ const QString prefix = rootPath + '/' + root;
+ devices += Utils::transform(QDir(prefix).entryList(), [prefix](const QString &s) {
+ return FilePath::fromString(prefix + '/' + s);
+ });
+ }
+ return devices;
}
FileSystemFilter::FileSystemFilter()
@@ -55,146 +120,199 @@ FileSystemFilter::FileSystemFilter()
"path. \"~\" refers to your home directory. You have the option to create a "
"file if it does not exist yet."));
setDefaultShortcutString("f");
- setDefaultIncludedByDefault(false);
+ *sDeviceRootIcon = qApp->style()->standardIcon(QStyle::SP_DriveHDIcon);
}
-void FileSystemFilter::prepareSearch(const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const QString &shortcutString, const FilePath &currentDocumentDir,
+ bool includeHidden)
{
- Q_UNUSED(entry)
- m_currentDocumentDirectory = DocumentManager::fileDialogInitialDirectory().toString();
- m_currentIncludeHidden = m_includeHidden;
-}
-
-QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
-{
- QList<LocatorFilterEntry> entries[int(MatchLevel::Count)];
-
- Environment env = Environment::systemEnvironment();
- const QString expandedEntry = env.expandVariables(entry);
-
- const QFileInfo entryInfo(expandedEntry);
- const QString entryFileName = entryInfo.fileName();
- QString directory = entryInfo.path();
- if (entryInfo.isRelative()) {
- if (entryInfo.filePath().startsWith("~/"))
- directory.replace(0, 1, QDir::homePath());
- else if (!m_currentDocumentDirectory.isEmpty())
- directory.prepend(m_currentDocumentDirectory + "/");
- }
- const QDir dirInfo(directory);
- QDir::Filters dirFilter = QDir::Dirs|QDir::Drives|QDir::NoDot|QDir::NoDotDot;
+ const QString input = storage.input();
+ LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)];
+
+ const Environment env = Environment::systemEnvironment();
+ const QString expandedEntry = env.expandVariables(input);
+ const auto expandedEntryPath = FilePath::fromUserInput(expandedEntry);
+ const FilePath absoluteEntryPath = currentDocumentDir.isEmpty()
+ ? expandedEntryPath
+ : currentDocumentDir.resolvePath(expandedEntryPath);
+ // The case of e.g. "ssh://", "ssh://*p", etc
+ const bool isPartOfDeviceRoot = expandedEntryPath.needsDevice()
+ && expandedEntryPath.path().isEmpty();
+
+ // Consider the entered path a directory if it ends with slash/backslash.
+ // If it is a dir but doesn't end with a backslash, we want to still show all (other) matching
+ // items from the same parent directory.
+ // Unfortunately fromUserInput removes slash/backslash at the end, so manually check the original.
+ const bool isDir = expandedEntry.isEmpty() || expandedEntry.endsWith('/')
+ || expandedEntry.endsWith('\\');
+ const FilePath directory = isDir ? absoluteEntryPath : absoluteEntryPath.parentDir();
+ const QString entryFileName = isDir ? QString() : absoluteEntryPath.fileName();
+
+ QDir::Filters dirFilter = QDir::Dirs | QDir::Drives | QDir::NoDot | QDir::NoDotDot;
QDir::Filters fileFilter = QDir::Files;
- if (m_currentIncludeHidden) {
+ if (includeHidden) {
dirFilter |= QDir::Hidden;
fileFilter |= QDir::Hidden;
}
// use only 'name' for case sensitivity decision, because we need to make the path
// match the case on the file system for case-sensitive file systems
- const Qt::CaseSensitivity caseSensitivity_ = caseSensitivity(entryFileName);
- const QStringList dirs = QStringList("..")
- + dirInfo.entryList(dirFilter, QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
- const QStringList files = dirInfo.entryList(fileFilter,
- QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
-
- QRegularExpression regExp = createRegExp(entryFileName, caseSensitivity_);
- if (!regExp.isValid())
- return {};
-
- for (const QString &dir : dirs) {
- if (future.isCanceled())
- break;
-
- const QRegularExpressionMatch match = regExp.match(dir);
- if (match.hasMatch()) {
- const MatchLevel level = matchLevelFor(match, dir);
- const QString fullPath = dirInfo.filePath(dir);
- LocatorFilterEntry filterEntry(this, dir);
- filterEntry.filePath = FilePath::fromString(fullPath);
- filterEntry.highlightInfo = highlightInfo(match);
-
- entries[int(level)].append(filterEntry);
+ const Qt::CaseSensitivity caseSensitivity = ILocatorFilter::caseSensitivity(entryFileName);
+ const FilePaths dirs = isPartOfDeviceRoot
+ ? FilePaths()
+ : FilePaths({directory / ".."})
+ + directory.dirEntries({{}, dirFilter},
+ QDir::Name | QDir::IgnoreCase
+ | QDir::LocaleAware);
+ const FilePaths files = isPartOfDeviceRoot ? FilePaths()
+ : directory.dirEntries({{}, fileFilter},
+ QDir::Name | QDir::IgnoreCase
+ | QDir::LocaleAware);
+
+ // directories
+ QRegularExpression regExp = ILocatorFilter::createRegExp(entryFileName, caseSensitivity);
+ if (regExp.isValid()) {
+ for (const FilePath &dir : dirs) {
+ if (promise.isCanceled())
+ return;
+
+ const QString dirString = dir.relativeChildPath(directory).nativePath();
+ const QRegularExpressionMatch match = regExp.match(dirString);
+ if (match.hasMatch()) {
+ const ILocatorFilter::MatchLevel level = matchLevelFor(match, dirString);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = dirString;
+ filterEntry.acceptor = [shortcutString, dir] {
+ const QString value
+ = shortcutString + ' '
+ + dir.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
+ return AcceptResult{value, int(value.length())};
+ };
+ filterEntry.filePath = dir;
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
+
+ entries[int(level)].append(filterEntry);
+ }
}
}
// file names can match with +linenumber or :linenumber
const Link link = Link::fromString(entryFileName, true);
- regExp = createRegExp(link.targetFilePath.toString(), caseSensitivity_);
- if (!regExp.isValid())
- return {};
- for (const QString &file : files) {
- if (future.isCanceled())
- break;
-
- const QRegularExpressionMatch match = regExp.match(file);
- if (match.hasMatch()) {
- const MatchLevel level = matchLevelFor(match, file);
- const QString fullPath = dirInfo.filePath(file);
- LocatorFilterEntry filterEntry(this, file);
- filterEntry.filePath = FilePath::fromString(fullPath);
- filterEntry.highlightInfo = highlightInfo(match);
- filterEntry.linkForEditor = Link(filterEntry.filePath, link.targetLine,
- link.targetColumn);
- entries[int(level)].append(filterEntry);
+ regExp = ILocatorFilter::createRegExp(link.targetFilePath.toString(), caseSensitivity);
+ if (regExp.isValid()) {
+ for (const FilePath &file : files) {
+ if (promise.isCanceled())
+ return;
+
+ const QString fileString = file.relativeChildPath(directory).nativePath();
+ const QRegularExpressionMatch match = regExp.match(fileString);
+ if (match.hasMatch()) {
+ const ILocatorFilter::MatchLevel level = matchLevelFor(match, fileString);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = fileString;
+ filterEntry.filePath = file;
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
+ filterEntry.linkForEditor = Link(filterEntry.filePath,
+ link.targetLine,
+ link.targetColumn);
+ entries[int(level)].append(filterEntry);
+ }
+ }
+ }
+ // device roots
+ // check against full search text
+ regExp = ILocatorFilter::createRegExp(expandedEntryPath.toUserOutput(), caseSensitivity);
+ if (regExp.isValid()) {
+ const FilePaths roots = deviceRoots();
+ for (const FilePath &root : roots) {
+ if (promise.isCanceled())
+ return;
+
+ const QString displayString = root.toUserOutput();
+ const QRegularExpressionMatch match = regExp.match(displayString);
+ if (match.hasMatch()) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = displayString;
+ filterEntry.acceptor = [shortcutString, root] {
+ const QString value
+ = shortcutString + ' '
+ + root.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
+ return AcceptResult{value, int(value.length())};
+ };
+ filterEntry.filePath = root;
+ filterEntry.displayIcon = *sDeviceRootIcon;
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
+
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ }
}
}
// "create and open" functionality
- const QString fullFilePath = dirInfo.filePath(entryFileName);
+ const FilePath fullFilePath = directory / entryFileName;
const bool containsWildcard = expandedEntry.contains('?') || expandedEntry.contains('*');
- if (!containsWildcard && !QFileInfo::exists(fullFilePath) && dirInfo.exists()) {
- LocatorFilterEntry createAndOpen(this, Tr::tr("Create and Open \"%1\"").arg(expandedEntry));
- createAndOpen.filePath = FilePath::fromString(fullFilePath);
- createAndOpen.extraInfo = FilePath::fromString(dirInfo.absolutePath()).shortNativePath();
- entries[int(MatchLevel::Normal)].append(createAndOpen);
+ if (!containsWildcard && !fullFilePath.exists() && directory.exists()) {
+ // create and open file
+ {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = Tr::tr("Create and Open File \"%1\"").arg(expandedEntry);
+ filterEntry.displayIcon = Utils::FileIconProvider::icon(QFileIconProvider::File);
+ filterEntry.acceptor = [fullFilePath] {
+ QMetaObject::invokeMethod(
+ EditorManager::instance(),
+ [fullFilePath] { createAndOpenFile(fullFilePath); },
+ Qt::QueuedConnection);
+ return AcceptResult();
+ };
+ filterEntry.filePath = fullFilePath;
+ filterEntry.extraInfo = directory.absoluteFilePath().shortNativePath();
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ }
+
+ // create directory
+ {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = Tr::tr("Create Directory \"%1\"").arg(expandedEntry);
+ filterEntry.displayIcon = Utils::FileIconProvider::icon(QFileIconProvider::Folder);
+ filterEntry.acceptor = [fullFilePath, shortcutString] {
+ QMetaObject::invokeMethod(
+ EditorManager::instance(),
+ [fullFilePath, shortcutString] {
+ if (createDirectory(fullFilePath)) {
+ const QString value = shortcutString + ' '
+ + fullFilePath.absoluteFilePath()
+ .cleanPath()
+ .pathAppended("/")
+ .toUserOutput();
+ LocatorManager::show(value, value.length());
+ }
+ },
+ Qt::QueuedConnection);
+ return AcceptResult();
+ };
+ filterEntry.filePath = fullFilePath;
+ filterEntry.extraInfo = directory.absoluteFilePath().shortNativePath();
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ }
}
- return std::accumulate(std::begin(entries), std::end(entries), QList<LocatorFilterEntry>());
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
}
-const char kAlwaysCreate[] = "Locator/FileSystemFilter/AlwaysCreate";
-
-void FileSystemFilter::accept(const LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+LocatorMatcherTasks FileSystemFilter::matchers()
{
- Q_UNUSED(selectionLength)
- if (selection.filePath.isDir()) {
- const QString value
- = shortcutString() + ' '
- + selection.filePath.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
- *newText = value;
- *selectionStart = value.length();
- } else {
- // Don't block locator filter execution with dialog
- QMetaObject::invokeMethod(EditorManager::instance(), [selection] {
- if (!selection.filePath.exists()) {
- if (CheckableMessageBox::shouldAskAgain(ICore::settings(), kAlwaysCreate)) {
- CheckableMessageBox messageBox(ICore::dialogParent());
- messageBox.setWindowTitle(Tr::tr("Create File"));
- messageBox.setIcon(QMessageBox::Question);
- messageBox.setText(Tr::tr("Create \"%1\"?").arg(selection.filePath.shortNativePath()));
- messageBox.setCheckBoxVisible(true);
- messageBox.setCheckBoxText(Tr::tr("Always create"));
- messageBox.setChecked(false);
- messageBox.setStandardButtons(QDialogButtonBox::Cancel);
- QPushButton *createButton = messageBox.addButton(Tr::tr("Create"),
- QDialogButtonBox::AcceptRole);
- messageBox.setDefaultButton(QDialogButtonBox::Cancel);
- messageBox.exec();
- if (messageBox.clickedButton() != createButton)
- return;
- if (messageBox.isChecked())
- CheckableMessageBox::doNotAskAgain(ICore::settings(), kAlwaysCreate);
- }
- QFile file(selection.filePath.toString());
- file.open(QFile::WriteOnly);
- file.close();
- VcsManager::promptToAdd(selection.filePath.absolutePath(), {selection.filePath});
- }
- BaseFileFilter::openEditorAt(selection);
- }, Qt::QueuedConnection);
- }
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, includeHidden = m_includeHidden, shortcut = shortcutString()]
+ (Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, shortcut,
+ DocumentManager::fileDialogInitialDirectory(), includeHidden);
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
class FileSystemFilterOptions : public QDialog
@@ -260,13 +378,13 @@ const char kIncludeHiddenKey[] = "includeHidden";
void FileSystemFilter::saveState(QJsonObject &object) const
{
- if (m_includeHidden != kIncludeHiddenDefault)
+ if (m_includeHidden != s_includeHiddenDefault)
object.insert(kIncludeHiddenKey, m_includeHidden);
}
void FileSystemFilter::restoreState(const QJsonObject &object)
{
- m_currentIncludeHidden = object.value(kIncludeHiddenKey).toBool(kIncludeHiddenDefault);
+ m_includeHidden = object.value(kIncludeHiddenKey).toBool(s_includeHiddenDefault);
}
void FileSystemFilter::restoreState(const QByteArray &state)
@@ -290,5 +408,4 @@ void FileSystemFilter::restoreState(const QByteArray &state)
}
}
-} // Internal
-} // Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.h b/src/plugins/coreplugin/locator/filesystemfilter.h
index 33338d626e..3dfac11b3a 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.h
+++ b/src/plugins/coreplugin/locator/filesystemfilter.h
@@ -5,40 +5,24 @@
#include "ilocatorfilter.h"
-#include <QByteArray>
-#include <QFutureInterface>
-#include <QList>
-#include <QString>
-
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class FileSystemFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit FileSystemFilter();
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void restoreState(const QByteArray &state) override;
- bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
+ FileSystemFilter();
+ void restoreState(const QByteArray &state) final;
+ bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
protected:
void saveState(QJsonObject &object) const final;
void restoreState(const QJsonObject &object) final;
private:
- static MatchLevel matchLevelFor(const QRegularExpressionMatch &match, const QString &matchText);
+ LocatorMatcherTasks matchers() final;
- static const bool kIncludeHiddenDefault = true;
- bool m_includeHidden = kIncludeHiddenDefault;
- bool m_currentIncludeHidden = kIncludeHiddenDefault;
- QString m_currentDocumentDirectory;
+ static const bool s_includeHiddenDefault = true;
+ bool m_includeHidden = s_includeHiddenDefault;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
index 4523c719fb..1a08792382 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
@@ -5,19 +5,27 @@
#include "../coreplugintr.h"
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fuzzymatcher.h>
#include <QBoxLayout>
#include <QCheckBox>
-#include <QCoreApplication>
#include <QDialog>
#include <QDialogButtonBox>
+#include <QFutureWatcher>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QRegularExpression>
+#include <QWaitCondition>
+
+#include <unordered_set>
+using namespace Tasking;
using namespace Utils;
/*!
@@ -44,6 +52,508 @@ using namespace Utils;
namespace Core {
+// ResultsDeduplicator squashes upcoming results from various filters and removes
+// duplicated entries. It also reports intermediate results (to be displayed in LocatorWidget).
+//
+// Assuming the results from filters come in this order (numbers are indices to filter results):
+// 2, 4, 3, 1 - the various strategies are possible. The current strategy looks like this:
+// - When 2nd result came - the result is stored
+// - When 4th result came - the result is stored
+// - When 3rd result came - it's being squased to the 2nd result and afterwards the 4th result
+// result is being squashed into the common result list.
+// - When 1st result came - the stored common list is squashed into the 1st result
+// and intermediate results are reported (for 1-4 results).
+// If the filterCount is 4, the deduplicator finishes now.
+// If the filterCount is greater than 4, it waits for the remaining
+// results.
+//
+// TODO: The other possible startegy would be to just store the newly reported data
+// and do the actual deduplication only when new results are reachable from 1st index
+// (i.e. skip the intermediate deduplication).
+class ResultsDeduplicator
+{
+ enum class State {
+ Awaiting, // Waiting in a separate thread for new data, or fetched the last new data and
+ // is currently deduplicating.
+ // This happens when all previous data were squashed in the separate thread but
+ // still some data needs to come (reportOutput wasn't called for all filters,
+ // yet). The expected number of calls to reportOutput equals m_filterCount.
+ NewData, // The new data came and the separate thread is being awaken in order to squash
+ // it. After the separate thread is awaken it transitions to Awaiting state.
+ Canceled // The Deduplicator task has been canceled.
+ };
+
+ // A separate item for keeping squashed entries. Call mergeWith() to squash a consecutive
+ // results into this results.
+ struct WorkingData {
+ WorkingData() = default;
+ WorkingData(const LocatorFilterEntries &entries, std::atomic<State> &state) {
+ mergeWith(entries, state);
+ }
+ LocatorFilterEntries mergeWith(const LocatorFilterEntries &entries,
+ std::atomic<State> &state) {
+ LocatorFilterEntries results;
+ results.reserve(entries.size());
+ for (const LocatorFilterEntry &entry : entries) {
+ if (state == State::Canceled)
+ return {};
+ const auto &link = entry.linkForEditor;
+ if (!link || m_cache.emplace(*link).second)
+ results.append(entry);
+ }
+ if (state == State::Canceled)
+ return {};
+
+ m_data += results;
+ return results;
+ }
+ LocatorFilterEntries entries() const { return m_data; }
+ private:
+ LocatorFilterEntries m_data;
+ std::unordered_set<Link> m_cache;
+ };
+
+public:
+ // filterCount is the expected numbers of running filters. The separate thread executing run()
+ // will stop after reportOutput was called filterCount times, for all different indices
+ // in range [0, filterCount).
+ ResultsDeduplicator(int filterCount)
+ : m_filterCount(filterCount)
+ , m_outputData(filterCount, {})
+ {}
+
+ void reportOutput(int index, const LocatorFilterEntries &outputData)
+ // Called directly by running filters. The calls may come from main thread in case of
+ // e.g. Sync task or directly from other threads when AsyncTask was used.
+ {
+ QTC_ASSERT(index >= 0, return);
+
+ QMutexLocker locker(&m_mutex);
+ // It may happen that the task tree was canceled, while tasks are still running in other
+ // threads and are about to be canceled. In this case we just ignore the call.
+ if (m_state == State::Canceled)
+ return;
+ QTC_ASSERT(index < m_filterCount, return);
+ QTC_ASSERT(!m_outputData.at(index).has_value(), return);
+
+ m_outputData[index] = outputData;
+ m_state = State::NewData;
+ m_waitCondition.wakeOne();
+ }
+
+ // Called when the LocatorMatcher was canceled. It wakes the separate thread in order to
+ // finish it, soon.
+ void cancel()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_state = State::Canceled;
+ m_waitCondition.wakeOne();
+ }
+
+ // Called from the separate thread (ResultsCollector's thread)
+ void run(QPromise<LocatorFilterEntries> &promise)
+ {
+ QList<std::optional<LocatorFilterEntries>> data;
+ QList<std::optional<WorkingData>> workingList(m_filterCount, {});
+ while (waitForData(&data)) {
+ // Emit new results only when new data is reachable from the beginning (i.e. no gaps)
+ int currentIndex = 0;
+ int mergeToIndex = 0;
+ bool hasGap = false;
+ while (currentIndex < m_filterCount) {
+ if (m_state == State::Canceled)
+ return;
+ const auto &outputData = data.at(currentIndex);
+ if (!outputData.has_value()) {
+ ++currentIndex;
+ mergeToIndex = currentIndex;
+ hasGap = true;
+ continue;
+ }
+ const auto &workingData = workingList.at(currentIndex);
+ if (!workingData.has_value()) {
+ const bool mergeToCurrent = currentIndex == mergeToIndex;
+ const LocatorFilterEntries dataForIndex = mergeToCurrent ? *outputData
+ : LocatorFilterEntries();
+ workingList[currentIndex] = std::make_optional(WorkingData(dataForIndex,
+ m_state));
+ if (m_state == State::Canceled)
+ return;
+ const LocatorFilterEntries newData = mergeToCurrent
+ ? workingList[currentIndex]->entries()
+ : workingList[mergeToIndex]->mergeWith(*outputData, m_state);
+ if (m_state == State::Canceled)
+ return;
+ if (!hasGap && !newData.isEmpty())
+ promise.addResult(newData);
+ } else if (currentIndex != mergeToIndex) {
+ const LocatorFilterEntries newData
+ = workingList[mergeToIndex]->mergeWith(workingData->entries(), m_state);
+ workingList[currentIndex] = std::make_optional<WorkingData>({});
+ if (m_state == State::Canceled)
+ return;
+ if (!hasGap && !newData.isEmpty())
+ promise.addResult(newData);
+ }
+ ++currentIndex;
+ }
+ // All data arrived (no gap), so finish here
+ if (!hasGap)
+ return;
+ }
+ }
+
+private:
+ // Called from the separate thread, exclusively by run(). Checks if the new data already
+ // came before sleeping with wait condition. If so, it doesn't sleep with wait condition,
+ // but returns the data collected in meantime. Otherwise, it calls wait() on wait condition.
+ bool waitForData(QList<std::optional<LocatorFilterEntries>> *data)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_state == State::Canceled)
+ return false;
+ if (m_state == State::NewData) {
+ m_state = State::Awaiting; // Mark the state as awaiting to detect new calls to
+ // setOutputData while the separate thread deduplicates the
+ // new data.
+ *data = m_outputData;
+ return true;
+ }
+ m_waitCondition.wait(&m_mutex);
+ QTC_ASSERT(m_state != State::Awaiting, return false);
+ if (m_state == State::Canceled)
+ return false;
+ m_state = State::Awaiting; // Mark the state as awaiting to detect new calls to
+ // setOutputData while the separate thread deduplicates the
+ // new data.
+ *data = m_outputData;
+ return true;
+ }
+
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+ const int m_filterCount = 0;
+ std::atomic<State> m_state = State::Awaiting;
+ QList<std::optional<LocatorFilterEntries>> m_outputData;
+};
+
+// This instance of this object is created by LocatorMatcher tree.
+// It starts a separate thread which collects and deduplicates the results reported
+// by LocatorStorage instances. The ResultsCollector is started as a first task in
+// LocatorMatcher and runs in parallel to all the filters started by LocatorMatcher.
+// When all the results are reported (the expected number of reports is set with setFilterCount()),
+// the ResultsCollector finishes. The intermediate results are reported with
+// serialOutputDataReady() signal.
+// The object of ResultsCollector is registered in Tasking namespace under the Collector name.
+class ResultsCollector : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~ResultsCollector();
+ void setFilterCount(int count);
+ void start();
+
+ bool isRunning() const { return m_watcher.get(); }
+
+ std::shared_ptr<ResultsDeduplicator> deduplicator() const { return m_deduplicator; }
+
+signals:
+ void serialOutputDataReady(const LocatorFilterEntries &serialOutputData);
+ void done();
+
+private:
+ int m_filterCount = 0;
+ std::unique_ptr<QFutureWatcher<LocatorFilterEntries>> m_watcher;
+ std::shared_ptr<ResultsDeduplicator> m_deduplicator;
+};
+
+ResultsCollector::~ResultsCollector()
+{
+ if (!isRunning())
+ return;
+
+ m_deduplicator->cancel();
+ if (ExtensionSystem::PluginManager::futureSynchronizer()) {
+ ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future());
+ return;
+ }
+ m_watcher->future().waitForFinished();
+}
+
+void ResultsCollector::setFilterCount(int count)
+{
+ QTC_ASSERT(!isRunning(), return);
+ QTC_ASSERT(count >= 0, return);
+
+ m_filterCount = count;
+}
+
+void ResultsCollector::start()
+{
+ QTC_ASSERT(!m_watcher, return);
+ QTC_ASSERT(!isRunning(), return);
+ if (m_filterCount == 0) {
+ emit done();
+ return;
+ }
+
+ m_deduplicator.reset(new ResultsDeduplicator(m_filterCount));
+ m_watcher.reset(new QFutureWatcher<LocatorFilterEntries>);
+ connect(m_watcher.get(), &QFutureWatcherBase::resultReadyAt, this, [this](int index) {
+ emit serialOutputDataReady(m_watcher->resultAt(index));
+ });
+ connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
+ emit done();
+ m_watcher.release()->deleteLater();
+ m_deduplicator.reset();
+ });
+
+ // TODO: When filterCount == 1, deliver results directly and finish?
+ auto deduplicate = [](QPromise<LocatorFilterEntries> &promise,
+ const std::shared_ptr<ResultsDeduplicator> &deduplicator) {
+ deduplicator->run(promise);
+ };
+ m_watcher->setFuture(Utils::asyncRun(deduplicate, m_deduplicator));
+}
+
+class ResultsCollectorAdapter : public TaskAdapter<ResultsCollector>
+{
+public:
+ ResultsCollectorAdapter() {
+ connect(task(), &ResultsCollector::done, this, [this] { emit done(true); });
+ }
+ void start() final { task()->start(); }
+};
+
+} // namespace Core
+
+TASKING_DECLARE_TASK(Collector, Core::ResultsCollectorAdapter);
+
+namespace Core {
+
+class LocatorStoragePrivate
+{
+public:
+ LocatorStoragePrivate(const QString &input, int index,
+ const std::shared_ptr<ResultsDeduplicator> &deduplicator)
+ : m_input(input)
+ , m_index(index)
+ , m_deduplicator(deduplicator)
+ {}
+
+ QString input() const { return m_input; }
+
+ void reportOutput(const LocatorFilterEntries &outputData)
+ {
+ QMutexLocker locker(&m_mutex);
+ QTC_ASSERT(m_deduplicator, return);
+ reportOutputImpl(outputData);
+ }
+
+ void finalize()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_deduplicator)
+ reportOutputImpl({});
+ }
+
+private:
+ // Call me with mutex locked
+ void reportOutputImpl(const LocatorFilterEntries &outputData)
+ {
+ QTC_ASSERT(m_index >= 0, return);
+ m_deduplicator->reportOutput(m_index, outputData);
+ // Deliver results only once for all copies of the storage, drop ref afterwards
+ m_deduplicator.reset();
+ }
+
+ const QString m_input;
+ const int m_index = -1;
+ std::shared_ptr<ResultsDeduplicator> m_deduplicator;
+ QMutex m_mutex = {};
+};
+
+QString LocatorStorage::input() const
+{
+ QTC_ASSERT(d, return {});
+ return d->input();
+}
+
+void LocatorStorage::reportOutput(const LocatorFilterEntries &outputData) const
+{
+ QTC_ASSERT(d, return);
+ d->reportOutput(outputData);
+}
+
+void LocatorStorage::finalize() const
+{
+ QTC_ASSERT(d, return);
+ d->finalize();
+}
+
+class LocatorMatcherPrivate
+{
+public:
+ LocatorMatcherTasks m_tasks;
+ QString m_input;
+ LocatorFilterEntries m_output;
+ int m_parallelLimit = 0;
+ std::unique_ptr<TaskTree> m_taskTree;
+};
+
+LocatorMatcher::LocatorMatcher()
+ : d(new LocatorMatcherPrivate) {}
+
+LocatorMatcher::~LocatorMatcher() = default;
+
+void LocatorMatcher::setTasks(const LocatorMatcherTasks &tasks)
+{
+ d->m_tasks = tasks;
+}
+
+void LocatorMatcher::setInputData(const QString &inputData)
+{
+ d->m_input = inputData;
+}
+
+void LocatorMatcher::setParallelLimit(int limit)
+{
+ d->m_parallelLimit = limit;
+}
+
+void LocatorMatcher::start()
+{
+ QTC_ASSERT(!isRunning(), return);
+ d->m_output = {};
+ d->m_taskTree.reset(new TaskTree);
+
+ struct CollectorStorage
+ {
+ ResultsCollector *m_collector = nullptr;
+ };
+ TreeStorage<CollectorStorage> collectorStorage;
+
+ const int filterCount = d->m_tasks.size();
+ const auto onCollectorSetup = [this, filterCount, collectorStorage](ResultsCollector &collector) {
+ collectorStorage->m_collector = &collector;
+ collector.setFilterCount(filterCount);
+ connect(&collector, &ResultsCollector::serialOutputDataReady,
+ this, [this](const LocatorFilterEntries &serialOutputData) {
+ d->m_output += serialOutputData;
+ emit serialOutputDataReady(serialOutputData);
+ });
+ };
+ const auto onCollectorDone = [collectorStorage](const ResultsCollector &collector) {
+ Q_UNUSED(collector)
+ collectorStorage->m_collector = nullptr;
+ };
+
+ QList<TaskItem> parallelTasks { ParallelLimit(d->m_parallelLimit) };
+
+ const auto onGroupSetup = [this, collectorStorage](const TreeStorage<LocatorStorage> &storage,
+ int index) {
+ return [this, collectorStorage, storage, index] {
+ ResultsCollector *collector = collectorStorage->m_collector;
+ QTC_ASSERT(collector, return);
+ *storage = std::make_shared<LocatorStoragePrivate>(d->m_input, index,
+ collector->deduplicator());
+ };
+ };
+
+ const auto onGroupDone = [](const TreeStorage<LocatorStorage> &storage) {
+ return [storage] { storage->finalize(); };
+ };
+
+ int index = 0;
+ for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) {
+ const auto storage = task.storage;
+ const Group group {
+ optional,
+ Storage(storage),
+ OnGroupSetup(onGroupSetup(storage, index)),
+ OnGroupDone(onGroupDone(storage)),
+ OnGroupError(onGroupDone(storage)),
+ task.task
+ };
+ parallelTasks << group;
+ ++index;
+ }
+
+ const Group root {
+ parallel,
+ Storage(collectorStorage),
+ Collector(onCollectorSetup, onCollectorDone, onCollectorDone),
+ Group {
+ parallelTasks
+ }
+ };
+
+ d->m_taskTree->setupRoot(root);
+
+ const auto onFinish = [this](bool success) {
+ return [this, success] {
+ emit done(success);
+ d->m_taskTree.release()->deleteLater();
+ };
+ };
+ connect(d->m_taskTree.get(), &TaskTree::done, this, onFinish(true));
+ connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, onFinish(false));
+ d->m_taskTree->start();
+}
+
+void LocatorMatcher::stop()
+{
+ if (!isRunning())
+ return;
+
+ d->m_taskTree->stop();
+ d->m_taskTree.reset();
+}
+
+bool LocatorMatcher::isRunning() const
+{
+ return d->m_taskTree.get() && d->m_taskTree->isRunning();
+}
+
+LocatorFilterEntries LocatorMatcher::outputData() const
+{
+ return d->m_output;
+}
+
+LocatorFilterEntries LocatorMatcher::runBlocking(const LocatorMatcherTasks &tasks,
+ const QString &input, int parallelLimit)
+{
+ LocatorMatcher tree;
+ tree.setTasks(tasks);
+ tree.setInputData(input);
+ tree.setParallelLimit(parallelLimit);
+
+ QEventLoop loop;
+ connect(&tree, &LocatorMatcher::done, &loop, [&loop] { loop.quit(); });
+ tree.start();
+ if (tree.isRunning())
+ loop.exec(QEventLoop::ExcludeUserInputEvents);
+ return tree.outputData();
+}
+
+static QHash<MatcherType, QList<LocatorMatcherTaskCreator>> s_matcherCreators = {};
+
+void LocatorMatcher::addMatcherCreator(MatcherType type, const LocatorMatcherTaskCreator &creator)
+{
+ QTC_ASSERT(creator, return);
+ s_matcherCreators[type].append(creator);
+}
+
+LocatorMatcherTasks LocatorMatcher::matchers(MatcherType type)
+{
+ const QList<LocatorMatcherTaskCreator> creators = s_matcherCreators.value(type);
+ LocatorMatcherTasks result;
+ for (const LocatorMatcherTaskCreator &creator : creators)
+ result << creator();
+ return result;
+}
+
static QList<ILocatorFilter *> g_locatorFilters;
/*!
@@ -83,19 +593,20 @@ QString ILocatorFilter::shortcutString() const
}
/*!
- Performs actions that need to be done in the main thread before actually
- running the search for \a entry.
-
- Called on the main thread before matchesFor() is called in a separate
- thread.
-
- The default implementation does nothing.
+ Sets the refresh recipe for refreshing cached data.
+*/
+void ILocatorFilter::setRefreshRecipe(const std::optional<TaskItem> &recipe)
+{
+ m_refreshRecipe = recipe;
+}
- \sa matchesFor()
+/*!
+ Returns the refresh recipe for refreshing cached data. By default, the locator filter has
+ no recipe set, so that it won't be refreshed.
*/
-void ILocatorFilter::prepareSearch(const QString &entry)
+std::optional<TaskItem> ILocatorFilter::refreshRecipe() const
{
- Q_UNUSED(entry)
+ return m_refreshRecipe;
}
/*!
@@ -201,13 +712,13 @@ void ILocatorFilter::restoreState(const QByteArray &state)
various aspects of the filter. Called when the user requests to configure
the filter.
- Set \a needsRefresh to \c true, if a refresh() should be done after
+ Set \a needsRefresh to \c true, if a refresh should be done after
closing the dialog. Return \c false if the user canceled the dialog.
The default implementation allows changing the shortcut and whether the
filter is included by default.
- \sa refresh()
+ \sa refreshRecipe()
*/
bool ILocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
{
@@ -425,7 +936,10 @@ ILocatorFilter::Priority ILocatorFilter::priority() const
*/
void ILocatorFilter::setEnabled(bool enabled)
{
+ if (enabled == m_enabled)
+ return;
m_enabled = enabled;
+ emit enabledChanged(m_enabled);
}
/*!
@@ -582,39 +1096,6 @@ bool ILocatorFilter::isOldSetting(const QByteArray &state)
}
/*!
- \fn QList<Core::LocatorFilterEntry> Core::ILocatorFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-
- Returns the list of results of this filter for the search term \a entry.
- This is run in a separate thread, but is guaranteed to only run in a single
- thread at any given time. Quickly running preparations can be done in the
- GUI thread in prepareSearch().
-
- Implementations should do a case sensitive or case insensitive search
- depending on caseSensitivity(). If \a future is \c canceled, the search
- should be aborted.
-
- \sa prepareSearch()
- \sa caseSensitivity()
-*/
-
-/*!
- \fn void Core::ILocatorFilter::accept(Core::const LocatorFilterEntry &selection, QString *newText, int *selectionStart, int *selectionLength) const
-
- Called with the entry specified by \a selection when the user activates it
- in the result list.
- Implementations can return a new search term \a newText, which has \a selectionLength characters
- starting from \a selectionStart preselected, and the cursor set to the end of the selection.
-*/
-
-/*!
- \fn void Core::ILocatorFilter::refresh(QFutureInterface<void> &future)
-
- Refreshes cached data asynchronously.
-
- If \a future is \c canceled, the refresh should be aborted.
-*/
-
-/*!
\enum Core::ILocatorFilter::Priority
This enum value holds the priority that is used for ordering the results
@@ -647,4 +1128,411 @@ bool ILocatorFilter::isOldSetting(const QByteArray &state)
expression.
*/
+std::atomic_int s_executeId = 0;
+
+
+class LocatorFileCachePrivate
+{
+public:
+ bool isValid() const { return bool(m_generator); }
+ void invalidate();
+ bool ensureValidated();
+ void bumpExecutionId() { m_executionId = s_executeId.fetch_add(1) + 1; }
+ void update(const LocatorFileCachePrivate &newCache);
+ void setGeneratorProvider(const LocatorFileCache::GeneratorProvider &provider)
+ { m_provider = provider; }
+ void setGenerator(const LocatorFileCache::FilePathsGenerator &generator);
+ LocatorFilterEntries generate(const QFuture<void> &future, const QString &input);
+
+ // Is persistent, does not reset on invalidate
+ LocatorFileCache::GeneratorProvider m_provider;
+ LocatorFileCache::FilePathsGenerator m_generator;
+ int m_executionId = 0;
+
+ std::optional<FilePaths> m_filePaths;
+
+ QString m_lastInput;
+ std::optional<FilePaths> m_cache;
+};
+
+// Clears all but provider
+void LocatorFileCachePrivate::invalidate()
+{
+ LocatorFileCachePrivate that;
+ that.m_provider = m_provider;
+ *this = that;
+}
+
+/*!
+ \internal
+
+ Returns true if the cache is valid. Otherwise, tries to validate the cache and returns whether
+ the validation succeeded.
+
+ When the cache is valid, it does nothing and returns true.
+ Otherwise, when the GeneratorProvider is not set, it does nothing and returns false.
+ Otherwise, the GeneratorProvider is used for recreating the FilePathsGenerator.
+ If the recreated FilePathsGenerator is not empty, it return true.
+ Otherwise, it returns false;
+*/
+bool LocatorFileCachePrivate::ensureValidated()
+{
+ if (isValid())
+ return true;
+
+ if (!m_provider)
+ return false;
+
+ invalidate();
+ m_generator = m_provider();
+ return isValid();
+}
+
+void LocatorFileCachePrivate::update(const LocatorFileCachePrivate &newCache)
+{
+ if (m_executionId != newCache.m_executionId)
+ return; // The mismatching executionId was detected, ignoring the update...
+ auto provider = m_provider;
+ *this = newCache;
+ m_provider = provider;
+}
+
+void LocatorFileCachePrivate::setGenerator(const LocatorFileCache::FilePathsGenerator &generator)
+{
+ invalidate();
+ m_generator = generator;
+}
+
+static bool containsPathSeparator(const QString &candidate)
+{
+ return candidate.contains('/') || candidate.contains('*');
+};
+
+/*!
+ \internal
+
+ Uses the generator to update the cache if needed and returns entries for the input.
+ Uses the cached data when no need for re-generation. Updates the cache accordingly.
+*/
+LocatorFilterEntries LocatorFileCachePrivate::generate(const QFuture<void> &future,
+ const QString &input)
+{
+ QTC_ASSERT(isValid(), return {});
+
+ // If search string contains spaces, treat them as wildcard '*' and search in full path
+ const QString wildcardInput = QDir::fromNativeSeparators(input).replace(' ', '*');
+ const Link inputLink = Link::fromString(wildcardInput, true);
+ const QString newInput = inputLink.targetFilePath.toString();
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(newInput);
+ if (!regExp.isValid())
+ return {}; // Don't clear the cache - still remember the cache for the last valid input.
+
+ if (future.isCanceled())
+ return {};
+
+ const bool hasPathSeparator = containsPathSeparator(newInput);
+ const bool containsLastInput = !m_lastInput.isEmpty() && newInput.contains(m_lastInput);
+ const bool pathSeparatorAdded = !containsPathSeparator(m_lastInput) && hasPathSeparator;
+ const bool searchInCache = m_filePaths && m_cache && containsLastInput && !pathSeparatorAdded;
+
+ std::optional<FilePaths> newPaths = m_filePaths;
+ if (!searchInCache && !newPaths) {
+ newPaths = m_generator(future);
+ if (future.isCanceled()) // Ensure we got not canceled results from generator.
+ return {};
+ }
+
+ const FilePaths &sourcePaths = searchInCache ? *m_cache : *newPaths;
+ LocatorFileCache::MatchedEntries entries = {};
+ const FilePaths newCache = LocatorFileCache::processFilePaths(
+ future, sourcePaths, hasPathSeparator, regExp, inputLink, &entries);
+ for (auto &entry : entries) {
+ if (future.isCanceled())
+ return {};
+
+ if (entry.size() < 1000)
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
+ }
+
+ if (future.isCanceled())
+ return {};
+
+ // Update all the cache data in one go
+ m_filePaths = newPaths;
+ m_lastInput = newInput;
+ m_cache = newCache;
+
+ return std::accumulate(std::begin(entries), std::end(entries), LocatorFilterEntries());
+}
+
+/*!
+ \class Core::LocatorFileCache
+
+ \brief The LocatorFileCache class encapsulates all the responsibilities needed for
+ implementing a cache for file filters.
+
+ LocatorFileCache serves as a replacement for the old BaseFileFilter interface.
+*/
+
+/*!
+ \fn LocatorFileCache
+
+ Constructs an invalid cache.
+
+ The cache is considered to be in an invalid state after a call to invalidate(),
+ of after a call to setFilePathsGenerator() when passed functions was empty.
+
+ It it possible to setup the automatic validator for the cache through the
+ setGeneratorProvider().
+
+ \sa invalidate, setGeneratorProvider, setFilePathsGenerator, setFilePaths
+*/
+
+LocatorFileCache::LocatorFileCache()
+ : d(new LocatorFileCachePrivate) {}
+
+/*!
+ Invalidates the cache.
+
+ In order to validate it, use either setFilePathsGenerator() or setFilePaths().
+ The cache may be automatically validated if the GeneratorProvider was set
+ through the setGeneratorProvider().
+
+ \note This function invalidates the cache permanently, clearing all the cached data,
+ and removing the stored generator. The stored generator provider is preserved.
+*/
+void LocatorFileCache::invalidate()
+{
+ d->invalidate();
+}
+
+/*!
+ Sets the file path generator provider.
+
+ The \a provider serves for an automatic validation of the invalid cache by recreating
+ the FilePathsGenerator. The automatic validation happens when the LocatorMatcherTask returned
+ by matcher() is being started, and the cache is not valid at that moment. In this case
+ the stored \a provider is being called.
+
+ The passed \a provider function is always called from the main thread. If needed, it is
+ called prior to starting an asynchronous task that collects the locator filter results.
+
+ When this function is called, the cache isn't invalidated.
+ Whenever cache's invalidation happens, e.g. when invalidate(), setFilePathsGenerator() or
+ setFilePaths() is called, the stored GeneratorProvider is being preserved.
+ In order to clear the stored GeneratorProvider, call this method with an empty
+ function {}.
+*/
+void LocatorFileCache::setGeneratorProvider(const GeneratorProvider &provider)
+{
+ d->setGeneratorProvider(provider);
+}
+
+std::optional<FilePaths> LocatorFileCache::filePaths() const
+{
+ return d->m_filePaths;
+}
+
+/*!
+ Sets the file path generator.
+
+ The \a generator serves for returning the full input list of file paths when the
+ associated LocatorMatherTask is being run in a separate thread. When the computation of the
+ full list of file paths takes a considerable amount of time, this computation may
+ be potentially moved to the separate thread, provided that all the dependent data may be safely
+ passed to the \a generator function when this function is being set in the main thread.
+
+ The passed \a generator is always called exclusively from the non-main thread when running
+ LocatorMatcherTask returned by matcher(). It is called when the cached data is
+ empty or when it needs to be regenerated due to a new search which can't reuse
+ the cache from the previous search.
+
+ Generating a new file path list may be a time consuming task. In order to finish the task early
+ when being canceled, the \e future argument of the FilePathsGenerator may be used.
+ The FilePathsGenerator returns the full list of file paths used for file filter's processing.
+
+ Whenever it is possible to postpone the creation of a file path list so that it may be done
+ safely later from the non-main thread, based on some other reentrant/thread-safe data,
+ this method should be used. The other dependent data should be passed by lambda capture.
+ The body of the passed \a generator should take extra care for ensuring that the passed
+ other data via lambda captures are reentrant and the lambda body is thread safe.
+ See the example usage of the generator inside CppIncludesFilter implementation.
+
+ Otherwise, when postponing the creation of file paths list isn't safe, use setFilePaths()
+ with ready made list, prepared in main thread.
+
+ \note This function invalidates the cache, clearing all the cached data,
+ and if the passed generator is non-empty, the cache is set to a valid state.
+ The stored generator provider is preserved.
+
+ \sa setGeneratorProvider, setFilePaths
+*/
+void LocatorFileCache::setFilePathsGenerator(const FilePathsGenerator &generator)
+{
+ d->setGenerator(generator);
+}
+
+/*!
+ Wraps the passed \a filePaths into a trivial FilePathsGenerator and sets it
+ as a cache's generator.
+
+ \note This function invalidates the cache temporarily, clearing all the cached data,
+ and sets it to a valid state with the new generator for the passed \a filePaths.
+ The stored generator provider is preserved.
+
+ \sa setGenerator
+*/
+void LocatorFileCache::setFilePaths(const FilePaths &filePaths)
+{
+ setFilePathsGenerator(filePathsGenerator(filePaths));
+ d->m_filePaths = filePaths;
+}
+
+/*!
+ Adapts the \a filePaths list into a LocatorFileCacheGenerator.
+ Useful when implementing GeneratorProvider in case a creation of file paths
+ can't be invoked from the non-main thread.
+*/
+LocatorFileCache::FilePathsGenerator LocatorFileCache::filePathsGenerator(
+ const FilePaths &filePaths)
+{
+ return [filePaths](const QFuture<void> &) { return filePaths; };
+}
+
+static ILocatorFilter::MatchLevel matchLevelFor(const QRegularExpressionMatch &match,
+ const QString &matchText)
+{
+ const int consecutivePos = match.capturedStart(1);
+ if (consecutivePos == 0)
+ return ILocatorFilter::MatchLevel::Best;
+ if (consecutivePos > 0) {
+ const QChar prevChar = matchText.at(consecutivePos - 1);
+ if (prevChar == '_' || prevChar == '.')
+ return ILocatorFilter::MatchLevel::Better;
+ }
+ if (match.capturedStart() == 0)
+ return ILocatorFilter::MatchLevel::Good;
+ return ILocatorFilter::MatchLevel::Normal;
+}
+
+/*!
+ Helper used internally and by SpotlightLocatorFilter.
+
+ To be called from non-main thread. The cancellation is controlled by the passed \a future.
+ This function periodically checks for the cancellation state of the \a future and returns
+ early when cancellation was detected.
+ Creates lists of matching LocatorFilterEntries categorized by MatcherType. These lists
+ are returned through the \a entries argument.
+
+ Returns a list of all matching files.
+
+ This function checks for each file in \a filePaths if it matches the passed \a regExp.
+ If so, a new entry is created using \a hasPathSeparator and \a inputLink and
+ it's being added into the \a entries argument and the results list.
+*/
+FilePaths LocatorFileCache::processFilePaths(const QFuture<void> &future,
+ const FilePaths &filePaths,
+ bool hasPathSeparator,
+ const QRegularExpression &regExp,
+ const Link &inputLink,
+ LocatorFileCache::MatchedEntries *entries)
+{
+ FilePaths cache;
+ for (const FilePath &path : filePaths) {
+ if (future.isCanceled())
+ return {};
+
+ const QString matchText = hasPathSeparator ? path.toString() : path.fileName();
+ const QRegularExpressionMatch match = regExp.match(matchText);
+
+ if (match.hasMatch()) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = path.fileName();
+ filterEntry.filePath = path;
+ filterEntry.extraInfo = path.shortNativePath();
+ filterEntry.linkForEditor = Link(path, inputLink.targetLine, inputLink.targetColumn);
+ filterEntry.highlightInfo = hasPathSeparator
+ ? ILocatorFilter::highlightInfo(regExp.match(filterEntry.extraInfo),
+ LocatorFilterEntry::HighlightInfo::ExtraInfo)
+ : ILocatorFilter::highlightInfo(match);
+ const ILocatorFilter::MatchLevel matchLevel = matchLevelFor(match, matchText);
+ (*entries)[int(matchLevel)].append(filterEntry);
+ cache << path;
+ }
+ }
+ return cache;
+}
+
+static void filter(QPromise<LocatorFileCachePrivate> &promise, const LocatorStorage &storage,
+ const LocatorFileCachePrivate &cache)
+{
+ QTC_ASSERT(cache.isValid(), return);
+ auto newCache = cache;
+ const LocatorFilterEntries output = newCache.generate(QFuture<void>(promise.future()),
+ storage.input());
+ if (promise.isCanceled())
+ return;
+ storage.reportOutput(output);
+ promise.addResult(newCache);
+}
+
+/*!
+ Returns the locator matcher task for the cache. The task, when successfully finished,
+ updates this LocatorFileCache instance if needed.
+
+ This method is to be used directly by the FilePaths filters. The FilePaths filter should
+ keep an instance of a LocatorFileCache internally. Ensure the LocatorFileCache instance
+ outlives the running matcher, otherwise the cache won't be updated after the task finished.
+
+ When returned LocatorMatcherTask is being run it checks if this cache is valid.
+ When the cache is invalid, it uses GeneratorProvider to update the
+ cache's FilePathsGenerator and validates the cache. If that failed, the task
+ is not started. When the cache is valid, the running task will reuse cached data for
+ calculating the LocatorMatcherTask's results.
+
+ After a successful run of the task, this cache is updated according to the last search.
+ When this cache started a new search in meantime, the cache was invalidated or even deleted,
+ the update of the cache after a successful run of the task is ignored.
+*/
+LocatorMatcherTask LocatorFileCache::matcher() const
+{
+ TreeStorage<LocatorStorage> storage;
+ std::weak_ptr<LocatorFileCachePrivate> weak = d;
+
+ const auto onSetup = [storage, weak](Async<LocatorFileCachePrivate> &async) {
+ auto that = weak.lock();
+ if (!that) // LocatorMatcher is running after *this LocatorFileCache was destructed.
+ return TaskAction::StopWithDone;
+
+ if (!that->ensureValidated())
+ return TaskAction::StopWithDone; // The cache is invalid and
+ // no provider is set or it returned empty generator
+ that->bumpExecutionId();
+
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(&filter, *storage, *that);
+ return TaskAction::Continue;
+ };
+ const auto onDone = [weak](const Async<LocatorFileCachePrivate> &async) {
+ auto that = weak.lock();
+ if (!that)
+ return; // LocatorMatcherTask finished after *this LocatorFileCache was destructed.
+
+ if (!that->isValid())
+ return; // The cache has been invalidated in meantime.
+
+ if (that->m_executionId == 0)
+ return; // The cache has been invalidated and not started.
+
+ if (!async.isResultAvailable())
+ return; // The async task didn't report updated cache.
+
+ that->update(async.result());
+ };
+
+ return {AsyncTask<LocatorFileCachePrivate>(onSetup, onDone), storage};
+}
+
} // Core
+
+#include "ilocatorfilter.moc"
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h
index e6b49ab5e9..b008c12bb5 100644
--- a/src/plugins/coreplugin/locator/ilocatorfilter.h
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.h
@@ -5,24 +5,44 @@
#include <coreplugin/core_global.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/filepath.h>
#include <utils/id.h>
#include <utils/link.h>
-#include <QFutureInterface>
#include <QIcon>
-#include <QMetaType>
-#include <QVariant>
#include <QKeySequence>
#include <optional>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QFuture;
+QT_END_NAMESPACE
+
namespace Core {
+namespace Internal {
+class Locator;
+class LocatorWidget;
+}
+
class ILocatorFilter;
+class LocatorStoragePrivate;
+class LocatorFileCachePrivate;
+
+class AcceptResult
+{
+public:
+ QString newText;
+ int selectionStart = -1;
+ int selectionLength = 0;
+};
-struct LocatorFilterEntry
+class LocatorFilterEntry
{
+public:
struct HighlightInfo {
enum DataType {
DisplayName,
@@ -66,18 +86,7 @@ struct LocatorFilterEntry
LocatorFilterEntry() = default;
- LocatorFilterEntry(ILocatorFilter *fromFilter,
- const QString &name,
- const QVariant &data = {},
- std::optional<QIcon> icon = std::nullopt)
- : filter(fromFilter)
- , displayName(name)
- , internalData(data)
- , displayIcon(icon)
- {}
-
- /* backpointer to creating filter */
- ILocatorFilter *filter = nullptr;
+ using Acceptor = std::function<AcceptResult()>;
/* displayed string */
QString displayName;
/* extra information displayed in parentheses and light-gray next to display name (optional)*/
@@ -86,8 +95,9 @@ struct LocatorFilterEntry
QString extraInfo;
/* additional tooltip */
QString toolTip;
- /* can be used by the filter to save more information about the entry */
- QVariant internalData;
+ /* called by locator widget on accept. By default, when acceptor is empty,
+ EditorManager::openEditor(LocatorFilterEntry) will be used instead. */
+ Acceptor acceptor;
/* icon to display along with the entry */
std::optional<QIcon> displayIcon;
/* file path, if the entry is related to a file, is used e.g. for resolving a file icon */
@@ -108,6 +118,76 @@ struct LocatorFilterEntry
}
};
+using LocatorFilterEntries = QList<LocatorFilterEntry>;
+
+class CORE_EXPORT LocatorStorage final
+{
+public:
+ LocatorStorage() = default;
+ QString input() const;
+ void reportOutput(const LocatorFilterEntries &outputData) const;
+
+private:
+ friend class LocatorMatcher;
+ LocatorStorage(const std::shared_ptr<LocatorStoragePrivate> &priv) { d = priv; }
+ void finalize() const;
+ std::shared_ptr<LocatorStoragePrivate> d;
+};
+
+class CORE_EXPORT LocatorMatcherTask final
+{
+public:
+ // The main task. Initial data (searchTerm) should be taken from storage.input().
+ // Results reporting is done via the storage.reportOutput().
+ Tasking::TaskItem task = Tasking::Group{};
+
+ // When constructing the task, don't place the storage inside the task above.
+ Tasking::TreeStorage<LocatorStorage> storage;
+};
+
+using LocatorMatcherTasks = QList<LocatorMatcherTask>;
+using LocatorMatcherTaskCreator = std::function<LocatorMatcherTasks()>;
+class LocatorMatcherPrivate;
+
+enum class MatcherType {
+ AllSymbols,
+ Classes,
+ Functions,
+ CurrentDocumentSymbols
+};
+
+class CORE_EXPORT LocatorMatcher final : public QObject
+{
+ Q_OBJECT
+
+public:
+ LocatorMatcher();
+ ~LocatorMatcher();
+ void setTasks(const LocatorMatcherTasks &tasks);
+ void setInputData(const QString &inputData);
+ void setParallelLimit(int limit); // by default 0 = parallel
+ void start();
+ void stop();
+
+ bool isRunning() const;
+ // Total data collected so far, even when running.
+ LocatorFilterEntries outputData() const;
+
+ // Note: Starts internal event loop.
+ static LocatorFilterEntries runBlocking(const LocatorMatcherTasks &tasks,
+ const QString &input, int parallelLimit = 0);
+
+ static void addMatcherCreator(MatcherType type, const LocatorMatcherTaskCreator &creator);
+ static LocatorMatcherTasks matchers(MatcherType type);
+
+signals:
+ void serialOutputDataReady(const LocatorFilterEntries &serialOutputData);
+ void done(bool success);
+
+private:
+ std::unique_ptr<LocatorMatcherPrivate> d;
+};
+
class CORE_EXPORT ILocatorFilter : public QObject
{
Q_OBJECT
@@ -126,8 +206,6 @@ public:
ILocatorFilter(QObject *parent = nullptr);
~ILocatorFilter() override;
- static const QList<ILocatorFilter *> allLocatorFilters();
-
Utils::Id id() const;
Utils::Id actionId() const;
@@ -149,15 +227,6 @@ public:
std::optional<QString> defaultSearchText() const;
void setDefaultSearchText(const QString &defaultSearchText);
- virtual void prepareSearch(const QString &entry);
-
- virtual QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry) = 0;
-
- virtual void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const = 0;
-
- virtual void refresh(QFutureInterface<void> &future) { Q_UNUSED(future) };
-
virtual QByteArray saveState() const;
virtual void restoreState(const QByteArray &state);
@@ -188,6 +257,9 @@ public:
public slots:
void setEnabled(bool enabled);
+signals:
+ void enabledChanged(bool enabled);
+
protected:
void setHidden(bool hidden);
void setId(Utils::Id id);
@@ -198,9 +270,18 @@ protected:
virtual void saveState(QJsonObject &object) const;
virtual void restoreState(const QJsonObject &object);
+ void setRefreshRecipe(const std::optional<Tasking::TaskItem> &recipe);
+ std::optional<Tasking::TaskItem> refreshRecipe() const;
+
static bool isOldSetting(const QByteArray &state);
private:
+ virtual LocatorMatcherTasks matchers() = 0;
+
+ friend class Internal::Locator;
+ friend class Internal::LocatorWidget;
+ static const QList<ILocatorFilter *> allLocatorFilters();
+
Utils::Id m_id;
QString m_shortcut;
Priority m_priority = Medium;
@@ -208,6 +289,7 @@ private:
QString m_description;
QString m_defaultShortcut;
std::optional<QString> m_defaultSearchText;
+ std::optional<Tasking::TaskItem> m_refreshRecipe;
QKeySequence m_defaultKeySequence;
bool m_defaultIncludedByDefault = false;
bool m_includedByDefault = m_defaultIncludedByDefault;
@@ -216,4 +298,37 @@ private:
bool m_isConfigurable = true;
};
+class CORE_EXPORT LocatorFileCache final
+{
+ Q_DISABLE_COPY_MOVE(LocatorFileCache)
+
+public:
+ // Always called from non-main thread.
+ using FilePathsGenerator = std::function<Utils::FilePaths(const QFuture<void> &)>;
+ // Always called from main thread.
+ using GeneratorProvider = std::function<FilePathsGenerator()>;
+
+ LocatorFileCache();
+
+ void invalidate();
+ void setFilePathsGenerator(const FilePathsGenerator &generator);
+ void setFilePaths(const Utils::FilePaths &filePaths);
+ void setGeneratorProvider(const GeneratorProvider &provider);
+
+ std::optional<Utils::FilePaths> filePaths() const;
+
+ static FilePathsGenerator filePathsGenerator(const Utils::FilePaths &filePaths);
+ LocatorMatcherTask matcher() const;
+
+ using MatchedEntries = std::array<LocatorFilterEntries, int(ILocatorFilter::MatchLevel::Count)>;
+ static Utils::FilePaths processFilePaths(const QFuture<void> &future,
+ const Utils::FilePaths &filePaths,
+ bool hasPathSeparator,
+ const QRegularExpression &regExp,
+ const Utils::Link &inputLink,
+ LocatorFileCache::MatchedEntries *entries);
+private:
+ std::shared_ptr<LocatorFileCachePrivate> d;
+};
+
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/javascriptfilter.cpp b/src/plugins/coreplugin/locator/javascriptfilter.cpp
index b97b054051..ea5a832c98 100644
--- a/src/plugins/coreplugin/locator/javascriptfilter.cpp
+++ b/src/plugins/coreplugin/locator/javascriptfilter.cpp
@@ -5,122 +5,434 @@
#include "../coreplugintr.h"
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
+
#include <QClipboard>
#include <QGuiApplication>
#include <QJSEngine>
+#include <QPair>
+#include <QPointer>
+#include <QScopeGuard>
-namespace Core {
-namespace Internal {
+#include <chrono>
-enum class EngineAction { Reset = 1, Abort };
+using namespace Core;
+using namespace Core::Internal;
+using namespace Tasking;
+using namespace Utils;
-JavaScriptFilter::JavaScriptFilter()
+using namespace std::chrono_literals;
+
+static const char s_initData[] = R"(
+ function abs(x) { return Math.abs(x); }
+ function acos(x) { return Math.acos(x); }
+ function asin(x) { return Math.asin(x); }
+ function atan(x) { return Math.atan(x); }
+ function atan2(x, y) { return Math.atan2(x, y); }
+ function bin(x) { return '0b' + x.toString(2); }
+ function ceil(x) { return Math.ceil(x); }
+ function cos(x) { return Math.cos(x); }
+ function exp(x) { return Math.exp(x); }
+ function e() { return Math.E; }
+ function floor(x) { return Math.floor(x); }
+ function hex(x) { return '0x' + x.toString(16); }
+ function log(x) { return Math.log(x); }
+ function max() { return Math.max.apply(null, arguments); }
+ function min() { return Math.min.apply(null, arguments); }
+ function oct(x) { return '0' + x.toString(8); }
+ function pi() { return Math.PI; }
+ function pow(x, y) { return Math.pow(x, y); }
+ function random() { return Math.random(); }
+ function round(x) { return Math.round(x); }
+ function sin(x) { return Math.sin(x); }
+ function sqrt(x) { return Math.sqrt(x); }
+ function tan(x) { return Math.tan(x); }
+)";
+
+enum class JavaScriptResult {
+ FinishedWithSuccess,
+ FinishedWithError,
+ TimedOut,
+ Canceled
+};
+
+class JavaScriptOutput
{
- setId("JavaScriptFilter");
- setDisplayName(Tr::tr("Evaluate JavaScript"));
- setDescription(Tr::tr("Evaluates arbitrary JavaScript expressions and copies the result."));
- setDefaultIncludedByDefault(false);
- setDefaultShortcutString("=");
- m_abortTimer.setSingleShot(true);
- m_abortTimer.setInterval(1000);
- connect(&m_abortTimer, &QTimer::timeout, this, [this] {
- m_aborted = true;
- if (m_engine)
- m_engine->setInterrupted(true);
- });
-}
+public:
+ QString m_output;
+ JavaScriptResult m_result = JavaScriptResult::Canceled;
+};
+
+using JavaScriptCallback = std::function<void(const JavaScriptOutput &)>;
-JavaScriptFilter::~JavaScriptFilter()
+class JavaScriptInput
{
-}
+public:
+ bool m_reset = false; // Recreates the QJSEngine, re-inits it and continues the request queue
+ QString m_input;
+ JavaScriptCallback m_callback = {};
+};
-void JavaScriptFilter::prepareSearch(const QString &entry)
+class JavaScriptThread : public QObject
{
- Q_UNUSED(entry)
+ Q_OBJECT
- if (!m_engine)
- setupEngine();
- m_engine->setInterrupted(false);
- m_aborted = false;
- m_abortTimer.start();
-}
+public:
+ // Called from the other thread, scheduled from the main thread through the queued
+ // invocation.
+ void run();
+
+ // Called from main thread exclusively
+ void cancel();
+ // Called from main thread exclusively
+ int addRequest(const JavaScriptInput &input);
+ // Called from main thread exclusively
+ void removeRequest(int id);
+
+ // Called from the main thread exclusively, scheduled from the other thread through the queued
+ // invocation when the new result is ready.
+ void flush();
+
+signals:
+ void newOutput();
+
+private:
+ struct QueueItem {
+ int m_id = 0;
+ JavaScriptInput m_input;
+ std::optional<JavaScriptOutput> m_output = {};
+ };
+
+ // Called from the main thread exclusively
+ QList<QueueItem> takeOutputQueue() {
+ QMutexLocker locker(&m_mutex);
+ return std::exchange(m_outputQueue, {});
+ }
+
+ int m_maxId = 0;
+ std::unique_ptr<QJSEngine> m_engine;
-QList<LocatorFilterEntry> JavaScriptFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+ mutable QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+ bool m_canceled = false;
+ QList<QueueItem> m_inputQueue;
+ std::optional<QueueItem> m_currentItem;
+ QList<QueueItem> m_outputQueue;
+};
+
+void JavaScriptThread::run()
{
- Q_UNUSED(future)
-
- QList<LocatorFilterEntry> entries;
- if (entry.trimmed().isEmpty()) {
- entries.append({this, Tr::tr("Reset Engine"), QVariant::fromValue(EngineAction::Reset)});
- } else {
- const QString result = m_engine->evaluate(entry).toString();
- if (m_aborted) {
- const QString message = entry + " = " + Tr::tr("Engine aborted after timeout.");
- entries.append({this, message, QVariant::fromValue(EngineAction::Abort)});
- } else {
- const QString expression = entry + " = " + result;
- entries.append({this, expression});
- entries.append({this, Tr::tr("Copy to clipboard: %1").arg(result), result});
- entries.append({this, Tr::tr("Copy to clipboard: %1").arg(expression), expression});
+ const auto evaluate = [this](const QString &input) {
+ const QJSValue result = m_engine->evaluate(input);
+ if (m_engine->isInterrupted()) {
+ return JavaScriptOutput{Tr::tr("The evaluation was interrupted."),
+ JavaScriptResult::Canceled};
}
+ return JavaScriptOutput{result.toString(),
+ result.isError() ? JavaScriptResult::FinishedWithError
+ : JavaScriptResult::FinishedWithSuccess};
+ };
+ const auto reset = [evaluate] {
+ JavaScriptOutput output = evaluate(s_initData);
+ output.m_output = output.m_result == JavaScriptResult::FinishedWithSuccess
+ ? Tr::tr("Engine reinitialized properly.")
+ : Tr::tr("Engine did not reinitialize properly.");
+ return output;
+ };
+
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_canceled)
+ return;
+ m_engine.reset(new QJSEngine);
}
- return entries;
+ // TODO: consider placing a reset request as the first input instead
+ const JavaScriptOutput output = reset();
+ QTC_ASSERT(output.m_result == JavaScriptResult::FinishedWithSuccess,
+ qWarning() << output.m_output);
+
+ QueueItem currentItem;
+ while (true) {
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_canceled)
+ return;
+ if (m_currentItem) {
+ QTC_CHECK(m_currentItem->m_id == currentItem.m_id);
+ m_outputQueue.append(currentItem);
+ m_currentItem = {};
+ emit newOutput();
+ }
+ while (m_inputQueue.isEmpty()) {
+ m_waitCondition.wait(&m_mutex);
+ if (m_canceled)
+ return;
+ }
+ m_currentItem = currentItem = m_inputQueue.takeFirst();
+ if (currentItem.m_input.m_reset)
+ m_engine.reset(new QJSEngine);
+ m_engine->setInterrupted(false);
+ }
+ const JavaScriptInput &input = currentItem.m_input;
+ if (input.m_reset) {
+ currentItem.m_output = reset();
+ QTC_ASSERT(currentItem.m_output->m_result == JavaScriptResult::FinishedWithSuccess,
+ qWarning() << currentItem.m_output->m_output);
+ continue;
+ }
+ currentItem.m_output = evaluate(input.m_input);
+ }
}
-void JavaScriptFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+void JavaScriptThread::cancel()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
+ QMutexLocker locker(&m_mutex);
+ m_canceled = true;
+ if (m_engine) // we may be canceling before the run() started
+ m_engine->setInterrupted(true);
+ m_waitCondition.wakeOne();
+}
- if (selection.internalData.isNull())
- return;
+int JavaScriptThread::addRequest(const JavaScriptInput &input)
+{
+ QMutexLocker locker(&m_mutex);
+ if (input.m_reset) {
+ if (m_currentItem) {
+ m_outputQueue += *m_currentItem;
+ m_engine->setInterrupted(true);
+ }
+ m_outputQueue += m_inputQueue;
+ m_currentItem = {};
+ m_inputQueue.clear();
+ for (int i = 0; i < m_outputQueue.size(); ++i)
+ m_outputQueue[i].m_output = {{}, JavaScriptResult::Canceled};
+ QMetaObject::invokeMethod(this, &JavaScriptThread::newOutput, Qt::QueuedConnection);
+ }
+ m_inputQueue.append({++m_maxId, input});
+ m_waitCondition.wakeOne();
+ return m_maxId;
+}
- const EngineAction action = selection.internalData.value<EngineAction>();
- if (action == EngineAction::Reset) {
- m_engine.reset();
+void JavaScriptThread::removeRequest(int id)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_currentItem && m_currentItem->m_id == id) {
+ m_currentItem = {};
+ m_engine->setInterrupted(true);
+ m_waitCondition.wakeOne();
return;
}
- if (action == EngineAction::Abort)
+ const auto predicate = [id](const QueueItem &item) { return item.m_id == id; };
+ if (Utils::eraseOne(m_inputQueue, predicate))
return;
+ Utils::eraseOne(m_outputQueue, predicate);
+}
+
+void JavaScriptThread::flush()
+{
+ const QList<QueueItem> outputQueue = takeOutputQueue();
+ for (const QueueItem &item : outputQueue) {
+ if (item.m_input.m_callback)
+ item.m_input.m_callback(*item.m_output);
+ }
+}
+
+class JavaScriptEngine : public QObject
+{
+ Q_OBJECT
- QClipboard *clipboard = QGuiApplication::clipboard();
- clipboard->setText(selection.internalData.toString());
+public:
+ JavaScriptEngine() : m_javaScriptThread(new JavaScriptThread) {
+ connect(m_javaScriptThread, &JavaScriptThread::newOutput, this, [this] {
+ m_javaScriptThread->flush();
+ });
+ m_javaScriptThread->moveToThread(&m_thread);
+ QObject::connect(&m_thread, &QThread::finished, m_javaScriptThread, &QObject::deleteLater);
+ m_thread.start();
+ QMetaObject::invokeMethod(m_javaScriptThread, &JavaScriptThread::run);
+ }
+ ~JavaScriptEngine() {
+ m_javaScriptThread->cancel();
+ m_thread.quit();
+ m_thread.wait();
+ }
+
+ int addRequest(const JavaScriptInput &input) { return m_javaScriptThread->addRequest(input); }
+ void removeRequest(int id) { m_javaScriptThread->removeRequest(id); }
+
+private:
+ QThread m_thread;
+ JavaScriptThread *m_javaScriptThread = nullptr;
+};
+
+class JavaScriptRequest : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~JavaScriptRequest()
+ {
+ if (m_engine && m_id) // In order to not to invoke a response callback anymore
+ m_engine->removeRequest(*m_id);
+ }
+
+ void setEngine(JavaScriptEngine *engine) {
+ QTC_ASSERT(!isRunning(), return);
+ m_engine = engine;
+ }
+ void setReset(bool reset) {
+ QTC_ASSERT(!isRunning(), return);
+ m_input.m_reset = reset; // Reply: "Engine has been reset"?
+ }
+ void setEvaluateData(const QString &input) {
+ QTC_ASSERT(!isRunning(), return);
+ m_input.m_input = input;
+ }
+ void setTimeout(std::chrono::milliseconds timeout) {
+ QTC_ASSERT(!isRunning(), return);
+ m_timeout = timeout;
+ }
+
+ void start() {
+ QTC_ASSERT(!isRunning(), return);
+ QTC_ASSERT(m_engine, return);
+
+ JavaScriptInput input = m_input;
+ input.m_callback = [this](const JavaScriptOutput &output) {
+ m_timer.reset();
+ m_output = output;
+ m_id = {};
+ emit done(output.m_result == JavaScriptResult::FinishedWithSuccess);
+ };
+ m_id = m_engine->addRequest(input);
+ if (m_timeout > 0ms) {
+ m_timer.reset(new QTimer);
+ m_timer->setSingleShot(true);
+ m_timer->setInterval(m_timeout);
+ connect(m_timer.get(), &QTimer::timeout, this, [this] {
+ if (m_engine && m_id)
+ m_engine->removeRequest(*m_id);
+ m_timer.release()->deleteLater();
+ m_id = {};
+ m_output = {Tr::tr("Engine aborted after timeout."), JavaScriptResult::Canceled};
+ emit done(false);
+ });
+ m_timer->start();
+ }
+ }
+
+ bool isRunning() const { return m_id.has_value(); }
+ JavaScriptOutput output() const { return m_output; }
+
+signals:
+ void done(bool success);
+
+private:
+ QPointer<JavaScriptEngine> m_engine;
+ JavaScriptInput m_input;
+ std::chrono::milliseconds m_timeout = 1000ms;
+
+ std::unique_ptr<QTimer> m_timer;
+
+ std::optional<int> m_id;
+ JavaScriptOutput m_output;
+};
+
+class JavaScriptRequestAdapter : public TaskAdapter<JavaScriptRequest>
+{
+public:
+ JavaScriptRequestAdapter() { connect(task(), &JavaScriptRequest::done,
+ this, &TaskInterface::done); }
+ void start() final { task()->start(); }
+};
+
+TASKING_DECLARE_TASK(JavaScriptRequestTask, JavaScriptRequestAdapter);
+
+namespace Core::Internal {
+
+JavaScriptFilter::JavaScriptFilter()
+{
+ setId("JavaScriptFilter");
+ setDisplayName(Tr::tr("Evaluate JavaScript"));
+ setDescription(Tr::tr("Evaluates arbitrary JavaScript expressions and copies the result."));
+ setDefaultShortcutString("=");
}
-void JavaScriptFilter::setupEngine()
+JavaScriptFilter::~JavaScriptFilter() = default;
+
+LocatorMatcherTasks JavaScriptFilter::matchers()
{
- m_engine.reset(new QJSEngine);
- m_engine->evaluate(
- "function abs(x) { return Math.abs(x); }\n"
- "function acos(x) { return Math.acos(x); }\n"
- "function asin(x) { return Math.asin(x); }\n"
- "function atan(x) { return Math.atan(x); }\n"
- "function atan2(x, y) { return Math.atan2(x, y); }\n"
- "function bin(x) { return '0b' + x.toString(2); }\n"
- "function ceil(x) { return Math.ceil(x); }\n"
- "function cos(x) { return Math.cos(x); }\n"
- "function exp(x) { return Math.exp(x); }\n"
- "function e() { return Math.E; }\n"
- "function floor(x) { return Math.floor(x); }\n"
- "function hex(x) { return '0x' + x.toString(16); }\n"
- "function log(x) { return Math.log(x); }\n"
- "function max() { return Math.max.apply(null, arguments); }\n"
- "function min() { return Math.min.apply(null, arguments); }\n"
- "function oct(x) { return '0' + x.toString(8); }\n"
- "function pi() { return Math.PI; }\n"
- "function pow(x, y) { return Math.pow(x, y); }\n"
- "function random() { return Math.random(); }\n"
- "function round(x) { return Math.round(x); }\n"
- "function sin(x) { return Math.sin(x); }\n"
- "function sqrt(x) { return Math.sqrt(x); }\n"
- "function tan(x) { return Math.tan(x); }\n");
+ TreeStorage<LocatorStorage> storage;
+ if (!m_javaScriptEngine)
+ m_javaScriptEngine.reset(new JavaScriptEngine);
+ QPointer<JavaScriptEngine> engine = m_javaScriptEngine.get();
+
+ const auto onGroupSetup = [storage, engine] {
+ if (!engine)
+ return TaskAction::StopWithError;
+ if (storage->input().trimmed().isEmpty()) {
+ LocatorFilterEntry entry;
+ entry.displayName = Tr::tr("Reset Engine");
+ entry.acceptor = [engine] {
+ if (engine) {
+ JavaScriptInput request;
+ request.m_reset = true;
+ engine->addRequest(request); // TODO: timeout not handled
+ }
+ return AcceptResult();
+ };
+ storage->reportOutput({entry});
+ return TaskAction::StopWithDone;
+ }
+ return TaskAction::Continue;
+ };
+
+ const auto onSetup = [storage, engine](JavaScriptRequest &request) {
+ request.setEngine(engine);
+ request.setEvaluateData(storage->input());
+ };
+ const auto onDone = [storage](const JavaScriptRequest &request) {
+ const auto acceptor = [](const QString &clipboardContents) {
+ return [clipboardContents] {
+ QGuiApplication::clipboard()->setText(clipboardContents);
+ return AcceptResult();
+ };
+ };
+ const QString input = storage->input();
+ const QString result = request.output().m_output;
+ const QString expression = input + " = " + result;
+
+ LocatorFilterEntry entry;
+ entry.displayName = expression;
+
+ LocatorFilterEntry copyResultEntry;
+ copyResultEntry.displayName = Tr::tr("Copy to clipboard: %1").arg(result);
+ copyResultEntry.acceptor = acceptor(result);
+
+ LocatorFilterEntry copyExpressionEntry;
+ copyExpressionEntry.displayName = Tr::tr("Copy to clipboard: %1").arg(expression);
+ copyExpressionEntry.acceptor = acceptor(expression);
+
+ storage->reportOutput({entry, copyResultEntry, copyExpressionEntry});
+ };
+ const auto onError = [storage](const JavaScriptRequest &request) {
+ LocatorFilterEntry entry;
+ entry.displayName = request.output().m_output;
+ storage->reportOutput({entry});
+ };
+
+ const Group root {
+ OnGroupSetup(onGroupSetup),
+ JavaScriptRequestTask(onSetup, onDone, onError)
+ };
+
+ return {{root, storage}};
}
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
-Q_DECLARE_METATYPE(Core::Internal::EngineAction)
+#include "javascriptfilter.moc"
diff --git a/src/plugins/coreplugin/locator/javascriptfilter.h b/src/plugins/coreplugin/locator/javascriptfilter.h
index 5153e0b7b5..8b7112fd27 100644
--- a/src/plugins/coreplugin/locator/javascriptfilter.h
+++ b/src/plugins/coreplugin/locator/javascriptfilter.h
@@ -5,38 +5,19 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QTimer>
+class JavaScriptEngine;
-#include <atomic>
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-class QJSEngine;
-QT_END_NAMESPACE
-
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class JavaScriptFilter : public Core::ILocatorFilter
{
- Q_OBJECT
public:
JavaScriptFilter();
- ~JavaScriptFilter() override;
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const override;
+ ~JavaScriptFilter();
private:
- void setupEngine();
-
- mutable std::unique_ptr<QJSEngine> m_engine;
- QTimer m_abortTimer;
- std::atomic_bool m_aborted = false;
+ LocatorMatcherTasks matchers() final;
+ std::unique_ptr<JavaScriptEngine> m_javaScriptEngine;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp
index afc060385b..30d996a08e 100644
--- a/src/plugins/coreplugin/locator/locator.cpp
+++ b/src/plugins/coreplugin/locator/locator.cpp
@@ -27,7 +27,7 @@
#include "../statusbarmanager.h"
#include <utils/algorithm.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -66,6 +66,7 @@ public:
LocatorData::LocatorData()
{
+ m_urlFilter.setDescription(Tr::tr("Triggers a web search with the selected search engine."));
m_urlFilter.setDefaultShortcutString("r");
m_urlFilter.addDefaultUrl("https://www.bing.com/search?q=%1");
m_urlFilter.addDefaultUrl("https://www.google.com/search?q=%1");
@@ -75,6 +76,7 @@ LocatorData::LocatorData()
"http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=%1");
m_urlFilter.addDefaultUrl("https://en.wikipedia.org/w/index.php?search=%1");
+ m_bugFilter.setDescription(Tr::tr("Triggers a search in the Qt bug tracker."));
m_bugFilter.setDefaultShortcutString("bug");
m_bugFilter.addDefaultUrl("https://bugreports.qt.io/secure/QuickSearch.jspa?searchString=%1");
}
@@ -143,13 +145,11 @@ bool Locator::delayedInitialize()
return true;
}
-ExtensionSystem::IPlugin::ShutdownFlag Locator::aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished)
+void Locator::aboutToShutdown()
{
m_shuttingDown = true;
m_refreshTimer.stop();
m_taskTree.reset();
- return LocatorWidget::aboutToShutdown(emitAsynchronousShutdownFinished);
}
void Locator::loadSettings()
@@ -382,14 +382,16 @@ void Locator::refresh(const QList<ILocatorFilter *> &filters)
using namespace Tasking;
QList<TaskItem> tasks{parallel};
for (ILocatorFilter *filter : std::as_const(m_refreshingFilters)) {
- const auto setupRefresh = [filter](AsyncTask<void> &async) {
- async.setAsyncCallData(&ILocatorFilter::refresh, filter);
- };
- const auto onRefreshDone = [this, filter](const AsyncTask<void> &async) {
- Q_UNUSED(async)
- m_refreshingFilters.removeOne(filter);
+ const auto task = filter->refreshRecipe();
+ if (!task.has_value())
+ continue;
+
+ const Group group {
+ optional,
+ *task,
+ OnGroupDone([this, filter] { m_refreshingFilters.removeOne(filter); })
};
- tasks.append(Async<void>(setupRefresh, onRefreshDone));
+ tasks.append(group);
}
m_taskTree.reset(new TaskTree{tasks});
diff --git a/src/plugins/coreplugin/locator/locator.h b/src/plugins/coreplugin/locator/locator.h
index 59da8767d8..3e73054153 100644
--- a/src/plugins/coreplugin/locator/locator.h
+++ b/src/plugins/coreplugin/locator/locator.h
@@ -13,7 +13,7 @@
#include <functional>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace Core {
namespace Internal {
@@ -30,8 +30,7 @@ public:
~Locator() override;
static Locator *instance();
- ExtensionSystem::IPlugin::ShutdownFlag aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished);
+ void aboutToShutdown();
void initialize();
void extensionsInitialized();
@@ -75,7 +74,7 @@ private:
QList<ILocatorFilter *> m_customFilters;
QMap<Utils::Id, QAction *> m_filterActionMap;
QTimer m_refreshTimer;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
QList<ILocatorFilter *> m_refreshingFilters;
};
diff --git a/src/plugins/coreplugin/locator/locator_test.cpp b/src/plugins/coreplugin/locator/locator_test.cpp
index eca70f2eaa..c83a2d8779 100644
--- a/src/plugins/coreplugin/locator/locator_test.cpp
+++ b/src/plugins/coreplugin/locator/locator_test.cpp
@@ -3,35 +3,22 @@
#include "../coreplugin.h"
-#include "basefilefilter.h"
#include "locatorfiltertest.h"
#include <coreplugin/testdatadir.h>
#include <utils/algorithm.h>
-#include <utils/filepath.h>
#include <utils/fileutils.h>
#include <QDir>
-#include <QTextStream>
#include <QtTest>
using namespace Core::Tests;
+using namespace Utils;
namespace {
QTC_DECLARE_MYTESTDATADIR("../../../tests/locators/")
-class MyBaseFileFilter : public Core::BaseFileFilter
-{
-public:
- MyBaseFileFilter(const Utils::FilePaths &theFiles)
- {
- setFileIterator(new BaseFileFilter::ListIterator(theFiles));
- }
-
- void refresh(QFutureInterface<void> &) override {}
-};
-
class ReferenceData
{
public:
@@ -53,14 +40,13 @@ void Core::Internal::CorePlugin::test_basefilefilter()
QFETCH(QStringList, testFiles);
QFETCH(QList<ReferenceData>, referenceDataList);
- MyBaseFileFilter filter(Utils::FileUtils::toFilePathList(testFiles));
- BasicLocatorFilterTest test(&filter);
-
+ LocatorFileCache cache;
+ cache.setFilePaths(FileUtils::toFilePathList(testFiles));
+ const LocatorMatcherTasks tasks = {cache.matcher()};
for (const ReferenceData &reference : std::as_const(referenceDataList)) {
- const QList<LocatorFilterEntry> filterEntries = test.matchesFor(reference.searchText);
+ const LocatorFilterEntries filterEntries = LocatorMatcher::runBlocking(
+ tasks, reference.searchText);
const ResultDataList results = ResultData::fromFilterEntryList(filterEntries);
-// QTextStream(stdout) << "----" << endl;
-// ResultData::printFilterEntries(results);
QCOMPARE(results, reference.results);
}
}
@@ -68,7 +54,7 @@ void Core::Internal::CorePlugin::test_basefilefilter()
void Core::Internal::CorePlugin::test_basefilefilter_data()
{
auto shortNativePath = [](const QString &file) {
- return Utils::FilePath::fromString(file).shortNativePath();
+ return FilePath::fromString(file).shortNativePath();
};
QTest::addColumn<QStringList>("testFiles");
diff --git a/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp b/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
index 800c9171db..782cb3f557 100644
--- a/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
+++ b/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
@@ -7,10 +7,9 @@
#include "../actionmanager/actionmanager.h"
#include "../coreplugintr.h"
-#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-Q_DECLARE_METATYPE(Core::ILocatorFilter*)
+using namespace Utils;
namespace Core::Internal {
@@ -25,65 +24,45 @@ LocatorFiltersFilter::LocatorFiltersFilter():
setConfigurable(false);
}
-void LocatorFiltersFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks LocatorFiltersFilter::matchers()
{
- m_filterShortcutStrings.clear();
- m_filterDisplayNames.clear();
- m_filterDescriptions.clear();
- if (!entry.isEmpty())
- return;
+ using namespace Tasking;
- QMap<QString, ILocatorFilter *> uniqueFilters;
- const QList<ILocatorFilter *> allFilters = Locator::filters();
- for (ILocatorFilter *filter : allFilters) {
- const QString filterId = filter->shortcutString() + ',' + filter->displayName();
- uniqueFilters.insert(filterId, filter);
- }
+ TreeStorage<LocatorStorage> storage;
- for (ILocatorFilter *filter : std::as_const(uniqueFilters)) {
- if (!filter->shortcutString().isEmpty() && !filter->isHidden() && filter->isEnabled()) {
- m_filterShortcutStrings.append(filter->shortcutString());
- m_filterDisplayNames.append(filter->displayName());
- m_filterDescriptions.append(filter->description());
- QString keyboardShortcut;
- if (auto command = ActionManager::command(filter->actionId()))
- keyboardShortcut = command->keySequence().toString(QKeySequence::NativeText);
- m_filterKeyboardShortcuts.append(keyboardShortcut);
- }
- }
-}
+ const auto onSetup = [storage, icon = m_icon] {
+ if (!storage->input().isEmpty())
+ return;
-QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
-{
- Q_UNUSED(entry) // search is already done in the GUI thread in prepareSearch
- QList<LocatorFilterEntry> entries;
- for (int i = 0; i < m_filterShortcutStrings.size(); ++i) {
- if (future.isCanceled())
- break;
- LocatorFilterEntry filterEntry(this,
- m_filterShortcutStrings.at(i),
- i,
- m_icon);
- filterEntry.extraInfo = m_filterDisplayNames.at(i);
- filterEntry.toolTip = m_filterDescriptions.at(i);
- filterEntry.displayExtra = m_filterKeyboardShortcuts.at(i);
- entries.append(filterEntry);
- }
- return entries;
-}
+ QMap<QString, ILocatorFilter *> uniqueFilters;
+ const QList<ILocatorFilter *> allFilters = Locator::filters();
+ for (ILocatorFilter *filter : allFilters) {
+ const QString filterId = filter->shortcutString() + ',' + filter->displayName();
+ uniqueFilters.insert(filterId, filter);
+ }
-void LocatorFiltersFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(selectionLength)
- bool ok;
- int index = selection.internalData.toInt(&ok);
- QTC_ASSERT(ok && index >= 0 && index < m_filterShortcutStrings.size(), return);
- const QString shortcutString = m_filterShortcutStrings.at(index);
- if (!shortcutString.isEmpty()) {
- *newText = shortcutString + ' ';
- *selectionStart = shortcutString.length() + 1;
- }
+ LocatorFilterEntries entries;
+ for (ILocatorFilter *filter : std::as_const(uniqueFilters)) {
+ const QString shortcutString = filter->shortcutString();
+ if (!shortcutString.isEmpty() && !filter->isHidden() && filter->isEnabled()) {
+ LocatorFilterEntry entry;
+ entry.displayName = shortcutString;
+ entry.acceptor = [shortcutString] {
+ return AcceptResult{shortcutString + ' ', int(shortcutString.size() + 1)};
+ };
+ entry.displayIcon = icon;
+ entry.extraInfo = filter->displayName();
+ entry.toolTip = filter->description();
+ QString keyboardShortcut;
+ if (auto command = ActionManager::command(filter->actionId()))
+ keyboardShortcut = command->keySequence().toString(QKeySequence::NativeText);
+ entry.displayExtra = keyboardShortcut;
+ entries.append(entry);
+ }
+ }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
} // Core::Internal
diff --git a/src/plugins/coreplugin/locator/locatorfiltersfilter.h b/src/plugins/coreplugin/locator/locatorfiltersfilter.h
index f4679ec9c1..f6dea092eb 100644
--- a/src/plugins/coreplugin/locator/locatorfiltersfilter.h
+++ b/src/plugins/coreplugin/locator/locatorfiltersfilter.h
@@ -5,12 +5,7 @@
#include "ilocatorfilter.h"
-#include <QIcon>
-
-namespace Core {
-namespace Internal {
-
-class Locator;
+namespace Core::Internal {
/*!
This filter provides the user with the list of available Locator filters.
@@ -18,19 +13,12 @@ class Locator;
*/
class LocatorFiltersFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
LocatorFiltersFilter();
- // ILocatorFilter
- void prepareSearch(const QString &entry) override;
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
-
private:
+ LocatorMatcherTasks matchers() final;
+
QStringList m_filterShortcutStrings;
QStringList m_filterDisplayNames;
QStringList m_filterDescriptions;
@@ -38,5 +26,4 @@ private:
QIcon m_icon;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/locatorfiltertest.cpp b/src/plugins/coreplugin/locator/locatorfiltertest.cpp
index c134db1669..152ac11733 100644
--- a/src/plugins/coreplugin/locator/locatorfiltertest.cpp
+++ b/src/plugins/coreplugin/locator/locatorfiltertest.cpp
@@ -3,53 +3,12 @@
#include "locatorfiltertest.h"
-#include "locatorsearchutils.h"
-
-#include <utils/runextensions.h>
-
-#include <QFuture>
-#include <QList>
-#include <QString>
#include <QTextStream>
using namespace Core;
using namespace Core::Tests;
/*!
- \class Core::Tests::BasicLocatorFilterTest
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \class Core::Tests::TestDataDir
- \inmodule QtCreator
- \internal
-*/
-
-/*!
- \namespace Core::Tests
- \inmodule QtCreator
- \internal
-*/
-BasicLocatorFilterTest::BasicLocatorFilterTest(ILocatorFilter *filter) : m_filter(filter)
-{
-}
-
-BasicLocatorFilterTest::~BasicLocatorFilterTest() = default;
-
-QList<LocatorFilterEntry> BasicLocatorFilterTest::matchesFor(const QString &searchText)
-{
- doBeforeLocatorRun();
- m_filter->prepareSearch(searchText);
- QFuture<LocatorFilterEntry> locatorSearch = Utils::runAsync(
- &Internal::runSearch, QList<ILocatorFilter *>({m_filter}), searchText);
- locatorSearch.waitForFinished();
- doAfterLocatorRun();
- return locatorSearch.results();
-}
-
-/*!
\class Core::Tests::ResultData
\inmodule QtCreator
\internal
@@ -71,7 +30,7 @@ bool ResultData::operator==(const ResultData &other) const
return textColumn1 == other.textColumn1 && textColumn2 == other.textColumn2 && highlightEqual;
}
-ResultData::ResultDataList ResultData::fromFilterEntryList(const QList<LocatorFilterEntry> &entries)
+ResultData::ResultDataList ResultData::fromFilterEntryList(const LocatorFilterEntries &entries)
{
ResultDataList result;
for (const LocatorFilterEntry &entry : entries) {
diff --git a/src/plugins/coreplugin/locator/locatorfiltertest.h b/src/plugins/coreplugin/locator/locatorfiltertest.h
index f6d5d59501..2ad18ac236 100644
--- a/src/plugins/coreplugin/locator/locatorfiltertest.h
+++ b/src/plugins/coreplugin/locator/locatorfiltertest.h
@@ -10,22 +10,6 @@
namespace Core {
namespace Tests {
-/// Runs a locator filter for a search text and returns the results.
-class CORE_EXPORT BasicLocatorFilterTest
-{
-public:
- BasicLocatorFilterTest(ILocatorFilter *filter);
- virtual ~BasicLocatorFilterTest();
-
- QList<LocatorFilterEntry> matchesFor(const QString &searchText = QString());
-
-private:
- virtual void doBeforeLocatorRun() {}
- virtual void doAfterLocatorRun() {}
-
- ILocatorFilter *m_filter = nullptr;
-};
-
class CORE_EXPORT ResultData
{
public:
@@ -37,7 +21,7 @@ public:
bool operator==(const ResultData &other) const;
- static ResultDataList fromFilterEntryList(const QList<LocatorFilterEntry> &entries);
+ static ResultDataList fromFilterEntryList(const LocatorFilterEntries &entries);
/// For debugging and creating reference data
static void printFilterEntries(const ResultDataList &entries, const QString &msg = QString());
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.cpp b/src/plugins/coreplugin/locator/locatorsearchutils.cpp
deleted file mode 100644
index 77e460ad4f..0000000000
--- a/src/plugins/coreplugin/locator/locatorsearchutils.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "locatorsearchutils.h"
-
-#include <utils/link.h>
-
-#include <unordered_set>
-
-void Core::Internal::runSearch(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QList<ILocatorFilter *> &filters, const QString &searchText)
-{
- std::unordered_set<Utils::FilePath> addedCache;
- const bool checkDuplicates = (filters.size() > 1);
- const auto duplicatesRemoved = [&](const QList<LocatorFilterEntry> &entries) {
- if (!checkDuplicates)
- return entries;
- QList<LocatorFilterEntry> results;
- results.reserve(entries.size());
- for (const LocatorFilterEntry &entry : entries) {
- const auto &link = entry.linkForEditor;
- if (!link || addedCache.emplace(link->targetFilePath).second)
- results.append(entry);
- }
- return results;
- };
-
- for (ILocatorFilter *filter : filters) {
- if (future.isCanceled())
- break;
- const auto results = duplicatesRemoved(filter->matchesFor(future, searchText));
- if (!results.isEmpty())
- future.reportResults(results);
- }
-}
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.h b/src/plugins/coreplugin/locator/locatorsearchutils.h
deleted file mode 100644
index d863b580a6..0000000000
--- a/src/plugins/coreplugin/locator/locatorsearchutils.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "ilocatorfilter.h"
-
-namespace Core {
-namespace Internal {
-
-void CORE_EXPORT runSearch(QFutureInterface<LocatorFilterEntry> &future,
- const QList<ILocatorFilter *> &filters,
- const QString &searchText);
-
-} // namespace Internal
-} // namespace Core
diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.cpp b/src/plugins/coreplugin/locator/locatorsettingspage.cpp
index 0193a791c3..74c3b45692 100644
--- a/src/plugins/coreplugin/locator/locatorsettingspage.cpp
+++ b/src/plugins/coreplugin/locator/locatorsettingspage.cpp
@@ -20,12 +20,15 @@
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
+#include <QAbstractTextDocumentLayout>
#include <QHash>
#include <QHeaderView>
#include <QLabel>
#include <QMenu>
+#include <QPainter>
#include <QPushButton>
#include <QSpinBox>
+#include <QStyledItemDelegate>
using namespace Utils;
@@ -77,8 +80,14 @@ QVariant FilterItem::data(int column, int role) const
{
switch (column) {
case FilterName:
- if (role == Qt::DisplayRole || role == SortRole)
+ if (role == SortRole)
return m_filter->displayName();
+ if (role == Qt::DisplayRole) {
+ if (m_filter->description().isEmpty())
+ return m_filter->displayName();
+ return QString("<html>%1<br/><span style=\"font-weight: 70\">%2</span>")
+ .arg(m_filter->displayName(), m_filter->description().toHtmlEscaped());
+ }
break;
case FilterPrefix:
if (role == Qt::DisplayRole || role == SortRole || role == Qt::EditRole)
@@ -92,8 +101,10 @@ QVariant FilterItem::data(int column, int role) const
break;
}
- if (role == Qt::ToolTipRole)
- return m_filter->description();
+ if (role == Qt::ToolTipRole) {
+ const QString description = m_filter->description();
+ return description.isEmpty() ? QString() : ("<html>" + description.toHtmlEscaped());
+ }
return QVariant();
}
@@ -146,6 +157,97 @@ QVariant CategoryItem::data(int column, int role) const
return QVariant();
}
+class RichTextDelegate : public QStyledItemDelegate
+{
+public:
+ RichTextDelegate(QObject *parent);
+ ~RichTextDelegate();
+
+ QTextDocument &doc() { return m_doc; }
+
+ void setMaxWidth(int width);
+ int maxWidth() const;
+
+private:
+ void paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+
+ int m_maxWidth = -1;
+ mutable QTextDocument m_doc;
+};
+
+RichTextDelegate::RichTextDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{}
+
+void RichTextDelegate::setMaxWidth(int width)
+{
+ m_maxWidth = width;
+ emit sizeHintChanged({});
+}
+
+int RichTextDelegate::maxWidth() const
+{
+ return m_maxWidth;
+}
+
+RichTextDelegate::~RichTextDelegate() = default;
+
+void RichTextDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
+
+ painter->save();
+ QTextOption textOption;
+ if (m_maxWidth > 0) {
+ textOption.setWrapMode(QTextOption::WordWrap);
+ m_doc.setDefaultTextOption(textOption);
+ if (options.rect.width() > m_maxWidth)
+ options.rect.setWidth(m_maxWidth);
+ }
+ m_doc.setHtml(options.text);
+ m_doc.setTextWidth(options.rect.width());
+ options.text = "";
+ options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);
+ painter->translate(options.rect.left(), options.rect.top());
+ QRect clip(0, 0, options.rect.width(), options.rect.height());
+ QAbstractTextDocumentLayout::PaintContext paintContext;
+ paintContext.palette = options.palette;
+ painter->setClipRect(clip);
+ paintContext.clip = clip;
+ if (qobject_cast<const QAbstractItemView *>(options.widget)->selectionModel()->isSelected(index)) {
+ QAbstractTextDocumentLayout::Selection selection;
+ selection.cursor = QTextCursor(&m_doc);
+ selection.cursor.select(QTextCursor::Document);
+ selection.format.setBackground(options.palette.brush(QPalette::Highlight));
+ selection.format.setForeground(options.palette.brush(QPalette::HighlightedText));
+ paintContext.selections << selection;
+ }
+ m_doc.documentLayout()->draw(painter, paintContext);
+ painter->restore();
+}
+
+QSize RichTextDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
+ QTextOption textOption;
+ if (m_maxWidth > 0) {
+ textOption.setWrapMode(QTextOption::WordWrap);
+ m_doc.setDefaultTextOption(textOption);
+ if (!options.rect.isValid() || options.rect.width() > m_maxWidth)
+ options.rect.setWidth(m_maxWidth);
+ }
+ m_doc.setHtml(options.text);
+ m_doc.setTextWidth(options.rect.width());
+ return QSize(m_doc.idealWidth(), m_doc.size().height());
+}
+
class LocatorSettingsWidget : public IOptionsPageWidget
{
public:
@@ -178,8 +280,17 @@ public:
m_filterList->setSelectionMode(QAbstractItemView::SingleSelection);
m_filterList->setSelectionBehavior(QAbstractItemView::SelectRows);
m_filterList->setSortingEnabled(true);
- m_filterList->setUniformRowHeights(true);
m_filterList->setActivationMode(Utils::DoubleClickActivation);
+ m_filterList->setAlternatingRowColors(true);
+ auto nameDelegate = new RichTextDelegate(m_filterList);
+ connect(m_filterList->header(),
+ &QHeaderView::sectionResized,
+ nameDelegate,
+ [nameDelegate](int col, [[maybe_unused]] int old, int updated) {
+ if (col == 0)
+ nameDelegate->setMaxWidth(updated);
+ });
+ m_filterList->setItemDelegateForColumn(0, nameDelegate);
m_model = new TreeModel<>(m_filterList);
initializeModel();
@@ -230,12 +341,13 @@ public:
auto addMenu = new QMenu(addButton);
addMenu->addAction(Tr::tr("Files in Directories"), this, [this] {
- addCustomFilter(new DirectoryFilter(Id(Constants::CUSTOM_DIRECTORY_FILTER_BASEID)
+ addCustomFilter(new DirectoryFilter(Utils::Id(Constants::CUSTOM_DIRECTORY_FILTER_BASEID)
.withSuffix(m_customFilters.size() + 1)));
});
addMenu->addAction(Tr::tr("URL Template"), this, [this] {
auto filter = new UrlLocatorFilter(
- Id(Constants::CUSTOM_URL_FILTER_BASEID).withSuffix(m_customFilters.size() + 1));
+ Utils::Id(Constants::CUSTOM_URL_FILTER_BASEID)
+ .withSuffix(m_customFilters.size() + 1));
filter->setIsCustomFilter(true);
addCustomFilter(filter);
});
diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.h b/src/plugins/coreplugin/locator/locatorsettingspage.h
index 4818fc2e23..408fe7b91f 100644
--- a/src/plugins/coreplugin/locator/locatorsettingspage.h
+++ b/src/plugins/coreplugin/locator/locatorsettingspage.h
@@ -5,16 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class LocatorSettingsPage : public IOptionsPage
{
- Q_OBJECT
-
public:
LocatorSettingsPage();
};
-} // namespace Internal
-} // namespace Core
+} // Core::Internal
diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp
index ba717ea0f9..8af3f4b753 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.cpp
+++ b/src/plugins/coreplugin/locator/locatorwidget.cpp
@@ -4,17 +4,15 @@
#include "locatorwidget.h"
#include "ilocatorfilter.h"
-#include "locator.h"
#include "locatorconstants.h"
#include "locatormanager.h"
-#include "locatorsearchutils.h"
#include "../actionmanager/actionmanager.h"
#include "../coreplugintr.h"
+#include "../editormanager/editormanager.h"
#include "../icore.h"
#include "../modemanager.h"
#include <utils/algorithm.h>
-#include <utils/appmainwindow.h>
#include <utils/fancylineedit.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/highlightingitemdelegate.h>
@@ -22,24 +20,20 @@
#include <utils/itemviews.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <utils/stylehelper.h>
+#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QApplication>
#include <QColor>
#include <QEvent>
-#include <QFileInfo>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QKeyEvent>
+#include <QMainWindow>
#include <QMenu>
-#include <QScreen>
#include <QScrollBar>
-#include <QTimer>
#include <QToolTip>
-#include <QTreeView>
Q_DECLARE_METATYPE(Core::LocatorFilterEntry)
@@ -50,10 +44,6 @@ const int LocatorEntryRole = int(HighlightingItemRole::User);
namespace Core {
namespace Internal {
-bool LocatorWidget::m_shuttingDown = false;
-QFuture<void> LocatorWidget::m_sharedFuture;
-LocatorWidget *LocatorWidget::m_sharedFutureOrigin = nullptr;
-
/* A model to represent the Locator results. */
class LocatorModel : public QAbstractListModel
{
@@ -67,8 +57,8 @@ public:
LocatorModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
- , mBackgroundColor(Utils::creatorTheme()->color(Utils::Theme::TextColorHighlightBackground))
- , mForegroundColor(Utils::creatorTheme()->color(Utils::Theme::TextColorNormal))
+ , m_backgroundColor(Utils::creatorTheme()->color(Theme::TextColorHighlightBackground))
+ , m_foregroundColor(Utils::creatorTheme()->color(Theme::TextColorNormal))
{}
void clear();
@@ -76,13 +66,13 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- void addEntries(const QList<LocatorFilterEntry> &entries);
+ void addEntries(const LocatorFilterEntries &entries);
private:
- mutable QList<LocatorFilterEntry> mEntries;
- bool hasExtraInfo = false;
- QColor mBackgroundColor;
- QColor mForegroundColor;
+ mutable LocatorFilterEntries m_entries;
+ bool m_hasExtraInfo = false;
+ QColor m_backgroundColor;
+ QColor m_foregroundColor;
};
class CompletionDelegate : public HighlightingItemDelegate
@@ -93,7 +83,7 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
-class CompletionList : public Utils::TreeView
+class CompletionList : public TreeView
{
public:
CompletionList(QWidget *parent = nullptr);
@@ -144,8 +134,8 @@ protected:
void LocatorModel::clear()
{
beginResetModel();
- mEntries.clear();
- hasExtraInfo = false;
+ m_entries.clear();
+ m_hasExtraInfo = false;
endResetModel();
}
@@ -153,30 +143,30 @@ int LocatorModel::rowCount(const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
- return mEntries.size();
+ return m_entries.size();
}
int LocatorModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
- return hasExtraInfo ? ColumnCount : 1;
+ return m_hasExtraInfo ? ColumnCount : 1;
}
QVariant LocatorModel::data(const QModelIndex &index, int role) const
{
- if (!index.isValid() || index.row() >= mEntries.size())
+ if (!index.isValid() || index.row() >= m_entries.size())
return QVariant();
switch (role) {
case Qt::DisplayRole:
if (index.column() == DisplayNameColumn)
- return mEntries.at(index.row()).displayName;
+ return m_entries.at(index.row()).displayName;
else if (index.column() == ExtraInfoColumn)
- return mEntries.at(index.row()).extraInfo;
+ return m_entries.at(index.row()).extraInfo;
break;
case Qt::ToolTipRole: {
- const LocatorFilterEntry &entry = mEntries.at(index.row());
+ const LocatorFilterEntry &entry = m_entries.at(index.row());
QString toolTip = entry.displayName;
if (!entry.extraInfo.isEmpty())
toolTip += "\n\n" + entry.extraInfo;
@@ -186,7 +176,7 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
}
case Qt::DecorationRole:
if (index.column() == DisplayNameColumn) {
- LocatorFilterEntry &entry = mEntries[index.row()];
+ LocatorFilterEntry &entry = m_entries[index.row()];
if (!entry.displayIcon && !entry.filePath.isEmpty())
entry.displayIcon = FileIconProvider::icon(entry.filePath);
return entry.displayIcon ? entry.displayIcon.value() : QIcon();
@@ -197,10 +187,10 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
return QColor(Qt::darkGray);
break;
case LocatorEntryRole:
- return QVariant::fromValue(mEntries.at(index.row()));
+ return QVariant::fromValue(m_entries.at(index.row()));
case int(HighlightingItemRole::StartColumn):
case int(HighlightingItemRole::Length): {
- const LocatorFilterEntry &entry = mEntries[index.row()];
+ const LocatorFilterEntry &entry = m_entries[index.row()];
auto highlights = [&](LocatorFilterEntry::HighlightInfo::DataType type){
const bool startIndexRole = role == int(HighlightingItemRole::StartColumn);
return startIndexRole ? QVariant::fromValue(entry.highlightInfo.starts(type))
@@ -214,7 +204,7 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
}
case int(HighlightingItemRole::DisplayExtra): {
if (index.column() == LocatorFilterEntry::HighlightInfo::DisplayName) {
- LocatorFilterEntry &entry = mEntries[index.row()];
+ LocatorFilterEntry &entry = m_entries[index.row()];
if (!entry.displayExtra.isEmpty())
return QString(" (" + entry.displayExtra + ')');
}
@@ -223,9 +213,9 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
case int(HighlightingItemRole::DisplayExtraForeground):
return QColor(Qt::darkGray);
case int(HighlightingItemRole::Background):
- return mBackgroundColor;
+ return m_backgroundColor;
case int(HighlightingItemRole::Foreground):
- return mForegroundColor;
+ return m_foregroundColor;
}
return QVariant();
@@ -233,14 +223,14 @@ QVariant LocatorModel::data(const QModelIndex &index, int role) const
void LocatorModel::addEntries(const QList<LocatorFilterEntry> &entries)
{
- beginInsertRows(QModelIndex(), mEntries.size(), mEntries.size() + entries.size() - 1);
- mEntries.append(entries);
+ beginInsertRows(QModelIndex(), m_entries.size(), m_entries.size() + entries.size() - 1);
+ m_entries.append(entries);
endInsertRows();
- if (hasExtraInfo)
+ if (m_hasExtraInfo)
return;
if (Utils::anyOf(entries, [](const LocatorFilterEntry &e) { return !e.extraInfo.isEmpty();})) {
beginInsertColumns(QModelIndex(), 1, 1);
- hasExtraInfo = true;
+ m_hasExtraInfo = true;
endInsertColumns();
}
}
@@ -248,7 +238,7 @@ void LocatorModel::addEntries(const QList<LocatorFilterEntry> &entries)
// =========== CompletionList ===========
CompletionList::CompletionList(QWidget *parent)
- : Utils::TreeView(parent)
+ : TreeView(parent)
{
// on macOS and Windows the popup doesn't really get focus, so fake the selection color
// which would then just be a very light gray, but should look as if it had focus
@@ -265,7 +255,7 @@ CompletionList::CompletionList(QWidget *parent)
header()->setStretchLastSection(true);
// This is too slow when done on all results
//header()->setSectionResizeMode(QHeaderView::ResizeToContents);
- if (Utils::HostOsInfo::isMacHost()) {
+ if (HostOsInfo::isMacHost()) {
if (horizontalScrollBar())
horizontalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
if (verticalScrollBar())
@@ -416,7 +406,7 @@ LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
m_tree(new CompletionList(this)),
m_inputWidget(locatorWidget)
{
- if (Utils::HostOsInfo::isMacHost())
+ if (HostOsInfo::isMacHost())
m_tree->setFrameStyle(QFrame::NoFrame); // tool tip already includes a frame
m_tree->setModel(locatorWidget->model());
m_tree->setTextElideMode(Qt::ElideMiddle);
@@ -444,9 +434,10 @@ LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
}, Qt::DirectConnection); // must be handled directly before event is deleted
connect(m_tree, &QAbstractItemView::activated, locatorWidget,
[this, locatorWidget](const QModelIndex &index) {
- if (isVisible())
- locatorWidget->scheduleAcceptEntry(index);
- });
+ if (!index.isValid() || !isVisible())
+ return;
+ locatorWidget->acceptEntry(index.row());
+ });
}
CompletionList *LocatorPopup::completionList() const
@@ -459,29 +450,28 @@ LocatorWidget *LocatorPopup::inputWidget() const
return m_inputWidget;
}
-void LocatorPopup::focusOutEvent(QFocusEvent *event) {
+void LocatorPopup::focusOutEvent(QFocusEvent *event)
+{
if (event->reason() == Qt::ActiveWindowFocusReason)
hide();
QWidget::focusOutEvent(event);
}
-void CompletionList::next() {
+void CompletionList::next()
+{
int index = currentIndex().row();
++index;
- if (index >= model()->rowCount(QModelIndex())) {
- // wrap
- index = 0;
- }
+ if (index >= model()->rowCount(QModelIndex()))
+ index = 0; // wrap
setCurrentIndex(model()->index(index, 0));
}
-void CompletionList::previous() {
+void CompletionList::previous()
+{
int index = currentIndex().row();
--index;
- if (index < 0) {
- // wrap
- index = model()->rowCount(QModelIndex()) - 1;
- }
+ if (index < 0)
+ index = model()->rowCount(QModelIndex()) - 1; // wrap
setCurrentIndex(model()->index(index, 0));
}
@@ -510,7 +500,7 @@ void CompletionList::keyPressEvent(QKeyEvent *event)
return;
case Qt::Key_P:
case Qt::Key_N:
- if (event->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (event->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
if (event->key() == Qt::Key_P)
previous();
else
@@ -529,7 +519,7 @@ void CompletionList::keyPressEvent(QKeyEvent *event)
}
break;
}
- Utils::TreeView::keyPressEvent(event);
+ TreeView::keyPressEvent(event);
}
bool CompletionList::eventFilter(QObject *watched, QEvent *event)
@@ -545,14 +535,14 @@ bool CompletionList::eventFilter(QObject *watched, QEvent *event)
break;
case Qt::Key_P:
case Qt::Key_N:
- if (ke->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (ke->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
event->accept();
return true;
}
break;
}
}
- return Utils::TreeView::eventFilter(watched, event);
+ return TreeView::eventFilter(watched, event);
}
// =========== LocatorWidget ===========
@@ -563,7 +553,7 @@ LocatorWidget::LocatorWidget(Locator *locator)
, m_centeredPopupAction(new QAction(Tr::tr("Open as Centered Popup"), this))
, m_refreshAction(new QAction(Tr::tr("Refresh"), this))
, m_configureAction(new QAction(ICore::msgShowOptionsDialog(), this))
- , m_fileLineEdit(new Utils::FancyLineEdit)
+ , m_fileLineEdit(new FancyLineEdit)
{
setAttribute(Qt::WA_Hover);
setFocusProxy(m_fileLineEdit);
@@ -581,12 +571,12 @@ LocatorWidget::LocatorWidget(Locator *locator)
const QIcon icon = Utils::Icons::MAGNIFIER.icon();
m_fileLineEdit->setFiltering(true);
- m_fileLineEdit->setButtonIcon(Utils::FancyLineEdit::Left, icon);
- m_fileLineEdit->setButtonToolTip(Utils::FancyLineEdit::Left, Tr::tr("Options"));
+ m_fileLineEdit->setButtonIcon(FancyLineEdit::Left, icon);
+ m_fileLineEdit->setButtonToolTip(FancyLineEdit::Left, Tr::tr("Options"));
m_fileLineEdit->setFocusPolicy(Qt::ClickFocus);
- m_fileLineEdit->setButtonVisible(Utils::FancyLineEdit::Left, true);
+ m_fileLineEdit->setButtonVisible(FancyLineEdit::Left, true);
// We set click focus since otherwise you will always get two popups
- m_fileLineEdit->setButtonFocusPolicy(Utils::FancyLineEdit::Left, Qt::ClickFocus);
+ m_fileLineEdit->setButtonFocusPolicy(FancyLineEdit::Left, Qt::ClickFocus);
m_fileLineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
m_fileLineEdit->installEventFilter(this);
@@ -597,6 +587,10 @@ LocatorWidget::LocatorWidget(Locator *locator)
connect(m_filterMenu, &QMenu::aboutToShow, this, [this] {
m_centeredPopupAction->setChecked(Locator::useCenteredPopupForShortcut());
});
+ connect(m_filterMenu, &QMenu::hovered, this, [this](QAction *action) {
+ ToolTip::show(m_filterMenu->mapToGlobal(m_filterMenu->actionGeometry(action).topRight()),
+ action->toolTip());
+ });
connect(m_centeredPopupAction, &QAction::toggled, locator, [locator](bool toggled) {
if (toggled != Locator::useCenteredPopupForShortcut()) {
Locator::setUseCenteredPopupForShortcut(toggled);
@@ -608,27 +602,15 @@ LocatorWidget::LocatorWidget(Locator *locator)
m_filterMenu->addAction(m_refreshAction);
m_filterMenu->addAction(m_configureAction);
- m_fileLineEdit->setButtonMenu(Utils::FancyLineEdit::Left, m_filterMenu);
+ m_fileLineEdit->setButtonMenu(FancyLineEdit::Left, m_filterMenu);
connect(m_refreshAction, &QAction::triggered, locator, [locator] {
locator->refresh(Locator::filters());
});
connect(m_configureAction, &QAction::triggered, this, &LocatorWidget::showConfigureDialog);
- connect(m_fileLineEdit, &QLineEdit::textChanged,
- this, &LocatorWidget::showPopupDelayed);
-
- m_entriesWatcher = new QFutureWatcher<LocatorFilterEntry>(this);
- connect(m_entriesWatcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt,
- this, &LocatorWidget::addSearchResults);
- connect(m_entriesWatcher, &QFutureWatcher<LocatorFilterEntry>::finished,
- this, &LocatorWidget::handleSearchFinished);
-
- m_showPopupTimer.setInterval(100);
- m_showPopupTimer.setSingleShot(true);
- connect(&m_showPopupTimer, &QTimer::timeout, this, &LocatorWidget::showPopupNow);
+ connect(m_fileLineEdit, &QLineEdit::textChanged, this, &LocatorWidget::showPopupNow);
- m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small,
- m_fileLineEdit);
+ m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Small, m_fileLineEdit);
m_progressIndicator->raise();
m_progressIndicator->hide();
m_showProgressTimer.setSingleShot(true);
@@ -638,7 +620,7 @@ LocatorWidget::LocatorWidget(Locator *locator)
Command *locateCmd = ActionManager::command(Constants::LOCATE);
if (QTC_GUARD(locateCmd)) {
- connect(locateCmd, &Command::keySequenceChanged, this, [this,locateCmd] {
+ connect(locateCmd, &Command::keySequenceChanged, this, [this, locateCmd] {
updatePlaceholderText(locateCmd);
});
updatePlaceholderText(locateCmd);
@@ -650,12 +632,7 @@ LocatorWidget::LocatorWidget(Locator *locator)
updateFilterList();
}
-LocatorWidget::~LocatorWidget()
-{
- // no need to completely finish a running search, cancel it
- if (m_entriesWatcher->future().isRunning())
- m_entriesWatcher->future().cancel();
-}
+LocatorWidget::~LocatorWidget() = default;
void LocatorWidget::updatePlaceholderText(Command *command)
{
@@ -670,12 +647,19 @@ void LocatorWidget::updatePlaceholderText(Command *command)
void LocatorWidget::updateFilterList()
{
m_filterMenu->clear();
- const QList<ILocatorFilter *> filters = Locator::filters();
+ const QList<ILocatorFilter *> filters = Utils::sorted(
+ Locator::filters(), [](ILocatorFilter *a, ILocatorFilter *b) {
+ return a->displayName() < b->displayName();
+ });
for (ILocatorFilter *filter : filters) {
if (filter->shortcutString().isEmpty() || filter->isHidden())
continue;
QAction *action = m_filterMenu->addAction(filter->displayName());
- action->setToolTip(filter->description());
+ action->setEnabled(filter->isEnabled());
+ const QString description = filter->description();
+ action->setToolTip(description.isEmpty() ? QString()
+ : ("<html>" + description.toHtmlEscaped()));
+ connect(filter, &ILocatorFilter::enabledChanged, action, &QAction::setEnabled);
connect(action, &QAction::triggered, this, [this, filter] {
Locator::showFilter(filter, this);
});
@@ -715,7 +699,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
switch (keyEvent->key()) {
case Qt::Key_P:
case Qt::Key_N:
- if (keyEvent->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (keyEvent->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
event->accept();
return true;
}
@@ -772,7 +756,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
return true;
case Qt::Key_Home:
case Qt::Key_End:
- if (Utils::HostOsInfo::isMacHost()
+ if (HostOsInfo::isMacHost()
!= (keyEvent->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier))) {
emit showPopup();
emit handleKey(keyEvent);
@@ -794,7 +778,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
break;
case Qt::Key_P:
case Qt::Key_N:
- if (keyEvent->modifiers() == Qt::KeyboardModifiers(Utils::HostOsInfo::controlModifier())) {
+ if (keyEvent->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
emit showPopup();
emit handleKey(keyEvent);
return true;
@@ -847,16 +831,9 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
return QWidget::eventFilter(obj, event);
}
-void LocatorWidget::showPopupDelayed()
-{
- m_updateRequested = true;
- m_showPopupTimer.start();
-}
-
void LocatorWidget::showPopupNow()
{
- m_showPopupTimer.stop();
- updateCompletionList(m_fileLineEdit->text());
+ runMatcher(m_fileLineEdit->text());
emit showPopup();
}
@@ -894,7 +871,7 @@ void LocatorWidget::setProgressIndicatorVisible(bool visible)
return;
}
const QSize iconSize = m_progressIndicator->sizeHint();
- m_progressIndicator->setGeometry(m_fileLineEdit->button(Utils::FancyLineEdit::Right)->geometry().x()
+ m_progressIndicator->setGeometry(m_fileLineEdit->button(FancyLineEdit::Right)->geometry().x()
- iconSize.width(),
(m_fileLineEdit->height() - iconSize.height()) / 2 /*center*/,
iconSize.width(),
@@ -902,118 +879,78 @@ void LocatorWidget::setProgressIndicatorVisible(bool visible)
m_progressIndicator->show();
}
-void LocatorWidget::updateCompletionList(const QString &text)
+void LocatorWidget::runMatcher(const QString &text)
{
- if (m_shuttingDown)
- return;
-
- m_updateRequested = true;
- if (m_sharedFuture.isRunning()) {
- // Cancel the old future. We may not just block the UI thread to wait for the search to
- // actually cancel.
- m_requestedCompletionText = text;
- if (m_sharedFutureOrigin == this) {
- // This locator widget is currently running. Make handleSearchFinished trigger another
- // update.
- m_rerunAfterFinished = true;
- } else {
- // Another locator widget is running. Trigger another update when that is finished.
- Utils::onFinished(m_sharedFuture, this, [this](const QFuture<void> &) {
- const QString text = m_requestedCompletionText;
- m_requestedCompletionText.clear();
- updateCompletionList(text);
- });
- }
- m_sharedFuture.cancel();
- return;
- }
-
- m_showProgressTimer.start();
- m_needsClearResult = true;
QString searchText;
const QList<ILocatorFilter *> filters = filtersFor(text, searchText);
+ LocatorMatcherTasks tasks;
for (ILocatorFilter *filter : filters)
- filter->prepareSearch(searchText);
- QFuture<LocatorFilterEntry> future = Utils::runAsync(&runSearch, filters, searchText);
- m_sharedFuture = QFuture<void>(future);
- m_sharedFutureOrigin = this;
- m_entriesWatcher->setFuture(future);
-}
-
-void LocatorWidget::handleSearchFinished()
-{
- m_showProgressTimer.stop();
- setProgressIndicatorVisible(false);
- m_updateRequested = false;
- if (m_rowRequestedForAccept) {
- acceptEntry(m_rowRequestedForAccept.value());
- m_rowRequestedForAccept.reset();
- return;
- }
- if (m_rerunAfterFinished) {
- m_rerunAfterFinished = false;
- const QString text = m_requestedCompletionText;
- m_requestedCompletionText.clear();
- updateCompletionList(text);
- return;
- }
-
- if (m_needsClearResult) {
- m_locatorModel->clear();
- m_needsClearResult = false;
- }
-}
-
-void LocatorWidget::scheduleAcceptEntry(const QModelIndex &index)
-{
- if (m_updateRequested) {
- // don't just accept the selected entry, since the list is not up to date
- // accept will be called after the update finished
- m_rowRequestedForAccept = index.row();
- // do not wait for the rest of the search to finish
- m_entriesWatcher->future().cancel();
- } else {
- acceptEntry(index.row());
- }
-}
+ tasks += filter->matchers();
+
+ m_locatorMatcher.reset(new LocatorMatcher);
+ m_locatorMatcher->setTasks(tasks);
+ m_locatorMatcher->setInputData(searchText);
+ m_rowRequestedForAccept.reset();
+
+ std::shared_ptr<std::atomic_bool> needsClearResult = std::make_shared<std::atomic_bool>(true);
+ connect(m_locatorMatcher.get(), &LocatorMatcher::done, this, [this, needsClearResult] {
+ m_showProgressTimer.stop();
+ setProgressIndicatorVisible(false);
+ m_locatorMatcher.release()->deleteLater();
+ if (m_rowRequestedForAccept) {
+ acceptEntry(m_rowRequestedForAccept.value());
+ m_rowRequestedForAccept.reset();
+ return;
+ }
+ if (needsClearResult->exchange(false))
+ m_locatorModel->clear();
+ });
+ connect(m_locatorMatcher.get(), &LocatorMatcher::serialOutputDataReady,
+ this, [this, needsClearResult](const LocatorFilterEntries &serialOutputData) {
+ if (needsClearResult->exchange(false))
+ m_locatorModel->clear();
+ const bool selectFirst = m_locatorModel->rowCount() == 0;
+ m_locatorModel->addEntries(serialOutputData);
+ if (selectFirst) {
+ emit selectRow(0);
+ if (m_rowRequestedForAccept)
+ m_rowRequestedForAccept = 0;
+ }
+ });
-ExtensionSystem::IPlugin::ShutdownFlag LocatorWidget::aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished)
-{
- m_shuttingDown = true;
- if (m_sharedFuture.isRunning()) {
- Utils::onFinished(m_sharedFuture,
- Locator::instance(),
- [emitAsynchronousShutdownFinished](const QFuture<void> &) {
- emitAsynchronousShutdownFinished();
- });
- m_sharedFuture.cancel();
- return ExtensionSystem::IPlugin::AsynchronousShutdown;
- }
- return ExtensionSystem::IPlugin::SynchronousShutdown;
+ m_showProgressTimer.start();
+ m_locatorMatcher->start();
}
void LocatorWidget::acceptEntry(int row)
{
+ if (m_locatorMatcher) {
+ m_rowRequestedForAccept = row;
+ return;
+ }
if (row < 0 || row >= m_locatorModel->rowCount())
return;
const QModelIndex index = m_locatorModel->index(row, 0);
if (!index.isValid())
return;
- const LocatorFilterEntry entry = m_locatorModel->data(index, LocatorEntryRole).value<LocatorFilterEntry>();
- Q_ASSERT(entry.filter != nullptr);
- QString newText;
- int selectionStart = -1;
- int selectionLength = 0;
+ const LocatorFilterEntry entry
+ = m_locatorModel->data(index, LocatorEntryRole).value<LocatorFilterEntry>();
+
+ if (!entry.acceptor) {
+ // Opening editors can open dialogs (e.g. the ssh prompt, or showing erros), so delay until
+ // we have hidden the popup with emit hidePopup below and Qt actually processed that
+ QMetaObject::invokeMethod(EditorManager::instance(),
+ [entry] { EditorManager::openEditor(entry); }, Qt::QueuedConnection);
+ }
QWidget *focusBeforeAccept = QApplication::focusWidget();
- entry.filter->accept(entry, &newText, &selectionStart, &selectionLength);
- if (newText.isEmpty()) {
+ const AcceptResult result = entry.acceptor ? entry.acceptor() : AcceptResult();
+ if (result.newText.isEmpty()) {
emit hidePopup();
if (QApplication::focusWidget() == focusBeforeAccept)
resetFocus(m_previousFocusWidget, isInMainWindow());
} else {
- showText(newText, selectionStart, selectionLength);
+ showText(result.newText, result.selectionStart, result.selectionLength);
}
}
@@ -1049,24 +986,6 @@ void LocatorWidget::showConfigureDialog()
ICore::showOptionsDialog(Constants::FILTER_OPTIONS_PAGE);
}
-void LocatorWidget::addSearchResults(int firstIndex, int endIndex)
-{
- if (m_needsClearResult) {
- m_locatorModel->clear();
- m_needsClearResult = false;
- }
- const bool selectFirst = m_locatorModel->rowCount() == 0;
- QList<LocatorFilterEntry> entries;
- for (int i = firstIndex; i < endIndex; ++i)
- entries.append(m_entriesWatcher->resultAt(i));
- m_locatorModel->addEntries(entries);
- if (selectFirst) {
- emit selectRow(0);
- if (m_rowRequestedForAccept)
- m_rowRequestedForAccept = 0;
- }
-}
-
LocatorWidget *createStaticLocatorWidget(Locator *locator)
{
auto widget = new LocatorWidget(locator);
diff --git a/src/plugins/coreplugin/locator/locatorwidget.h b/src/plugins/coreplugin/locator/locatorwidget.h
index eef5a14414..c766637c3d 100644
--- a/src/plugins/coreplugin/locator/locatorwidget.h
+++ b/src/plugins/coreplugin/locator/locatorwidget.h
@@ -7,8 +7,8 @@
#include <extensionsystem/iplugin.h>
-#include <QFutureWatcher>
#include <QPointer>
+#include <QTimer>
#include <QWidget>
#include <functional>
@@ -28,8 +28,7 @@ namespace Internal {
class LocatorModel;
class CompletionList;
-class LocatorWidget
- : public QWidget
+class LocatorWidget : public QWidget
{
Q_OBJECT
@@ -42,11 +41,7 @@ public:
QAbstractItemModel *model() const;
void updatePlaceholderText(Command *command);
-
- void scheduleAcceptEntry(const QModelIndex &index);
-
- static ExtensionSystem::IPlugin::ShutdownFlag aboutToShutdown(
- const std::function<void()> &emitAsynchronousShutdownFinished);
+ void acceptEntry(int row);
signals:
void showCurrentItemToolTip();
@@ -58,44 +53,30 @@ signals:
void showPopup();
private:
- void showPopupDelayed();
void showPopupNow();
- void acceptEntry(int row);
static void showConfigureDialog();
- void addSearchResults(int firstIndex, int endIndex);
- void handleSearchFinished();
void updateFilterList();
bool isInMainWindow() const;
void updatePreviousFocusWidget(QWidget *previous, QWidget *current);
bool eventFilter(QObject *obj, QEvent *event) override;
- void updateCompletionList(const QString &text);
+ void runMatcher(const QString &text);
static QList<ILocatorFilter*> filtersFor(const QString &text, QString &searchText);
void setProgressIndicatorVisible(bool visible);
LocatorModel *m_locatorModel = nullptr;
-
- static bool m_shuttingDown;
- static QFuture<void> m_sharedFuture;
- static LocatorWidget *m_sharedFutureOrigin;
-
QMenu *m_filterMenu = nullptr;
QAction *m_centeredPopupAction = nullptr;
QAction *m_refreshAction = nullptr;
QAction *m_configureAction = nullptr;
Utils::FancyLineEdit *m_fileLineEdit = nullptr;
- QTimer m_showPopupTimer;
- QFutureWatcher<LocatorFilterEntry> *m_entriesWatcher = nullptr;
- QString m_requestedCompletionText;
- bool m_needsClearResult = true;
- bool m_updateRequested = false;
- bool m_rerunAfterFinished = false;
bool m_possibleToolTipRequest = false;
QWidget *m_progressIndicator = nullptr;
QTimer m_showProgressTimer;
std::optional<int> m_rowRequestedForAccept;
QPointer<QWidget> m_previousFocusWidget;
+ std::unique_ptr<LocatorMatcher> m_locatorMatcher;
};
class LocatorPopup : public QWidget
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
index f8febc69f3..95e076ec2b 100644
--- a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
@@ -3,102 +3,63 @@
#include "opendocumentsfilter.h"
-#include "basefilefilter.h"
#include "../coreplugintr.h"
-#include <utils/filepath.h>
+#include <coreplugin/editormanager/documentmodel.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/link.h>
-#include <utils/linecolumn.h>
-#include <QAbstractItemModel>
-#include <QMutexLocker>
#include <QRegularExpression>
+using namespace Tasking;
using namespace Utils;
namespace Core::Internal {
+class Entry
+{
+public:
+ Utils::FilePath fileName;
+ QString displayName;
+};
+
OpenDocumentsFilter::OpenDocumentsFilter()
{
setId("Open documents");
setDisplayName(Tr::tr("Open Documents"));
+ setDescription(Tr::tr("Switches to an open document."));
setDefaultShortcutString("o");
setPriority(High);
setDefaultIncludedByDefault(true);
-
- connect(DocumentModel::model(), &QAbstractItemModel::dataChanged,
- this, &OpenDocumentsFilter::slotDataChanged);
- connect(DocumentModel::model(), &QAbstractItemModel::rowsInserted,
- this, &OpenDocumentsFilter::slotRowsInserted);
- connect(DocumentModel::model(), &QAbstractItemModel::rowsRemoved,
- this, &OpenDocumentsFilter::slotRowsRemoved);
-}
-
-void OpenDocumentsFilter::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
- const QVector<int> &roles)
-{
- Q_UNUSED(roles)
-
- const int topIndex = std::max(0, topLeft.row() - 1 /*<no document>*/);
- const int bottomIndex = bottomRight.row() - 1 /*<no document>*/;
-
- QMutexLocker lock(&m_mutex);
-
- const QList<DocumentModel::Entry *> documentEntries = DocumentModel::entries();
- for (int i = topIndex; i <= bottomIndex; ++i) {
- QTC_ASSERT(i < m_editors.size(), break);
- DocumentModel::Entry *e = documentEntries.at(i);
- m_editors[i] = {e->filePath(), e->displayName()};
- }
-}
-
-void OpenDocumentsFilter::slotRowsInserted(const QModelIndex &, int first, int last)
-{
- const int firstIndex = std::max(0, first - 1 /*<no document>*/);
-
- QMutexLocker lock(&m_mutex);
-
- const QList<DocumentModel::Entry *> documentEntries = DocumentModel::entries();
- for (int i = firstIndex; i < last; ++i) {
- DocumentModel::Entry *e = documentEntries.at(i);
- m_editors.insert(i, {e->filePath(), e->displayName()});
- }
}
-void OpenDocumentsFilter::slotRowsRemoved(const QModelIndex &, int first, int last)
+static void matchEditors(QPromise<void> &promise, const LocatorStorage &storage,
+ const QList<Entry> &editorsData)
{
- QMutexLocker lock(&m_mutex);
-
- const int firstIndex = std::max(0, first - 1 /*<no document>*/);
- for (int i = firstIndex; i < last; ++i)
- m_editors.removeAt(i);
-}
+ const Link link = Link::fromString(storage.input(), true);
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(link.targetFilePath.toString());
+ if (!regexp.isValid())
+ return;
-QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry)
-{
- QList<LocatorFilterEntry> goodEntries;
- QList<LocatorFilterEntry> betterEntries;
- const Link link = Link::fromString(entry, true);
+ LocatorFilterEntries goodEntries;
+ LocatorFilterEntries betterEntries;
- const QRegularExpression regexp = createRegExp(link.targetFilePath.toString());
- if (!regexp.isValid())
- return goodEntries;
-
- const QList<Entry> editorEntries = editors();
- for (const Entry &editorEntry : editorEntries) {
- if (future.isCanceled())
- break;
- QString fileName = editorEntry.fileName.toString();
- if (fileName.isEmpty())
+ for (const Entry &editorData : editorsData) {
+ if (promise.isCanceled())
+ return;
+ if (editorData.fileName.isEmpty())
continue;
- QString displayName = editorEntry.displayName;
- const QRegularExpressionMatch match = regexp.match(displayName);
+ const QRegularExpressionMatch match = regexp.match(editorData.displayName);
if (match.hasMatch()) {
- LocatorFilterEntry filterEntry(this, displayName);
- filterEntry.filePath = FilePath::fromString(fileName);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = editorData.displayName;
+ filterEntry.filePath = editorData.fileName;
filterEntry.extraInfo = filterEntry.filePath.shortNativePath();
- filterEntry.highlightInfo = highlightInfo(match);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
filterEntry.linkForEditor = Link(filterEntry.filePath, link.targetLine,
link.targetColumn);
if (match.capturedStart() == 0)
@@ -107,23 +68,21 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Locat
goodEntries.append(filterEntry);
}
}
- betterEntries.append(goodEntries);
- return betterEntries;
+ storage.reportOutput(betterEntries + goodEntries);
}
-QList<OpenDocumentsFilter::Entry> OpenDocumentsFilter::editors() const
+LocatorMatcherTasks OpenDocumentsFilter::matchers()
{
- QMutexLocker lock(&m_mutex);
- return m_editors;
-}
+ TreeStorage<LocatorStorage> storage;
-void OpenDocumentsFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- BaseFileFilter::openEditorAt(selection);
+ const auto onSetup = [storage](Async<void> &async) {
+ const QList<Entry> editorsData = Utils::transform(DocumentModel::entries(),
+ [](const DocumentModel::Entry *e) { return Entry{e->filePath(), e->displayName()}; });
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matchEditors, *storage, editorsData);
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
-} // Core::Internal
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.h b/src/plugins/coreplugin/locator/opendocumentsfilter.h
index 537bdd45a5..94306d0515 100644
--- a/src/plugins/coreplugin/locator/opendocumentsfilter.h
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.h
@@ -5,46 +5,15 @@
#include "ilocatorfilter.h"
-#include <coreplugin/editormanager/documentmodel.h>
-
-#include <QFutureInterface>
-#include <QList>
-#include <QMutex>
-#include <QString>
-
-namespace Core {
-namespace Internal {
+namespace Core::Internal {
class OpenDocumentsFilter : public ILocatorFilter
{
- Q_OBJECT
-
public:
OpenDocumentsFilter();
- QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
-
-public slots:
- void slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
- const QVector<int> &roles);
- void slotRowsInserted(const QModelIndex &, int first, int last);
- void slotRowsRemoved(const QModelIndex &, int first, int last);
private:
- class Entry
- {
- public:
- Utils::FilePath fileName;
- QString displayName;
- };
-
- QList<Entry> editors() const;
-
- mutable QMutex m_mutex;
- QList<Entry> m_editors;
+ LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
index cc62ec91c0..64ea71998b 100644
--- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp
@@ -6,149 +6,28 @@
#include "../coreplugintr.h"
#include "../messagemanager.h"
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/fancylineedit.h>
#include <utils/link.h>
#include <utils/macroexpander.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/variablechooser.h>
#include <QFormLayout>
-#include <QJsonDocument>
#include <QJsonObject>
-#include <QMutex>
-#include <QMutexLocker>
#include <QRegularExpression>
-#include <QWaitCondition>
using namespace Utils;
-namespace Core {
-namespace Internal {
-
-// #pragma mark -- SpotlightIterator
-
-class SpotlightIterator : public BaseFileFilter::Iterator
-{
-public:
- SpotlightIterator(const CommandLine &command);
- ~SpotlightIterator() override;
-
- void toFront() override;
- bool hasNext() const override;
- Utils::FilePath next() override;
- Utils::FilePath filePath() const override;
-
- void scheduleKillProcess();
- void killProcess();
-
-private:
- void ensureNext();
-
- std::unique_ptr<QtcProcess> m_process;
- QMutex m_mutex;
- QWaitCondition m_waitForItems;
- FilePaths m_queue;
- FilePaths m_filePaths;
- int m_index;
- bool m_finished;
-};
-
-SpotlightIterator::SpotlightIterator(const CommandLine &command)
- : m_index(-1)
- , m_finished(false)
-{
- QTC_ASSERT(!command.isEmpty(), return );
- m_process.reset(new QtcProcess);
- m_process->setCommand(command);
- m_process->setEnvironment(Utils::Environment::systemEnvironment());
- QObject::connect(m_process.get(), &QtcProcess::done,
- m_process.get(), [this, exe = command.executable().toUserOutput()] {
- if (m_process->result() != ProcessResult::FinishedWithSuccess) {
- MessageManager::writeFlashing(Tr::tr(
- "Locator: Error occurred when running \"%1\".").arg(exe));
- }
- scheduleKillProcess();
- });
- QObject::connect(m_process.get(), &QtcProcess::readyReadStandardOutput,
- m_process.get(), [this] {
- QString output = m_process->readAllStandardOutput();
- output.replace("\r\n", "\n");
- const QStringList items = output.split('\n');
- QMutexLocker lock(&m_mutex);
- m_queue.append(Utils::transform(items, &FilePath::fromUserInput));
- if (m_filePaths.size() + m_queue.size() > 10000) // limit the amount of data
- scheduleKillProcess();
- m_waitForItems.wakeAll();
- });
- m_process->start();
-}
-
-SpotlightIterator::~SpotlightIterator()
-{
- killProcess();
-}
-
-void SpotlightIterator::toFront()
-{
- m_index = -1;
-}
-
-bool SpotlightIterator::hasNext() const
-{
- auto that = const_cast<SpotlightIterator *>(this);
- that->ensureNext();
- return (m_index + 1 < m_filePaths.size());
-}
-
-Utils::FilePath SpotlightIterator::next()
-{
- ensureNext();
- ++m_index;
- QTC_ASSERT(m_index < m_filePaths.size(), return FilePath());
- return m_filePaths.at(m_index);
-}
-
-Utils::FilePath SpotlightIterator::filePath() const
-{
- QTC_ASSERT(m_index < m_filePaths.size(), return FilePath());
- return m_filePaths.at(m_index);
-}
-
-void SpotlightIterator::scheduleKillProcess()
-{
- QMetaObject::invokeMethod(m_process.get(), [this] { killProcess(); }, Qt::QueuedConnection);
-}
-
-void SpotlightIterator::killProcess()
-{
- if (!m_process)
- return;
- m_process->disconnect();
- QMutexLocker lock(&m_mutex);
- m_finished = true;
- m_waitForItems.wakeAll();
- m_process.reset();
-}
-
-void SpotlightIterator::ensureNext()
-{
- if (m_index + 1 < m_filePaths.size()) // nothing to do
- return;
- // check if there are items in the queue, otherwise wait for some
- QMutexLocker lock(&m_mutex);
- if (m_queue.isEmpty() && !m_finished)
- m_waitForItems.wait(&m_mutex);
- m_filePaths.append(m_queue);
- m_queue.clear();
-}
-
-// #pragma mark -- SpotlightLocatorFilter
+namespace Core::Internal {
static QString defaultCommand()
{
@@ -223,33 +102,97 @@ SpotlightLocatorFilter::SpotlightLocatorFilter()
{
setId("SpotlightFileNamesLocatorFilter");
setDefaultShortcutString("md");
- setDefaultIncludedByDefault(false);
setDisplayName(Tr::tr("File Name Index"));
- setDescription(
- Tr::tr("Matches files from a global file system index (Spotlight, Locate, Everything). Append "
- "\"+<number>\" or \":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
- setConfigurable(true);
- reset();
+ setDescription(Tr::tr(
+ "Locates files from a global file system index (Spotlight, Locate, Everything). Append "
+ "\"+<number>\" or \":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ m_command = defaultCommand();
+ m_arguments = defaultArguments();
+ m_caseSensitiveArguments = defaultArguments(Qt::CaseSensitive);
}
-void SpotlightLocatorFilter::prepareSearch(const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const CommandLine &command)
{
- Link link = Utils::Link::fromString(entry, true);
- if (link.targetFilePath.isEmpty()) {
- setFileIterator(new BaseFileFilter::ListIterator(Utils::FilePaths()));
- } else {
- // only pass the file name part to allow searches like "somepath/*foo"
+ // If search string contains spaces, treat them as wildcard '*' and search in full path
+ const QString wildcardInput = QDir::fromNativeSeparators(storage.input()).replace(' ', '*');
+ const Link inputLink = Link::fromString(wildcardInput, true);
+ const QString newInput = inputLink.targetFilePath.toString();
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(newInput);
+ if (!regExp.isValid())
+ return;
+
+ const bool hasPathSeparator = newInput.contains('/') || newInput.contains('*');
+ LocatorFileCache::MatchedEntries entries = {};
+ QEventLoop loop;
+ Process process;
+ process.setCommand(command);
+ process.setEnvironment(Environment::systemEnvironment()); // TODO: Is it needed?
- std::unique_ptr<MacroExpander> expander(createMacroExpander(link.targetFilePath.fileName()));
- const QString argumentString = expander->expand(
- caseSensitivity(link.targetFilePath.toString()) == Qt::CaseInsensitive
- ? m_arguments
- : m_caseSensitiveArguments);
- const CommandLine cmd(FilePath::fromString(m_command), argumentString, CommandLine::Raw);
- setFileIterator(new SpotlightIterator(cmd));
+ QObject::connect(&process, &Process::readyReadStandardOutput, &process,
+ [&, entriesPtr = &entries] {
+ QString output = process.readAllStandardOutput();
+ output.replace("\r\n", "\n");
+ const QStringList items = output.split('\n');
+ const FilePaths filePaths = Utils::transform(items, &FilePath::fromUserInput);
+ LocatorFileCache::processFilePaths(promise.future(), filePaths, hasPathSeparator, regExp,
+ inputLink, entriesPtr);
+ if (promise.isCanceled())
+ loop.exit();
+ });
+ QObject::connect(&process, &Process::done, &process, [&] {
+ if (process.result() != ProcessResult::FinishedWithSuccess) {
+ MessageManager::writeFlashing(Tr::tr("Locator: Error occurred when running \"%1\".")
+ .arg(command.executable().toUserOutput()));
+ }
+ loop.exit();
+ });
+ QFutureWatcher<void> watcher;
+ watcher.setFuture(promise.future());
+ QObject::connect(&watcher, &QFutureWatcherBase::canceled, &watcher, [&loop] { loop.exit(); });
+ if (promise.isCanceled())
+ return;
+ process.start();
+ loop.exec();
+
+ for (auto &entry : entries) {
+ if (promise.isCanceled())
+ return;
+ if (entry.size() < 1000)
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
}
- BaseFileFilter::prepareSearch(entry);
+ if (promise.isCanceled())
+ return;
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
+}
+
+LocatorMatcherTasks SpotlightLocatorFilter::matchers()
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, command = m_command, insensArgs = m_arguments,
+ sensArgs = m_caseSensitiveArguments](Async<void> &async) {
+ const Link link = Link::fromString(storage->input(), true);
+ const FilePath input = link.targetFilePath;
+ if (input.isEmpty())
+ return TaskAction::StopWithDone;
+
+ // only pass the file name part to allow searches like "somepath/*foo"
+ const std::unique_ptr<MacroExpander> expander(createMacroExpander(input.fileName()));
+ const QString args = caseSensitivity(input.toString()) == Qt::CaseInsensitive
+ ? insensArgs : sensArgs;
+ const CommandLine cmd(FilePath::fromString(command), expander->expand(args),
+ CommandLine::Raw);
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, cmd);
+ return TaskAction::Continue;
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
@@ -275,7 +218,7 @@ bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefres
chooser->addMacroExpanderProvider([expander = expander.get()] { return expander; });
chooser->addSupportedWidget(argumentsEdit);
chooser->addSupportedWidget(caseSensitiveArgumentsEdit);
- const bool accepted = openConfigDialog(parent, &configWidget);
+ const bool accepted = ILocatorFilter::openConfigDialog(parent, &configWidget);
if (accepted) {
m_command = commandEdit->rawFilePath().toString();
m_arguments = argumentsEdit->text();
@@ -301,12 +244,4 @@ void SpotlightLocatorFilter::restoreState(const QJsonObject &obj)
m_caseSensitiveArguments = obj.value(kCaseSensitiveKey).toString(defaultArguments(Qt::CaseSensitive));
}
-void SpotlightLocatorFilter::reset()
-{
- m_command = defaultCommand();
- m_arguments = defaultArguments();
- m_caseSensitiveArguments = defaultArguments(Qt::CaseSensitive);
-}
-
-} // Internal
-} // Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h
index 174319d434..06114d4e26 100644
--- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h
+++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h
@@ -3,22 +3,15 @@
#pragma once
-#include "basefilefilter.h"
+#include "ilocatorfilter.h"
-#include <functional>
+namespace Core::Internal {
-namespace Core {
-namespace Internal {
-
-class SpotlightLocatorFilter : public BaseFileFilter
+class SpotlightLocatorFilter : public ILocatorFilter
{
- Q_OBJECT
public:
SpotlightLocatorFilter();
- void prepareSearch(const QString &entry) override;
-
- using ILocatorFilter::openConfigDialog;
bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
protected:
@@ -26,12 +19,11 @@ protected:
void restoreState(const QJsonObject &obj) final;
private:
- void reset();
+ LocatorMatcherTasks matchers() final;
QString m_command;
QString m_arguments;
QString m_caseSensitiveArguments;
};
-} // Internal
-} // Core
+} // namespace Core::Internal
diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.cpp b/src/plugins/coreplugin/locator/urllocatorfilter.cpp
index 70651e74ee..c9025b2237 100644
--- a/src/plugins/coreplugin/locator/urllocatorfilter.cpp
+++ b/src/plugins/coreplugin/locator/urllocatorfilter.cpp
@@ -7,7 +7,6 @@
#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
-#include <utils/stringutils.h>
#include <QCheckBox>
#include <QDesktopServices>
@@ -17,9 +16,7 @@
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
-#include <QMutexLocker>
#include <QPushButton>
-#include <QUrl>
using namespace Utils;
@@ -162,38 +159,32 @@ UrlLocatorFilter::UrlLocatorFilter(const QString &displayName, Id id)
setId(id);
m_defaultDisplayName = displayName;
setDisplayName(displayName);
- setDefaultIncludedByDefault(false);
}
-UrlLocatorFilter::~UrlLocatorFilter() = default;
-
-QList<Core::LocatorFilterEntry> UrlLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-{
- QList<Core::LocatorFilterEntry> entries;
- const QStringList urls = remoteUrls();
- for (const QString &url : urls) {
- if (future.isCanceled())
- break;
- const QString name = url.arg(entry);
- Core::LocatorFilterEntry filterEntry(this, name);
- filterEntry.highlightInfo = {int(name.lastIndexOf(entry)), int(entry.length())};
- entries.append(filterEntry);
- }
- return entries;
-}
-
-void UrlLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const
+LocatorMatcherTasks UrlLocatorFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- const QString &url = selection.displayName;
- if (!url.isEmpty())
- QDesktopServices::openUrl(url);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, urls = remoteUrls()] {
+ const QString input = storage->input();
+ LocatorFilterEntries entries;
+ for (const QString &url : urls) {
+ const QString name = url.arg(input);
+ LocatorFilterEntry entry;
+ entry.displayName = name;
+ entry.acceptor = [name] {
+ if (!name.isEmpty())
+ QDesktopServices::openUrl(name);
+ return AcceptResult();
+ };
+ entry.highlightInfo = {int(name.lastIndexOf(input)), int(input.length())};
+ entries.append(entry);
+ }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
const char kDisplayNameKey[] = "displayName";
@@ -249,7 +240,6 @@ bool UrlLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
Q_UNUSED(needsRefresh)
Internal::UrlFilterOptions optionsDialog(this, parent);
if (optionsDialog.exec() == QDialog::Accepted) {
- QMutexLocker lock(&m_mutex);
m_remoteUrls.clear();
setIncludedByDefault(optionsDialog.includeByDefault->isChecked());
setShortcutString(optionsDialog.shortcutEdit->text().trimmed());
@@ -268,20 +258,4 @@ void UrlLocatorFilter::addDefaultUrl(const QString &urlTemplate)
m_defaultUrls.append(urlTemplate);
}
-QStringList UrlLocatorFilter::remoteUrls() const
-{
- QMutexLocker lock(&m_mutex);
- return m_remoteUrls;
-}
-
-void UrlLocatorFilter::setIsCustomFilter(bool value)
-{
- m_isCustomFilter = value;
-}
-
-bool UrlLocatorFilter::isCustomFilter() const
-{
- return m_isCustomFilter;
-}
-
} // namespace Core
diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.h b/src/plugins/coreplugin/locator/urllocatorfilter.h
index befe162d60..57f652aeca 100644
--- a/src/plugins/coreplugin/locator/urllocatorfilter.h
+++ b/src/plugins/coreplugin/locator/urllocatorfilter.h
@@ -8,7 +8,6 @@
#include <coreplugin/core_global.h>
#include <QDialog>
-#include <QMutex>
QT_BEGIN_NAMESPACE
class QCheckBox;
@@ -21,36 +20,30 @@ namespace Core {
class CORE_EXPORT UrlLocatorFilter final : public Core::ILocatorFilter
{
- Q_OBJECT
public:
UrlLocatorFilter(Utils::Id id);
UrlLocatorFilter(const QString &displayName, Utils::Id id);
- ~UrlLocatorFilter() final;
- // ILocatorFilter
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void restoreState(const QByteArray &state) override;
- bool openConfigDialog(QWidget *parent, bool &needsRefresh) override;
+ void restoreState(const QByteArray &state) final;
+ bool openConfigDialog(QWidget *parent, bool &needsRefresh) final;
void addDefaultUrl(const QString &urlTemplate);
- QStringList remoteUrls() const;
+ QStringList remoteUrls() const { return m_remoteUrls; }
- void setIsCustomFilter(bool value);
- bool isCustomFilter() const;
+ void setIsCustomFilter(bool value) { m_isCustomFilter = value; }
+ bool isCustomFilter() const { return m_isCustomFilter; }
protected:
void saveState(QJsonObject &object) const final;
void restoreState(const QJsonObject &object) final;
private:
+ LocatorMatcherTasks matchers() final;
+
QString m_defaultDisplayName;
QStringList m_defaultUrls;
QStringList m_remoteUrls;
bool m_isCustomFilter = false;
- mutable QMutex m_mutex;
};
namespace Internal {
@@ -81,5 +74,4 @@ private:
};
} // namespace Internal
-
} // namespace Core
diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp
index 3688b6c0ce..2d7ec70c8e 100644
--- a/src/plugins/coreplugin/mainwindow.cpp
+++ b/src/plugins/coreplugin/mainwindow.cpp
@@ -112,6 +112,10 @@ static const char modeSelectorLayoutKey[] = "ModeSelectorLayout";
static const bool askBeforeExitDefault = false;
+static bool hideToolsMenu()
+{
+ return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool();
+}
enum { debugMainWindow = 0 };
@@ -498,7 +502,10 @@ void MainWindow::registerDefaultContainers()
// Tools Menu
ActionContainer *ac = ActionManager::createMenu(Constants::M_TOOLS);
- menubar->addMenu(ac, Constants::G_TOOLS);
+ ac->setParent(this);
+ if (!hideToolsMenu())
+ menubar->addMenu(ac, Constants::G_TOOLS);
+
ac->menu()->setTitle(Tr::tr("&Tools"));
// Window Menu
@@ -1377,67 +1384,6 @@ public:
}
};
-class MarkdownHighlighter : public QSyntaxHighlighter
-{
- QBrush h2Brush;
-public:
- MarkdownHighlighter(QTextDocument *parent)
- : QSyntaxHighlighter(parent)
- , h2Brush(Qt::NoBrush)
- {
- parent->setIndentWidth(30); // default value is 40
- }
-
- void highlightBlock(const QString &text)
- {
- if (text.isEmpty())
- return;
-
- QTextBlockFormat fmt = currentBlock().blockFormat();
- QTextCursor cur(currentBlock());
- if (fmt.hasProperty(QTextFormat::HeadingLevel)) {
- fmt.setTopMargin(10);
- fmt.setBottomMargin(10);
-
- // Draw an underline for Heading 2, by creating a texture brush
- // with the last pixel visible
- if (fmt.property(QTextFormat::HeadingLevel) == 2) {
- QTextCharFormat charFmt = currentBlock().charFormat();
- charFmt.setBaselineOffset(15);
- setFormat(0, text.length(), charFmt);
-
- if (h2Brush.style() == Qt::NoBrush) {
- const int height = QFontMetrics(charFmt.font()).height();
- QImage image(1, height, QImage::Format_ARGB32);
-
- image.fill(QColor(0, 0, 0, 0).rgba());
- image.setPixel(0,
- height - 1,
- Utils::creatorTheme()->color(Theme::TextColorDisabled).rgba());
-
- h2Brush = QBrush(image);
- }
- fmt.setBackground(h2Brush);
- }
- cur.setBlockFormat(fmt);
- } else if (fmt.hasProperty(QTextFormat::BlockCodeLanguage) && fmt.indent() == 0) {
- // set identation for code blocks
- fmt.setIndent(1);
- cur.setBlockFormat(fmt);
- }
-
- // Show the bulet points as filled circles
- QTextList *list = cur.currentList();
- if (list) {
- QTextListFormat listFmt = list->format();
- if (listFmt.indent() == 1 && listFmt.style() == QTextListFormat::ListCircle) {
- listFmt.setStyle(QTextListFormat::ListDisc);
- list->setFormat(listFmt);
- }
- }
- }
-};
-
void MainWindow::changeLog()
{
static QPointer<LogDialog> dialog;
@@ -1477,8 +1423,7 @@ void MainWindow::changeLog()
aggregate->add(textEdit);
aggregate->add(new Core::BaseTextFind(textEdit));
- auto highlighter = new MarkdownHighlighter(textEdit->document());
- (void)highlighter;
+ new MarkdownHighlighter(textEdit->document());
auto textEditWidget = new QFrame;
textEditWidget->setFrameStyle(QFrame::NoFrame);
diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp
index 3ec8340553..b729ab0c19 100644
--- a/src/plugins/coreplugin/manhattanstyle.cpp
+++ b/src/plugins/coreplugin/manhattanstyle.cpp
@@ -3,16 +3,17 @@
#include "manhattanstyle.h"
-#include "styleanimator.h"
+#include "generalsettings.h"
#include <utils/algorithm.h>
+#include <utils/fancymainwindow.h>
#include <utils/hostosinfo.h>
+#include <utils/itemviews.h>
+#include <utils/qtcassert.h>
+#include <utils/styleanimator.h>
#include <utils/stylehelper.h>
-
-#include <utils/fancymainwindow.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
-#include <utils/qtcassert.h>
#include <QApplication>
#include <QCheckBox>
@@ -26,6 +27,8 @@
#include <QPainter>
#include <QPainterPath>
#include <QPixmap>
+#include <QPixmapCache>
+#include <QScrollArea>
#include <QSpinBox>
#include <QStatusBar>
#include <QStyleFactory>
@@ -55,7 +58,7 @@ static bool isInUnstyledDialogOrPopup(const QWidget *widget)
{
// Do not style contents of dialogs or popups without "panelwidget" property
const QWidget *window = widget->window();
- if (window->property("panelwidget").toBool())
+ if (window->property(StyleHelper::C_PANEL_WIDGET).toBool())
return false;
const Qt::WindowType windowType = window->windowType();
return (windowType == Qt::Dialog || windowType == Qt::Popup);
@@ -76,12 +79,15 @@ bool panelWidget(const QWidget *widget)
if (qobject_cast<const QTabBar *>(widget))
return styleEnabled(widget);
+ if (qobject_cast<const QScrollArea *>(widget)) // See DebuggerMainWindowPrivate
+ return widget->property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool();
+
const QWidget *p = widget;
while (p) {
if (qobject_cast<const QToolBar *>(p) ||
qobject_cast<const QStatusBar *>(p) ||
qobject_cast<const QMenuBar *>(p) ||
- p->property("panelwidget").toBool())
+ p->property(StyleHelper::C_PANEL_WIDGET).toBool())
return styleEnabled(widget);
p = p->parentWidget();
}
@@ -89,6 +95,23 @@ bool panelWidget(const QWidget *widget)
}
// Consider making this a QStyle state
+static bool isQmlEditorMenu(const QWidget *widget)
+{
+ const QMenu *menu = qobject_cast<const QMenu *> (widget);
+ if (!menu)
+ return false;
+
+ const QWidget *p = widget;
+ while (p) {
+ if (p->property("qmlEditorMenu").toBool())
+ return styleEnabled(widget);
+ p = p->parentWidget();
+ }
+
+ return false;
+}
+
+// Consider making this a QStyle state
bool lightColored(const QWidget *widget)
{
if (!widget)
@@ -99,7 +122,7 @@ bool lightColored(const QWidget *widget)
const QWidget *p = widget;
while (p) {
- if (p->property("lightColored").toBool())
+ if (p->property(StyleHelper::C_LIGHT_COLORED).toBool())
return true;
p = p->parentWidget();
}
@@ -112,6 +135,246 @@ static bool isDarkFusionStyle(const QStyle *style)
&& strcmp(style->metaObject()->className(), "QFusionStyle") == 0;
}
+QColor qmlEditorTextColor(bool enabled,
+ bool active,
+ bool checked)
+{
+ Theme::Color themePenColorId = enabled ? (active
+ ? (checked ? Theme::DSsubPanelBackground
+ : Theme::DSpanelBackground)
+ : Theme::DStextColor)
+ : Theme::DStextColorDisabled;
+
+ return creatorTheme()->color(themePenColorId);
+}
+
+QPixmap getDeletePixmap(bool enabled,
+ bool active,
+ const QSize &sizeLimit)
+{
+ using Utils::Theme;
+ using Utils::creatorTheme;
+ using namespace Utils::StyleHelper;
+
+ const double xRatio = 19;
+ const double yRatio = 9;
+ double sizeConst = std::min(xRatio * sizeLimit.height(), yRatio * sizeLimit.width());
+ sizeConst = std::max(xRatio, sizeConst);
+
+ const int height = sizeConst/xRatio;
+ const int width = sizeConst/yRatio;
+ QPixmap retval(width, height);
+ QPainter p(&retval);
+ const qreal devicePixelRatio = p.device()->devicePixelRatio();
+
+ QPixmap pixmap;
+ QString pixmapName = QLatin1String("StyleHelper::drawDelete")
+ + "-" + QString::number(sizeConst)
+ + "-" + QString::number(enabled)
+ + "-" + QString::number(active)
+ + "-" + QString::number(devicePixelRatio);
+
+ if (!QPixmapCache::find(pixmapName, &pixmap)) {
+ QImage image(width * devicePixelRatio, height * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
+ image.fill(Qt::transparent);
+ QPainter painter(&image);
+
+ auto drawDelete = [&painter, yRatio](const QRect &rect, const QColor &color) -> void
+ {
+ static const QStyle* const style = QApplication::style();
+ if (!style)
+ return;
+
+ const int height = rect.height();
+ const int width = rect.width();
+ const int insideW = height / 2;
+ const int penWidth = std::ceil(height / yRatio);
+ const int pixelGuard = penWidth / 2;
+ const QRect xRect = {insideW + (4 * penWidth),
+ 2 * penWidth,
+ 4 * penWidth,
+ 5 * penWidth};
+
+ // Workaround for QTCREATORBUG-28470
+ painter.save();
+ painter.setOpacity(color.alphaF());
+
+ QPen pen(color, penWidth);
+ pen.setJoinStyle(Qt::MiterJoin);
+ painter.setPen(pen);
+
+ QPainterPath pp(QPointF(pixelGuard, insideW));
+ pp.lineTo(insideW, pixelGuard);
+ pp.lineTo(width - pixelGuard, pixelGuard);
+ pp.lineTo(width - pixelGuard, height - pixelGuard);
+ pp.lineTo(insideW, height - pixelGuard);
+ pp.lineTo(pixelGuard, insideW);
+
+ painter.drawPath(pp);
+
+ // drawing X
+ painter.setPen(QPen(color, 1));
+ QPoint stepOver(penWidth, 0);
+
+ pp.clear();
+ pp.moveTo(xRect.topLeft());
+ pp.lineTo(xRect.topLeft() + stepOver);
+ pp.lineTo(xRect.bottomRight());
+ pp.lineTo(xRect.bottomRight() - stepOver);
+ pp.lineTo(xRect.topLeft());
+ painter.fillPath(pp, QBrush(color));
+
+ pp.clear();
+ pp.moveTo(xRect.topRight());
+ pp.lineTo(xRect.topRight() - stepOver);
+ pp.lineTo(xRect.bottomLeft());
+ pp.lineTo(xRect.bottomLeft() + stepOver);
+ pp.lineTo(xRect.topRight());
+ painter.fillPath(pp, QBrush(color));
+
+ painter.restore();
+ };
+
+ if (enabled && creatorTheme()->flag(Theme::ToolBarIconShadow))
+ drawDelete(image.rect().translated(0, devicePixelRatio), StyleHelper::toolBarDropShadowColor());
+
+ drawDelete(image.rect(), qmlEditorTextColor(enabled, active, false));
+
+ painter.end();
+ pixmap = QPixmap::fromImage(image);
+ pixmap.setDevicePixelRatio(devicePixelRatio);
+ QPixmapCache::insert(pixmapName, pixmap);
+ }
+ return pixmap;
+}
+
+struct ManhattanShortcut {
+ ManhattanShortcut(const QStyleOptionMenuItem *option,
+ const QString &shortcutText)
+ : shortcutText(shortcutText)
+ , enabled(option->state & QStyle::State_Enabled)
+ , active(option->state & QStyle::State_Selected)
+ , font(option->font)
+ , fm(font)
+ , defaultHeight(fm.height())
+ , palette(option->palette)
+ , spaceConst(fm.boundingRect(".").width())
+ {
+ reset();
+ }
+
+ QSize getSize()
+ {
+ if (isFirstParticle)
+ calcResult();
+ return _size;
+ }
+
+ QPixmap getPixmap()
+ {
+ if (!isFirstParticle && !_pixmap.isNull())
+ return _pixmap;
+
+ _pixmap = QPixmap(getSize());
+ _pixmap.fill(Qt::transparent);
+ QPainter painter(&_pixmap);
+ painter.setFont(font);
+ QPen pPen = painter.pen();
+ pPen.setColor(qmlEditorTextColor(enabled, active, false));
+ painter.setPen(pPen);
+ calcResult(&painter);
+ painter.end();
+
+ return _pixmap;
+ }
+
+private:
+ void applySize(const QSize &itemSize) {
+ width += itemSize.width();
+ height = std::max(height, itemSize.height());
+ if (isFirstParticle)
+ isFirstParticle = false;
+ else
+ width += spaceConst;
+ };
+
+ void addText(const QString &txt, QPainter *painter = nullptr)
+ {
+ if (txt.size()) {
+ int textWidth = fm.boundingRect(txt).width();
+ QSize itemSize = {textWidth, defaultHeight};
+ if (painter) {
+ QRect placeRect({width, 0}, itemSize);
+ painter->drawText(placeRect, txt, textOption);
+ }
+ applySize(itemSize);
+ }
+ };
+
+ void addPixmap(const QPixmap &pixmap, QPainter *painter = nullptr)
+ {
+ if (painter)
+ painter->drawPixmap(QRect({width, 0}, pixmap.size()), pixmap);
+
+ applySize(pixmap.size());
+ };
+
+ void calcResult(QPainter *painter = nullptr)
+ {
+ reset();
+#ifndef QT_NO_SHORTCUT
+ if (!shortcutText.isEmpty()) {
+ int fwdIndex = 0;
+ QRegularExpressionMatch mMatch = backspaceDetect.match(shortcutText);
+ int matchCount = mMatch.lastCapturedIndex();
+
+ for (int i = 0; i <= matchCount; ++i) {
+ QString mStr = mMatch.captured(i);
+ QPixmap pixmap = getDeletePixmap(enabled,
+ active,
+ {defaultHeight * 3, defaultHeight});
+
+ int lIndex = shortcutText.indexOf(mStr, fwdIndex);
+ int diffChars = lIndex - fwdIndex;
+ addText(shortcutText.mid(fwdIndex, diffChars), painter);
+ addPixmap(pixmap, painter);
+ fwdIndex = lIndex + mStr.size();
+ }
+ addText(shortcutText.mid(fwdIndex), painter);
+ }
+#endif
+ _size = {width, height};
+ }
+
+ void reset()
+ {
+ isFirstParticle = true;
+ width = 0;
+ height = 0;
+ }
+
+ const QString shortcutText;
+ const bool enabled;
+ const bool active;
+ const QFont font;
+ const QFontMetrics fm;
+ const int defaultHeight;
+ const QPalette palette;
+ const int spaceConst;
+ static const QTextOption textOption;
+ static const QRegularExpression backspaceDetect;
+ bool isFirstParticle = true;
+
+ int width = 0;
+ int height = 0;
+ QSize _size;
+ QPixmap _pixmap;
+};
+const QRegularExpression ManhattanShortcut::backspaceDetect("\\+*backspace\\+*",
+ QRegularExpression::CaseInsensitiveOption);
+const QTextOption ManhattanShortcut::textOption(Qt::AlignLeft | Qt::AlignVCenter);
+
+
class ManhattanStylePrivate
{
public:
@@ -134,6 +397,7 @@ ManhattanStyle::ManhattanStyle(const QString &baseStyleName)
: QProxyStyle(QStyleFactory::create(baseStyleName)),
d(new ManhattanStylePrivate())
{
+ Core::Internal::GeneralSettings::applyToolbarStyleFromSettings();
}
ManhattanStyle::~ManhattanStyle()
@@ -152,10 +416,66 @@ QSize ManhattanStyle::sizeFromContents(ContentsType type, const QStyleOption *op
{
QSize newSize = QProxyStyle::sizeFromContents(type, option, size, widget);
- if (type == CT_Splitter && widget && widget->property("minisplitter").toBool())
- return QSize(1, 1);
- else if (type == CT_ComboBox && panelWidget(widget))
- newSize += QSize(14, 0);
+ switch (type) {
+ case CT_Splitter:
+ if (widget && widget->property(StyleHelper::C_MINI_SPLITTER).toBool())
+ newSize = QSize(1, 1);
+ break;
+ case CT_ComboBox:
+ if (panelWidget(widget))
+ newSize += QSize(14, 0);
+ break;
+ case CT_MenuItem:
+ if (isQmlEditorMenu(widget)) {
+ if (const auto mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ const int leftMargin = pixelMetric(QStyle::PM_LayoutLeftMargin, option, widget);
+ const int rightMargin = pixelMetric(QStyle::PM_LayoutRightMargin, option, widget);
+ const int horizontalSpacing = pixelMetric(QStyle::PM_LayoutHorizontalSpacing, option, widget);
+ const int iconHeight = pixelMetric(QStyle::PM_SmallIconSize, option, widget) + horizontalSpacing;
+ int width = leftMargin + rightMargin;
+ if (mbi->menuHasCheckableItems || mbi->maxIconWidth)
+ width += iconHeight + horizontalSpacing;
+
+ if (!mbi->text.isEmpty()) {
+ QString itemText = mbi->text;
+ QString shortcutText;
+ int tabIndex = itemText.indexOf("\t");
+ if (tabIndex > -1) {
+ shortcutText = itemText.mid(tabIndex + 1);
+ itemText = itemText.left(tabIndex);
+ }
+
+ if (itemText.size())
+ width += option->fontMetrics.boundingRect(itemText).width() + horizontalSpacing;
+
+ if (shortcutText.size()) {
+ QSize shortcutSize = ManhattanShortcut(mbi, shortcutText).getSize();
+ width += shortcutSize.width() + 2 * horizontalSpacing;
+ }
+ }
+
+ if (mbi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ width += iconHeight + horizontalSpacing;
+
+ newSize.setWidth(width);
+
+ switch (mbi->menuItemType) {
+ case QStyleOptionMenuItem::Normal:
+ case QStyleOptionMenuItem::DefaultItem:
+ case QStyleOptionMenuItem::SubMenu:
+ newSize.setHeight(19);
+ break;
+ default:
+ newSize += QSize(0, 7);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
return newSize;
}
@@ -175,7 +495,30 @@ QRect ManhattanStyle::subControlRect(ComplexControl control, const QStyleOptionC
return QRect(); // breaks the scrollbar, but avoids the crash
}
#endif
- return QProxyStyle::subControlRect(control, option, subControl, widget);
+
+ QRect retval = QProxyStyle::subControlRect(control, option, subControl, widget);;
+ if (panelWidget(widget)) {
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ switch (subControl) {
+ case SubControl::SC_SliderGroove:
+ return option->rect;
+ case SubControl::SC_SliderHandle:
+ {
+ int thickness = 2;
+ QPoint center = retval.center();
+ const QRect &rect = slider->rect;
+ if (slider->orientation == Qt::Horizontal)
+ return QRect(center.x() - thickness, rect.top(), (thickness * 2) + 1, rect.height());
+ else
+ return QRect(rect.left(), center.y() - thickness, rect.width(), (thickness * 2) + 1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return retval;
}
QStyle::SubControl ManhattanStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
@@ -186,11 +529,17 @@ QStyle::SubControl ManhattanStyle::hitTestComplexControl(ComplexControl control,
int ManhattanStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
{
- int retval = 0;
- retval = QProxyStyle::pixelMetric(metric, option, widget);
+ int retval = QProxyStyle::pixelMetric(metric, option, widget);
+
switch (metric) {
+#ifdef Q_OS_MACOS
+ case PM_MenuButtonIndicator:
+ if (widget && option->type == QStyleOption::SO_ToolButton)
+ return 12;
+ break;
+#endif
case PM_SplitterWidth:
- if (widget && widget->property("minisplitter").toBool())
+ if (widget && widget->property(StyleHelper::C_MINI_SPLITTER).toBool())
retval = 1;
break;
case PM_ToolBarIconSize:
@@ -199,30 +548,61 @@ int ManhattanStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
retval = 16;
break;
case PM_SmallIconSize:
- retval = 16;
+ if (isQmlEditorMenu(widget))
+ retval = 10;
+ else
+ retval = 16;
break;
case PM_DockWidgetHandleExtent:
case PM_DockWidgetSeparatorExtent:
return 1;
+ case PM_LayoutLeftMargin:
+ case PM_LayoutRightMargin:
+ if (isQmlEditorMenu(widget))
+ retval = 7;
+ break;
+ case PM_LayoutHorizontalSpacing:
+ if (isQmlEditorMenu(widget))
+ retval = 12;
+ break;
+ case PM_MenuHMargin:
+ if (isQmlEditorMenu(widget))
+ retval = 5;
+ break;
+ case PM_SubMenuOverlap:
+ if (isQmlEditorMenu(widget))
+ retval = 10;
+ break;
case PM_MenuPanelWidth:
case PM_MenuBarHMargin:
case PM_MenuBarVMargin:
case PM_ToolBarFrameWidth:
- if (panelWidget(widget))
+ if (panelWidget(widget) || isQmlEditorMenu(widget))
retval = 1;
break;
case PM_ButtonShiftVertical:
case PM_ButtonShiftHorizontal:
case PM_MenuBarPanelWidth:
case PM_ToolBarItemMargin:
+ if (StyleHelper::isQDSTheme()) {
+ retval = 0;
+ break;
+ }
+ [[fallthrough]];
case PM_ToolBarItemSpacing:
if (panelWidget(widget))
retval = 0;
+ if (StyleHelper::isQDSTheme())
+ retval = 4;
break;
case PM_DefaultFrameWidth:
if (qobject_cast<const QLineEdit*>(widget) && panelWidget(widget))
return 1;
break;
+ case PM_ToolBarExtensionExtent:
+ if (StyleHelper::isQDSTheme())
+ retval = 29;
+ break;
default:
break;
}
@@ -280,16 +660,26 @@ void ManhattanStyle::polish(QWidget *widget)
widget->setAttribute(Qt::WA_LayoutUsesWidgetRect, true);
// So that text isn't cutoff in line-edits, comboboxes... etc.
const int height = qMax(StyleHelper::navigationWidgetHeight(), QApplication::fontMetrics().height());
- if (qobject_cast<QToolButton*>(widget) || qobject_cast<QLineEdit*>(widget)) {
+ if (qobject_cast<QToolButton*>(widget)) {
+ widget->setMinimumWidth(
+ StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact ? 24 : 28);
widget->setAttribute(Qt::WA_Hover);
widget->setMaximumHeight(height - 2);
+ } else if (qobject_cast<QLineEdit*>(widget)) {
+ widget->setAttribute(Qt::WA_Hover);
+ widget->setFixedHeight(height - (StyleHelper::toolbarStyle()
+ == StyleHelper::ToolbarStyleCompact ? 1 : 3));
} else if (qobject_cast<QLabel*>(widget) || qobject_cast<QSpinBox*>(widget)
|| qobject_cast<QCheckBox*>(widget)) {
widget->setPalette(panelPalette(widget->palette(), lightColored(widget)));
- } else if (widget->property("panelwidget_singlerow").toBool()) {
+ } else if ((qobject_cast<QToolBar*>(widget) && !StyleHelper::isQDSTheme())
+ || widget->property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool()) {
widget->setFixedHeight(height);
} else if (qobject_cast<QStatusBar*>(widget)) {
- widget->setFixedHeight(height + 2);
+ const bool flatAndNotCompact =
+ StyleHelper::toolbarStyle() != StyleHelper::ToolbarStyleCompact
+ && creatorTheme()->flag(Theme::FlatToolBars);
+ widget->setFixedHeight(height + (flatAndNotCompact ? 3 : 2));
} else if (qobject_cast<QComboBox*>(widget)) {
const bool isLightColored = lightColored(widget);
QPalette palette = panelPalette(widget->palette(), isLightColored);
@@ -299,6 +689,9 @@ void ManhattanStyle::polish(QWidget *widget)
widget->setPalette(palette);
widget->setMaximumHeight(height - 2);
widget->setAttribute(Qt::WA_Hover);
+ } else if (qobject_cast<QScrollArea*>(widget)
+ && widget->property(StyleHelper::C_PANEL_WIDGET_SINGLE_ROW).toBool()) {
+ widget->setFixedHeight(height);
}
}
}
@@ -375,7 +768,7 @@ int ManhattanStyle::styleHint(StyleHint hint, const QStyleOption *option, const
case QStyle::SH_ItemView_ActivateItemOnSingleClick:
// default depends on the style
if (widget) {
- QVariant activationMode = widget->property("ActivationMode");
+ QVariant activationMode = widget->property(activationModeC);
if (activationMode.isValid())
ret = activationMode.toBool();
}
@@ -534,8 +927,11 @@ static void drawPrimitiveTweakedForDarkTheme(QStyle::PrimitiveElement element,
void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const
{
- const bool isPanelWidget = panelWidget(widget);
- if (!isPanelWidget) {
+ if (panelWidget(widget)) {
+ drawPrimitiveForPanelWidget(element, option, painter, widget);
+ } else if (isQmlEditorMenu(widget)) {
+ drawPrimitiveForQmlEditor(element, option, painter, widget);
+ } else {
const bool tweakDarkTheme =
(element == PE_Frame
|| element == PE_FrameLineEdit
@@ -550,7 +946,13 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption
QProxyStyle::drawPrimitive(element, option, painter, widget);
return;
}
+}
+void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
bool animating = (option->state & State_Animating);
int state = option->state;
QRect rect = option->rect;
@@ -624,7 +1026,10 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption
painter->save();
if (!enabled)
painter->setOpacity(0.75);
- painter->fillRect(backgroundRect, option->palette.base());
+ QBrush baseBrush = option->palette.base();
+ if (widget && qobject_cast<const QSpinBox *>(widget->parentWidget()))
+ baseBrush = creatorTheme()->color(Theme::DScontrolBackgroundDisabled);
+ painter->fillRect(backgroundRect, baseBrush);
painter->restore();
} else {
backgroundRect.adjust(1, 1, -1, -1);
@@ -659,22 +1064,23 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption
if (!animating && anim) {
anim->paint(painter, option);
} else {
- bool pressed = option->state & State_Sunken || option->state & State_On;
+ const bool pressed = option->state & State_Sunken || option->state & State_On
+ || (widget && widget->property(StyleHelper::C_HIGHLIGHT_WIDGET)
+ .toBool());
painter->setPen(StyleHelper::sidebarShadow());
if (pressed) {
- const QColor shade = creatorTheme()->color(Theme::FancyToolButtonSelectedColor);
- painter->fillRect(rect, shade);
- if (!creatorTheme()->flag(Theme::FlatToolBars)) {
+ StyleHelper::drawPanelBgRect(
+ painter, rect, creatorTheme()->color(Theme::FancyToolButtonSelectedColor));
+ if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact
+ && !creatorTheme()->flag(Theme::FlatToolBars)) {
const QRectF borderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5);
painter->drawLine(borderRect.topLeft() + QPointF(1, 0), borderRect.topRight() - QPointF(1, 0));
painter->drawLine(borderRect.topLeft(), borderRect.bottomLeft());
painter->drawLine(borderRect.topRight(), borderRect.bottomRight());
}
} else if (option->state & State_Enabled && option->state & State_MouseOver) {
- painter->fillRect(rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
- } else if (widget && widget->property("highlightWidget").toBool()) {
- QColor shade(0, 0, 0, 128);
- painter->fillRect(rect, shade);
+ StyleHelper::drawPanelBgRect(
+ painter, rect, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
}
if (option->state & State_HasFocus && (option->state & State_KeyboardFocusChange)) {
QColor highlight = option->palette.highlight().color();
@@ -714,7 +1120,7 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption
break;
case PE_IndicatorToolBarSeparator:
- {
+ if (!StyleHelper::isQDSTheme()) {
QRect separatorRect = rect;
separatorRect.setLeft(rect.width() / 2);
separatorRect.setWidth(1);
@@ -779,6 +1185,225 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption
}
}
+void ManhattanStyle::drawPrimitiveForQmlEditor(PrimitiveElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ const auto mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option);
+ if (!mbi) {
+ QProxyStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+
+ switch (element) {
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ {
+ QStyleOptionMenuItem item = *mbi;
+ item.palette = QPalette(Qt::white);
+ StyleHelper::drawMinimalArrow(element, painter, &item);
+ }
+ break;
+ case PE_IndicatorArrowRight:
+ drawQmlEditorIcon(element, option, "cascadeIconRight", painter, widget);
+ break;
+ case PE_IndicatorArrowLeft:
+ drawQmlEditorIcon(element, option, "cascadeIconLeft", painter, widget);
+ break;
+ case PE_PanelButtonCommand:
+ break;
+ case PE_IndicatorMenuCheckMark:
+ drawQmlEditorIcon(element, option, "tickIcon", painter, widget);
+ break;
+ case PE_FrameMenu:
+ case PE_PanelMenu:
+ {
+ painter->save();
+ painter->setBrush(creatorTheme()->color(Theme::DSsubPanelBackground));
+ painter->setPen(Qt::NoPen);
+ painter->drawRect(option->rect);
+ painter->restore();
+ }
+ break;
+ default:
+ QProxyStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+}
+
+void ManhattanStyle::drawControlForQmlEditor(ControlElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ Q_UNUSED(element)
+ if (const auto mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ painter->save();
+ const int iconHeight = pixelMetric(QStyle::PM_SmallIconSize, option, widget);
+ const int horizontalSpacing = pixelMetric(QStyle::PM_LayoutHorizontalSpacing, option, widget);
+ const int iconWidth = iconHeight;
+ const bool isActive = mbi->state & State_Selected;
+ const bool isDisabled = !(mbi->state & State_Enabled);
+ const bool isCheckable = mbi->checkType != QStyleOptionMenuItem::NotCheckable;
+ const bool isChecked = isCheckable ? mbi->checked : false;
+ int startMargin = pixelMetric(QStyle::PM_LayoutLeftMargin, option, widget);
+ int endMargin = pixelMetric(QStyle::PM_LayoutRightMargin, option, widget);
+ int forwardX = 0;
+
+ if (option->direction == Qt::RightToLeft)
+ std::swap(startMargin, endMargin);
+
+ QStyleOptionMenuItem item = *mbi;
+
+ if (isActive) {
+ painter->fillRect(item.rect, creatorTheme()->color(Theme::DSinteraction));
+ }
+ forwardX += startMargin;
+
+ if (item.menuItemType == QStyleOptionMenuItem::Separator) {
+ int commonHeight = item.rect.center().y();
+ int additionalMargin = forwardX /*hmargin*/;
+ QLineF separatorLine (item.rect.left() + additionalMargin,
+ commonHeight,
+ item.rect.right() - additionalMargin,
+ commonHeight);
+
+ painter->setPen(creatorTheme()->color(Theme::DSstateSeparatorColor));
+ painter->drawLine(separatorLine);
+ item.text.clear();
+ painter->restore();
+ return;
+ }
+
+ QPixmap iconPixmap;
+ QIcon::Mode mode = isDisabled ? QIcon::Disabled : ((isActive) ? QIcon::Active : QIcon::Normal);
+ QIcon::State state = isChecked ? QIcon::On : QIcon::Off;
+ QColor themePenColor = qmlEditorTextColor(!isDisabled, isActive, isChecked);
+
+ if (!item.icon.isNull()) {
+ iconPixmap = item.icon.pixmap(QSize(iconHeight, iconHeight), mode, state);
+ } else if (isCheckable) {
+ iconPixmap = QPixmap(iconHeight, iconHeight);
+ iconPixmap.fill(Qt::transparent);
+
+ if (item.checked) {
+ QStyleOptionMenuItem so = item;
+ so.rect = iconPixmap.rect();
+ QPainter dPainter(&iconPixmap);
+ dPainter.setPen(themePenColor);
+ drawPrimitive(PE_IndicatorMenuCheckMark, &so, &dPainter, widget);
+ }
+ }
+
+ if (!iconPixmap.isNull()) {
+ QRect vCheckRect = visualRect(item.direction,
+ item.rect,
+ QRect(item.rect.x() + forwardX,
+ item.rect.y(),
+ iconWidth,
+ item.rect.height()));
+
+ QRect pmr(QPoint(0, 0), iconPixmap.deviceIndependentSize().toSize());
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(themePenColor);
+ painter->drawPixmap(pmr.topLeft(), iconPixmap);
+
+ item.checkType = QStyleOptionMenuItem::NotCheckable;
+ item.checked = false;
+ item.icon = {};
+ }
+ if (item.menuHasCheckableItems || item.maxIconWidth > 0) {
+ forwardX += iconWidth + horizontalSpacing;
+ }
+
+ QString shortcutText;
+ int tabIndex = item.text.indexOf("\t");
+ if (tabIndex > -1) {
+ shortcutText = item.text.mid(tabIndex + 1);
+ item.text = item.text.left(tabIndex);
+ }
+
+ if (item.text.size()) {
+ painter->save();
+
+ QRect vTextRect = visualRect(item.direction,
+ item.rect,
+ item.rect.adjusted(forwardX, 0 , 0 , 0));
+
+ Qt::Alignment alignmentFlags = item.direction == Qt::LeftToRight ? Qt::AlignLeft
+ : Qt::AlignRight;
+ alignmentFlags |= Qt::AlignVCenter;
+
+ int textFlags = Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, &item, widget))
+ textFlags |= Qt::TextHideMnemonic;
+ textFlags |= alignmentFlags;
+
+ painter->setPen(themePenColor);
+ painter->drawText(vTextRect, textFlags, item.text);
+ painter->restore();
+ }
+
+ if (item.menuItemType == QStyleOptionMenuItem::SubMenu) {
+ PrimitiveElement dropDirElement = item.direction == Qt::LeftToRight ? PE_IndicatorArrowRight
+ : PE_IndicatorArrowLeft;
+
+ QSize elSize(iconHeight, iconHeight);
+ int xOffset = iconHeight + endMargin;
+ int yOffset = (item.rect.height() - iconHeight) / 2;
+ QRect dropRect(item.rect.topRight(), elSize);
+ dropRect.adjust(-xOffset, yOffset, -xOffset, yOffset);
+
+ QStyleOptionMenuItem so = item;
+ so.rect = visualRect(item.direction,
+ item.rect,
+ dropRect);
+
+ drawPrimitive(dropDirElement, &so, painter, widget);
+ } else if (!shortcutText.isEmpty()) {
+ QPixmap pix = ManhattanShortcut(&item, shortcutText).getPixmap();
+
+ if (pix.width()) {
+ int xOffset = pix.width() + (iconHeight / 2) + endMargin;
+ QRect shortcutRect = item.rect.translated({item.rect.width() - xOffset, 0});
+ shortcutRect.setSize({pix.width(), item.rect.height()});
+ shortcutRect = visualRect(item.direction,
+ item.rect,
+ shortcutRect);
+ drawItemPixmap(painter,
+ shortcutRect,
+ Qt::AlignRight | Qt::AlignVCenter,
+ pix);
+ }
+ }
+ painter->restore();
+ }
+}
+
+void ManhattanStyle::drawQmlEditorIcon(PrimitiveElement element,
+ const QStyleOption *option,
+ const char *propertyName,
+ QPainter *painter,
+ const QWidget *widget) const
+{
+ if (option->styleObject && option->styleObject->property(propertyName).isValid()) {
+ const auto mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option);
+ if (mbi) {
+ const bool checkable = mbi->checkType != QStyleOptionMenuItem::NotCheckable;
+ const bool isDisabled = !(mbi->state & State_Enabled);
+ const bool isActive = mbi->state & State_Selected;
+ QIcon icon = mbi->styleObject->property(propertyName).value<QIcon>();
+ QIcon::Mode mode = isDisabled ? QIcon::Disabled : ((isActive) ? QIcon::Active : QIcon::Normal);
+ QIcon::State state = (checkable && mbi->checked) ? QIcon::On : QIcon::Off;
+ QPixmap pix = icon.pixmap(option->rect.size(), mode, state);
+ drawItemPixmap(painter, option->rect, Qt::AlignCenter, pix);
+ return;
+ }
+ }
+ QProxyStyle::drawPrimitive(element, option, painter, widget);
+}
+
void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const
{
@@ -802,7 +1427,11 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
pal.setBrush(QPalette::Text, color);
item.palette = pal;
}
- QProxyStyle::drawControl(element, &item, painter, widget);
+
+ if (isQmlEditorMenu(widget))
+ drawControlForQmlEditor(element, &item, painter, widget);
+ else
+ QProxyStyle::drawControl(element, &item, painter, widget);
}
painter->restore();
break;
@@ -852,7 +1481,8 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
painter->save();
QRect editRect = subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget);
QPalette customPal = cb->palette;
- bool drawIcon = !(widget && widget->property("hideicon").toBool());
+ const bool drawIcon =
+ !(widget && widget->property(StyleHelper::C_HIDE_ICON).toBool());
if (!cb->currentIcon.isNull() && drawIcon) {
QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal
@@ -877,14 +1507,15 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
}
Qt::TextElideMode elideMode = Qt::ElideRight;
- if (widget && widget->dynamicPropertyNames().contains("elidemode"))
- elideMode = widget->property("elidemode").value<Qt::TextElideMode>();
+ if (widget && widget->dynamicPropertyNames().contains(StyleHelper::C_ELIDE_MODE))
+ elideMode = widget->property(StyleHelper::C_ELIDE_MODE)
+ .value<Qt::TextElideMode>();
QLatin1Char asterisk('*');
int elideWidth = editRect.width();
bool notElideAsterisk = elideMode == Qt::ElideRight && widget
- && widget->property("notelideasterisk").toBool()
+ && widget->property(StyleHelper::C_NOT_ELIDE_ASTERISK).toBool()
&& cb->currentText.endsWith(asterisk)
&& option->fontMetrics.horizontalAdvance(cb->currentText)
> elideWidth;
@@ -961,6 +1592,13 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
}
break;
+ case CE_MenuEmptyArea:
+ if (isQmlEditorMenu(widget))
+ drawPrimitive(PE_PanelMenu, option, painter, widget);
+ else
+ QProxyStyle::drawControl(element, option, painter, widget);
+
+ break;
case CE_ToolBar:
{
QRect rect = option->rect;
@@ -978,7 +1616,7 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
bool drawLightColored = lightColored(widget);
// draws the background of the 'Type hierarchy', 'Projects' headers
if (creatorTheme()->flag(Theme::FlatToolBars))
- painter->fillRect(rect, StyleHelper::baseColor(drawLightColored));
+ painter->fillRect(rect, StyleHelper::toolbarBaseColor(drawLightColored));
else if (horizontal)
StyleHelper::horizontalGradient(painter, gradientSpan, rect, drawLightColored);
else
@@ -999,7 +1637,7 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
: StyleHelper::sidebarHighlight();
const QColor borderColor = drawLightColored
? QColor(255, 255, 255, 180) : hightLight;
- if (widget && widget->property("topBorder").toBool()) {
+ if (widget && widget->property(StyleHelper::C_TOP_BORDER).toBool()) {
painter->drawLine(borderRect.topLeft(), borderRect.topRight());
painter->setPen(borderColor);
painter->drawLine(borderRect.topLeft() + QPointF(0, 1), borderRect.topRight() + QPointF(0, 1));
@@ -1015,7 +1653,7 @@ void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *opt
}
if (creatorTheme()->flag(Theme::DrawToolBarBorders)) {
painter->setPen(StyleHelper::toolBarBorderColor());
- if (widget && widget->property("topBorder").toBool())
+ if (widget && widget->property(StyleHelper::C_TOP_BORDER).toBool())
painter->drawLine(borderRect.topLeft(), borderRect.topRight());
else
painter->drawLine(borderRect.bottomLeft(), borderRect.bottomRight());
@@ -1037,14 +1675,15 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
QPainter *painter, const QWidget *widget) const
{
if (!panelWidget(widget))
- return QProxyStyle::drawComplexControl(control, option, painter, widget);
+ return QProxyStyle::drawComplexControl(control, option, painter, widget);
QRect rect = option->rect;
switch (control) {
case CC_ToolButton:
if (const auto toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
bool reverse = option->direction == Qt::RightToLeft;
- bool drawborder = (widget && widget->property("showborder").toBool());
+ const bool drawborder =
+ (widget && widget->property(StyleHelper::C_SHOW_BORDER).toBool());
if (drawborder)
drawButtonSeparator(painter, rect, reverse);
@@ -1078,7 +1717,7 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
QStyleOptionToolButton label = *toolbutton;
label.palette = panelPalette(option->palette, lightColored(widget));
- if (widget && widget->property("highlightWidget").toBool()) {
+ if (widget && widget->property(StyleHelper::C_HIGHLIGHT_WIDGET).toBool()) {
label.palette.setColor(QPalette::ButtonText,
creatorTheme()->color(Theme::IconsWarningToolBarColor));
}
@@ -1105,7 +1744,7 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
tool.rect = tool.rect.adjusted(2, 2, -2, -2);
drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
} else if (toolbutton->features & QStyleOptionToolButton::HasMenu
- && widget && !widget->property("noArrow").toBool()) {
+ && widget && !widget->property(StyleHelper::C_NO_ARROW).toBool()) {
int arrowSize = 6;
QRect ir = toolbutton->rect.adjusted(1, 1, -1, -1);
QStyleOptionToolButton newBtn = *toolbutton;
@@ -1122,9 +1761,12 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
painter->save();
bool isEmpty = cb->currentText.isEmpty() && cb->currentIcon.isNull();
bool reverse = option->direction == Qt::RightToLeft;
- bool drawborder = !(widget && widget->property("hideborder").toBool());
- bool drawleftborder = (widget && widget->property("drawleftborder").toBool());
- bool alignarrow = !(widget && widget->property("alignarrow").toBool());
+ const bool drawborder =
+ !(widget && widget->property(StyleHelper::C_HIDE_BORDER).toBool());
+ const bool drawleftborder =
+ (widget && widget->property(StyleHelper::C_DRAW_LEFT_BORDER).toBool());
+ const bool alignarrow =
+ !(widget && widget->property(StyleHelper::C_ALIGN_ARROW).toBool());
if (drawborder) {
drawButtonSeparator(painter, rect, reverse);
@@ -1178,7 +1820,170 @@ void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOpti
painter->restore();
}
break;
+ case CC_Slider:
+ if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
+ QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
+
+ bool horizontal = slider->orientation == Qt::Horizontal;
+ bool ticksAbove = slider->tickPosition & QSlider::TicksAbove;
+ bool ticksBelow = slider->tickPosition & QSlider::TicksBelow;
+ bool enabled = option->state & QStyle::State_Enabled;
+ bool grooveHover = slider->activeSubControls & SC_SliderGroove;
+ bool handleHover = slider->activeSubControls & SC_SliderHandle;
+ bool interaction = option->state & State_Sunken;
+ bool activeFocus = option->state & State_HasFocus && option->state & State_KeyboardFocusChange;
+
+ int sliderPaintingOffset = horizontal
+ ? handle.center().x()
+ : handle.center().y();
+
+ int borderRadius = 4;
+
+ painter->save();
+ painter->setRenderHint(QPainter::RenderHint::Antialiasing);
+
+ int lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget);
+ Theme::Color themeframeColor = enabled
+ ? interaction
+ ? Theme::DSstateControlBackgroundColor_hover // Pressed
+ : grooveHover
+ ? Theme::DSstateSeparatorColor // GrooveHover
+ : Theme::DSpopupBackground // Idle
+ : Theme::DSpopupBackground; // Disabled
+
+ QColor frameColor = creatorTheme()->color(themeframeColor);
+
+ if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
+ Theme::Color bgPlusColor = enabled
+ ? interaction
+ ? Theme::DSstateControlBackgroundColor_hover // Pressed
+ : grooveHover
+ ? Theme::DSstateSeparatorColor // GrooveHover
+ : Theme::DStoolbarBackground // Idle
+ : Theme::DStoolbarBackground; // Disabled
+ Theme::Color bgMinusColor = Theme::DSpopupBackground;
+
+ QRect minusRect(groove);
+ QRect plusRect(groove);
+ if (horizontal) {
+ if (slider->upsideDown) {
+ minusRect.setLeft(sliderPaintingOffset);
+ plusRect.setRight(sliderPaintingOffset);
+ } else {
+ minusRect.setRight(sliderPaintingOffset);
+ plusRect.setLeft(sliderPaintingOffset);
+ }
+ } else {
+ if (slider->upsideDown) {
+ minusRect.setBottom(sliderPaintingOffset);
+ plusRect.setTop(sliderPaintingOffset);
+ } else {
+ minusRect.setTop(sliderPaintingOffset);
+ plusRect.setBottom(sliderPaintingOffset);
+ }
+ }
+
+ painter->save();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(creatorTheme()->color(bgPlusColor));
+ painter->drawRoundedRect(plusRect, borderRadius, borderRadius);
+ painter->setBrush(creatorTheme()->color(bgMinusColor));
+ painter->drawRoundedRect(minusRect, borderRadius, borderRadius);
+ painter->restore();
+ }
+
+ if (option->subControls & SC_SliderTickmarks) {
+ Theme::Color tickPen = enabled
+ ? activeFocus
+ ? Theme::DSstateBackgroundColor_hover
+ : Theme::DSBackgroundColorAlternate
+ : Theme::DScontrolBackgroundDisabled;
+
+ painter->setPen(tickPen);
+ int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget);
+ int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+ if (interval <= 0) {
+ interval = slider->singleStep;
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+ if (interval <= 0)
+ interval = 1;
+
+ int v = slider->minimum;
+ int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ int pos = sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, (horizontal
+ ? slider->rect.width()
+ : slider->rect.height()) - len,
+ slider->upsideDown) + len / 2;
+ int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0);
+
+ if (horizontal) {
+ if (ticksAbove) {
+ painter->drawLine(pos, slider->rect.top() + extra,
+ pos, slider->rect.top() + tickSize);
+ }
+ if (ticksBelow) {
+ painter->drawLine(pos, slider->rect.bottom() - extra,
+ pos, slider->rect.bottom() - tickSize);
+ }
+ } else {
+ if (ticksAbove) {
+ painter->drawLine(slider->rect.left() + extra, pos,
+ slider->rect.left() + tickSize, pos);
+ }
+ if (ticksBelow) {
+ painter->drawLine(slider->rect.right() - extra, pos,
+ slider->rect.right() - tickSize, pos);
+ }
+ }
+ // in the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ }
+
+ // draw handle
+ if ((option->subControls & SC_SliderHandle) ) {
+ Theme::Color handleColor = enabled
+ ? interaction
+ ? Theme::DSinteraction // Interaction
+ : grooveHover || handleHover
+ ? Theme::DStabActiveText // Hover
+ : Theme::PalettePlaceholderText // Idle
+ : Theme::DStoolbarIcon_blocked; // Disabled
+
+ int halfSliderThickness = horizontal
+ ? handle.width() / 2
+ : handle.height() / 2;
+ painter->setBrush(creatorTheme()->color(handleColor));
+ painter->setPen(Qt::NoPen);
+ painter->drawRoundedRect(handle,
+ halfSliderThickness,
+ halfSliderThickness);
+ }
+
+ if (groove.isValid()) {
+ painter->setBrush(Qt::NoBrush);
+ painter->setPen(QPen(frameColor, lineWidth));
+ painter->drawRoundedRect(groove, borderRadius, borderRadius);
+ }
+ painter->restore();
+ }
+ break;
default:
QProxyStyle::drawComplexControl(control, option, painter, widget);
break;
diff --git a/src/plugins/coreplugin/manhattanstyle.h b/src/plugins/coreplugin/manhattanstyle.h
index 40b293cf71..07079c7d2d 100644
--- a/src/plugins/coreplugin/manhattanstyle.h
+++ b/src/plugins/coreplugin/manhattanstyle.h
@@ -30,7 +30,6 @@ public:
QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget = nullptr) const override;
QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override;
int styleHint(StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override;
- QRect itemRect(QPainter *p, const QRect &r, int flags, bool enabled, const QPixmap *pixmap, const QString &text, int len = -1) const;
QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const override;
int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const override;
@@ -45,6 +44,27 @@ public:
void unpolish(QApplication *app) override;
private:
+ void drawPrimitiveForPanelWidget(PrimitiveElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const;
+
+ void drawPrimitiveForQmlEditor(PrimitiveElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget) const;
+
+ void drawControlForQmlEditor(ControlElement element,
+ const QStyleOption *option,
+ QPainter *painter,
+ const QWidget *widget = nullptr) const;
+
+ void drawQmlEditorIcon(PrimitiveElement element,
+ const QStyleOption *option,
+ const char *propertyName,
+ QPainter *painter,
+ const QWidget *widget = nullptr) const;
+
static void drawButtonSeparator(QPainter *painter, const QRect &rect, bool reverse);
ManhattanStylePrivate *d;
diff --git a/src/plugins/coreplugin/mimetypemagicdialog.cpp b/src/plugins/coreplugin/mimetypemagicdialog.cpp
index 475cc57dd3..2a55539bf7 100644
--- a/src/plugins/coreplugin/mimetypemagicdialog.cpp
+++ b/src/plugins/coreplugin/mimetypemagicdialog.cpp
@@ -78,7 +78,7 @@ MimeTypeMagicDialog::MimeTypeMagicDialog(QWidget *parent) :
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp
index a4efe1c30e..116fcf4a06 100644
--- a/src/plugins/coreplugin/mimetypesettings.cpp
+++ b/src/plugins/coreplugin/mimetypesettings.cpp
@@ -82,8 +82,6 @@ public:
// MimeTypeSettingsModel
class MimeTypeSettingsModel : public QAbstractTableModel
{
- Q_OBJECT
-
public:
enum class Role {
DefaultHandler = Qt::UserRole
@@ -227,8 +225,6 @@ void MimeTypeSettingsModel::resetUserDefaults()
// MimeTypeSettingsPrivate
class MimeTypeSettingsPrivate : public QObject
{
- Q_OBJECT
-
public:
MimeTypeSettingsPrivate();
~MimeTypeSettingsPrivate() override;
@@ -291,6 +287,31 @@ MimeTypeSettingsPrivate::MimeTypeSettingsPrivate()
MimeTypeSettingsPrivate::~MimeTypeSettingsPrivate() = default;
+class MimeTypeSettingsWidget : public IOptionsPageWidget
+{
+public:
+ MimeTypeSettingsWidget(MimeTypeSettingsPrivate *d)
+ : d(d)
+ {
+ d->configureUi(this);
+ }
+
+ void apply() final
+ {
+ MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(d->m_pendingModifiedMimeTypes);
+ Core::Internal::setUserPreferredEditorTypes(d->m_model->m_userDefault);
+ d->m_pendingModifiedMimeTypes.clear();
+ d->m_model->load();
+ }
+
+ void finish() final
+ {
+ d->m_pendingModifiedMimeTypes.clear();
+ }
+
+ MimeTypeSettingsPrivate *d;
+};
+
void MimeTypeSettingsPrivate::configureUi(QWidget *w)
{
auto filterLineEdit = new FancyLineEdit;
@@ -713,6 +734,7 @@ MimeTypeSettings::MimeTypeSettings()
setId(Constants::SETTINGS_ID_MIMETYPES);
setDisplayName(Tr::tr("MIME Types"));
setCategory(Constants::SETTINGS_CATEGORY_CORE);
+ setWidgetCreator([this] { return new MimeTypeSettingsWidget(d); });
}
MimeTypeSettings::~MimeTypeSettings()
@@ -720,29 +742,6 @@ MimeTypeSettings::~MimeTypeSettings()
delete d;
}
-QWidget *MimeTypeSettings::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new QWidget;
- d->configureUi(d->m_widget);
- }
- return d->m_widget;
-}
-
-void MimeTypeSettings::apply()
-{
- MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(d->m_pendingModifiedMimeTypes);
- Core::Internal::setUserPreferredEditorTypes(d->m_model->m_userDefault);
- d->m_pendingModifiedMimeTypes.clear();
- d->m_model->load();
-}
-
-void MimeTypeSettings::finish()
-{
- d->m_pendingModifiedMimeTypes.clear();
- delete d->m_widget;
-}
-
void MimeTypeSettings::restoreSettings()
{
MimeTypeSettingsPrivate::UserMimeTypeHash mimetypes
@@ -784,5 +783,3 @@ void MimeEditorDelegate::setModelData(QWidget *editor,
}
} // Core::Internal
-
-#include "mimetypesettings.moc"
diff --git a/src/plugins/coreplugin/mimetypesettings.h b/src/plugins/coreplugin/mimetypesettings.h
index f676de53e6..0127e22768 100644
--- a/src/plugins/coreplugin/mimetypesettings.h
+++ b/src/plugins/coreplugin/mimetypesettings.h
@@ -11,17 +11,12 @@ class MimeTypeSettingsPrivate;
class MimeTypeSettings : public IOptionsPage
{
- Q_OBJECT
-
public:
MimeTypeSettings();
~MimeTypeSettings() override;
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
static void restoreSettings();
+
private:
MimeTypeSettingsPrivate *d;
};
diff --git a/src/plugins/coreplugin/minisplitter.cpp b/src/plugins/coreplugin/minisplitter.cpp
index d589a7dfc6..e4e9db129b 100644
--- a/src/plugins/coreplugin/minisplitter.cpp
+++ b/src/plugins/coreplugin/minisplitter.cpp
@@ -84,7 +84,7 @@ MiniSplitter::MiniSplitter(QWidget *parent, SplitterStyle style)
{
setHandleWidth(1);
setChildrenCollapsible(false);
- setProperty("minisplitter", true);
+ setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
}
MiniSplitter::MiniSplitter(Qt::Orientation orientation, QWidget *parent, SplitterStyle style)
@@ -93,7 +93,7 @@ MiniSplitter::MiniSplitter(Qt::Orientation orientation, QWidget *parent, Splitte
{
setHandleWidth(1);
setChildrenCollapsible(false);
- setProperty("minisplitter", true);
+ setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
}
/*!
diff --git a/src/plugins/coreplugin/navigationsubwidget.cpp b/src/plugins/coreplugin/navigationsubwidget.cpp
index 1d5b0e4bb6..838635c7b2 100644
--- a/src/plugins/coreplugin/navigationsubwidget.cpp
+++ b/src/plugins/coreplugin/navigationsubwidget.cpp
@@ -11,6 +11,7 @@
#include "navigationwidget.h"
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QHBoxLayout>
@@ -53,7 +54,7 @@ NavigationSubWidget::NavigationSubWidget(NavigationWidget *parentWidget, int pos
splitAction->setIcon(Utils::Icons::SPLIT_HORIZONTAL_TOOLBAR.icon());
splitAction->setToolTip(Tr::tr("Split"));
splitAction->setPopupMode(QToolButton::InstantPopup);
- splitAction->setProperty("noArrow", true);
+ splitAction->setProperty(StyleHelper::C_NO_ARROW, true);
m_splitMenu = new QMenu(splitAction);
splitAction->setMenu(m_splitMenu);
connect(m_splitMenu, &QMenu::aboutToShow, this, &NavigationSubWidget::populateSplitMenu);
diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp
index a249686f9e..906a940a40 100644
--- a/src/plugins/coreplugin/outputpanemanager.cpp
+++ b/src/plugins/coreplugin/outputpanemanager.cpp
@@ -803,6 +803,13 @@ QSize OutputPaneToggleButton::sizeHint() const
return s;
}
+static QRect bgRect(const QRect &widgetRect)
+{
+ // Removes/compensates the left and right margins of StyleHelper::drawPanelBgRect
+ return StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleCompact
+ ? widgetRect : widgetRect.adjusted(-2, 0, 2, 0);
+}
+
void OutputPaneToggleButton::paintEvent(QPaintEvent*)
{
const QFontMetrics fm = fontMetrics();
@@ -824,7 +831,7 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
c = Theme::BackgroundColorSelected;
if (c != Theme::BackgroundColorDark)
- p.fillRect(rect(), creatorTheme()->color(c));
+ StyleHelper::drawPanelBgRect(&p, bgRect(rect()), creatorTheme()->color(c));
} else {
const QImage *image = nullptr;
if (isDown()) {
@@ -860,9 +867,10 @@ void OutputPaneToggleButton::paintEvent(QPaintEvent*)
{
QColor c = creatorTheme()->color(Theme::OutputPaneButtonFlashColor);
c.setAlpha (m_flashTimer->currentFrame());
- QRect r = creatorTheme()->flag(Theme::FlatToolBars)
- ? rect() : rect().adjusted(numberAreaWidth(), 1, -1, -1);
- p.fillRect(r, c);
+ if (creatorTheme()->flag(Theme::FlatToolBars))
+ StyleHelper::drawPanelBgRect(&p, bgRect(rect()), c);
+ else
+ p.fillRect(rect().adjusted(numberAreaWidth(), 1, -1, -1), c);
}
p.setFont(font());
@@ -922,13 +930,7 @@ OutputPaneManageButton::OutputPaneManageButton()
{
setFocusPolicy(Qt::NoFocus);
setCheckable(true);
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
-}
-
-QSize OutputPaneManageButton::sizeHint() const
-{
- ensurePolished();
- return QSize(numberAreaWidth(), 16);
+ setFixedWidth(StyleHelper::toolbarStyle() == Utils::StyleHelper::ToolbarStyleCompact ? 17 : 21);
}
void OutputPaneManageButton::paintEvent(QPaintEvent*)
@@ -941,7 +943,9 @@ void OutputPaneManageButton::paintEvent(QPaintEvent*)
QStyle *s = style();
QStyleOption arrowOpt;
arrowOpt.initFrom(this);
- arrowOpt.rect = QRect(6, rect().center().y() - 3, 8, 8);
+ constexpr int arrowSize = 8;
+ arrowOpt.rect = QRect(0, 0, arrowSize, arrowSize);
+ arrowOpt.rect.moveCenter(rect().center());
arrowOpt.rect.translate(0, -3);
s->drawPrimitive(QStyle::PE_IndicatorArrowUp, &arrowOpt, &p, this);
arrowOpt.rect.translate(0, 6);
diff --git a/src/plugins/coreplugin/outputpanemanager.h b/src/plugins/coreplugin/outputpanemanager.h
index aa3a550a28..7dc7085d53 100644
--- a/src/plugins/coreplugin/outputpanemanager.h
+++ b/src/plugins/coreplugin/outputpanemanager.h
@@ -134,7 +134,6 @@ class OutputPaneManageButton : public QToolButton
Q_OBJECT
public:
OutputPaneManageButton();
- QSize sizeHint() const override;
void paintEvent(QPaintEvent*) override;
};
diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp
index 9af0835380..af6c11825d 100644
--- a/src/plugins/coreplugin/patchtool.cpp
+++ b/src/plugins/coreplugin/patchtool.cpp
@@ -7,7 +7,7 @@
#include "patchtool.h"
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QMessageBox>
@@ -76,7 +76,7 @@ static bool runPatchHelper(const QByteArray &input, const FilePath &workingDirec
return false;
}
- QtcProcess patchProcess;
+ Process patchProcess;
if (!workingDirectory.isEmpty())
patchProcess.setWorkingDirectory(workingDirectory);
Environment env = Environment::systemEnvironment();
diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp
index 1298238722..f6ea25e719 100644
--- a/src/plugins/coreplugin/plugindialog.cpp
+++ b/src/plugins/coreplugin/plugindialog.cpp
@@ -40,6 +40,7 @@ PluginDialog::PluginDialog(QWidget *parent)
auto filterLayout = new QHBoxLayout;
vl->addLayout(filterLayout);
auto filterEdit = new Utils::FancyLineEdit(this);
+ filterEdit->setFocus();
filterEdit->setFiltering(true);
connect(filterEdit, &Utils::FancyLineEdit::filterChanged,
m_view, &ExtensionSystem::PluginView::setFilter);
diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp
index 6547c78835..c5694016d4 100644
--- a/src/plugins/coreplugin/plugininstallwizard.cpp
+++ b/src/plugins/coreplugin/plugininstallwizard.cpp
@@ -11,13 +11,13 @@
#include <extensionsystem/pluginspec.h>
#include <utils/archive.h>
+#include <utils/async.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/infolabel.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <utils/wizard.h>
#include <utils/wizardpage.h>
@@ -221,8 +221,8 @@ public:
m_label->setText(Tr::tr("There was an error while unarchiving."));
}
} else { // unarchiving was successful, run a check
- m_archiveCheck = Utils::runAsync(
- [this](QFutureInterface<ArchiveIssue> &fi) { return checkContents(fi); });
+ m_archiveCheck = Utils::asyncRun([this](QPromise<ArchiveIssue> &promise)
+ { return checkContents(promise); });
Utils::onFinished(m_archiveCheck, this, [this](const QFuture<ArchiveIssue> &f) {
m_cancelButton->setVisible(false);
m_cancelButton->disconnect();
@@ -248,7 +248,7 @@ public:
}
// Async. Result is set if any issue was found.
- void checkContents(QFutureInterface<ArchiveIssue> &fi)
+ void checkContents(QPromise<ArchiveIssue> &promise)
{
QTC_ASSERT(m_tempDir.get(), return );
@@ -260,7 +260,7 @@ public:
QDir::Files | QDir::NoSymLinks,
QDirIterator::Subdirectories);
while (it.hasNext()) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
it.next();
PluginSpec *spec = PluginSpec::read(it.filePath());
@@ -275,18 +275,18 @@ public:
});
if (found != dependencies.constEnd()) {
if (!coreplugin->provides(found->name, found->version)) {
- fi.reportResult({Tr::tr("Plugin requires an incompatible version of %1 (%2).")
- .arg(Constants::IDE_DISPLAY_NAME)
- .arg(found->version),
- InfoLabel::Error});
+ promise.addResult(ArchiveIssue{
+ Tr::tr("Plugin requires an incompatible version of %1 (%2).")
+ .arg(Constants::IDE_DISPLAY_NAME).arg(found->version),
+ InfoLabel::Error});
return;
}
}
return; // successful / no error
}
}
- fi.reportResult({Tr::tr("Did not find %1 plugin.").arg(Constants::IDE_DISPLAY_NAME),
- InfoLabel::Error});
+ promise.addResult(ArchiveIssue{Tr::tr("Did not find %1 plugin.")
+ .arg(Constants::IDE_DISPLAY_NAME), InfoLabel::Error});
}
void cleanupPage() final
@@ -403,7 +403,7 @@ static std::function<void(FilePath)> postCopyOperation()
return;
// On macOS, downloaded files get a quarantine flag, remove it, otherwise it is a hassle
// to get it loaded as a plugin in Qt Creator.
- QtcProcess xattr;
+ Process xattr;
xattr.setTimeoutS(1);
xattr.setCommand({"/usr/bin/xattr", {"-d", "com.apple.quarantine", filePath.absoluteFilePath().toString()}});
xattr.runBlocking();
diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp
index 9a545561a7..57b9dcbbfb 100644
--- a/src/plugins/coreplugin/progressmanager/processprogress.cpp
+++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp
@@ -6,8 +6,8 @@
#include "progressmanager.h"
#include "../coreplugintr.h"
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QFutureWatcher>
@@ -18,13 +18,13 @@ namespace Core {
class ProcessProgressPrivate : public QObject
{
public:
- explicit ProcessProgressPrivate(ProcessProgress *progress, QtcProcess *process);
+ explicit ProcessProgressPrivate(ProcessProgress *progress, Process *process);
~ProcessProgressPrivate();
QString displayName() const;
void parseProgress(const QString &inputText);
- QtcProcess *m_process = nullptr;
+ Process *m_process = nullptr;
ProgressParser m_parser = {};
QFutureWatcher<void> m_watcher;
QFutureInterface<void> m_futureInterface;
@@ -33,7 +33,7 @@ public:
FutureProgress::KeepOnFinishType m_keep = FutureProgress::HideOnFinish;
};
-ProcessProgressPrivate::ProcessProgressPrivate(ProcessProgress *progress, QtcProcess *process)
+ProcessProgressPrivate::ProcessProgressPrivate(ProcessProgress *progress, Process *process)
: QObject(progress)
, m_process(process)
{
@@ -74,17 +74,17 @@ void ProcessProgressPrivate::parseProgress(const QString &inputText)
\brief The ProcessProgress class is responsible for showing progress of the running process.
It's possible to cancel the running process automatically after pressing a small 'x'
- indicator on progress panel. In this case QtcProcess::stop() method is being called.
+ indicator on progress panel. In this case Process::stop() method is being called.
*/
-ProcessProgress::ProcessProgress(QtcProcess *process)
+ProcessProgress::ProcessProgress(Process *process)
: QObject(process)
, d(new ProcessProgressPrivate(this, process))
{
connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, [this] {
d->m_process->stop(); // TODO: See TaskProgress::setAutoStopOnCancel
});
- connect(d->m_process, &QtcProcess::starting, this, [this] {
+ connect(d->m_process, &Process::starting, this, [this] {
d->m_futureInterface = QFutureInterface<void>();
d->m_futureInterface.setProgressRange(0, 1);
d->m_watcher.setFuture(d->m_futureInterface.future());
@@ -100,7 +100,7 @@ ProcessProgress::ProcessProgress(QtcProcess *process)
}
d->m_futureProgress->setKeepOnFinish(d->m_keep);
});
- connect(d->m_process, &QtcProcess::done, this, [this] {
+ connect(d->m_process, &Process::done, this, [this] {
if (d->m_process->result() != ProcessResult::FinishedWithSuccess)
d->m_futureInterface.reportCanceled();
d->m_futureInterface.reportFinished();
@@ -124,9 +124,9 @@ void ProcessProgress::setKeepOnFinish(FutureProgress::KeepOnFinishType keepType)
void ProcessProgress::setProgressParser(const ProgressParser &parser)
{
if (d->m_parser) {
- disconnect(d->m_process, &QtcProcess::textOnStandardOutput,
+ disconnect(d->m_process, &Process::textOnStandardOutput,
d.get(), &ProcessProgressPrivate::parseProgress);
- disconnect(d->m_process, &QtcProcess::textOnStandardError,
+ disconnect(d->m_process, &Process::textOnStandardError,
d.get(), &ProcessProgressPrivate::parseProgress);
}
d->m_parser = parser;
@@ -137,9 +137,9 @@ void ProcessProgress::setProgressParser(const ProgressParser &parser)
qWarning() << "Setting progress parser on a process without changing process' "
"text channel mode is no-op.");
- connect(d->m_process, &QtcProcess::textOnStandardOutput,
+ connect(d->m_process, &Process::textOnStandardOutput,
d.get(), &ProcessProgressPrivate::parseProgress);
- connect(d->m_process, &QtcProcess::textOnStandardError,
+ connect(d->m_process, &Process::textOnStandardError,
d.get(), &ProcessProgressPrivate::parseProgress);
}
diff --git a/src/plugins/coreplugin/progressmanager/processprogress.h b/src/plugins/coreplugin/progressmanager/processprogress.h
index fd2f64caf0..e91c0b7b3c 100644
--- a/src/plugins/coreplugin/progressmanager/processprogress.h
+++ b/src/plugins/coreplugin/progressmanager/processprogress.h
@@ -9,7 +9,7 @@
#include <QObject>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Core {
@@ -20,7 +20,7 @@ class ProcessProgressPrivate;
class CORE_EXPORT ProcessProgress : public QObject
{
public:
- ProcessProgress(Utils::QtcProcess *process); // Makes ProcessProgress a child of process
+ ProcessProgress(Utils::Process *process); // Makes ProcessProgress a child of process
~ProcessProgress() override;
void setDisplayName(const QString &name);
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
index efc9731221..5504f8fe0c 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
@@ -109,7 +109,7 @@ namespace Core {
start a task concurrently in a different thread.
QtConcurrent has several different functions to run e.g.
a class function in a different thread. Qt Creator itself
- adds a few more in \c{src/libs/qtconcurrent/runextensions.h}.
+ adds a few more in \c{src/libs/utils/async.h}.
The QtConcurrent functions to run a concurrent task return a
\c QFuture object. This is what you want to give the
ProgressManager in the addTask() function.
@@ -740,6 +740,33 @@ FutureProgress *ProgressManager::addTimedTask(const QFutureInterface<void> &futu
return fp;
}
+FutureProgress *ProgressManager::addTimedTask(const QFuture<void> &future, const QString &title,
+ Id type, int expectedSeconds, ProgressFlags flags)
+{
+ QFutureInterface<void> dummyFutureInterface;
+ QFuture<void> dummyFuture = dummyFutureInterface.future();
+ FutureProgress *fp = m_instance->doAddTask(dummyFuture, title, type, flags);
+ (void) new ProgressTimer(dummyFutureInterface, expectedSeconds, fp);
+
+ QFutureWatcher<void> *dummyWatcher = new QFutureWatcher<void>(fp);
+ connect(dummyWatcher, &QFutureWatcher<void>::canceled, dummyWatcher, [future] {
+ QFuture<void> mutableFuture = future;
+ mutableFuture.cancel();
+ });
+ dummyWatcher->setFuture(dummyFuture);
+
+ QFutureWatcher<void> *origWatcher = new QFutureWatcher<void>(fp);
+ connect(origWatcher, &QFutureWatcher<void>::finished, origWatcher, [future, dummyFutureInterface] {
+ QFutureInterface<void> mutableDummyFutureInterface = dummyFutureInterface;
+ if (future.isCanceled())
+ mutableDummyFutureInterface.reportCanceled();
+ mutableDummyFutureInterface.reportFinished();
+ });
+ origWatcher->setFuture(future);
+
+ return fp;
+}
+
/*!
Shows the given \a text in a platform dependent way in the application
icon in the system's task bar or dock. This is used to show the number
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.h b/src/plugins/coreplugin/progressmanager/progressmanager.h
index 78c1b771ae..8c51bf4ccd 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager.h
+++ b/src/plugins/coreplugin/progressmanager/progressmanager.h
@@ -40,6 +40,8 @@ public:
Utils::Id type, ProgressFlags flags = {});
static FutureProgress *addTimedTask(const QFutureInterface<void> &fi, const QString &title,
Utils::Id type, int expectedSeconds, ProgressFlags flags = {});
+ static FutureProgress *addTimedTask(const QFuture<void> &future, const QString &title,
+ Utils::Id type, int expectedSeconds, ProgressFlags flags = {});
static void setApplicationLabel(const QString &text);
public slots:
diff --git a/src/plugins/coreplugin/progressmanager/progressview.cpp b/src/plugins/coreplugin/progressmanager/progressview.cpp
index 498ec8ae7b..45b10286dc 100644
--- a/src/plugins/coreplugin/progressmanager/progressview.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressview.cpp
@@ -5,11 +5,19 @@
#include "../coreplugintr.h"
+#include <utils/icon.h>
+#include <utils/overlaywidget.h>
+
#include <QApplication>
#include <QEvent>
#include <QMouseEvent>
+#include <QPainter>
#include <QVBoxLayout>
+using namespace Utils;
+
+const int PIN_SIZE = 12;
+
namespace Core::Internal {
ProgressView::ProgressView(QWidget *parent)
@@ -21,15 +29,29 @@ ProgressView::ProgressView(QWidget *parent)
m_layout->setSpacing(0);
m_layout->setSizeConstraint(QLayout::SetFixedSize);
setWindowTitle(Tr::tr("Processes"));
+
+ auto pinButton = new OverlayWidget(this);
+ pinButton->attachToWidget(this);
+ pinButton->setAttribute(Qt::WA_TransparentForMouseEvents, false); // override OverlayWidget
+ pinButton->setPaintFunction([](QWidget *that, QPainter &p, QPaintEvent *) {
+ static const QIcon icon = Icon({{":/utils/images/pinned_small.png", Theme::IconsBaseColor}},
+ Icon::Tint)
+ .icon();
+ QRect iconRect(0, 0, PIN_SIZE, PIN_SIZE);
+ iconRect.moveTopRight(that->rect().topRight());
+ icon.paint(&p, iconRect);
+ });
+ pinButton->setVisible(false);
+ pinButton->installEventFilter(this);
+ m_pinButton = pinButton;
}
ProgressView::~ProgressView() = default;
void ProgressView::addProgressWidget(QWidget *widget)
{
- if (m_layout->count() == 0)
- m_anchorBottomRight = {}; // reset temporarily user-moved progress details
m_layout->insertWidget(0, widget);
+ m_pinButton->raise();
}
void ProgressView::removeProgressWidget(QWidget *widget)
@@ -63,9 +85,12 @@ bool ProgressView::event(QEvent *event)
reposition();
} else if (event->type() == QEvent::Enter) {
m_hovered = true;
+ if (m_anchorBottomRight != QPoint())
+ m_pinButton->setVisible(true);
emit hoveredChanged(m_hovered);
} else if (event->type() == QEvent::Leave) {
m_hovered = false;
+ m_pinButton->setVisible(false);
emit hoveredChanged(m_hovered);
} else if (event->type() == QEvent::Show) {
m_anchorBottomRight = {}; // reset temporarily user-moved progress details
@@ -78,6 +103,16 @@ bool ProgressView::eventFilter(QObject *obj, QEvent *event)
{
if ((obj == parentWidget() || obj == m_referenceWidget) && event->type() == QEvent::Resize)
reposition();
+ if (obj == m_pinButton && event->type() == QEvent::MouseButtonRelease) {
+ auto me = static_cast<QMouseEvent *>(event);
+ if (me->button() == Qt::LeftButton
+ && QRectF(m_pinButton->width() - PIN_SIZE, 0, PIN_SIZE, PIN_SIZE)
+ .contains(me->position())) {
+ me->accept();
+ m_anchorBottomRight = {};
+ reposition();
+ }
+ }
return false;
}
@@ -133,6 +168,9 @@ void ProgressView::reposition()
{
if (!parentWidget() || !m_referenceWidget)
return;
+
+ m_pinButton->setVisible(m_anchorBottomRight != QPoint() && m_hovered);
+
move(boundedInParent(this, topRightReferenceInParent() + m_anchorBottomRight, parentWidget())
- rect().bottomRight());
}
diff --git a/src/plugins/coreplugin/progressmanager/progressview.h b/src/plugins/coreplugin/progressmanager/progressview.h
index 37cfa24826..e0b42c8547 100644
--- a/src/plugins/coreplugin/progressmanager/progressview.h
+++ b/src/plugins/coreplugin/progressmanager/progressview.h
@@ -44,6 +44,7 @@ private:
QVBoxLayout *m_layout;
QWidget *m_referenceWidget = nullptr;
+ QWidget *m_pinButton = nullptr;
// dragging
std::optional<QPointF> m_clickPosition;
diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.cpp b/src/plugins/coreplugin/progressmanager/taskprogress.cpp
index 6c7ee0bbb7..c82ec339c5 100644
--- a/src/plugins/coreplugin/progressmanager/taskprogress.cpp
+++ b/src/plugins/coreplugin/progressmanager/taskprogress.cpp
@@ -6,14 +6,16 @@
#include "futureprogress.h"
#include "progressmanager.h"
+#include <solutions/tasking/tasktree.h>
+
#include <utils/mathutils.h>
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <QFutureWatcher>
#include <QTimer>
using namespace Utils;
+using namespace Tasking;
namespace Core {
diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.h b/src/plugins/coreplugin/progressmanager/taskprogress.h
index e15967fa2b..d82fc433d5 100644
--- a/src/plugins/coreplugin/progressmanager/taskprogress.h
+++ b/src/plugins/coreplugin/progressmanager/taskprogress.h
@@ -9,7 +9,7 @@
#include <QObject>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace Core {
@@ -20,7 +20,7 @@ class CORE_EXPORT TaskProgress : public QObject
Q_OBJECT
public:
- TaskProgress(Utils::TaskTree *taskTree); // Makes TaskProgress a child of task tree
+ TaskProgress(Tasking::TaskTree *taskTree); // Makes TaskProgress a child of task tree
~TaskProgress() override;
void setId(Utils::Id id);
diff --git a/src/plugins/coreplugin/statusbarmanager.cpp b/src/plugins/coreplugin/statusbarmanager.cpp
index 4841337975..83d910867b 100644
--- a/src/plugins/coreplugin/statusbarmanager.cpp
+++ b/src/plugins/coreplugin/statusbarmanager.cpp
@@ -59,7 +59,6 @@ static void createStatusBarManager()
m_statusBarWidgets.append(w);
QWidget *w2 = createWidget(m_splitter);
- w2->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
m_splitter->addWidget(w2);
// second
w = createWidget(w2);
diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp
index f34e7fb5b7..5d9a5fd3f9 100644
--- a/src/plugins/coreplugin/systemsettings.cpp
+++ b/src/plugins/coreplugin/systemsettings.cpp
@@ -170,19 +170,18 @@ public:
{Tr::tr("When files are externally modified:"), Span(2, Row{m_reloadBehavior, st})});
form.addRow(
{m_autoSaveCheckBox, Span(2, Row{Tr::tr("Interval:"), m_autoSaveInterval, st})});
- form.addRow(Span(3, m_autoSaveRefactoringCheckBox));
+ form.addRow({Span(3, m_autoSaveRefactoringCheckBox)});
form.addRow({m_autoSuspendCheckBox,
Span(2, Row{autoSuspendLabel, m_autoSuspendMinDocumentCount, st})});
- form.addRow(Span(3, Row{m_warnBeforeOpeningBigFiles, m_bigFilesLimitSpinBox, st}));
- form.addRow(Span(3,
+ form.addRow({Span(3, Row{m_warnBeforeOpeningBigFiles, m_bigFilesLimitSpinBox, st})});
+ form.addRow({Span(3,
Row{Tr::tr("Maximum number of entries in \"Recent Files\":"),
m_maxRecentFilesSpinBox,
- st}));
- form.addRow(m_askBeforeExitCheckBox);
+ st})});
+ form.addRow({m_askBeforeExitCheckBox});
#ifdef ENABLE_CRASHPAD
- form.addRow(
- Span(3, Row{m_enableCrashReportingCheckBox, helpCrashReportingButton, st}));
- form.addRow(Span(3, Row{m_clearCrashReportsButton, m_crashReportsSizeText, st}));
+ form.addRow({Span(3, Row{m_enableCrashReportingCheckBox, helpCrashReportingButton, st})});
+ form.addRow({Span(3, Row{m_clearCrashReportsButton, m_crashReportsSizeText, st})});
#endif
Column {
@@ -438,9 +437,9 @@ void SystemSettingsWidget::resetFileBrowser()
void SystemSettingsWidget::updatePath()
{
- EnvironmentChange change;
- change.addAppendToPath(VcsManager::additionalToolsPath());
- m_patchChooser->setEnvironmentChange(change);
+ Environment env;
+ env.appendToPath(VcsManager::additionalToolsPath());
+ m_patchChooser->setEnvironment(env);
}
void SystemSettingsWidget::updateEnvironmentChangesLabel()
diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp
index 0c46ea98f3..d0b1c9f3f9 100644
--- a/src/plugins/coreplugin/welcomepagehelper.cpp
+++ b/src/plugins/coreplugin/welcomepagehelper.cpp
@@ -7,6 +7,7 @@
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
@@ -25,9 +26,11 @@
#include <qdrawutil.h>
+using namespace Utils;
+
namespace Core {
-using namespace Utils;
+using namespace WelcomePageHelpers;
static QColor themeColor(Theme::Color role)
{
@@ -123,6 +126,17 @@ SectionGridView::SectionGridView(QWidget *parent)
: GridView(parent)
{}
+void SectionGridView::setMaxRows(std::optional<int> max)
+{
+ m_maxRows = max;
+ updateGeometry();
+}
+
+std::optional<int> SectionGridView::maxRows() const
+{
+ return m_maxRows;
+}
+
bool SectionGridView::hasHeightForWidth() const
{
return true;
@@ -130,12 +144,39 @@ bool SectionGridView::hasHeightForWidth() const
int SectionGridView::heightForWidth(int width) const
{
- const int columnCount = width / Core::ListItemDelegate::GridItemWidth;
+ const int columnCount = qMax(1, width / Core::WelcomePageHelpers::GridItemWidth);
const int rowCount = (model()->rowCount() + columnCount - 1) / columnCount;
- return rowCount * Core::ListItemDelegate::GridItemHeight;
+ const int maxRowCount = m_maxRows ? std::min(*m_maxRows, rowCount) : rowCount;
+ return maxRowCount * Core::WelcomePageHelpers::GridItemHeight;
+}
+
+void SectionGridView::wheelEvent(QWheelEvent *e)
+{
+ if (m_maxRows) // circumvent scrolling of the list view
+ QWidget::wheelEvent(e);
+ else
+ GridView::wheelEvent(e);
}
-const QSize ListModel::defaultImageSize(214, 160);
+bool SectionGridView::event(QEvent *e)
+{
+ if (e->type() == QEvent::Resize) {
+ const auto itemsFit = [this](const QSize &size) {
+ const int maxColumns = std::max(size.width() / WelcomePageHelpers::GridItemWidth, 1);
+ const int maxRows = std::max(size.height() / WelcomePageHelpers::GridItemHeight, 1);
+ const int maxItems = maxColumns * maxRows;
+ const int items = model()->rowCount();
+ return maxItems >= items;
+ };
+ auto resizeEvent = static_cast<QResizeEvent *>(e);
+ const bool itemsCurrentyFit = itemsFit(size());
+ if (!resizeEvent->oldSize().isValid()
+ || itemsFit(resizeEvent->oldSize()) != itemsCurrentyFit) {
+ emit itemsFitChanged(itemsCurrentyFit);
+ }
+ }
+ return GridView::event(e);
+}
ListModel::ListModel(QObject *parent)
: QAbstractListModel(parent)
@@ -403,6 +444,7 @@ ListItemDelegate::ListItemDelegate()
: backgroundPrimaryColor(themeColor(Theme::Welcome_BackgroundPrimaryColor))
, backgroundSecondaryColor(themeColor(Theme::Welcome_BackgroundSecondaryColor))
, foregroundPrimaryColor(themeColor(Theme::Welcome_ForegroundPrimaryColor))
+ , foregroundSecondaryColor(themeColor(Theme::Welcome_ForegroundSecondaryColor))
, hoverColor(themeColor(Theme::Welcome_HoverColor))
, textColor(themeColor(Theme::Welcome_TextColor))
{
@@ -415,13 +457,14 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
const QRect rc = option.rect;
const QRect tileRect(0, 0, rc.width() - GridItemGap, rc.height() - GridItemGap);
- const QSize thumbnailBgSize = ListModel::defaultImageSize.grownBy(QMargins(1, 1, 1, 1));
+ const QSize thumbnailBgSize = GridItemImageSize.grownBy(QMargins(1, 1, 1, 1));
const QRect thumbnailBgRect((tileRect.width() - thumbnailBgSize.width()) / 2, GridItemGap,
thumbnailBgSize.width(), thumbnailBgSize.height());
const QRect textArea = tileRect.adjusted(GridItemGap, GridItemGap, -GridItemGap, -GridItemGap);
const bool hovered = option.state & QStyle::State_MouseOver;
+ constexpr int TagsSeparatorY = GridItemHeight - GridItemGap - 52;
constexpr int tagsBase = TagsSeparatorY + 17;
constexpr int shiftY = TagsSeparatorY - 16;
constexpr int nameY = TagsSeparatorY - 20;
@@ -524,7 +567,7 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
// The separator line below the example title.
const int ll = nameRect.height() + 3;
const QLine line = QLine(0, ll, textArea.width(), ll).translated(shiftedTextRect.topLeft());
- painter->setPen(foregroundPrimaryColor);
+ painter->setPen(foregroundSecondaryColor);
painter->setOpacity(animationProgress); // "fade in" separator line and description
painter->drawLine(line);
@@ -543,7 +586,7 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
}
// Separator line between text and 'Tags:' section
- painter->setPen(foregroundPrimaryColor);
+ painter->setPen(foregroundSecondaryColor);
painter->drawLine(QLineF(textArea.topLeft(), textArea.topRight())
.translated(0, TagsSeparatorY));
@@ -633,13 +676,9 @@ void ListItemDelegate::goon()
SectionedGridView::SectionedGridView(QWidget *parent)
: QStackedWidget(parent)
- , m_allItemsView(new Core::GridView(this))
{
- auto allItemsModel = new ListModel(this);
- allItemsModel->setPixmapFunction(m_pixmapFunction);
- // it just "borrows" the items from the section models:
- allItemsModel->setOwnsItems(false);
- m_filteredAllItemsModel = new Core::ListModelFilter(allItemsModel, this);
+ m_allItemsModel.reset(new ListModel);
+ m_allItemsModel->setPixmapFunction(m_pixmapFunction);
auto area = new QScrollArea(this);
area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -650,21 +689,23 @@ SectionedGridView::SectionedGridView(QWidget *parent)
auto sectionedView = new QWidget;
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
- layout->addStretch();
+ layout->addStretch(1);
sectionedView->setLayout(layout);
area->setWidget(sectionedView);
addWidget(area);
-
- m_allItemsView->setModel(m_filteredAllItemsModel);
- addWidget(m_allItemsView);
}
-SectionedGridView::~SectionedGridView() = default;
+SectionedGridView::~SectionedGridView()
+{
+ clear();
+}
void SectionedGridView::setItemDelegate(QAbstractItemDelegate *delegate)
{
- m_allItemsView->setItemDelegate(delegate);
+ m_itemDelegate = delegate;
+ if (m_allItemsView)
+ m_allItemsView->setItemDelegate(delegate);
for (GridView *view : std::as_const(m_gridViews))
view->setItemDelegate(delegate);
}
@@ -672,38 +713,97 @@ void SectionedGridView::setItemDelegate(QAbstractItemDelegate *delegate)
void SectionedGridView::setPixmapFunction(const Core::ListModel::PixmapFunction &pixmapFunction)
{
m_pixmapFunction = pixmapFunction;
- auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
- allProducts->setPixmapFunction(pixmapFunction);
+ m_allItemsModel->setPixmapFunction(pixmapFunction);
for (ListModel *model : std::as_const(m_sectionModels))
model->setPixmapFunction(pixmapFunction);
}
void SectionedGridView::setSearchString(const QString &searchString)
{
- int view = searchString.isEmpty() ? 0 // sectioned view
- : 1; // search view
- setCurrentIndex(view);
- m_filteredAllItemsModel->setSearchString(searchString);
+ if (searchString.isEmpty()) {
+ // back to previous view
+ m_allItemsView.reset();
+ if (m_zoomedInWidget)
+ setCurrentWidget(m_zoomedInWidget);
+ else
+ setCurrentIndex(0);
+ return;
+ }
+ if (!m_allItemsView) {
+ // We don't have a grid set for searching yet.
+ // Create all items view for filtering.
+ m_allItemsView.reset(new GridView);
+ m_allItemsView->setModel(new ListModelFilter(m_allItemsModel.get(), m_allItemsView.get()));
+ if (m_itemDelegate)
+ m_allItemsView->setItemDelegate(m_itemDelegate);
+ addWidget(m_allItemsView.get());
+ }
+ setCurrentWidget(m_allItemsView.get());
+ auto filterModel = static_cast<ListModelFilter *>(m_allItemsView.get()->model());
+ filterModel->setSearchString(searchString);
+}
+
+static QWidget *createSeparator(QWidget *parent)
+{
+ QWidget *line = Layouting::createHr(parent);
+ QSizePolicy linePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
+ linePolicy.setHorizontalStretch(2);
+ line->setSizePolicy(linePolicy);
+ QPalette pal = line->palette();
+ pal.setColor(QPalette::Dark, Qt::transparent);
+ pal.setColor(QPalette::Light, themeColor(Theme::Welcome_ForegroundSecondaryColor));
+ line->setPalette(pal);
+ return line;
+}
+
+static QLabel *createLinkLabel(const QString &text, QWidget *parent)
+{
+ const QString linkColor = themeColor(Theme::Welcome_LinkColor).name();
+ auto link = new QLabel("<a href=\"link\" style=\"color: " + linkColor + ";\">"
+ + text + "</a>", parent);
+ return link;
}
ListModel *SectionedGridView::addSection(const Section &section, const QList<ListItem *> &items)
{
auto model = new ListModel(this);
model->setPixmapFunction(m_pixmapFunction);
+ // the sections only keep a weak reference to the items,
+ // they are owned by the allProducts model, since multiple sections can contain duplicates
+ // of the same item
+ model->setOwnsItems(false);
model->appendItems(items);
auto gridView = new SectionGridView(this);
- gridView->setItemDelegate(m_allItemsView->itemDelegate());
+ gridView->setItemDelegate(m_itemDelegate);
gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gridView->setModel(model);
+ gridView->setMaxRows(section.maxRows);
m_sectionModels.insert(section, model);
const auto it = m_gridViews.insert(section, gridView);
- auto sectionLabel = new QLabel(section.name);
+ QLabel *seeAllLink = createLinkLabel(Tr::tr("Show All") + " &gt;", this);
+ if (gridView->maxRows().has_value()) {
+ seeAllLink->setVisible(true);
+ connect(gridView, &SectionGridView::itemsFitChanged, seeAllLink, [seeAllLink](bool fits) {
+ seeAllLink->setVisible(!fits);
+ });
+ } else {
+ seeAllLink->setVisible(false);
+ }
+ connect(seeAllLink, &QLabel::linkActivated, this, [this, section] { zoomInSection(section); });
+ using namespace Layouting;
+ QWidget *sectionLabel = Row {
+ section.name,
+ createSeparator(this),
+ seeAllLink,
+ Space(HSpacing),
+ noMargin
+ }.emerge();
m_sectionLabels.append(sectionLabel);
- sectionLabel->setContentsMargins(0, Core::WelcomePageHelpers::ItemGap, 0, 0);
+ sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
@@ -715,8 +815,11 @@ ListModel *SectionedGridView::addSection(const Section &section, const QList<Lis
vbox->insertWidget(position + 1, gridView);
// add the items also to the all products model to be able to search correctly
- auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
- allProducts->appendItems(items);
+ const QSet<ListItem *> allItems = toSet(m_allItemsModel->items());
+ const QList<ListItem *> newItems = filtered(items, [&allItems](ListItem *item) {
+ return !allItems.contains(item);
+ });
+ m_allItemsModel->appendItems(newItems);
// only show section label(s) if there is more than one section
m_sectionLabels.at(0)->setVisible(m_sectionLabels.size() > 1);
@@ -726,14 +829,62 @@ ListModel *SectionedGridView::addSection(const Section &section, const QList<Lis
void SectionedGridView::clear()
{
- auto allProducts = static_cast<ListModel *>(m_filteredAllItemsModel->sourceModel());
- allProducts->clear();
+ m_allItemsModel->clear();
qDeleteAll(m_sectionModels);
qDeleteAll(m_sectionLabels);
qDeleteAll(m_gridViews);
m_sectionModels.clear();
m_sectionLabels.clear();
m_gridViews.clear();
+ m_allItemsView.reset();
+}
+
+void SectionedGridView::zoomInSection(const Section &section)
+{
+ auto zoomedInWidget = new QWidget(this);
+ auto layout = new QVBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ zoomedInWidget->setLayout(layout);
+
+ QLabel *backLink = createLinkLabel("&lt; " + Tr::tr("Back"), this);
+ connect(backLink, &QLabel::linkActivated, this, [this, zoomedInWidget] {
+ removeWidget(zoomedInWidget);
+ delete zoomedInWidget;
+ setCurrentIndex(0);
+ });
+ using namespace Layouting;
+ QWidget *sectionLabel = Row {
+ section.name,
+ createSeparator(this),
+ backLink,
+ Space(HSpacing),
+ noMargin
+ }.emerge();
+ sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
+ sectionLabel->setFont(Core::WelcomePageHelpers::brandFont());
+
+ auto gridView = new GridView(zoomedInWidget);
+ gridView->setItemDelegate(m_itemDelegate);
+ gridView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ gridView->setModel(m_sectionModels.value(section));
+
+ layout->addWidget(sectionLabel);
+ layout->addWidget(gridView);
+
+ m_zoomedInWidget = zoomedInWidget;
+ addWidget(zoomedInWidget);
+ setCurrentWidget(zoomedInWidget);
}
+Section::Section(const QString &name, int priority)
+ : name(name)
+ , priority(priority)
+{}
+
+Section::Section(const QString &name, int priority, std::optional<int> maxRows)
+ : name(name)
+ , priority(priority)
+ , maxRows(maxRows)
+{}
+
} // namespace Core
diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h
index 77760000e9..8599c227b9 100644
--- a/src/plugins/coreplugin/welcomepagehelper.h
+++ b/src/plugins/coreplugin/welcomepagehelper.h
@@ -24,6 +24,16 @@ namespace WelcomePageHelpers {
constexpr int HSpacing = 20;
constexpr int ItemGap = 4;
+
+constexpr int GridItemGap = 3 * ItemGap;
+constexpr int GridItemWidth = 240 + GridItemGap; // Extra GridItemGap as "spacing"
+constexpr int GridItemHeight = GridItemWidth;
+constexpr QSize GridItemImageSize(GridItemWidth - GridItemGap
+ - 2 * (GridItemGap + 1), // Horizontal margins + 1 pixel
+ GridItemHeight - GridItemGap
+ - GridItemGap - 1 // Upper margin + 1 pixel
+ - 67); // Bottom margin (for title + tags)
+
CORE_EXPORT QFont brandFont();
CORE_EXPORT QWidget *panelBar(QWidget *parent = nullptr);
@@ -40,7 +50,7 @@ public:
class CORE_EXPORT GridView : public QListView
{
public:
- explicit GridView(QWidget *parent);
+ explicit GridView(QWidget *parent = nullptr);
protected:
void leaveEvent(QEvent *) final;
@@ -48,11 +58,25 @@ protected:
class CORE_EXPORT SectionGridView : public GridView
{
+ Q_OBJECT
+
public:
explicit SectionGridView(QWidget *parent);
- bool hasHeightForWidth() const;
- int heightForWidth(int width) const;
+ void setMaxRows(std::optional<int> max);
+ std::optional<int> maxRows() const;
+
+ bool hasHeightForWidth() const override;
+ int heightForWidth(int width) const override;
+
+ void wheelEvent(QWheelEvent *e) override;
+ bool event(QEvent *e) override;
+
+signals:
+ void itemsFitChanged(bool fit);
+
+private:
+ std::optional<int> m_maxRows;
};
using OptModelIndex = std::optional<QModelIndex>;
@@ -74,7 +98,7 @@ public:
using PixmapFunction = std::function<QPixmap(QString)>;
- explicit ListModel(QObject *parent);
+ explicit ListModel(QObject *parent = nullptr);
~ListModel() override;
void appendItems(const QList<ListItem *> &items);
@@ -85,8 +109,6 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setPixmapFunction(const PixmapFunction &fetchPixmapAndUpdatePixmapCache);
- static const QSize defaultImageSize;
-
void setOwnsItems(bool owns);
private:
@@ -128,11 +150,6 @@ public:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
- static constexpr int GridItemGap = 3 * WelcomePageHelpers::ItemGap;
- static constexpr int GridItemWidth = 240 + GridItemGap;
- static constexpr int GridItemHeight = GridItemWidth;
- static constexpr int TagsSeparatorY = GridItemHeight - GridItemGap - 52;
-
signals:
void tagClicked(const QString &tag);
@@ -151,6 +168,7 @@ protected:
const QColor backgroundPrimaryColor;
const QColor backgroundSecondaryColor;
const QColor foregroundPrimaryColor;
+ const QColor foregroundSecondaryColor;
const QColor hoverColor;
const QColor textColor;
@@ -165,6 +183,9 @@ private:
class CORE_EXPORT Section
{
public:
+ Section(const QString &name, int priority);
+ Section(const QString &name, int priority, std::optional<int> maxRows);
+
friend bool operator<(const Section &lhs, const Section &rhs)
{
if (lhs.priority < rhs.priority)
@@ -179,6 +200,7 @@ public:
QString name;
int priority;
+ std::optional<int> maxRows;
};
class CORE_EXPORT SectionedGridView : public QStackedWidget
@@ -196,12 +218,16 @@ public:
void clear();
private:
+ void zoomInSection(const Section &section);
+
QMap<Section, Core::ListModel *> m_sectionModels;
QList<QWidget *> m_sectionLabels;
QMap<Section, Core::GridView *> m_gridViews;
- Core::GridView *m_allItemsView = nullptr;
- Core::ListModelFilter *m_filteredAllItemsModel = nullptr;
+ std::unique_ptr<Core::ListModel> m_allItemsModel;
+ std::unique_ptr<Core::GridView> m_allItemsView;
+ QPointer<QWidget> m_zoomedInWidget;
Core::ListModel::PixmapFunction m_pixmapFunction;
+ QAbstractItemDelegate *m_itemDelegate = nullptr;
};
} // namespace Core
diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp
index 65b3d25179..dd576f228f 100644
--- a/src/plugins/cpaster/cpasterplugin.cpp
+++ b/src/plugins/cpaster/cpasterplugin.cpp
@@ -247,8 +247,8 @@ void CodePasterPluginPrivate::post(QString data, const QString &mimeType)
const FileDataList diffChunks = splitDiffToFiles(data);
const int dialogResult = diffChunks.isEmpty() ?
- view.show(username, {}, {}, m_settings.expiryDays.value(), data) :
- view.show(username, {}, {}, m_settings.expiryDays.value(), diffChunks);
+ view.show(username, {}, {}, m_settings.expiryDays(), data) :
+ view.show(username, {}, {}, m_settings.expiryDays(), diffChunks);
// Save new protocol in case user changed it.
if (dialogResult == QDialog::Accepted && m_settings.protocols.value() != view.protocol()) {
diff --git a/src/plugins/cpaster/fileshareprotocol.cpp b/src/plugins/cpaster/fileshareprotocol.cpp
index 2ca4bfa677..e9b85ee1af 100644
--- a/src/plugins/cpaster/fileshareprotocol.cpp
+++ b/src/plugins/cpaster/fileshareprotocol.cpp
@@ -6,7 +6,6 @@
#include "cpastertr.h"
#include "fileshareprotocolsettingspage.h"
-#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <utils/fileutils.h>
@@ -29,20 +28,13 @@ const char textElementC[] = "text";
namespace CodePaster {
-FileShareProtocol::FileShareProtocol() :
- m_settingsPage(new FileShareProtocolSettingsPage(&m_settings))
-{
- m_settings.readSettings(Core::ICore::settings());
-}
+FileShareProtocol::FileShareProtocol() = default;
-FileShareProtocol::~FileShareProtocol()
-{
- delete m_settingsPage;
-}
+FileShareProtocol::~FileShareProtocol() = default;
QString FileShareProtocol::name() const
{
- return m_settingsPage->displayName();
+ return m_settings.displayName();
}
unsigned FileShareProtocol::capabilities() const
@@ -55,9 +47,9 @@ bool FileShareProtocol::hasSettings() const
return true;
}
-Core::IOptionsPage *FileShareProtocol::settingsPage() const
+const Core::IOptionsPage *FileShareProtocol::settingsPage() const
{
- return m_settingsPage;
+ return &m_settings;
}
static bool parse(const QString &fileName,
@@ -141,7 +133,7 @@ void FileShareProtocol::list()
QString errorMessage;
const QChar blank = QLatin1Char(' ');
const QFileInfoList entryInfoList = dir.entryInfoList();
- const int count = qMin(int(m_settings.displayCount.value()), entryInfoList.size());
+ const int count = qMin(int(m_settings.displayCount()), entryInfoList.size());
for (int i = 0; i < count; i++) {
const QFileInfo& entryFi = entryInfoList.at(i);
if (parse(entryFi.absoluteFilePath(), &errorMessage, &user, &description)) {
diff --git a/src/plugins/cpaster/fileshareprotocol.h b/src/plugins/cpaster/fileshareprotocol.h
index 100c0aece0..db03bb11bf 100644
--- a/src/plugins/cpaster/fileshareprotocol.h
+++ b/src/plugins/cpaster/fileshareprotocol.h
@@ -8,8 +8,6 @@
namespace CodePaster {
-class FileShareProtocolSettingsPage;
-
/* FileShareProtocol: Allows for pasting via a shared network
* drive by writing XML files. */
@@ -22,7 +20,7 @@ public:
QString name() const override;
unsigned capabilities() const override;
bool hasSettings() const override;
- Core::IOptionsPage *settingsPage() const override;
+ const Core::IOptionsPage *settingsPage() const override;
bool checkConfiguration(QString *errorMessage = nullptr) override;
void fetch(const QString &id) override;
@@ -35,7 +33,6 @@ public:
private:
FileShareProtocolSettings m_settings;
- FileShareProtocolSettingsPage *m_settingsPage;
};
} // CodePaster
diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp
index 775c0438ab..f3c6daad53 100644
--- a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp
+++ b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp
@@ -15,8 +15,10 @@ namespace CodePaster {
FileShareProtocolSettings::FileShareProtocolSettings()
{
+ setId("X.CodePaster.FileSharePaster");
+ setDisplayName(Tr::tr("Fileshare"));
+ setCategory(Constants::CPASTER_SETTINGS_CATEGORY);
setSettingsGroup("FileSharePasterSettings");
- setAutoApply(false);
registerAspect(&path);
path.setSettingsKey("Path");
@@ -30,18 +32,8 @@ FileShareProtocolSettings::FileShareProtocolSettings()
displayCount.setDefaultValue(10);
displayCount.setSuffix(' ' + Tr::tr("entries"));
displayCount.setLabelText(Tr::tr("&Display:"));
-}
-
-// Settings page
-FileShareProtocolSettingsPage::FileShareProtocolSettingsPage(FileShareProtocolSettings *settings)
-{
- setId("X.CodePaster.FileSharePaster");
- setDisplayName(Tr::tr("Fileshare"));
- setCategory(Constants::CPASTER_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([&s = *settings](QWidget *widget) {
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
auto label = new QLabel(Tr::tr(
@@ -52,12 +44,14 @@ FileShareProtocolSettingsPage::FileShareProtocolSettingsPage(FileShareProtocolSe
Column {
Form {
label, br,
- s.path,
- s.displayCount
+ path, br,
+ displayCount
},
st
}.attachTo(widget);
});
+
+ readSettings();
}
} // namespace CodePaster
diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.h b/src/plugins/cpaster/fileshareprotocolsettingspage.h
index 3b92c3596d..36a06c2106 100644
--- a/src/plugins/cpaster/fileshareprotocolsettingspage.h
+++ b/src/plugins/cpaster/fileshareprotocolsettingspage.h
@@ -5,11 +5,9 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace CodePaster {
-class FileShareProtocolSettings : public Utils::AspectContainer
+class FileShareProtocolSettings : public Core::PagedSettings
{
public:
FileShareProtocolSettings();
@@ -18,10 +16,4 @@ public:
Utils::IntegerAspect displayCount;
};
-class FileShareProtocolSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit FileShareProtocolSettingsPage(FileShareProtocolSettings *settings);
-};
-
} // CodePaster
diff --git a/src/plugins/cpaster/pasteselectdialog.cpp b/src/plugins/cpaster/pasteselectdialog.cpp
index 5b4c164c3b..2df87e7137 100644
--- a/src/plugins/cpaster/pasteselectdialog.cpp
+++ b/src/plugins/cpaster/pasteselectdialog.cpp
@@ -54,7 +54,7 @@ PasteSelectDialog::PasteSelectDialog(const QList<Protocol*> &protocols, QWidget
listFont.setStyleHint(QFont::TypeWriter);
m_listWidget->setFont(listFont);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
Tr::tr("Protocol:"), m_protocolBox, br,
diff --git a/src/plugins/cpaster/pasteview.cpp b/src/plugins/cpaster/pasteview.cpp
index 02ae0f6530..b1f4864b6e 100644
--- a/src/plugins/cpaster/pasteview.cpp
+++ b/src/plugins/cpaster/pasteview.cpp
@@ -100,7 +100,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
m_uiPatchList->setSortingEnabled(false);
m_uiPatchList->setSortingEnabled(__sortingEnabled);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_uiPatchList,
@@ -108,6 +108,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
}.attachTo(groupBox);
Column {
+ spacing(2),
Form {
Tr::tr("Protocol:"), m_protocolBox, br,
Tr::tr("&Expires after:"), m_expirySpinBox, br,
@@ -117,7 +118,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
m_uiComment,
m_stackedWidget,
buttonBox
- }.setSpacing(2).attachTo(this);
+ }.attachTo(this);
connect(m_uiPatchList, &QListWidget::itemChanged, this, &PasteView::contentChanged);
diff --git a/src/plugins/cpaster/protocol.cpp b/src/plugins/cpaster/protocol.cpp
index d26396205a..c2630d09c1 100644
--- a/src/plugins/cpaster/protocol.cpp
+++ b/src/plugins/cpaster/protocol.cpp
@@ -47,7 +47,7 @@ bool Protocol::checkConfiguration(QString *)
return true;
}
-Core::IOptionsPage *Protocol::settingsPage() const
+const Core::IOptionsPage *Protocol::settingsPage() const
{
return nullptr;
}
diff --git a/src/plugins/cpaster/protocol.h b/src/plugins/cpaster/protocol.h
index a8d233619b..90464768c2 100644
--- a/src/plugins/cpaster/protocol.h
+++ b/src/plugins/cpaster/protocol.h
@@ -38,7 +38,7 @@ public:
virtual unsigned capabilities() const = 0;
virtual bool hasSettings() const;
- virtual Core::IOptionsPage *settingsPage() const;
+ virtual const Core::IOptionsPage *settingsPage() const;
virtual bool checkConfiguration(QString *errorMessage = nullptr);
virtual void fetch(const QString &id) = 0;
diff --git a/src/plugins/cpaster/settings.cpp b/src/plugins/cpaster/settings.cpp
index 7459ae1098..8f599dcd50 100644
--- a/src/plugins/cpaster/settings.cpp
+++ b/src/plugins/cpaster/settings.cpp
@@ -67,8 +67,8 @@ SettingsPage::SettingsPage(Settings *settings)
Column {
Form {
- s.protocols,
- s.username,
+ s.protocols, br,
+ s.username, br,
s.expiryDays
},
s.copyToClipboard,
diff --git a/src/plugins/cpaster/stickynotespasteprotocol.h b/src/plugins/cpaster/stickynotespasteprotocol.h
index e17508e084..f212c883ef 100644
--- a/src/plugins/cpaster/stickynotespasteprotocol.h
+++ b/src/plugins/cpaster/stickynotespasteprotocol.h
@@ -38,7 +38,6 @@ private:
QNetworkReply *m_listReply = nullptr;
QString m_fetchId;
- int m_postId = -1;
bool m_hostChecked = false;
};
diff --git a/src/plugins/cppcheck/cppcheckconstants.h b/src/plugins/cppcheck/cppcheckconstants.h
index 6efbb2f615..427bdfc10f 100644
--- a/src/plugins/cppcheck/cppcheckconstants.h
+++ b/src/plugins/cppcheck/cppcheckconstants.h
@@ -9,23 +9,6 @@ const char TEXTMARK_CATEGORY_ID[] = "Cppcheck";
const char OPTIONS_PAGE_ID[] = "Analyzer.Cppcheck.Settings";
-const char SETTINGS_ID[] = "Cppcheck";
-const char SETTINGS_BINARY[] = "binary";
-const char SETTINGS_WARNING[] = "warning";
-const char SETTINGS_STYLE[] = "style";
-const char SETTINGS_PERFORMANCE[] = "performance";
-const char SETTINGS_PORTABILITY[] = "portability";
-const char SETTINGS_INFORMATION[] = "information";
-const char SETTINGS_UNUSED_FUNCTION[] = "unusedFunction";
-const char SETTINGS_MISSING_INCLUDE[] = "missingInclude";
-const char SETTINGS_INCONCLUSIVE[] = "inconclusive";
-const char SETTINGS_FORCE_DEFINES[] = "forceDefines";
-const char SETTINGS_CUSTOM_ARGUMENTS[] = "customArguments";
-const char SETTINGS_IGNORE_PATTERNS[] = "ignorePatterns";
-const char SETTINGS_SHOW_OUTPUT[] = "showOutput";
-const char SETTINGS_ADD_INCLUDE_PATHS[] = "addIncludePaths";
-const char SETTINGS_GUESS_ARGUMENTS[] = "guessArguments";
-
const char CHECK_PROGRESS_ID[] = "Cppcheck.CheckingTask";
const char MANUAL_CHECK_PROGRESS_ID[] = "Cppcheck.ManualCheckingTask";
diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp
index 1f53e6835b..060f888cc2 100644
--- a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp
+++ b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp
@@ -18,11 +18,9 @@
namespace Cppcheck::Internal {
-ManualRunDialog::ManualRunDialog(const CppcheckOptions &options,
+ManualRunDialog::ManualRunDialog(QWidget *optionsWidget,
const ProjectExplorer::Project *project)
- : QDialog(),
- m_options(new OptionsWidget(this)),
- m_model(new ProjectExplorer::SelectableFilesFromDirModel(this))
+ : m_model(new ProjectExplorer::SelectableFilesFromDirModel(this))
{
QTC_ASSERT(project, return );
@@ -55,21 +53,12 @@ ManualRunDialog::ManualRunDialog(const CppcheckOptions &options,
});
auto layout = new QVBoxLayout(this);
- layout->addWidget(m_options);
+ layout->addWidget(optionsWidget);
layout->addWidget(view);
layout->addWidget(buttons);
- if (auto layout = m_options->layout())
+ if (auto layout = optionsWidget->layout())
layout->setContentsMargins(0, 0, 0, 0);
-
- m_options->load(options);
-}
-
-CppcheckOptions ManualRunDialog::options() const
-{
- CppcheckOptions result;
- m_options->save(result);
- return result;
}
Utils::FilePaths ManualRunDialog::filePaths() const
diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.h b/src/plugins/cppcheck/cppcheckmanualrundialog.h
index 8ffadcc48a..460a85e75c 100644
--- a/src/plugins/cppcheck/cppcheckmanualrundialog.h
+++ b/src/plugins/cppcheck/cppcheckmanualrundialog.h
@@ -17,21 +17,15 @@ class SelectableFilesFromDirModel;
namespace Cppcheck::Internal {
-class OptionsWidget;
-class CppcheckOptions;
-
class ManualRunDialog : public QDialog
{
public:
- ManualRunDialog(const CppcheckOptions &options,
- const ProjectExplorer::Project *project);
+ ManualRunDialog(QWidget *optionsWidget, const ProjectExplorer::Project *project);
- CppcheckOptions options() const;
Utils::FilePaths filePaths() const;
QSize sizeHint() const override;
private:
- OptionsWidget *m_options;
ProjectExplorer::SelectableFilesFromDirModel *m_model;
};
diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp
index 11563430cc..37f537f2fe 100644
--- a/src/plugins/cppcheck/cppcheckoptions.cpp
+++ b/src/plugins/cppcheck/cppcheckoptions.cpp
@@ -12,6 +12,7 @@
#include <utils/flowlayout.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
@@ -20,211 +21,135 @@
#include <debugger/analyzer/analyzericons.h>
#include <debugger/debuggertr.h>
-#include <QCheckBox>
-#include <QFormLayout>
-
using namespace Utils;
namespace Cppcheck::Internal {
-OptionsWidget::OptionsWidget(QWidget *parent)
- : QWidget(parent),
- m_binary(new Utils::PathChooser(this)),
- m_customArguments(new QLineEdit(this)),
- m_ignorePatterns(new QLineEdit(this)),
- m_warning(new QCheckBox(Tr::tr("Warnings"), this)),
- m_style(new QCheckBox(Tr::tr("Style"), this)),
- m_performance(new QCheckBox(Tr::tr("Performance"), this)),
- m_portability(new QCheckBox(Tr::tr("Portability"), this)),
- m_information(new QCheckBox(Tr::tr("Information"), this)),
- m_unusedFunction(new QCheckBox(Tr::tr("Unused functions"), this)),
- m_missingInclude(new QCheckBox(Tr::tr("Missing includes"), this)),
- m_inconclusive(new QCheckBox(Tr::tr("Inconclusive errors"), this)),
- m_forceDefines(new QCheckBox(Tr::tr("Check all define combinations"), this)),
- m_showOutput(new QCheckBox(Tr::tr("Show raw output"), this)),
- m_addIncludePaths(new QCheckBox(Tr::tr("Add include paths"), this)),
- m_guessArguments(new QCheckBox(Tr::tr("Calculate additional arguments"), this))
-{
- m_binary->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_binary->setCommandVersionArguments({"--version"});
-
- auto variableChooser = new Utils::VariableChooser(this);
- variableChooser->addSupportedWidget (m_customArguments);
-
- m_unusedFunction->setToolTip(Tr::tr("Disables multithreaded check."));
- m_ignorePatterns->setToolTip(Tr::tr("Comma-separated wildcards of full file paths. "
- "Files still can be checked if others include them."));
- m_addIncludePaths->setToolTip(Tr::tr("Can find missing includes but makes "
- "checking slower. Use only when needed."));
- m_guessArguments->setToolTip(Tr::tr("Like C++ standard and language."));
-
- auto layout = new QFormLayout(this);
- layout->addRow(Tr::tr("Binary:"), m_binary);
-
- auto checks = new Utils::FlowLayout;
- layout->addRow(Tr::tr("Checks:"), checks);
- checks->addWidget(m_warning);
- checks->addWidget(m_style);
- checks->addWidget(m_performance);
- checks->addWidget(m_portability);
- checks->addWidget(m_information);
- checks->addWidget(m_unusedFunction);
- checks->addWidget(m_missingInclude);
-
- layout->addRow(Tr::tr("Custom arguments:"), m_customArguments);
- layout->addRow(Tr::tr("Ignored file patterns:"), m_ignorePatterns);
- auto flags = new Utils::FlowLayout;
- layout->addRow(flags);
- flags->addWidget(m_inconclusive);
- flags->addWidget(m_forceDefines);
- flags->addWidget(m_showOutput);
- flags->addWidget(m_addIncludePaths);
- flags->addWidget(m_guessArguments);
-}
-
-void OptionsWidget::load(const CppcheckOptions &options)
-{
- m_binary->setFilePath(options.binary);
- m_customArguments->setText(options.customArguments);
- m_ignorePatterns->setText(options.ignoredPatterns);
- m_warning->setChecked(options.warning);
- m_style->setChecked(options.style);
- m_performance->setChecked(options.performance);
- m_portability->setChecked(options.portability);
- m_information->setChecked(options.information);
- m_unusedFunction->setChecked(options.unusedFunction);
- m_missingInclude->setChecked(options.missingInclude);
- m_inconclusive->setChecked(options.inconclusive);
- m_forceDefines->setChecked(options.forceDefines);
- m_showOutput->setChecked(options.showOutput);
- m_addIncludePaths->setChecked(options.addIncludePaths);
- m_guessArguments->setChecked(options.guessArguments);
-}
-
-void OptionsWidget::save(CppcheckOptions &options) const
-{
- options.binary = m_binary->filePath();
- options.customArguments = m_customArguments->text();
- options.ignoredPatterns = m_ignorePatterns->text();
- options.warning = m_warning->isChecked();
- options.style = m_style->isChecked();
- options.performance = m_performance->isChecked();
- options.portability = m_portability->isChecked();
- options.information = m_information->isChecked();
- options.unusedFunction = m_unusedFunction->isChecked();
- options.missingInclude = m_missingInclude->isChecked();
- options.inconclusive = m_inconclusive->isChecked();
- options.forceDefines = m_forceDefines->isChecked();
- options.showOutput = m_showOutput->isChecked();
- options.addIncludePaths = m_addIncludePaths->isChecked();
- options.guessArguments = m_guessArguments->isChecked();
-}
-
-CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger):
- m_tool(tool),
- m_trigger(trigger)
+CppcheckOptions::CppcheckOptions()
{
setId(Constants::OPTIONS_PAGE_ID);
setDisplayName(Tr::tr("Cppcheck"));
setCategory("T.Analyzer");
setDisplayCategory(::Debugger::Tr::tr("Analyzer"));
setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER);
-
- CppcheckOptions options;
+ setSettingsGroup("Cppcheck");
+
+ registerAspect(&binary);
+ binary.setSettingsKey("binary");
+ binary.setDisplayStyle(StringAspect::PathChooserDisplay);
+ binary.setExpectedKind(PathChooser::ExistingCommand);
+ binary.setCommandVersionArguments({"--version"});
+ binary.setLabelText(Tr::tr("Binary:"));
if (HostOsInfo::isAnyUnixHost()) {
- options.binary = "cppcheck";
+ binary.setDefaultValue("cppcheck");
} else {
FilePath programFiles = FilePath::fromUserInput(qtcEnvironmentVariable("PROGRAMFILES"));
if (programFiles.isEmpty())
programFiles = "C:/Program Files";
- options.binary = programFiles / "Cppcheck/cppcheck.exe";
+ binary.setDefaultValue(programFiles.pathAppended("Cppcheck/cppcheck.exe").toString());
}
- load(options);
-
- m_tool.updateOptions(options);
-}
-
-QWidget *CppcheckOptionsPage::widget()
-{
- if (!m_widget)
- m_widget = new OptionsWidget;
- m_widget->load(m_tool.options());
- return m_widget.data();
-}
-
-void CppcheckOptionsPage::apply()
-{
- CppcheckOptions options;
- m_widget->save(options);
- save(options);
- m_tool.updateOptions(options);
- m_trigger.recheck();
-}
-
-void CppcheckOptionsPage::finish()
-{
-}
-
-void CppcheckOptionsPage::save(const CppcheckOptions &options) const
-{
- QSettings *s = Core::ICore::settings();
- QTC_ASSERT(s, return);
- s->beginGroup(Constants::SETTINGS_ID);
- s->setValue(Constants::SETTINGS_BINARY, options.binary.toString());
- s->setValue(Constants::SETTINGS_CUSTOM_ARGUMENTS, options.customArguments);
- s->setValue(Constants::SETTINGS_IGNORE_PATTERNS, options.ignoredPatterns);
- s->setValue(Constants::SETTINGS_WARNING, options.warning);
- s->setValue(Constants::SETTINGS_STYLE, options.style);
- s->setValue(Constants::SETTINGS_PERFORMANCE, options.performance);
- s->setValue(Constants::SETTINGS_PORTABILITY, options.portability);
- s->setValue(Constants::SETTINGS_INFORMATION, options.information);
- s->setValue(Constants::SETTINGS_UNUSED_FUNCTION, options.unusedFunction);
- s->setValue(Constants::SETTINGS_MISSING_INCLUDE, options.missingInclude);
- s->setValue(Constants::SETTINGS_INCONCLUSIVE, options.inconclusive);
- s->setValue(Constants::SETTINGS_FORCE_DEFINES, options.forceDefines);
- s->setValue(Constants::SETTINGS_SHOW_OUTPUT, options.showOutput);
- s->setValue(Constants::SETTINGS_ADD_INCLUDE_PATHS, options.addIncludePaths);
- s->setValue(Constants::SETTINGS_GUESS_ARGUMENTS, options.guessArguments);
- s->endGroup();
+ registerAspect(&warning);
+ warning.setSettingsKey("warning");
+ warning.setDefaultValue(true);
+ warning.setLabelText(Tr::tr("Warnings"));
+
+ registerAspect(&style);
+ style.setSettingsKey("style");
+ style.setDefaultValue(true);
+ style.setLabelText(Tr::tr("Style"));
+
+ registerAspect(&performance);
+ performance.setSettingsKey("performance");
+ performance.setDefaultValue(true);
+ performance.setLabelText(Tr::tr("Performance"));
+
+ registerAspect(&portability);
+ portability.setSettingsKey("portability");
+ portability.setDefaultValue(true);
+ portability.setLabelText(Tr::tr("Portability"));
+
+ registerAspect(&information);
+ information.setSettingsKey("information");
+ information.setDefaultValue(true);
+ information.setLabelText(Tr::tr("Information"));
+
+ registerAspect(&unusedFunction);
+ unusedFunction.setSettingsKey("unusedFunction");
+ unusedFunction.setLabelText(Tr::tr("Unused functions"));
+ unusedFunction.setToolTip(Tr::tr("Disables multithreaded check."));
+
+ registerAspect(&missingInclude);
+ missingInclude.setSettingsKey("missingInclude");
+ missingInclude.setLabelText(Tr::tr("Missing includes"));
+
+ registerAspect(&inconclusive);
+ inconclusive.setSettingsKey("inconclusive");
+ inconclusive.setLabelText(Tr::tr("Inconclusive errors"));
+
+ registerAspect(&forceDefines);
+ forceDefines.setSettingsKey("forceDefines");
+ forceDefines.setLabelText(Tr::tr("Check all define combinations"));
+
+ registerAspect(&customArguments);
+ customArguments.setSettingsKey("customArguments");
+ customArguments.setDisplayStyle(StringAspect::LineEditDisplay);
+ customArguments.setLabelText(Tr::tr("Custom arguments:"));
+
+ registerAspect(&ignoredPatterns);
+ ignoredPatterns.setSettingsKey("ignoredPatterns");
+ ignoredPatterns.setDisplayStyle(StringAspect::LineEditDisplay);
+ ignoredPatterns.setLabelText(Tr::tr("Ignored file patterns:"));
+ ignoredPatterns.setToolTip(Tr::tr("Comma-separated wildcards of full file paths. "
+ "Files still can be checked if others include them."));
+
+ registerAspect(&showOutput);
+ showOutput.setSettingsKey("showOutput");
+ showOutput.setLabelText(Tr::tr("Show raw output"));
+
+ registerAspect(&addIncludePaths);
+ addIncludePaths.setSettingsKey("addIncludePaths");
+ addIncludePaths.setLabelText(Tr::tr("Add include paths"));
+ addIncludePaths.setToolTip(Tr::tr("Can find missing includes but makes "
+ "checking slower. Use only when needed."));
+
+ registerAspect(&guessArguments);
+ guessArguments.setSettingsKey("guessArguments");
+ guessArguments.setDefaultValue(true);
+ guessArguments.setLabelText(Tr::tr("Calculate additional arguments"));
+ guessArguments.setToolTip(Tr::tr("Like C++ standard and language."));
+
+ setLayouter(layouter());
+
+ readSettings();
}
-void CppcheckOptionsPage::load(CppcheckOptions &options) const
+std::function<void(QWidget *widget)> CppcheckOptions::layouter()
{
- QSettings *s = Core::ICore::settings();
- QTC_ASSERT(s, return);
- s->beginGroup(Constants::SETTINGS_ID);
- options.binary = FilePath::fromString(s->value(Constants::SETTINGS_BINARY,
- options.binary.toString()).toString());
- options.customArguments = s->value(Constants::SETTINGS_CUSTOM_ARGUMENTS,
- options.customArguments).toString();
- options.ignoredPatterns = s->value(Constants::SETTINGS_IGNORE_PATTERNS,
- options.ignoredPatterns).toString();
- options.warning = s->value(Constants::SETTINGS_WARNING,
- options.warning).toBool();
- options.style = s->value(Constants::SETTINGS_STYLE,
- options.style).toBool();
- options.performance = s->value(Constants::SETTINGS_PERFORMANCE,
- options.performance).toBool();
- options.portability = s->value(Constants::SETTINGS_PORTABILITY,
- options.portability).toBool();
- options.information = s->value(Constants::SETTINGS_INFORMATION,
- options.information).toBool();
- options.unusedFunction = s->value(Constants::SETTINGS_UNUSED_FUNCTION,
- options.unusedFunction).toBool();
- options.missingInclude = s->value(Constants::SETTINGS_MISSING_INCLUDE,
- options.missingInclude).toBool();
- options.inconclusive = s->value(Constants::SETTINGS_INCONCLUSIVE,
- options.inconclusive).toBool();
- options.forceDefines = s->value(Constants::SETTINGS_FORCE_DEFINES,
- options.forceDefines).toBool();
- options.showOutput = s->value(Constants::SETTINGS_SHOW_OUTPUT,
- options.showOutput).toBool();
- options.addIncludePaths = s->value(Constants::SETTINGS_ADD_INCLUDE_PATHS,
- options.addIncludePaths).toBool();
- options.guessArguments = s->value(Constants::SETTINGS_GUESS_ARGUMENTS,
- options.guessArguments).toBool();
- s->endGroup();
+ return [this](QWidget *widget) {
+ using namespace Layouting;
+ Form {
+ binary, br,
+ Tr::tr("Checks:"), Flow {
+ warning,
+ style,
+ performance,
+ portability,
+ information,
+ unusedFunction,
+ missingInclude
+ }, br,
+ customArguments, br,
+ ignoredPatterns, br,
+ Flow {
+ inconclusive,
+ forceDefines,
+ showOutput,
+ addIncludePaths,
+ guessArguments
+ }
+ }.attachTo(widget);
+ };
}
} // Cppcheck::Internal
diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h
index 7c23d69f60..84fbf483bb 100644
--- a/src/plugins/cppcheck/cppcheckoptions.h
+++ b/src/plugins/cppcheck/cppcheckoptions.h
@@ -4,88 +4,35 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/filepath.h>
-
-#include <QCoreApplication>
-#include <QPointer>
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-class QLineEdit;
-class QCheckBox;
-QT_END_NAMESPACE
-
-namespace Utils { class PathChooser; }
namespace Cppcheck::Internal {
class CppcheckTool;
class CppcheckTrigger;
-class OptionsWidget;
-
-class CppcheckOptions final
-{
-public:
- Utils::FilePath binary;
-
- bool warning = true;
- bool style = true;
- bool performance = true;
- bool portability = true;
- bool information = true;
- bool unusedFunction = false;
- bool missingInclude = false;
- bool inconclusive = false;
- bool forceDefines = false;
-
- QString customArguments;
- QString ignoredPatterns;
- bool showOutput = false;
- bool addIncludePaths = false;
- bool guessArguments = true;
-};
-class OptionsWidget final : public QWidget
+class CppcheckOptions final : public Core::PagedSettings
{
public:
- explicit OptionsWidget(QWidget *parent = nullptr);
- void load(const CppcheckOptions &options);
- void save(CppcheckOptions &options) const;
-
-private:
- Utils::PathChooser *m_binary = nullptr;
- QLineEdit *m_customArguments = nullptr;
- QLineEdit *m_ignorePatterns = nullptr;
- QCheckBox *m_warning = nullptr;
- QCheckBox *m_style = nullptr;
- QCheckBox *m_performance = nullptr;
- QCheckBox *m_portability = nullptr;
- QCheckBox *m_information = nullptr;
- QCheckBox *m_unusedFunction = nullptr;
- QCheckBox *m_missingInclude = nullptr;
- QCheckBox *m_inconclusive = nullptr;
- QCheckBox *m_forceDefines = nullptr;
- QCheckBox *m_showOutput = nullptr;
- QCheckBox *m_addIncludePaths = nullptr;
- QCheckBox *m_guessArguments = nullptr;
-};
-
-class CppcheckOptionsPage final : public Core::IOptionsPage
-{
-public:
- explicit CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger);
-
- QWidget *widget() final;
- void apply() final;
- void finish() final;
-
-private:
- void save(const CppcheckOptions &options) const;
- void load(CppcheckOptions &options) const;
-
- CppcheckTool &m_tool;
- CppcheckTrigger &m_trigger;
- QPointer<OptionsWidget> m_widget;
+ CppcheckOptions();
+
+ std::function<void(QWidget *widget)> layouter();
+
+ Utils::StringAspect binary;
+ Utils::BoolAspect warning;
+ Utils::BoolAspect style;
+ Utils::BoolAspect performance;
+ Utils::BoolAspect portability;
+ Utils::BoolAspect information;
+ Utils::BoolAspect unusedFunction;
+ Utils::BoolAspect missingInclude;
+ Utils::BoolAspect inconclusive;
+ Utils::BoolAspect forceDefines;
+
+ Utils::StringAspect customArguments;
+ Utils::StringAspect ignoredPatterns;
+ Utils::BoolAspect showOutput;
+ Utils::BoolAspect addIncludePaths;
+ Utils::BoolAspect guessArguments;
};
} // Cppcheck::Internal
diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp
index 5dd09a1820..890e2e3c0a 100644
--- a/src/plugins/cppcheck/cppcheckplugin.cpp
+++ b/src/plugins/cppcheck/cppcheckplugin.cpp
@@ -16,7 +16,7 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <coreplugin/actionmanager/actioncontainer.h>
@@ -28,6 +28,8 @@
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
+using namespace Utils;
+
namespace Cppcheck::Internal {
class CppcheckPluginPrivate final : public QObject
@@ -36,11 +38,11 @@ public:
explicit CppcheckPluginPrivate();
CppcheckTextMarkManager marks;
- CppcheckTool tool{marks, Constants::CHECK_PROGRESS_ID};
+ CppcheckOptions options;
+ CppcheckTool tool{options, marks, Constants::CHECK_PROGRESS_ID};
CppcheckTrigger trigger{marks, tool};
- CppcheckOptionsPage options{tool, trigger};
DiagnosticsModel manualRunModel;
- CppcheckTool manualRunTool{manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID};
+ CppcheckTool manualRunTool{options, manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID};
Utils::Perspective perspective{Constants::PERSPECTIVE_ID, ::Cppcheck::Tr::tr("Cppcheck")};
QAction *manualRunAction;
@@ -51,7 +53,11 @@ public:
CppcheckPluginPrivate::CppcheckPluginPrivate()
{
- manualRunTool.updateOptions(tool.options());
+ tool.updateOptions();
+ connect(&options, &AspectContainer::changed, [this] {
+ tool.updateOptions();
+ trigger.recheck();
+ });
auto manualRunView = new DiagnosticView;
manualRunView->setModel(&manualRunModel);
@@ -97,12 +103,18 @@ CppcheckPluginPrivate::CppcheckPluginPrivate()
}
}
-void CppcheckPluginPrivate::startManualRun() {
- auto project = ProjectExplorer::SessionManager::startupProject();
+void CppcheckPluginPrivate::startManualRun()
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return;
- ManualRunDialog dialog(manualRunTool.options(), project);
+ manualRunTool.updateOptions();
+
+ auto optionsWidget = new QWidget;
+ options.layouter()(optionsWidget);
+
+ ManualRunDialog dialog(optionsWidget, project);
if (dialog.exec() == ManualRunDialog::Rejected)
return;
@@ -113,7 +125,7 @@ void CppcheckPluginPrivate::startManualRun() {
return;
manualRunTool.setProject(project);
- manualRunTool.updateOptions(dialog.options());
+ manualRunTool.updateOptions();
manualRunTool.check(files);
perspective.select();
}
@@ -121,8 +133,8 @@ void CppcheckPluginPrivate::startManualRun() {
void CppcheckPluginPrivate::updateManualRunAction()
{
using namespace ProjectExplorer;
- const Project *project = SessionManager::startupProject();
- const Target *target = SessionManager::startupTarget();
+ const Project *project = ProjectManager::startupProject();
+ const Target *target = ProjectManager::startupTarget();
const Utils::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
const bool canRun = target && project->projectLanguages().contains(cxx)
&& ToolChainKitAspect::cxxToolChain(target->kit());
diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp
index 8595eac28b..436c8ab392 100644
--- a/src/plugins/cppcheck/cppcheckrunner.cpp
+++ b/src/plugins/cppcheck/cppcheckrunner.cpp
@@ -16,7 +16,7 @@ namespace Cppcheck::Internal {
CppcheckRunner::CppcheckRunner(CppcheckTool &tool) : m_tool(tool)
{
if (HostOsInfo::hostOs() == OsTypeLinux) {
- QtcProcess getConf;
+ Process getConf;
getConf.setCommand({"getconf", {"ARG_MAX"}});
getConf.start();
getConf.waitForFinished(2000);
@@ -31,8 +31,8 @@ CppcheckRunner::CppcheckRunner(CppcheckTool &tool) : m_tool(tool)
m_tool.parseErrorLine(line);
});
- connect(&m_process, &QtcProcess::started, &m_tool, &CppcheckTool::startParsing);
- connect(&m_process, &QtcProcess::done, this, &CppcheckRunner::handleDone);
+ connect(&m_process, &Process::started, &m_tool, &CppcheckTool::startParsing);
+ connect(&m_process, &Process::done, this, &CppcheckRunner::handleDone);
m_queueTimer.setSingleShot(true);
const int checkDelayInMs = 200;
diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h
index 23689b2b05..686a6c9eb9 100644
--- a/src/plugins/cppcheck/cppcheckrunner.h
+++ b/src/plugins/cppcheck/cppcheckrunner.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QHash>
#include <QTimer>
@@ -35,7 +35,7 @@ private:
void handleDone();
CppcheckTool &m_tool;
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
Utils::FilePath m_binary;
QString m_arguments;
QHash<QString, Utils::FilePaths> m_queue;
diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp
index a5c3bc6afb..bd44957c37 100644
--- a/src/plugins/cppcheck/cppchecktool.cpp
+++ b/src/plugins/cppcheck/cppchecktool.cpp
@@ -26,7 +26,8 @@ using namespace Utils;
namespace Cppcheck::Internal {
-CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, const Id &progressId) :
+CppcheckTool::CppcheckTool(CppcheckOptions &options, CppcheckDiagnosticManager &manager, const Id &progressId) :
+ m_options(options),
m_manager(manager),
m_progressRegexp("^.* checked (\\d+)% done$"),
m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$"),
@@ -39,11 +40,10 @@ CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, const Id &progres
CppcheckTool::~CppcheckTool() = default;
-void CppcheckTool::updateOptions(const CppcheckOptions &options)
+void CppcheckTool::updateOptions()
{
- m_options = options;
m_filters.clear();
- for (const QString &pattern : m_options.ignoredPatterns.split(',')) {
+ for (const QString &pattern : m_options.ignoredPatterns().split(',')) {
const QString trimmedPattern = pattern.trimmed();
if (trimmedPattern.isEmpty())
continue;
@@ -70,44 +70,44 @@ void CppcheckTool::updateArguments()
m_cachedAdditionalArguments.clear();
QStringList arguments;
- if (!m_options.customArguments.isEmpty()) {
+ if (!m_options.customArguments().isEmpty()) {
Utils::MacroExpander *expander = Utils::globalMacroExpander();
- const QString expanded = expander->expand(m_options.customArguments);
+ const QString expanded = expander->expand(m_options.customArguments());
arguments.push_back(expanded);
}
- if (m_options.warning)
+ if (m_options.warning())
arguments.push_back("--enable=warning");
- if (m_options.style)
+ if (m_options.style())
arguments.push_back("--enable=style");
- if (m_options.performance)
+ if (m_options.performance())
arguments.push_back("--enable=performance");
- if (m_options.portability)
+ if (m_options.portability())
arguments.push_back("--enable=portability");
- if (m_options.information)
+ if (m_options.information())
arguments.push_back("--enable=information");
- if (m_options.unusedFunction)
+ if (m_options.unusedFunction())
arguments.push_back("--enable=unusedFunction");
- if (m_options.missingInclude)
+ if (m_options.missingInclude())
arguments.push_back("--enable=missingInclude");
- if (m_options.inconclusive)
+ if (m_options.inconclusive())
arguments.push_back("--inconclusive");
- if (m_options.forceDefines)
+ if (m_options.forceDefines())
arguments.push_back("--force");
- if (!m_options.unusedFunction && !m_options.customArguments.contains("-j "))
+ if (!m_options.unusedFunction() && !m_options.customArguments().contains("-j "))
arguments.push_back("-j " + QString::number(QThread::idealThreadCount()));
arguments.push_back("--template=\"{file},{line},{severity},{id},{message}\"");
- m_runner->reconfigure(m_options.binary, arguments.join(' '));
+ m_runner->reconfigure(m_options.binary.filePath(), arguments.join(' '));
}
QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part) const
{
QStringList result;
- if (m_options.addIncludePaths) {
+ if (m_options.addIncludePaths()) {
for (const ProjectExplorer::HeaderPath &path : part.headerPaths) {
const QString projectDir = m_project->projectDirectory().toString();
if (path.type == ProjectExplorer::HeaderPathType::User
@@ -116,7 +116,7 @@ QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part
}
}
- if (!m_options.guessArguments)
+ if (!m_options.guessArguments())
return result;
using Version = Utils::LanguageVersion;
@@ -158,11 +158,6 @@ QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part
return result;
}
-const CppcheckOptions &CppcheckTool::options() const
-{
- return m_options;
-}
-
void CppcheckTool::check(const Utils::FilePaths &files)
{
QTC_ASSERT(m_project, return);
@@ -226,7 +221,7 @@ void CppcheckTool::stop(const Utils::FilePaths &files)
void CppcheckTool::startParsing()
{
- if (m_options.showOutput) {
+ if (m_options.showOutput()) {
const QString message = Tr::tr("Cppcheck started: \"%1\".").arg(m_runner->currentCommand());
Core::MessageManager::writeSilently(message);
}
@@ -245,7 +240,7 @@ void CppcheckTool::parseOutputLine(const QString &line)
if (line.isEmpty())
return;
- if (m_options.showOutput)
+ if (m_options.showOutput())
Core::MessageManager::writeSilently(line);
enum Matches { Percentage = 1 };
@@ -276,7 +271,7 @@ void CppcheckTool::parseErrorLine(const QString &line)
if (line.isEmpty())
return;
- if (m_options.showOutput)
+ if (m_options.showOutput())
Core::MessageManager::writeSilently(line);
enum Matches { File = 1, Line, Severity, Id, Message };
@@ -301,7 +296,7 @@ void CppcheckTool::parseErrorLine(const QString &line)
void CppcheckTool::finishParsing()
{
- if (m_options.showOutput)
+ if (m_options.showOutput())
Core::MessageManager::writeSilently(Tr::tr("Cppcheck finished."));
QTC_ASSERT(m_progress, return);
diff --git a/src/plugins/cppcheck/cppchecktool.h b/src/plugins/cppcheck/cppchecktool.h
index ccb8e19d0f..4dc6699e33 100644
--- a/src/plugins/cppcheck/cppchecktool.h
+++ b/src/plugins/cppcheck/cppchecktool.h
@@ -31,10 +31,10 @@ class CppcheckTool final : public QObject
Q_OBJECT
public:
- CppcheckTool(CppcheckDiagnosticManager &manager, const Utils::Id &progressId);
+ CppcheckTool(CppcheckOptions &options, CppcheckDiagnosticManager &manager, const Utils::Id &progressId);
~CppcheckTool() override;
- void updateOptions(const CppcheckOptions &options);
+ void updateOptions();
void setProject(ProjectExplorer::Project *project);
void check(const Utils::FilePaths &files);
void stop(const Utils::FilePaths &files);
@@ -44,15 +44,13 @@ public:
void parseErrorLine(const QString &line);
void finishParsing();
- const CppcheckOptions &options() const;
-
private:
void updateArguments();
void addToQueue(const Utils::FilePaths &files, const CppEditor::ProjectPart &part);
QStringList additionalArguments(const CppEditor::ProjectPart &part) const;
+ CppcheckOptions &m_options;
CppcheckDiagnosticManager &m_manager;
- CppcheckOptions m_options;
QPointer<ProjectExplorer::Project> m_project;
std::unique_ptr<CppcheckRunner> m_runner;
std::unique_ptr<QFutureInterface<void>> m_progress;
diff --git a/src/plugins/cppcheck/cppchecktrigger.cpp b/src/plugins/cppcheck/cppchecktrigger.cpp
index 55b1cf7ac0..8be098df55 100644
--- a/src/plugins/cppcheck/cppchecktrigger.cpp
+++ b/src/plugins/cppcheck/cppchecktrigger.cpp
@@ -13,7 +13,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
using namespace Core;
using namespace ProjectExplorer;
@@ -34,7 +34,7 @@ CppcheckTrigger::CppcheckTrigger(CppcheckTextMarkManager &marks, CppcheckTool &t
connect(EditorManager::instance(), &EditorManager::aboutToSave,
this, &CppcheckTrigger::checkChangedDocument);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &CppcheckTrigger::changeCurrentProject);
connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated,
diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt
index b3db8c4d1d..021bf0b763 100644
--- a/src/plugins/cppeditor/CMakeLists.txt
+++ b/src/plugins/cppeditor/CMakeLists.txt
@@ -32,7 +32,6 @@ add_qtc_plugin(CppEditor
cppcompletionassist.cpp cppcompletionassist.h
cppcompletionassistprocessor.cpp cppcompletionassistprocessor.h
cppcompletionassistprovider.cpp cppcompletionassistprovider.h
- cppcurrentdocumentfilter.cpp cppcurrentdocumentfilter.h
cppcursorinfo.h
cppdoxygen.cpp cppdoxygen.h
cppeditor.qrc
@@ -107,7 +106,6 @@ add_qtc_plugin(CppEditor
resourcepreviewhoverhandler.cpp resourcepreviewhoverhandler.h
searchsymbols.cpp searchsymbols.h
semantichighlighter.cpp semantichighlighter.h
- senddocumenttracker.cpp senddocumenttracker.h
symbolfinder.cpp symbolfinder.h
symbolsfindfilter.cpp symbolsfindfilter.h
typehierarchybuilder.cpp typehierarchybuilder.h
diff --git a/src/plugins/cppeditor/baseeditordocumentparser.cpp b/src/plugins/cppeditor/baseeditordocumentparser.cpp
index 8a8d1bc9af..cbfa6f9685 100644
--- a/src/plugins/cppeditor/baseeditordocumentparser.cpp
+++ b/src/plugins/cppeditor/baseeditordocumentparser.cpp
@@ -8,6 +8,8 @@
#include "cppprojectpartchooser.h"
#include "editordocumenthandle.h"
+#include <QPromise>
+
using namespace Utils;
namespace CppEditor {
@@ -59,15 +61,16 @@ void BaseEditorDocumentParser::setConfiguration(const Configuration &configurati
void BaseEditorDocumentParser::update(const UpdateParams &updateParams)
{
- QFutureInterface<void> dummy;
+ QPromise<void> dummy;
+ dummy.start();
update(dummy, updateParams);
}
-void BaseEditorDocumentParser::update(const QFutureInterface<void> &future,
+void BaseEditorDocumentParser::update(const QPromise<void> &promise,
const UpdateParams &updateParams)
{
QMutexLocker locker(&m_updateIsRunning);
- updateImpl(future, updateParams);
+ updateImpl(promise, updateParams);
}
BaseEditorDocumentParser::State BaseEditorDocumentParser::state() const
diff --git a/src/plugins/cppeditor/baseeditordocumentparser.h b/src/plugins/cppeditor/baseeditordocumentparser.h
index fb7f79d101..45f6b95399 100644
--- a/src/plugins/cppeditor/baseeditordocumentparser.h
+++ b/src/plugins/cppeditor/baseeditordocumentparser.h
@@ -6,14 +6,17 @@
#include "cppeditor_global.h"
#include "cpptoolsreuse.h"
#include "cppworkingcopy.h"
-#include "projectpart.h"
#include <projectexplorer/project.h>
-#include <QFutureInterface>
#include <QObject>
#include <QMutex>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+QT_END_NAMESPACE
+
namespace ProjectExplorer { class Project; }
namespace CppEditor {
@@ -66,7 +69,7 @@ public:
void setConfiguration(const Configuration &configuration);
void update(const UpdateParams &updateParams);
- void update(const QFutureInterface<void> &future, const UpdateParams &updateParams);
+ void update(const QPromise<void> &promise, const UpdateParams &updateParams);
ProjectPartInfo projectPartInfo() const;
@@ -91,7 +94,7 @@ protected:
mutable QMutex m_stateAndConfigurationMutex;
private:
- virtual void updateImpl(const QFutureInterface<void> &future,
+ virtual void updateImpl(const QPromise<void> &promise,
const UpdateParams &updateParams) = 0;
const Utils::FilePath m_filePath;
diff --git a/src/plugins/cppeditor/baseeditordocumentprocessor.cpp b/src/plugins/cppeditor/baseeditordocumentprocessor.cpp
index ca344aa6c7..c6c51675eb 100644
--- a/src/plugins/cppeditor/baseeditordocumentprocessor.cpp
+++ b/src/plugins/cppeditor/baseeditordocumentprocessor.cpp
@@ -8,9 +8,12 @@
#include "cpptoolsreuse.h"
#include "editordocumenthandle.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/quickfix.h>
+#include <QPromise>
+
namespace CppEditor {
/*!
@@ -37,7 +40,7 @@ void BaseEditorDocumentProcessor::run(bool projectsUpdated)
: Utils::Language::Cxx;
runImpl({CppModelManager::instance()->workingCopy(),
- ProjectExplorer::SessionManager::startupProject(),
+ ProjectExplorer::ProjectManager::startupProject(),
languagePreference,
projectsUpdated});
}
@@ -58,20 +61,20 @@ void BaseEditorDocumentProcessor::setParserConfig(
parser()->setConfiguration(config);
}
-void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
+void BaseEditorDocumentProcessor::runParser(QPromise<void> &promise,
BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams)
{
- future.setProgressRange(0, 1);
- if (future.isCanceled()) {
- future.setProgressValue(1);
+ promise.setProgressRange(0, 1);
+ if (promise.isCanceled()) {
+ promise.setProgressValue(1);
return;
}
- parser->update(future, updateParams);
+ parser->update(promise, updateParams);
CppModelManager::instance()->finishedRefreshingSourceFiles({parser->filePath().toString()});
- future.setProgressValue(1);
+ promise.setProgressValue(1);
}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/baseeditordocumentprocessor.h b/src/plugins/cppeditor/baseeditordocumentprocessor.h
index afb74f46e6..78c0f55aab 100644
--- a/src/plugins/cppeditor/baseeditordocumentprocessor.h
+++ b/src/plugins/cppeditor/baseeditordocumentprocessor.h
@@ -23,6 +23,11 @@
#include <functional>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+QT_END_NAMESPACE
+
namespace TextEditor { class TextDocument; }
namespace CppEditor {
@@ -83,7 +88,7 @@ signals:
void semanticInfoUpdated(const SemanticInfo semanticInfo); // TODO: Remove me
protected:
- static void runParser(QFutureInterface<void> &future,
+ static void runParser(QPromise<void> &promise,
BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams);
diff --git a/src/plugins/cppeditor/builtincursorinfo.cpp b/src/plugins/cppeditor/builtincursorinfo.cpp
index 14d99ce4db..ffc4d8b12a 100644
--- a/src/plugins/cppeditor/builtincursorinfo.cpp
+++ b/src/plugins/cppeditor/builtincursorinfo.cpp
@@ -14,9 +14,9 @@
#include <cplusplus/Macro.h>
#include <cplusplus/TranslationUnit.h>
-#include <utils/textutils.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
+#include <utils/textutils.h>
#include <QTextBlock>
@@ -322,7 +322,7 @@ QFuture<CursorInfo> BuiltinCursorInfo::run(const CursorInfoParams &cursorInfoPar
QString expression;
Scope *scope = canonicalSymbol.getScopeAndExpression(textCursor, &expression);
- return Utils::runAsync(&FindUses::find, document, snapshot, line, column, scope, expression);
+ return Utils::asyncRun(&FindUses::find, document, snapshot, line, column, scope, expression);
}
SemanticInfo::LocalUseMap
diff --git a/src/plugins/cppeditor/builtineditordocumentparser.cpp b/src/plugins/cppeditor/builtineditordocumentparser.cpp
index 60c768b3a1..f6d7d6faf2 100644
--- a/src/plugins/cppeditor/builtineditordocumentparser.cpp
+++ b/src/plugins/cppeditor/builtineditordocumentparser.cpp
@@ -11,6 +11,8 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QPromise>
+
using namespace CPlusPlus;
using namespace Utils;
@@ -42,7 +44,7 @@ BuiltinEditorDocumentParser::BuiltinEditorDocumentParser(const FilePath &filePat
qRegisterMetaType<CPlusPlus::Snapshot>("CPlusPlus::Snapshot");
}
-void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &future,
+void BuiltinEditorDocumentParser::updateImpl(const QPromise<void> &promise,
const UpdateParams &updateParams)
{
if (filePath().isEmpty())
@@ -142,8 +144,8 @@ void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &futur
QSet<Utils::FilePath> toRemove;
for (const Document::Ptr &doc : std::as_const(state.snapshot)) {
const Utils::FilePath filePath = doc->filePath();
- if (workingCopy.contains(filePath)) {
- if (workingCopy.get(filePath).second != doc->editorRevision())
+ if (const auto entry = workingCopy.get(filePath)) {
+ if (entry->second != doc->editorRevision())
addFileAndDependencies(&state.snapshot, &toRemove, filePath);
continue;
}
@@ -180,7 +182,7 @@ void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &futur
doc->releaseSourceAndAST();
});
sourceProcessor.setFileSizeLimitInMb(m_fileSizeLimitInMb);
- sourceProcessor.setCancelChecker([future]() { return future.isCanceled(); });
+ sourceProcessor.setCancelChecker([&promise] { return promise.isCanceled(); });
Snapshot globalSnapshot = modelManager->snapshot();
globalSnapshot.remove(filePath());
diff --git a/src/plugins/cppeditor/builtineditordocumentparser.h b/src/plugins/cppeditor/builtineditordocumentparser.h
index b1a400a7c4..31ac126c69 100644
--- a/src/plugins/cppeditor/builtineditordocumentparser.h
+++ b/src/plugins/cppeditor/builtineditordocumentparser.h
@@ -34,8 +34,7 @@ public:
static Ptr get(const Utils::FilePath &filePath);
private:
- void updateImpl(const QFutureInterface<void> &future,
- const UpdateParams &updateParams) override;
+ void updateImpl(const QPromise<void> &promise, const UpdateParams &updateParams) override;
void addFileAndDependencies(CPlusPlus::Snapshot *snapshot,
QSet<Utils::FilePath> *toRemove,
const Utils::FilePath &fileName) const;
diff --git a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp
index c94a639eef..4baa2bff34 100644
--- a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp
+++ b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp
@@ -6,11 +6,14 @@
#include "builtincursorinfo.h"
#include "cppchecksymbols.h"
#include "cppcodemodelsettings.h"
+#include "cppeditordocument.h"
#include "cppeditorplugin.h"
#include "cppmodelmanager.h"
#include "cpptoolsreuse.h"
#include "cppworkingcopy.h"
+#include <coreplugin/editormanager/documentmodel.h>
+
#include <texteditor/fontsettings.h>
#include <texteditor/refactoroverlay.h>
#include <texteditor/texteditorsettings.h>
@@ -18,9 +21,9 @@
#include <cplusplus/CppDocument.h>
#include <cplusplus/SimpleLexer.h>
-#include <utils/textutils.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
+#include <utils/textutils.h>
#include <QLoggingCategory>
#include <QTextBlock>
@@ -180,10 +183,8 @@ BuiltinEditorDocumentProcessor::~BuiltinEditorDocumentProcessor()
void BuiltinEditorDocumentProcessor::runImpl(
const BaseEditorDocumentParser::UpdateParams &updateParams)
{
- m_parserFuture = Utils::runAsync(CppModelManager::instance()->sharedThreadPool(),
- runParser,
- parser(),
- updateParams);
+ m_parserFuture = Utils::asyncRun(CppModelManager::instance()->sharedThreadPool(),
+ runParser, parser(), updateParams);
}
BaseEditorDocumentParser::Ptr BuiltinEditorDocumentProcessor::parser()
@@ -266,6 +267,22 @@ void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr d
const auto source = createSemanticInfoSource(false);
QTC_CHECK(source.snapshot.contains(document->filePath()));
m_semanticInfoUpdater.updateDetached(source);
+
+ const QList<Core::IDocument *> openDocuments = Core::DocumentModel::openedDocuments();
+ for (Core::IDocument * const openDocument : openDocuments) {
+ const auto cppEditorDoc = qobject_cast<Internal::CppEditorDocument *>(openDocument);
+ if (!cppEditorDoc)
+ continue;
+ if (cppEditorDoc->filePath() == document->filePath())
+ continue;
+ CPlusPlus::Document::Ptr cppDoc = CppModelManager::instance()->document(
+ cppEditorDoc->filePath());
+ if (!cppDoc)
+ continue;
+ if (!cppDoc->includedFiles().contains(document->filePath()))
+ continue;
+ cppEditorDoc->scheduleProcessDocument();
+ }
}
void BuiltinEditorDocumentProcessor::onSemanticInfoUpdated(const SemanticInfo semanticInfo)
@@ -304,12 +321,13 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated(
SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const
{
- const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy();
- return SemanticInfo::Source(filePath().toString(),
- workingCopy.source(filePath()),
- workingCopy.revision(filePath()),
- m_documentSnapshot,
- force);
+ QByteArray source;
+ int revision = 0;
+ if (const auto entry = CppModelManager::instance()->workingCopy().get(filePath())) {
+ source = entry->first;
+ revision = entry->second;
+ }
+ return SemanticInfo::Source(filePath().toString(), source, revision, m_documentSnapshot, force);
}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/clangdiagnosticconfig.h b/src/plugins/cppeditor/clangdiagnosticconfig.h
index c1d21cf7d7..3442c95db5 100644
--- a/src/plugins/cppeditor/clangdiagnosticconfig.h
+++ b/src/plugins/cppeditor/clangdiagnosticconfig.h
@@ -42,10 +42,8 @@ public:
// Clang-Tidy
enum class TidyMode
{
- // Disabled, // Used by Qt Creator 4.10 and below.
UseCustomChecks = 1,
- UseConfigFile,
- UseDefaultChecks,
+ UseDefaultChecks = 3,
};
TidyMode clangTidyMode() const;
void setClangTidyMode(TidyMode mode);
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp
index d2c5e16325..5f09212121 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.cpp
+++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp
@@ -130,6 +130,7 @@ QStringList CompilerOptionsBuilder::build(ProjectFile::Kind fileKind,
undefineCppLanguageFeatureMacrosForMsvc2015();
addDefineFunctionMacrosMsvc();
addDefineFunctionMacrosQnx();
+ addQtMacros();
addHeaderPathOptions();
@@ -786,6 +787,12 @@ void CompilerOptionsBuilder::addDefineFunctionMacrosQnx()
addMacros({{"_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE"}});
}
+void CompilerOptionsBuilder::addQtMacros()
+{
+ if (m_projectPart.qtVersion != QtMajorVersion::None)
+ addMacros({{"QT_ANNOTATE_FUNCTION(x)", "__attribute__((annotate(#x)))"}});
+}
+
void CompilerOptionsBuilder::reset()
{
m_options.clear();
diff --git a/src/plugins/cppeditor/compileroptionsbuilder.h b/src/plugins/cppeditor/compileroptionsbuilder.h
index 0e8c1a5fad..d47a7e87e3 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder.h
+++ b/src/plugins/cppeditor/compileroptionsbuilder.h
@@ -62,6 +62,7 @@ public:
void undefineClangVersionMacrosForMsvc();
void addDefineFunctionMacrosQnx();
+ void addQtMacros();
// Add custom options
void add(const QString &arg, bool gccOnlyOption = false);
diff --git a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp
index 3a19c16a66..b09a8061fc 100644
--- a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp
+++ b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp
@@ -593,6 +593,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptions()
(QStringList{"-nostdinc", "-nostdinc++", "-arch", "x86_64", "-fsyntax-only", "-m64",
"--target=x86_64-apple-darwin10", "-x", "c++", "-std=c++17",
"-DprojectFoo=projectBar",
+ "-DQT_ANNOTATE_FUNCTION(x)=__attribute__((annotate(#x)))",
wrappedQtHeadersPath, // contains -I already
wrappedQtCoreHeadersPath, // contains -I already
"-I" + t.toNative("/tmp/path"),
@@ -621,6 +622,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvc()
"-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"",
"-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"",
"-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"",
+ "-DQT_ANNOTATE_FUNCTION(x)=__attribute__((annotate(#x)))",
wrappedQtHeadersPath, // contains -I already
wrappedQtCoreHeadersPath, // contains -I already
"-I" + t.toNative("/tmp/path"),
@@ -651,6 +653,7 @@ void CompilerOptionsBuilderTest::testBuildAllOptionsMsvcWithExceptions()
"-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"",
"-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"",
"-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"",
+ "-DQT_ANNOTATE_FUNCTION(x)=__attribute__((annotate(#x)))",
wrappedQtHeadersPath, // contains -I already
wrappedQtCoreHeadersPath, // contains -I already
"-I" + t.toNative("/tmp/path"),
diff --git a/src/plugins/cppeditor/cppcodeformatter.cpp b/src/plugins/cppeditor/cppcodeformatter.cpp
index ce250f672b..6b30e78fa6 100644
--- a/src/plugins/cppeditor/cppcodeformatter.cpp
+++ b/src/plugins/cppeditor/cppcodeformatter.cpp
@@ -924,6 +924,7 @@ bool CodeFormatter::tryStatement()
return true;
switch (kind) {
case T_RETURN:
+ case T_CO_RETURN:
enter(return_statement);
enter(expression);
return true;
@@ -1651,6 +1652,7 @@ void QtStyleCodeFormatter::adjustIndent(const Tokens &tokens, int lexerState, in
case T_BREAK:
case T_CONTINUE:
case T_RETURN:
+ case T_CO_RETURN:
if (topState.type == case_cont) {
*indentDepth = topState.savedIndentDepth;
if (m_styleSettings.indentControlFlowRelativeToSwitchLabels)
diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
index 8b9cba0d63..48ee4c80f1 100644
--- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
+++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp
@@ -1429,7 +1429,9 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
m_workingCopyView->setModel(m_proxyWorkingCopyModel);
using namespace Layouting;
- m_projectPartTab = qobject_cast<QTabWidget*>(TabWidget{
+
+ TabWidget projectPart {
+ bindTo(&m_projectPartTab),
Tab("&General",
Row {
m_partGeneralView,
@@ -1454,10 +1456,10 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
),
Tab("&Header Paths", Column{ projectHeaderPathsView }),
Tab("Pre&compiled Headers", Column{ m_partPrecompiledHeadersEdit }),
- }.widget);
- QTC_CHECK(m_projectPartTab);
+ };
- m_docTab = qobject_cast<QTabWidget*>(TabWidget{
+ TabWidget docTab {
+ bindTo(&m_docTab),
Tab("&General", Column { m_docGeneralView }),
Tab("&Includes", Column { m_docIncludesView }),
Tab("&Diagnostic Messages", Column { m_docDiagnosticMessagesView }),
@@ -1465,8 +1467,7 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Tab("P&reprocessed Source", Column { m_docPreprocessedSourceEdit }),
Tab("&Symbols", Column { m_docSymbolsView }),
Tab("&Tokens", Column { m_docTokensView }),
- }.widget);
- QTC_CHECK(m_docTab);
+ };
Column {
TabWidget {
@@ -1474,7 +1475,7 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Column {
Splitter {
m_projectPartsView,
- m_projectPartTab,
+ projectPart,
},
}
),
@@ -1484,8 +1485,9 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
Column {
Form { QString("Sn&apshot:"), m_snapshotSelector },
m_snapshotView,
- }.emerge(Utils::Layouting::WithoutMargins),
- m_docTab,
+ noMargin,
+ }.emerge(),
+ docTab,
},
}
),
@@ -1506,6 +1508,7 @@ CppCodeModelInspectorDialog::CppCodeModelInspectorDialog(QWidget *parent)
}
}.attachTo(this);
+ QTC_CHECK(m_projectPartTab);
m_projectPartTab->setCurrentIndex(3);
connect(m_snapshotView->selectionModel(),
diff --git a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp
index 45de8f3053..2a6dfb6e1e 100644
--- a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp
+++ b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp
@@ -254,12 +254,19 @@ QString Utils::toString(CPlusPlus::Kind kind)
TOKEN(T_CASE);
TOKEN(T_CATCH);
TOKEN(T_CHAR);
+ TOKEN(T_CHAR8_T);
TOKEN(T_CHAR16_T);
TOKEN(T_CHAR32_T);
TOKEN(T_CLASS);
+ TOKEN(T_CO_AWAIT);
+ TOKEN(T_CO_RETURN);
+ TOKEN(T_CO_YIELD);
+ TOKEN(T_CONCEPT);
TOKEN_AND_ALIASES(T_CONST, T___CONST/T___CONST__);
TOKEN(T_CONST_CAST);
TOKEN(T_CONSTEXPR);
+ TOKEN(T_CONSTEVAL);
+ TOKEN(T_CONSTINIT);
TOKEN(T_CONTINUE);
TOKEN_AND_ALIASES(T_DECLTYPE, T___DECLTYPE);
TOKEN(T_DEFAULT);
@@ -292,6 +299,7 @@ QString Utils::toString(CPlusPlus::Kind kind)
TOKEN(T_PUBLIC);
TOKEN(T_REGISTER);
TOKEN(T_REINTERPRET_CAST);
+ TOKEN(T_REQUIRES);
TOKEN(T_RETURN);
TOKEN(T_SHORT);
TOKEN(T_SIGNED);
diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp
index e0873427f7..5919a97b84 100644
--- a/src/plugins/cppeditor/cppcodemodelsettings.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp
@@ -9,13 +9,14 @@
#include "cpptoolsreuse.h"
#include <coreplugin/icore.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/settingsutils.h>
#include <QDateTime>
@@ -339,7 +340,7 @@ static FilePath getClangHeadersPathFromClang(const FilePath &clangdFilePath)
.withExecutableSuffix();
if (!clangFilePath.exists())
return {};
- QtcProcess clang;
+ Process clang;
clang.setCommand({clangFilePath, {"-print-resource-dir"}});
clang.start();
if (!clang.waitForFinished())
diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
index 46870ba252..00224f5d90 100644
--- a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
+++ b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp
@@ -103,7 +103,7 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(CppCodeModelSettings *s)
m_ignorePchCheckBox->setChecked(m_settings->pchUsage() == CppCodeModelSettings::PchUse_None);
m_useBuiltinPreprocessorCheckBox->setChecked(m_settings->useBuiltinPreprocessor());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
@@ -241,6 +241,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
d->clangdChooser.setFilePath(settings.clangdFilePath());
d->clangdChooser.setAllowPathFromDevice(true);
d->clangdChooser.setEnabled(d->useClangdCheckBox.isChecked());
+ d->clangdChooser.setCommandVersionArguments({"--version"});
using Priority = ClangdSettings::IndexingPriority;
for (Priority prio : {Priority::Off, Priority::Background, Priority::Low, Priority::Normal}) {
d->indexingComboBox.addItem(ClangdSettings::priorityToDisplayString(prio), int(prio));
@@ -400,7 +401,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
else
Core::EditorManager::openEditor(Utils::FilePath::fromString(link));
});
- layout->addWidget(Utils::Layouting::createHr());
+ layout->addWidget(Layouting::createHr());
layout->addWidget(configFilesHelpLabel);
layout->addStretch(1);
diff --git a/src/plugins/cppeditor/cppcodestylesettingspage.cpp b/src/plugins/cppeditor/cppcodestylesettingspage.cpp
index 02adc53969..84573bdeed 100644
--- a/src/plugins/cppeditor/cppcodestylesettingspage.cpp
+++ b/src/plugins/cppeditor/cppcodestylesettingspage.cpp
@@ -34,6 +34,7 @@
#include <QCheckBox>
#include <QTabWidget>
#include <QTextBlock>
+#include <QVBoxLayout>
using namespace TextEditor;
using namespace Utils;
@@ -161,11 +162,8 @@ public:
, m_bindStarToLeftSpecifier(createCheckBox(Tr::tr("Left const/volatile")))
, m_bindStarToRightSpecifier(createCheckBox(Tr::tr("Right const/volatile"),
Tr::tr("This does not apply to references.")))
- , m_categoryTab(new QTabWidget)
, m_tabSettingsWidget(new TabSettingsWidget)
{
- m_categoryTab->setProperty("_q_custom_style_disabled", true);
-
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
@@ -175,10 +173,17 @@ public:
QObject::connect(m_tabSettingsWidget, &TabSettingsWidget::settingsChanged,
q, &CppCodeStylePreferencesWidget::slotTabSettingsChanged);
- using namespace Utils::Layouting;
+ using namespace Layouting;
+
+ QWidget *contentGroupWidget = nullptr;
+ QWidget *bracesGroupWidget = nullptr;
+ QWidget *switchGroupWidget = nullptr;
+ QWidget *alignmentGroupWidget = nullptr;
+ QWidget *typesGroupWidget = nullptr;
const Group contentGroup {
title(Tr::tr("Indent")),
+ bindTo(&contentGroupWidget),
Column {
m_indentAccessSpecifiers,
m_indentDeclarationsRelativeToAccessSpecifiers,
@@ -191,6 +196,7 @@ public:
const Group bracesGroup {
title(Tr::tr("Indent Braces")),
+ bindTo(&bracesGroupWidget),
Column {
m_indentClassBraces,
m_indentNamespaceBraces,
@@ -203,6 +209,7 @@ public:
const Group switchGroup {
title(Tr::tr("Indent within \"switch\"")),
+ bindTo(&switchGroupWidget),
Column {
m_indentSwitchLabels,
m_indentCaseStatements,
@@ -214,6 +221,7 @@ public:
const Group alignmentGroup {
title(Tr::tr("Align")),
+ bindTo(&alignmentGroupWidget),
Column {
m_alignAssignments,
m_extraPaddingConditions,
@@ -223,6 +231,7 @@ public:
const Group typesGroup {
title(Tr::tr("Bind '*' and '&&' in types/declarations to")),
+ bindTo(&typesGroupWidget),
Column {
m_bindStarToIdentifier,
m_bindStarToTypeName,
@@ -233,7 +242,8 @@ public:
};
Row {
- TabWidget { m_categoryTab, {
+ TabWidget {
+ bindTo(&m_categoryTab),
Tab { Tr::tr("General"),
Row { Column { m_tabSettingsWidget, st }, createPreview(0) }
},
@@ -242,15 +252,17 @@ public:
Tab { Tr::tr("\"switch\""), Row { switchGroup, createPreview(3) } },
Tab { Tr::tr("Alignment"), Row { alignmentGroup, createPreview(4) } },
Tab { Tr::tr("Pointers and References"), Row { typesGroup, createPreview(5) } }
- } }
+ }
}.attachTo(q);
+ m_categoryTab->setProperty("_q_custom_style_disabled", true);
+
m_controllers.append(m_tabSettingsWidget);
- m_controllers.append(contentGroup.widget);
- m_controllers.append(bracesGroup.widget);
- m_controllers.append(switchGroup.widget);
- m_controllers.append(alignmentGroup.widget);
- m_controllers.append(typesGroup.widget);
+ m_controllers.append(contentGroupWidget);
+ m_controllers.append(bracesGroupWidget);
+ m_controllers.append(switchGroupWidget);
+ m_controllers.append(alignmentGroupWidget);
+ m_controllers.append(typesGroupWidget);
}
QCheckBox *createCheckBox(const QString &text, const QString &toolTip = {})
@@ -549,18 +561,13 @@ void CppCodeStylePreferencesWidget::finish()
emit finishEmitted();
}
-// ------------------ CppCodeStyleSettingsPage
-
-CppCodeStyleSettingsPage::CppCodeStyleSettingsPage()
-{
- setId(Constants::CPP_CODE_STYLE_SETTINGS_ID);
- setDisplayName(Tr::tr("Code Style"));
- setCategory(Constants::CPP_SETTINGS_CATEGORY);
-}
+// CppCodeStyleSettingsPageWidget
-QWidget *CppCodeStyleSettingsPage::widget()
+class CppCodeStyleSettingsPageWidget : public Core::IOptionsPageWidget
{
- if (!m_widget) {
+public:
+ CppCodeStyleSettingsPageWidget()
+ {
CppCodeStylePreferences *originalCodeStylePreferences = CppToolsSettings::instance()
->cppCodeStyle();
m_pageCppCodeStylePreferences = new CppCodeStylePreferences();
@@ -572,15 +579,16 @@ QWidget *CppCodeStyleSettingsPage::widget()
originalCodeStylePreferences->currentDelegate());
// we set id so that it won't be possible to set delegate to the original prefs
m_pageCppCodeStylePreferences->setId(originalCodeStylePreferences->id());
- m_widget = TextEditorSettings::codeStyleFactory(CppEditor::Constants::CPP_SETTINGS_ID)
- ->createCodeStyleEditor(m_pageCppCodeStylePreferences);
+
+ m_codeStyleEditor = TextEditorSettings::codeStyleFactory(CppEditor::Constants::CPP_SETTINGS_ID)
+ ->createCodeStyleEditor(m_pageCppCodeStylePreferences);
+
+ auto hbox = new QVBoxLayout(this);
+ hbox->addWidget(m_codeStyleEditor);
}
- return m_widget;
-}
-void CppCodeStyleSettingsPage::apply()
-{
- if (m_widget) {
+ void apply() final
+ {
QSettings *s = Core::ICore::settings();
CppCodeStylePreferences *originalCppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle();
@@ -597,15 +605,21 @@ void CppCodeStyleSettingsPage::apply()
originalCppCodeStylePreferences->toSettings(QLatin1String(CppEditor::Constants::CPP_SETTINGS_ID), s);
}
- m_widget->apply();
+ m_codeStyleEditor->apply();
}
-}
-void CppCodeStyleSettingsPage::finish()
+ CppCodeStylePreferences *m_pageCppCodeStylePreferences = nullptr;
+ CodeStyleEditorWidget *m_codeStyleEditor;
+};
+
+// CppCodeStyleSettingsPage
+
+CppCodeStyleSettingsPage::CppCodeStyleSettingsPage()
{
- if (m_widget)
- m_widget->finish();
- delete m_widget;
+ setId(Constants::CPP_CODE_STYLE_SETTINGS_ID);
+ setDisplayName(Tr::tr("Code Style"));
+ setCategory(Constants::CPP_SETTINGS_CATEGORY);
+ setWidgetCreator([] { return new CppCodeStyleSettingsPageWidget; });
}
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppcodestylesettingspage.h b/src/plugins/cppeditor/cppcodestylesettingspage.h
index da15c38948..666b3ab4a2 100644
--- a/src/plugins/cppeditor/cppcodestylesettingspage.h
+++ b/src/plugins/cppeditor/cppcodestylesettingspage.h
@@ -86,14 +86,6 @@ class CppCodeStyleSettingsPage : public Core::IOptionsPage
{
public:
CppCodeStyleSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- CppCodeStylePreferences *m_pageCppCodeStylePreferences = nullptr;
- QPointer<TextEditor::CodeStyleEditorWidget> m_widget;
};
} // namespace Internal
diff --git a/src/plugins/cppeditor/cppcompletion_test.cpp b/src/plugins/cppeditor/cppcompletion_test.cpp
index 78407d5959..1e212dde60 100644
--- a/src/plugins/cppeditor/cppcompletion_test.cpp
+++ b/src/plugins/cppeditor/cppcompletion_test.cpp
@@ -70,6 +70,10 @@ public:
// Get Document
const Document::Ptr document = waitForFileInGlobalSnapshot(filePath);
QVERIFY(document);
+ if (!document->diagnosticMessages().isEmpty()) {
+ for (const Document::DiagnosticMessage &m : document->diagnosticMessages())
+ qDebug().noquote() << m.text();
+ }
QVERIFY(document->diagnosticMessages().isEmpty());
m_snapshot.insert(document);
@@ -409,16 +413,16 @@ static void enumTestCase(const QByteArray &tag, const QByteArray &source,
const QByteArray &prefix = QByteArray())
{
QByteArray fullSource = source;
- fullSource.replace('$', "enum E { val1, val2, val3 };");
- QTest::newRow(tag) << fullSource << (prefix + "val")
- << QStringList({"val1", "val2", "val3"});
+ fullSource.replace('$', "enum E { value1, value2, value3 };");
+ QTest::newRow(tag) << fullSource << (prefix + "value")
+ << QStringList({"value1", "value2", "value3"});
QTest::newRow(QByteArray{tag + "_cxx11"}) << fullSource << QByteArray{prefix + "E::"}
- << QStringList({"E", "val1", "val2", "val3"});
+ << QStringList({"E", "value1", "value2", "value3"});
fullSource.replace("enum E ", "enum ");
- QTest::newRow(QByteArray{tag + "_anon"}) << fullSource << QByteArray{prefix + "val"}
- << QStringList({"val1", "val2", "val3"});
+ QTest::newRow(QByteArray{tag + "_anon"}) << fullSource << QByteArray{prefix + "value"}
+ << QStringList({"value1", "value2", "value3"});
}
void CompletionTest::testCompletion_data()
diff --git a/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp b/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp
deleted file mode 100644
index 0285fd71fd..0000000000
--- a/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cppcurrentdocumentfilter.h"
-
-#include "cppeditorconstants.h"
-#include "cppeditortr.h"
-#include "cppmodelmanager.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/idocument.h>
-
-#include <QRegularExpression>
-
-using namespace CPlusPlus;
-
-namespace CppEditor::Internal {
-
-CppCurrentDocumentFilter::CppCurrentDocumentFilter(CppModelManager *manager)
- : m_modelManager(manager)
-{
- setId(Constants::CURRENT_DOCUMENT_FILTER_ID);
- setDisplayName(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(".");
- setPriority(High);
- setDefaultIncludedByDefault(false);
-
- search.setSymbolsToSearchFor(SymbolSearcher::Declarations |
- SymbolSearcher::Enums |
- SymbolSearcher::Functions |
- SymbolSearcher::Classes);
-
- connect(manager, &CppModelManager::documentUpdated,
- this, &CppCurrentDocumentFilter::onDocumentUpdated);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
- this, &CppCurrentDocumentFilter::onCurrentEditorChanged);
- connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
- this, &CppCurrentDocumentFilter::onEditorAboutToClose);
-}
-
-void CppCurrentDocumentFilter::makeAuxiliary()
-{
- setId({});
- setDisplayName({});
- setDefaultShortcutString({});
- setEnabled(false);
- setHidden(true);
-}
-
-QList<Core::LocatorFilterEntry> CppCurrentDocumentFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString & entry)
-{
- QList<Core::LocatorFilterEntry> goodEntries;
- QList<Core::LocatorFilterEntry> betterEntries;
-
- const QRegularExpression regexp = createRegExp(entry);
- if (!regexp.isValid())
- return goodEntries;
-
- const QList<IndexItem::Ptr> items = itemsOfCurrentDocument();
- for (const IndexItem::Ptr &info : items) {
- if (future.isCanceled())
- break;
-
- QString matchString = info->symbolName();
- if (info->type() == IndexItem::Declaration)
- matchString = info->representDeclaration();
- else if (info->type() == IndexItem::Function)
- matchString += info->symbolType();
-
- QRegularExpressionMatch match = regexp.match(matchString);
- if (match.hasMatch()) {
- const bool betterMatch = match.capturedStart() == 0;
- QVariant id = QVariant::fromValue(info);
- QString name = matchString;
- QString extraInfo = info->symbolScope();
- if (info->type() == IndexItem::Function) {
- if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) {
- name += info->symbolType();
- match = regexp.match(name);
- }
- }
-
- Core::LocatorFilterEntry filterEntry(this, name, id, info->icon());
- filterEntry.extraInfo = extraInfo;
- if (match.hasMatch()) {
- filterEntry.highlightInfo = highlightInfo(match);
- } else {
- match = regexp.match(extraInfo);
- filterEntry.highlightInfo =
- highlightInfo(match, Core::LocatorFilterEntry::HighlightInfo::ExtraInfo);
- }
-
- if (betterMatch)
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
- }
- }
-
- // entries are unsorted by design!
-
- betterEntries += goodEntries;
- return betterEntries;
-}
-
-void CppCurrentDocumentFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart,
- int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData);
- Core::EditorManager::openEditorAt({info->filePath(), info->line(), info->column()});
-}
-
-void CppCurrentDocumentFilter::onDocumentUpdated(Document::Ptr doc)
-{
- QMutexLocker locker(&m_mutex);
- if (m_currentFileName == doc->filePath())
- m_itemsOfCurrentDoc.clear();
-}
-
-void CppCurrentDocumentFilter::onCurrentEditorChanged(Core::IEditor *currentEditor)
-{
- QMutexLocker locker(&m_mutex);
- if (currentEditor)
- m_currentFileName = currentEditor->document()->filePath();
- else
- m_currentFileName.clear();
- m_itemsOfCurrentDoc.clear();
-}
-
-void CppCurrentDocumentFilter::onEditorAboutToClose(Core::IEditor *editorAboutToClose)
-{
- if (!editorAboutToClose)
- return;
-
- QMutexLocker locker(&m_mutex);
- if (m_currentFileName == editorAboutToClose->document()->filePath()) {
- m_currentFileName.clear();
- m_itemsOfCurrentDoc.clear();
- }
-}
-
-QList<IndexItem::Ptr> CppCurrentDocumentFilter::itemsOfCurrentDocument()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_currentFileName.isEmpty())
- return QList<IndexItem::Ptr>();
-
- if (m_itemsOfCurrentDoc.isEmpty()) {
- const Snapshot snapshot = m_modelManager->snapshot();
- if (const Document::Ptr thisDocument = snapshot.document(m_currentFileName)) {
- IndexItem::Ptr rootNode = search(thisDocument);
- rootNode->visitAllChildren([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
- m_itemsOfCurrentDoc.append(info);
- return IndexItem::Recurse;
- });
- }
- }
-
- return m_itemsOfCurrentDoc;
-}
-
-} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppcurrentdocumentfilter.h b/src/plugins/cppeditor/cppcurrentdocumentfilter.h
deleted file mode 100644
index 484812a0cb..0000000000
--- a/src/plugins/cppeditor/cppcurrentdocumentfilter.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "searchsymbols.h"
-
-#include <coreplugin/locator/ilocatorfilter.h>
-
-namespace Core { class IEditor; }
-
-namespace CppEditor {
-
-class CppModelManager;
-
-namespace Internal {
-
-class CppCurrentDocumentFilter : public Core::ILocatorFilter
-{
- Q_OBJECT
-
-public:
- explicit CppCurrentDocumentFilter(CppModelManager *manager);
- ~CppCurrentDocumentFilter() override = default;
-
- void makeAuxiliary();
-
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
-
-private:
- void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
- void onCurrentEditorChanged(Core::IEditor *currentEditor);
- void onEditorAboutToClose(Core::IEditor *currentEditor);
-
- QList<IndexItem::Ptr> itemsOfCurrentDocument();
-
- CppModelManager * m_modelManager;
- SearchSymbols search;
-
- mutable QMutex m_mutex;
- Utils::FilePath m_currentFileName;
- QList<IndexItem::Ptr> m_itemsOfCurrentDoc;
-};
-
-} // namespace Internal
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs
index cb7b145078..b7d2aea031 100644
--- a/src/plugins/cppeditor/cppeditor.qbs
+++ b/src/plugins/cppeditor/cppeditor.qbs
@@ -82,8 +82,6 @@ QtcPlugin {
"cppcompletionassistprocessor.h",
"cppcompletionassistprovider.cpp",
"cppcompletionassistprovider.h",
- "cppcurrentdocumentfilter.cpp",
- "cppcurrentdocumentfilter.h",
"cppcursorinfo.h",
"cppdoxygen.cpp",
"cppdoxygen.h",
@@ -91,7 +89,8 @@ QtcPlugin {
"cppeditorwidget.cpp",
"cppeditorwidget.h",
"cppeditor.qrc",
- "cppeditor_global.h", "cppeditortr.h",
+ "cppeditor_global.h",
+ "cppeditortr.h",
"cppeditorconstants.h",
"cppeditordocument.cpp",
"cppeditordocument.h",
@@ -224,8 +223,6 @@ QtcPlugin {
"searchsymbols.h",
"semantichighlighter.cpp",
"semantichighlighter.h",
- "senddocumenttracker.cpp",
- "senddocumenttracker.h",
"symbolfinder.cpp",
"symbolfinder.h",
"symbolsfindfilter.cpp",
@@ -245,9 +242,7 @@ QtcPlugin {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
files: [
"compileroptionsbuilder_test.cpp",
diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h
index 3c87aa6d63..179c3b3b9b 100644
--- a/src/plugins/cppeditor/cppeditorconstants.h
+++ b/src/plugins/cppeditor/cppeditorconstants.h
@@ -110,12 +110,18 @@ const char CPP_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++");
const char CURRENT_DOCUMENT_FILTER_ID[] = "Methods in current Document";
const char CURRENT_DOCUMENT_FILTER_DISPLAY_NAME[]
= QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Symbols in Current Document");
+const char CURRENT_DOCUMENT_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::CppEditor", "Locates C++ symbols in the current document.");
const char CLASSES_FILTER_ID[] = "Classes";
const char CLASSES_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Classes");
+const char CLASSES_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::CppEditor", "Locates C++ classes in any open project.");
const char FUNCTIONS_FILTER_ID[] = "Methods";
const char FUNCTIONS_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Functions");
+const char FUNCTIONS_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::CppEditor", "Locates C++ functions in any open project.");
const char INCLUDES_FILTER_ID[] = "All Included C/C++ Files";
const char INCLUDES_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor",
@@ -124,6 +130,8 @@ const char INCLUDES_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor",
const char LOCATOR_FILTER_ID[] = "Classes and Methods";
const char LOCATOR_FILTER_DISPLAY_NAME[]
= QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Classes, Enums, Functions and Type Aliases");
+const char LOCATOR_FILTER_DESCRIPTION[] = QT_TRANSLATE_NOOP(
+ "QtC::CppEditor", "Locates C++ classes, enums, functions and type aliases in any open project.");
const char SYMBOLS_FIND_FILTER_ID[] = "Symbols";
const char SYMBOLS_FIND_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::CppEditor", "C++ Symbols");
diff --git a/src/plugins/cppeditor/cppeditoroutline.cpp b/src/plugins/cppeditor/cppeditoroutline.cpp
index b0d14ca119..701eea0a76 100644
--- a/src/plugins/cppeditor/cppeditoroutline.cpp
+++ b/src/plugins/cppeditor/cppeditoroutline.cpp
@@ -14,7 +14,6 @@
#include <texteditor/textdocument.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <utils/linecolumn.h>
#include <utils/treeviewcombobox.h>
#include <QAction>
@@ -152,7 +151,7 @@ void CppEditorOutline::updateIndexNow()
int line = 0, column = 0;
m_editorWidget->convertPosition(m_editorWidget->position(), &line, &column);
- QModelIndex comboIndex = m_model->indexForPosition(line, column);
+ QModelIndex comboIndex = m_model->indexForPosition(line, column - 1);
if (comboIndex.isValid()) {
QSignalBlocker blocker(m_combo);
diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp
index f1fc63b49a..5791a5cb62 100644
--- a/src/plugins/cppeditor/cppeditorplugin.cpp
+++ b/src/plugins/cppeditor/cppeditorplugin.cpp
@@ -49,7 +49,6 @@
#include "functionutils.h"
#include "includeutils.h"
#include "projectinfo_test.h"
-#include "senddocumenttracker.h"
#include "symbolsearcher_test.h"
#include "typehierarchybuilder_test.h"
#endif
@@ -431,6 +430,10 @@ void CppEditorPlugin::initialize()
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
cppToolsMenu->addAction(cmd);
+ cmd = ActionManager::command(TextEditor::Constants::OPEN_CALL_HIERARCHY);
+ contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
+ cppToolsMenu->addAction(cmd);
+
// Refactoring sub-menu
Command *sep = contextMenu->addSeparator();
sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT));
@@ -482,7 +485,6 @@ void CppEditorPlugin::initialize()
addTest<ProjectFileCategorizerTest>();
addTest<ProjectInfoGeneratorTest>();
addTest<ProjectPartChooserTest>();
- addTest<DocumentTrackerTest>();
addTest<SourceProcessorTest>();
addTest<SymbolSearcherTest>();
addTest<TypeHierarchyBuilderTest>();
diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp
index 0430f210a0..96f97feea3 100644
--- a/src/plugins/cppeditor/cppeditorwidget.cpp
+++ b/src/plugins/cppeditor/cppeditorwidget.cpp
@@ -30,9 +30,9 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/extracompiler.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <texteditor/basefilefind.h>
@@ -56,6 +56,7 @@
#include <utils/infobar.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/textutils.h>
#include <utils/utilsicons.h>
@@ -684,13 +685,13 @@ void CppEditorWidget::updateWidgetHighlighting(QWidget *widget, bool highlight)
if (!widget)
return;
- widget->setProperty("highlightWidget", highlight);
+ widget->setProperty(StyleHelper::C_HIGHLIGHT_WIDGET, highlight);
widget->update();
}
bool CppEditorWidget::isWidgetHighlighted(QWidget *widget)
{
- return widget ? widget->property("highlightWidget").toBool() : false;
+ return widget ? widget->property(StyleHelper::C_HIGHLIGHT_WIDGET).toBool() : false;
}
namespace {
@@ -755,7 +756,7 @@ void CppEditorWidget::showRenameWarningIfFileIsGenerated(const Utils::FilePath &
{
if (filePath.isEmpty())
return;
- for (const Project * const project : SessionManager::projects()) {
+ for (const Project * const project : ProjectManager::projects()) {
const Node * const node = project->nodeForFilePath(filePath);
if (!node)
continue;
@@ -989,7 +990,7 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor,
const QString fileName = filePath.fileName();
if (fileName.startsWith("ui_") && fileName.endsWith(".h")) {
const QString uiFileName = fileName.mid(3, fileName.length() - 4) + "ui";
- for (const Project * const project : SessionManager::projects()) {
+ for (const Project * const project : ProjectManager::projects()) {
const auto nodeMatcher = [uiFileName](Node *n) {
return n->filePath().fileName() == uiFileName;
};
diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp
index dabb8cffc0..221c75dd83 100644
--- a/src/plugins/cppeditor/cppelementevaluator.cpp
+++ b/src/plugins/cppeditor/cppelementevaluator.cpp
@@ -14,7 +14,7 @@
#include <cplusplus/Icons.h>
#include <cplusplus/TypeOfExpression.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <QDir>
#include <QQueue>
@@ -140,20 +140,20 @@ CppClass *CppClass::toCppClass()
return this;
}
-void CppClass::lookupBases(QFutureInterfaceBase &futureInterface,
- Symbol *declaration, const LookupContext &context)
+void CppClass::lookupBases(const QFuture<void> &future, Symbol *declaration,
+ const LookupContext &context)
{
ClassOrNamespace *hierarchy = context.lookupType(declaration);
if (!hierarchy)
return;
QSet<ClassOrNamespace *> visited;
- addBaseHierarchy(futureInterface, context, hierarchy, &visited);
+ addBaseHierarchy(future, context, hierarchy, &visited);
}
-void CppClass::addBaseHierarchy(QFutureInterfaceBase &futureInterface, const LookupContext &context,
+void CppClass::addBaseHierarchy(const QFuture<void> &future, const LookupContext &context,
ClassOrNamespace *hierarchy, QSet<ClassOrNamespace *> *visited)
{
- if (futureInterface.isCanceled())
+ if (future.isCanceled())
return;
visited->insert(hierarchy);
const QList<ClassOrNamespace *> &baseClasses = hierarchy->usings();
@@ -165,21 +165,21 @@ void CppClass::addBaseHierarchy(QFutureInterfaceBase &futureInterface, const Loo
ClassOrNamespace *baseHierarchy = context.lookupType(symbol);
if (baseHierarchy && !visited->contains(baseHierarchy)) {
CppClass classSymbol(symbol);
- classSymbol.addBaseHierarchy(futureInterface, context, baseHierarchy, visited);
+ classSymbol.addBaseHierarchy(future, context, baseHierarchy, visited);
bases.append(classSymbol);
}
}
}
}
-void CppClass::lookupDerived(QFutureInterfaceBase &futureInterface,
- Symbol *declaration, const Snapshot &snapshot)
+void CppClass::lookupDerived(const QFuture<void> &future, Symbol *declaration,
+ const Snapshot &snapshot)
{
- snapshot.updateDependencyTable(futureInterface);
- if (futureInterface.isCanceled())
+ snapshot.updateDependencyTable(future);
+ if (future.isCanceled())
return;
addDerivedHierarchy(TypeHierarchyBuilder::buildDerivedTypeHierarchy(
- futureInterface, declaration, snapshot));
+ declaration, snapshot, future));
}
void CppClass::addDerivedHierarchy(const TypeHierarchy &hierarchy)
@@ -340,13 +340,13 @@ static Symbol *followTemplateAsClass(Symbol *symbol)
return symbol;
}
-static void createTypeHierarchy(QFutureInterface<QSharedPointer<CppElement>> &futureInterface,
+static void createTypeHierarchy(QPromise<QSharedPointer<CppElement>> &promise,
const Snapshot &snapshot,
const LookupItem &lookupItem,
const LookupContext &context,
SymbolFinder symbolFinder)
{
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
Symbol *declaration = lookupItem.declaration();
@@ -360,16 +360,17 @@ static void createTypeHierarchy(QFutureInterface<QSharedPointer<CppElement>> &fu
declaration = followClassDeclaration(declaration, snapshot, symbolFinder, &contextToUse);
declaration = followTemplateAsClass(declaration);
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
QSharedPointer<CppClass> cppClass(new CppClass(declaration));
- cppClass->lookupBases(futureInterface, declaration, contextToUse);
- if (futureInterface.isCanceled())
+ const QFuture<void> future = QFuture<void>(promise.future());
+ cppClass->lookupBases(future, declaration, contextToUse);
+ if (promise.isCanceled())
return;
- cppClass->lookupDerived(futureInterface, declaration, snapshot);
- if (futureInterface.isCanceled())
+ cppClass->lookupDerived(future, declaration, snapshot);
+ if (promise.isCanceled())
return;
- futureInterface.reportResult(cppClass);
+ promise.addResult(cppClass);
}
static QSharedPointer<CppElement> handleLookupItemMatch(const Snapshot &snapshot,
@@ -495,7 +496,7 @@ static QFuture<QSharedPointer<CppElement>> asyncExec(
const CPlusPlus::Snapshot &snapshot, const CPlusPlus::LookupItem &lookupItem,
const CPlusPlus::LookupContext &lookupContext)
{
- return Utils::runAsync(&createTypeHierarchy, snapshot, lookupItem, lookupContext,
+ return Utils::asyncRun(&createTypeHierarchy, snapshot, lookupItem, lookupContext,
*CppModelManager::instance()->symbolFinder());
}
diff --git a/src/plugins/cppeditor/cppelementevaluator.h b/src/plugins/cppeditor/cppelementevaluator.h
index 7d03df6712..47ddcbeae1 100644
--- a/src/plugins/cppeditor/cppelementevaluator.h
+++ b/src/plugins/cppeditor/cppelementevaluator.h
@@ -92,16 +92,16 @@ public:
CppClass *toCppClass() final;
- void lookupBases(QFutureInterfaceBase &futureInterface,
- CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context);
- void lookupDerived(QFutureInterfaceBase &futureInterface,
- CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot);
+ void lookupBases(const QFuture<void> &future, CPlusPlus::Symbol *declaration,
+ const CPlusPlus::LookupContext &context);
+ void lookupDerived(const QFuture<void> &future, CPlusPlus::Symbol *declaration,
+ const CPlusPlus::Snapshot &snapshot);
QList<CppClass> bases;
QList<CppClass> derived;
private:
- void addBaseHierarchy(QFutureInterfaceBase &futureInterface,
+ void addBaseHierarchy(const QFuture<void> &future,
const CPlusPlus::LookupContext &context,
CPlusPlus::ClassOrNamespace *hierarchy,
QSet<CPlusPlus::ClassOrNamespace *> *visited);
diff --git a/src/plugins/cppeditor/cppfindreferences.cpp b/src/plugins/cppeditor/cppfindreferences.cpp
index 940eb358e9..5da2285438 100644
--- a/src/plugins/cppeditor/cppfindreferences.cpp
+++ b/src/plugins/cppeditor/cppfindreferences.cpp
@@ -10,25 +10,27 @@
#include "cpptoolsreuse.h"
#include "cppworkingcopy.h"
+#include <cplusplus/Overview.h>
+
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
+
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+
#include <texteditor/basefilefind.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/textfileformat.h>
-#include <cplusplus/Overview.h>
#include <QtConcurrentMap>
#include <QCheckBox>
-#include <QDir>
#include <QFutureWatcher>
#include <QVBoxLayout>
@@ -103,8 +105,8 @@ namespace Internal {
static QByteArray getSource(const Utils::FilePath &fileName,
const WorkingCopy &workingCopy)
{
- if (workingCopy.contains(fileName)) {
- return workingCopy.source(fileName);
+ if (const auto source = workingCopy.source(fileName)) {
+ return *source;
} else {
QString fileContents;
Utils::TextFileFormat format;
@@ -218,7 +220,7 @@ class ProcessFile
const CPlusPlus::Snapshot snapshot;
CPlusPlus::Document::Ptr symbolDocument;
CPlusPlus::Symbol *symbol;
- QFutureInterface<CPlusPlus::Usage> *future;
+ QPromise<CPlusPlus::Usage> *m_promise;
const bool categorize;
public:
@@ -230,22 +232,21 @@ public:
const CPlusPlus::Snapshot snapshot,
CPlusPlus::Document::Ptr symbolDocument,
CPlusPlus::Symbol *symbol,
- QFutureInterface<CPlusPlus::Usage> *future,
+ QPromise<CPlusPlus::Usage> *promise,
bool categorize)
: workingCopy(workingCopy),
snapshot(snapshot),
symbolDocument(symbolDocument),
symbol(symbol),
- future(future),
+ m_promise(promise),
categorize(categorize)
{ }
QList<CPlusPlus::Usage> operator()(const Utils::FilePath &filePath)
{
QList<CPlusPlus::Usage> usages;
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise->suspendIfRequested();
+ if (m_promise->isCanceled())
return usages;
const CPlusPlus::Identifier *symbolId = symbol->identifier();
@@ -275,25 +276,24 @@ public:
usages = process.usages();
}
- if (future->isPaused())
- future->waitForResume();
+ m_promise->suspendIfRequested();
return usages;
}
};
class UpdateUI
{
- QFutureInterface<CPlusPlus::Usage> *future;
+ QPromise<CPlusPlus::Usage> *m_promise;
public:
- explicit UpdateUI(QFutureInterface<CPlusPlus::Usage> *future): future(future) {}
+ explicit UpdateUI(QPromise<CPlusPlus::Usage> *promise): m_promise(promise) {}
void operator()(QList<CPlusPlus::Usage> &, const QList<CPlusPlus::Usage> &usages)
{
for (const CPlusPlus::Usage &u : usages)
- future->reportResult(u);
+ m_promise->addResult(u);
- future->setProgressValue(future->progressValue() + 1);
+ m_promise->setProgressValue(m_promise->future().progressValue() + 1);
}
};
@@ -319,7 +319,7 @@ QList<int> CppFindReferences::references(CPlusPlus::Symbol *symbol,
return references;
}
-static void find_helper(QFutureInterface<CPlusPlus::Usage> &future,
+static void find_helper(QPromise<CPlusPlus::Usage> &promise,
const WorkingCopy workingCopy,
const CPlusPlus::LookupContext &context,
CPlusPlus::Symbol *symbol,
@@ -353,16 +353,16 @@ static void find_helper(QFutureInterface<CPlusPlus::Usage> &future,
}
files = Utils::filteredUnique(files);
- future.setProgressRange(0, files.size());
+ promise.setProgressRange(0, files.size());
- ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &future, categorize);
- UpdateUI reduce(&future);
+ ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &promise, categorize);
+ UpdateUI reduce(&promise);
// This thread waits for blockingMappedReduced to finish, so reduce the pool's used thread count
// so the blockingMappedReduced can use one more thread, and increase it again afterwards.
QThreadPool::globalInstance()->releaseThread();
QtConcurrent::blockingMappedReduced<QList<CPlusPlus::Usage> > (files, process, reduce);
QThreadPool::globalInstance()->reserveThread();
- future.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
}
void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
@@ -437,7 +437,7 @@ void CppFindReferences::findAll_helper(SearchResult *search, CPlusPlus::Symbol *
SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
const WorkingCopy workingCopy = m_modelManager->workingCopy();
QFuture<CPlusPlus::Usage> result;
- result = Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper,
+ result = Utils::asyncRun(m_modelManager->sharedThreadPool(), find_helper,
workingCopy, context, symbol, categorize);
createWatcher(result, search);
@@ -456,10 +456,8 @@ void CppFindReferences::setupSearch(Core::SearchResult *search)
std::bind(&CppFindReferences::onReplaceButtonClicked, this, search, _1, _2, _3));
}
-void CppFindReferences::onReplaceButtonClicked(Core::SearchResult *search,
- const QString &text,
- const QList<SearchResultItem> &items,
- bool preserveCase)
+void CppFindReferences::onReplaceButtonClicked(Core::SearchResult *search, const QString &text,
+ const SearchResultItems &items, bool preserveCase)
{
const Utils::FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase);
if (!filePaths.isEmpty()) {
@@ -577,7 +575,7 @@ static void displayResults(SearchResult *search,
item.setStyle(colorStyleForUsageType(result.tags));
item.setUseTextEditorFont(true);
if (search->supportsReplace())
- item.setSelectForReplacement(SessionManager::projectForFile(result.path));
+ item.setSelectForReplacement(ProjectManager::projectForFile(result.path));
search->addResult(item);
if (parameters.prettySymbolName.isEmpty())
@@ -586,7 +584,7 @@ static void displayResults(SearchResult *search,
if (parameters.filesToRename.contains(result.path))
continue;
- if (!SessionManager::projectForFile(result.path))
+ if (!ProjectManager::projectForFile(result.path))
continue;
if (result.path.baseName().compare(parameters.prettySymbolName, Qt::CaseInsensitive) == 0)
@@ -623,7 +621,7 @@ class FindMacroUsesInFile
const WorkingCopy workingCopy;
const CPlusPlus::Snapshot snapshot;
const CPlusPlus::Macro &macro;
- QFutureInterface<CPlusPlus::Usage> *future;
+ QPromise<CPlusPlus::Usage> *m_promise;
public:
// needed by QtConcurrent
@@ -633,8 +631,8 @@ public:
FindMacroUsesInFile(const WorkingCopy &workingCopy,
const CPlusPlus::Snapshot snapshot,
const CPlusPlus::Macro &macro,
- QFutureInterface<CPlusPlus::Usage> *future)
- : workingCopy(workingCopy), snapshot(snapshot), macro(macro), future(future)
+ QPromise<CPlusPlus::Usage> *promise)
+ : workingCopy(workingCopy), snapshot(snapshot), macro(macro), m_promise(promise)
{ }
QList<CPlusPlus::Usage> operator()(const Utils::FilePath &fileName)
@@ -644,9 +642,8 @@ public:
QByteArray source;
restart_search:
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise->suspendIfRequested();
+ if (m_promise->isCanceled())
return usages;
usages.clear();
@@ -674,8 +671,7 @@ restart_search:
}
}
- if (future->isPaused())
- future->waitForResume();
+ m_promise->suspendIfRequested();
return usages;
}
@@ -704,7 +700,7 @@ restart_search:
} // end of anonymous namespace
-static void findMacroUses_helper(QFutureInterface<CPlusPlus::Usage> &future,
+static void findMacroUses_helper(QPromise<CPlusPlus::Usage> &promise,
const WorkingCopy workingCopy,
const CPlusPlus::Snapshot snapshot,
const CPlusPlus::Macro macro)
@@ -713,15 +709,15 @@ static void findMacroUses_helper(QFutureInterface<CPlusPlus::Usage> &future,
FilePaths files{sourceFile};
files = Utils::filteredUnique(files + snapshot.filesDependingOn(sourceFile));
- future.setProgressRange(0, files.size());
- FindMacroUsesInFile process(workingCopy, snapshot, macro, &future);
- UpdateUI reduce(&future);
+ promise.setProgressRange(0, files.size());
+ FindMacroUsesInFile process(workingCopy, snapshot, macro, &promise);
+ UpdateUI reduce(&promise);
// This thread waits for blockingMappedReduced to finish, so reduce the pool's used thread count
// so the blockingMappedReduced can use one more thread, and increase it again afterwards.
QThreadPool::globalInstance()->releaseThread();
QtConcurrent::blockingMappedReduced<QList<CPlusPlus::Usage> > (files, process, reduce);
QThreadPool::globalInstance()->reserveThread();
- future.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
}
void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro)
@@ -745,10 +741,9 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro, const QStri
setupSearch(search);
SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
- connect(search, &SearchResult::activated,
- [](const Core::SearchResultItem& item) {
- Core::EditorManager::openEditorAtSearchResult(item);
- });
+ connect(search, &SearchResult::activated, search, [](const SearchResultItem &item) {
+ Core::EditorManager::openEditorAtSearchResult(item);
+ });
const CPlusPlus::Snapshot snapshot = m_modelManager->snapshot();
const WorkingCopy workingCopy = m_modelManager->workingCopy();
@@ -766,12 +761,12 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro, const QStri
item.setMainRange(macro.line(), column, macro.nameToQString().length());
item.setUseTextEditorFont(true);
if (search->supportsReplace())
- item.setSelectForReplacement(SessionManager::projectForFile(filePath));
+ item.setSelectForReplacement(ProjectManager::projectForFile(filePath));
search->addResult(item);
}
QFuture<CPlusPlus::Usage> result;
- result = Utils::runAsync(m_modelManager->sharedThreadPool(), findMacroUses_helper,
+ result = Utils::asyncRun(m_modelManager->sharedThreadPool(), findMacroUses_helper,
workingCopy, snapshot, macro);
createWatcher(result, search);
@@ -832,7 +827,7 @@ void CppFindReferences::checkUnused(Core::SearchResult *search, const Link &link
});
connect(search, &SearchResult::canceled, watcher, [watcher] { watcher->cancel(); });
connect(search, &SearchResult::destroyed, watcher, [watcher] { watcher->cancel(); });
- watcher->setFuture(Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper,
+ watcher->setFuture(Utils::asyncRun(m_modelManager->sharedThreadPool(), find_helper,
m_modelManager->workingCopy(), context, symbol, true));
}
diff --git a/src/plugins/cppeditor/cppfindreferences.h b/src/plugins/cppeditor/cppfindreferences.h
index 54f40bf4cf..b05ce723c4 100644
--- a/src/plugins/cppeditor/cppfindreferences.h
+++ b/src/plugins/cppeditor/cppfindreferences.h
@@ -9,30 +9,29 @@
#include <cplusplus/FindUsages.h>
#include <utils/filepath.h>
#include <utils/link.h>
+#include <utils/searchresultitem.h>
-#include <QObject>
-#include <QPointer>
#include <QFuture>
+#include <QPointer>
#include <functional>
-QT_FORWARD_DECLARE_CLASS(QTimer)
+QT_BEGIN_NAMESPACE
+class QTimer;
+QT_END_NAMESPACE
-namespace Core {
-class SearchResultItem;
-class SearchResult;
-} // namespace Core
+namespace Core { class SearchResult; }
namespace CppEditor {
class CppModelManager;
-Core::SearchResultColor::Style CPPEDITOR_EXPORT
+Utils::SearchResultColor::Style CPPEDITOR_EXPORT
colorStyleForUsageType(CPlusPlus::Usage::Tags tags);
class CPPEDITOR_EXPORT CppSearchResultFilter : public Core::SearchResultFilter
{
QWidget *createWidget() override;
- bool matches(const Core::SearchResultItem &item) const override;
+ bool matches(const Utils::SearchResultItem &item) const override;
void setValue(bool &member, bool value);
@@ -79,7 +78,7 @@ public:
private:
void setupSearch(Core::SearchResult *search);
void onReplaceButtonClicked(Core::SearchResult *search, const QString &text,
- const QList<Core::SearchResultItem> &items, bool preserveCase);
+ const Utils::SearchResultItems &items, bool preserveCase);
void searchAgain(Core::SearchResult *search);
void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
index fa271108cf..b43e87758e 100644
--- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
+++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
@@ -21,9 +21,9 @@
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
+#include <utils/async.h>
#include <utils/proxyaction.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/tooltip/tooltip.h>
#include <QRegularExpression>
@@ -232,7 +232,7 @@ void FunctionDeclDefLinkFinder::startFindLinkAt(
// handle the rest in a thread
m_watcher.reset(new QFutureWatcher<QSharedPointer<FunctionDeclDefLink> >());
connect(m_watcher.data(), &QFutureWatcherBase::finished, this, &FunctionDeclDefLinkFinder::onFutureDone);
- m_watcher->setFuture(Utils::runAsync(findLinkHelper, result, refactoringChanges));
+ m_watcher->setFuture(Utils::asyncRun(findLinkHelper, result, refactoringChanges));
}
bool FunctionDeclDefLink::isValid() const
diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp
index 1f83b317ad..87e36491ad 100644
--- a/src/plugins/cppeditor/cpphighlighter.cpp
+++ b/src/plugins/cppeditor/cpphighlighter.cpp
@@ -161,10 +161,8 @@ void CppHighlighter::highlightBlock(const QString &text)
} else if (tk.is(T_NUMERIC_LITERAL)) {
setFormat(tk.utf16charsBegin(), tk.utf16chars(), formatForCategory(C_NUMBER));
} else if (tk.isStringLiteral() || tk.isCharLiteral()) {
- if (!highlightRawStringLiteral(text, tk, QString::fromUtf8(inheritedRawStringSuffix))) {
- setFormatWithSpaces(text, tk.utf16charsBegin(), tk.utf16chars(),
- formatForCategory(C_STRING));
- }
+ if (!highlightRawStringLiteral(text, tk, QString::fromUtf8(inheritedRawStringSuffix)))
+ highlightStringLiteral(text, tk);
} else if (tk.isComment()) {
const int startPosition = initialLexerState ? previousTokenEnd : tk.utf16charsBegin();
if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT)) {
@@ -413,8 +411,18 @@ bool CppHighlighter::highlightRawStringLiteral(QStringView text, const Token &tk
stringOffset = delimiterOffset + delimiter.length() + 1;
stringLength -= delimiter.length() + 1;
}();
- if (text.mid(tk.utf16charsBegin(), tk.utf16chars()).endsWith(expectedSuffix)) {
- endDelimiterOffset = tk.utf16charsBegin() + tk.utf16chars() - expectedSuffix.size();
+ int operatorOffset = tk.utf16charsBegin() + tk.utf16chars();
+ int operatorLength = 0;
+ if (tk.f.userDefinedLiteral) {
+ const int closingQuoteOffset = text.lastIndexOf('"', operatorOffset);
+ QTC_ASSERT(closingQuoteOffset >= tk.utf16charsBegin(), return false);
+ operatorOffset = closingQuoteOffset + 1;
+ operatorLength = tk.utf16charsBegin() + tk.utf16chars() - operatorOffset;
+ stringLength -= operatorLength;
+ }
+ if (text.mid(tk.utf16charsBegin(), operatorOffset - tk.utf16charsBegin())
+ .endsWith(expectedSuffix)) {
+ endDelimiterOffset = operatorOffset - expectedSuffix.size();
stringLength -= expectedSuffix.size();
}
@@ -422,13 +430,52 @@ bool CppHighlighter::highlightRawStringLiteral(QStringView text, const Token &tk
// a string, and the rest (including the delimiter) as a keyword.
const QTextCharFormat delimiterFormat = formatForCategory(C_KEYWORD);
if (delimiterOffset != -1)
- setFormat(tk.utf16charsBegin(), stringOffset, delimiterFormat);
+ setFormat(tk.utf16charsBegin(), stringOffset - tk.utf16charsBegin(), delimiterFormat);
setFormatWithSpaces(text.toString(), stringOffset, stringLength, formatForCategory(C_STRING));
if (endDelimiterOffset != -1)
setFormat(endDelimiterOffset, expectedSuffix.size(), delimiterFormat);
+ if (operatorLength > 0)
+ setFormat(operatorOffset, operatorLength, formatForCategory(C_OPERATOR));
return true;
}
+void CppHighlighter::highlightStringLiteral(QStringView text, const CPlusPlus::Token &tk)
+{
+ switch (tk.kind()) {
+ case T_WIDE_STRING_LITERAL:
+ case T_UTF8_STRING_LITERAL:
+ case T_UTF16_STRING_LITERAL:
+ case T_UTF32_STRING_LITERAL:
+ break;
+ default:
+ if (!tk.userDefinedLiteral()) { // Simple case: No prefix, no suffix.
+ setFormatWithSpaces(text.toString(), tk.utf16charsBegin(), tk.utf16chars(),
+ formatForCategory(C_STRING));
+ return;
+ }
+ }
+
+ int stringOffset = 0;
+ if (!tk.f.joined) {
+ stringOffset = text.indexOf('"', tk.utf16charsBegin());
+ QTC_ASSERT(stringOffset > 0, return);
+ setFormat(tk.utf16charsBegin(), stringOffset - tk.utf16charsBegin(),
+ formatForCategory(C_KEYWORD));
+ }
+ int operatorOffset = tk.utf16charsBegin() + tk.utf16chars();
+ if (tk.userDefinedLiteral()) {
+ const int closingQuoteOffset = text.lastIndexOf('"', operatorOffset);
+ QTC_ASSERT(closingQuoteOffset >= tk.utf16charsBegin(), return);
+ operatorOffset = closingQuoteOffset + 1;
+ }
+ setFormatWithSpaces(text.toString(), stringOffset, operatorOffset - tk.utf16charsBegin(),
+ formatForCategory(C_STRING));
+ if (const int operatorLength = tk.utf16charsBegin() + tk.utf16chars() - operatorOffset;
+ operatorLength > 0) {
+ setFormat(operatorOffset, operatorLength, formatForCategory(C_OPERATOR));
+ }
+}
+
void CppHighlighter::highlightDoxygenComment(const QString &text, int position, int)
{
int initial = position;
@@ -513,6 +560,33 @@ void CppHighlighterTest::test_data()
QTest::newRow("struct keyword") << 25 << 1 << 25 << 6 << C_KEYWORD;
QTest::newRow("operator keyword") << 26 << 5 << 26 << 12 << C_KEYWORD;
QTest::newRow("type in conversion operator") << 26 << 14 << 26 << 16 << C_PRIMITIVE_TYPE;
+ QTest::newRow("concept keyword") << 29 << 22 << 29 << 28 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 string literal (prefix)")
+ << 32 << 16 << 32 << 16 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 string literal (content)")
+ << 32 << 17 << 32 << 21 << C_STRING;
+ QTest::newRow("user-defined UTF-16 string literal (suffix)")
+ << 32 << 22 << 32 << 23 << C_OPERATOR;
+ QTest::newRow("wide string literal (prefix)") << 33 << 17 << 33 << 17 << C_KEYWORD;
+ QTest::newRow("wide string literal (content)") << 33 << 18 << 33 << 24 << C_STRING;
+ QTest::newRow("UTF-8 string literal (prefix)") << 34 << 17 << 34 << 18 << C_KEYWORD;
+ QTest::newRow("UTF-8 string literal (content)") << 34 << 19 << 34 << 24 << C_STRING;
+ QTest::newRow("UTF-32 string literal (prefix)") << 35 << 17 << 35 << 17 << C_KEYWORD;
+ QTest::newRow("UTF-8 string literal (content)") << 35 << 18 << 35 << 23 << C_STRING;
+ QTest::newRow("user-defined UTF-16 raw string literal (prefix)")
+ << 36 << 17 << 36 << 20 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 raw string literal (content)")
+ << 36 << 38 << 37 << 8 << C_STRING;
+ QTest::newRow("user-defined UTF-16 raw string literal (suffix 1)")
+ << 37 << 9 << 37 << 10 << C_KEYWORD;
+ QTest::newRow("user-defined UTF-16 raw string literal (suffix 2)")
+ << 37 << 11 << 37 << 12 << C_OPERATOR;
+ QTest::newRow("multi-line user-defined UTF-16 string literal (prefix)")
+ << 38 << 17 << 38 << 17 << C_KEYWORD;
+ QTest::newRow("multi-line user-defined UTF-16 string literal (content)")
+ << 38 << 18 << 39 << 3 << C_STRING;
+ QTest::newRow("multi-line user-defined UTF-16 string literal (suffix)")
+ << 39 << 4 << 39 << 5 << C_OPERATOR;
}
void CppHighlighterTest::test()
diff --git a/src/plugins/cppeditor/cpphighlighter.h b/src/plugins/cppeditor/cpphighlighter.h
index 358fcb2760..1728ffeb77 100644
--- a/src/plugins/cppeditor/cpphighlighter.h
+++ b/src/plugins/cppeditor/cpphighlighter.h
@@ -29,6 +29,7 @@ private:
void highlightWord(QStringView word, int position, int length);
bool highlightRawStringLiteral(QStringView text, const CPlusPlus::Token &tk,
const QString &inheritedSuffix);
+ void highlightStringLiteral(QStringView text, const CPlusPlus::Token &tk);
void highlightDoxygenComment(const QString &text, int position,
int length);
diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp
index c2a9ead6b7..caad2e0937 100644
--- a/src/plugins/cppeditor/cppincludehierarchy.cpp
+++ b/src/plugins/cppeditor/cppincludehierarchy.cpp
@@ -27,6 +27,7 @@
#include <utils/navigationtreeview.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QCoreApplication>
@@ -382,6 +383,7 @@ CppIncludeHierarchyWidget::CppIncludeHierarchyWidget()
this, &CppIncludeHierarchyWidget::perform);
m_toggleSync = new QToolButton(this);
+ StyleHelper::setPanelWidget(m_toggleSync);
m_toggleSync->setIcon(Utils::Icons::LINK_TOOLBAR.icon());
m_toggleSync->setCheckable(true);
m_toggleSync->setToolTip(Tr::tr("Synchronize with Editor"));
diff --git a/src/plugins/cppeditor/cppincludesfilter.cpp b/src/plugins/cppeditor/cppincludesfilter.cpp
index 9f637f6582..9711b6f52d 100644
--- a/src/plugins/cppeditor/cppincludesfilter.cpp
+++ b/src/plugins/cppeditor/cppincludesfilter.cpp
@@ -7,11 +7,11 @@
#include "cppeditortr.h"
#include "cppmodelmanager.h"
-#include <cplusplus/CppDocument.h>
#include <coreplugin/editormanager/documentmodel.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
using namespace Core;
using namespace ProjectExplorer;
@@ -19,79 +19,33 @@ using namespace Utils;
namespace CppEditor::Internal {
-class CppIncludesIterator final : public BaseFileFilter::Iterator
-{
-public:
- CppIncludesIterator(CPlusPlus::Snapshot snapshot, const QSet<FilePath> &seedPaths)
- : m_snapshot(snapshot),
- m_paths(seedPaths)
- {
- toFront();
- }
-
- void toFront() override;
- bool hasNext() const override;
- Utils::FilePath next() override;
- Utils::FilePath filePath() const override;
-
-private:
- void fetchMore();
-
- CPlusPlus::Snapshot m_snapshot;
- QSet<FilePath> m_paths;
- QSet<FilePath> m_queuedPaths;
- QSet<FilePath> m_allResultPaths;
- FilePaths m_resultQueue;
- FilePath m_currentPath;
-};
-
-
-
-void CppIncludesIterator::toFront()
-{
- m_queuedPaths = m_paths;
- m_allResultPaths.clear();
- m_resultQueue.clear();
- fetchMore();
-}
-
-bool CppIncludesIterator::hasNext() const
-{
- return !m_resultQueue.isEmpty();
-}
-
-FilePath CppIncludesIterator::next()
+static FilePaths generateFilePaths(const QFuture<void> &future,
+ const CPlusPlus::Snapshot &snapshot,
+ const std::unordered_set<FilePath> &inputFilePaths)
{
- if (m_resultQueue.isEmpty())
- return {};
- m_currentPath = m_resultQueue.takeFirst();
- if (m_resultQueue.isEmpty())
- fetchMore();
- return m_currentPath;
-}
-
-FilePath CppIncludesIterator::filePath() const
-{
- return m_currentPath;
-}
-
-void CppIncludesIterator::fetchMore()
-{
- while (!m_queuedPaths.isEmpty() && m_resultQueue.isEmpty()) {
- const FilePath filePath = *m_queuedPaths.begin();
- m_queuedPaths.remove(filePath);
- CPlusPlus::Document::Ptr doc = m_snapshot.document(filePath);
+ FilePaths results;
+ std::unordered_set<FilePath> resultsCache;
+ std::unordered_set<FilePath> queuedPaths = inputFilePaths;
+
+ while (!queuedPaths.empty()) {
+ if (future.isCanceled())
+ return {};
+
+ const auto iterator = queuedPaths.cbegin();
+ const FilePath filePath = *iterator;
+ queuedPaths.erase(iterator);
+ const CPlusPlus::Document::Ptr doc = snapshot.document(filePath);
if (!doc)
continue;
const FilePaths includedFiles = doc->includedFiles();
- for (const FilePath &includedPath : includedFiles ) {
- if (!m_allResultPaths.contains(includedPath)) {
- m_allResultPaths.insert(includedPath);
- m_queuedPaths.insert(includedPath);
- m_resultQueue.append(includedPath);
+ for (const FilePath &includedFile : includedFiles) {
+ if (resultsCache.emplace(includedFile).second) {
+ queuedPaths.emplace(includedFile);
+ results.append(includedFile);
}
}
}
+ return results;
}
CppIncludesFilter::CppIncludesFilter()
@@ -99,61 +53,50 @@ CppIncludesFilter::CppIncludesFilter()
setId(Constants::INCLUDES_FILTER_ID);
setDisplayName(Tr::tr(Constants::INCLUDES_FILTER_DISPLAY_NAME));
setDescription(
- Tr::tr("Matches all files that are included by all C++ files in all projects. Append "
+ Tr::tr("Locates files that are included by C++ files of any open project. Append "
"\"+<number>\" or \":<number>\" to jump to the given line number. Append another "
"\"+<number>\" or \":<number>\" to jump to the column number as well."));
setDefaultShortcutString("ai");
setDefaultIncludedByDefault(true);
+ const auto invalidate = [this] { m_cache.invalidate(); };
+ setRefreshRecipe(Tasking::Sync([invalidate] { invalidate(); return true; }));
setPriority(ILocatorFilter::Low);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(CppModelManager::instance(), &CppModelManager::documentUpdated,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(CppModelManager::instance(), &CppModelManager::aboutToRemoveFiles,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::rowsInserted,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::rowsRemoved,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::dataChanged,
- this, &CppIncludesFilter::markOutdated);
+ this, invalidate);
connect(DocumentModel::model(), &QAbstractItemModel::modelReset,
- this, &CppIncludesFilter::markOutdated);
-}
+ this, invalidate);
-void CppIncludesFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- if (m_needsUpdate) {
- m_needsUpdate = false;
- QSet<FilePath> seedPaths;
- for (Project *project : SessionManager::projects()) {
+ const auto generatorProvider = [] {
+ // This body runs in main thread
+ std::unordered_set<FilePath> inputFilePaths;
+ for (Project *project : ProjectManager::projects()) {
const FilePaths allFiles = project->files(Project::SourceFiles);
- for (const FilePath &filePath : allFiles )
- seedPaths.insert(filePath);
+ for (const FilePath &filePath : allFiles)
+ inputFilePaths.insert(filePath);
}
const QList<DocumentModel::Entry *> entries = DocumentModel::entries();
for (DocumentModel::Entry *entry : entries) {
if (entry)
- seedPaths.insert(entry->filePath());
+ inputFilePaths.insert(entry->filePath());
}
- CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
- setFileIterator(new CppIncludesIterator(snapshot, seedPaths));
- }
- BaseFileFilter::prepareSearch(entry);
-}
-
-void CppIncludesFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- QMetaObject::invokeMethod(this, &CppIncludesFilter::markOutdated, Qt::QueuedConnection);
-}
-
-void CppIncludesFilter::markOutdated()
-{
- m_needsUpdate = true;
- setFileIterator(nullptr); // clean up
+ const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
+ return [snapshot, inputFilePaths](const QFuture<void> &future) {
+ // This body runs in non-main thread
+ return generateFilePaths(future, snapshot, inputFilePaths);
+ };
+ };
+ m_cache.setGeneratorProvider(generatorProvider);
}
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppincludesfilter.h b/src/plugins/cppeditor/cppincludesfilter.h
index 6ae5dbb91c..4ab5ffba97 100644
--- a/src/plugins/cppeditor/cppincludesfilter.h
+++ b/src/plugins/cppeditor/cppincludesfilter.h
@@ -3,24 +3,18 @@
#pragma once
-#include <coreplugin/locator/basefilefilter.h>
+#include <coreplugin/locator/ilocatorfilter.h>
namespace CppEditor::Internal {
-class CppIncludesFilter : public Core::BaseFileFilter
+class CppIncludesFilter : public Core::ILocatorFilter
{
public:
CppIncludesFilter();
- // ILocatorFilter interface
-public:
- void prepareSearch(const QString &entry) override;
- void refresh(QFutureInterface<void> &future) override;
-
private:
- void markOutdated();
-
- bool m_needsUpdate = true;
+ Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
+ Core::LocatorFileCache m_cache;
};
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppindexingsupport.cpp b/src/plugins/cppeditor/cppindexingsupport.cpp
index 2d0e2b5519..22ff93cdaa 100644
--- a/src/plugins/cppeditor/cppindexingsupport.cpp
+++ b/src/plugins/cppeditor/cppindexingsupport.cpp
@@ -10,13 +10,13 @@
#include "cppsourceprocessor.h"
#include "searchsymbols.h"
-#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cplusplus/LookupContext.h>
+#include <utils/async.h>
#include <utils/filepath.h>
-#include <utils/runextensions.h>
+#include <utils/searchresultitem.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -54,7 +54,7 @@ class WriteTaskFileForDiagnostics
public:
WriteTaskFileForDiagnostics()
{
- const QString fileName = Utils::TemporaryDirectory::masterDirectoryPath()
+ const QString fileName = TemporaryDirectory::masterDirectoryPath()
+ "/qtc_findErrorsIndexing.diagnostics."
+ QDateTime::currentDateTime().toString("yyMMdd_HHmm") + ".tasks";
@@ -116,8 +116,7 @@ void classifyFiles(const QSet<QString> &files, QStringList *headers, QStringList
}
}
-void indexFindErrors(QFutureInterface<void> &indexingFuture,
- const ParseParams params)
+void indexFindErrors(QPromise<void> &promise, const ParseParams params)
{
QStringList sources, headers;
classifyFiles(params.sourceFiles, &headers, &sources);
@@ -130,7 +129,7 @@ void indexFindErrors(QFutureInterface<void> &indexingFuture,
timer.start();
for (int i = 0, end = files.size(); i < end ; ++i) {
- if (indexingFuture.isCanceled())
+ if (promise.isCanceled())
break;
const QString file = files.at(i);
@@ -140,7 +139,7 @@ void indexFindErrors(QFutureInterface<void> &indexingFuture,
BuiltinEditorDocumentParser parser(FilePath::fromString(file));
parser.setReleaseSourceAndAST(false);
parser.update({CppModelManager::instance()->workingCopy(), nullptr,
- Utils::Language::Cxx, false});
+ Language::Cxx, false});
CPlusPlus::Document::Ptr document = parser.document();
QTC_ASSERT(document, return);
@@ -153,15 +152,14 @@ void indexFindErrors(QFutureInterface<void> &indexingFuture,
document->releaseSourceAndAST();
- indexingFuture.setProgressValue(i + 1);
+ promise.setProgressValue(i + 1);
}
const QString elapsedTime = Utils::formatElapsedTime(timer.elapsed());
qDebug("FindErrorsIndexing: %s", qPrintable(elapsedTime));
}
-void index(QFutureInterface<void> &indexingFuture,
- const ParseParams params)
+void index(QPromise<void> &promise, const ParseParams params)
{
QScopedPointer<Internal::CppSourceProcessor> sourceProcessor(CppModelManager::createSourceProcessor());
sourceProcessor->setFileSizeLimitInMb(params.indexerFileSizeLimitInMb);
@@ -190,7 +188,7 @@ void index(QFutureInterface<void> &indexingFuture,
qCDebug(indexerLog) << "About to index" << files.size() << "files.";
for (int i = 0; i < files.size(); ++i) {
- if (indexingFuture.isCanceled())
+ if (promise.isCanceled())
break;
const QString fileName = files.at(i);
@@ -216,7 +214,7 @@ void index(QFutureInterface<void> &indexingFuture,
sourceProcessor->setHeaderPaths(headerPaths);
sourceProcessor->run(FilePath::fromString(fileName));
- indexingFuture.setProgressValue(files.size() - sourceProcessor->todo().size());
+ promise.setProgressValue(files.size() - sourceProcessor->todo().size());
if (isSourceFile)
sourceProcessor->resetEnvironment();
@@ -224,29 +222,29 @@ void index(QFutureInterface<void> &indexingFuture,
qCDebug(indexerLog) << "Indexing finished.";
}
-void parse(QFutureInterface<void> &indexingFuture, const ParseParams params)
+void parse(QPromise<void> &promise, const ParseParams params)
{
const QSet<QString> &files = params.sourceFiles;
if (files.isEmpty())
return;
- indexingFuture.setProgressRange(0, files.size());
+ promise.setProgressRange(0, files.size());
if (CppIndexingSupport::isFindErrorsIndexingActive())
- indexFindErrors(indexingFuture, params);
+ indexFindErrors(promise, params);
else
- index(indexingFuture, params);
+ index(promise, params);
- indexingFuture.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
CppModelManager::instance()->finishedRefreshingSourceFiles(files);
}
} // anonymous namespace
-void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
+void SymbolSearcher::runSearch(QPromise<SearchResultItem> &promise)
{
- future.setProgressRange(0, m_snapshot.size());
- future.setProgressValue(0);
+ promise.setProgressRange(0, m_snapshot.size());
+ promise.setProgressValue(0);
int progress = 0;
SearchSymbols search;
@@ -262,12 +260,11 @@ void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
: QRegularExpression::CaseInsensitiveOption));
matcher.optimize();
while (it != m_snapshot.end()) {
- if (future.isPaused())
- future.waitForResume();
- if (future.isCanceled())
+ promise.suspendIfRequested();
+ if (promise.isCanceled())
break;
if (m_fileNames.isEmpty() || m_fileNames.contains(it.value()->filePath().path())) {
- QVector<Core::SearchResultItem> resultItems;
+ SearchResultItems resultItems;
auto filter = [&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
if (matcher.match(info->symbolName()).hasMatch()) {
QString text = info->symbolName();
@@ -280,7 +277,7 @@ void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
text = info->representDeclaration();
}
- Core::SearchResultItem item;
+ SearchResultItem item;
item.setPath(scope.split(QLatin1String("::"), Qt::SkipEmptyParts));
item.setLineText(text);
item.setIcon(info->icon());
@@ -291,24 +288,16 @@ void SymbolSearcher::runSearch(QFutureInterface<Core::SearchResultItem> &future)
return IndexItem::Recurse;
};
search(it.value())->visitAllChildren(filter);
- if (!resultItems.isEmpty())
- future.reportResults(resultItems);
+ for (const SearchResultItem &item : std::as_const(resultItems))
+ promise.addResult(item);
}
++it;
++progress;
- future.setProgressValue(progress);
+ promise.setProgressValue(progress);
}
- if (future.isPaused())
- future.waitForResume();
+ promise.suspendIfRequested();
}
-CppIndexingSupport::CppIndexingSupport()
-{
- m_synchronizer.setCancelOnWait(true);
-}
-
-CppIndexingSupport::~CppIndexingSupport() = default;
-
bool CppIndexingSupport::isFindErrorsIndexingActive()
{
return Utils::qtcEnvironmentVariable("QTC_FIND_ERRORS_INDEXING") == "1";
@@ -325,7 +314,7 @@ QFuture<void> CppIndexingSupport::refreshSourceFiles(const QSet<QString> &source
params.workingCopy = mgr->workingCopy();
params.sourceFiles = sourceFiles;
- QFuture<void> result = Utils::runAsync(mgr->sharedThreadPool(), parse, params);
+ QFuture<void> result = Utils::asyncRun(mgr->sharedThreadPool(), parse, params);
m_synchronizer.addFuture(result);
if (mode == CppModelManager::ForcedProgressNotification || sourceFiles.count() > 1) {
diff --git a/src/plugins/cppeditor/cppindexingsupport.h b/src/plugins/cppeditor/cppindexingsupport.h
index 584632a4f0..b5aa68585d 100644
--- a/src/plugins/cppeditor/cppindexingsupport.h
+++ b/src/plugins/cppeditor/cppindexingsupport.h
@@ -11,7 +11,7 @@
#include <QFuture>
-namespace Core { class SearchResultItem; }
+namespace Utils { class SearchResultItem; }
namespace CppEditor {
@@ -44,7 +44,7 @@ public:
};
SymbolSearcher(const SymbolSearcher::Parameters &parameters, const QSet<QString> &fileNames);
- void runSearch(QFutureInterface<Core::SearchResultItem> &future);
+ void runSearch(QPromise<Utils::SearchResultItem> &promise);
private:
const CPlusPlus::Snapshot m_snapshot;
@@ -55,9 +55,6 @@ private:
class CPPEDITOR_EXPORT CppIndexingSupport
{
public:
- CppIndexingSupport();
- ~CppIndexingSupport();
-
static bool isFindErrorsIndexingActive();
QFuture<void> refreshSourceFiles(const QSet<QString> &sourceFiles,
diff --git a/src/plugins/cppeditor/cpplocatordata.cpp b/src/plugins/cppeditor/cpplocatordata.cpp
index 9b260ccc2e..a0899b0ddd 100644
--- a/src/plugins/cppeditor/cpplocatordata.cpp
+++ b/src/plugins/cppeditor/cpplocatordata.cpp
@@ -20,6 +20,22 @@ CppLocatorData::CppLocatorData()
m_pendingDocuments.reserve(MaxPendingDocuments);
}
+QList<IndexItem::Ptr> CppLocatorData::findSymbols(IndexItem::ItemType type,
+ const QString &symbolName) const
+{
+ QList<IndexItem::Ptr> matches;
+ filterAllFiles([&](const IndexItem::Ptr &info) {
+ if (info->type() & type) {
+ if (info->symbolName() == symbolName || info->scopedSymbolName() == symbolName)
+ matches << info;
+ }
+ if (info->type() & IndexItem::Enum)
+ return IndexItem::Continue;
+ return IndexItem::Recurse;
+ });
+ return matches;
+}
+
void CppLocatorData::onDocumentUpdated(const CPlusPlus::Document::Ptr &document)
{
QMutexLocker locker(&m_pendingDocumentsMutex);
diff --git a/src/plugins/cppeditor/cpplocatordata.h b/src/plugins/cppeditor/cpplocatordata.h
index 2170deb75d..d981eb7c04 100644
--- a/src/plugins/cppeditor/cpplocatordata.h
+++ b/src/plugins/cppeditor/cpplocatordata.h
@@ -33,6 +33,8 @@ public:
return;
}
+ QList<IndexItem::Ptr> findSymbols(IndexItem::ItemType type, const QString &symbolName) const;
+
public slots:
void onDocumentUpdated(const CPlusPlus::Document::Ptr &document);
void onAboutToRemoveFiles(const QStringList &files);
diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp
index 083c5df1b2..cd1add7786 100644
--- a/src/plugins/cppeditor/cpplocatorfilter.cpp
+++ b/src/plugins/cppeditor/cpplocatorfilter.cpp
@@ -4,60 +4,49 @@
#include "cpplocatorfilter.h"
#include "cppeditorconstants.h"
+#include "cppeditorplugin.h"
#include "cppeditortr.h"
+#include "cpplocatordata.h"
+#include "cppmodelmanager.h"
#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
+#include <utils/fuzzymatcher.h>
#include <QRegularExpression>
-#include <algorithm>
-#include <numeric>
+using namespace Core;
+using namespace CPlusPlus;
+using namespace Utils;
namespace CppEditor {
-CppLocatorFilter::CppLocatorFilter(CppLocatorData *locatorData)
- : m_data(locatorData)
-{
- setId(Constants::LOCATOR_FILTER_ID);
- setDisplayName(Tr::tr(Constants::LOCATOR_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(":");
- setDefaultIncludedByDefault(false);
-}
+using EntryFromIndex = std::function<LocatorFilterEntry(const IndexItem::Ptr &)>;
-CppLocatorFilter::~CppLocatorFilter() = default;
-
-Core::LocatorFilterEntry CppLocatorFilter::filterEntryFromIndexItem(IndexItem::Ptr info)
-{
- const QVariant id = QVariant::fromValue(info);
- Core::LocatorFilterEntry filterEntry(this, info->scopedSymbolName(), id, info->icon());
- if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum)
- filterEntry.extraInfo = info->shortNativeFilePath();
- else
- filterEntry.extraInfo = info->symbolType();
-
- return filterEntry;
-}
-
-QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+void matchesFor(QPromise<void> &promise, const LocatorStorage &storage,
+ IndexItem::ItemType wantedType, const EntryFromIndex &converter)
{
- QList<Core::LocatorFilterEntry> entries[int(MatchLevel::Count)];
- const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
- const IndexItem::ItemType wanted = matchTypes();
-
- const QRegularExpression regexp = createRegExp(entry);
+ const QString input = storage.input();
+ LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)];
+ const Qt::CaseSensitivity caseSensitivityForPrefix = ILocatorFilter::caseSensitivity(input);
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(input);
if (!regexp.isValid())
- return {};
- const bool hasColonColon = entry.contains("::");
- const QRegularExpression shortRegexp =
- hasColonColon ? createRegExp(entry.mid(entry.lastIndexOf("::") + 2)) : regexp;
+ return;
- m_data->filterAllFiles([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult {
- if (future.isCanceled())
+ const bool hasColonColon = input.contains("::");
+ const QRegularExpression shortRegexp = hasColonColon
+ ? ILocatorFilter::createRegExp(input.mid(input.lastIndexOf("::") + 2)) : regexp;
+ CppLocatorData *locatorData = CppModelManager::instance()->locatorData();
+ locatorData->filterAllFiles([&](const IndexItem::Ptr &info) {
+ if (promise.isCanceled())
return IndexItem::Break;
const IndexItem::ItemType type = info->type();
- if (type & wanted) {
+ if (type & wantedType) {
const QString symbolName = info->symbolName();
QString matchString = hasColonColon ? info->scopedSymbolName() : symbolName;
int matchOffset = hasColonColon ? matchString.size() - symbolName.size() : 0;
@@ -70,7 +59,7 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
}
if (match.hasMatch()) {
- Core::LocatorFilterEntry filterEntry = filterEntryFromIndexItem(info);
+ LocatorFilterEntry filterEntry = converter(info);
// Highlight the matched characters, therefore it may be necessary
// to update the match if the displayName is different from matchString
@@ -78,103 +67,323 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor(
match = shortRegexp.match(filterEntry.displayName);
matchOffset = 0;
}
- filterEntry.highlightInfo = highlightInfo(match);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
if (matchInParameterList && filterEntry.highlightInfo.startsDisplay.isEmpty()) {
match = regexp.match(filterEntry.extraInfo);
- filterEntry.highlightInfo
- = highlightInfo(match, Core::LocatorFilterEntry::HighlightInfo::ExtraInfo);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(
+ match, LocatorFilterEntry::HighlightInfo::ExtraInfo);
} else if (matchOffset > 0) {
for (int &start : filterEntry.highlightInfo.startsDisplay)
start -= matchOffset;
}
if (matchInParameterList)
- entries[int(MatchLevel::Normal)].append(filterEntry);
- else if (filterEntry.displayName.startsWith(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Best)].append(filterEntry);
- else if (filterEntry.displayName.contains(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Better)].append(filterEntry);
+ entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry);
+ else if (filterEntry.displayName.startsWith(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Best)].append(filterEntry);
+ else if (filterEntry.displayName.contains(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Better)].append(filterEntry);
else
- entries[int(MatchLevel::Good)].append(filterEntry);
+ entries[int(ILocatorFilter::MatchLevel::Good)].append(filterEntry);
}
}
if (info->type() & IndexItem::Enum)
return IndexItem::Continue;
- else
- return IndexItem::Recurse;
+ return IndexItem::Recurse;
});
for (auto &entry : entries) {
if (entry.size() < 1000)
- Utils::sort(entry, Core::LocatorFilterEntry::compareLexigraphically);
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
}
- return std::accumulate(std::begin(entries), std::end(entries), QList<Core::LocatorFilterEntry>());
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
+}
+
+LocatorMatcherTask locatorMatcher(IndexItem::ItemType type, const EntryFromIndex &converter)
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matchesFor, *storage, type, converter);
+ };
+ return {AsyncTask<void>(onSetup), storage};
+}
+
+LocatorMatcherTask allSymbolsMatcher()
+{
+ const auto converter = [](const IndexItem::Ptr &info) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = info->scopedSymbolName();
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum)
+ filterEntry.extraInfo = info->shortNativeFilePath();
+ else
+ filterEntry.extraInfo = info->symbolType();
+ return filterEntry;
+ };
+ return locatorMatcher(IndexItem::All, converter);
}
-void CppLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
+LocatorMatcherTask classMatcher()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData);
- Core::EditorManager::openEditorAt({info->filePath(), info->line(), info->column()},
- {},
- Core::EditorManager::AllowExternalEditor);
+ const auto converter = [](const IndexItem::Ptr &info) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = info->symbolName();
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ filterEntry.extraInfo = info->symbolScope().isEmpty()
+ ? info->shortNativeFilePath()
+ : info->symbolScope();
+ filterEntry.filePath = info->filePath();
+ return filterEntry;
+ };
+ return locatorMatcher(IndexItem::Class, converter);
}
-CppClassesFilter::CppClassesFilter(CppLocatorData *locatorData)
- : CppLocatorFilter(locatorData)
+LocatorMatcherTask functionMatcher()
+{
+ const auto converter = [](const IndexItem::Ptr &info) {
+ QString name = info->symbolName();
+ QString extraInfo = info->symbolScope();
+ info->unqualifiedNameAndScope(name, &name, &extraInfo);
+ if (extraInfo.isEmpty())
+ extraInfo = info->shortNativeFilePath();
+ else
+ extraInfo.append(" (" + info->filePath().fileName() + ')');
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = name + info->symbolType();
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ filterEntry.extraInfo = extraInfo;
+
+ return filterEntry;
+ };
+ return locatorMatcher(IndexItem::Function, converter);
+}
+
+QList<IndexItem::Ptr> itemsOfCurrentDocument(const FilePath &currentFileName)
+{
+ if (currentFileName.isEmpty())
+ return {};
+
+ QList<IndexItem::Ptr> results;
+ const Snapshot snapshot = CppModelManager::instance()->snapshot();
+ if (const Document::Ptr thisDocument = snapshot.document(currentFileName)) {
+ SearchSymbols search;
+ search.setSymbolsToSearchFor(SymbolSearcher::Declarations |
+ SymbolSearcher::Enums |
+ SymbolSearcher::Functions |
+ SymbolSearcher::Classes);
+ IndexItem::Ptr rootNode = search(thisDocument);
+ rootNode->visitAllChildren([&](const IndexItem::Ptr &info) {
+ results.append(info);
+ return IndexItem::Recurse;
+ });
+ }
+ return results;
+}
+
+LocatorFilterEntry::HighlightInfo highlightInfo(const QRegularExpressionMatch &match,
+ LocatorFilterEntry::HighlightInfo::DataType dataType)
+{
+ const FuzzyMatcher::HighlightingPositions positions =
+ FuzzyMatcher::highlightingPositions(match);
+
+ return LocatorFilterEntry::HighlightInfo(positions.starts, positions.lengths, dataType);
+}
+
+void matchesForCurrentDocument(QPromise<void> &promise, const LocatorStorage &storage,
+ const FilePath &currentFileName)
+{
+ const QString input = storage.input();
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(input);
+ if (!regexp.isValid())
+ return;
+
+ struct Entry
+ {
+ LocatorFilterEntry entry;
+ IndexItem::Ptr info;
+ };
+ QList<Entry> goodEntries;
+ QList<Entry> betterEntries;
+ const QList<IndexItem::Ptr> items = itemsOfCurrentDocument(currentFileName);
+ for (const IndexItem::Ptr &info : items) {
+ if (promise.isCanceled())
+ break;
+
+ QString matchString = info->symbolName();
+ if (info->type() == IndexItem::Declaration)
+ matchString = info->representDeclaration();
+ else if (info->type() == IndexItem::Function)
+ matchString += info->symbolType();
+
+ QRegularExpressionMatch match = regexp.match(matchString);
+ if (match.hasMatch()) {
+ const bool betterMatch = match.capturedStart() == 0;
+ QString name = matchString;
+ QString extraInfo = info->symbolScope();
+ if (info->type() == IndexItem::Function) {
+ if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) {
+ name += info->symbolType();
+ match = regexp.match(name);
+ }
+ }
+
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = name;
+ filterEntry.displayIcon = info->icon();
+ filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()};
+ filterEntry.extraInfo = extraInfo;
+ if (match.hasMatch()) {
+ filterEntry.highlightInfo = highlightInfo(match,
+ LocatorFilterEntry::HighlightInfo::DisplayName);
+ } else {
+ match = regexp.match(extraInfo);
+ filterEntry.highlightInfo =
+ highlightInfo(match, LocatorFilterEntry::HighlightInfo::ExtraInfo);
+ }
+
+ if (betterMatch)
+ betterEntries.append({filterEntry, info});
+ else
+ goodEntries.append({filterEntry, info});
+ }
+ }
+
+ // entries are unsorted by design!
+ betterEntries += goodEntries;
+
+ QHash<QString, QList<Entry>> possibleDuplicates;
+ for (const Entry &e : std::as_const(betterEntries))
+ possibleDuplicates[e.info->scopedSymbolName() + e.info->symbolType()] << e;
+ for (auto it = possibleDuplicates.cbegin(); it != possibleDuplicates.cend(); ++it) {
+ const QList<Entry> &duplicates = it.value();
+ if (duplicates.size() == 1)
+ continue;
+ QList<Entry> declarations;
+ QList<Entry> definitions;
+ for (const Entry &candidate : duplicates) {
+ const IndexItem::Ptr info = candidate.info;
+ if (info->type() != IndexItem::Function)
+ break;
+ if (info->isFunctionDefinition())
+ definitions << candidate;
+ else
+ declarations << candidate;
+ }
+ if (definitions.size() == 1
+ && declarations.size() + definitions.size() == duplicates.size()) {
+ for (const Entry &decl : std::as_const(declarations)) {
+ Utils::erase(betterEntries, [&decl](const Entry &e) {
+ return e.info == decl.info;
+ });
+ }
+ }
+ }
+ storage.reportOutput(Utils::transform(betterEntries,
+ [](const Entry &entry) { return entry.entry; }));
+}
+
+FilePath currentFileName()
+{
+ IEditor *currentEditor = EditorManager::currentEditor();
+ return currentEditor ? currentEditor->document()->filePath() : FilePath();
+}
+
+LocatorMatcherTask currentDocumentMatcher()
+{
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [=](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matchesForCurrentDocument, *storage, currentFileName());
+ };
+ return {AsyncTask<void>(onSetup), storage};
+}
+
+using MatcherCreator = std::function<Core::LocatorMatcherTask()>;
+
+static MatcherCreator creatorForType(MatcherType type)
+{
+ switch (type) {
+ case MatcherType::AllSymbols: return &allSymbolsMatcher;
+ case MatcherType::Classes: return &classMatcher;
+ case MatcherType::Functions: return &functionMatcher;
+ case MatcherType::CurrentDocumentSymbols: return &currentDocumentMatcher;
+ }
+ return {};
+}
+
+LocatorMatcherTasks cppMatchers(MatcherType type)
+{
+ const MatcherCreator creator = creatorForType(type);
+ if (!creator)
+ return {};
+ return {creator()};
+}
+
+CppAllSymbolsFilter::CppAllSymbolsFilter()
+{
+ setId(Constants::LOCATOR_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::LOCATOR_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LOCATOR_FILTER_DESCRIPTION));
+ setDefaultShortcutString(":");
+}
+
+LocatorMatcherTasks CppAllSymbolsFilter::matchers()
+{
+ return {allSymbolsMatcher()};
+}
+
+
+CppClassesFilter::CppClassesFilter()
{
setId(Constants::CLASSES_FILTER_ID);
setDisplayName(Tr::tr(Constants::CLASSES_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::CLASSES_FILTER_DESCRIPTION));
setDefaultShortcutString("c");
- setDefaultIncludedByDefault(false);
}
-CppClassesFilter::~CppClassesFilter() = default;
-
-Core::LocatorFilterEntry CppClassesFilter::filterEntryFromIndexItem(IndexItem::Ptr info)
+LocatorMatcherTasks CppClassesFilter::matchers()
{
- const QVariant id = QVariant::fromValue(info);
- Core::LocatorFilterEntry filterEntry(this, info->symbolName(), id, info->icon());
- filterEntry.extraInfo = info->symbolScope().isEmpty()
- ? info->shortNativeFilePath()
- : info->symbolScope();
- filterEntry.filePath = info->filePath();
- return filterEntry;
+ return {classMatcher()};
}
-CppFunctionsFilter::CppFunctionsFilter(CppLocatorData *locatorData)
- : CppLocatorFilter(locatorData)
+CppFunctionsFilter::CppFunctionsFilter()
{
setId(Constants::FUNCTIONS_FILTER_ID);
setDisplayName(Tr::tr(Constants::FUNCTIONS_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::FUNCTIONS_FILTER_DESCRIPTION));
setDefaultShortcutString("m");
- setDefaultIncludedByDefault(false);
}
-CppFunctionsFilter::~CppFunctionsFilter() = default;
-
-Core::LocatorFilterEntry CppFunctionsFilter::filterEntryFromIndexItem(IndexItem::Ptr info)
+LocatorMatcherTasks CppFunctionsFilter::matchers()
{
- const QVariant id = QVariant::fromValue(info);
-
- QString name = info->symbolName();
- QString extraInfo = info->symbolScope();
- info->unqualifiedNameAndScope(name, &name, &extraInfo);
- if (extraInfo.isEmpty()) {
- extraInfo = info->shortNativeFilePath();
- } else {
- extraInfo.append(" (" + info->filePath().fileName() + ')');
- }
+ return {functionMatcher()};
+}
- Core::LocatorFilterEntry filterEntry(this, name + info->symbolType(), id, info->icon());
- filterEntry.extraInfo = extraInfo;
+CppCurrentDocumentFilter::CppCurrentDocumentFilter()
+{
+ setId(Constants::CURRENT_DOCUMENT_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DESCRIPTION));
+ setDefaultShortcutString(".");
+ setPriority(High);
+}
- return filterEntry;
+LocatorMatcherTasks CppCurrentDocumentFilter::matchers()
+{
+ return {currentDocumentMatcher()};
}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cpplocatorfilter.h b/src/plugins/cppeditor/cpplocatorfilter.h
index 9770afe21b..0b012fac6b 100644
--- a/src/plugins/cppeditor/cpplocatorfilter.h
+++ b/src/plugins/cppeditor/cpplocatorfilter.h
@@ -4,58 +4,47 @@
#pragma once
#include "cppeditor_global.h"
-#include "cpplocatordata.h"
-#include "searchsymbols.h"
#include <coreplugin/locator/ilocatorfilter.h>
namespace CppEditor {
-class CPPEDITOR_EXPORT CppLocatorFilter : public Core::ILocatorFilter
-{
- Q_OBJECT
+Core::LocatorMatcherTasks CPPEDITOR_EXPORT cppMatchers(Core::MatcherType type);
+class CppAllSymbolsFilter : public Core::ILocatorFilter
+{
public:
- explicit CppLocatorFilter(CppLocatorData *locatorData);
- ~CppLocatorFilter() override;
+ CppAllSymbolsFilter();
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+private:
+ Core::LocatorMatcherTasks matchers() final;
+};
-protected:
- virtual IndexItem::ItemType matchTypes() const { return IndexItem::All; }
- virtual Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info);
+class CppClassesFilter : public Core::ILocatorFilter
+{
+public:
+ CppClassesFilter();
-protected:
- CppLocatorData *m_data = nullptr;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class CPPEDITOR_EXPORT CppClassesFilter : public CppLocatorFilter
+class CppFunctionsFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit CppClassesFilter(CppLocatorData *locatorData);
- ~CppClassesFilter() override;
+ CppFunctionsFilter();
-protected:
- IndexItem::ItemType matchTypes() const override { return IndexItem::Class; }
- Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class CPPEDITOR_EXPORT CppFunctionsFilter : public CppLocatorFilter
+class CppCurrentDocumentFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit CppFunctionsFilter(CppLocatorData *locatorData);
- ~CppFunctionsFilter() override;
+ CppCurrentDocumentFilter();
-protected:
- IndexItem::ItemType matchTypes() const override { return IndexItem::Function; }
- Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cpplocatorfilter_test.cpp b/src/plugins/cppeditor/cpplocatorfilter_test.cpp
index 598a6e8564..18e9b933cb 100644
--- a/src/plugins/cppeditor/cpplocatorfilter_test.cpp
+++ b/src/plugins/cppeditor/cpplocatorfilter_test.cpp
@@ -3,17 +3,12 @@
#include "cpplocatorfilter_test.h"
-#include "cppcurrentdocumentfilter.h"
-#include "cpplocatorfilter.h"
-#include "cppmodelmanager.h"
#include "cpptoolstestcase.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/locator/locatorfiltertest.h>
#include <coreplugin/testdatadir.h>
-#include <extensionsystem/pluginmanager.h>
#include <utils/environment.h>
-#include <utils/fileutils.h>
#include <QDebug>
#include <QFileInfo>
@@ -21,7 +16,6 @@
using namespace Core;
using namespace Core::Tests;
-using namespace ExtensionSystem;
using namespace Utils;
namespace CppEditor::Internal {
@@ -31,23 +25,22 @@ const bool debug = qtcEnvironmentVariable("QTC_DEBUG_CPPLOCATORFILTERTESTCASE")
QTC_DECLARE_MYTESTDATADIR("../../../tests/cpplocators/")
-class CppLocatorFilterTestCase
- : public BasicLocatorFilterTest
- , public CppEditor::Tests::TestCase
+class CppLocatorFilterTestCase : public CppEditor::Tests::TestCase
{
public:
- CppLocatorFilterTestCase(ILocatorFilter *filter,
+ CppLocatorFilterTestCase(const QList<LocatorMatcherTask> &matchers,
const QString &fileName,
const QString &searchText,
const ResultDataList &expectedResults)
- : BasicLocatorFilterTest(filter)
- , m_fileName(fileName)
{
QVERIFY(succeededSoFar());
- QVERIFY(!m_fileName.isEmpty());
+ QVERIFY(!fileName.isEmpty());
QVERIFY(garbageCollectGlobalSnapshot());
- ResultDataList results = ResultData::fromFilterEntryList(matchesFor(searchText));
+ QVERIFY(parseFiles(fileName));
+ const LocatorFilterEntries entries = LocatorMatcher::runBlocking(matchers, searchText);
+ QVERIFY(garbageCollectGlobalSnapshot());
+ const ResultDataList results = ResultData::fromFilterEntryList(entries);
if (debug) {
ResultData::printFilterEntries(expectedResults, "Expected:");
ResultData::printFilterEntries(results, "Results:");
@@ -55,61 +48,39 @@ public:
QVERIFY(!results.isEmpty());
QCOMPARE(results, expectedResults);
}
-
-private:
- void doBeforeLocatorRun() override { QVERIFY(parseFiles(m_fileName)); }
- void doAfterLocatorRun() override { QVERIFY(garbageCollectGlobalSnapshot()); }
-
-private:
- const QString m_fileName;
};
-class CppCurrentDocumentFilterTestCase
- : public BasicLocatorFilterTest
- , public CppEditor::Tests::TestCase
+class CppCurrentDocumentFilterTestCase : public CppEditor::Tests::TestCase
{
public:
CppCurrentDocumentFilterTestCase(const FilePath &filePath,
+ const QList<LocatorMatcherTask> &matchers,
const ResultDataList &expectedResults,
const QString &searchText = QString())
- : BasicLocatorFilterTest(CppModelManager::instance()->currentDocumentFilter())
- , m_filePath(filePath)
{
QVERIFY(succeededSoFar());
- QVERIFY(!m_filePath.isEmpty());
+ QVERIFY(!filePath.isEmpty());
- ResultDataList results = ResultData::fromFilterEntryList(matchesFor(searchText));
- if (debug) {
- ResultData::printFilterEntries(expectedResults, "Expected:");
- ResultData::printFilterEntries(results, "Results:");
- }
- QVERIFY(!results.isEmpty());
- QCOMPARE(results, expectedResults);
- }
-
-private:
- void doBeforeLocatorRun() override
- {
QVERIFY(DocumentModel::openedDocuments().isEmpty());
QVERIFY(garbageCollectGlobalSnapshot());
- m_editor = EditorManager::openEditor(m_filePath);
- QVERIFY(m_editor);
-
- QVERIFY(waitForFileInGlobalSnapshot(m_filePath));
- }
+ const auto editor = EditorManager::openEditor(filePath);
+ QVERIFY(editor);
- void doAfterLocatorRun() override
- {
- QVERIFY(closeEditorWithoutGarbageCollectorInvocation(m_editor));
+ QVERIFY(waitForFileInGlobalSnapshot(filePath));
+ const LocatorFilterEntries entries = LocatorMatcher::runBlocking(matchers, searchText);
+ QVERIFY(closeEditorWithoutGarbageCollectorInvocation(editor));
QCoreApplication::processEvents();
QVERIFY(DocumentModel::openedDocuments().isEmpty());
QVERIFY(garbageCollectGlobalSnapshot());
+ const ResultDataList results = ResultData::fromFilterEntryList(entries);
+ if (debug) {
+ ResultData::printFilterEntries(expectedResults, "Expected:");
+ ResultData::printFilterEntries(results, "Results:");
+ }
+ QVERIFY(!results.isEmpty());
+ QCOMPARE(results, expectedResults);
}
-
-private:
- IEditor *m_editor = nullptr;
- const FilePath m_filePath;
};
} // anonymous namespace
@@ -117,28 +88,22 @@ private:
void LocatorFilterTest::testLocatorFilter()
{
QFETCH(QString, testFile);
- QFETCH(ILocatorFilter *, filter);
+ QFETCH(MatcherType, matcherType);
QFETCH(QString, searchText);
QFETCH(ResultDataList, expectedResults);
Tests::VerifyCleanCppModelManager verify;
-
- CppLocatorFilterTestCase(filter, testFile, searchText, expectedResults);
+ CppLocatorFilterTestCase(LocatorMatcher::matchers(matcherType), testFile, searchText,
+ expectedResults);
}
void LocatorFilterTest::testLocatorFilter_data()
{
QTest::addColumn<QString>("testFile");
- QTest::addColumn<ILocatorFilter *>("filter");
+ QTest::addColumn<MatcherType>("matcherType");
QTest::addColumn<QString>("searchText");
QTest::addColumn<ResultDataList>("expectedResults");
- CppModelManager *cppModelManager = CppModelManager::instance();
-
- ILocatorFilter *cppFunctionsFilter = cppModelManager->functionsFilter();
- ILocatorFilter *cppClassesFilter = cppModelManager->classesFilter();
- ILocatorFilter *cppLocatorFilter = cppModelManager->locatorFilter();
-
MyTestDataDir testDirectory("testdata_basic");
QString testFile = testDirectory.file("file1.cpp");
testFile[0] = testFile[0].toLower(); // Ensure Windows path sorts after scope names.
@@ -148,7 +113,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "function"
<< ResultDataList{
ResultData("functionDefinedInClass(bool, int)",
@@ -170,7 +135,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-Sorting")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "pos"
<< ResultDataList{
ResultData("positiveNumber()", testFileShort),
@@ -181,7 +146,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-arguments")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "function*bool"
<< ResultDataList{
ResultData("functionDefinedInClass(bool, int)",
@@ -197,7 +162,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-WithNamespacePrefix")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "mynamespace::"
<< ResultDataList{
ResultData("MyClass()", "MyNamespace::MyClass (file1.cpp)"),
@@ -212,7 +177,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-WithClassPrefix")
<< testFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "MyClass::func"
<< ResultDataList{
ResultData("functionDefinedInClass(bool, int)",
@@ -233,7 +198,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppClassesFilter")
<< testFile
- << cppClassesFilter
+ << MatcherType::Classes
<< "myclass"
<< ResultDataList{
ResultData("MyClass", "<anonymous namespace>"),
@@ -243,7 +208,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppClassesFilter-WithNamespacePrefix")
<< testFile
- << cppClassesFilter
+ << MatcherType::Classes
<< "mynamespace::"
<< ResultDataList{
ResultData("MyClass", "MyNamespace")
@@ -252,7 +217,7 @@ void LocatorFilterTest::testLocatorFilter_data()
// all symbols in the left column are expected to be fully qualified.
QTest::newRow("CppLocatorFilter-filtered")
<< testFile
- << cppLocatorFilter
+ << MatcherType::AllSymbols
<< "my"
<< ResultDataList{
ResultData("MyClass", testFileShort),
@@ -278,7 +243,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppClassesFilter-ObjC")
<< objTestFile
- << cppClassesFilter
+ << MatcherType::Classes
<< "M"
<< ResultDataList{
ResultData("MyClass", objTestFileShort),
@@ -289,7 +254,7 @@ void LocatorFilterTest::testLocatorFilter_data()
QTest::newRow("CppFunctionsFilter-ObjC")
<< objTestFile
- << cppFunctionsFilter
+ << MatcherType::Functions
<< "M"
<< ResultDataList{
ResultData("anotherMethod", "MyClass (file1.mm)"),
@@ -319,7 +284,6 @@ void LocatorFilterTest::testCurrentDocumentFilter()
ResultData("functionDeclaredOnly()", "MyClass"),
ResultData("functionDefinedInClass(bool, int)", "MyClass"),
ResultData("functionDefinedOutSideClass(char)", "MyClass"),
- ResultData("functionDefinedOutSideClass(char)", "MyClass"),
ResultData("int myVariable", "MyNamespace"),
ResultData("myFunction(bool, int)", "MyNamespace"),
ResultData("MyEnum", "MyNamespace"),
@@ -332,9 +296,6 @@ void LocatorFilterTest::testCurrentDocumentFilter()
ResultData("functionDefinedOutSideClass(char)", "MyNamespace::MyClass"),
ResultData("functionDefinedOutSideClassAndNamespace(float)",
"MyNamespace::MyClass"),
- ResultData("functionDefinedOutSideClass(char)", "MyNamespace::MyClass"),
- ResultData("functionDefinedOutSideClassAndNamespace(float)",
- "MyNamespace::MyClass"),
ResultData("int myVariable", "<anonymous namespace>"),
ResultData("myFunction(bool, int)", "<anonymous namespace>"),
ResultData("MyEnum", "<anonymous namespace>"),
@@ -345,11 +306,12 @@ void LocatorFilterTest::testCurrentDocumentFilter()
ResultData("functionDeclaredOnly()", "<anonymous namespace>::MyClass"),
ResultData("functionDefinedInClass(bool, int)", "<anonymous namespace>::MyClass"),
ResultData("functionDefinedOutSideClass(char)", "<anonymous namespace>::MyClass"),
- ResultData("functionDefinedOutSideClass(char)", "<anonymous namespace>::MyClass"),
ResultData("main()", ""),
};
- CppCurrentDocumentFilterTestCase(testFile, expectedResults);
+ Tests::VerifyCleanCppModelManager verify;
+ CppCurrentDocumentFilterTestCase(
+ testFile, LocatorMatcher::matchers(MatcherType::CurrentDocumentSymbols), expectedResults);
}
void LocatorFilterTest::testCurrentDocumentHighlighting()
@@ -372,8 +334,8 @@ void LocatorFilterTest::testCurrentDocumentHighlighting()
};
Tests::VerifyCleanCppModelManager verify;
-
- CppCurrentDocumentFilterTestCase(testFile, expectedResults, searchText);
+ CppCurrentDocumentFilterTestCase(testFile,
+ LocatorMatcher::matchers(MatcherType::CurrentDocumentSymbols), expectedResults, searchText);
}
void LocatorFilterTest::testFunctionsFilterHighlighting()
@@ -394,12 +356,9 @@ void LocatorFilterTest::testFunctionsFilterHighlighting()
" ~~~ ")
};
- CppModelManager *cppModelManager = CppModelManager::instance();
- ILocatorFilter *filter = cppModelManager->functionsFilter();
-
Tests::VerifyCleanCppModelManager verify;
-
- CppLocatorFilterTestCase(filter, testFile, searchText, expectedResults);
+ CppLocatorFilterTestCase(LocatorMatcher::matchers(MatcherType::Functions), testFile,
+ searchText, expectedResults);
}
} // namespace CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp
index 8fc44622ab..07b4e67d27 100644
--- a/src/plugins/cppeditor/cppmodelmanager.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager.cpp
@@ -6,10 +6,10 @@
#include "abstracteditorsupport.h"
#include "baseeditordocumentprocessor.h"
#include "compileroptionsbuilder.h"
+#include "cppbuiltinmodelmanagersupport.h"
#include "cppcanonicalsymbol.h"
#include "cppcodemodelinspectordumper.h"
#include "cppcodemodelsettings.h"
-#include "cppcurrentdocumentfilter.h"
#include "cppeditorconstants.h"
#include "cppeditortr.h"
#include "cppfindreferences.h"
@@ -17,7 +17,6 @@
#include "cppindexingsupport.h"
#include "cpplocatordata.h"
#include "cpplocatorfilter.h"
-#include "cppbuiltinmodelmanagersupport.h"
#include "cppprojectfile.h"
#include "cppsourceprocessor.h"
#include "cpptoolsjsextension.h"
@@ -37,9 +36,11 @@
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/vcsmanager.h>
+
#include <cplusplus/ASTPath.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/TypeOfExpression.h>
+
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/buildconfiguration.h>
@@ -49,6 +50,7 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectmacro.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
@@ -60,9 +62,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/savefile.h>
#include <utils/temporarydirectory.h>
@@ -90,6 +91,7 @@
#include <sstream>
#endif
+using namespace Core;
using namespace CPlusPlus;
using namespace ProjectExplorer;
using namespace Utils;
@@ -155,7 +157,7 @@ public:
class CppModelManagerPrivate
{
public:
- void setupWatcher(const QFuture<void> &future, ProjectExplorer::Project *project,
+ void setupWatcher(const QFuture<void> &future, Project *project,
ProjectData *projectData, CppModelManager *q);
// Snapshot
@@ -164,15 +166,15 @@ public:
// Project integration
QReadWriteLock m_projectLock;
- QHash<ProjectExplorer::Project *, ProjectData> m_projectData;
- QMap<Utils::FilePath, QList<ProjectPart::ConstPtr> > m_fileToProjectParts;
+ QHash<Project *, ProjectData> m_projectData;
+ QMap<FilePath, QList<ProjectPart::ConstPtr> > m_fileToProjectParts;
QMap<QString, ProjectPart::ConstPtr> m_projectPartIdToProjectProjectPart;
// The members below are cached/(re)calculated from the projects and/or their project parts
bool m_dirty;
- Utils::FilePaths m_projectFiles;
- ProjectExplorer::HeaderPaths m_headerPaths;
- ProjectExplorer::Macros m_definedMacros;
+ FilePaths m_projectFiles;
+ HeaderPaths m_headerPaths;
+ Macros m_definedMacros;
// Editor integration
mutable QMutex m_cppEditorDocumentsMutex;
@@ -201,12 +203,12 @@ public:
QTimer m_fallbackProjectPartTimer;
CppLocatorData m_locatorData;
- std::unique_ptr<Core::ILocatorFilter> m_locatorFilter;
- std::unique_ptr<Core::ILocatorFilter> m_classesFilter;
- std::unique_ptr<Core::ILocatorFilter> m_includesFilter;
- std::unique_ptr<Core::ILocatorFilter> m_functionsFilter;
- std::unique_ptr<Core::IFindFilter> m_symbolsFindFilter;
- std::unique_ptr<Core::ILocatorFilter> m_currentDocumentFilter;
+ std::unique_ptr<ILocatorFilter> m_locatorFilter;
+ std::unique_ptr<ILocatorFilter> m_classesFilter;
+ std::unique_ptr<ILocatorFilter> m_includesFilter;
+ std::unique_ptr<ILocatorFilter> m_functionsFilter;
+ std::unique_ptr<IFindFilter> m_symbolsFindFilter;
+ std::unique_ptr<ILocatorFilter> m_currentDocumentFilter;
QList<Document::DiagnosticMessage> m_diagnosticMessages;
};
@@ -338,7 +340,7 @@ void CppModelManager::findUsages(const CursorInEditor &data, Backend backend)
void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend)
{
- const Core::IDocument *currentDocument = Core::EditorManager::currentDocument();
+ const IDocument *currentDocument = EditorManager::currentDocument();
QTC_ASSERT(currentDocument, return);
instance()->modelManagerSupport(backend)->switchHeaderSource(currentDocument->filePath(),
inNextSplit);
@@ -346,16 +348,16 @@ void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend)
void CppModelManager::showPreprocessedFile(bool inNextSplit)
{
- const Core::IDocument *doc = Core::EditorManager::currentDocument();
+ const IDocument *doc = EditorManager::currentDocument();
QTC_ASSERT(doc, return);
static const auto showError = [](const QString &reason) {
- Core::MessageManager::writeFlashing(Tr::tr("Cannot show preprocessed file: %1")
- .arg(reason));
+ MessageManager::writeFlashing(Tr::tr("Cannot show preprocessed file: %1")
+ .arg(reason));
};
static const auto showFallbackWarning = [](const QString &reason) {
- Core::MessageManager::writeSilently(
- Tr::tr("Falling back to built-in preprocessor: %1").arg(reason));
+ MessageManager::writeSilently(Tr::tr("Falling back to built-in preprocessor: %1")
+ .arg(reason));
};
static const auto saveAndOpen = [](const FilePath &filePath, const QByteArray &contents,
bool inNextSplit) {
@@ -446,10 +448,10 @@ void CppModelManager::showPreprocessedFile(bool inNextSplit)
compilerArgs.append("/E");
compilerArgs.append(filePath.toUserOutput());
const CommandLine compilerCommandLine(tc->compilerCommand(), compilerArgs);
- const auto compiler = new QtcProcess(instance());
+ const auto compiler = new Process(instance());
compiler->setCommand(compilerCommandLine);
compiler->setEnvironment(project->activeTarget()->activeBuildConfiguration()->environment());
- connect(compiler, &QtcProcess::done, instance(), [compiler, outFilePath, inNextSplit,
+ connect(compiler, &Process::done, instance(), [compiler, outFilePath, inNextSplit,
useBuiltinPreprocessor, isMsvc] {
compiler->deleteLater();
if (compiler->result() != ProcessResult::FinishedWithSuccess) {
@@ -469,24 +471,24 @@ class FindUnusedActionsEnabledSwitcher
{
public:
FindUnusedActionsEnabledSwitcher()
- : actions{Core::ActionManager::command("CppTools.FindUnusedFunctions"),
- Core::ActionManager::command("CppTools.FindUnusedFunctionsInSubProject")}
+ : actions{ActionManager::command("CppTools.FindUnusedFunctions"),
+ ActionManager::command("CppTools.FindUnusedFunctionsInSubProject")}
{
- for (Core::Command * const action : actions)
+ for (Command * const action : actions)
action->action()->setEnabled(false);
}
~FindUnusedActionsEnabledSwitcher()
{
- for (Core::Command * const action : actions)
+ for (Command * const action : actions)
action->action()->setEnabled(true);
}
private:
- const QList<Core::Command *> actions;
+ const QList<Command *> actions;
};
using FindUnusedActionsEnabledSwitcherPtr = std::shared_ptr<FindUnusedActionsEnabledSwitcher>;
static void checkNextFunctionForUnused(
- const QPointer<Core::SearchResult> &search,
+ const QPointer<SearchResult> &search,
const std::shared_ptr<QFutureInterface<bool>> &findRefsFuture,
const FindUnusedActionsEnabledSwitcherPtr &actionsSwitcher)
{
@@ -531,54 +533,43 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
const auto actionsSwitcher = std::make_shared<FindUnusedActionsEnabledSwitcher>();
// Step 1: Employ locator to find all functions
- Core::ILocatorFilter *const functionsFilter
- = Utils::findOrDefault(Core::ILocatorFilter::allLocatorFilters(),
- Utils::equal(&Core::ILocatorFilter::id,
- Id(Constants::FUNCTIONS_FILTER_ID)));
- QTC_ASSERT(functionsFilter, return);
- const QPointer<Core::SearchResult> search
- = Core::SearchResultWindow::instance()
- ->startNewSearch(Tr::tr("Find Unused Functions"),
+ LocatorMatcher *matcher = new LocatorMatcher;
+ matcher->setTasks(LocatorMatcher::matchers(MatcherType::Functions));
+ const QPointer<SearchResult> search
+ = SearchResultWindow::instance()->startNewSearch(Tr::tr("Find Unused Functions"),
{},
{},
- Core::SearchResultWindow::SearchOnly,
- Core::SearchResultWindow::PreserveCaseDisabled,
+ SearchResultWindow::SearchOnly,
+ SearchResultWindow::PreserveCaseDisabled,
"CppEditor");
- connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
- Core::EditorManager::openEditorAtSearchResult(item);
+ matcher->setParent(search);
+ connect(search, &SearchResult::activated, [](const SearchResultItem &item) {
+ EditorManager::openEditorAtSearchResult(item);
});
- Core::SearchResultWindow::instance()->popup(Core::IOutputPane::ModeSwitch
- | Core::IOutputPane::WithFocus);
- const auto locatorWatcher = new QFutureWatcher<Core::LocatorFilterEntry>(search);
- functionsFilter->prepareSearch({});
- connect(search, &Core::SearchResult::canceled, locatorWatcher, [locatorWatcher] {
- locatorWatcher->cancel();
- });
- connect(locatorWatcher, &QFutureWatcher<Core::LocatorFilterEntry>::finished, search,
- [locatorWatcher, search, folder, actionsSwitcher] {
- locatorWatcher->deleteLater();
- if (locatorWatcher->isCanceled()) {
+ SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
+ connect(search, &SearchResult::canceled, matcher, [matcher] { delete matcher; });
+ connect(matcher, &LocatorMatcher::done, search,
+ [matcher, search, folder, actionsSwitcher](bool success) {
+ matcher->deleteLater();
+ if (!success) {
search->finishSearch(true);
return;
}
Links links;
- for (int i = 0; i < locatorWatcher->future().resultCount(); ++i) {
- const Core::LocatorFilterEntry &entry = locatorWatcher->resultAt(i);
+ const LocatorFilterEntries entries = matcher->outputData();
+ for (const LocatorFilterEntry &entry : entries) {
static const QStringList prefixBlacklist{"main(", "~", "qHash(", "begin()", "end()",
"cbegin()", "cend()", "constBegin()", "constEnd()"};
if (Utils::anyOf(prefixBlacklist, [&entry](const QString &prefix) {
return entry.displayName.startsWith(prefix); })) {
continue;
}
- Link link;
- if (entry.internalData.canConvert<Link>())
- link = qvariant_cast<Link>(entry.internalData);
- else if (const auto item = qvariant_cast<IndexItem::Ptr>(entry.internalData))
- link = Link(item->filePath(), item->line(), item->column());
-
+ if (!entry.linkForEditor)
+ continue;
+ const Link link = *entry.linkForEditor;
if (link.hasValidTarget() && link.targetFilePath.isReadableFile()
&& (folder.isEmpty() || link.targetFilePath.isChildOf(folder))
- && SessionManager::projectForFile(link.targetFilePath)) {
+ && ProjectManager::projectForFile(link.targetFilePath)) {
links << link;
}
}
@@ -593,12 +584,11 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
}));
search->setUserData(remainingAndActiveLinks);
const auto findRefsFuture = std::make_shared<QFutureInterface<bool>>();
- Core::FutureProgress *const progress
- = Core::ProgressManager::addTask(findRefsFuture->future(),
- Tr::tr("Finding Unused Functions"),
- "CppEditor.FindUnusedFunctions");
+ FutureProgress *const progress = ProgressManager::addTask(findRefsFuture->future(),
+ Tr::tr("Finding Unused Functions"),
+ "CppEditor.FindUnusedFunctions");
connect(progress,
- &Core::FutureProgress::canceled,
+ &FutureProgress::canceled,
search,
[search, future = std::weak_ptr<QFutureInterface<bool>>(findRefsFuture)] {
search->finishSearch(true);
@@ -608,7 +598,7 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
}
});
findRefsFuture->setProgressRange(0, links.size());
- connect(search, &Core::SearchResult::canceled, [findRefsFuture] {
+ connect(search, &SearchResult::canceled, [findRefsFuture] {
findRefsFuture->cancel();
findRefsFuture->reportFinished();
});
@@ -619,13 +609,10 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
for (int i = 0; i < inFlightCount; ++i)
checkNextFunctionForUnused(search, findRefsFuture, actionsSwitcher);
});
- locatorWatcher->setFuture(
- Utils::runAsync([functionsFilter](QFutureInterface<Core::LocatorFilterEntry> &future) {
- future.reportResults(functionsFilter->matchesFor(future, {}));
- }));
+ matcher->start();
}
-void CppModelManager::checkForUnusedSymbol(Core::SearchResult *search,
+void CppModelManager::checkForUnusedSymbol(SearchResult *search,
const Link &link,
CPlusPlus::Symbol *symbol,
const CPlusPlus::LookupContext &context,
@@ -785,62 +772,62 @@ static void setFilter(std::unique_ptr<FilterClass> &filter,
filter = std::move(newFilter);
}
-void CppModelManager::setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setLocatorFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_locatorFilter, std::move(filter));
}
-void CppModelManager::setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setClassesFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_classesFilter, std::move(filter));
}
-void CppModelManager::setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setIncludesFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_includesFilter, std::move(filter));
}
-void CppModelManager::setFunctionsFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setFunctionsFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_functionsFilter, std::move(filter));
}
-void CppModelManager::setSymbolsFindFilter(std::unique_ptr<Core::IFindFilter> &&filter)
+void CppModelManager::setSymbolsFindFilter(std::unique_ptr<IFindFilter> &&filter)
{
setFilter(d->m_symbolsFindFilter, std::move(filter));
}
-void CppModelManager::setCurrentDocumentFilter(std::unique_ptr<Core::ILocatorFilter> &&filter)
+void CppModelManager::setCurrentDocumentFilter(std::unique_ptr<ILocatorFilter> &&filter)
{
setFilter(d->m_currentDocumentFilter, std::move(filter));
}
-Core::ILocatorFilter *CppModelManager::locatorFilter() const
+ILocatorFilter *CppModelManager::locatorFilter() const
{
return d->m_locatorFilter.get();
}
-Core::ILocatorFilter *CppModelManager::classesFilter() const
+ILocatorFilter *CppModelManager::classesFilter() const
{
return d->m_classesFilter.get();
}
-Core::ILocatorFilter *CppModelManager::includesFilter() const
+ILocatorFilter *CppModelManager::includesFilter() const
{
return d->m_includesFilter.get();
}
-Core::ILocatorFilter *CppModelManager::functionsFilter() const
+ILocatorFilter *CppModelManager::functionsFilter() const
{
return d->m_functionsFilter.get();
}
-Core::IFindFilter *CppModelManager::symbolsFindFilter() const
+IFindFilter *CppModelManager::symbolsFindFilter() const
{
return d->m_symbolsFindFilter.get();
}
-Core::ILocatorFilter *CppModelManager::currentDocumentFilter() const
+ILocatorFilter *CppModelManager::currentDocumentFilter() const
{
return d->m_currentDocumentFilter.get();
}
@@ -880,7 +867,7 @@ CppModelManager *CppModelManager::instance()
void CppModelManager::registerJsExtension()
{
- Core::JsExpander::registerGlobalObject("Cpp", [this] {
+ JsExpander::registerGlobalObject("Cpp", [this] {
return new CppToolsJsExtension(&d->m_locatorData);
});
}
@@ -888,10 +875,10 @@ void CppModelManager::registerJsExtension()
void CppModelManager::initCppTools()
{
// Objects
- connect(Core::VcsManager::instance(), &Core::VcsManager::repositoryChanged,
+ connect(VcsManager::instance(), &VcsManager::repositoryChanged,
this, &CppModelManager::updateModifiedSourceFiles);
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::filesChangedInternally,
- [this](const FilePaths &filePaths) {
+ connect(DocumentManager::instance(), &DocumentManager::filesChangedInternally,
+ this, [this](const FilePaths &filePaths) {
updateSourceFiles(toSet(filePaths));
});
@@ -902,13 +889,25 @@ void CppModelManager::initCppTools()
&d->m_locatorData, &CppLocatorData::onAboutToRemoveFiles);
// Set up builtin filters
- setLocatorFilter(std::make_unique<CppLocatorFilter>(&d->m_locatorData));
- setClassesFilter(std::make_unique<CppClassesFilter>(&d->m_locatorData));
+ setLocatorFilter(std::make_unique<CppAllSymbolsFilter>());
+ setClassesFilter(std::make_unique<CppClassesFilter>());
setIncludesFilter(std::make_unique<CppIncludesFilter>());
- setFunctionsFilter(std::make_unique<CppFunctionsFilter>(&d->m_locatorData));
+ setFunctionsFilter(std::make_unique<CppFunctionsFilter>());
setSymbolsFindFilter(std::make_unique<SymbolsFindFilter>(this));
- setCurrentDocumentFilter(
- std::make_unique<Internal::CppCurrentDocumentFilter>(this));
+ setCurrentDocumentFilter(std::make_unique<CppCurrentDocumentFilter>());
+ // Setup matchers
+ LocatorMatcher::addMatcherCreator(MatcherType::AllSymbols, [] {
+ return cppMatchers(MatcherType::AllSymbols);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Classes, [] {
+ return cppMatchers(MatcherType::Classes);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::Functions, [] {
+ return cppMatchers(MatcherType::Functions);
+ });
+ LocatorMatcher::addMatcherCreator(MatcherType::CurrentDocumentSymbols, [] {
+ return cppMatchers(MatcherType::CurrentDocumentSymbols);
+ });
}
CppModelManager::CppModelManager()
@@ -924,7 +923,7 @@ CppModelManager::CppModelManager()
d->m_enableGC = true;
// Visual C++ has 1MiB, macOSX has 512KiB
- if (Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost())
+ if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
d->m_threadPool.setStackSize(2 * 1024 * 1024);
qRegisterMetaType<QSet<QString> >();
@@ -940,23 +939,23 @@ CppModelManager::CppModelManager()
d->m_delayedGcTimer.setSingleShot(true);
connect(&d->m_delayedGcTimer, &QTimer::timeout, this, &CppModelManager::GC);
- auto sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::projectAdded,
+ auto projectManager = ProjectManager::instance();
+ connect(projectManager, &ProjectManager::projectAdded,
this, &CppModelManager::onProjectAdded);
- connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
+ connect(projectManager, &ProjectManager::aboutToRemoveProject,
this, &CppModelManager::onAboutToRemoveProject);
- connect(sessionManager, &ProjectExplorer::SessionManager::aboutToLoadSession,
+ connect(SessionManager::instance(), &SessionManager::aboutToLoadSession,
this, &CppModelManager::onAboutToLoadSession);
- connect(sessionManager, &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(projectManager, &ProjectManager::startupProjectChanged,
this, &CppModelManager::onActiveProjectChanged);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
+ connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
this, &CppModelManager::onCurrentEditorChanged);
- connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
+ connect(DocumentManager::instance(), &DocumentManager::allDocumentsRenamed,
this, &CppModelManager::renameIncludes);
- connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
+ connect(ICore::instance(), &ICore::coreAboutToClose,
this, &CppModelManager::onCoreAboutToClose);
d->m_fallbackProjectPartTimer.setSingleShot(true);
@@ -1002,7 +1001,7 @@ Document::Ptr CppModelManager::document(const FilePath &filePath) const
/// Replace the document in the snapshot.
///
-/// \returns true if successful, false if the new document is out-dated.
+/// Returns true if successful, false if the new document is out-dated.
bool CppModelManager::replaceDocument(Document::Ptr newDoc)
{
QMutexLocker locker(&d->m_snapshotMutex);
@@ -1041,13 +1040,13 @@ FilePaths CppModelManager::internalProjectFiles() const
return files;
}
-ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const
+HeaderPaths CppModelManager::internalHeaderPaths() const
{
- ProjectExplorer::HeaderPaths headerPaths;
+ HeaderPaths headerPaths;
for (const ProjectData &projectData: std::as_const(d->m_projectData)) {
for (const ProjectPart::ConstPtr &part : projectData.projectInfo->projectParts()) {
- for (const ProjectExplorer::HeaderPath &path : part->headerPaths) {
- ProjectExplorer::HeaderPath hp(QDir::cleanPath(path.path), path.type);
+ for (const HeaderPath &path : part->headerPaths) {
+ HeaderPath hp(QDir::cleanPath(path.path), path.type);
if (!headerPaths.contains(hp))
headerPaths.push_back(std::move(hp));
}
@@ -1056,8 +1055,7 @@ ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const
return headerPaths;
}
-static void addUnique(const ProjectExplorer::Macros &newMacros,
- ProjectExplorer::Macros &macros,
+static void addUnique(const Macros &newMacros, Macros &macros,
QSet<ProjectExplorer::Macro> &alreadyIn)
{
for (const ProjectExplorer::Macro &macro : newMacros) {
@@ -1068,9 +1066,9 @@ static void addUnique(const ProjectExplorer::Macros &newMacros,
}
}
-ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const
+Macros CppModelManager::internalDefinedMacros() const
{
- ProjectExplorer::Macros macros;
+ Macros macros;
QSet<ProjectExplorer::Macro> alreadyIn;
for (const ProjectData &projectData : std::as_const(d->m_projectData)) {
for (const ProjectPart::ConstPtr &part : projectData.projectInfo->projectParts()) {
@@ -1093,7 +1091,7 @@ void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
dumper.dumpWorkingCopy(workingCopy());
dumper.dumpMergedEntities(headerPaths(),
- ProjectExplorer:: Macro::toByteArray(definedMacros()));
+ ProjectExplorer::Macro::toByteArray(definedMacros()));
}
QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
@@ -1268,8 +1266,8 @@ static QSet<QString> filteredFilesRemoved(const QSet<QString> &files, int fileSi
const QString msg = Tr::tr("C++ Indexer: Skipping file \"%1\" "
"because its path matches the ignore pattern.")
.arg(filePath.displayName());
- QMetaObject::invokeMethod(Core::MessageManager::instance(),
- [msg]() { Core::MessageManager::writeSilently(msg); });
+ QMetaObject::invokeMethod(MessageManager::instance(),
+ [msg] { MessageManager::writeSilently(msg); });
skip = true;
break;
}
@@ -1304,7 +1302,7 @@ ProjectInfoList CppModelManager::projectInfos() const
[](const ProjectData &d) { return d.projectInfo; });
}
-ProjectInfo::ConstPtr CppModelManager::projectInfo(ProjectExplorer::Project *project) const
+ProjectInfo::ConstPtr CppModelManager::projectInfo(Project *project) const
{
QReadLocker locker(&d->m_projectLock);
return d->m_projectData.value(project).projectInfo;
@@ -1424,8 +1422,7 @@ void CppModelManager::recalculateProjectPartMappings()
d->m_symbolFinder.clearCache();
}
-void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future,
- ProjectExplorer::Project *project,
+void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future, Project *project,
ProjectData *projectData, CppModelManager *q)
{
projectData->indexer = new QFutureWatcher<void>(q);
@@ -1446,10 +1443,10 @@ void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future,
void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
{
// Refresh visible documents
- QSet<Core::IDocument *> visibleCppEditorDocuments;
- const QList<Core::IEditor *> editors = Core::EditorManager::visibleEditors();
- for (Core::IEditor *editor: editors) {
- if (Core::IDocument *document = editor->document()) {
+ QSet<IDocument *> visibleCppEditorDocuments;
+ const QList<IEditor *> editors = EditorManager::visibleEditors();
+ for (IEditor *editor: editors) {
+ if (IDocument *document = editor->document()) {
const FilePath filePath = document->filePath();
if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
visibleCppEditorDocuments.insert(document);
@@ -1459,10 +1456,10 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
}
// Mark invisible documents dirty
- QSet<Core::IDocument *> invisibleCppEditorDocuments
- = Utils::toSet(Core::DocumentModel::openedDocuments());
+ QSet<IDocument *> invisibleCppEditorDocuments
+ = Utils::toSet(DocumentModel::openedDocuments());
invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
- for (Core::IDocument *document : std::as_const(invisibleCppEditorDocuments)) {
+ for (IDocument *document : std::as_const(invisibleCppEditorDocuments)) {
const FilePath filePath = document->filePath();
if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
const CppEditorDocumentHandle::RefreshReason refreshReason = projectsUpdated
@@ -1483,7 +1480,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::ConstPtr &ne
QStringList removedProjectParts;
bool filesRemoved = false;
- ProjectExplorer::Project * const project = projectForProjectInfo(*newProjectInfo);
+ Project * const project = projectForProjectInfo(*newProjectInfo);
if (!project)
return {};
@@ -1589,20 +1586,20 @@ ProjectPart::ConstPtr CppModelManager::projectPartForId(const QString &projectPa
return d->m_projectPartIdToProjectProjectPart.value(projectPartId);
}
-QList<ProjectPart::ConstPtr> CppModelManager::projectPart(const Utils::FilePath &fileName) const
+QList<ProjectPart::ConstPtr> CppModelManager::projectPart(const FilePath &fileName) const
{
QReadLocker locker(&d->m_projectLock);
return d->m_fileToProjectParts.value(fileName.canonicalPath());
}
QList<ProjectPart::ConstPtr> CppModelManager::projectPartFromDependencies(
- const Utils::FilePath &fileName) const
+ const FilePath &fileName) const
{
QSet<ProjectPart::ConstPtr> parts;
- const Utils::FilePaths deps = snapshot().filesDependingOn(fileName);
+ const FilePaths deps = snapshot().filesDependingOn(fileName);
QReadLocker locker(&d->m_projectLock);
- for (const Utils::FilePath &dep : deps)
+ for (const FilePath &dep : deps)
parts.unite(Utils::toSet(d->m_fileToProjectParts.value(dep.canonicalPath())));
return parts.values();
@@ -1614,7 +1611,7 @@ ProjectPart::ConstPtr CppModelManager::fallbackProjectPart()
return d->m_fallbackProjectPart;
}
-bool CppModelManager::isCppEditor(Core::IEditor *editor)
+bool CppModelManager::isCppEditor(IEditor *editor)
{
return editor->context().contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
}
@@ -1647,7 +1644,7 @@ void CppModelManager::emitAbstractEditorSupportRemoved(const QString &filePath)
emit abstractEditorSupportRemoved(filePath);
}
-void CppModelManager::onProjectAdded(ProjectExplorer::Project *)
+void CppModelManager::onProjectAdded(Project *)
{
QWriteLocker locker(&d->m_projectLock);
d->m_dirty = true;
@@ -1667,7 +1664,7 @@ static QStringList removedProjectParts(const QStringList &before, const QStringL
return Utils::toList(b);
}
-void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
+void CppModelManager::onAboutToRemoveProject(Project *project)
{
QStringList idsOfRemovedProjectParts;
@@ -1689,7 +1686,7 @@ void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project)
delayedGC();
}
-void CppModelManager::onActiveProjectChanged(ProjectExplorer::Project *project)
+void CppModelManager::onActiveProjectChanged(Project *project)
{
if (!project)
return; // Last project closed.
@@ -1711,7 +1708,7 @@ void CppModelManager::onSourceFilesRefreshed() const
}
}
-void CppModelManager::onCurrentEditorChanged(Core::IEditor *editor)
+void CppModelManager::onCurrentEditorChanged(IEditor *editor)
{
if (!editor || !editor->document())
return;
@@ -1752,7 +1749,7 @@ QSet<QString> CppModelManager::dependingInternalTargets(const FilePath &file) co
return result;
}
-QSet<QString> CppModelManager::internalTargets(const Utils::FilePath &filePath) const
+QSet<QString> CppModelManager::internalTargets(const FilePath &filePath) const
{
const QList<ProjectPart::ConstPtr> projectParts = projectPart(filePath);
// if we have no project parts it's most likely a header with declarations only and CMake based
@@ -1761,14 +1758,13 @@ QSet<QString> CppModelManager::internalTargets(const Utils::FilePath &filePath)
QSet<QString> targets;
for (const ProjectPart::ConstPtr &part : projectParts) {
targets.insert(part->buildSystemTarget);
- if (part->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
+ if (part->buildTargetType != BuildTargetType::Executable)
targets.unite(dependingInternalTargets(filePath));
}
return targets;
}
-void CppModelManager::renameIncludes(const Utils::FilePath &oldFilePath,
- const Utils::FilePath &newFilePath)
+void CppModelManager::renameIncludes(const FilePath &oldFilePath, const FilePath &newFilePath)
{
if (oldFilePath.isEmpty() || newFilePath.isEmpty())
return;
@@ -1815,7 +1811,7 @@ void CppModelManager::renameIncludes(const Utils::FilePath &oldFilePath,
const QTextBlock &block = file->document()->findBlockByNumber(loc.second - 1);
const int replaceStart = block.text().indexOf(oldFileName);
if (replaceStart > -1) {
- Utils::ChangeSet changeSet;
+ ChangeSet changeSet;
changeSet.replace(block.position() + replaceStart,
block.position() + replaceStart + oldFileName.length(),
newFileName);
@@ -1843,13 +1839,13 @@ static const char *belongingClassName(const Function *function)
return nullptr;
}
-QSet<QString> CppModelManager::symbolsInFiles(const QSet<Utils::FilePath> &files) const
+QSet<QString> CppModelManager::symbolsInFiles(const QSet<FilePath> &files) const
{
QSet<QString> uniqueSymbols;
const Snapshot cppSnapShot = snapshot();
// Iterate over the files and get interesting symbols
- for (const Utils::FilePath &file : files) {
+ for (const FilePath &file : files) {
// Add symbols from the C++ code model
const CPlusPlus::Document::Ptr doc = cppSnapShot.document(file);
if (!doc.isNull() && doc->control()) {
@@ -1881,7 +1877,7 @@ QSet<QString> CppModelManager::symbolsInFiles(const QSet<Utils::FilePath> &files
void CppModelManager::onCoreAboutToClose()
{
- Core::ProgressManager::cancelTasks(Constants::TASK_INDEX);
+ ProgressManager::cancelTasks(Constants::TASK_INDEX);
d->m_enableGC = false;
}
@@ -1891,27 +1887,27 @@ void CppModelManager::setupFallbackProjectPart()
RawProjectPart rpp;
rpp.setMacros(definedMacros());
rpp.setHeaderPaths(headerPaths());
- rpp.setQtVersion(Utils::QtMajorVersion::Qt5);
+ rpp.setQtVersion(QtMajorVersion::Qt5);
// Do not activate ObjectiveCExtensions since this will lead to the
// "objective-c++" language option for a project-less *.cpp file.
- Utils::LanguageExtensions langExtensions = Utils::LanguageExtension::All;
- langExtensions &= ~Utils::LanguageExtensions(Utils::LanguageExtension::ObjectiveC);
+ LanguageExtensions langExtensions = LanguageExtension::All;
+ langExtensions &= ~LanguageExtensions(LanguageExtension::ObjectiveC);
// TODO: Use different fallback toolchain for different kinds of files?
const Kit * const defaultKit = KitManager::isLoaded() ? KitManager::defaultKit() : nullptr;
const ToolChain * const defaultTc = defaultKit
? ToolChainKitAspect::cxxToolChain(defaultKit) : nullptr;
if (defaultKit && defaultTc) {
- Utils::FilePath sysroot = SysRootKitAspect::sysRoot(defaultKit);
+ FilePath sysroot = SysRootKitAspect::sysRoot(defaultKit);
if (sysroot.isEmpty())
- sysroot = Utils::FilePath::fromString(defaultTc->sysRoot());
+ sysroot = FilePath::fromString(defaultTc->sysRoot());
Utils::Environment env = defaultKit->buildEnvironment();
tcInfo = ToolChainInfo(defaultTc, sysroot, env);
const auto macroInspectionWrapper = [runner = tcInfo.macroInspectionRunner](
const QStringList &flags) {
ToolChain::MacroInspectionReport report = runner(flags);
- report.languageVersion = Utils::LanguageVersion::LatestCxx;
+ report.languageVersion = LanguageVersion::LatestCxx;
return report;
};
tcInfo.macroInspectionRunner = macroInspectionWrapper;
@@ -1941,7 +1937,7 @@ void CppModelManager::GC()
filesInEditorSupports << abstractEditorSupport->filePath();
Snapshot currentSnapshot = snapshot();
- QSet<Utils::FilePath> reachableFiles;
+ QSet<FilePath> reachableFiles;
// The configuration file is part of the project files, which is just fine.
// If single files are open, without any project, then there is no need to
// keep the configuration file around.
@@ -1964,7 +1960,7 @@ void CppModelManager::GC()
QStringList notReachableFiles;
Snapshot newSnapshot;
for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) {
- const Utils::FilePath &fileName = it.key();
+ const FilePath &fileName = it.key();
if (reachableFiles.contains(fileName))
newSnapshot.insert(it.value());
@@ -2001,7 +1997,7 @@ TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
}
void CppModelManager::followSymbol(const CursorInEditor &data,
- const Utils::LinkHandler &processLinkCallback,
+ const LinkHandler &processLinkCallback,
bool resolveTarget, bool inNextSplit, Backend backend)
{
instance()->modelManagerSupport(backend)->followSymbol(data, processLinkCallback,
@@ -2009,7 +2005,7 @@ void CppModelManager::followSymbol(const CursorInEditor &data,
}
void CppModelManager::followSymbolToType(const CursorInEditor &data,
- const Utils::LinkHandler &processLinkCallback,
+ const LinkHandler &processLinkCallback,
bool inNextSplit, Backend backend)
{
instance()->modelManagerSupport(backend)->followSymbolToType(data, processLinkCallback,
@@ -2017,19 +2013,12 @@ void CppModelManager::followSymbolToType(const CursorInEditor &data,
}
void CppModelManager::switchDeclDef(const CursorInEditor &data,
- const Utils::LinkHandler &processLinkCallback,
+ const LinkHandler &processLinkCallback,
Backend backend)
{
instance()->modelManagerSupport(backend)->switchDeclDef(data, processLinkCallback);
}
-Core::ILocatorFilter *CppModelManager::createAuxiliaryCurrentDocumentFilter()
-{
- const auto filter = new Internal::CppCurrentDocumentFilter(instance());
- filter->makeAuxiliary();
- return filter;
-}
-
BaseEditorDocumentProcessor *CppModelManager::createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) const
{
@@ -2049,7 +2038,7 @@ FilePaths CppModelManager::projectFiles()
return d->m_projectFiles;
}
-ProjectExplorer::HeaderPaths CppModelManager::headerPaths()
+HeaderPaths CppModelManager::headerPaths()
{
QWriteLocker locker(&d->m_projectLock);
ensureUpdated();
@@ -2057,13 +2046,13 @@ ProjectExplorer::HeaderPaths CppModelManager::headerPaths()
return d->m_headerPaths;
}
-void CppModelManager::setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths)
+void CppModelManager::setHeaderPaths(const HeaderPaths &headerPaths)
{
QWriteLocker locker(&d->m_projectLock);
d->m_headerPaths = headerPaths;
}
-ProjectExplorer::Macros CppModelManager::definedMacros()
+Macros CppModelManager::definedMacros()
{
QWriteLocker locker(&d->m_projectLock);
ensureUpdated();
diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h
index 2929d615e6..3ab9ee2689 100644
--- a/src/plugins/cppeditor/cppmodelmanager.h
+++ b/src/plugins/cppeditor/cppmodelmanager.h
@@ -196,8 +196,6 @@ public:
const CPlusPlus::LookupContext &context,
const Utils::LinkHandler &callback);
- static Core::ILocatorFilter *createAuxiliaryCurrentDocumentFilter();
-
CppIndexingSupport *indexingSupport();
Utils::FilePaths projectFiles();
diff --git a/src/plugins/cppeditor/cppmodelmanager_test.cpp b/src/plugins/cppeditor/cppmodelmanager_test.cpp
index 97d13d0704..c3fb341646 100644
--- a/src/plugins/cppeditor/cppmodelmanager_test.cpp
+++ b/src/plugins/cppeditor/cppmodelmanager_test.cpp
@@ -18,7 +18,7 @@
#include <cplusplus/LookupContext.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/executeondestruction.h>
#include <utils/hostosinfo.h>
@@ -649,7 +649,7 @@ void ModelManagerTest::testGcIfLastCppeditorClosed()
QVERIFY(editor);
QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1);
QVERIFY(mm->isCppEditor(editor));
- QVERIFY(mm->workingCopy().contains(file));
+ QVERIFY(mm->workingCopy().get(file));
// Wait until the file is refreshed
helper.waitForRefreshedSourceFiles();
@@ -659,7 +659,7 @@ void ModelManagerTest::testGcIfLastCppeditorClosed()
helper.waitForFinishedGc();
// Check: File is removed from the snapshpt
- QVERIFY(!mm->workingCopy().contains(file));
+ QVERIFY(!mm->workingCopy().get(file));
QVERIFY(!mm->snapshot().contains(file));
}
@@ -684,13 +684,13 @@ void ModelManagerTest::testDontGcOpenedFiles()
// Wait until the file is refreshed and check whether it is in the working copy
helper.waitForRefreshedSourceFiles();
- QVERIFY(mm->workingCopy().contains(file));
+ QVERIFY(mm->workingCopy().get(file));
// Run the garbage collector
mm->GC();
// Check: File is still there
- QVERIFY(mm->workingCopy().contains(file));
+ QVERIFY(mm->workingCopy().get(file));
QVERIFY(mm->snapshot().contains(file));
// Close editor
@@ -1090,7 +1090,7 @@ void ModelManagerTest::testRenameIncludesInEditor()
});
QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1);
QVERIFY(modelManager->isCppEditor(editor));
- QVERIFY(modelManager->workingCopy().contains(mainFile));
+ QVERIFY(modelManager->workingCopy().get(mainFile));
// Test the renaming of a header file where a pragma once guard is present
QVERIFY(Core::FileUtils::renameFile(headerWithPragmaOnce,
diff --git a/src/plugins/cppeditor/cppoutline.cpp b/src/plugins/cppeditor/cppoutline.cpp
index 8c28c7e30a..d492ad4fa7 100644
--- a/src/plugins/cppeditor/cppoutline.cpp
+++ b/src/plugins/cppeditor/cppoutline.cpp
@@ -13,7 +13,6 @@
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
-#include <utils/linecolumn.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -169,7 +168,7 @@ void CppOutlineWidget::updateIndexNow()
int line = 0, column = 0;
m_editor->convertPosition(m_editor->position(), &line, &column);
- QModelIndex index = m_model->indexForPosition(line, column);
+ QModelIndex index = m_model->indexForPosition(line, column - 1);
if (index.isValid()) {
m_blockCursorSync = true;
@@ -184,8 +183,8 @@ void CppOutlineWidget::updateIndexNow()
void CppOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)
{
QModelIndex index = m_proxyModel->mapToSource(proxyIndex);
- Utils::LineColumn lineColumn
- = m_editor->cppEditorDocument()->outlineModel().lineColumnFromIndex(index);
+ Utils::Text::Position lineColumn
+ = m_editor->cppEditorDocument()->outlineModel().positionFromIndex(index);
if (!lineColumn.isValid())
return;
@@ -194,8 +193,7 @@ void CppOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex)
Core::EditorManager::cutForwardNavigationHistory();
Core::EditorManager::addCurrentPositionToNavigationHistory();
- // line has to be 1 based, column 0 based!
- m_editor->gotoLine(lineColumn.line, lineColumn.column - 1, true, true);
+ m_editor->gotoLine(lineColumn.line, lineColumn.column, true, true);
m_blockCursorSync = false;
}
diff --git a/src/plugins/cppeditor/cppoutlinemodel.cpp b/src/plugins/cppeditor/cppoutlinemodel.cpp
index d1875b6bb3..068dc310b1 100644
--- a/src/plugins/cppeditor/cppoutlinemodel.cpp
+++ b/src/plugins/cppeditor/cppoutlinemodel.cpp
@@ -9,8 +9,8 @@
#include <cplusplus/Scope.h>
#include <cplusplus/Symbols.h>
-#include <utils/linecolumn.h>
#include <utils/link.h>
+#include <utils/theme/theme.h>
#include <QTimer>
@@ -103,6 +103,24 @@ public:
return name;
}
+ case Qt::ForegroundRole: {
+ const auto isFwdDecl = [&] {
+ const FullySpecifiedType type = symbol->type();
+ if (type->asForwardClassDeclarationType())
+ return true;
+ if (const Template * const tmpl = type->asTemplateType())
+ return tmpl->declaration() && tmpl->declaration()->asForwardClassDeclaration();
+ if (type->asObjCForwardClassDeclarationType())
+ return true;
+ if (type->asObjCForwardProtocolDeclarationType())
+ return true;
+ return false;
+ };
+ if (isFwdDecl())
+ return Utils::creatorTheme()->color(Utils::Theme::TextColorDisabled);
+ return TreeItem::data(column, role);
+ }
+
case Qt::DecorationRole:
return Icons::iconForSymbol(symbol);
@@ -220,20 +238,20 @@ Utils::Link OutlineModel::linkFromIndex(const QModelIndex &sourceIndex) const
return symbol->toLink();
}
-Utils::LineColumn OutlineModel::lineColumnFromIndex(const QModelIndex &sourceIndex) const
+Utils::Text::Position OutlineModel::positionFromIndex(const QModelIndex &sourceIndex) const
{
- Utils::LineColumn lineColumn;
+ Utils::Text::Position lineColumn;
CPlusPlus::Symbol *symbol = symbolFromIndex(sourceIndex);
if (!symbol)
return lineColumn;
lineColumn.line = symbol->line();
- lineColumn.column = symbol->column();
+ lineColumn.column = symbol->column() - 1;
return lineColumn;
}
OutlineModel::Range OutlineModel::rangeFromIndex(const QModelIndex &sourceIndex) const
{
- Utils::LineColumn lineColumn = lineColumnFromIndex(sourceIndex);
+ Utils::Text::Position lineColumn = positionFromIndex(sourceIndex);
return {lineColumn, lineColumn};
}
diff --git a/src/plugins/cppeditor/cppoutlinemodel.h b/src/plugins/cppeditor/cppoutlinemodel.h
index 08b7adb442..ce499c91a0 100644
--- a/src/plugins/cppeditor/cppoutlinemodel.h
+++ b/src/plugins/cppeditor/cppoutlinemodel.h
@@ -4,6 +4,7 @@
#pragma once
#include <utils/dropsupport.h>
+#include <utils/textutils.h>
#include <utils/treemodel.h>
#include <cplusplus/CppDocument.h>
@@ -44,10 +45,11 @@ public:
bool isGenerated(const QModelIndex &sourceIndex) const;
Utils::Link linkFromIndex(const QModelIndex &sourceIndex) const;
- Utils::LineColumn lineColumnFromIndex(const QModelIndex &sourceIndex) const;
- using Range = std::pair<Utils::LineColumn, Utils::LineColumn>;
+ Utils::Text::Position positionFromIndex(const QModelIndex &sourceIndex) const;
+ using Range = std::pair<Utils::Text::Position, Utils::Text::Position>;
Range rangeFromIndex(const QModelIndex &sourceIndex) const;
+ // line is 1-based and column is 0-based
QModelIndex indexForPosition(int line, int column, const QModelIndex &rootIndex = {}) const;
private:
diff --git a/src/plugins/cppeditor/cppprojectinfogenerator.cpp b/src/plugins/cppeditor/cppprojectinfogenerator.cpp
index fe077bd377..5c4c03de4c 100644
--- a/src/plugins/cppeditor/cppprojectinfogenerator.cpp
+++ b/src/plugins/cppeditor/cppprojectinfogenerator.cpp
@@ -10,30 +10,24 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
-#include <utils/qtcassert.h>
-
+#include <QPromise>
#include <QTimer>
-#include <set>
-
using namespace ProjectExplorer;
using namespace Utils;
namespace CppEditor::Internal {
-ProjectInfoGenerator::ProjectInfoGenerator(
- const QFutureInterface<ProjectInfo::ConstPtr> &futureInterface,
- const ProjectUpdateInfo &projectUpdateInfo)
- : m_futureInterface(futureInterface)
- , m_projectUpdateInfo(projectUpdateInfo)
+ProjectInfoGenerator::ProjectInfoGenerator(const ProjectUpdateInfo &projectUpdateInfo)
+ : m_projectUpdateInfo(projectUpdateInfo)
{
}
-ProjectInfo::ConstPtr ProjectInfoGenerator::generate()
+ProjectInfo::ConstPtr ProjectInfoGenerator::generate(const QPromise<ProjectInfo::ConstPtr> &promise)
{
QVector<ProjectPart::ConstPtr> projectParts;
for (const RawProjectPart &rpp : m_projectUpdateInfo.rawProjectParts) {
- if (m_futureInterface.isCanceled())
+ if (promise.isCanceled())
return {};
for (const ProjectPart::ConstPtr &part : createProjectParts(
rpp, m_projectUpdateInfo.projectFilePath)) {
diff --git a/src/plugins/cppeditor/cppprojectinfogenerator.h b/src/plugins/cppeditor/cppprojectinfogenerator.h
index c090974c04..8720d3e1cd 100644
--- a/src/plugins/cppeditor/cppprojectinfogenerator.h
+++ b/src/plugins/cppeditor/cppprojectinfogenerator.h
@@ -5,17 +5,19 @@
#include "projectinfo.h"
-#include <QFutureInterface>
+QT_BEGIN_NAMESPACE
+template <class T>
+class QPromise;
+QT_END_NAMESPACE
namespace CppEditor::Internal {
class ProjectInfoGenerator
{
public:
- ProjectInfoGenerator(const QFutureInterface<ProjectInfo::ConstPtr> &futureInterface,
- const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo);
+ ProjectInfoGenerator(const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo);
- ProjectInfo::ConstPtr generate();
+ ProjectInfo::ConstPtr generate(const QPromise<ProjectInfo::ConstPtr> &promise);
private:
const QVector<ProjectPart::ConstPtr> createProjectParts(
@@ -29,7 +31,6 @@ private:
Utils::LanguageExtensions languageExtensions);
private:
- const QFutureInterface<ProjectInfo::ConstPtr> m_futureInterface;
const ProjectExplorer::ProjectUpdateInfo &m_projectUpdateInfo;
bool m_cToolchainMissing = false;
bool m_cxxToolchainMissing = false;
diff --git a/src/plugins/cppeditor/cppprojectupdater.cpp b/src/plugins/cppeditor/cppprojectupdater.cpp
index a784d3bb79..e848c6eba5 100644
--- a/src/plugins/cppeditor/cppprojectupdater.cpp
+++ b/src/plugins/cppeditor/cppprojectupdater.cpp
@@ -13,21 +13,15 @@
#include <projectexplorer/extracompiler.h>
#include <utils/algorithm.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <QFutureInterface>
-
using namespace ProjectExplorer;
using namespace Utils;
namespace CppEditor {
-CppProjectUpdater::CppProjectUpdater()
-{
- m_futureSynchronizer.setCancelOnWait(true);
-}
-
+CppProjectUpdater::CppProjectUpdater() = default;
CppProjectUpdater::~CppProjectUpdater() = default;
void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo)
@@ -49,12 +43,12 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
using namespace ProjectExplorer;
// Run the project info generator in a worker thread and continue if that one is finished.
- const auto infoGenerator = [=](QFutureInterface<ProjectInfo::ConstPtr> &futureInterface) {
+ const auto infoGenerator = [=](QPromise<ProjectInfo::ConstPtr> &promise) {
ProjectUpdateInfo fullProjectUpdateInfo = projectUpdateInfo;
if (fullProjectUpdateInfo.rppGenerator)
fullProjectUpdateInfo.rawProjectParts = fullProjectUpdateInfo.rppGenerator();
- Internal::ProjectInfoGenerator generator(futureInterface, fullProjectUpdateInfo);
- futureInterface.reportResult(generator.generate());
+ Internal::ProjectInfoGenerator generator(fullProjectUpdateInfo);
+ promise.addResult(generator.generate(promise));
};
using namespace Tasking;
@@ -62,16 +56,16 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
ProjectInfo::ConstPtr projectInfo = nullptr;
};
const TreeStorage<UpdateStorage> storage;
- const auto setupInfoGenerator = [=](AsyncTask<ProjectInfo::ConstPtr> &async) {
- async.setAsyncCallData(infoGenerator);
+ const auto setupInfoGenerator = [=](Async<ProjectInfo::ConstPtr> &async) {
+ async.setConcurrentCallData(infoGenerator);
async.setFutureSynchronizer(&m_futureSynchronizer);
};
- const auto onInfoGeneratorDone = [=](const AsyncTask<ProjectInfo::ConstPtr> &async) {
+ const auto onInfoGeneratorDone = [=](const Async<ProjectInfo::ConstPtr> &async) {
if (async.isResultAvailable())
storage->projectInfo = async.result();
};
QList<TaskItem> tasks{parallel};
- tasks.append(Async<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone));
+ tasks.append(AsyncTask<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone));
for (QPointer<ExtraCompiler> compiler : compilers) {
if (compiler && compiler->isDirty())
tasks.append(compiler->compileFileItem());
diff --git a/src/plugins/cppeditor/cppprojectupdater.h b/src/plugins/cppeditor/cppprojectupdater.h
index 50910e8320..a8cebc5b1e 100644
--- a/src/plugins/cppeditor/cppprojectupdater.h
+++ b/src/plugins/cppeditor/cppprojectupdater.h
@@ -10,7 +10,7 @@
#include <utils/futuresynchronizer.h>
namespace ProjectExplorer { class ExtraCompiler; }
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace CppEditor {
@@ -44,7 +44,7 @@ public:
private:
Utils::FutureSynchronizer m_futureSynchronizer;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
};
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp
index 560a27fe24..f57670c161 100644
--- a/src/plugins/cppeditor/cppquickfix_test.cpp
+++ b/src/plugins/cppeditor/cppquickfix_test.cpp
@@ -1725,7 +1725,7 @@ void QuickfixTest::testGeneric_data()
<< _(R"(const char *str = @"\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)")
<< _(R"(const char *str = "àf23бgб1";)");
QTest::newRow("AddLocalDeclaration_QTCREATORBUG-26004")
- << CppQuickFixFactoryPtr(new AddLocalDeclaration)
+ << CppQuickFixFactoryPtr(new AddDeclarationForUndeclaredIdentifier)
<< _("void func() {\n"
" QStringList list;\n"
" @it = list.cbegin();\n"
@@ -2315,7 +2315,7 @@ signals:
void newFooBarTestValue();
private:
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
};
)-";
QTest::addRow("create right names") << QByteArrayList{originalSource, expectedSource} << 4;
@@ -2346,7 +2346,7 @@ signals:
void newFooBarTestValue();
private:
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
};
)-";
expectedSource = "";
@@ -2355,7 +2355,7 @@ private:
// create from Q_PROPERTY with custom names
originalSource = R"-(
class Test {
- Q_PROPER@TY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPER@TY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
public:
int give_me_foo_bar_test() const
@@ -2380,7 +2380,7 @@ signals:
)-";
expectedSource = R"-(
class Test {
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
public:
int give_me_foo_bar_test() const
@@ -2411,14 +2411,14 @@ private:
// create from Q_PROPERTY with custom names
originalSource = R"-(
class Test {
- Q_PROPE@RTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPE@RTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
int mem_fooBar_test;
public:
};
)-";
expectedSource = R"-(
class Test {
- Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue)
+ Q_PROPERTY(int fooBar_test READ give_me_foo_bar_test WRITE Seet_FooBar_test RESET set_fooBarTest_toDefault NOTIFY newFooBarTestValue FINAL)
int mem_fooBar_test;
public:
int give_me_foo_bar_test() const
@@ -2766,7 +2766,7 @@ public:
signals:
void barChanged(N2::test *bar);
private:
- Q_PROPERTY(N2::test *bar READ getBar NOTIFY barChanged)
+ Q_PROPERTY(N2::test *bar READ getBar NOTIFY barChanged FINAL)
};
})--";
testDocuments << CppTestDocument::create("file.h", original, expected);
@@ -3303,7 +3303,7 @@ void QuickfixTest::testGenerateGetterSetterAnonymousClass()
void fooChanged();
private:
- Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged)
+ Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged FINAL)
} bar;
)";
testDocuments << CppTestDocument::create("file.h", original, expected);
@@ -3334,7 +3334,7 @@ public:
signals:
void barChanged();
private:
- Q_PROPERTY(int bar READ getBar WRITE setBar RESET resetBar NOTIFY barChanged)
+ Q_PROPERTY(int bar READ getBar WRITE setBar RESET resetBar NOTIFY barChanged FINAL)
};
inline int Foo::getBar() const
@@ -3560,8 +3560,8 @@ private:
int m_bar;
int bar2_;
QString bar3;
- Q_PROPERTY(int bar2 READ getBar2 WRITE setBar2 RESET resetBar2 NOTIFY bar2Changed)
- Q_PROPERTY(QString bar3 READ getBar3 WRITE setBar3 RESET resetBar3 NOTIFY bar3Changed)
+ Q_PROPERTY(int bar2 READ getBar2 WRITE setBar2 RESET resetBar2 NOTIFY bar2Changed FINAL)
+ Q_PROPERTY(QString bar3 READ getBar3 WRITE setBar3 RESET resetBar3 NOTIFY bar3Changed FINAL)
};
inline void Foo::resetBar()
{
@@ -3780,7 +3780,7 @@ void QuickfixTest::testInsertQtPropertyMembers()
QuickFixOperationTest({CppTestDocument::create("file.cpp", original, expected)}, &factory);
}
-void QuickfixTest::testInsertMemberFromInitialization_data()
+void QuickfixTest::testInsertMemberFromUse_data()
{
QTest::addColumn<QByteArray>("original");
QTest::addColumn<QByteArray>("expected");
@@ -3852,9 +3852,241 @@ void QuickfixTest::testInsertMemberFromInitialization_data()
" int m_x;\n"
"};\n";
QTest::addRow("initialization via function call") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@value = v; }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " int value;\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.value = v; }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ QTest::addRow("add member to other struct") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::@value = v; }\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static int value;\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::value = v; }\n"
+ "};\n";
+ QTest::addRow("add static member to other struct (explicit)") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@value = v; }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static int value;\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.value = v; }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ QTest::addRow("add static member to other struct (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "};\n"
+ "void C::setValue(int v) { this->@m_value = v; }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "private:\n"
+ " int m_value;\n"
+ "};\n"
+ "void C::setValue(int v) { this->@m_value = v; }\n";
+ QTest::addRow("add member to this (explicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { @m_value = v; }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_value = v; }\n"
+ "private:\n"
+ " int m_value;\n"
+ "};\n";
+ QTest::addRow("add member to this (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v) { @m_value = v; }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v) { m_value = v; }\n"
+ "private:\n"
+ " static int m_value;\n"
+ "};\n";
+ QTest::addRow("add static member to this (inline)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v);\n"
+ "};\n"
+ "void C::setValue(int v) { @m_value = v; }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static void setValue(int v);\n"
+ "private:\n"
+ " static int m_value;\n"
+ "};\n"
+ "void C::setValue(int v) { @m_value = v; }\n";
+ QTest::addRow("add static member to this (non-inline)") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@setValue(v); }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " void setValue(int);\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.setValue(v); }\n"
+ "private:\n"
+ " S m_s;\n"
+ "};\n";
+ QTest::addRow("add member function to other struct") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::@setValue(v); }\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static void setValue(int);\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { S::setValue(v); }\n"
+ "};\n";
+ QTest::addRow("add static member function to other struct (explicit)") << original << expected;
+
+ original =
+ "struct S {\n\n};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.@setValue(v); }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ expected =
+ "struct S {\n\n"
+ " static void setValue(int);\n"
+ "};\n"
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { m_s.setValue(v); }\n"
+ "private:\n"
+ " static S m_s;\n"
+ "};\n";
+ QTest::addRow("add static member function to other struct (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "};\n"
+ "void C::setValue(int v) { this->@setValueInternal(v); }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v);\n"
+ "private:\n"
+ " void setValueInternal(int);\n"
+ "};\n"
+ "void C::setValue(int v) { this->setValueInternal(v); }\n";
+ QTest::addRow("add member function to this (explicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { @setValueInternal(v); }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " void setValue(int v) { setValueInternal(v); }\n"
+ "private:\n"
+ " void setValueInternal(int);\n"
+ "};\n";
+ QTest::addRow("add member function to this (implicit)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static int value() { int i = @valueInternal(); return i; }\n"
+ "};\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static int value() { int i = @valueInternal(); return i; }\n"
+ "private:\n"
+ " static int valueInternal();\n"
+ "};\n";
+ QTest::addRow("add static member function to this (inline)") << original << expected;
+
+ original =
+ "class C {\n"
+ "public:\n"
+ " static int value();\n"
+ "};\n"
+ "int C::value() { return @valueInternal(); }\n";
+ expected =
+ "class C {\n"
+ "public:\n"
+ " static int value();\n"
+ "private:\n"
+ " static int valueInternal();\n"
+ "};\n"
+ "int C::value() { return valueInternal(); }\n";
+ QTest::addRow("add static member function to this (non-inline)") << original << expected;
}
-void QuickfixTest::testInsertMemberFromInitialization()
+void QuickfixTest::testInsertMemberFromUse()
{
QFETCH(QByteArray, original);
QFETCH(QByteArray, expected);
@@ -3863,7 +4095,8 @@ void QuickfixTest::testInsertMemberFromInitialization()
CppTestDocument::create("file.h", original, expected)
});
- InsertMemberFromInitialization factory;
+ AddDeclarationForUndeclaredIdentifier factory;
+ factory.setMembersOnly();
QuickFixOperationTest(testDocuments, &factory);
}
diff --git a/src/plugins/cppeditor/cppquickfix_test.h b/src/plugins/cppeditor/cppquickfix_test.h
index 56cacb367c..2d0dd0ebc9 100644
--- a/src/plugins/cppeditor/cppquickfix_test.h
+++ b/src/plugins/cppeditor/cppquickfix_test.h
@@ -101,8 +101,8 @@ private slots:
void testInsertQtPropertyMembers_data();
void testInsertQtPropertyMembers();
- void testInsertMemberFromInitialization_data();
- void testInsertMemberFromInitialization();
+ void testInsertMemberFromUse_data();
+ void testInsertMemberFromUse();
void testConvertQt4ConnectConnectOutOfClass();
void testConvertQt4ConnectConnectWithinClass_data();
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 4410ab60a8..85489b877e 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -10,6 +10,7 @@
#include "cppeditorwidget.h"
#include "cppfunctiondecldeflink.h"
#include "cppinsertvirtualmethods.h"
+#include "cpplocatordata.h"
#include "cpppointerdeclarationformatter.h"
#include "cppquickfixassistant.h"
#include "cppquickfixprojectsettings.h"
@@ -26,6 +27,7 @@
#include <cplusplus/ASTPath.h>
#include <cplusplus/CPlusPlusForwardDeclarations.h>
#include <cplusplus/CppRewriter.h>
+#include <cplusplus/NamePrettyPrinter.h>
#include <cplusplus/TypeOfExpression.h>
#include <cplusplus/TypePrettyPrinter.h>
@@ -33,7 +35,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/basetreeview.h>
@@ -301,6 +303,64 @@ ClassSpecifierAST *astForClassOperations(const CppQuickFixInterface &interface)
return nullptr;
}
+QString nameString(const NameAST *name)
+{
+ return CppCodeStyleSettings::currentProjectCodeStyleOverview().prettyName(name->name);
+}
+
+// FIXME: Needs to consider the scope at the insertion site.
+QString declFromExpr(const TypeOrExpr &typeOrExpr, const CallAST *call, const NameAST *varName,
+ const Snapshot &snapshot, const LookupContext &context,
+ const CppRefactoringFilePtr &file)
+{
+ const auto getTypeFromUser = [varName, call]() -> QString {
+ if (call)
+ return {};
+ const QString typeFromUser = QInputDialog::getText(Core::ICore::dialogParent(),
+ Tr::tr("Provide the type"),
+ Tr::tr("Data type:"), QLineEdit::Normal);
+ if (!typeFromUser.isEmpty())
+ return typeFromUser + ' ' + nameString(varName);
+ return {};
+ };
+ const auto getTypeOfExpr = [&](const ExpressionAST *expr) -> FullySpecifiedType {
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(file->cppDocument(), snapshot, context.bindings());
+ Scope *scope = file->scopeAt(expr->firstToken());
+ const QList<LookupItem> result = typeOfExpression(
+ file->textOf(expr).toUtf8(), scope, TypeOfExpression::Preprocess);
+ if (result.isEmpty())
+ return {};
+
+ SubstitutionEnvironment env;
+ env.setContext(context);
+ env.switchScope(result.first().scope());
+ ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
+ if (!con)
+ con = typeOfExpression.context().globalNamespace();
+ UseMinimalNames q(con);
+ env.enter(&q);
+
+ Control *control = context.bindings()->control().data();
+ return rewriteType(result.first().type(), &env, control);
+ };
+
+ const Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
+ const FullySpecifiedType type = std::holds_alternative<FullySpecifiedType>(typeOrExpr)
+ ? std::get<FullySpecifiedType>(typeOrExpr)
+ : getTypeOfExpr(std::get<const ExpressionAST *>(typeOrExpr));
+ if (!call)
+ return type.isValid() ? oo.prettyType(type, varName->name) : getTypeFromUser();
+
+ Function func(file->cppDocument()->translationUnit(), 0, varName->name);
+ for (ExpressionListAST *it = call->expression_list; it; it = it->next) {
+ Argument * const arg = new Argument(nullptr, 0, nullptr);
+ arg->setType(getTypeOfExpr(it->value));
+ func.addMember(arg);
+ }
+ return oo.prettyType(type) + ' ' + oo.prettyType(func.type(), varName->name);
+}
+
} // anonymous namespace
namespace {
@@ -1610,33 +1670,8 @@ private:
if (currentFile->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto)
return "auto " + oo.prettyName(simpleNameAST->name);
-
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(semanticInfo().doc, snapshot(), context().bindings());
- Scope *scope = currentFile->scopeAt(binaryAST->firstToken());
- const QList<LookupItem> result =
- typeOfExpression(currentFile->textOf(binaryAST->right_expression).toUtf8(),
- scope,
- TypeOfExpression::Preprocess);
-
- if (!result.isEmpty()) {
- SubstitutionEnvironment env;
- env.setContext(context());
- env.switchScope(result.first().scope());
- ClassOrNamespace *con = typeOfExpression.context().lookupType(scope);
- if (!con)
- con = typeOfExpression.context().globalNamespace();
- UseMinimalNames q(con);
- env.enter(&q);
-
- Control *control = context().bindings()->control().data();
- FullySpecifiedType tn = rewriteType(result.first().type(), &env, control);
-
- QString declaration = oo.prettyType(tn, simpleNameAST->name);
- return declaration;
- }
-
- return {};
+ return declFromExpr(binaryAST->right_expression, nullptr, simpleNameAST, snapshot(),
+ context(), currentFile);
}
const BinaryExpressionAST *binaryAST;
@@ -1645,42 +1680,6 @@ private:
} // anonymous namespace
-void AddLocalDeclaration::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
-{
- const QList<AST *> &path = interface.path();
- CppRefactoringFilePtr file = interface.currentFile();
-
- for (int index = path.size() - 1; index != -1; --index) {
- if (BinaryExpressionAST *binary = path.at(index)->asBinaryExpression()) {
- if (binary->left_expression && binary->right_expression
- && file->tokenAt(binary->binary_op_token).is(T_EQUAL)) {
- IdExpressionAST *idExpr = binary->left_expression->asIdExpression();
- if (interface.isCursorOn(binary->left_expression) && idExpr
- && idExpr->name->asSimpleName() != nullptr) {
- SimpleNameAST *nameAST = idExpr->name->asSimpleName();
- const QList<LookupItem> results = interface.context().lookup(nameAST->name, file->scopeAt(nameAST->firstToken()));
- Declaration *decl = nullptr;
- for (const LookupItem &r : results) {
- if (!r.declaration())
- continue;
- if (Declaration *d = r.declaration()->asDeclaration()) {
- if (!d->type()->asFunctionType()) {
- decl = d;
- break;
- }
- }
- }
-
- if (!decl) {
- result << new AddLocalDeclarationOp(interface, index, binary, nameAST);
- return;
- }
- }
- }
- }
- }
-}
-
namespace {
class ConvertToCamelCaseOp: public CppQuickFixOperation
@@ -1990,45 +1989,45 @@ Snapshot forwardingHeaders(const CppQuickFixInterface &interface)
return result;
}
-bool matchName(const Name *name, QList<Core::LocatorFilterEntry> *matches, QString *className) {
+QList<IndexItem::Ptr> matchName(const Name *name, QString *className)
+{
if (!name)
- return false;
+ return {};
QString simpleName;
- if (Core::ILocatorFilter *classesFilter = CppModelManager::instance()->classesFilter()) {
- QFutureInterface<Core::LocatorFilterEntry> dummy;
-
- const Overview oo;
- if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId()) {
- const Name *name = qualifiedName->name();
- if (const TemplateNameId *templateName = name->asTemplateNameId()) {
- *className = templateNameAsString(templateName);
- } else {
- simpleName = oo.prettyName(name);
- *className = simpleName;
- *matches = classesFilter->matchesFor(dummy, *className);
- if (matches->empty()) {
- if (const Name *name = qualifiedName->base()) {
- if (const TemplateNameId *templateName = name->asTemplateNameId())
- *className = templateNameAsString(templateName);
- else
- *className = oo.prettyName(name);
- }
- }
- }
- } else if (const TemplateNameId *templateName = name->asTemplateNameId()) {
+ QList<IndexItem::Ptr> matches;
+ CppLocatorData *locatorData = CppModelManager::instance()->locatorData();
+ const Overview oo;
+ if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId()) {
+ const Name *name = qualifiedName->name();
+ if (const TemplateNameId *templateName = name->asTemplateNameId()) {
*className = templateNameAsString(templateName);
} else {
- *className = oo.prettyName(name);
- }
-
- if (matches->empty())
- *matches = classesFilter->matchesFor(dummy, *className);
- if (matches->empty() && !simpleName.isEmpty())
+ simpleName = oo.prettyName(name);
*className = simpleName;
+ matches = locatorData->findSymbols(IndexItem::Class, *className);
+ if (matches.isEmpty()) {
+ if (const Name *name = qualifiedName->base()) {
+ if (const TemplateNameId *templateName = name->asTemplateNameId())
+ *className = templateNameAsString(templateName);
+ else
+ *className = oo.prettyName(name);
+ }
+ }
+ }
+ } else if (const TemplateNameId *templateName = name->asTemplateNameId()) {
+ *className = templateNameAsString(templateName);
+ } else {
+ *className = oo.prettyName(name);
}
- return !matches->empty();
+ if (matches.isEmpty())
+ matches = locatorData->findSymbols(IndexItem::Class, *className);
+
+ if (matches.isEmpty() && !simpleName.isEmpty())
+ *className = simpleName;
+
+ return matches;
}
} // anonymous namespace
@@ -2045,17 +2044,16 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
return;
QString className;
- QList<Core::LocatorFilterEntry> matches;
const QString currentDocumentFilePath = interface.semanticInfo().doc->filePath().toString();
const ProjectExplorer::HeaderPaths headerPaths = relevantHeaderPaths(currentDocumentFilePath);
FilePaths headers;
+ const QList<IndexItem::Ptr> matches = matchName(nameAst->name, &className);
// Find an include file through the locator
- if (matchName(nameAst->name, &matches, &className)) {
+ if (!matches.isEmpty()) {
QList<IndexItem::Ptr> indexItems;
const Snapshot forwardHeaders = forwardingHeaders(interface);
- for (const Core::LocatorFilterEntry &entry : std::as_const(matches)) {
- IndexItem::Ptr info = entry.internalData.value<IndexItem::Ptr>();
+ for (const IndexItem::Ptr &info : matches) {
if (!info || info->symbolName() != className)
continue;
indexItems << info;
@@ -2935,70 +2933,239 @@ class InsertMemberFromInitializationOp : public CppQuickFixOperation
{
public:
InsertMemberFromInitializationOp(
- const CppQuickFixInterface &interface,
- const Class *theClass,
- const QString &member,
- const QString &type)
- : CppQuickFixOperation(interface), m_class(theClass), m_member(member), m_type(type)
- {
- setDescription(Tr::tr("Add Class Member \"%1\"").arg(m_member));
+ const CppQuickFixInterface &interface,
+ const Class *theClass,
+ const NameAST *memberName,
+ const TypeOrExpr &typeOrExpr,
+ const CallAST *call,
+ InsertionPointLocator::AccessSpec accessSpec,
+ bool makeStatic)
+ : CppQuickFixOperation(interface),
+ m_class(theClass), m_memberName(memberName), m_typeOrExpr(typeOrExpr), m_call(call),
+ m_accessSpec(accessSpec), m_makeStatic(makeStatic)
+ {
+ if (call)
+ setDescription(Tr::tr("Add Member Function \"%1\"").arg(nameString(memberName)));
+ else
+ setDescription(Tr::tr("Add Class Member \"%1\"").arg(nameString(memberName)));
}
private:
void perform() override
{
- QString type = m_type;
- if (type.isEmpty()) {
- type = QInputDialog::getText(
- Core::ICore::dialogParent(),
- Tr::tr("Provide the type"),
- Tr::tr("Data type:"),
- QLineEdit::Normal);
- }
- if (type.isEmpty())
+ QString decl = declFromExpr(m_typeOrExpr, m_call, m_memberName, snapshot(), context(),
+ currentFile());
+ if (decl.isEmpty())
return;
+ if (m_makeStatic)
+ decl.prepend("static ");
const CppRefactoringChanges refactoring(snapshot());
const InsertionPointLocator locator(refactoring);
const FilePath filePath = FilePath::fromUtf8(m_class->fileName());
const InsertionLocation loc = locator.methodDeclarationInClass(
- filePath, m_class, InsertionPointLocator::Private);
+ filePath, m_class, m_accessSpec);
QTC_ASSERT(loc.isValid(), return);
CppRefactoringFilePtr targetFile = refactoring.file(filePath);
const int targetPosition1 = targetFile->position(loc.line(), loc.column());
const int targetPosition2 = qMax(0, targetFile->position(loc.line(), 1) - 1);
ChangeSet target;
- target.insert(targetPosition1, loc.prefix() + type + ' ' + m_member + ";\n");
+ target.insert(targetPosition1, loc.prefix() + decl + ";\n");
targetFile->setChangeSet(target);
targetFile->appendIndentRange(ChangeSet::Range(targetPosition2, targetPosition1));
targetFile->apply();
}
const Class * const m_class;
- const QString m_member;
- const QString m_type;
+ const NameAST * const m_memberName;
+ const TypeOrExpr m_typeOrExpr;
+ const CallAST * m_call;
+ const InsertionPointLocator::AccessSpec m_accessSpec;
+ const bool m_makeStatic;
};
-void InsertMemberFromInitialization::match(const CppQuickFixInterface &interface,
- QuickFixOperations &result)
+void AddDeclarationForUndeclaredIdentifier::match(const CppQuickFixInterface &interface,
+ QuickFixOperations &result)
{
- // First check whether we are on a member initialization.
- const QList<AST *> path = interface.path();
- const int size = path.size();
- if (size < 4)
+ // Are we on a name?
+ const QList<AST *> &path = interface.path();
+ if (path.isEmpty())
return;
- const SimpleNameAST * const name = path.at(size - 1)->asSimpleName();
- if (!name)
+ if (!path.last()->asSimpleName())
+ return;
+
+ // Special case: Member initializer.
+ if (!checkForMemberInitializer(interface, result))
+ return;
+
+ // Are we inside a function?
+ const FunctionDefinitionAST *func = nullptr;
+ for (auto it = path.rbegin(); !func && it != path.rend(); ++it)
+ func = (*it)->asFunctionDefinition();
+ if (!func)
return;
+
+ // Is this name declared somewhere already?
+ const CursorInEditor cursorInEditor(interface.cursor(), interface.filePath(),
+ interface.editor(), interface.editor()->textDocument());
+ const auto followSymbolFallback = [&](const Link &link) {
+ if (!link.hasValidTarget())
+ collectOperations(interface, result);
+ };
+ CppModelManager::followSymbol(cursorInEditor, followSymbolFallback, false, false,
+ CppModelManager::Backend::Builtin);
+}
+
+void AddDeclarationForUndeclaredIdentifier::collectOperations(
+ const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result)
+{
+ const QList<AST *> &path = interface.path();
+ const CppRefactoringFilePtr &file = interface.currentFile();
+ for (int index = path.size() - 1; index != -1; --index) {
+ if (const auto call = path.at(index)->asCall())
+ return handleCall(call, interface, result);
+
+ // We only trigger if the identifier appears on the left-hand side of an
+ // assignment expression.
+ const auto binExpr = path.at(index)->asBinaryExpression();
+ if (!binExpr)
+ continue;
+ if (!binExpr->left_expression || !binExpr->right_expression
+ || file->tokenAt(binExpr->binary_op_token).kind() != T_EQUAL
+ || !interface.isCursorOn(binExpr->left_expression)) {
+ return;
+ }
+
+ // In the case of "a.|b = c", find out the type of a, locate the class declaration
+ // and add a member b there.
+ if (const auto memberAccess = binExpr->left_expression->asMemberAccess()) {
+ if (interface.isCursorOn(memberAccess->member_name)
+ && memberAccess->member_name == path.last()) {
+ maybeAddMember(interface, file->scopeAt(memberAccess->firstToken()),
+ file->textOf(memberAccess->base_expression).toUtf8(),
+ binExpr->right_expression, nullptr, result);
+ }
+ return;
+ }
+
+ const auto idExpr = binExpr->left_expression->asIdExpression();
+ if (!idExpr || !idExpr->name)
+ return;
+
+ // In the case of "A::|b = c", add a static member b to A.
+ if (const auto qualName = idExpr->name->asQualifiedName()) {
+ return maybeAddStaticMember(interface, qualName, binExpr->right_expression, nullptr,
+ result);
+ }
+
+ // For an unqualified access, offer a local declaration and, if we are
+ // in a member function, a member declaration.
+ if (const auto simpleName = idExpr->name->asSimpleName()) {
+ if (!m_membersOnly)
+ result << new AddLocalDeclarationOp(interface, index, binExpr, simpleName);
+ maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
+ binExpr->right_expression, nullptr, result);
+ return;
+ }
+ }
+}
+
+void AddDeclarationForUndeclaredIdentifier::handleCall(
+ const CallAST *call, const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result)
+{
+ if (!call->base_expression)
+ return;
+
+ // In order to find out the return type, we need to check the context of the call.
+ // If it is a statement expression, the type is void, if it's a binary expression,
+ // we assume the type of the other side of the expression, if it's a return statement,
+ // we use the return type of the surrounding function, and if it's a declaration,
+ // we use the type of the variable. Other cases are not supported.
+ const QList<AST *> &path = interface.path();
+ const CppRefactoringFilePtr &file = interface.currentFile();
+ TypeOrExpr returnTypeOrExpr;
+ for (auto it = path.rbegin(); it != path.rend(); ++it) {
+ if ((*it)->asCompoundStatement())
+ return;
+ if ((*it)->asExpressionStatement()) {
+ returnTypeOrExpr = FullySpecifiedType(new VoidType);
+ break;
+ }
+ if (const auto binExpr = (*it)->asBinaryExpression()) {
+ returnTypeOrExpr = interface.isCursorOn(binExpr->left_expression)
+ ? binExpr->right_expression : binExpr->left_expression;
+ break;
+ }
+ if (const auto returnExpr = (*it)->asReturnStatement()) {
+ for (auto it2 = std::next(it); it2 != path.rend(); ++it2) {
+ if (const auto func = (*it2)->asFunctionDefinition()) {
+ if (!func->symbol)
+ return;
+ returnTypeOrExpr = func->symbol->returnType();
+ break;
+ }
+ }
+ break;
+ }
+ if (const auto declarator = (*it)->asDeclarator()) {
+ if (!interface.isCursorOn(declarator->initializer))
+ return;
+ const auto decl = (*std::next(it))->asSimpleDeclaration();
+ if (!decl || !decl->symbols)
+ return;
+ if (!decl->symbols->value->type().isValid())
+ return;
+ returnTypeOrExpr = decl->symbols->value->type();
+ break;
+ }
+ }
+
+ if (std::holds_alternative<const ExpressionAST *>(returnTypeOrExpr)
+ && !std::get<const ExpressionAST *>(returnTypeOrExpr)) {
+ return;
+ }
+
+ // a.f()
+ if (const auto memberAccess = call->base_expression->asMemberAccess()) {
+ if (!interface.isCursorOn(memberAccess->member_name))
+ return;
+ maybeAddMember(
+ interface, file->scopeAt(call->firstToken()),
+ file->textOf(memberAccess->base_expression).toUtf8(), returnTypeOrExpr, call, result);
+ }
+
+ const auto idExpr = call->base_expression->asIdExpression();
+ if (!idExpr || !idExpr->name)
+ return;
+
+ // A::f()
+ if (const auto qualName = idExpr->name->asQualifiedName())
+ return maybeAddStaticMember(interface, qualName, returnTypeOrExpr, call, result);
+
+ // f()
+ if (const auto simpleName = idExpr->name->asSimpleName()) {
+ maybeAddMember(interface, file->scopeAt(idExpr->firstToken()), "this",
+ returnTypeOrExpr, call, result);
+ }
+}
+
+bool AddDeclarationForUndeclaredIdentifier::checkForMemberInitializer(
+ const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result)
+{
+ const QList<AST *> &path = interface.path();
+ const int size = path.size();
+ if (size < 4)
+ return true;
const MemInitializerAST * const memInitializer = path.at(size - 2)->asMemInitializer();
if (!memInitializer)
- return;
+ return true;
if (!path.at(size - 3)->asCtorInitializer())
- return;
+ return true;
const FunctionDefinitionAST * ctor = path.at(size - 4)->asFunctionDefinition();
if (!ctor)
- return;
+ return false;
// Now find the class.
const Class *theClass = nullptr;
@@ -3011,65 +3178,141 @@ void InsertMemberFromInitialization::match(const CppQuickFixInterface &interface
// Out-of-line constructor. We need to find the class.
SymbolFinder finder;
const QList<Declaration *> matches = finder.findMatchingDeclaration(
- LookupContext(interface.currentFile()->cppDocument(), interface.snapshot()),
- ctor->symbol);
+ LookupContext(interface.currentFile()->cppDocument(), interface.snapshot()),
+ ctor->symbol);
if (!matches.isEmpty())
theClass = matches.first()->enclosingClass();
}
if (!theClass)
- return;
+ return false;
+
+ const SimpleNameAST * const name = path.at(size - 1)->asSimpleName();
+ QTC_ASSERT(name, return false);
// Check whether the member exists already.
if (theClass->find(interface.currentFile()->cppDocument()->translationUnit()->identifier(
- name->identifier_token))) {
- return;
+ name->identifier_token))) {
+ return false;
}
- const QString type = getType(interface, memInitializer, ctor);
- const Identifier * const memberId = interface.currentFile()->cppDocument()
- ->translationUnit()->identifier(name->identifier_token);
- const QString member = QString::fromUtf8(memberId->chars(), memberId->size());
+ result << new InsertMemberFromInitializationOp(
+ interface, theClass, memInitializer->name->asSimpleName(), memInitializer->expression,
+ nullptr, InsertionPointLocator::Private, false);
+ return false;
+}
+
+void AddDeclarationForUndeclaredIdentifier::maybeAddMember(
+ const CppQuickFixInterface &interface, Scope *scope, const QByteArray &classTypeExpr,
+ const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
+{
+ const QList<AST *> &path = interface.path();
+
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot(),
+ interface.context().bindings());
+ const QList<LookupItem> lhsTypes = typeOfExpression(
+ classTypeExpr, scope,
+ TypeOfExpression::Preprocess);
+ if (lhsTypes.isEmpty())
+ return;
- result << new InsertMemberFromInitializationOp(interface, theClass, member, type);
+ const Type *type = lhsTypes.first().type().type();
+ if (!type)
+ return;
+ if (type->asPointerType()) {
+ type = type->asPointerType()->elementType().type();
+ if (!type)
+ return;
+ }
+ const auto namedType = type->asNamedType();
+ if (!namedType)
+ return;
+ const ClassOrNamespace * const classOrNamespace
+ = interface.context().lookupType(namedType->name(), scope);
+ if (!classOrNamespace || !classOrNamespace->rootClass())
+ return;
+
+ const Class * const theClass = classOrNamespace->rootClass();
+ bool needsStatic = lhsTypes.first().type().isStatic();
+
+ // If the base expression refers to the same class that the member function is in,
+ // then we want to insert a private member, otherwise a public one.
+ const FunctionDefinitionAST *func = nullptr;
+ for (auto it = path.rbegin(); !func && it != path.rend(); ++it)
+ func = (*it)->asFunctionDefinition();
+ QTC_ASSERT(func, return);
+ InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
+ for (int i = 0; i < theClass->memberCount(); ++i) {
+ if (theClass->memberAt(i) == func->symbol) {
+ accessSpec = InsertionPointLocator::Private;
+ needsStatic = func->symbol->isStatic();
+ break;
+ }
+ }
+ if (accessSpec == InsertionPointLocator::Public) {
+ QList<Declaration *> decls;
+ QList<Declaration *> dummy;
+ SymbolFinder().findMatchingDeclaration(interface.context(), func->symbol, &decls,
+ &dummy, &dummy);
+ for (const Declaration * const decl : std::as_const(decls)) {
+ for (int i = 0; i < theClass->memberCount(); ++i) {
+ if (theClass->memberAt(i) == decl) {
+ accessSpec = InsertionPointLocator::Private;
+ needsStatic = decl->isStatic();
+ break;
+ }
+ }
+ if (accessSpec == InsertionPointLocator::Private)
+ break;
+ }
+ }
+ result << new InsertMemberFromInitializationOp(interface, theClass, path.last()->asName(),
+ typeOrExpr, call, accessSpec, needsStatic);
}
-QString InsertMemberFromInitialization::getType(
- const CppQuickFixInterface &interface,
- const MemInitializerAST *memInitializer,
- const FunctionDefinitionAST *ctor) const
+void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember(
+ const CppQuickFixInterface &interface, const QualifiedNameAST *qualName,
+ const TypeOrExpr &typeOrExpr, const CallAST *call, TextEditor::QuickFixOperations &result)
{
- // Try to deduce the type: If the initialization expression is just a name
- // (e.g. a constructor argument) or a function call, we don't bother the user.
- if (!memInitializer->expression)
- return {};
- const ExpressionListParenAST * const lParenAst
- = memInitializer->expression->asExpressionListParen();
- if (!lParenAst || !lParenAst->expression_list || !lParenAst->expression_list->value)
- return {};
- const IdExpressionAST *idExpr = lParenAst->expression_list->value->asIdExpression();
- if (!idExpr) { // Not a variable, so check for function call.
- const CallAST * const call = lParenAst->expression_list->value->asCall();
- if (!call || !call->base_expression)
- return {};
- idExpr = call->base_expression->asIdExpression();
+ const QList<AST *> &path = interface.path();
+
+ if (!interface.isCursorOn(qualName->unqualified_name))
+ return;
+ if (qualName->unqualified_name != path.last())
+ return;
+ if (!qualName->nested_name_specifier_list)
+ return;
+
+ const NameAST * const topLevelName
+ = qualName->nested_name_specifier_list->value->class_or_namespace_name;
+ if (!topLevelName)
+ return;
+ ClassOrNamespace * const classOrNamespace = interface.context().lookupType(
+ topLevelName->name, interface.currentFile()->scopeAt(qualName->firstToken()));
+ if (!classOrNamespace)
+ return;
+ QList<const Name *> otherNames;
+ for (auto it = qualName->nested_name_specifier_list->next; it; it = it->next) {
+ if (!it->value || !it->value->class_or_namespace_name)
+ return;
+ otherNames << it->value->class_or_namespace_name->name;
}
- if (!idExpr || !idExpr->name)
- return {};
- LookupContext context(interface.currentFile()->cppDocument(), interface.snapshot());
- const QList<LookupItem> matches = context.lookup(idExpr->name->name, ctor->symbol);
- if (matches.isEmpty())
- return {};
- Overview o = CppCodeStyleSettings::currentProjectCodeStyleOverview();
- TypePrettyPrinter tpp(&o);
- FullySpecifiedType type = matches.first().type();
- if (!type.type())
- return {};
- const Function * const funcType = type.type()->asFunctionType();
- if (funcType)
- type = funcType->returnType();
- return tpp(type);
+ const Class *theClass = nullptr;
+ if (!otherNames.isEmpty()) {
+ const Symbol * const symbol = classOrNamespace->lookupInScope(otherNames);
+ if (!symbol)
+ return;
+ theClass = symbol->asClass();
+ } else {
+ theClass = classOrNamespace->rootClass();
+ }
+ if (theClass) {
+ result << new InsertMemberFromInitializationOp(
+ interface, theClass, path.last()->asName(), typeOrExpr, call,
+ InsertionPointLocator::Public, true);
+ }
}
class MemberFunctionImplSetting
@@ -4183,7 +4426,7 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
propertyDeclaration.append(QLatin1String(" NOTIFY ")).append(data.signalName);
}
- propertyDeclaration.append(QLatin1String(")\n"));
+ propertyDeclaration.append(QLatin1String(" FINAL)\n"));
addHeaderCode(InsertionPointLocator::Private, propertyDeclaration);
}
}
@@ -7987,7 +8230,7 @@ private:
}
};
- if (const Project *project = SessionManager::projectForFile(filePath())) {
+ if (const Project *project = ProjectManager::projectForFile(filePath())) {
const FilePaths files = project->files(ProjectExplorer::Project::SourceFiles);
QSet<FilePath> projectFiles(files.begin(), files.end());
for (const auto &file : files) {
@@ -9073,7 +9316,6 @@ void createCppQuickFixes()
new SplitIfStatement;
new SplitSimpleDeclaration;
- new AddLocalDeclaration;
new AddBracesToIf;
new RearrangeParamDeclarationList;
new ReformatPointerDeclaration;
@@ -9090,7 +9332,7 @@ void createCppQuickFixes()
new GenerateGettersSettersForClass;
new InsertDeclFromDef;
new InsertDefFromDecl;
- new InsertMemberFromInitialization;
+ new AddDeclarationForUndeclaredIdentifier;
new InsertDefsFromDecls;
new MoveFuncDefOutside;
diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h
index 376bfa99bf..61db3ebf53 100644
--- a/src/plugins/cppeditor/cppquickfixes.h
+++ b/src/plugins/cppeditor/cppquickfixes.h
@@ -3,9 +3,10 @@
#pragma once
-#include "cppeditor_global.h"
#include "cppquickfix.h"
+#include <variant>
+
///
/// Adding New Quick Fixes
///
@@ -16,6 +17,7 @@
namespace CppEditor {
namespace Internal {
+using TypeOrExpr = std::variant<const CPlusPlus::ExpressionAST *, CPlusPlus::FullySpecifiedType>;
void createCppQuickFixes();
void destroyCppQuickFixes();
@@ -285,23 +287,6 @@ public:
};
/*!
- Rewrites
- a = foo();
-
- As
- Type a = foo();
-
- Where Type is the return type of foo()
-
- Activates on: the assignee, if the type of the right-hand side of the assignment is known.
-*/
-class AddLocalDeclaration: public CppQuickFixFactory
-{
-public:
- void match(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
-};
-
-/*!
Add curly braces to a if statement that doesn't already contain a
compound statement. I.e.
@@ -374,20 +359,36 @@ public:
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
};
-/*!
- Adds a class member from an initialization in the constructor.
- */
-class InsertMemberFromInitialization : public CppQuickFixFactory
+class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
{
public:
void match(const CppQuickFixInterface &interface,
TextEditor::QuickFixOperations &result) override;
+#ifdef WITH_TESTS
+ void setMembersOnly() { m_membersOnly = true; }
+#endif
+
private:
- QString getType(
- const CppQuickFixInterface &interface,
- const CPlusPlus::MemInitializerAST *memInitializer,
- const CPlusPlus::FunctionDefinitionAST *ctor) const;
+ void collectOperations(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result);
+ void handleCall(const CPlusPlus::CallAST *call, const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result);
+
+ // Returns whether to still do other checks.
+ bool checkForMemberInitializer(const CppQuickFixInterface &interface,
+ TextEditor::QuickFixOperations &result);
+
+ void maybeAddMember(const CppQuickFixInterface &interface, CPlusPlus::Scope *scope,
+ const QByteArray &classTypeExpr, const TypeOrExpr &typeOrExpr,
+ const CPlusPlus::CallAST *call, TextEditor::QuickFixOperations &result);
+
+ void maybeAddStaticMember(
+ const CppQuickFixInterface &interface, const CPlusPlus::QualifiedNameAST *qualName,
+ const TypeOrExpr &typeOrExpr, const CPlusPlus::CallAST *call,
+ TextEditor::QuickFixOperations &result);
+
+ bool m_membersOnly = false;
};
/*!
diff --git a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp b/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
index c0fb864665..c82985043f 100644
--- a/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
+++ b/src/plugins/cppeditor/cppquickfixprojectsettingswidget.cpp
@@ -28,7 +28,7 @@ CppQuickFixProjectSettingsWidget::CppQuickFixProjectSettingsWidget(ProjectExplor
auto layout = new QVBoxLayout();
gridLayout->addLayout(layout, 2, 0, 1, 2);
- m_settingsWidget = new CppQuickFixSettingsWidget(this);
+ m_settingsWidget = new CppQuickFixSettingsWidget;
m_settingsWidget->loadSettings(m_projectSettings->getSettings());
if (QLayout *layout = m_settingsWidget->layout())
diff --git a/src/plugins/cppeditor/cppquickfixsettingspage.cpp b/src/plugins/cppeditor/cppquickfixsettingspage.cpp
index 05aa4c586a..735dbea3b4 100644
--- a/src/plugins/cppeditor/cppquickfixsettingspage.cpp
+++ b/src/plugins/cppeditor/cppquickfixsettingspage.cpp
@@ -5,12 +5,8 @@
#include "cppeditorconstants.h"
#include "cppeditortr.h"
-#include "cppquickfixsettings.h"
#include "cppquickfixsettingswidget.h"
-#include <QCoreApplication>
-#include <QtDebug>
-
namespace CppEditor::Internal {
CppQuickFixSettingsPage::CppQuickFixSettingsPage()
@@ -18,27 +14,7 @@ CppQuickFixSettingsPage::CppQuickFixSettingsPage()
setId(Constants::QUICK_FIX_SETTINGS_ID);
setDisplayName(Tr::tr(Constants::QUICK_FIX_SETTINGS_DISPLAY_NAME));
setCategory(Constants::CPP_SETTINGS_CATEGORY);
-}
-
-QWidget *CppQuickFixSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new CppQuickFixSettingsWidget;
- m_widget->loadSettings(CppQuickFixSettings::instance());
- }
- return m_widget;
-}
-
-void CppQuickFixSettingsPage::apply()
-{
- const auto s = CppQuickFixSettings::instance();
- m_widget->saveSettings(s);
- s->saveAsGlobalSettings();
-}
-
-void CppQuickFixSettingsPage::finish()
-{
- delete m_widget;
+ setWidgetCreator([] { return new CppQuickFixSettingsWidget; });
}
} // CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppquickfixsettingspage.h b/src/plugins/cppeditor/cppquickfixsettingspage.h
index 3e14fde864..1965aae956 100644
--- a/src/plugins/cppeditor/cppquickfixsettingspage.h
+++ b/src/plugins/cppeditor/cppquickfixsettingspage.h
@@ -2,25 +2,15 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "coreplugin/dialogs/ioptionspage.h"
-#include <QPointer>
-namespace CppEditor {
-namespace Internal {
-class CppQuickFixSettingsWidget;
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace CppEditor::Internal {
class CppQuickFixSettingsPage : public Core::IOptionsPage
{
public:
CppQuickFixSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<CppQuickFixSettingsWidget> m_widget;
};
-} // namespace Internal
-} // namespace CppEditor
+} // CppEditor::Internal
diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp b/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
index 689a519ce3..679d140967 100644
--- a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
+++ b/src/plugins/cppeditor/cppquickfixsettingswidget.cpp
@@ -20,8 +20,6 @@
#include <QSpinBox>
#include <QtDebug>
-using namespace Utils;
-
namespace CppEditor::Internal {
class LineCountSpinBox : public QWidget
@@ -56,7 +54,7 @@ LineCountSpinBox::LineCountSpinBox(QWidget *parent)
m_unitLabel = new QLabel(Tr::tr("lines"));
using namespace Layouting;
- Row { m_checkBox, m_opLabel, m_spinBox, m_unitLabel, }.attachTo(this, WithoutMargins);
+ Row { m_checkBox, m_opLabel, m_spinBox, m_unitLabel, noMargin }.attachTo(this);
auto handleChange = [this] {
updateFields();
@@ -88,9 +86,8 @@ void LineCountSpinBox::updateFields()
m_unitLabel->setEnabled(enabled);
}
-CppQuickFixSettingsWidget::CppQuickFixSettingsWidget(QWidget *parent)
- : QWidget(parent)
- , m_typeSplitter("\\s*,\\s*")
+CppQuickFixSettingsWidget::CppQuickFixSettingsWidget()
+ : m_typeSplitter("\\s*,\\s*")
{
m_lines_getterOutsideClass = new LineCountSpinBox;
m_lines_getterInCppFile = new LineCountSpinBox;
@@ -223,7 +220,8 @@ e.g. name = "m_test_foo_":
Tr::tr("Inside class:"), Tr::tr("Default"), Tr::tr("Default"), br,
Tr::tr("Outside class:"), m_lines_setterOutsideClass, m_lines_getterOutsideClass, br,
Tr::tr("In .cpp file:"), m_lines_setterInCppFile, m_lines_getterInCppFile, br,
- }.attachTo(functionLocationsGrid, WithoutMargins);
+ noMargin,
+ }.attachTo(functionLocationsGrid);
if (QGridLayout *gl = qobject_cast<QGridLayout*>(functionLocationsGrid->layout()))
gl->setHorizontalSpacing(48);
@@ -319,6 +317,8 @@ e.g. name = "m_test_foo_":
connect(m_radioButton_addUsingnamespace, &QRadioButton::clicked, then);
connect(m_radioButton_generateMissingNamespace, &QRadioButton::clicked, then);
connect(m_radioButton_rewriteTypes, &QRadioButton::clicked, then);
+
+ loadSettings(CppQuickFixSettings::instance());
}
void CppQuickFixSettingsWidget::loadSettings(CppQuickFixSettings *settings)
@@ -426,6 +426,13 @@ void CppQuickFixSettingsWidget::saveSettings(CppQuickFixSettings *settings)
}
}
+void CppQuickFixSettingsWidget::apply()
+{
+ const auto s = CppQuickFixSettings::instance();
+ saveSettings(s);
+ s->saveAsGlobalSettings();
+}
+
void CppQuickFixSettingsWidget::currentCustomItemChanged(QListWidgetItem *newItem,
QListWidgetItem *oldItem)
{
diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.h b/src/plugins/cppeditor/cppquickfixsettingswidget.h
index f921afbff0..e11d81a9f2 100644
--- a/src/plugins/cppeditor/cppquickfixsettingswidget.h
+++ b/src/plugins/cppeditor/cppquickfixsettingswidget.h
@@ -3,6 +3,8 @@
#pragma once
+#include <coreplugin/dialogs/ioptionspage.h>
+
#include <QApplication>
#include <QRegularExpression>
#include <QWidget>
@@ -23,7 +25,7 @@ namespace CppEditor::Internal {
class LineCountSpinBox;
-class CppQuickFixSettingsWidget : public QWidget
+class CppQuickFixSettingsWidget : public Core::IOptionsPageWidget
{
Q_OBJECT
@@ -36,7 +38,7 @@ class CppQuickFixSettingsWidget : public QWidget
};
public:
- explicit CppQuickFixSettingsWidget(QWidget *parent = nullptr);
+ CppQuickFixSettingsWidget();
void loadSettings(CppQuickFixSettings *settings);
void saveSettings(CppQuickFixSettings *settings);
@@ -45,6 +47,7 @@ signals:
void settingsChanged();
private:
+ void apply() final;
void currentCustomItemChanged(QListWidgetItem *newItem, QListWidgetItem *oldItem);
bool m_isLoadingSettings = false;
diff --git a/src/plugins/cppeditor/cpprefactoringchanges.cpp b/src/plugins/cppeditor/cpprefactoringchanges.cpp
index 1cce747fc6..bb34174d06 100644
--- a/src/plugins/cppeditor/cpprefactoringchanges.cpp
+++ b/src/plugins/cppeditor/cpprefactoringchanges.cpp
@@ -57,8 +57,8 @@ CppRefactoringFilePtr CppRefactoringChanges::file(const FilePath &filePath) cons
CppRefactoringFileConstPtr CppRefactoringChanges::fileNoEditor(const FilePath &filePath) const
{
QTextDocument *document = nullptr;
- if (data()->m_workingCopy.contains(filePath))
- document = new QTextDocument(QString::fromUtf8(data()->m_workingCopy.source(filePath)));
+ if (const auto source = data()->m_workingCopy.source(filePath))
+ document = new QTextDocument(QString::fromUtf8(*source));
CppRefactoringFilePtr result(new CppRefactoringFile(document, filePath));
result->m_data = m_data;
diff --git a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
index 3e2438dbf8..4fc9dd7c66 100644
--- a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
+++ b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp
@@ -3,11 +3,10 @@
#include "cppsemanticinfoupdater.h"
-#include "cpplocalsymbols.h"
#include "cppmodelmanager.h"
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <cplusplus/Control.h>
#include <cplusplus/CppDocument.h>
@@ -29,11 +28,11 @@ public:
class FuturizedTopLevelDeclarationProcessor: public TopLevelDeclarationProcessor
{
public:
- explicit FuturizedTopLevelDeclarationProcessor(QFutureInterface<void> &future): m_future(future) {}
+ explicit FuturizedTopLevelDeclarationProcessor(QPromise<void> &promise): m_promise(promise) {}
bool processDeclaration(DeclarationAST *) override { return !isCanceled(); }
- bool isCanceled() { return m_future.isCanceled(); }
+ bool isCanceled() { return m_promise.isCanceled(); }
private:
- QFutureInterface<void> m_future;
+ QPromise<void> &m_promise;
};
public:
@@ -49,7 +48,7 @@ public:
bool reuseCurrentSemanticInfo(const SemanticInfo::Source &source, bool emitSignalWhenFinished);
- void update_helper(QFutureInterface<void> &future, const SemanticInfo::Source &source);
+ void update_helper(QPromise<void> &promise, const SemanticInfo::Source &source);
public:
SemanticInfoUpdater *q;
@@ -136,10 +135,10 @@ bool SemanticInfoUpdaterPrivate::reuseCurrentSemanticInfo(const SemanticInfo::So
return false;
}
-void SemanticInfoUpdaterPrivate::update_helper(QFutureInterface<void> &future,
+void SemanticInfoUpdaterPrivate::update_helper(QPromise<void> &promise,
const SemanticInfo::Source &source)
{
- FuturizedTopLevelDeclarationProcessor processor(future);
+ FuturizedTopLevelDeclarationProcessor processor(promise);
update(source, true, &processor);
}
@@ -179,7 +178,7 @@ void SemanticInfoUpdater::updateDetached(const SemanticInfo::Source &source)
return;
}
- d->m_future = Utils::runAsync(CppModelManager::instance()->sharedThreadPool(),
+ d->m_future = Utils::asyncRun(CppModelManager::instance()->sharedThreadPool(),
&SemanticInfoUpdaterPrivate::update_helper, d.data(), source);
}
diff --git a/src/plugins/cppeditor/cppsourceprocessor.cpp b/src/plugins/cppeditor/cppsourceprocessor.cpp
index 66cca58a0f..55dc1f10e0 100644
--- a/src/plugins/cppeditor/cppsourceprocessor.cpp
+++ b/src/plugins/cppeditor/cppsourceprocessor.cpp
@@ -81,7 +81,8 @@ inline const CPlusPlus::Macro revision(const WorkingCopy &workingCopy,
const CPlusPlus::Macro &macro)
{
CPlusPlus::Macro newMacro(macro);
- newMacro.setFileRevision(workingCopy.get(macro.filePath()).second);
+ if (const auto entry = workingCopy.get(macro.filePath()))
+ newMacro.setFileRevision(entry->second);
return newMacro;
}
@@ -189,10 +190,9 @@ bool CppSourceProcessor::getFileContents(const FilePath &absoluteFilePath,
return false;
// Get from working copy
- if (m_workingCopy.contains(absoluteFilePath)) {
- const QPair<QByteArray, unsigned> entry = m_workingCopy.get(absoluteFilePath);
- *contents = entry.first;
- *revision = entry.second;
+ if (const auto entry = m_workingCopy.get(absoluteFilePath)) {
+ *contents = entry->first;
+ *revision = entry->second;
return true;
}
@@ -216,7 +216,7 @@ bool CppSourceProcessor::checkFile(const FilePath &absoluteFilePath) const
{
if (absoluteFilePath.isEmpty()
|| m_included.contains(absoluteFilePath)
- || m_workingCopy.contains(absoluteFilePath)) {
+ || m_workingCopy.get(absoluteFilePath)) {
return true;
}
@@ -281,7 +281,7 @@ FilePath CppSourceProcessor::resolveFile_helper(const FilePath &filePath,
} else {
path = FilePath::fromString(headerPathsIt->path) / fileName;
}
- if (m_workingCopy.contains(path) || checkFile(path))
+ if (m_workingCopy.get(path) || checkFile(path))
return path;
}
}
@@ -468,8 +468,8 @@ void CppSourceProcessor::sourceNeeded(int line, const FilePath &filePath, Includ
document->setUtf8Source(preprocessedCode);
document->keepSourceAndAST();
document->tokenize();
- document->check(m_workingCopy.contains(document->filePath()) ? Document::FullCheck
- : Document::FastCheck);
+ document->check(m_workingCopy.get(document->filePath()) ? Document::FullCheck
+ : Document::FastCheck);
m_documentFinished(document);
diff --git a/src/plugins/cppeditor/cpptoolsjsextension.cpp b/src/plugins/cppeditor/cpptoolsjsextension.cpp
index a3cbfe1204..73a846593c 100644
--- a/src/plugins/cppeditor/cpptoolsjsextension.cpp
+++ b/src/plugins/cppeditor/cpptoolsjsextension.cpp
@@ -10,12 +10,13 @@
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/session.h>
#include <cplusplus/AST.h>
#include <cplusplus/ASTPath.h>
#include <cplusplus/Overview.h>
+
#include <utils/codegeneration.h>
#include <utils/fileutils.h>
@@ -134,13 +135,13 @@ bool CppToolsJsExtension::hasQObjectParent(const QString &klassName) const
// Find class in AST.
const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy();
- QByteArray source = workingCopy.source(item->filePath());
- if (source.isEmpty()) {
+ std::optional<QByteArray> source = workingCopy.source(item->filePath());
+ if (!source) {
const Utils::expected_str<QByteArray> contents = item->filePath().fileContents();
QTC_ASSERT_EXPECTED(contents, return false);
source = *contents;
}
- const auto doc = snapshot.preprocessedDocument(source, item->filePath());
+ const auto doc = snapshot.preprocessedDocument(*source, item->filePath());
if (!doc)
return false;
doc->check();
@@ -234,7 +235,7 @@ QString CppToolsJsExtension::includeStatement(
}
return false;
};
- for (const Project * const p : SessionManager::projects()) {
+ for (const Project * const p : ProjectManager::projects()) {
const Node *theNode = p->rootProjectNode()->findNode(nodeMatchesFileName);
if (theNode) {
const bool sameDir = pathOfIncludingFile == theNode->filePath().toFileInfo().path();
diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp
index 7d17e30e4b..8f68ed3ec9 100644
--- a/src/plugins/cppeditor/cpptoolsreuse.cpp
+++ b/src/plugins/cppeditor/cpptoolsreuse.cpp
@@ -21,7 +21,9 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
#include <coreplugin/messagemanager.h>
-#include <projectexplorer/session.h>
+
+#include <projectexplorer/projectmanager.h>
+
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/textdocument.h>
@@ -29,6 +31,7 @@
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
#include <cplusplus/SimpleLexer.h>
+
#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <utils/qtcassert.h>
@@ -263,10 +266,7 @@ const Macro *findCanonicalMacro(const QTextCursor &cursor, Document::Ptr documen
{
QTC_ASSERT(document, return nullptr);
- int line, column;
- Utils::Text::convertPosition(cursor.document(), cursor.position(), &line, &column);
-
- if (const Macro *macro = document->findMacroDefinitionAt(line)) {
+ if (const Macro *macro = document->findMacroDefinitionAt(cursor.blockNumber() + 1)) {
QTextCursor macroCursor = cursor;
const QByteArray name = identifierUnderCursor(&macroCursor).toUtf8();
if (macro->name() == name)
@@ -596,12 +596,12 @@ NamespaceAST *NSCheckerVisitor::currentNamespace()
ProjectExplorer::Project *projectForProjectPart(const ProjectPart &part)
{
- return ProjectExplorer::SessionManager::projectWithProjectFilePath(part.topLevelProject);
+ return ProjectExplorer::ProjectManager::projectWithProjectFilePath(part.topLevelProject);
}
ProjectExplorer::Project *projectForProjectInfo(const ProjectInfo &info)
{
- return ProjectExplorer::SessionManager::projectWithProjectFilePath(info.projectFilePath());
+ return ProjectExplorer::ProjectManager::projectWithProjectFilePath(info.projectFilePath());
}
void openEditor(const Utils::FilePath &filePath, bool inNextSplit, Utils::Id editorId)
diff --git a/src/plugins/cppeditor/cpptoolssettings.cpp b/src/plugins/cppeditor/cpptoolssettings.cpp
index 3d5b4658b8..0b5536fdd7 100644
--- a/src/plugins/cppeditor/cpptoolssettings.cpp
+++ b/src/plugins/cppeditor/cpptoolssettings.cpp
@@ -134,54 +134,6 @@ CppToolsSettings::CppToolsSettings()
// load global settings (after built-in settings are added to the pool)
d->m_globalCodeStyle->fromSettings(QLatin1String(Constants::CPP_SETTINGS_ID), s);
- // legacy handling start (Qt Creator Version < 2.4)
- const bool legacyTransformed =
- s->value(QLatin1String("CppCodeStyleSettings/LegacyTransformed"), false).toBool();
-
- if (!legacyTransformed) {
- // creator 2.4 didn't mark yet the transformation (first run of creator 2.4)
-
- // we need to transform the settings only if at least one from
- // below settings was already written - otherwise we use
- // defaults like it would be the first run of creator 2.4 without stored settings
- const QStringList groups = s->childGroups();
- const bool needTransform = groups.contains(QLatin1String("textTabPreferences")) ||
- groups.contains(QLatin1String("CppTabPreferences")) ||
- groups.contains(QLatin1String("CppCodeStyleSettings"));
- if (needTransform) {
- CppCodeStyleSettings legacyCodeStyleSettings;
- if (groups.contains(QLatin1String("CppCodeStyleSettings"))) {
- Utils::fromSettings(QLatin1String("CppCodeStyleSettings"),
- QString(), s, &legacyCodeStyleSettings);
- }
-
- const QString currentFallback = s->value(QLatin1String("CppTabPreferences/CurrentFallback")).toString();
- TabSettings legacyTabSettings;
- if (currentFallback == QLatin1String("CppGlobal")) {
- // no delegate, global overwritten
- Utils::fromSettings(QLatin1String("CppTabPreferences"),
- QString(), s, &legacyTabSettings);
- } else {
- // delegating to global
- legacyTabSettings = TextEditorSettings::codeStyle()->currentTabSettings();
- }
-
- // create custom code style out of old settings
- QVariant v;
- v.setValue(legacyCodeStyleSettings);
- ICodeStylePreferences *oldCreator = pool->createCodeStyle(
- "legacy", legacyTabSettings, v, Tr::tr("Old Creator"));
-
- // change the current delegate and save
- d->m_globalCodeStyle->setCurrentDelegate(oldCreator);
- d->m_globalCodeStyle->toSettings(QLatin1String(Constants::CPP_SETTINGS_ID), s);
- }
- // mark old settings as transformed
- s->setValue(QLatin1String("CppCodeStyleSettings/LegacyTransformed"), true);
- // legacy handling stop
- }
-
-
// mimetypes to be handled
TextEditorSettings::registerMimeTypeForLanguageId(Constants::C_SOURCE_MIMETYPE, Constants::CPP_SETTINGS_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Constants::C_HEADER_MIMETYPE, Constants::CPP_SETTINGS_ID);
diff --git a/src/plugins/cppeditor/cpptoolstestcase.cpp b/src/plugins/cppeditor/cpptoolstestcase.cpp
index b7124b01ca..706423c051 100644
--- a/src/plugins/cppeditor/cpptoolstestcase.cpp
+++ b/src/plugins/cppeditor/cpptoolstestcase.cpp
@@ -18,7 +18,7 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/texteditor.h>
#include <texteditor/codeassist/iassistproposal.h>
@@ -158,7 +158,7 @@ bool VerifyCleanCppModelManager::isClean(bool testOnlyForCleanedProjects)
if (!testOnlyForCleanedProjects) {
RETURN_FALSE_IF_NOT(mm->snapshot().isEmpty());
RETURN_FALSE_IF_NOT(mm->workingCopy().size() == 1);
- RETURN_FALSE_IF_NOT(mm->workingCopy().contains(mm->configurationFileName()));
+ RETURN_FALSE_IF_NOT(mm->workingCopy().get(mm->configurationFileName()));
}
return true;
}
@@ -348,8 +348,8 @@ bool TestCase::waitUntilProjectIsFullyOpened(Project *project, int timeOutInMs)
return QTest::qWaitFor(
[project]() {
- return SessionManager::startupBuildSystem()
- && !SessionManager::startupBuildSystem()->isParsing()
+ return ProjectManager::startupBuildSystem()
+ && !ProjectManager::startupBuildSystem()->isParsing()
&& CppModelManager::instance()->projectInfo(project);
},
timeOutInMs);
@@ -367,7 +367,7 @@ bool TestCase::writeFile(const FilePath &filePath, const QByteArray &contents)
ProjectOpenerAndCloser::ProjectOpenerAndCloser()
{
- QVERIFY(!SessionManager::hasProjects());
+ QVERIFY(!ProjectManager::hasProjects());
}
ProjectOpenerAndCloser::~ProjectOpenerAndCloser()
diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp
index c7a9e472ca..837375b6f3 100644
--- a/src/plugins/cppeditor/cpptypehierarchy.cpp
+++ b/src/plugins/cppeditor/cpptypehierarchy.cpp
@@ -154,8 +154,6 @@ CppTypeHierarchyWidget::CppTypeHierarchyWidget()
this, &CppTypeHierarchyWidget::perform);
connect(&m_futureWatcher, &QFutureWatcher<void>::finished,
this, &CppTypeHierarchyWidget::displayHierarchy);
-
- m_synchronizer.setCancelOnWait(true);
}
void CppTypeHierarchyWidget::perform()
@@ -183,7 +181,8 @@ void CppTypeHierarchyWidget::perform()
m_futureWatcher.setFuture(QFuture<void>(m_future));
m_synchronizer.addFuture(m_future);
- Core::ProgressManager::addTask(m_future, Tr::tr("Evaluating Type Hierarchy"), "TypeHierarchy");
+ Core::ProgressManager::addTimedTask(m_futureWatcher.future(),
+ Tr::tr("Evaluating Type Hierarchy"), "TypeHierarchy", 2);
}
void CppTypeHierarchyWidget::performFromExpression(const QString &expression, const FilePath &filePath)
diff --git a/src/plugins/cppeditor/cppworkingcopy.cpp b/src/plugins/cppeditor/cppworkingcopy.cpp
index b8922b7076..828388c96c 100644
--- a/src/plugins/cppeditor/cppworkingcopy.cpp
+++ b/src/plugins/cppeditor/cppworkingcopy.cpp
@@ -20,4 +20,18 @@ namespace CppEditor {
WorkingCopy::WorkingCopy() = default;
+std::optional<QByteArray> WorkingCopy::source(const Utils::FilePath &fileName) const
+{
+ if (const auto value = get(fileName))
+ return value->first;
+ return {};
+}
+
+std::optional<QPair<QByteArray, unsigned>> WorkingCopy::get(const Utils::FilePath &fileName) const
+{
+ const auto it = _elements.constFind(fileName);
+ if (it == _elements.constEnd())
+ return {};
+ return it.value();
+}
} // namespace CppEditor
diff --git a/src/plugins/cppeditor/cppworkingcopy.h b/src/plugins/cppeditor/cppworkingcopy.h
index ccc8258745..87a613bc7a 100644
--- a/src/plugins/cppeditor/cppworkingcopy.h
+++ b/src/plugins/cppeditor/cppworkingcopy.h
@@ -11,6 +11,8 @@
#include <QString>
#include <QPair>
+#include <optional>
+
namespace CppEditor {
class CPPEDITOR_EXPORT WorkingCopy
@@ -21,17 +23,12 @@ public:
void insert(const Utils::FilePath &fileName, const QByteArray &source, unsigned revision = 0)
{ _elements.insert(fileName, {source, revision}); }
- bool contains(const Utils::FilePath &fileName) const
- { return _elements.contains(fileName); }
-
- QByteArray source(const Utils::FilePath &fileName) const
- { return _elements.value(fileName).first; }
+ std::optional<QByteArray> source(const Utils::FilePath &fileName) const;
unsigned revision(const Utils::FilePath &fileName) const
{ return _elements.value(fileName).second; }
- QPair<QByteArray, unsigned> get(const Utils::FilePath &fileName) const
- { return _elements.value(fileName); }
+ std::optional<QPair<QByteArray, unsigned>> get(const Utils::FilePath &fileName) const;
using Table = QHash<Utils::FilePath, QPair<QByteArray, unsigned> >;
const Table &elements() const
diff --git a/src/plugins/cppeditor/doxygengenerator.cpp b/src/plugins/cppeditor/doxygengenerator.cpp
index 24cfba4abf..3d88ddd0f5 100644
--- a/src/plugins/cppeditor/doxygengenerator.cpp
+++ b/src/plugins/cppeditor/doxygengenerator.cpp
@@ -44,16 +44,6 @@ void DoxygenGenerator::setAddLeadingAsterisks(bool add)
m_addLeadingAsterisks = add;
}
-static int lineBeforeCursor(const QTextCursor &cursor)
-{
- int line, column;
- const bool converted = Utils::Text::convertPosition(cursor.document(), cursor.position(), &line,
- &column);
- QTC_ASSERT(converted, return std::numeric_limits<int>::max());
-
- return line - 1;
-}
-
QString DoxygenGenerator::generate(QTextCursor cursor,
const CPlusPlus::Snapshot &snapshot,
const Utils::FilePath &documentFilePath)
@@ -104,7 +94,7 @@ QString DoxygenGenerator::generate(QTextCursor cursor,
Document::Ptr doc = snapshot.preprocessedDocument(declCandidate.toUtf8(),
documentFilePath,
- lineBeforeCursor(initialCursor));
+ cursor.blockNumber());
doc->parse(Document::ParseDeclaration);
doc->check(Document::FastCheck);
diff --git a/src/plugins/cppeditor/editordocumenthandle.cpp b/src/plugins/cppeditor/editordocumenthandle.cpp
index 89b20f2ef3..fe1638f85a 100644
--- a/src/plugins/cppeditor/editordocumenthandle.cpp
+++ b/src/plugins/cppeditor/editordocumenthandle.cpp
@@ -14,11 +14,6 @@ namespace CppEditor {
CppEditorDocumentHandle::~CppEditorDocumentHandle() = default;
-SendDocumentTracker &CppEditorDocumentHandle::sendTracker()
-{
- return m_sendTracker;
-}
-
CppEditorDocumentHandle::RefreshReason CppEditorDocumentHandle::refreshReason() const
{
return m_refreshReason;
diff --git a/src/plugins/cppeditor/editordocumenthandle.h b/src/plugins/cppeditor/editordocumenthandle.h
index 28c60ddae9..ef9b8403b5 100644
--- a/src/plugins/cppeditor/editordocumenthandle.h
+++ b/src/plugins/cppeditor/editordocumenthandle.h
@@ -4,7 +4,6 @@
#pragma once
#include "cppeditor_global.h"
-#include "senddocumenttracker.h"
namespace Utils { class FilePath; }
@@ -35,10 +34,7 @@ public:
virtual void resetProcessor() = 0;
- SendDocumentTracker &sendTracker();
-
private:
- SendDocumentTracker m_sendTracker;
RefreshReason m_refreshReason = None;
};
diff --git a/src/plugins/cppeditor/fileandtokenactions_test.cpp b/src/plugins/cppeditor/fileandtokenactions_test.cpp
index b4b60d6828..2ad6b6b4ef 100644
--- a/src/plugins/cppeditor/fileandtokenactions_test.cpp
+++ b/src/plugins/cppeditor/fileandtokenactions_test.cpp
@@ -163,7 +163,7 @@ TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Acti
QCOMPARE(DocumentModel::openedDocuments().size(), 1);
QVERIFY(m_modelManager->isCppEditor(editor));
- QVERIFY(m_modelManager->workingCopy().contains(filePath));
+ QVERIFY(m_modelManager->workingCopy().get(filePath));
// Rehighlight
waitForRehighlightedSemanticDocument(editorWidget);
diff --git a/src/plugins/cppeditor/generatedcodemodelsupport.cpp b/src/plugins/cppeditor/generatedcodemodelsupport.cpp
index 400e77fb37..22a9b2735e 100644
--- a/src/plugins/cppeditor/generatedcodemodelsupport.cpp
+++ b/src/plugins/cppeditor/generatedcodemodelsupport.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/extracompiler.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
diff --git a/src/plugins/cppeditor/generatedcodemodelsupport.h b/src/plugins/cppeditor/generatedcodemodelsupport.h
index cd7591a3b0..d37177d991 100644
--- a/src/plugins/cppeditor/generatedcodemodelsupport.h
+++ b/src/plugins/cppeditor/generatedcodemodelsupport.h
@@ -20,7 +20,7 @@ public:
const Utils::FilePath &generatedFile);
~GeneratedCodeModelSupport() override;
- /// \returns the contents encoded in UTF-8.
+ /// Returns the contents encoded in UTF-8.
QByteArray contents() const override;
Utils::FilePath filePath() const override; // The generated file
Utils::FilePath sourceFilePath() const override;
diff --git a/src/plugins/cppeditor/indexitem.cpp b/src/plugins/cppeditor/indexitem.cpp
index 3094c9d494..879625263e 100644
--- a/src/plugins/cppeditor/indexitem.cpp
+++ b/src/plugins/cppeditor/indexitem.cpp
@@ -9,7 +9,8 @@ namespace CppEditor {
IndexItem::Ptr IndexItem::create(const QString &symbolName, const QString &symbolType,
const QString &symbolScope, IndexItem::ItemType type,
- const QString &fileName, int line, int column, const QIcon &icon)
+ const QString &fileName, int line, int column, const QIcon &icon,
+ bool isFunctionDefinition)
{
Ptr ptr(new IndexItem);
@@ -21,6 +22,7 @@ IndexItem::Ptr IndexItem::create(const QString &symbolName, const QString &symbo
ptr->m_line = line;
ptr->m_column = column;
ptr->m_icon = icon;
+ ptr->m_isFuncDef = isFunctionDefinition;
return ptr;
}
diff --git a/src/plugins/cppeditor/indexitem.h b/src/plugins/cppeditor/indexitem.h
index eda023394d..135bd60adc 100644
--- a/src/plugins/cppeditor/indexitem.h
+++ b/src/plugins/cppeditor/indexitem.h
@@ -40,7 +40,8 @@ public:
const QString &fileName,
int line,
int column,
- const QIcon &icon);
+ const QIcon &icon,
+ bool isFunctionDefinition);
static Ptr create(const QString &fileName, int sizeHint);
QString scopedSymbolName() const
@@ -64,6 +65,7 @@ public:
ItemType type() const { return m_type; }
int line() const { return m_line; }
int column() const { return m_column; }
+ bool isFunctionDefinition() const { return m_isFuncDef; }
void addChild(IndexItem::Ptr childItem) { m_children.append(childItem); }
void squeeze();
@@ -106,6 +108,7 @@ private:
ItemType m_type = All;
int m_line = 0;
int m_column = 0;
+ bool m_isFuncDef = false;
QVector<IndexItem::Ptr> m_children;
};
diff --git a/src/plugins/cppeditor/insertionpointlocator.h b/src/plugins/cppeditor/insertionpointlocator.h
index c4091d5f9d..d6fd5d735c 100644
--- a/src/plugins/cppeditor/insertionpointlocator.h
+++ b/src/plugins/cppeditor/insertionpointlocator.h
@@ -23,27 +23,21 @@ public:
InsertionLocation(const Utils::FilePath &filePath, const QString &prefix,
const QString &suffix, int line, int column);
- const Utils::FilePath &filePath() const
- { return m_filePath; }
+ const Utils::FilePath &filePath() const { return m_filePath; }
- /// \returns The prefix to insert before any other text.
- QString prefix() const
- { return m_prefix; }
+ /// Returns the prefix to insert before any other text.
+ QString prefix() const { return m_prefix; }
- /// \returns The suffix to insert after the other inserted text.
- QString suffix() const
- { return m_suffix; }
+ /// Returns the suffix to insert after the other inserted text.
+ QString suffix() const { return m_suffix; }
- /// \returns The line where to insert. The line number is 1-based.
- int line() const
- { return m_line; }
+ /// Returns the line where to insert. The line number is 1-based.
+ int line() const { return m_line; }
- /// \returns The column where to insert. The column number is 1-based.
- int column() const
- { return m_column; }
+ /// Returns the column where to insert. The column number is 1-based.
+ int column() const { return m_column; }
- bool isValid() const
- { return !m_filePath.isEmpty() && m_line > 0 && m_column > 0; }
+ bool isValid() const { return !m_filePath.isEmpty() && m_line > 0 && m_column > 0; }
private:
Utils::FilePath m_filePath;
diff --git a/src/plugins/cppeditor/modelmanagertesthelper.cpp b/src/plugins/cppeditor/modelmanagertesthelper.cpp
index f4114c33b2..a133ac7fef 100644
--- a/src/plugins/cppeditor/modelmanagertesthelper.cpp
+++ b/src/plugins/cppeditor/modelmanagertesthelper.cpp
@@ -6,7 +6,7 @@
#include "cpptoolstestcase.h"
#include "projectinfo.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
@@ -59,7 +59,7 @@ void ModelManagerTestHelper::cleanup()
CppModelManager *mm = CppModelManager::instance();
QList<ProjectInfo::ConstPtr> pies = mm->projectInfos();
for (Project * const p : std::as_const(m_projects)) {
- ProjectExplorer::SessionManager::removeProject(p);
+ ProjectExplorer::ProjectManager::removeProject(p);
emit aboutToRemoveProject(p);
}
@@ -72,7 +72,7 @@ ModelManagerTestHelper::Project *ModelManagerTestHelper::createProject(
{
auto tp = new TestProject(name, this, filePath);
m_projects.push_back(tp);
- ProjectExplorer::SessionManager::addProject(tp);
+ ProjectExplorer::ProjectManager::addProject(tp);
emit projectAdded(tp);
return tp;
}
diff --git a/src/plugins/cppeditor/projectinfo_test.cpp b/src/plugins/cppeditor/projectinfo_test.cpp
index 73254647f5..88115e2ea4 100644
--- a/src/plugins/cppeditor/projectinfo_test.cpp
+++ b/src/plugins/cppeditor/projectinfo_test.cpp
@@ -362,12 +362,12 @@ public:
ProjectInfo::ConstPtr generate()
{
- QFutureInterface<ProjectInfo::ConstPtr> fi;
+ QPromise<ProjectInfo::ConstPtr> promise;
projectUpdateInfo.rawProjectParts += rawProjectPart;
- ProjectInfoGenerator generator(fi, projectUpdateInfo);
+ ProjectInfoGenerator generator(projectUpdateInfo);
- return generator.generate();
+ return generator.generate(promise);
}
ProjectUpdateInfo projectUpdateInfo;
diff --git a/src/plugins/cppeditor/searchsymbols.cpp b/src/plugins/cppeditor/searchsymbols.cpp
index 22e0c28197..2aea7f8e1b 100644
--- a/src/plugins/cppeditor/searchsymbols.cpp
+++ b/src/plugins/cppeditor/searchsymbols.cpp
@@ -285,7 +285,8 @@ IndexItem::Ptr SearchSymbols::addChildItem(const QString &symbolName, const QStr
StringTable::insert(path),
symbol->line(),
symbol->column() - 1, // 1-based vs 0-based column
- icon);
+ icon,
+ symbol->asFunction());
_parent->addChild(newItem);
return newItem;
}
diff --git a/src/plugins/cppeditor/senddocumenttracker.cpp b/src/plugins/cppeditor/senddocumenttracker.cpp
deleted file mode 100644
index f7044a65a2..0000000000
--- a/src/plugins/cppeditor/senddocumenttracker.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "senddocumenttracker.h"
-
-#include <algorithm>
-
-#ifdef WITH_TESTS
-#include <QtTest>
-#endif
-
-namespace CppEditor {
-
-void SendDocumentTracker::setLastSentRevision(int revision)
-{
- m_lastSentRevision = revision;
- m_contentChangeStartPosition = std::numeric_limits<int>::max();
-}
-
-int SendDocumentTracker::lastSentRevision() const
-{
- return m_lastSentRevision;
-}
-
-void SendDocumentTracker::setLastCompletionPosition(int lastCompletionPosition)
-{
- m_lastCompletionPosition = lastCompletionPosition;
-}
-
-int SendDocumentTracker::lastCompletionPosition() const
-{
- return m_lastCompletionPosition;
-}
-
-void SendDocumentTracker::applyContentChange(int startPosition)
-{
- if (startPosition < m_lastCompletionPosition)
- m_lastCompletionPosition = -1;
-
- m_contentChangeStartPosition = std::min(startPosition, m_contentChangeStartPosition);
-}
-
-bool SendDocumentTracker::shouldSendCompletion(int newCompletionPosition) const
-{
- return m_lastCompletionPosition != newCompletionPosition;
-}
-
-bool SendDocumentTracker::shouldSendRevision(uint newRevision) const
-{
- return m_lastSentRevision != int(newRevision);
-}
-
-bool SendDocumentTracker::shouldSendRevisionWithCompletionPosition(int newRevision, int newCompletionPosition) const
-{
- if (shouldSendRevision(newRevision))
- return changedBeforeCompletionPosition(newCompletionPosition);
-
- return false;
-}
-
-bool SendDocumentTracker::changedBeforeCompletionPosition(int newCompletionPosition) const
-{
- return m_contentChangeStartPosition < newCompletionPosition;
-}
-
-#ifdef WITH_TESTS
-namespace Internal {
-
-void DocumentTrackerTest::testDefaultLastSentRevision()
-{
- SendDocumentTracker tracker;
-
- QCOMPARE(tracker.lastSentRevision(), -1);
- QCOMPARE(tracker.lastCompletionPosition(), -1);
-}
-
-void DocumentTrackerTest::testSetRevision()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
-
- QCOMPARE(tracker.lastSentRevision(), 46);
- QCOMPARE(tracker.lastCompletionPosition(), -1);
-}
-
-void DocumentTrackerTest::testSetLastCompletionPosition()
-{
- SendDocumentTracker tracker;
- tracker.setLastCompletionPosition(33);
-
- QCOMPARE(tracker.lastSentRevision(), -1);
- QCOMPARE(tracker.lastCompletionPosition(), 33);
-}
-
-void DocumentTrackerTest::testApplyContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(10);
-
- QCOMPARE(tracker.lastSentRevision(), 46);
- QCOMPARE(tracker.lastCompletionPosition(), -1);
-}
-
-void DocumentTrackerTest::testDontSendCompletionIfPositionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendCompletion(33));
-}
-
-void DocumentTrackerTest::testSendCompletionIfPositionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(tracker.shouldSendCompletion(22));
-}
-
-void DocumentTrackerTest::testSendCompletionIfChangeIsBeforeCompletionPositionAndPositionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(10);
-
- QVERIFY(tracker.shouldSendCompletion(33));
-}
-
-void DocumentTrackerTest::testDontSendCompletionIfChangeIsAfterCompletionPositionAndPositionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(40);
-
- QVERIFY(!tracker.shouldSendCompletion(33));
-}
-
-void DocumentTrackerTest::testDontSendRevisionIfRevisionIsEqual()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
-
- QVERIFY(!tracker.shouldSendRevision(46));
-}
-
-void DocumentTrackerTest::testSendRevisionIfRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
-
- QVERIFY(tracker.shouldSendRevision(21));
-}
-
-void DocumentTrackerTest::testDontSendRevisionWithDefaults()
-{
- SendDocumentTracker tracker;
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 33));
-}
-
-void DocumentTrackerTest::testDontSendIfRevisionIsDifferentAndCompletionPositionIsEqualAndNoContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 33));
-}
-
-void DocumentTrackerTest::testDontSendIfRevisionIsDifferentAndCompletionPositionIsDifferentAndNoContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 44));
-}
-
-void DocumentTrackerTest::testDontSendIfRevisionIsEqualAndCompletionPositionIsDifferentAndNoContentChange()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(46,44));
-}
-
-void DocumentTrackerTest::testSendIfChangeIsBeforeCompletionAndPositionIsEqualAndRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(33);
- tracker.applyContentChange(10);
-
- QVERIFY(tracker.shouldSendRevisionWithCompletionPosition(45, 33));
-}
-
-void DocumentTrackerTest::testDontSendIfChangeIsAfterCompletionPositionAndRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(50);
- tracker.applyContentChange(40);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(45, 36));
-}
-
-void DocumentTrackerTest::testSendIfChangeIsBeforeCompletionPositionAndRevisionIsDifferent()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(50);
- tracker.applyContentChange(30);
-
- QVERIFY(tracker.shouldSendRevisionWithCompletionPosition(45, 36));
-}
-
-void DocumentTrackerTest::testResetChangedContentStartPositionIfLastRevisionIsSet()
-{
- SendDocumentTracker tracker;
- tracker.setLastSentRevision(46);
- tracker.setLastCompletionPosition(50);
- tracker.applyContentChange(30);
- tracker.setLastSentRevision(47);
-
- QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(45, 36));
-}
-
-} // namespace Internal
-#endif
-
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/senddocumenttracker.h b/src/plugins/cppeditor/senddocumenttracker.h
deleted file mode 100644
index f2736efdd9..0000000000
--- a/src/plugins/cppeditor/senddocumenttracker.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "cppeditor_global.h"
-
-#include <QObject>
-
-#include <limits>
-
-namespace CppEditor {
-
-class CPPEDITOR_EXPORT SendDocumentTracker
-{
-public:
- void setLastSentRevision(int lastSentRevision);
- int lastSentRevision() const;
-
- void setLastCompletionPosition(int lastCompletionPosition);
- int lastCompletionPosition() const;
-
- void applyContentChange(int startPosition);
-
- bool shouldSendCompletion(int newCompletionPosition) const;
- bool shouldSendRevision(uint newRevision) const;
- bool shouldSendRevisionWithCompletionPosition(int newRevision, int newCompletionPosition) const;
-
-private:
- bool changedBeforeCompletionPosition(int newCompletionPosition) const;
-
-private:
- int m_lastSentRevision = -1;
- int m_lastCompletionPosition = -1;
- int m_contentChangeStartPosition = std::numeric_limits<int>::max();
-};
-
-#ifdef WITH_TESTS
-namespace Internal {
-class DocumentTrackerTest : public QObject
-{
- Q_OBJECT
-
-private slots:
- void testDefaultLastSentRevision();
- void testSetRevision();
- void testSetLastCompletionPosition();
- void testApplyContentChange();
- void testDontSendCompletionIfPositionIsEqual();
- void testSendCompletionIfPositionIsDifferent();
- void testSendCompletionIfChangeIsBeforeCompletionPositionAndPositionIsEqual();
- void testDontSendCompletionIfChangeIsAfterCompletionPositionAndPositionIsEqual();
- void testDontSendRevisionIfRevisionIsEqual();
- void testSendRevisionIfRevisionIsDifferent();
- void testDontSendRevisionWithDefaults();
- void testDontSendIfRevisionIsDifferentAndCompletionPositionIsEqualAndNoContentChange();
- void testDontSendIfRevisionIsDifferentAndCompletionPositionIsDifferentAndNoContentChange();
- void testDontSendIfRevisionIsEqualAndCompletionPositionIsDifferentAndNoContentChange();
- void testSendIfChangeIsBeforeCompletionAndPositionIsEqualAndRevisionIsDifferent();
- void testDontSendIfChangeIsAfterCompletionPositionAndRevisionIsDifferent();
- void testSendIfChangeIsBeforeCompletionPositionAndRevisionIsDifferent();
- void testResetChangedContentStartPositionIfLastRevisionIsSet();
-};
-} // namespace Internal
-#endif // WITH_TESTS
-
-} // namespace CppEditor
diff --git a/src/plugins/cppeditor/symbolsearcher_test.cpp b/src/plugins/cppeditor/symbolsearcher_test.cpp
index 9f700d9972..688df3d924 100644
--- a/src/plugins/cppeditor/symbolsearcher_test.cpp
+++ b/src/plugins/cppeditor/symbolsearcher_test.cpp
@@ -4,13 +4,13 @@
#include "symbolsearcher_test.h"
#include "cppindexingsupport.h"
-#include "cppmodelmanager.h"
#include "cpptoolstestcase.h"
#include "searchsymbols.h"
#include <coreplugin/testdatadir.h>
#include <coreplugin/find/searchresultwindow.h>
-#include <utils/runextensions.h>
+
+#include <utils/async.h>
#include <QtTest>
@@ -35,10 +35,10 @@ public:
return m_symbolName == other.m_symbolName && m_scope == other.m_scope;
}
- static ResultDataList fromSearchResultList(const QList<Core::SearchResultItem> &entries)
+ static ResultDataList fromSearchResultList(const Utils::SearchResultItems &entries)
{
ResultDataList result;
- for (const Core::SearchResultItem &entry : entries)
+ for (const Utils::SearchResultItem &entry : entries)
result << ResultData(entry.lineText(), entry.path().join(QLatin1String("::")));
return result;
}
@@ -77,8 +77,8 @@ public:
const QScopedPointer<SymbolSearcher> symbolSearcher(
new SymbolSearcher(searchParameters, QSet<QString>{testFile}));
- QFuture<Core::SearchResultItem> search
- = Utils::runAsync(&SymbolSearcher::runSearch, symbolSearcher.data());
+ QFuture<Utils::SearchResultItem> search
+ = Utils::asyncRun(&SymbolSearcher::runSearch, symbolSearcher.data());
search.waitForFinished();
ResultDataList results = ResultData::fromSearchResultList(search.results());
QCOMPARE(results, expectedResults);
diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp
index 3b901715b2..eb6a4c81da 100644
--- a/src/plugins/cppeditor/symbolsfindfilter.cpp
+++ b/src/plugins/cppeditor/symbolsfindfilter.cpp
@@ -12,18 +12,19 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/searchresultwindow.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <QSet>
+#include <QButtonGroup>
#include <QGridLayout>
#include <QLabel>
-#include <QButtonGroup>
+#include <QSet>
using namespace Core;
using namespace Utils;
@@ -108,7 +109,7 @@ void SymbolsFindFilter::startSearch(SearchResult *search)
SymbolSearcher::Parameters parameters = search->userData().value<SymbolSearcher::Parameters>();
QSet<QString> projectFileNames;
if (parameters.scope == SymbolSearcher::SearchProjectsOnly) {
- for (ProjectExplorer::Project *project : ProjectExplorer::SessionManager::projects())
+ for (ProjectExplorer::Project *project : ProjectExplorer::ProjectManager::projects())
projectFileNames += Utils::transform<QSet>(project->files(ProjectExplorer::Project::AllFiles), &Utils::FilePath::toString);
}
@@ -120,7 +121,7 @@ void SymbolsFindFilter::startSearch(SearchResult *search)
SymbolSearcher *symbolSearcher = new SymbolSearcher(parameters, projectFileNames);
connect(watcher, &QFutureWatcherBase::finished,
symbolSearcher, &QObject::deleteLater);
- watcher->setFuture(Utils::runAsync(m_manager->sharedThreadPool(),
+ watcher->setFuture(Utils::asyncRun(m_manager->sharedThreadPool(),
&SymbolSearcher::runSearch, symbolSearcher));
FutureProgress *progress = ProgressManager::addTask(watcher->future(), Tr::tr("Searching for Symbol"),
Core::Constants::TASK_SEARCH);
@@ -135,7 +136,7 @@ void SymbolsFindFilter::addResults(QFutureWatcher<SearchResultItem> *watcher, in
watcher->cancel();
return;
}
- QList<SearchResultItem> items;
+ SearchResultItems items;
for (int i = begin; i < end; ++i)
items << watcher->resultAt(i);
search->addResults(items, SearchResult::AddSorted);
diff --git a/src/plugins/cppeditor/symbolsfindfilter.h b/src/plugins/cppeditor/symbolsfindfilter.h
index 61866aae4d..a77f587360 100644
--- a/src/plugins/cppeditor/symbolsfindfilter.h
+++ b/src/plugins/cppeditor/symbolsfindfilter.h
@@ -6,7 +6,6 @@
#include "searchsymbols.h"
#include <coreplugin/find/ifindfilter.h>
-#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/find/searchresultwindow.h>
#include <QFutureWatcher>
@@ -16,6 +15,7 @@
#include <QRadioButton>
namespace Core { class SearchResult; }
+namespace Utils { class SearchResultItem; }
namespace CppEditor {
class CppModelManager;
@@ -52,10 +52,10 @@ signals:
void symbolsToSearchChanged();
private:
- void openEditor(const Core::SearchResultItem &item);
+ void openEditor(const Utils::SearchResultItem &item);
- void addResults(QFutureWatcher<Core::SearchResultItem> *watcher, int begin, int end);
- void finish(QFutureWatcher<Core::SearchResultItem> *watcher);
+ void addResults(QFutureWatcher<Utils::SearchResultItem> *watcher, int begin, int end);
+ void finish(QFutureWatcher<Utils::SearchResultItem> *watcher);
void cancel(Core::SearchResult *search);
void setPaused(Core::SearchResult *search, bool paused);
void onTaskStarted(Utils::Id type);
@@ -67,7 +67,7 @@ private:
CppModelManager *m_manager;
bool m_enabled;
- QMap<QFutureWatcher<Core::SearchResultItem> *, QPointer<Core::SearchResult> > m_watchers;
+ QMap<QFutureWatcher<Utils::SearchResultItem> *, QPointer<Core::SearchResult> > m_watchers;
QPointer<Core::SearchResult> m_currentSearch;
SearchSymbols::SymbolTypes m_symbolsToSearch;
SearchScope m_scope;
diff --git a/src/plugins/cppeditor/testcases/highlightingtestcase.cpp b/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
index 20fa252646..dd1f9c1b3e 100644
--- a/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
+++ b/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
@@ -25,3 +25,15 @@ template<int n = 5> class C;
struct ConversionFunction {
operator int();
};
+
+template<typename T> concept NoConstraint = true;
+
+const char16_t *operator ""_w(const char16_t *s, size_t) { return s; }
+const auto s = u"one"_w;
+const auto s2 = L"hello";
+const auto s3 = u8"hello";
+const auto s4 = U"hello";
+const auto s5 = uR"("o
+ ne")"_w;
+const auto s6 = u"o\
+ne"_w;
diff --git a/src/plugins/cppeditor/typehierarchybuilder.cpp b/src/plugins/cppeditor/typehierarchybuilder.cpp
index 5f3aae00e7..889d0eacfc 100644
--- a/src/plugins/cppeditor/typehierarchybuilder.cpp
+++ b/src/plugins/cppeditor/typehierarchybuilder.cpp
@@ -108,20 +108,12 @@ const QList<TypeHierarchy> &TypeHierarchy::hierarchy() const
}
TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(Symbol *symbol,
- const Snapshot &snapshot)
-{
- QFutureInterfaceBase dummy;
- return TypeHierarchyBuilder::buildDerivedTypeHierarchy(dummy, symbol, snapshot);
-}
-
-TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface,
- Symbol *symbol,
- const Snapshot &snapshot)
+ const Snapshot &snapshot, const std::optional<QFuture<void>> &future)
{
TypeHierarchy hierarchy(symbol);
TypeHierarchyBuilder builder;
QHash<QString, QHash<QString, QString>> cache;
- builder.buildDerived(futureInterface, &hierarchy, snapshot, cache);
+ builder.buildDerived(future, &hierarchy, snapshot, cache);
return hierarchy;
}
@@ -172,11 +164,10 @@ static FilePaths filesDependingOn(const Snapshot &snapshot, Symbol *symbol)
return FilePaths{file} + snapshot.filesDependingOn(file);
}
-void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
+void TypeHierarchyBuilder::buildDerived(const std::optional<QFuture<void>> &future,
TypeHierarchy *typeHierarchy,
const Snapshot &snapshot,
- QHash<QString, QHash<QString, QString>> &cache,
- int depth)
+ QHash<QString, QHash<QString, QString>> &cache)
{
Symbol *symbol = typeHierarchy->_symbol;
if (_visited.contains(symbol))
@@ -188,15 +179,10 @@ void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
DerivedHierarchyVisitor visitor(symbolName, cache);
const FilePaths dependingFiles = filesDependingOn(snapshot, symbol);
- if (depth == 0)
- futureInterface.setProgressRange(0, dependingFiles.size());
- int i = -1;
for (const FilePath &fileName : dependingFiles) {
- if (futureInterface.isCanceled())
+ if (future && future->isCanceled())
return;
- if (depth == 0)
- futureInterface.setProgressValue(++i);
Document::Ptr doc = snapshot.document(fileName);
if ((_candidates.contains(fileName) && !_candidates.value(fileName).contains(symbolName))
|| !doc->control()->findIdentifier(symbol->identifier()->chars(),
@@ -210,8 +196,8 @@ void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
const QList<Symbol *> &derived = visitor.derived();
for (Symbol *s : derived) {
TypeHierarchy derivedHierarchy(s);
- buildDerived(futureInterface, &derivedHierarchy, snapshot, cache, depth + 1);
- if (futureInterface.isCanceled())
+ buildDerived(future, &derivedHierarchy, snapshot, cache);
+ if (future && future->isCanceled())
return;
typeHierarchy->_hierarchy.append(derivedHierarchy);
}
diff --git a/src/plugins/cppeditor/typehierarchybuilder.h b/src/plugins/cppeditor/typehierarchybuilder.h
index 07556126dd..c6ee8a56bf 100644
--- a/src/plugins/cppeditor/typehierarchybuilder.h
+++ b/src/plugins/cppeditor/typehierarchybuilder.h
@@ -6,7 +6,7 @@
#include <cplusplus/CppDocument.h>
#include <cplusplus/Overview.h>
-#include <QFutureInterface>
+#include <QFuture>
#include <QList>
#include <QSet>
@@ -44,19 +44,17 @@ class TypeHierarchyBuilder
{
public:
static TypeHierarchy buildDerivedTypeHierarchy(CPlusPlus::Symbol *symbol,
- const CPlusPlus::Snapshot &snapshot);
- static TypeHierarchy buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface,
- CPlusPlus::Symbol *symbol,
- const CPlusPlus::Snapshot &snapshot);
+ const CPlusPlus::Snapshot &snapshot,
+ const std::optional<QFuture<void>> &future = {});
static CPlusPlus::LookupItem followTypedef(const CPlusPlus::LookupContext &context,
const CPlusPlus::Name *symbolName,
CPlusPlus::Scope *enclosingScope,
std::set<const CPlusPlus::Symbol *> typedefs = {});
private:
TypeHierarchyBuilder() = default;
- void buildDerived(QFutureInterfaceBase &futureInterface, TypeHierarchy *typeHierarchy,
+ void buildDerived(const std::optional<QFuture<void>> &future, TypeHierarchy *typeHierarchy,
const CPlusPlus::Snapshot &snapshot,
- QHash<QString, QHash<QString, QString> > &cache, int depth = 0);
+ QHash<QString, QHash<QString, QString> > &cache);
QSet<CPlusPlus::Symbol *> _visited;
QHash<Utils::FilePath, QSet<QString> > _candidates;
diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp
index 0fbc5cac65..05cf55d729 100644
--- a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp
+++ b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp
@@ -15,6 +15,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <debugger/analyzer/analyzerconstants.h>
+
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -64,7 +66,7 @@ CtfVisualizerTool::CtfVisualizerTool()
m_restrictToThreadsButton->setIcon(Utils::Icons::FILTER.icon());
m_restrictToThreadsButton->setToolTip(Tr::tr("Restrict to Threads"));
m_restrictToThreadsButton->setPopupMode(QToolButton::InstantPopup);
- m_restrictToThreadsButton->setProperty("noArrow", true);
+ m_restrictToThreadsButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
m_restrictToThreadsButton->setMenu(m_restrictToThreadsMenu);
connect(m_restrictToThreadsMenu, &QMenu::triggered,
this, &CtfVisualizerTool::toggleThreadRestriction);
diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp
index 85e98c1e4c..d9980328fc 100644
--- a/src/plugins/cvs/cvsplugin.cpp
+++ b/src/plugins/cvs/cvsplugin.cpp
@@ -137,34 +137,28 @@ static inline bool messageBoxQuestion(const QString &title, const QString &quest
class CvsDiffConfig : public VcsBaseEditorConfig
{
public:
- CvsDiffConfig(CvsSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar),
- m_settings(settings)
+ CvsDiffConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("-w", Tr::tr("Ignore Whitespace")),
- &settings.diffIgnoreWhiteSpace);
+ &settings().diffIgnoreWhiteSpace);
mapSetting(addToggleButton("-B", Tr::tr("Ignore Blank Lines")),
- &settings.diffIgnoreBlankLines);
+ &settings().diffIgnoreBlankLines);
}
QStringList arguments() const override
{
- return m_settings.diffOptions.value().split(' ', Qt::SkipEmptyParts)
+ return settings().diffOptions.value().split(' ', Qt::SkipEmptyParts)
+ VcsBaseEditorConfig::arguments();
}
-
-private:
- CvsSettings &m_settings;
};
class CvsClient : public VcsBaseClient
{
public:
- explicit CvsClient(CvsSettings *settings) : VcsBaseClient(settings)
+ explicit CvsClient() : VcsBaseClient(&Internal::settings())
{
- setDiffConfigCreator([settings](QToolBar *toolBar) {
- return new CvsDiffConfig(*settings, toolBar);
- });
+ setDiffConfigCreator([](QToolBar *toolBar) { return new CvsDiffConfig(toolBar); });
}
ExitCodeInterpreter exitCodeInterpreter(VcsCommandTag cmd) const override
@@ -294,7 +288,7 @@ private:
bool commit(const QString &messageFile, const QStringList &subVersionFileList);
void cleanCommitMessageFile();
- CvsSettings m_settings;
+ CvsSettings m_setting;
CvsClient *m_client = nullptr;
QString m_commitMessageFileName;
@@ -327,8 +321,6 @@ private:
QAction *m_menuAction = nullptr;
- CvsSettingsPage m_settingsPage{&m_settings};
-
public:
VcsSubmitEditorFactory submitEditorFactory {
submitParameters,
@@ -374,7 +366,7 @@ bool CvsPluginPrivate::isVcsFileOrDirectory(const Utils::FilePath &filePath) con
bool CvsPluginPrivate::isConfigured() const
{
- const Utils::FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
if (binary.isEmpty())
return false;
QFileInfo fi = binary.toFileInfo();
@@ -447,7 +439,7 @@ VcsCommand *CvsPluginPrivate::createInitialCheckoutCommand(const QString &url,
auto command = VcsBaseClient::createVcsCommand(baseDirectory, Environment::systemEnvironment());
command->setDisplayName(Tr::tr("CVS Checkout"));
- command->addJob({m_settings.binaryPath.filePath(), m_settings.addOptions(args)}, -1);
+ command->addJob({settings().binaryPath(), settings().addOptions(args)}, -1);
return command;
}
@@ -497,7 +489,7 @@ CvsPluginPrivate::CvsPluginPrivate()
dd = this;
Context context(CVS_CONTEXT);
- m_client = new CvsClient(&m_settings);
+ m_client = new CvsClient;
const QString prefix = QLatin1String("cvs");
m_commandLocator = new CommandLocator("CVS", prefix, prefix, this);
@@ -692,7 +684,7 @@ CvsPluginPrivate::CvsPluginPrivate()
cvsMenu->addAction(command);
m_commandLocator->appendCommand(command);
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
void CvsPluginPrivate::vcsDescribe(const FilePath &source, const QString &changeNr)
@@ -1230,7 +1222,7 @@ bool CvsPluginPrivate::describe(const FilePath &toplevel, const QString &file,
*errorMessage = Tr::tr("Parsing of the log output failed.");
return false;
}
- if (m_settings.describeByCommitId.value()) {
+ if (settings().describeByCommitId()) {
// Run a log command over the repo, filtering by the commit date
// and commit id, collecting all files touched by the commit.
const QString commitId = fileLog.front().revisions.front().commitId;
@@ -1286,7 +1278,7 @@ bool CvsPluginPrivate::describe(const FilePath &repositoryPath,
for (QList<CvsLogEntry>::iterator it = entries.begin(); it != lend; ++it) {
const QString &revision = it->revisions.front().revision;
if (!isFirstRevision(revision)) {
- const QStringList args{"diff", m_settings.diffOptions.value(),
+ const QStringList args{"diff", settings().diffOptions(),
"-r", previousRevision(revision),
"-r", it->revisions.front().revision, it->file};
const auto diffResponse = runCvs(repositoryPath, args, RunFlags::None, codec);
@@ -1329,13 +1321,13 @@ CommandResult CvsPluginPrivate::runCvs(const FilePath &workingDirectory,
const QStringList &arguments, RunFlags flags,
QTextCodec *outputCodec, int timeoutMultiplier) const
{
- const FilePath executable = m_settings.binaryPath.filePath();
+ const FilePath executable = settings().binaryPath();
if (executable.isEmpty())
return CommandResult(ProcessResult::StartFailed, Tr::tr("No CVS executable specified."));
- const int timeoutS = m_settings.timeout.value() * timeoutMultiplier;
+ const int timeoutS = settings().timeout() * timeoutMultiplier;
return m_client->vcsSynchronousExec(workingDirectory,
- {executable, m_settings.addOptions(arguments)},
+ {executable, settings().addOptions(arguments)},
flags, timeoutS, outputCodec);
}
diff --git a/src/plugins/cvs/cvssettings.cpp b/src/plugins/cvs/cvssettings.cpp
index 917db71921..dbfd41e500 100644
--- a/src/plugins/cvs/cvssettings.cpp
+++ b/src/plugins/cvs/cvssettings.cpp
@@ -17,15 +17,24 @@ using namespace Utils;
namespace Cvs::Internal {
-// CvsSettings
+static CvsSettings *theSettings;
+
+CvsSettings &settings()
+{
+ return *theSettings;
+}
CvsSettings::CvsSettings()
{
+ theSettings = this;
setSettingsGroup("CVS");
+ setId(VcsBase::Constants::VCS_ID_CVS);
+ setDisplayName(Tr::tr("CVS"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+
registerAspect(&binaryPath);
binaryPath.setDefaultValue("cvs" QTC_HOST_EXE_SUFFIX);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setHistoryCompleter(QLatin1String("Cvs.Command.History"));
binaryPath.setDisplayName(Tr::tr("CVS Command"));
@@ -55,48 +64,25 @@ CvsSettings::CvsSettings()
registerAspect(&diffIgnoreBlankLines);
diffIgnoreBlankLines.setSettingsKey("DiffIgnoreBlankLines");
-}
-
-QStringList CvsSettings::addOptions(const QStringList &args) const
-{
- const QString cvsRoot = this->cvsRoot.value();
- if (cvsRoot.isEmpty())
- return args;
-
- QStringList rc;
- rc.push_back(QLatin1String("-d"));
- rc.push_back(cvsRoot);
- rc.append(args);
- return rc;
-}
-
-CvsSettingsPage::CvsSettingsPage(CvsSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_CVS);
- setDisplayName(Tr::tr("CVS"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- CvsSettings &s = *settings;
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
-
Column {
Group {
title(Tr::tr("Configuration")),
Form {
- s.binaryPath,
- s.cvsRoot
+ binaryPath, br,
+ cvsRoot
}
},
Group {
title(Tr::tr("Miscellaneous")),
Column {
Form {
- s.timeout,
- s.diffOptions,
+ timeout, br,
+ diffOptions,
},
- s.describeByCommitId,
+ describeByCommitId,
}
},
st
@@ -104,4 +90,17 @@ CvsSettingsPage::CvsSettingsPage(CvsSettings *settings)
});
}
+QStringList CvsSettings::addOptions(const QStringList &args) const
+{
+ const QString cvsRoot = this->cvsRoot.value();
+ if (cvsRoot.isEmpty())
+ return args;
+
+ QStringList rc;
+ rc.push_back(QLatin1String("-d"));
+ rc.push_back(cvsRoot);
+ rc.append(args);
+ return rc;
+}
+
} // Cvs::Internal
diff --git a/src/plugins/cvs/cvssettings.h b/src/plugins/cvs/cvssettings.h
index ac52ce9402..0c2e5442a6 100644
--- a/src/plugins/cvs/cvssettings.h
+++ b/src/plugins/cvs/cvssettings.h
@@ -3,8 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
-
#include <vcsbase/vcsbaseclientsettings.h>
namespace Cvs::Internal {
@@ -12,21 +10,17 @@ namespace Cvs::Internal {
class CvsSettings : public VcsBase::VcsBaseSettings
{
public:
+ CvsSettings();
+
Utils::StringAspect cvsRoot;
Utils::StringAspect diffOptions;
Utils::BoolAspect diffIgnoreWhiteSpace;
Utils::BoolAspect diffIgnoreBlankLines;
Utils::BoolAspect describeByCommitId;
- CvsSettings();
-
QStringList addOptions(const QStringList &args) const;
};
-class CvsSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit CvsSettingsPage(CvsSettings *settings);
-};
+CvsSettings &settings();
} // Cvs::Internal
diff --git a/src/plugins/debugger/CMakeLists.txt b/src/plugins/debugger/CMakeLists.txt
index f5f45deb4a..d06e82a27e 100644
--- a/src/plugins/debugger/CMakeLists.txt
+++ b/src/plugins/debugger/CMakeLists.txt
@@ -27,6 +27,7 @@ add_qtc_plugin(Debugger
console/consoleitemmodel.cpp console/consoleitemmodel.h
console/consoleproxymodel.cpp console/consoleproxymodel.h
console/consoleview.cpp console/consoleview.h
+ dap/dapengine.cpp dap/dapengine.h
debugger.qrc
debugger_global.h
debuggeractions.cpp debuggeractions.h
diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp
index bcc50a4b0d..2b21db5be5 100644
--- a/src/plugins/debugger/breakhandler.cpp
+++ b/src/plugins/debugger/breakhandler.cpp
@@ -41,6 +41,7 @@
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
+#include <QDialogButtonBox>
#include <QDir>
#include <QFormLayout>
#include <QGroupBox>
@@ -1126,9 +1127,9 @@ void BreakpointItem::addToCommand(DebuggerCommand *cmd) const
cmd->arg("expression", requested.expression);
}
-void BreakpointItem::updateFromGdbOutput(const GdbMi &bkpt)
+void BreakpointItem::updateFromGdbOutput(const GdbMi &bkpt, const FilePath &fileRoot)
{
- m_parameters.updateFromGdbOutput(bkpt);
+ m_parameters.updateFromGdbOutput(bkpt, fileRoot);
adjustMarker();
}
@@ -2691,14 +2692,14 @@ void BreakpointManager::gotoLocation(const GlobalBreakpoint &gbp) const
void BreakpointManager::executeDeleteAllBreakpointsDialog()
{
- QDialogButtonBox::StandardButton pressed =
- CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
- Tr::tr("Remove All Breakpoints"),
- Tr::tr("Are you sure you want to remove all breakpoints "
- "from all files in the current session?"),
- ICore::settings(),
- "RemoveAllBreakpoints");
- if (pressed != QDialogButtonBox::Yes)
+ QMessageBox::StandardButton pressed
+ = CheckableMessageBox::question(ICore::dialogParent(),
+ Tr::tr("Remove All Breakpoints"),
+ Tr::tr("Are you sure you want to remove all breakpoints "
+ "from all files in the current session?"),
+ ICore::settings(),
+ "RemoveAllBreakpoints");
+ if (pressed != QMessageBox::Yes)
return;
for (GlobalBreakpoint gbp : globalBreakpoints())
diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h
index 1024783db3..0b939a011f 100644
--- a/src/plugins/debugger/breakhandler.h
+++ b/src/plugins/debugger/breakhandler.h
@@ -107,7 +107,7 @@ public:
const BreakpointParameters &requestedParameters() const;
void addToCommand(DebuggerCommand *cmd) const;
- void updateFromGdbOutput(const GdbMi &bkpt);
+ void updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot);
int modelId() const;
QString responseId() const { return m_responseId; }
diff --git a/src/plugins/debugger/breakpoint.cpp b/src/plugins/debugger/breakpoint.cpp
index 8d456ef67f..79e2badf7b 100644
--- a/src/plugins/debugger/breakpoint.cpp
+++ b/src/plugins/debugger/breakpoint.cpp
@@ -265,7 +265,8 @@ static QString cleanupFullName(const QString &fileName)
return cleanFilePath;
}
-void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
+
+void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot)
{
QTC_ASSERT(bkpt.isValid(), return);
@@ -358,7 +359,7 @@ void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
QString name;
if (!fullName.isEmpty()) {
name = cleanupFullName(fullName);
- fileName = Utils::FilePath::fromString(name);
+ fileName = fileRoot.withNewPath(name);
//if (data->markerFileName().isEmpty())
// data->setMarkerFileName(name);
} else {
@@ -367,7 +368,7 @@ void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt)
// gdb's own. No point in assigning markerFileName for now.
}
if (!name.isEmpty())
- fileName = Utils::FilePath::fromString(name);
+ fileName = fileRoot.withNewPath(name);
if (fileName.isEmpty())
updateLocation(originalLocation);
diff --git a/src/plugins/debugger/breakpoint.h b/src/plugins/debugger/breakpoint.h
index 52a150f15e..b5ea0628da 100644
--- a/src/plugins/debugger/breakpoint.h
+++ b/src/plugins/debugger/breakpoint.h
@@ -128,7 +128,7 @@ public:
bool isQmlFileAndLineBreakpoint() const;
QString toString() const;
void updateLocation(const QString &location); // file.cpp:42
- void updateFromGdbOutput(const GdbMi &bkpt);
+ void updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot);
bool operator==(const BreakpointParameters &p) const { return equals(p); }
bool operator!=(const BreakpointParameters &p) const { return !equals(p); }
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index b95d1c93d5..52e6d71fe6 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -15,6 +15,7 @@
#include <debugger/debuggerinternalconstants.h>
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerprotocol.h>
+#include <debugger/debuggersourcepathmappingwidget.h>
#include <debugger/debuggertooltipmanager.h>
#include <debugger/debuggertr.h>
#include <debugger/disassembleragent.h>
@@ -44,9 +45,9 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/winutils.h>
@@ -181,8 +182,8 @@ CdbEngine::CdbEngine() :
DebuggerSettings *s = debuggerSettings();
connect(s->createFullBacktrace.action(), &QAction::triggered,
this, &CdbEngine::createFullBacktrace);
- connect(&m_process, &QtcProcess::started, this, &CdbEngine::processStarted);
- connect(&m_process, &QtcProcess::done, this, &CdbEngine::processDone);
+ connect(&m_process, &Process::started, this, &CdbEngine::processStarted);
+ connect(&m_process, &Process::done, this, &CdbEngine::processDone);
m_process.setStdOutLineCallback([this](const QString &line) { parseOutputLine(line); });
m_process.setStdErrLineCallback([this](const QString &line) { parseOutputLine(line); });
connect(&s->useDebuggingHelpers, &BaseAspect::changed,
@@ -224,7 +225,8 @@ void CdbEngine::init()
}
}
- const SourcePathMap &sourcePathMap = debuggerSettings()->sourcePathMap.value();
+ const SourcePathMap &sourcePathMap
+ = mergePlatformQtPath(runParameters(), debuggerSettings()->sourcePathMap.value());
if (!sourcePathMap.isEmpty()) {
for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) {
m_sourcePathMappings.push_back({QDir::toNativeSeparators(it.key()),
@@ -419,10 +421,11 @@ void CdbEngine::setupEngine()
inferiorEnvironment.set(qtLoggingToConsoleKey, "0");
static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
- inferiorEnvironment.prependOrSet(cdbExtensionPathVariableC, extensionFi.absolutePath(), {";"});
+ const QString pSep = OsSpecificAspects::pathListSeparator(Utils::OsTypeWindows);
+ inferiorEnvironment.prependOrSet(cdbExtensionPathVariableC, extensionFi.absolutePath(), pSep);
const QString oldCdbExtensionPath = qtcEnvironmentVariable(cdbExtensionPathVariableC);
if (!oldCdbExtensionPath.isEmpty())
- inferiorEnvironment.appendOrSet(cdbExtensionPathVariableC, oldCdbExtensionPath, {";"});
+ inferiorEnvironment.appendOrSet(cdbExtensionPathVariableC, oldCdbExtensionPath, pSep);
m_process.setEnvironment(inferiorEnvironment);
if (!sp.inferior.workingDirectory.isEmpty())
@@ -485,10 +488,10 @@ void CdbEngine::handleInitialSessionIdle()
runCommand({"sxn ibp", NoFlags}); // Do not break on initial breakpoints.
runCommand({".asm source_line", NoFlags}); // Source line in assembly
runCommand({m_extensionCommandPrefix
- + "setparameter maxStringLength=" + QString::number(s.maximalStringLength.value())
- + " maxStackDepth=" + QString::number(s.maximalStackDepth.value())
- + " firstChance=" + (s.firstChanceExceptionTaskEntry.value() ? "1" : "0")
- + " secondChance=" + (s.secondChanceExceptionTaskEntry.value() ? "1" : "0")
+ + "setparameter maxStringLength=" + QString::number(s.maximalStringLength())
+ + " maxStackDepth=" + QString::number(s.maximalStackDepth())
+ + " firstChance=" + (s.firstChanceExceptionTaskEntry() ? "1" : "0")
+ + " secondChance=" + (s.secondChanceExceptionTaskEntry() ? "1" : "0")
, NoFlags});
if (s.cdbUsePythonDumper.value())
@@ -954,9 +957,11 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
return;
}
- if (dbgCmd.flags == ScriptCommand) {
+ if (dbgCmd.flags & ScriptCommand) {
// repack script command into an extension command
DebuggerCommand newCmd("script", ExtensionCommand, dbgCmd.callback);
+ if (dbgCmd.flags & DebuggerCommand::Silent)
+ newCmd.flags |= DebuggerCommand::Silent;
if (!dbgCmd.args.isNull())
newCmd.args = QString{dbgCmd.function + '(' + dbgCmd.argsToPython() + ')'};
else
@@ -975,7 +980,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
} else {
const int token = ++m_nextCommandToken;
StringInputStream str(fullCmd);
- if (dbgCmd.flags == BuiltinCommand) {
+ if (dbgCmd.flags & BuiltinCommand) {
// Post a built-in-command producing free-format output with a callback.
// In order to catch the output, it is enclosed in 'echo' commands
// printing a specially formatted token to be identifiable in the output.
@@ -986,7 +991,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
showMessage("Command is longer than 4096 characters execution will likely fail.",
LogWarning);
}
- } else if (dbgCmd.flags == ExtensionCommand) {
+ } else if (dbgCmd.flags & ExtensionCommand) {
// Post an extension command producing one-line output with a callback,
// pass along token for identification in hash.
@@ -1020,7 +1025,8 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
if (debug) {
qDebug("CdbEngine::postCommand: resulting command '%s'\n", qPrintable(fullCmd));
}
- showMessage(cmd, LogInput);
+ if (!(dbgCmd.flags & DebuggerCommand::Silent))
+ showMessage(cmd, LogInput);
m_process.write(fullCmd);
}
@@ -1441,15 +1447,16 @@ void CdbEngine::reloadModules()
runCommand({"modules", ExtensionCommand, CB(handleModules)});
}
-void CdbEngine::loadSymbols(const QString & /* moduleName */)
+void CdbEngine::loadSymbols(const FilePath &moduleName)
{
+ Q_UNUSED(moduleName)
}
void CdbEngine::loadAllSymbols()
{
}
-void CdbEngine::requestModuleSymbols(const QString &moduleName)
+void CdbEngine::requestModuleSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -1487,12 +1494,13 @@ void CdbEngine::handleModules(const DebuggerResponse &response)
{
if (response.resultClass == ResultDone) {
if (response.data.type() == GdbMi::List) {
+ const FilePath inferior = runParameters().inferior.command.executable();
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
for (const GdbMi &gdbmiModule : response.data) {
Module module;
module.moduleName = gdbmiModule["name"].data();
- module.modulePath = gdbmiModule["image"].data();
+ module.modulePath = inferior.withNewPath(gdbmiModule["image"].data());
module.startAddress = gdbmiModule["start"].data().toULongLong(nullptr, 0);
module.endAddress = gdbmiModule["end"].data().toULongLong(nullptr, 0);
if (gdbmiModule["deferred"].type() == GdbMi::Invalid)
@@ -2254,12 +2262,11 @@ void CdbEngine::checkQtSdkPdbFiles(const QString &module)
"symbols for the debugger.")
.arg(qtName);
- CheckableMessageBox::doNotShowAgainInformation(
- Core::ICore::dialogParent(),
- Tr::tr("Missing Qt Debug Information"),
- message,
- Core::ICore::settings(),
- "CdbQtSdkPdbHint");
+ CheckableMessageBox::information(Core::ICore::dialogParent(),
+ Tr::tr("Missing Qt Debug Information"),
+ message,
+ Core::ICore::settings(),
+ "CdbQtSdkPdbHint");
showMessage("Missing Qt Debug Information Files package for " + qtName, LogMisc);
};
@@ -2280,7 +2287,7 @@ void CdbEngine::parseOutputLine(QString line)
// An extension notification (potentially consisting of several chunks)
if (!m_initialSessionIdleHandled && line.startsWith("SECURE: File not allowed to be loaded")
&& line.endsWith("qtcreatorcdbext.dll")) {
- CheckableMessageBox::doNotShowAgainInformation(
+ CheckableMessageBox::information(
Core::ICore::dialogParent(),
Tr::tr("Debugger Start Failed"),
Tr::tr("The system prevents loading of %1, which is required for debugging. "
@@ -2459,8 +2466,8 @@ static CPlusPlus::Document::Ptr getParsedDocument(const FilePath &filePath,
const CPlusPlus::Snapshot &snapshot)
{
QByteArray src;
- if (workingCopy.contains(filePath))
- src = workingCopy.source(filePath);
+ if (const auto source = workingCopy.source(filePath))
+ src = *source;
else
src = QString::fromLocal8Bit(filePath.fileContents().value_or(QByteArray())).toUtf8();
@@ -2764,12 +2771,60 @@ void CdbEngine::setupScripting(const DebuggerResponse &response)
return;
}
- QString dumperPath = runParameters().dumperPath.toUserOutput();
- dumperPath.replace('\\', "\\\\");
- runCommand({"sys.path.insert(1, '" + dumperPath + "')", ScriptCommand});
- runCommand({"from cdbbridge import Dumper", ScriptCommand});
- runCommand({"print(dir())", ScriptCommand});
- runCommand({"theDumper = Dumper()", ScriptCommand});
+
+ if (runParameters().startMode == AttachToRemoteServer) {
+ FilePath dumperPath = Core::ICore::resourcePath("debugger");
+ const FilePath loadOrderFile = dumperPath / "loadorder.txt";
+ const expected_str<QByteArray> toLoad = loadOrderFile.fileContents();
+ if (!toLoad) {
+ Core::AsynchronousMessageBox::critical(
+ Tr::tr("Cannot Find Debugger Initialization Script"),
+ Tr::tr("Cannot read %1: %2").arg(loadOrderFile.toUserOutput(), toLoad.error()));
+ notifyEngineSetupFailed();
+ return;
+ }
+
+ runCommand({"import sys, types", ScriptCommand});
+ QStringList moduleList;
+ for (const QByteArray &rawModuleName : toLoad->split('\n')) {
+ QString module = QString::fromUtf8(rawModuleName).trimmed();
+ if (module.startsWith('#') || module.isEmpty())
+ continue;
+ if (module == "***bridge***")
+ module = "cdbbridge";
+
+ const FilePath codeFile = dumperPath / (module + ".py");
+ const expected_str<QByteArray> code = codeFile.fileContents();
+ if (!code) {
+ qDebug() << Tr::tr("Cannot read %1: %2").arg(codeFile.toUserOutput(), code.error());
+ continue;
+ }
+
+ showMessage("Reading " + codeFile.toUserOutput(), LogInput);
+ runCommand({QString("module = types.ModuleType('%1')").arg(module), ScriptCommand});
+ runCommand({QString("code = bytes.fromhex('%1').decode('utf-8')")
+ .arg(QString::fromUtf8(code->toHex())), ScriptCommand | DebuggerCommand::Silent});
+ runCommand({QString("exec(code, module.__dict__)"), ScriptCommand});
+ runCommand({QString("sys.modules['%1'] = module").arg(module), ScriptCommand});
+ runCommand({QString("import %1").arg(module), ScriptCommand});
+
+ if (module.endsWith("types"))
+ moduleList.append('"' + module + '"');
+ }
+
+ runCommand({"from cdbbridge import Dumper", ScriptCommand});
+ runCommand({"print(dir())", ScriptCommand});
+ runCommand({"theDumper = Dumper()", ScriptCommand});
+ runCommand({QString("theDumper.dumpermodules = [%1]").arg(moduleList.join(',')), ScriptCommand});
+
+ } else {
+ QString dumperPath = Core::ICore::resourcePath("debugger").toUserOutput();
+ dumperPath.replace('\\', "\\\\");
+ runCommand({"sys.path.insert(1, '" + dumperPath + "')", ScriptCommand});
+ runCommand({"from cdbbridge import Dumper", ScriptCommand});
+ runCommand({"print(dir())", ScriptCommand});
+ runCommand({"theDumper = Dumper()", ScriptCommand});
+ }
const QString path = debuggerSettings()->extraDumperFile.value();
if (!path.isEmpty() && QFileInfo(path).isReadable()) {
diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h
index 3bea692d5b..84a0dded1a 100644
--- a/src/plugins/debugger/cdb/cdbengine.h
+++ b/src/plugins/debugger/cdb/cdbengine.h
@@ -10,7 +10,7 @@
#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QElapsedTimer>
@@ -65,9 +65,9 @@ public:
void changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) override;
void reloadModules() override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadRegisters() override;
void reloadSourceFiles() override;
@@ -110,9 +110,9 @@ private:
};
enum CommandFlags {
NoFlags = 0,
- BuiltinCommand,
- ExtensionCommand,
- ScriptCommand
+ BuiltinCommand = DebuggerCommand::Silent << 1,
+ ExtensionCommand = DebuggerCommand::Silent << 2,
+ ScriptCommand = DebuggerCommand::Silent << 3
};
void init();
@@ -174,7 +174,7 @@ private:
const QString m_tokenPrefix;
void handleSetupFailure(const QString &errorMessage);
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
DebuggerStartMode m_effectiveStartMode = NoStartMode;
//! Debugger accessible (expecting commands)
bool m_accessible = false;
diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp
index dc47fb79e0..97e9442266 100644
--- a/src/plugins/debugger/commonoptionspage.cpp
+++ b/src/plugins/debugger/commonoptionspage.cpp
@@ -15,8 +15,7 @@ using namespace Core;
using namespace Debugger::Constants;
using namespace Utils;
-namespace Debugger {
-namespace Internal {
+namespace Debugger::Internal {
///////////////////////////////////////////////////////////////////////
//
@@ -30,6 +29,19 @@ public:
explicit CommonOptionsPageWidget()
{
DebuggerSettings &s = *debuggerSettings();
+
+ setOnApply([&s] {
+ const bool originalPostMortem = s.registerForPostMortem->value();
+ const bool currentPostMortem = s.registerForPostMortem->volatileValue().toBool();
+ // explicitly trigger setValue() to override the setValueSilently() and trigger the registration
+ if (originalPostMortem != currentPostMortem)
+ s.registerForPostMortem->setValue(currentPostMortem);
+ s.page1.apply();
+ s.page1.writeSettings(ICore::settings());
+ });
+
+ setOnFinish([&s] { s.page1.finish(); });
+
using namespace Layouting;
Column col1 {
@@ -60,27 +72,8 @@ public:
st
}.attachTo(this);
}
-
- void apply() final;
- void finish() final { m_group.finish(); }
-
-private:
- AspectContainer &m_group = debuggerSettings()->page1;
};
-void CommonOptionsPageWidget::apply()
-{
- const DebuggerSettings *s = debuggerSettings();
- const bool originalPostMortem = s->registerForPostMortem->value();
- const bool currentPostMortem = s->registerForPostMortem->volatileValue().toBool();
- // explicitly trigger setValue() to override the setValueSilently() and trigger the registration
- if (originalPostMortem != currentPostMortem)
- s->registerForPostMortem->setValue(currentPostMortem);
-
- m_group.apply();
- m_group.writeSettings(ICore::settings());
-}
-
CommonOptionsPage::CommonOptionsPage()
{
setId(DEBUGGER_COMMON_SETTINGS_ID);
@@ -122,8 +115,9 @@ class LocalsAndExpressionsOptionsPageWidget : public IOptionsPageWidget
public:
LocalsAndExpressionsOptionsPageWidget()
{
- using namespace Layouting;
DebuggerSettings &s = *debuggerSettings();
+ setOnApply([&s] { s.page4.apply(); s.page4.writeSettings(ICore::settings()); });
+ setOnFinish([&s] { s.page4.finish(); });
auto label = new QLabel; //(useHelperGroup);
label->setTextFormat(Qt::AutoText);
@@ -134,6 +128,7 @@ public:
"std::map in the &quot;Locals&quot; and &quot;Expressions&quot; views.")
+ "</p></body></html>");
+ using namespace Layouting;
Column left {
label,
s.useCodeModel,
@@ -153,7 +148,8 @@ public:
Grid limits {
s.maximalStringLength, br,
- s.displayStringLimit
+ s.displayStringLimit, br,
+ s.defaultArraySize
};
Column {
@@ -168,12 +164,6 @@ public:
st
}.attachTo(this);
}
-
- void apply() final { m_group.apply(); m_group.writeSettings(ICore::settings()); }
- void finish() final { m_group.finish(); }
-
-private:
- AspectContainer &m_group = debuggerSettings()->page4;
};
LocalsAndExpressionsOptionsPage::LocalsAndExpressionsOptionsPage()
@@ -185,5 +175,4 @@ LocalsAndExpressionsOptionsPage::LocalsAndExpressionsOptionsPage()
setWidgetCreator([] { return new LocalsAndExpressionsOptionsPageWidget; });
}
-} // namespace Internal
-} // namespace Debugger
+} // Debugger::Internal
diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp
new file mode 100644
index 0000000000..9db7c0f7e5
--- /dev/null
+++ b/src/plugins/debugger/dap/dapengine.cpp
@@ -0,0 +1,799 @@
+// Copyright (C) 2016 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 "dapengine.h"
+
+#include <debugger/breakhandler.h>
+#include <debugger/debuggeractions.h>
+#include <debugger/debuggercore.h>
+#include <debugger/debuggerdialogs.h>
+#include <debugger/debuggerplugin.h>
+#include <debugger/debuggerprotocol.h>
+#include <debugger/debuggertooltipmanager.h>
+#include <debugger/debuggertr.h>
+#include <debugger/moduleshandler.h>
+#include <debugger/procinterrupt.h>
+#include <debugger/registerhandler.h>
+#include <debugger/sourceutils.h>
+#include <debugger/stackhandler.h>
+#include <debugger/threaddata.h>
+#include <debugger/watchhandler.h>
+#include <debugger/watchutils.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
+
+#include <coreplugin/idocument.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/messagebox.h>
+
+#include <QDateTime>
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QTimer>
+#include <QVariant>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QThread>
+
+using namespace Core;
+using namespace Utils;
+
+namespace Debugger::Internal {
+
+DapEngine::DapEngine()
+{
+ setObjectName("DapEngine");
+ setDebuggerName("DAP");
+}
+
+void DapEngine::executeDebuggerCommand(const QString &/*command*/)
+{
+ QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
+// if (state() == DebuggerNotReady) {
+// showMessage("DAP PROCESS NOT RUNNING, PLAIN CMD IGNORED: " + command);
+// return;
+// }
+// QTC_ASSERT(m_proc.isRunning(), notifyEngineIll());
+// postDirectCommand(command);
+}
+
+void DapEngine::postDirectCommand(const QJsonObject &ob)
+{
+ static int seq = 1;
+ QJsonObject obseq = ob;
+ obseq.insert("seq", seq++);
+
+ const QByteArray data = QJsonDocument(obseq).toJson(QJsonDocument::Compact);
+ const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data;
+ qDebug() << msg;
+
+ m_proc.writeRaw(msg);
+
+ showMessage(QString::fromUtf8(msg), LogInput);
+}
+
+void DapEngine::runCommand(const DebuggerCommand &cmd)
+{
+ if (state() == EngineSetupRequested) { // cmd has been triggered too early
+ showMessage("IGNORED COMMAND: " + cmd.function);
+ return;
+ }
+ QTC_ASSERT(m_proc.isRunning(), notifyEngineIll());
+// postDirectCommand(cmd.args.toObject());
+// const QByteArray data = QJsonDocument(cmd.args.toObject()).toJson(QJsonDocument::Compact);
+// m_proc.writeRaw("Content-Length: " + QByteArray::number(data.size()) + "\r\n" + data + "\r\n");
+
+// showMessage(QString::fromUtf8(data), LogInput);
+}
+
+void DapEngine::shutdownInferior()
+{
+ QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
+ postDirectCommand({{"command", "terminate"},
+ {"type", "request"}});
+
+ qDebug() << "DapEngine::shutdownInferior()";
+ notifyInferiorShutdownFinished();
+}
+
+void DapEngine::shutdownEngine()
+{
+ QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
+
+ qDebug() << "DapEngine::shutdownEngine()";
+ m_proc.kill();
+}
+
+void DapEngine::setupEngine()
+{
+ QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
+
+ connect(&m_proc, &Process::started, this, &DapEngine::handleDabStarted);
+ connect(&m_proc, &Process::done, this, &DapEngine::handleDapDone);
+ connect(&m_proc, &Process::readyReadStandardOutput, this, &DapEngine::readDapStandardOutput);
+ connect(&m_proc, &Process::readyReadStandardError, this, &DapEngine::readDapStandardError);
+
+ const DebuggerRunParameters &rp = runParameters();
+ const CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}};
+ showMessage("STARTING " + cmd.toUserOutput());
+ m_proc.setProcessMode(ProcessMode::Writer);
+ m_proc.setEnvironment(rp.debugger.environment);
+ m_proc.setCommand(cmd);
+ m_proc.start();
+ notifyEngineRunAndInferiorRunOk();
+}
+
+// From the docs:
+// The sequence of events/requests is as follows:
+// * adapters sends initialized event (after the initialize request has returned)
+// * client sends zero or more setBreakpoints requests
+// * client sends one setFunctionBreakpoints request
+// (if corresponding capability supportsFunctionBreakpoints is true)
+// * client sends a setExceptionBreakpoints request if one or more exceptionBreakpointFilters
+// have been defined (or if supportsConfigurationDoneRequest is not true)
+// * client sends other future configuration requests
+// * client sends one configurationDone request to indicate the end of the configuration.
+
+void DapEngine::handleDabStarted()
+{
+ notifyEngineSetupOk();
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+
+// CHECK_STATE(EngineRunRequested);
+
+ postDirectCommand({
+ {"command", "initialize"},
+ {"type", "request"},
+ {"arguments", QJsonObject {
+ {"clientID", "QtCreator"}, // The ID of the client using this adapter.
+ {"clientName", "QtCreator"} // The human-readable name of the client using this adapter.
+ }}
+ });
+
+ qDebug() << "handleDabStarted";
+}
+
+void DapEngine::handleDabConfigurationDone()
+{
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+
+ // CHECK_STATE(EngineRunRequested);
+
+ postDirectCommand({{"command", "configurationDone"}, {"type", "request"}});
+
+ qDebug() << "handleDabConfigurationDone";
+}
+
+
+void DapEngine::handleDabLaunch()
+{
+ QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
+
+ // CHECK_STATE(EngineRunRequested);
+
+ postDirectCommand(
+ {{"command", "launch"},
+ {"type", "request"},
+// {"program", runParameters().inferior.command.executable().path()},
+ {"arguments",
+ QJsonObject{
+ {"noDebug", false},
+ {"program", runParameters().inferior.command.executable().path()},
+ {"__restart", ""}
+ }}});
+ qDebug() << "handleDabLaunch";
+}
+
+void DapEngine::interruptInferior()
+{
+ postDirectCommand({{"command", "pause"},
+ {"type", "request"}});
+}
+
+void DapEngine::executeStepIn(bool)
+{
+ notifyInferiorRunRequested();
+
+// postDirectCommand({{"command", "stepIn"},
+// {"type", "request"},
+// {"arguments",
+// QJsonObject{
+// {"threadId", 1}, // The ID of the client using this adapter.
+// }}});
+
+ notifyInferiorRunOk();
+}
+
+void DapEngine::executeStepOut()
+{
+ notifyInferiorRunRequested();
+ notifyInferiorRunOk();
+// postDirectCommand("return");
+}
+
+void DapEngine::executeStepOver(bool)
+{
+ notifyInferiorRunRequested();
+ notifyInferiorRunOk();
+// postDirectCommand("next");
+}
+
+void DapEngine::continueInferior()
+{
+ notifyInferiorRunRequested();
+ postDirectCommand({{"command", "continue"},
+ {"type", "request"},
+ {"arguments",
+ QJsonObject{
+ {"threadId", 1}, // The ID of the client using this adapter.
+ }}});
+}
+
+void DapEngine::executeRunToLine(const ContextData &data)
+{
+ Q_UNUSED(data)
+ QTC_CHECK("FIXME: DapEngine::runToLineExec()" && false);
+}
+
+void DapEngine::executeRunToFunction(const QString &functionName)
+{
+ Q_UNUSED(functionName)
+ QTC_CHECK("FIXME: DapEngine::runToFunctionExec()" && false);
+}
+
+void DapEngine::executeJumpToLine(const ContextData &data)
+{
+ Q_UNUSED(data)
+ QTC_CHECK("FIXME: DapEngine::jumpToLineExec()" && false);
+}
+
+void DapEngine::activateFrame(int frameIndex)
+{
+ if (state() != InferiorStopOk && state() != InferiorUnrunnable)
+ return;
+
+ StackHandler *handler = stackHandler();
+ QTC_ASSERT(frameIndex < handler->stackSize(), return);
+ handler->setCurrentIndex(frameIndex);
+ gotoLocation(handler->currentFrame());
+ updateLocals();
+}
+
+void DapEngine::selectThread(const Thread &thread)
+{
+ Q_UNUSED(thread)
+}
+
+bool DapEngine::acceptsBreakpoint(const BreakpointParameters &) const
+{
+ return true; // FIXME: Too bold.
+}
+
+static QJsonObject createBreakpoint(const Breakpoint &breakpoint)
+{
+ const BreakpointParameters &params = breakpoint->requestedParameters();
+
+ if (params.fileName.isEmpty())
+ return QJsonObject();
+
+ QJsonObject bp;
+ bp["line"] = params.lineNumber;
+ bp["source"] = QJsonObject{{"name", params.fileName.fileName()},
+ {"path", params.fileName.path()}};
+ return bp;
+}
+
+
+void DapEngine::insertBreakpoint(const Breakpoint &bp)
+{
+ QTC_ASSERT(bp, return);
+ QTC_CHECK(bp->state() == BreakpointInsertionRequested);
+ notifyBreakpointInsertProceeding(bp);
+
+ const BreakpointParameters &params = bp->requestedParameters();
+ bp->setResponseId(QString::number(m_nextBreakpointId++));
+
+ QJsonArray breakpoints;
+ for (const auto &breakpoint : breakHandler()->breakpoints()) {
+ QJsonObject jsonBp = createBreakpoint(breakpoint);
+ if (!jsonBp.isEmpty()
+ && params.fileName.path() == jsonBp["source"].toObject()["path"].toString()) {
+ breakpoints.append(jsonBp);
+ }
+ }
+
+ postDirectCommand(
+ {{"command", "setBreakpoints"},
+ {"type", "request"},
+ {"arguments",
+ QJsonObject{{"source", QJsonObject{{"path", params.fileName.path()}}},
+ {"breakpoints", breakpoints}
+
+ }}});
+
+ qDebug() << "insertBreakpoint" << bp->modelId() << bp->responseId();
+}
+
+void DapEngine::updateBreakpoint(const Breakpoint &bp)
+{
+ notifyBreakpointChangeProceeding(bp);
+// QTC_ASSERT(bp, return);
+// const BreakpointState state = bp->state();
+// if (QTC_GUARD(state == BreakpointUpdateRequested))
+// if (bp->responseId().isEmpty()) // FIXME postpone update somehow (QTimer::singleShot?)
+// return;
+
+// // FIXME figure out what needs to be changed (there might be more than enabled state)
+// const BreakpointParameters &requested = bp->requestedParameters();
+// if (requested.enabled != bp->isEnabled()) {
+// if (bp->isEnabled())
+// postDirectCommand("disable " + bp->responseId());
+// else
+// postDirectCommand("enable " + bp->responseId());
+// bp->setEnabled(!bp->isEnabled());
+// }
+// // Pretend it succeeds without waiting for response.
+ notifyBreakpointChangeOk(bp);
+}
+
+void DapEngine::removeBreakpoint(const Breakpoint &bp)
+{
+ QTC_ASSERT(bp, return);
+ QTC_CHECK(bp->state() == BreakpointRemoveRequested);
+ notifyBreakpointRemoveProceeding(bp);
+
+ const BreakpointParameters &params = bp->requestedParameters();
+
+ QJsonArray breakpoints;
+ for (const auto &breakpoint : breakHandler()->breakpoints())
+ if (breakpoint->responseId() != bp->responseId()
+ && params.fileName == breakpoint->requestedParameters().fileName) {
+ QJsonObject jsonBp = createBreakpoint(breakpoint);
+ breakpoints.append(jsonBp);
+ }
+
+ postDirectCommand({{"command", "setBreakpoints"},
+ {"type", "request"},
+ {"arguments",
+ QJsonObject{{"source", QJsonObject{{"path", params.fileName.path()}}},
+ {"breakpoints", breakpoints}}}});
+
+ qDebug() << "removeBreakpoint" << bp->modelId() << bp->responseId();
+}
+
+void DapEngine::loadSymbols(const Utils::FilePath &/*moduleName*/)
+{
+}
+
+void DapEngine::loadAllSymbols()
+{
+}
+
+void DapEngine::reloadModules()
+{
+ runCommand({"listModules"});
+}
+
+void DapEngine::refreshModules(const GdbMi &modules)
+{
+ ModulesHandler *handler = modulesHandler();
+ handler->beginUpdateAll();
+ for (const GdbMi &item : modules) {
+ Module module;
+ module.moduleName = item["name"].data();
+ QString path = item["value"].data();
+ int pos = path.indexOf("' from '");
+ if (pos != -1) {
+ path = path.mid(pos + 8);
+ if (path.size() >= 2)
+ path.chop(2);
+ } else if (path.startsWith("<module '")
+ && path.endsWith("' (built-in)>")) {
+ path = "(builtin)";
+ }
+ module.modulePath = FilePath::fromString(path);
+ handler->updateModule(module);
+ }
+ handler->endUpdateAll();
+}
+
+void DapEngine::requestModuleSymbols(const Utils::FilePath &/*moduleName*/)
+{
+// DebuggerCommand cmd("listSymbols");
+// cmd.arg("module", moduleName);
+// runCommand(cmd);
+}
+
+void DapEngine::refreshState(const GdbMi &reportedState)
+{
+ QString newState = reportedState.data();
+ if (newState == "stopped") {
+ notifyInferiorSpontaneousStop();
+ updateAll();
+ } else if (newState == "inferiorexited") {
+ notifyInferiorExited();
+ }
+}
+
+void DapEngine::refreshLocation(const GdbMi &reportedLocation)
+{
+ StackFrame frame;
+ frame.file = FilePath::fromString(reportedLocation["file"].data());
+ frame.line = reportedLocation["line"].toInt();
+ frame.usable = frame.file.isReadableFile();
+ if (state() == InferiorRunOk) {
+ showMessage(QString("STOPPED AT: %1:%2").arg(frame.file.toUserOutput()).arg(frame.line));
+ gotoLocation(frame);
+ notifyInferiorSpontaneousStop();
+ updateAll();
+ }
+}
+
+void DapEngine::refreshSymbols(const GdbMi &symbols)
+{
+ QString moduleName = symbols["module"].data();
+ Symbols syms;
+ for (const GdbMi &item : symbols["symbols"]) {
+ Symbol symbol;
+ symbol.name = item["name"].data();
+ syms.append(symbol);
+ }
+ showModuleSymbols(FilePath::fromString(moduleName), syms);
+}
+
+bool DapEngine::canHandleToolTip(const DebuggerToolTipContext &) const
+{
+ return state() == InferiorStopOk;
+}
+
+void DapEngine::assignValueInDebugger(WatchItem *, const QString &/*expression*/, const QVariant &/*value*/)
+{
+ //DebuggerCommand cmd("assignValue");
+ //cmd.arg("expression", expression);
+ //cmd.arg("value", value.toString());
+ //runCommand(cmd);
+// postDirectCommand("global " + expression + ';' + expression + "=" + value.toString());
+ updateLocals();
+}
+
+void DapEngine::updateItem(const QString &iname)
+{
+ Q_UNUSED(iname)
+ updateAll();
+}
+
+QString DapEngine::errorMessage(QProcess::ProcessError error) const
+{
+ switch (error) {
+ case QProcess::FailedToStart:
+ return Tr::tr("The DAP process failed to start. Either the "
+ "invoked program \"%1\" is missing, or you may have insufficient "
+ "permissions to invoke the program.")
+ .arg(m_proc.commandLine().executable().toUserOutput());
+ case QProcess::Crashed:
+ return Tr::tr("The DAP process crashed some time after starting "
+ "successfully.");
+ case QProcess::Timedout:
+ return Tr::tr("The last waitFor...() function timed out. "
+ "The state of QProcess is unchanged, and you can try calling "
+ "waitFor...() again.");
+ case QProcess::WriteError:
+ return Tr::tr("An error occurred when attempting to write "
+ "to the DAP process. For example, the process may not be running, "
+ "or it may have closed its input channel.");
+ case QProcess::ReadError:
+ return Tr::tr("An error occurred when attempting to read from "
+ "the DAP process. For example, the process may not be running.");
+ default:
+ return Tr::tr("An unknown error in the DAP process occurred.") + ' ';
+ }
+}
+
+void DapEngine::handleDapDone()
+{
+ if (m_proc.result() == ProcessResult::StartFailed) {
+ notifyEngineSetupFailed();
+ showMessage("ADAPTER START FAILED");
+ ICore::showWarningWithOptions(Tr::tr("Adapter start failed"), m_proc.exitMessage());
+ return;
+ }
+
+ const QProcess::ProcessError error = m_proc.error();
+ if (error != QProcess::UnknownError) {
+ showMessage("HANDLE DAP ERROR");
+ if (error != QProcess::Crashed)
+ AsynchronousMessageBox::critical(Tr::tr("DAP I/O Error"), errorMessage(error));
+ if (error == QProcess::FailedToStart)
+ return;
+ }
+ showMessage(QString("DAP PROCESS FINISHED, status %1, code %2")
+ .arg(m_proc.exitStatus()).arg(m_proc.exitCode()));
+ notifyEngineSpontaneousShutdown();
+}
+
+void DapEngine::readDapStandardError()
+{
+ QString err = m_proc.readAllStandardError();
+ //qWarning() << "Unexpected DAP stderr:" << err;
+ showMessage("Unexpected DAP stderr: " + err);
+ //handleOutput(err);
+}
+
+void DapEngine::readDapStandardOutput()
+{
+ m_inbuffer.append(m_proc.readAllStandardOutput().toUtf8());
+// qDebug() << m_inbuffer;
+
+ while (true) {
+ // Something like
+ // Content-Length: 128\r\n
+ // {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n
+ // FIXME: There coud be more than one header line.
+ int pos1 = m_inbuffer.indexOf("Content-Length:");
+ if (pos1 == -1)
+ break;
+ pos1 += 15;
+
+ int pos2 = m_inbuffer.indexOf('\n', pos1);
+ if (pos2 == -1)
+ break;
+
+ const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt();
+ if (len < 4)
+ break;
+
+ pos2 += 3; // Skip \r\n\r
+
+ if (pos2 + len > m_inbuffer.size())
+ break;
+
+ QJsonParseError error;
+ const auto doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error);
+
+ m_inbuffer = m_inbuffer.mid(pos2 + len);
+
+ handleOutput(doc);
+ }
+}
+
+void DapEngine::handleOutput(const QJsonDocument &data)
+{
+ QJsonObject ob = data.object();
+
+ const QJsonValue t = ob.value("type");
+ const QString type = t.toString();
+ qDebug() << "response" << ob;
+
+ if (type == "response") {
+ const QString command = ob.value("command").toString();
+ if (command == "configurationDone") {
+ showMessage("configurationDone", LogDebug);
+ qDebug() << "configurationDone success";
+ notifyInferiorRunOk();
+ return;
+ }
+
+ if (command == "continue") {
+ showMessage("continue", LogDebug);
+ qDebug() << "continue success";
+ notifyInferiorRunOk();
+ return;
+ }
+
+ }
+
+ if (type == "event") {
+ const QString event = ob.value("event").toString();
+ const QJsonObject body = ob.value("body").toObject();
+
+ if (event == "output") {
+ const QString category = body.value("category").toString();
+ const QString output = body.value("output").toString();
+ if (category == "stdout")
+ showMessage(output, AppOutput);
+ else if (category == "stderr")
+ showMessage(output, AppError);
+ else
+ showMessage(output, LogDebug);
+ return;
+ }
+ qDebug() << data;
+
+ if (event == "initialized") {
+ showMessage(event, LogDebug);
+ qDebug() << "initialize success";
+ handleDabLaunch();
+ handleDabConfigurationDone();
+ return;
+ }
+
+ if (event == "initialized") {
+ showMessage(event, LogDebug);
+ return;
+ }
+
+ if (event == "stopped") {
+ showMessage(event, LogDebug);
+ if (body.value("reason").toString() == "breakpoint") {
+ QString id = QString::number(body.value("hitBreakpointIds").toArray().first().toInteger());
+ const BreakpointParameters &params
+ = breakHandler()->findBreakpointByResponseId(id)->requestedParameters();
+ gotoLocation(Location(params.fileName, params.lineNumber));
+ }
+
+ if (state() == InferiorStopRequested)
+ notifyInferiorStopOk();
+ else
+ notifyInferiorSpontaneousStop();
+ return;
+ }
+
+ if (event == "thread") {
+ showMessage(event, LogDebug);
+ if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1)
+ claimInitialBreakpoints();
+ return;
+ }
+
+ if (event == "breakpoint") {
+ showMessage(event, LogDebug);
+ QJsonObject breakpoint = body.value("breakpoint").toObject();
+ Breakpoint bp = breakHandler()->findBreakpointByResponseId(
+ QString::number(breakpoint.value("id").toInt()));
+ qDebug() << "breakpoint id :" << breakpoint.value("id").toInt();
+
+ if (body.value("reason").toString() == "new") {
+ if (breakpoint.value("verified").toBool()) {
+// bp->setPending(false);
+ notifyBreakpointInsertOk(bp);
+ qDebug() << "breakpoint inserted";
+ } else {
+ notifyBreakpointInsertFailed(bp);
+ qDebug() << "breakpoint insertion failed";
+ }
+ return;
+ }
+
+ if (body.value("reason").toString() == "removed") {
+ if (breakpoint.value("verified").toBool()) {
+ notifyBreakpointRemoveOk(bp);
+ qDebug() << "breakpoint removed";
+ } else {
+ notifyBreakpointRemoveFailed(bp);
+ qDebug() << "breakpoint remove failed";
+ }
+ return;
+ }
+ return;
+ }
+
+
+ showMessage("UNKNOWN EVENT:" + event);
+ return;
+ }
+
+ showMessage("UNKNOWN TYPE:" + type);
+}
+
+void DapEngine::refreshLocals(const GdbMi &vars)
+{
+ WatchHandler *handler = watchHandler();
+ handler->resetValueCache();
+ handler->insertItems(vars);
+ handler->notifyUpdateFinished();
+
+ updateToolTips();
+}
+
+void DapEngine::refreshStack(const GdbMi &stack)
+{
+ StackHandler *handler = stackHandler();
+ StackFrames frames;
+ for (const GdbMi &item : stack["frames"]) {
+ StackFrame frame;
+ frame.level = item["level"].data();
+ frame.file = FilePath::fromString(item["file"].data());
+ frame.function = item["function"].data();
+ frame.module = item["function"].data();
+ frame.line = item["line"].toInt();
+ frame.address = item["address"].toAddress();
+ GdbMi usable = item["usable"];
+ if (usable.isValid())
+ frame.usable = usable.data().toInt();
+ else
+ frame.usable = frame.file.isReadableFile();
+ frames.append(frame);
+ }
+ bool canExpand = stack["hasmore"].toInt();
+ //action(ExpandStack)->setEnabled(canExpand);
+ handler->setFrames(frames, canExpand);
+
+ int index = stackHandler()->firstUsableIndex();
+ handler->setCurrentIndex(index);
+ if (index >= 0 && index < handler->stackSize())
+ gotoLocation(handler->frameAt(index));
+}
+
+void DapEngine::updateAll()
+{
+ runCommand({"stackListFrames"});
+ updateLocals();
+}
+
+void DapEngine::updateLocals()
+{
+// DebuggerCommand cmd("updateData");
+// cmd.arg("nativeMixed", isNativeMixedActive());
+// watchHandler()->appendFormatRequests(&cmd);
+// watchHandler()->appendWatchersAndTooltipRequests(&cmd);
+
+// const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE");
+// cmd.arg("passexceptions", alwaysVerbose);
+// cmd.arg("fancy", debuggerSettings()->useDebuggingHelpers.value());
+
+// //cmd.arg("resultvarname", m_resultVarName);
+// //m_lastDebuggableCommand = cmd;
+// //m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1");
+// cmd.arg("frame", stackHandler()->currentIndex());
+
+// watchHandler()->notifyUpdateStarted();
+// runCommand(cmd);
+}
+
+bool DapEngine::hasCapability(unsigned cap) const
+{
+ return cap & (ReloadModuleCapability
+ | BreakConditionCapability
+ | ShowModuleSymbolsCapability);
+}
+
+void DapEngine::claimInitialBreakpoints()
+{
+ BreakpointManager::claimBreakpointsForEngine(this);
+ qDebug() << "claimInitialBreakpoints";
+// const Breakpoints bps = breakHandler()->breakpoints();
+// for (const Breakpoint &bp : bps)
+// qDebug() << "breakpoit: " << bp->fileName() << bp->lineNumber();
+// qDebug() << "claimInitialBreakpoints end";
+
+// const DebuggerRunParameters &rp = runParameters();
+// if (rp.startMode != AttachToCore) {
+// showStatusMessage(Tr::tr("Setting breakpoints..."));
+// showMessage(Tr::tr("Setting breakpoints..."));
+// BreakpointManager::claimBreakpointsForEngine(this);
+
+// const DebuggerSettings &s = *debuggerSettings();
+// const bool onAbort = s.breakOnAbort.value();
+// const bool onWarning = s.breakOnWarning.value();
+// const bool onFatal = s.breakOnFatal.value();
+// if (onAbort || onWarning || onFatal) {
+// DebuggerCommand cmd("createSpecialBreakpoints");
+// cmd.arg("breakonabort", onAbort);
+// cmd.arg("breakonwarning", onWarning);
+// cmd.arg("breakonfatal", onFatal);
+// runCommand(cmd);
+// }
+// }
+
+// // It is ok to cut corners here and not wait for createSpecialBreakpoints()'s
+// // response, as the command is synchronous from Creator's point of view,
+// // and even if it fails (e.g. due to stripped binaries), continuing with
+// // the start up is the best we can do.
+
+// if (!rp.commandsAfterConnect.isEmpty()) {
+// const QString commands = expand(rp.commandsAfterConnect);
+// for (const QString &command : commands.split('\n'))
+// runCommand({command, NativeCommand});
+// }
+}
+
+DebuggerEngine *createDapEngine()
+{
+ return new DapEngine;
+}
+
+} // Debugger::Internal
diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h
new file mode 100644
index 0000000000..2fa3019cad
--- /dev/null
+++ b/src/plugins/debugger/dap/dapengine.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <debugger/debuggerengine.h>
+#include <utils/process.h>
+
+#include <QVariant>
+
+namespace Debugger::Internal {
+
+class DebuggerCommand;
+class GdbMi;
+
+/*
+ * A debugger engine for the debugger adapter protocol.
+ */
+
+class DapEngine : public DebuggerEngine
+{
+public:
+ DapEngine();
+
+private:
+ void executeStepIn(bool) override;
+ void executeStepOut() override;
+ void executeStepOver(bool) override;
+
+ void setupEngine() override;
+ void shutdownInferior() override;
+ void shutdownEngine() override;
+
+ bool canHandleToolTip(const DebuggerToolTipContext &) const override;
+
+ void continueInferior() override;
+ void interruptInferior() override;
+
+ void executeRunToLine(const ContextData &data) override;
+ void executeRunToFunction(const QString &functionName) override;
+ void executeJumpToLine(const ContextData &data) override;
+
+ void activateFrame(int index) override;
+ void selectThread(const Thread &thread) override;
+
+ bool acceptsBreakpoint(const BreakpointParameters &bp) const override;
+ void insertBreakpoint(const Breakpoint &bp) override;
+ void updateBreakpoint(const Breakpoint &bp) override;
+ void removeBreakpoint(const Breakpoint &bp) override;
+
+ void assignValueInDebugger(WatchItem *item,
+ const QString &expr, const QVariant &value) override;
+ void executeDebuggerCommand(const QString &command) override;
+
+ void loadSymbols(const Utils::FilePath &moduleName) override;
+ void loadAllSymbols() override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
+ void reloadModules() override;
+ void reloadRegisters() override {}
+ void reloadSourceFiles() override {}
+ void reloadFullStack() override {}
+
+ bool supportsThreads() const { return true; }
+ void updateItem(const QString &iname) override;
+
+ void runCommand(const DebuggerCommand &cmd) override;
+ void postDirectCommand(const QJsonObject &ob);
+
+ void refreshLocation(const GdbMi &reportedLocation);
+ void refreshStack(const GdbMi &stack);
+ void refreshLocals(const GdbMi &vars);
+ void refreshModules(const GdbMi &modules);
+ void refreshState(const GdbMi &reportedState);
+ void refreshSymbols(const GdbMi &symbols);
+
+ QString errorMessage(QProcess::ProcessError error) const;
+ bool hasCapability(unsigned cap) const override;
+
+ void claimInitialBreakpoints();
+
+ void handleDabStarted();
+ void handleDabLaunch();
+ void handleDabConfigurationDone();
+
+ void handleDapDone();
+ void readDapStandardOutput();
+ void readDapStandardError();
+ void handleOutput(const QJsonDocument &data);
+ void handleResponse(const QString &ba);
+ void updateAll() override;
+ void updateLocals() override;
+
+ QByteArray m_inbuffer;
+ Utils::Process m_proc;
+ int m_nextBreakpointId = 1;
+};
+
+} // Debugger::Internal
diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs
index 50f1eba24f..be2adfc04d 100644
--- a/src/plugins/debugger/debugger.qbs
+++ b/src/plugins/debugger/debugger.qbs
@@ -123,6 +123,12 @@ Project {
}
Group {
+ name: "dap"
+ prefix: "dap/"
+ files: ["dapengine.cpp", "dapengine.h"]
+ }
+
+ Group {
name: "uvsc"
prefix: "uvsc/"
files: [
@@ -175,7 +181,7 @@ Project {
Group {
name: "Images"
prefix: "images/"
- files: ["*.png", "*.xpm"]
+ files: ["*.png"]
}
Group {
@@ -239,9 +245,7 @@ Project {
]
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"debuggerunittests.qrc",
]
diff --git a/src/plugins/debugger/debugger.qrc b/src/plugins/debugger/debugger.qrc
index 86a05a73ff..6fc31bb4fb 100644
--- a/src/plugins/debugger/debugger.qrc
+++ b/src/plugins/debugger/debugger.qrc
@@ -42,7 +42,6 @@
<file>images/mode_debug@2x.png</file>
<file>images/mode_debug_mask.png</file>
<file>images/mode_debug_mask@2x.png</file>
- <file>images/pin.xpm</file>
<file>images/debugger_restart_small.png</file>
<file>images/debugger_restart_small@2x.png</file>
<file>images/recordfill.png</file>
diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp
index a3a6187a41..208ca88d61 100644
--- a/src/plugins/debugger/debuggeractions.cpp
+++ b/src/plugins/debugger/debuggeractions.cpp
@@ -531,6 +531,15 @@ DebuggerSettings::DebuggerSettings()
+ Tr::tr("The maximum length for strings in separated windows. "
"Longer strings are cut off and displayed with an ellipsis attached."));
+ defaultArraySize.setSettingsKey(debugModeGroup, "DefaultArraySize");
+ defaultArraySize.setDefaultValue(100);
+ defaultArraySize.setRange(10, 1000000000);
+ defaultArraySize.setSingleStep(100);
+ defaultArraySize.setLabelText(Tr::tr("Default array size:"));
+ defaultArraySize.setToolTip("<p>"
+ + Tr::tr("The number of array elements requested when expanding "
+ "entries in the Locals and Expressions views."));
+
expandStack.setLabelText(Tr::tr("Reload Full Stack"));
createFullBacktrace.setLabelText(Tr::tr("Create Full Backtrace"));
@@ -610,6 +619,7 @@ DebuggerSettings::DebuggerSettings()
page4.registerAspect(&showQObjectNames);
page4.registerAspect(&displayStringLimit);
page4.registerAspect(&maximalStringLength);
+ page4.registerAspect(&defaultArraySize);
// Page 5
page5.registerAspect(&cdbAdditionalArguments);
diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h
index 69cc76a1f1..40fabfef1e 100644
--- a/src/plugins/debugger/debuggeractions.h
+++ b/src/plugins/debugger/debuggeractions.h
@@ -30,7 +30,7 @@ public:
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QVariant volatileValue() const override;
void setVolatileValue(const QVariant &val) override;
@@ -146,6 +146,7 @@ public:
Utils::BoolAspect autoDerefPointers;
Utils::IntegerAspect maximalStringLength;
Utils::IntegerAspect displayStringLimit;
+ Utils::IntegerAspect defaultArraySize;
Utils::BoolAspect sortStructMembers;
Utils::BoolAspect useToolTipsInLocalsView;
diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h
index b071713a84..06ccb94f1b 100644
--- a/src/plugins/debugger/debuggerconstants.h
+++ b/src/plugins/debugger/debuggerconstants.h
@@ -99,6 +99,7 @@ enum DebuggerEngineType
CdbEngineType = 0x004,
PdbEngineType = 0x008,
LldbEngineType = 0x100,
+ DapEngineType = 0x200,
UvscEngineType = 0x1000
};
diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp
index ece2c22669..c382e91c17 100644
--- a/src/plugins/debugger/debuggerdialogs.cpp
+++ b/src/plugins/debugger/debuggerdialogs.cpp
@@ -212,6 +212,7 @@ StartApplicationDialog::StartApplicationDialog(QWidget *parent)
d->localExecutablePathChooser->setHistoryCompleter("LocalExecutable");
d->arguments = new FancyLineEdit(this);
+ d->arguments->setClearButtonEnabled(true);
d->arguments->setHistoryCompleter("CommandlineArguments");
d->workingDirectory = new PathChooser(this);
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index 14cab12750..28d218929a 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -13,15 +13,16 @@
#include "debuggertr.h"
#include "breakhandler.h"
+#include "debuggermainwindow.h"
#include "disassembleragent.h"
+#include "enginemanager.h"
#include "localsandexpressionswindow.h"
#include "logwindow.h"
-#include "debuggermainwindow.h"
-#include "enginemanager.h"
#include "memoryagent.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "peripheralregisterhandler.h"
+#include "shared/peutils.h"
#include "sourcefileshandler.h"
#include "sourceutils.h"
#include "stackhandler.h"
@@ -31,7 +32,6 @@
#include "watchhandler.h"
#include "watchutils.h"
#include "watchwindow.h"
-#include "debugger/shared/peutils.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -54,10 +54,10 @@
#include <utils/basetreeview.h>
#include <utils/checkablemessagebox.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/processhandle.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/styledbar.h>
#include <utils/utilsicons.h>
@@ -471,7 +471,7 @@ public:
QString m_qtNamespace;
// Safety net to avoid infinite lookups.
- QSet<QString> m_lookupRequests; // FIXME: Integrate properly.
+ QHash<QString, int> m_lookupRequests; // FIXME: Integrate properly.
QPointer<QWidget> m_alertBox;
QPointer<BaseTreeView> m_breakView;
@@ -1036,11 +1036,6 @@ void DebuggerEngine::setRunTool(DebuggerRunTool *runTool)
{
d->m_device = runTool->device();
- IDevice::ConstPtr debuggerDevice =
- DeviceManager::deviceForPath(d->m_runParameters.debugger.command.executable());
- if (QTC_GUARD(debuggerDevice))
- d->m_runParameters.dumperPath = debuggerDevice->debugDumperPath();
-
d->m_terminalRunner = runTool->terminalRunner();
validateRunParameters(d->m_runParameters);
@@ -2083,7 +2078,7 @@ void DebuggerEngine::examineModules()
{
}
-void DebuggerEngine::loadSymbols(const QString &)
+void DebuggerEngine::loadSymbols(const FilePath &)
{
}
@@ -2095,11 +2090,11 @@ void DebuggerEngine::loadSymbolsForStack()
{
}
-void DebuggerEngine::requestModuleSymbols(const QString &)
+void DebuggerEngine::requestModuleSymbols(const FilePath &)
{
}
-void DebuggerEngine::requestModuleSections(const QString &)
+void DebuggerEngine::requestModuleSections(const FilePath &)
{
}
@@ -2360,9 +2355,10 @@ bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) con
void DebuggerEngine::updateItem(const QString &iname)
{
- if (d->m_lookupRequests.contains(iname)) {
+ WatchHandler *handler = watchHandler();
+ const int maxArrayCount = handler->maxArrayCount(iname);
+ if (d->m_lookupRequests.value(iname, -1) == maxArrayCount) {
showMessage(QString("IGNORING REPEATED REQUEST TO EXPAND " + iname));
- WatchHandler *handler = watchHandler();
WatchItem *item = handler->findItem(iname);
QTC_CHECK(item);
WatchModelBase *model = handler->model();
@@ -2382,7 +2378,7 @@ void DebuggerEngine::updateItem(const QString &iname)
}
// We could legitimately end up here after expanding + closing + re-expaning an item.
}
- d->m_lookupRequests.insert(iname);
+ d->m_lookupRequests[iname] = maxArrayCount;
UpdateParameters params;
params.partialVariable = iname;
@@ -2591,6 +2587,7 @@ bool DebuggerRunParameters::isCppDebugging() const
return cppEngineType == GdbEngineType
|| cppEngineType == LldbEngineType
|| cppEngineType == CdbEngineType
+ || cppEngineType == DapEngineType
|| cppEngineType == UvscEngineType;
}
@@ -2651,7 +2648,7 @@ static void createNewDock(QWidget *widget)
dockWidget->show();
}
-void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols &symbols)
+void DebuggerEngine::showModuleSymbols(const FilePath &moduleName, const Symbols &symbols)
{
auto w = new QTreeWidget;
w->setUniformRowHeights(true);
@@ -2659,7 +2656,7 @@ void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols
w->setRootIsDecorated(false);
w->setAlternatingRowColors(true);
w->setSortingEnabled(true);
- w->setObjectName("Symbols." + moduleName);
+ w->setObjectName("Symbols." + moduleName.toFSPathString());
QStringList header;
header.append(Tr::tr("Symbol"));
header.append(Tr::tr("Address"));
@@ -2667,7 +2664,7 @@ void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols
header.append(Tr::tr("Section"));
header.append(Tr::tr("Name"));
w->setHeaderLabels(header);
- w->setWindowTitle(Tr::tr("Symbols in \"%1\"").arg(moduleName));
+ w->setWindowTitle(Tr::tr("Symbols in \"%1\"").arg(moduleName.toUserOutput()));
for (const Symbol &s : symbols) {
auto it = new QTreeWidgetItem;
it->setData(0, Qt::DisplayRole, s.name);
@@ -2680,7 +2677,7 @@ void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols
createNewDock(w);
}
-void DebuggerEngine::showModuleSections(const QString &moduleName, const Sections &sections)
+void DebuggerEngine::showModuleSections(const FilePath &moduleName, const Sections &sections)
{
auto w = new QTreeWidget;
w->setUniformRowHeights(true);
@@ -2688,7 +2685,7 @@ void DebuggerEngine::showModuleSections(const QString &moduleName, const Section
w->setRootIsDecorated(false);
w->setAlternatingRowColors(true);
w->setSortingEnabled(true);
- w->setObjectName("Sections." + moduleName);
+ w->setObjectName("Sections." + moduleName.toFSPathString());
QStringList header;
header.append(Tr::tr("Name"));
header.append(Tr::tr("From"));
@@ -2696,7 +2693,7 @@ void DebuggerEngine::showModuleSections(const QString &moduleName, const Section
header.append(Tr::tr("Address"));
header.append(Tr::tr("Flags"));
w->setHeaderLabels(header);
- w->setWindowTitle(Tr::tr("Sections in \"%1\"").arg(moduleName));
+ w->setWindowTitle(Tr::tr("Sections in \"%1\"").arg(moduleName.toUserOutput()));
for (const Section &s : sections) {
auto it = new QTreeWidgetItem;
it->setData(0, Qt::DisplayRole, s.name);
@@ -2725,9 +2722,11 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
&& rp.toolChainAbi.osFlavor() != Abi::AndroidLinuxFlavor;
bool warnOnInappropriateDebugger = false;
QString detailedWarning;
+ auto shouldAskAgain = CheckableMessageBox::make_decider(coreSettings,
+ warnOnInappropriateDebuggerKey);
switch (rp.toolChainAbi.binaryFormat()) {
case Abi::PEFormat: {
- if (CheckableMessageBox::shouldAskAgain(coreSettings, warnOnInappropriateDebuggerKey)) {
+ if (CheckableMessageBox::shouldAskAgain(shouldAskAgain)) {
QString preferredDebugger;
if (rp.toolChainAbi.osFlavor() == Abi::WindowsMSysFlavor) {
if (rp.cppEngineType == CdbEngineType)
@@ -2767,7 +2766,7 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
break;
}
case Abi::ElfFormat: {
- if (CheckableMessageBox::shouldAskAgain(coreSettings, warnOnInappropriateDebuggerKey)) {
+ if (CheckableMessageBox::shouldAskAgain(shouldAskAgain)) {
if (rp.cppEngineType == CdbEngineType) {
warnOnInappropriateDebugger = true;
detailedWarning = Tr::tr(
@@ -2873,16 +2872,14 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp)
return;
}
if (warnOnInappropriateDebugger) {
- CheckableMessageBox::doNotShowAgainInformation(
+ CheckableMessageBox::information(
Core::ICore::dialogParent(),
Tr::tr("Warning"),
- Tr::tr(
- "The selected debugger may be inappropriate for the inferior.\n"
- "Examining symbols and setting breakpoints by file name and line number "
- "may fail.\n")
+ Tr::tr("The selected debugger may be inappropriate for the inferior.\n"
+ "Examining symbols and setting breakpoints by file name and line number "
+ "may fail.\n")
+ '\n' + detailedWarning,
- Core::ICore::settings(),
- warnOnInappropriateDebuggerKey);
+ shouldAskAgain);
} else if (warnOnRelease) {
AsynchronousMessageBox::information(Tr::tr("Warning"),
Tr::tr("This does not seem to be a \"Debug\" build.\n"
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index 422d4eb788..ac91ecb644 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -7,12 +7,14 @@
#include "debuggerconstants.h"
#include "debuggerprotocol.h"
#include "breakhandler.h"
-#include "projectexplorer/abi.h"
#include "threadshandler.h"
#include <coreplugin/icontext.h>
+
+#include <projectexplorer/abi.h>
#include <projectexplorer/devicesupport/idevicefwd.h>
#include <projectexplorer/runcontrol.h>
+
#include <texteditor/textmark.h>
#include <utils/filepath.h>
@@ -182,7 +184,6 @@ public:
QStringList validationErrors;
- Utils::FilePath dumperPath;
int fallbackQtVersion = 0x50200;
// Common debugger constants.
@@ -303,11 +304,11 @@ public:
virtual void reloadModules();
virtual void examineModules();
- virtual void loadSymbols(const QString &moduleName);
+ virtual void loadSymbols(const Utils::FilePath &moduleName);
virtual void loadSymbolsForStack();
virtual void loadAllSymbols();
- virtual void requestModuleSymbols(const QString &moduleName);
- virtual void requestModuleSections(const QString &moduleName);
+ virtual void requestModuleSymbols(const Utils::FilePath &moduleName);
+ virtual void requestModuleSections(const Utils::FilePath &moduleName);
virtual void reloadRegisters();
virtual void reloadPeripheralRegisters();
@@ -452,8 +453,8 @@ public:
void openMemoryEditor();
- static void showModuleSymbols(const QString &moduleName, const QVector<Symbol> &symbols);
- static void showModuleSections(const QString &moduleName, const QVector<Section> &sections);
+ static void showModuleSymbols(const Utils::FilePath &moduleName, const QVector<Symbol> &symbols);
+ static void showModuleSections(const Utils::FilePath &moduleName, const QVector<Section> &sections);
void handleExecDetach();
void handleExecContinue();
diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp
index a7205d34a1..241ac32ecd 100644
--- a/src/plugins/debugger/debuggeritem.cpp
+++ b/src/plugins/debugger/debuggeritem.cpp
@@ -12,8 +12,8 @@
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
#include <utils/winutils.h>
@@ -43,7 +43,7 @@ const char DEBUGGER_INFORMATION_WORKINGDIRECTORY[] = "WorkingDirectory";
static QString getGdbConfiguration(const FilePath &command, const Environment &sysEnv)
{
// run gdb with the --configuration opion
- QtcProcess proc;
+ Process proc;
proc.setEnvironment(sysEnv);
proc.setCommand({command, {"--configuration"}});
proc.runBlocking();
@@ -116,6 +116,9 @@ void DebuggerItem::createId()
void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *customEnv)
{
+ if (isGeneric())
+ return;
+
// CDB only understands the single-dash -version, whereas GDB and LLDB are
// happy with both -version and --version. So use the "working" -version
// except for the experimental LLDB-MI which insists on --version.
@@ -159,7 +162,7 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust
// hack below tricks it into giving us the information we want.
env.set("QNX_TARGET", QString());
- QtcProcess proc;
+ Process proc;
proc.setEnvironment(env);
proc.setCommand({m_command, {version}});
proc.runBlocking();
@@ -171,8 +174,12 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust
return;
}
m_abis.clear();
+
if (output.contains("gdb")) {
m_engineType = GdbEngineType;
+ // FIXME: HACK while introducing DAP support
+ if (m_command.fileName().endsWith("-dap"))
+ m_engineType = DapEngineType;
// Version
bool isMacGdb, isQnxGdb;
@@ -208,6 +215,7 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust
//! \note If unable to determine the GDB ABI, no ABI is appended to m_abis here.
return;
}
+
if (output.contains("lldb") || output.startsWith("LLDB")) {
m_engineType = LldbEngineType;
m_abis = Abi::abisOfBinary(m_command);
@@ -275,6 +283,8 @@ QString DebuggerItem::engineTypeName() const
return QLatin1String("CDB");
case LldbEngineType:
return QLatin1String("LLDB");
+ case DapEngineType:
+ return QLatin1String("DAP");
case UvscEngineType:
return QLatin1String("UVSC");
default:
@@ -282,6 +292,16 @@ QString DebuggerItem::engineTypeName() const
}
}
+void DebuggerItem::setGeneric(bool on)
+{
+ m_detectionSource = on ? QLatin1String("Generic") : QLatin1String();
+}
+
+bool DebuggerItem::isGeneric() const
+{
+ return m_detectionSource == "Generic";
+}
+
QStringList DebuggerItem::abiNames() const
{
QStringList list;
@@ -297,13 +317,15 @@ QDateTime DebuggerItem::lastModified() const
QIcon DebuggerItem::decoration() const
{
+ if (isGeneric())
+ return {};
if (m_engineType == NoEngineType)
return Icons::CRITICAL.icon();
if (!m_command.isExecutableFile())
return Icons::WARNING.icon();
if (!m_workingDirectory.isEmpty() && !m_workingDirectory.isDir())
return Icons::WARNING.icon();
- return QIcon();
+ return {};
}
QString DebuggerItem::validityMessage() const
diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h
index 1edaf283ca..95993665b6 100644
--- a/src/plugins/debugger/debuggeritem.h
+++ b/src/plugins/debugger/debuggeritem.h
@@ -8,7 +8,7 @@
#include <projectexplorer/abi.h>
-#include <utils/fileutils.h>
+#include <utils/filepath.h>
#include <utils/environment.h>
#include <QDateTime>
@@ -84,6 +84,9 @@ public:
QString detectionSource() const { return m_detectionSource; }
void setDetectionSource(const QString &source) { m_detectionSource = source; }
+ bool isGeneric() const;
+ void setGeneric(bool on);
+
static bool addAndroidLldbPythonEnv(const Utils::FilePath &lldbCmd, Utils::Environment &env);
private:
diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp
index b379255416..aca5a97635 100644
--- a/src/plugins/debugger/debuggeritemmanager.cpp
+++ b/src/plugins/debugger/debuggeritemmanager.cpp
@@ -9,8 +9,6 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
-#include <extensionsystem/pluginmanager.h>
-
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorericons.h>
@@ -22,8 +20,8 @@
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <utils/winutils.h>
@@ -92,7 +90,8 @@ static DebuggerItemManagerPrivate *d = nullptr;
class DebuggerItemConfigWidget : public QWidget
{
public:
- explicit DebuggerItemConfigWidget();
+ DebuggerItemConfigWidget();
+
void load(const DebuggerItem *item);
void store() const;
@@ -104,13 +103,18 @@ private:
QLineEdit *m_displayNameLineEdit;
QLineEdit *m_typeLineEdit;
QLabel *m_cdbLabel;
- QLineEdit *m_versionLabel;
PathChooser *m_binaryChooser;
- PathChooser *m_workingDirectoryChooser;
- QLineEdit *m_abis;
bool m_autodetected = false;
+ bool m_generic = false;
DebuggerEngineType m_engineType = NoEngineType;
QVariant m_id;
+
+ QLabel *m_abisLabel;
+ QLineEdit *m_abis;
+ QLabel *m_versionLabel;
+ QLineEdit *m_version;
+ QLabel *m_workingDirectoryLabel;
+ PathChooser *m_workingDirectoryChooser;
};
// --------------------------------------------------------------------------
@@ -170,10 +174,11 @@ class DebuggerItemModel : public TreeModel<TreeItem, StaticTreeItem, DebuggerTre
{
public:
DebuggerItemModel();
+ enum { Generic, AutoDetected, Manual };
QModelIndex lastIndex() const;
void setCurrentIndex(const QModelIndex &index);
- void addDebugger(const DebuggerItem &item, bool changed = false);
+ DebuggerTreeItem *addDebugger(const DebuggerItem &item, bool changed = false);
void updateDebugger(const DebuggerItem &item);
void apply();
void cancel();
@@ -202,17 +207,40 @@ const DebuggerItem *findDebugger(const Predicate &pred)
DebuggerItemModel::DebuggerItemModel()
{
setHeader({Tr::tr("Name"), Tr::tr("Path"), Tr::tr("Type")});
- rootItem()->appendChild(
- new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()},
- {ProjectExplorer::Constants::msgAutoDetectedToolTip()}));
+
+ auto generic = new StaticTreeItem(Tr::tr("Generic"));
+ auto autoDetected = new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()},
+ {ProjectExplorer::Constants::msgAutoDetectedToolTip()});
+ rootItem()->appendChild(generic);
+ rootItem()->appendChild(autoDetected);
rootItem()->appendChild(new StaticTreeItem(ProjectExplorer::Constants::msgManual()));
+
+ DebuggerItem genericGdb(QVariant("gdb"));
+ genericGdb.setAutoDetected(true);
+ genericGdb.setGeneric(true);
+ genericGdb.setEngineType(GdbEngineType);
+ genericGdb.setAbi(Abi());
+ genericGdb.setCommand("gdb");
+ genericGdb.setUnexpandedDisplayName(Tr::tr("%1 from PATH on Build Device").arg("GDB"));
+ generic->appendChild(new DebuggerTreeItem(genericGdb, false));
+
+ DebuggerItem genericLldb(QVariant("lldb"));
+ genericLldb.setAutoDetected(true);
+ genericLldb.setEngineType(LldbEngineType);
+ genericLldb.setGeneric(true);
+ genericLldb.setAbi(Abi());
+ genericLldb.setCommand("lldb");
+ genericLldb.setUnexpandedDisplayName(Tr::tr("%1 from PATH on Build Device").arg("LLDB"));
+ generic->appendChild(new DebuggerTreeItem(genericLldb, false));
}
-void DebuggerItemModel::addDebugger(const DebuggerItem &item, bool changed)
+DebuggerTreeItem *DebuggerItemModel::addDebugger(const DebuggerItem &item, bool changed)
{
- QTC_ASSERT(item.id().isValid(), return);
- int group = item.isAutoDetected() ? 0 : 1;
- rootItem()->childAt(group)->appendChild(new DebuggerTreeItem(item, changed));
+ QTC_ASSERT(item.id().isValid(), return {});
+ int group = item.isGeneric() ? Generic : (item.isAutoDetected() ? AutoDetected : Manual);
+ auto treeItem = new DebuggerTreeItem(item, changed);
+ rootItem()->childAt(group)->appendChild(treeItem);
+ return treeItem;
}
void DebuggerItemModel::updateDebugger(const DebuggerItem &item)
@@ -301,6 +329,7 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
});
m_binaryChooser->setAllowPathFromDevice(true);
+ m_workingDirectoryLabel = new QLabel(Tr::tr("ABIs:"));
m_workingDirectoryChooser = new PathChooser(this);
m_workingDirectoryChooser->setExpectedKind(PathChooser::Directory);
m_workingDirectoryChooser->setMinimumWidth(400);
@@ -310,10 +339,12 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
m_cdbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
m_cdbLabel->setOpenExternalLinks(true);
- m_versionLabel = new QLineEdit(this);
- m_versionLabel->setPlaceholderText(Tr::tr("Unknown"));
- m_versionLabel->setEnabled(false);
+ m_versionLabel = new QLabel(Tr::tr("Version:"));
+ m_version = new QLineEdit(this);
+ m_version->setPlaceholderText(Tr::tr("Unknown"));
+ m_version->setEnabled(false);
+ m_abisLabel = new QLabel(Tr::tr("Working directory:"));
m_abis = new QLineEdit(this);
m_abis->setEnabled(false);
@@ -323,9 +354,9 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
formLayout->addRow(m_cdbLabel);
formLayout->addRow(new QLabel(Tr::tr("Path:")), m_binaryChooser);
formLayout->addRow(new QLabel(Tr::tr("Type:")), m_typeLineEdit);
- formLayout->addRow(new QLabel(Tr::tr("ABIs:")), m_abis);
- formLayout->addRow(new QLabel(Tr::tr("Version:")), m_versionLabel);
- formLayout->addRow(new QLabel(Tr::tr("Working directory:")), m_workingDirectoryChooser);
+ formLayout->addRow(m_abisLabel, m_abis);
+ formLayout->addRow(m_versionLabel, m_version);
+ formLayout->addRow(m_workingDirectoryLabel, m_workingDirectoryChooser);
connect(m_binaryChooser, &PathChooser::textChanged,
this, &DebuggerItemConfigWidget::binaryPathHasChanged);
@@ -337,21 +368,24 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget()
DebuggerItem DebuggerItemConfigWidget::item() const
{
+ static const QRegularExpression noAbi("[^A-Za-z0-9-_]+");
+
DebuggerItem item(m_id);
item.setUnexpandedDisplayName(m_displayNameLineEdit->text());
item.setCommand(m_binaryChooser->filePath());
item.setWorkingDirectory(m_workingDirectoryChooser->filePath());
item.setAutoDetected(m_autodetected);
Abis abiList;
- const QStringList abis = m_abis->text().split(QRegularExpression("[^A-Za-z0-9-_]+"));
+ const QStringList abis = m_abis->text().split(noAbi);
for (const QString &a : abis) {
if (a.isNull())
continue;
abiList << Abi::fromString(a);
}
item.setAbis(abiList);
- item.setVersion(m_versionLabel->text());
+ item.setVersion(m_version->text());
item.setEngineType(m_engineType);
+ item.setGeneric(m_generic);
return item;
}
@@ -373,6 +407,7 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
return;
// Set values:
+ m_generic = item->isGeneric();
m_autodetected = item->isAutoDetected();
m_displayNameLineEdit->setEnabled(!item->isAutoDetected());
@@ -382,6 +417,15 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
m_binaryChooser->setReadOnly(item->isAutoDetected());
m_binaryChooser->setFilePath(item->command());
+ m_binaryChooser->setExpectedKind(m_generic ? PathChooser::Any : PathChooser::ExistingCommand);
+
+ m_abisLabel->setVisible(!m_generic);
+ m_abis->setVisible(!m_generic);
+ m_versionLabel->setVisible(!m_generic);
+ m_version->setVisible(!m_generic);
+ m_workingDirectoryLabel->setVisible(!m_generic);
+ m_workingDirectoryChooser->setVisible(!m_generic);
+
m_workingDirectoryChooser->setReadOnly(item->isAutoDetected());
m_workingDirectoryChooser->setFilePath(item->workingDirectory());
@@ -405,7 +449,7 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
m_cdbLabel->setText(text);
m_cdbLabel->setVisible(!text.isEmpty());
m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand));
- m_versionLabel->setText(item->version());
+ m_version->setText(item->version());
setAbis(item->abiNames());
m_engineType = item->engineType();
m_id = item->id();
@@ -417,16 +461,18 @@ void DebuggerItemConfigWidget::binaryPathHasChanged()
if (!m_id.isValid())
return;
- DebuggerItem tmp;
- if (m_binaryChooser->filePath().isExecutableFile()) {
- tmp = item();
- tmp.reinitializeFromFile();
- }
+ if (!m_generic) {
+ DebuggerItem tmp;
+ if (m_binaryChooser->filePath().isExecutableFile()) {
+ tmp = item();
+ tmp.reinitializeFromFile();
+ }
- setAbis(tmp.abiNames());
- m_versionLabel->setText(tmp.version());
- m_engineType = tmp.engineType();
- m_typeLineEdit->setText(tmp.engineTypeName());
+ setAbis(tmp.abiNames());
+ m_version->setText(tmp.version());
+ m_engineType = tmp.engineType();
+ m_typeLineEdit->setText(tmp.engineTypeName());
+ }
store();
}
@@ -534,8 +580,10 @@ void DebuggerConfigWidget::cloneDebugger()
newItem.setUnexpandedDisplayName(d->uniqueDisplayName(Tr::tr("Clone of %1").arg(item->displayName())));
newItem.reinitializeFromFile();
newItem.setAutoDetected(false);
- d->m_model->addDebugger(newItem, true);
- m_debuggerView->setCurrentIndex(d->m_model->lastIndex());
+ newItem.setGeneric(item->isGeneric());
+ newItem.setEngineType(item->engineType());
+ auto addedItem = d->m_model->addDebugger(newItem, true);
+ m_debuggerView->setCurrentIndex(d->m_model->indexForItem(addedItem));
}
void DebuggerConfigWidget::addDebugger()
@@ -545,8 +593,8 @@ void DebuggerConfigWidget::addDebugger()
item.setEngineType(NoEngineType);
item.setUnexpandedDisplayName(d->uniqueDisplayName(Tr::tr("New Debugger")));
item.setAutoDetected(false);
- d->m_model->addDebugger(item, true);
- m_debuggerView->setCurrentIndex(d->m_model->lastIndex());
+ auto addedItem = d->m_model->addDebugger(item, true);
+ m_debuggerView->setCurrentIndex(d->m_model->indexForItem(addedItem));
}
void DebuggerConfigWidget::removeDebugger()
@@ -712,7 +760,7 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s
FilePaths suspects;
if (searchPaths.front().osType() == OsTypeMac) {
- QtcProcess proc;
+ Process proc;
proc.setTimeoutS(2);
proc.setCommand({"xcrun", {"--find", "lldb"}});
proc.runBlocking();
@@ -763,6 +811,14 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s
item.setUnexpandedDisplayName(name.arg(item.engineTypeName()).arg(command.toUserOutput()));
m_model->addDebugger(item);
logMessages.append(Tr::tr("Found: \"%1\"").arg(command.toUserOutput()));
+
+ if (item.engineType() == GdbEngineType) {
+ if (item.version().startsWith("GNU gdb (GDB) 14.0.50.2023")) {
+ // FIXME: Use something more robust
+ item.setEngineType(DapEngineType);
+ m_model->addDebugger(item);
+ }
+ }
}
if (logMessage)
*logMessage = logMessages.join('\n');
@@ -820,7 +876,6 @@ DebuggerItemManagerPrivate::DebuggerItemManagerPrivate()
d = this;
m_model = new DebuggerItemModel;
m_optionsPage = new DebuggerOptionsPage;
- ExtensionSystem::PluginManager::addObject(m_optionsPage);
}
void DebuggerItemManagerPrivate::extensionsInitialized()
@@ -830,7 +885,6 @@ void DebuggerItemManagerPrivate::extensionsInitialized()
DebuggerItemManagerPrivate::~DebuggerItemManagerPrivate()
{
- ExtensionSystem::PluginManager::removeObject(m_optionsPage);
delete m_optionsPage;
delete m_model;
}
@@ -932,6 +986,8 @@ void DebuggerItemManagerPrivate::saveDebuggers()
int count = 0;
forAllDebuggers([&count, &data](DebuggerItem &item) {
+ if (item.isGeneric()) // do not store generic debuggers, these get added automatically
+ return;
if (item.isValid() && item.engineType() != NoEngineType) {
QVariantMap tmp = item.toMap();
if (!tmp.isEmpty()) {
diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp
index cac42d5a42..b1ba6bf1d5 100644
--- a/src/plugins/debugger/debuggerkitinformation.cpp
+++ b/src/plugins/debugger/debuggerkitinformation.cpp
@@ -60,11 +60,11 @@ public:
}
private:
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_comboBox);
- builder.addItem(m_comboBox);
- builder.addItem(m_manageButton);
+ parent.addItem(m_comboBox);
+ parent.addItem(m_manageButton);
}
void makeReadOnly() override
@@ -228,66 +228,6 @@ void DebuggerKitAspect::setup(Kit *k)
k->setValue(DebuggerKitAspect::id(), bestLevel != DebuggerItem::DoesNotMatch ? bestItem.id() : QVariant());
}
-
-// This handles the upgrade path from 2.8 to 3.0
-void DebuggerKitAspect::fix(Kit *k)
-{
- QTC_ASSERT(k, return);
-
- // This can be Id, binary path, but not "auto" anymore.
- const QVariant rawId = k->value(DebuggerKitAspect::id());
-
- if (rawId.toString().isEmpty()) // No debugger set, that is fine.
- return;
-
- if (rawId.type() == QVariant::String) {
- const DebuggerItem * const item = DebuggerItemManager::findById(rawId);
- if (!item) {
- qWarning("Unknown debugger id %s in kit %s",
- qPrintable(rawId.toString()), qPrintable(k->displayName()));
- k->setValue(DebuggerKitAspect::id(), QVariant());
- setup(k);
- return;
- }
-
- Abi kitAbi;
- if (ToolChainKitAspect::toolChains(k).isEmpty()) {
- if (DeviceTypeKitAspect::deviceTypeId(k)
- != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
- return;
- }
- kitAbi = Abi(Abi::UnknownArchitecture, Abi::hostAbi().os());
- } else {
- kitAbi = ToolChainKitAspect::targetAbi(k);
- }
- if (item->matchTarget(kitAbi) != DebuggerItem::DoesNotMatch)
- return;
- k->setValue(DebuggerKitAspect::id(), QVariant());
- setup(k);
- return; // All fine (now).
- }
-
- QMap<QString, QVariant> map = rawId.toMap();
- QString binary = map.value("Binary").toString();
- if (binary == "auto") {
- // This should not happen as "auto" is handled by setup() already.
- QTC_CHECK(false);
- k->setValue(DebuggerKitAspect::id(), QVariant());
- return;
- }
-
- FilePath fileName = FilePath::fromUserInput(binary);
- const DebuggerItem *item = DebuggerItemManager::findByCommand(fileName);
- if (!item) {
- qWarning("Debugger command %s invalid in kit %s",
- qPrintable(binary), qPrintable(k->displayName()));
- k->setValue(DebuggerKitAspect::id(), QVariant());
- return;
- }
-
- k->setValue(DebuggerKitAspect::id(), item->id());
-}
-
// Check the configuration errors and return a flag mask. Provide a quick check and
// a verbose one with a list of errors.
@@ -299,15 +239,15 @@ DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(co
if (!item)
return NoDebugger;
- if (item->command().isEmpty())
+ const FilePath debugger = item->command();
+ if (debugger.isEmpty())
return NoDebugger;
+ if (debugger.isRelativePath())
+ return NoConfigurationError;
+
ConfigurationErrors result = NoConfigurationError;
- const FilePath debugger = item->command();
- const bool found = debugger.exists() && !debugger.isDir();
- if (!found)
- result |= DebuggerNotFound;
- else if (!debugger.isExecutableFile())
+ if (!debugger.isExecutableFile())
result |= DebuggerNotExecutable;
const Abi tcAbi = ToolChainKitAspect::targetAbi(k);
@@ -318,16 +258,15 @@ DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(co
result |= DebuggerDoesNotMatch;
}
- if (!found) {
- if (item->engineType() == NoEngineType)
- return NoDebugger;
+ if (item->engineType() == NoEngineType)
+ return NoDebugger;
- // We need an absolute path to be able to locate Python on Windows.
- if (item->engineType() == GdbEngineType) {
- if (tcAbi.os() == Abi::WindowsOS && !debugger.isAbsolutePath())
- result |= DebuggerNeedsAbsolutePath;
- }
+ // We need an absolute path to be able to locate Python on Windows.
+ if (item->engineType() == GdbEngineType) {
+ if (tcAbi.os() == Abi::WindowsOS && !debugger.isAbsolutePath())
+ result |= DebuggerNeedsAbsolutePath;
}
+
return result;
}
@@ -342,7 +281,12 @@ Runnable DebuggerKitAspect::runnable(const Kit *kit)
{
Runnable runnable;
if (const DebuggerItem *item = debugger(kit)) {
- runnable.command = CommandLine{item->command()};
+ FilePath cmd = item->command();
+ if (cmd.isRelativePath()) {
+ if (const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit))
+ cmd = buildDevice->searchExecutableInPath(cmd.path());
+ }
+ runnable.command.setExecutable(cmd);
runnable.workingDirectory = item->workingDirectory();
runnable.environment = kit->runEnvironment();
runnable.environment.set("LC_NUMERIC", "C");
diff --git a/src/plugins/debugger/debuggerkitinformation.h b/src/plugins/debugger/debuggerkitinformation.h
index eb6ad1d58a..548f76793b 100644
--- a/src/plugins/debugger/debuggerkitinformation.h
+++ b/src/plugins/debugger/debuggerkitinformation.h
@@ -21,7 +21,6 @@ public:
{ return DebuggerKitAspect::validateDebugger(k); }
void setup(ProjectExplorer::Kit *k) override;
- void fix(ProjectExplorer::Kit *k) override;
static const DebuggerItem *debugger(const ProjectExplorer::Kit *kit);
static ProjectExplorer::Runnable runnable(const ProjectExplorer::Kit *kit);
diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp
index 047fc3ec89..2a5ec866ed 100644
--- a/src/plugins/debugger/debuggermainwindow.cpp
+++ b/src/plugins/debugger/debuggermainwindow.cpp
@@ -162,13 +162,13 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
{
m_centralWidgetStack = new QStackedWidget;
m_statusLabel = new Utils::StatusLabel;
- m_statusLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_statusLabel);
m_statusLabel->setIndent(2 * QFontMetrics(q->font()).horizontalAdvance(QChar('x')));
m_editorPlaceHolder = new EditorManagerPlaceHolder;
m_perspectiveChooser = new QComboBox;
m_perspectiveChooser->setObjectName("PerspectiveChooser");
- m_perspectiveChooser->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_perspectiveChooser);
m_perspectiveChooser->setSizeAdjustPolicy(QComboBox::AdjustToContents);
connect(m_perspectiveChooser, &QComboBox::activated, this, [this](int item) {
Perspective *perspective = Perspective::findPerspective(m_perspectiveChooser->itemData(item).toString());
@@ -201,7 +201,7 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
closeButton->setToolTip(Tr::tr("Leave Debug Mode"));
auto toolbar = new Utils::StyledBar;
- toolbar->setProperty("topBorder", true);
+ toolbar->setProperty(StyleHelper::C_TOP_BORDER, true);
// "Engine switcher" style comboboxes
auto subPerspectiveSwitcher = new QWidget;
@@ -233,8 +233,8 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent)
scrolledToolbar->setFrameStyle(QFrame::NoFrame);
scrolledToolbar->setWidgetResizable(true);
scrolledToolbar->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- scrolledToolbar->setFixedHeight(toolbar->height());
scrolledToolbar->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ StyleHelper::setPanelWidgetSingleRow(scrolledToolbar);
auto dock = new QDockWidget(Tr::tr("Toolbar"), q);
dock->setObjectName("Toolbar");
@@ -507,7 +507,7 @@ QWidget *DebuggerMainWindow::centralWidgetStack()
void DebuggerMainWindow::addSubPerspectiveSwitcher(QWidget *widget)
{
widget->setVisible(false);
- widget->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(widget);
d->m_subPerspectiveSwitcherLayout->addWidget(widget);
}
@@ -810,7 +810,7 @@ QToolButton *PerspectivePrivate::setupToolButton(QAction *action)
{
QTC_ASSERT(action, return nullptr);
auto toolButton = new QToolButton(m_innerToolBar);
- toolButton->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(toolButton);
toolButton->setDefaultAction(action);
toolButton->setToolTip(action->toolTip());
m_innerToolBarLayout->addWidget(toolButton);
@@ -833,7 +833,7 @@ void Perspective::addToolBarWidget(QWidget *widget)
{
QTC_ASSERT(widget, return);
// QStyle::polish is called before it is added to the toolbar, explicitly make it a panel widget
- widget->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(widget);
widget->setParent(d->m_innerToolBar);
d->m_innerToolBarLayout->addWidget(widget);
}
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 8ab67c0bc5..8b14ff0e23 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -67,9 +67,10 @@
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorersettings.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
@@ -1187,7 +1188,7 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments)
setInitialState();
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &DebuggerPluginPrivate::onStartupProjectChanged);
connect(EngineManager::instance(), &EngineManager::engineStateChanged,
this, &DebuggerPluginPrivate::updatePresetState);
@@ -1394,8 +1395,8 @@ void DebuggerPluginPrivate::updatePresetState()
if (m_shuttingDown)
return;
- Project *startupProject = SessionManager::startupProject();
- RunConfiguration *startupRunConfig = SessionManager::startupRunConfiguration();
+ Project *startupProject = ProjectManager::startupProject();
+ RunConfiguration *startupRunConfig = ProjectManager::startupRunConfiguration();
DebuggerEngine *currentEngine = EngineManager::currentEngine();
QString whyNot;
@@ -1997,7 +1998,7 @@ void DebuggerPluginPrivate::aboutToShutdown()
{
m_shuttingDown = true;
- disconnect(SessionManager::instance(), &SessionManager::startupProjectChanged, this, nullptr);
+ disconnect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, nullptr);
m_shutdownTimer.setInterval(0);
m_shutdownTimer.setSingleShot(true);
@@ -2165,7 +2166,7 @@ static bool buildTypeAccepted(QFlags<ToolMode> toolMode, BuildConfiguration::Bui
static BuildConfiguration::BuildType startupBuildType()
{
BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
- if (RunConfiguration *runConfig = SessionManager::startupRunConfiguration()) {
+ if (RunConfiguration *runConfig = ProjectManager::startupRunConfiguration()) {
if (const BuildConfiguration *buildConfig = runConfig->target()->activeBuildConfiguration())
buildType = buildConfig->buildType();
}
@@ -2239,10 +2240,13 @@ bool wantRunTool(ToolMode toolMode, const QString &toolName)
"or otherwise insufficient output.</p><p>"
"Do you want to continue and run the tool in %2 mode?</p></body></html>")
.arg(toolName).arg(currentMode).arg(toolModeString);
- if (Utils::CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
- title, message, ICore::settings(), "AnalyzerCorrectModeWarning")
- != QDialogButtonBox::Yes)
- return false;
+ if (Utils::CheckableMessageBox::question(ICore::dialogParent(),
+ title,
+ message,
+ ICore::settings(),
+ "AnalyzerCorrectModeWarning")
+ != QMessageBox::Yes)
+ return false;
}
return true;
@@ -2335,12 +2339,12 @@ void DebuggerUnitTests::testStateMachine()
QEventLoop loop;
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
&loop, &QEventLoop::quit);
- BuildManager::buildProjectWithDependencies(SessionManager::startupProject());
+ BuildManager::buildProjectWithDependencies(ProjectManager::startupProject());
loop.exec();
ExecuteOnDestruction guard([] { EditorManager::closeAllEditors(false); });
- RunConfiguration *rc = SessionManager::startupRunConfiguration();
+ RunConfiguration *rc = ProjectManager::startupRunConfiguration();
QVERIFY(rc);
auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
index ef80d58a33..4b67e82fd8 100644
--- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
+++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp
@@ -5,6 +5,8 @@
#include "debuggertr.h"
+#include <cppeditor/cppmodelmanager.h>
+
#include <coreplugin/helpmanager.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
@@ -20,6 +22,7 @@
#include <qtsupport/qtbuildaspects.h>
+#include <utils/detailswidget.h>
#include <utils/environment.h>
#include <utils/layoutbuilder.h>
@@ -28,117 +31,10 @@
#include <QLabel>
#include <QTextEdit>
-using namespace Debugger::Internal;
using namespace ProjectExplorer;
using namespace Utils;
namespace Debugger {
-namespace Internal {
-
-enum DebuggerLanguageStatus {
- DisabledLanguage = 0,
- EnabledLanguage,
- AutoEnabledLanguage
-};
-
-class DebuggerLanguageAspect : public BaseAspect
-{
-public:
- DebuggerLanguageAspect() = default;
-
- void addToLayout(Layouting::LayoutBuilder &builder) override;
-
- bool value() const;
- void setValue(bool val);
-
- void setAutoSettingsKey(const QString &settingsKey);
- void setLabel(const QString &label);
- void setInfoLabelText(const QString &text) { m_infoLabelText = text; }
-
- void setClickCallBack(const std::function<void (bool)> &clickCallBack)
- {
- m_clickCallBack = clickCallBack;
- }
-
- void fromMap(const QVariantMap &map) override;
- void toMap(QVariantMap &map) const override;
-
-public:
- DebuggerLanguageStatus m_value = AutoEnabledLanguage;
- bool m_defaultValue = false;
- QString m_label;
- QString m_infoLabelText;
- QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
- QPointer<QLabel> m_infoLabel; // Owned by configuration widget
- QString m_autoSettingsKey;
-
- std::function<void(bool)> m_clickCallBack;
-};
-
-void DebuggerLanguageAspect::addToLayout(Layouting::LayoutBuilder &builder)
-{
- QTC_CHECK(!m_checkBox);
- m_checkBox = new QCheckBox(m_label);
- m_checkBox->setChecked(m_value);
- m_checkBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
-
- QTC_CHECK(m_clickCallBack);
- connect(m_checkBox, &QAbstractButton::clicked, this, m_clickCallBack, Qt::QueuedConnection);
-
- connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
- m_value = m_checkBox->isChecked() ? EnabledLanguage : DisabledLanguage;
- emit changed();
- });
- builder.addItem(QString());
- builder.addItem(m_checkBox.data());
-
- if (!m_infoLabelText.isEmpty()) {
- QTC_CHECK(!m_infoLabel);
- m_infoLabel = new QLabel(m_infoLabelText);
- connect(m_infoLabel, &QLabel::linkActivated, [](const QString &link) {
- Core::HelpManager::showHelpUrl(link);
- });
- builder.addItem(m_infoLabel.data());
- }
-}
-
-void DebuggerLanguageAspect::setAutoSettingsKey(const QString &settingsKey)
-{
- m_autoSettingsKey = settingsKey;
-}
-
-void DebuggerLanguageAspect::fromMap(const QVariantMap &map)
-{
- const bool val = map.value(settingsKey(), false).toBool();
- const bool autoVal = map.value(m_autoSettingsKey, false).toBool();
- m_value = autoVal ? AutoEnabledLanguage : val ? EnabledLanguage : DisabledLanguage;
-}
-
-void DebuggerLanguageAspect::toMap(QVariantMap &data) const
-{
- data.insert(settingsKey(), m_value == EnabledLanguage);
- data.insert(m_autoSettingsKey, m_value == AutoEnabledLanguage);
-}
-
-
-bool DebuggerLanguageAspect::value() const
-{
- return m_value;
-}
-
-void DebuggerLanguageAspect::setValue(bool value)
-{
- m_value = value ? EnabledLanguage : DisabledLanguage;
- if (m_checkBox)
- m_checkBox->setChecked(m_value);
-}
-
-void DebuggerLanguageAspect::setLabel(const QString &label)
-{
- m_label = label;
-}
-
-} // Internal
/*!
\class Debugger::DebuggerRunConfigurationAspect
@@ -151,15 +47,53 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
setDisplayName(Tr::tr("Debugger settings"));
setConfigWidgetCreator([this] {
- Layouting::Form builder;
- builder.addRow(m_cppAspect);
- builder.addRow(m_qmlAspect);
- builder.addRow(m_overrideStartupAspect);
+ Layouting::Grid builder;
+ builder.addRow({m_cppAspect});
+ auto info = new QLabel(
+ Tr::tr("<a href=\""
+ "qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
+ "\">What are the prerequisites?</a>"));
+ builder.addRow({m_qmlAspect, info});
+ connect(info, &QLabel::linkActivated, [](const QString &link) {
+ Core::HelpManager::showHelpUrl(link);
+ });
+ builder.addRow({m_overrideStartupAspect});
static const QString env = qtcEnvironmentVariable("QTC_DEBUGGER_MULTIPROCESS");
if (env.toInt())
- builder.addRow(m_multiProcessAspect);
- return builder.emerge(Layouting::WithoutMargins);
+ builder.addRow({m_multiProcessAspect});
+
+ auto details = new DetailsWidget;
+ details->setState(DetailsWidget::Expanded);
+ auto innerPane = new QWidget;
+ details->setWidget(innerPane);
+ builder.addItem(Layouting::noMargin);
+ builder.attachTo(innerPane);
+
+ const auto setSummaryText = [this, details] {
+ QStringList items;
+ if (m_cppAspect->value() == TriState::Enabled)
+ items.append(Tr::tr("Enable C++ debugger"));
+ else if (m_cppAspect->value() == TriState::Default)
+ items.append(Tr::tr("Try to determine need for C++ debugger"));
+
+ if (m_qmlAspect->value() == TriState::Enabled)
+ items.append(Tr::tr("Enable QML debugger"));
+ else if (m_qmlAspect->value() == TriState::Default)
+ items.append(Tr::tr("Try to determine need for QML debugger"));
+
+ items.append(m_overrideStartupAspect->value().isEmpty()
+ ? Tr::tr("Without additional startup commands")
+ : Tr::tr("With additional startup commands"));
+ details->setSummaryText(items.join(". "));
+ };
+ setSummaryText();
+
+ connect(m_cppAspect, &BaseAspect::changed, this, setSummaryText);
+ connect(m_qmlAspect, &BaseAspect::changed, this, setSummaryText);
+ connect(m_overrideStartupAspect, &BaseAspect::changed, this, setSummaryText);
+
+ return details;
});
addDataExtractor(this, &DebuggerRunConfigurationAspect::useCppDebugger, &Data::useCppDebugger);
@@ -167,29 +101,25 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
addDataExtractor(this, &DebuggerRunConfigurationAspect::useMultiProcess, &Data::useMultiProcess);
addDataExtractor(this, &DebuggerRunConfigurationAspect::overrideStartup, &Data::overrideStartup);
- m_cppAspect = new DebuggerLanguageAspect;
- m_cppAspect->setLabel(Tr::tr("Enable C++"));
+ m_cppAspect = new TriStateAspect(Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic"));
+ m_cppAspect->setLabelText(Tr::tr("C++ debugger:"));
m_cppAspect->setSettingsKey("RunConfiguration.UseCppDebugger");
- m_cppAspect->setAutoSettingsKey("RunConfiguration.UseCppDebuggerAuto");
- m_qmlAspect = new DebuggerLanguageAspect;
- m_qmlAspect->setLabel(Tr::tr("Enable QML"));
+ m_qmlAspect = new TriStateAspect(Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic"));
+ m_qmlAspect->setLabelText(Tr::tr("QML debugger:"));
m_qmlAspect->setSettingsKey("RunConfiguration.UseQmlDebugger");
- m_qmlAspect->setAutoSettingsKey("RunConfiguration.UseQmlDebuggerAuto");
- m_qmlAspect->setInfoLabelText(Tr::tr("<a href=\""
- "qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
- "\">What are the prerequisites?</a>"));
// Make sure at least one of the debuggers is set to be active.
- m_cppAspect->setClickCallBack([this](bool on) {
- if (!on && !m_qmlAspect->value())
- m_qmlAspect->setValue(true);
+ connect(m_cppAspect, &TriStateAspect::changed, this, [this]{
+ if (m_cppAspect->value() == TriState::Disabled && m_qmlAspect->value() == TriState::Disabled)
+ m_qmlAspect->setValue(TriState::Default);
});
- m_qmlAspect->setClickCallBack([this](bool on) {
- if (!on && !m_cppAspect->value())
- m_cppAspect->setValue(true);
+ connect(m_qmlAspect, &TriStateAspect::changed, this, [this]{
+ if (m_qmlAspect->value() == TriState::Disabled && m_cppAspect->value() == TriState::Disabled)
+ m_cppAspect->setValue(TriState::Default);
});
+
m_multiProcessAspect = new BoolAspect;
m_multiProcessAspect->setSettingsKey("RunConfiguration.UseMultiProcess");
m_multiProcessAspect->setLabel(Tr::tr("Enable Debugging of Subprocesses"),
@@ -211,23 +141,37 @@ DebuggerRunConfigurationAspect::~DebuggerRunConfigurationAspect()
void DebuggerRunConfigurationAspect::setUseQmlDebugger(bool value)
{
- m_qmlAspect->setValue(value);
+ m_qmlAspect->setValue(value ? TriState::Enabled : TriState::Disabled);
}
bool DebuggerRunConfigurationAspect::useCppDebugger() const
{
- if (m_cppAspect->m_value == AutoEnabledLanguage)
+ if (m_cppAspect->value() == TriState::Default)
return m_target->project()->projectLanguages().contains(
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
- return m_cppAspect->m_value == EnabledLanguage;
+ return m_cppAspect->value() == TriState::Enabled;
+}
+
+static bool projectHasQmlDefines(ProjectExplorer::Project *project)
+{
+ auto projectInfo = CppEditor::CppModelManager::instance()->projectInfo(project);
+ QTC_ASSERT(projectInfo, return false);
+ return Utils::anyOf(projectInfo->projectParts(),
+ [](const CppEditor::ProjectPart::ConstPtr &part){
+ return Utils::anyOf(part->projectMacros, [](const Macro &macro){
+ return macro.key == "QT_DECLARATIVE_LIB"
+ || macro.key == "QT_QUICK_LIB"
+ || macro.key == "QT_QML_LIB";
+ });
+ });
}
bool DebuggerRunConfigurationAspect::useQmlDebugger() const
{
- if (m_qmlAspect->m_value == AutoEnabledLanguage) {
+ if (m_qmlAspect->value() == TriState::Default) {
const Core::Context languages = m_target->project()->projectLanguages();
if (!languages.contains(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID))
- return false;
+ return projectHasQmlDefines(m_target->project());
//
// Try to find a build configuration to check whether qml debugging is enabled there
@@ -238,7 +182,7 @@ bool DebuggerRunConfigurationAspect::useQmlDebugger() const
return !languages.contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
}
- return m_qmlAspect->m_value == EnabledLanguage;
+ return m_qmlAspect->value() == TriState::Enabled;
}
bool DebuggerRunConfigurationAspect::useMultiProcess() const
@@ -272,12 +216,23 @@ void DebuggerRunConfigurationAspect::toMap(QVariantMap &map) const
m_qmlAspect->toMap(map);
m_multiProcessAspect->toMap(map);
m_overrideStartupAspect->toMap(map);
+
+ // compatibility to old settings
+ map.insert("RunConfiguration.UseCppDebuggerAuto", m_cppAspect->value() == TriState::Default);
+ map.insert("RunConfiguration.UseQmlDebuggerAuto", m_qmlAspect->value() == TriState::Default);
}
void DebuggerRunConfigurationAspect::fromMap(const QVariantMap &map)
{
m_cppAspect->fromMap(map);
m_qmlAspect->fromMap(map);
+
+ // respect old project settings
+ if (map.value("RunConfiguration.UseCppDebuggerAuto", false).toBool())
+ m_cppAspect->setValue(TriState::Default);
+ if (map.value("RunConfiguration.UseQmlDebuggerAuto", false).toBool())
+ m_qmlAspect->setValue(TriState::Default);
+
m_multiProcessAspect->fromMap(map);
m_overrideStartupAspect->fromMap(map);
}
diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.h b/src/plugins/debugger/debuggerrunconfigurationaspect.h
index 2534d4f81c..53b9c1f54d 100644
--- a/src/plugins/debugger/debuggerrunconfigurationaspect.h
+++ b/src/plugins/debugger/debuggerrunconfigurationaspect.h
@@ -10,8 +10,6 @@
namespace Debugger {
-namespace Internal { class DebuggerLanguageAspect; }
-
class DEBUGGER_EXPORT DebuggerRunConfigurationAspect
: public ProjectExplorer::GlobalOrProjectAspect
{
@@ -40,8 +38,8 @@ public:
};
private:
- Internal::DebuggerLanguageAspect *m_cppAspect;
- Internal::DebuggerLanguageAspect *m_qmlAspect;
+ Utils::TriStateAspect *m_cppAspect;
+ Utils::TriStateAspect *m_qmlAspect;
Utils::BoolAspect *m_multiProcessAspect;
Utils::StringAspect *m_overrideStartupAspect;
ProjectExplorer::Target *m_target;
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index 8b6ac29c99..17da601429 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -23,8 +23,9 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
@@ -34,8 +35,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <utils/temporaryfile.h>
#include <utils/url.h>
@@ -69,6 +70,7 @@ DebuggerEngine *createPdbEngine();
DebuggerEngine *createQmlEngine();
DebuggerEngine *createLldbEngine();
DebuggerEngine *createUvscEngine();
+DebuggerEngine *createDapEngine();
static QString noEngineMessage()
{
@@ -107,7 +109,7 @@ private:
}
m_coreUnpackProcess.setWorkingDirectory(TemporaryDirectory::masterDirectoryFilePath());
- connect(&m_coreUnpackProcess, &QtcProcess::done, this, [this] {
+ connect(&m_coreUnpackProcess, &Process::done, this, [this] {
if (m_coreUnpackProcess.error() == QProcess::UnknownError) {
reportStopped();
return;
@@ -130,7 +132,7 @@ private:
appendMessage(msg.arg(m_tempCoreFilePath.toUserOutput()), LogMessageFormat);
m_tempCoreFile.setFileName(m_tempCoreFilePath.path());
m_tempCoreFile.open(QFile::WriteOnly);
- connect(&m_coreUnpackProcess, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_coreUnpackProcess, &Process::readyReadStandardOutput, this, [this] {
m_tempCoreFile.write(m_coreUnpackProcess.readAllRawStandardOutput());
});
m_coreUnpackProcess.setCommand({"gzip", {"-c", "-d", m_coreFilePath.path()}});
@@ -146,7 +148,7 @@ private:
QFile m_tempCoreFile;
FilePath m_coreFilePath;
FilePath m_tempCoreFilePath;
- QtcProcess m_coreUnpackProcess;
+ Process m_coreUnpackProcess;
};
class DebuggerRunToolPrivate
@@ -182,8 +184,8 @@ void DebuggerRunTool::setStartMode(DebuggerStartMode startMode)
// FIXME: This is horribly wrong.
// get files from all the projects in the session
- QList<Project *> projects = SessionManager::projects();
- if (Project *startupProject = SessionManager::startupProject()) {
+ QList<Project *> projects = ProjectManager::projects();
+ if (Project *startupProject = ProjectManager::startupProject()) {
// startup project first
projects.removeOne(startupProject);
projects.insert(0, startupProject);
@@ -508,6 +510,9 @@ void DebuggerRunTool::start()
case UvscEngineType:
m_engine = createUvscEngine();
break;
+ case DapEngineType:
+ m_engine = createDapEngine();
+ break;
default:
if (!m_runParameters.isQmlDebugging) {
reportFailure(noEngineMessage() + '\n' +
@@ -611,14 +616,12 @@ void DebuggerRunTool::start()
showMessage(warningMessage, LogWarning);
- static bool checked = true;
- if (checked)
- CheckableMessageBox::information(Core::ICore::dialogParent(),
- Tr::tr("Debugger"),
- warningMessage,
- Tr::tr("&Show this message again."),
- &checked,
- QDialogButtonBox::Ok);
+ static bool doNotShowAgain = false;
+ CheckableMessageBox::information(Core::ICore::dialogParent(),
+ Tr::tr("Debugger"),
+ warningMessage,
+ doNotShowAgain,
+ QMessageBox::Ok);
}
}
@@ -885,8 +888,8 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
Runnable inferior = runControl->runnable();
const FilePath &debuggerExecutable = m_runParameters.debugger.command.executable();
- inferior.command.setExecutable(inferior.command.executable().onDevice(debuggerExecutable));
- inferior.workingDirectory = inferior.workingDirectory.onDevice(debuggerExecutable);
+ inferior.command.setExecutable(debuggerExecutable.withNewMappedPath(inferior.command.executable()));
+ inferior.workingDirectory = debuggerExecutable.withNewMappedPath(inferior.workingDirectory);
// Normalize to work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch'...)
inferior.workingDirectory = inferior.workingDirectory.normalizedPathName();
m_runParameters.inferior = inferior;
@@ -900,6 +903,9 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
if (Project *project = runControl->project()) {
m_runParameters.projectSourceDirectory = project->projectDirectory();
m_runParameters.projectSourceFiles = project->files(Project::SourceFiles);
+ } else {
+ m_runParameters.projectSourceDirectory = m_runParameters.debugger.command.executable().parentDir();
+ m_runParameters.projectSourceFiles.clear();
}
m_runParameters.toolChainAbi = ToolChainKitAspect::targetAbi(kit);
@@ -924,7 +930,6 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
}
}
- m_runParameters.dumperPath = Core::ICore::resourcePath("debugger/");
if (QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(kit)) {
const QVersionNumber qtVersion = baseQtVersion->qtVersion();
m_runParameters.fallbackQtVersion = 0x10000 * qtVersion.majorVersion()
@@ -1036,14 +1041,37 @@ DebugServerRunner::DebugServerRunner(RunControl *runControl, DebugServerPortsGat
cmd.setExecutable(commandLine().executable()); // FIXME: Case should not happen?
} else {
cmd.setExecutable(runControl->device()->debugServerPath());
- if (cmd.isEmpty())
- cmd.setExecutable(runControl->device()->filePath("gdbserver"));
+
+ if (cmd.isEmpty()) {
+ if (runControl->device()->osType() == Utils::OsTypeMac) {
+ const FilePath debugServerLocation = runControl->device()->filePath(
+ "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/"
+ "Resources/debugserver");
+
+ if (debugServerLocation.isExecutableFile()) {
+ cmd.setExecutable(debugServerLocation);
+ } else {
+ // TODO: In the future it is expected that the debugserver will be
+ // replaced by lldb-server. Remove the check for debug server at that point.
+ const FilePath lldbserver
+ = runControl->device()->filePath("lldb-server").searchInPath();
+ if (lldbserver.isExecutableFile())
+ cmd.setExecutable(lldbserver);
+ }
+ } else {
+ cmd.setExecutable(runControl->device()->filePath("gdbserver"));
+ }
+ }
args.clear();
- if (cmd.executable().toString().contains("lldb-server")) {
+ if (cmd.executable().baseName().contains("lldb-server")) {
args.append("platform");
args.append("--listen");
args.append(QString("*:%1").arg(portsGatherer->gdbServer().port()));
args.append("--server");
+ } else if (cmd.executable().baseName() == "debugserver") {
+ args.append(QString("*:%1").arg(portsGatherer->gdbServer().port()));
+ args.append("--attach");
+ args.append(QString::number(m_pid.pid()));
} else {
// Something resembling gdbserver
if (m_useMulti)
diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
index 33417b55a0..9d3af533bc 100644
--- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
+++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
@@ -12,8 +12,8 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QFileDialog>
@@ -420,7 +420,7 @@ static QString findQtInstallPath(const FilePath &qmakePath)
{
if (qmakePath.isEmpty())
return QString();
- QtcProcess proc;
+ Process proc;
proc.setCommand({qmakePath, {"-query", "QT_INSTALL_HEADERS"}});
proc.start();
if (!proc.waitForFinished()) {
@@ -491,12 +491,12 @@ void SourcePathMapAspect::toMap(QVariantMap &) const
QTC_CHECK(false);
}
-void SourcePathMapAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void SourcePathMapAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!d->m_widget);
d->m_widget = createSubWidget<DebuggerSourcePathMappingWidget>();
d->m_widget->setSourcePathMap(value());
- builder.addRow(d->m_widget.data());
+ parent.addItem(d->m_widget.data());
}
QVariant SourcePathMapAspect::volatileValue() const
diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp
index 70e5235586..21ed34d549 100644
--- a/src/plugins/debugger/debuggertooltipmanager.cpp
+++ b/src/plugins/debugger/debuggertooltipmanager.cpp
@@ -514,7 +514,7 @@ DebuggerToolTipWidget::DebuggerToolTipWidget()
setAttribute(Qt::WA_DeleteOnClose);
isPinned = false;
- const QIcon pinIcon(":/debugger/images/pin.xpm");
+ const QIcon pinIcon = Utils::Icons::PINNED_SMALL.icon();
pinButton = new QToolButton;
pinButton->setIcon(pinIcon);
@@ -529,9 +529,7 @@ DebuggerToolTipWidget::DebuggerToolTipWidget()
auto toolBar = new QToolBar(this);
toolBar->setProperty("_q_custom_style_disabled", QVariant(true));
- const QList<QSize> pinIconSizes = pinIcon.availableSizes();
- if (!pinIconSizes.isEmpty())
- toolBar->setIconSize(pinIconSizes.front());
+ toolBar->setIconSize({12, 12});
toolBar->addWidget(pinButton);
toolBar->addWidget(copyButton);
toolBar->addWidget(titleLabel);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 9f8dbac661..aa84c6d2d7 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -38,9 +38,9 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
@@ -146,13 +146,13 @@ GdbEngine::GdbEngine()
connect(&s.useDynamicType, &BaseAspect::changed,
this, &GdbEngine::reloadLocals);
- connect(&m_gdbProc, &QtcProcess::started,
+ connect(&m_gdbProc, &Process::started,
this, &GdbEngine::handleGdbStarted);
- connect(&m_gdbProc, &QtcProcess::done,
+ connect(&m_gdbProc, &Process::done,
this, &GdbEngine::handleGdbDone);
- connect(&m_gdbProc, &QtcProcess::readyReadStandardOutput,
+ connect(&m_gdbProc, &Process::readyReadStandardOutput,
this, &GdbEngine::readGdbStandardOutput);
- connect(&m_gdbProc, &QtcProcess::readyReadStandardError,
+ connect(&m_gdbProc, &Process::readyReadStandardError,
this, &GdbEngine::readGdbStandardError);
// Output
@@ -470,7 +470,8 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
module.startAddress = 0;
module.endAddress = 0;
module.hostPath = result["host-name"].data();
- module.modulePath = result["target-name"].data();
+ const QString target = result["target-name"].data();
+ module.modulePath = runParameters().inferior.command.executable().withNewPath(target);
module.moduleName = QFileInfo(module.hostPath).baseName();
modulesHandler()->updateModule(module);
} else if (asyncClass == u"library-unloaded") {
@@ -478,7 +479,8 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
// target-name="/usr/lib/libdrm.so.2",
// host-name="/usr/lib/libdrm.so.2"
QString id = result["id"].data();
- modulesHandler()->removeModule(result["target-name"].data());
+ const QString target = result["target-name"].data();
+ modulesHandler()->removeModule(runParameters().inferior.command.executable().withNewPath(target));
progressPing();
showStatusMessage(Tr::tr("Library %1 unloaded.").arg(id), 1000);
} else if (asyncClass == u"thread-group-added") {
@@ -537,6 +539,7 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
ba.remove(pos1, pos3 - pos1 + 1);
GdbMi res;
res.fromString(ba);
+ const FilePath &fileRoot = runParameters().projectSourceDirectory;
BreakHandler *handler = breakHandler();
Breakpoint bp;
for (const GdbMi &bkpt : res) {
@@ -545,13 +548,13 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
// A sub-breakpoint.
QTC_ASSERT(bp, continue);
SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
- loc->params.updateFromGdbOutput(bkpt);
+ loc->params.updateFromGdbOutput(bkpt, fileRoot);
loc->params.type = bp->type();
} else {
// A primary breakpoint.
bp = handler->findBreakpointByResponseId(nr);
if (bp)
- bp->updateFromGdbOutput(bkpt);
+ bp->updateFromGdbOutput(bkpt, fileRoot);
}
}
if (bp)
@@ -567,7 +570,7 @@ void GdbEngine::handleAsyncOutput(const QStringView asyncClass, const GdbMi &res
const QString nr = bkpt["number"].data();
BreakpointParameters br;
br.type = BreakpointByFileAndLine;
- br.updateFromGdbOutput(bkpt);
+ br.updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
handler->handleAlienBreakpoint(nr, br);
}
} else if (asyncClass == u"breakpoint-deleted") {
@@ -762,7 +765,8 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
if (cmd.flags & ConsoleCommand)
cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
cmd.function = QString::number(token) + cmd.function;
- showMessage(cmd.function, LogInput);
+
+ showMessage(cmd.function.left(100), LogInput);
if (m_scheduledTestResponses.contains(token)) {
// Fake response for test cases.
@@ -797,7 +801,7 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
int GdbEngine::commandTimeoutTime() const
{
- const int time = debuggerSettings()->gdbWatchdogTimeout.value();
+ const int time = debuggerSettings()->gdbWatchdogTimeout();
return 1000 * qMax(20, time);
}
@@ -1012,7 +1016,7 @@ void GdbEngine::updateAll()
{
//PENDING_DEBUG("UPDATING ALL\n");
QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk);
- DebuggerCommand cmd(stackCommand(debuggerSettings()->maximalStackDepth.value()));
+ DebuggerCommand cmd(stackCommand(debuggerSettings()->maximalStackDepth()));
cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); };
runCommand(cmd);
stackHandler()->setCurrentIndex(0);
@@ -1026,7 +1030,7 @@ void GdbEngine::handleQuerySources(const DebuggerResponse &response)
{
m_sourcesListUpdating = false;
if (response.resultClass == ResultDone) {
- QMap<QString, QString> oldShortToFull = m_shortToFullName;
+ QMap<QString, FilePath> oldShortToFull = m_shortToFullName;
m_shortToFullName.clear();
m_fullToShortName.clear();
// "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
@@ -1037,7 +1041,7 @@ void GdbEngine::handleQuerySources(const DebuggerResponse &response)
continue;
GdbMi fullName = item["fullname"];
QString file = fileName.data();
- QString full;
+ FilePath full;
if (fullName.isValid()) {
full = cleanupFullName(fullName.data());
m_fullToShortName[full] = file;
@@ -1102,9 +1106,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
// Ignore trap on Windows terminals, which results in
// spurious "* stopped" message.
if (m_expectTerminalTrap) {
- m_expectTerminalTrap = false;
if ((!data.isValid() || !data["reason"].isValid())
&& Abi::hostAbi().os() == Abi::WindowsOS) {
+ m_expectTerminalTrap = false;
showMessage("IGNORING TERMINAL SIGTRAP", LogMisc);
return;
}
@@ -1181,7 +1185,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
const QString nr = data["bkptno"].data();
int lineNumber = 0;
- QString fullName;
+ FilePath fullName;
QString function;
QString language;
if (frame.isValid()) {
@@ -1194,15 +1198,13 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
lineNumber = lineNumberG.toInt();
fullName = cleanupFullName(frame["fullname"].data());
if (fullName.isEmpty())
- fullName = frame["file"].data();
+ fullName = runParameters().projectSourceDirectory.withNewPath(frame["file"].data());
} // found line number
} else {
showMessage("INVALID STOPPED REASON", LogWarning);
}
- const FilePath onDevicePath = FilePath::fromString(fullName).onDevice(
- runParameters().debugger.command.executable());
- const FilePath fileName = onDevicePath.localSource().value_or(onDevicePath);
+ const FilePath fileName = fullName.localSource().value_or(fullName);
if (!nr.isEmpty() && frame.isValid()) {
// Use opportunity to update the breakpoint marker position.
@@ -1420,14 +1422,19 @@ void GdbEngine::handleStop2(const GdbMi &data)
} else if (m_isQnxGdb && name == "0" && meaning == "Signal 0") {
showMessage("SIGNAL 0 CONSIDERED BOGUS.");
} else {
- showMessage("HANDLING SIGNAL " + name);
- if (debuggerSettings()->useMessageBoxForSignals.value() && !isStopperThread)
- if (!showStoppedBySignalMessageBox(meaning, name)) {
- showMessage("SIGNAL RECEIVED WHILE SHOWING SIGNAL MESSAGE");
- return;
- }
- if (!name.isEmpty() && !meaning.isEmpty())
- reasontr = msgStoppedBySignal(meaning, name);
+ if (terminal() && name == "SIGCONT" && m_expectTerminalTrap) {
+ continueInferior();
+ m_expectTerminalTrap = false;
+ } else {
+ showMessage("HANDLING SIGNAL " + name);
+ if (debuggerSettings()->useMessageBoxForSignals.value() && !isStopperThread)
+ if (!showStoppedBySignalMessageBox(meaning, name)) {
+ showMessage("SIGNAL RECEIVED WHILE SHOWING SIGNAL MESSAGE");
+ return;
+ }
+ if (!name.isEmpty() && !meaning.isEmpty())
+ reasontr = msgStoppedBySignal(meaning, name);
+ }
}
}
if (reason.isEmpty())
@@ -1558,50 +1565,47 @@ void GdbEngine::handleExecuteContinue(const DebuggerResponse &response)
}
}
-QString GdbEngine::fullName(const QString &fileName)
+FilePath GdbEngine::fullName(const QString &fileName)
{
if (fileName.isEmpty())
- return QString();
- QTC_ASSERT(!m_sourcesListUpdating, /* */);
- return m_shortToFullName.value(fileName, QString());
+ return {};
+ QTC_CHECK(!m_sourcesListUpdating);
+ return m_shortToFullName.value(fileName, {});
}
-QString GdbEngine::cleanupFullName(const QString &fileName)
+FilePath GdbEngine::cleanupFullName(const QString &fileName)
{
- QString cleanFilePath = fileName;
+ FilePath cleanFilePath =
+ runParameters().projectSourceDirectory.withNewPath(fileName).cleanPath();
// Gdb running on windows often delivers "fullnames" which
// (a) have no drive letter and (b) are not normalized.
if (Abi::hostAbi().os() == Abi::WindowsOS) {
if (fileName.isEmpty())
- return QString();
- QFileInfo fi(fileName);
- if (fi.isReadable())
- cleanFilePath = QDir::cleanPath(fi.absoluteFilePath());
+ return {};
}
if (!debuggerSettings()->autoEnrichParameters.value())
return cleanFilePath;
- const QString sysroot = runParameters().sysRoot.toString();
- if (QFileInfo(cleanFilePath).isReadable())
+ if (cleanFilePath.isReadableFile())
return cleanFilePath;
+
+ const FilePath sysroot = runParameters().sysRoot;
if (!sysroot.isEmpty() && fileName.startsWith('/')) {
- cleanFilePath = sysroot + fileName;
- if (QFileInfo(cleanFilePath).isReadable())
+ cleanFilePath = sysroot.pathAppended(fileName.mid(1));
+ if (cleanFilePath.isReadableFile())
return cleanFilePath;
}
if (m_baseNameToFullName.isEmpty()) {
- FilePath filePath = FilePath::fromString(sysroot + "/usr/src/debug");
+ FilePath filePath = sysroot.pathAppended("/usr/src/debug");
if (filePath.isDir()) {
filePath.iterateDirectory(
[this](const FilePath &filePath) {
QString name = filePath.fileName();
- if (!name.startsWith('.')) {
- QString path = filePath.path();
- m_baseNameToFullName.insert(name, path);
- }
+ if (!name.startsWith('.'))
+ m_baseNameToFullName.insert(name, filePath);
return IterationPolicy::Continue;
},
{{"*"}, QDir::NoFilter, QDirIterator::Subdirectories});
@@ -1611,7 +1615,7 @@ QString GdbEngine::cleanupFullName(const QString &fileName)
cleanFilePath.clear();
const QString base = FilePath::fromUserInput(fileName).fileName();
- QMultiMap<QString, QString>::const_iterator jt = m_baseNameToFullName.constFind(base);
+ auto jt = m_baseNameToFullName.constFind(base);
while (jt != m_baseNameToFullName.constEnd() && jt.key() == base) {
// FIXME: Use some heuristics to find the "best" match.
return jt.value();
@@ -1948,7 +1952,7 @@ void GdbEngine::executeRunToLine(const ContextData &data)
if (data.address)
loc = addressSpec(data.address);
else
- loc = '"' + breakLocation(data.fileName.toString()) + '"' + ':' + QString::number(data.lineNumber);
+ loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.lineNumber);
runCommand({"tbreak " + loc});
runCommand({"continue", NativeCommand|RunRequest, CB(handleExecuteRunToLine)});
@@ -1976,7 +1980,7 @@ void GdbEngine::executeJumpToLine(const ContextData &data)
if (data.address)
loc = addressSpec(data.address);
else
- loc = '"' + breakLocation(data.fileName.toString()) + '"' + ':' + QString::number(data.lineNumber);
+ loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.lineNumber);
runCommand({"tbreak " + loc});
notifyInferiorRunRequested();
@@ -2050,11 +2054,11 @@ void GdbEngine::setTokenBarrier()
//
//////////////////////////////////////////////////////////////////////
-QString GdbEngine::breakLocation(const QString &file) const
+QString GdbEngine::breakLocation(const FilePath &file) const
{
QString where = m_fullToShortName.value(file);
if (where.isEmpty())
- return FilePath::fromString(file).fileName();
+ return file.fileName();
return where;
}
@@ -2078,7 +2082,7 @@ QString GdbEngine::breakpointLocation(const BreakpointParameters &data)
usage = BreakpointUseShortPath;
const QString fileName = usage == BreakpointUseFullPath
- ? data.fileName.toString() : breakLocation(data.fileName.toString());
+ ? data.fileName.path() : breakLocation(data.fileName);
// The argument is simply a C-quoted version of the argument to the
// non-MI "break" command, including the "original" quoting it wants.
return "\"\\\"" + GdbMi::escapeCString(fileName) + "\\\":"
@@ -2092,7 +2096,7 @@ QString GdbEngine::breakpointLocation2(const BreakpointParameters &data)
usage = BreakpointUseShortPath;
const QString fileName = usage == BreakpointUseFullPath
- ? data.fileName.toString() : breakLocation(data.fileName.toString());
+ ? data.fileName.path() : breakLocation(data.fileName);
return GdbMi::escapeCString(fileName) + ':' + QString::number(data.lineNumber);
}
@@ -2105,7 +2109,7 @@ void GdbEngine::handleInsertInterpreterBreakpoint(const DebuggerResponse &respon
notifyBreakpointInsertOk(bp);
} else {
bp->setResponseId(response.data["number"].data());
- bp->updateFromGdbOutput(response.data);
+ bp->updateFromGdbOutput(response.data, runParameters().projectSourceDirectory);
notifyBreakpointInsertOk(bp);
}
}
@@ -2115,7 +2119,7 @@ void GdbEngine::handleInterpreterBreakpointModified(const GdbMi &data)
int modelId = data["modelid"].toInt();
Breakpoint bp = breakHandler()->findBreakpointByModelId(modelId);
QTC_ASSERT(bp, return);
- bp->updateFromGdbOutput(data);
+ bp->updateFromGdbOutput(data, runParameters().projectSourceDirectory);
}
void GdbEngine::handleWatchInsert(const DebuggerResponse &response, const Breakpoint &bp)
@@ -2165,7 +2169,7 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
// A sub-breakpoint.
SubBreakpoint sub = bp->findOrCreateSubBreakpoint(nr);
QTC_ASSERT(sub, return);
- sub->params.updateFromGdbOutput(bkpt);
+ sub->params.updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
sub->params.type = bp->type();
if (usePseudoTracepoints && bp->isTracepoint()) {
sub->params.tracepoint = true;
@@ -2183,7 +2187,7 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
const QString subnr = location["number"].data();
SubBreakpoint sub = bp->findOrCreateSubBreakpoint(subnr);
QTC_ASSERT(sub, return);
- sub->params.updateFromGdbOutput(location);
+ sub->params.updateFromGdbOutput(location, runParameters().projectSourceDirectory);
sub->params.type = bp->type();
if (usePseudoTracepoints && bp->isTracepoint()) {
sub->params.tracepoint = true;
@@ -2194,7 +2198,7 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
// A (the?) primary breakpoint.
bp->setResponseId(nr);
- bp->updateFromGdbOutput(bkpt);
+ bp->updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
if (usePseudoTracepoints && bp->isTracepoint())
bp->setMessage(bp->requestedParameters().message);
}
@@ -2501,7 +2505,7 @@ void GdbEngine::handleTracepointModified(const GdbMi &data)
// A sub-breakpoint.
QTC_ASSERT(bp, continue);
SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
- loc->params.updateFromGdbOutput(bkpt);
+ loc->params.updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
loc->params.type = bp->type();
if (bp->isTracepoint()) {
loc->params.tracepoint = true;
@@ -2511,7 +2515,7 @@ void GdbEngine::handleTracepointModified(const GdbMi &data)
// A primary breakpoint.
bp = handler->findBreakpointByResponseId(nr);
if (bp)
- bp->updateFromGdbOutput(bkpt);
+ bp->updateFromGdbOutput(bkpt, runParameters().projectSourceDirectory);
}
}
QTC_ASSERT(bp, return);
@@ -2612,13 +2616,13 @@ void GdbEngine::insertBreakpoint(const Breakpoint &bp)
"QTC_DEBUGGER_PYTHON_VERBOSE");
const DebuggerSettings &s = *debuggerSettings();
cmd.arg("passexceptions", alwaysVerbose);
- cmd.arg("fancy", s.useDebuggingHelpers.value());
- cmd.arg("autoderef", s.autoDerefPointers.value());
- cmd.arg("dyntype", s.useDynamicType.value());
- cmd.arg("qobjectnames", s.showQObjectNames.value());
+ cmd.arg("fancy", s.useDebuggingHelpers());
+ cmd.arg("autoderef", s.autoDerefPointers());
+ cmd.arg("dyntype", s.useDynamicType());
+ cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("nativemixed", isNativeMixedActive());
- cmd.arg("stringcutoff", s.maximalStringLength.value());
- cmd.arg("displaystringlimit", s.displayStringLimit.value());
+ cmd.arg("stringcutoff", s.maximalStringLength());
+ cmd.arg("displaystringlimit", s.displayStringLimit());
cmd.arg("spec", breakpointLocation2(requested));
cmd.callback = [this, bp](const DebuggerResponse &r) { handleTracepointInsert(r, bp); };
@@ -2783,10 +2787,10 @@ static QString dotEscape(QString str)
return str;
}
-void GdbEngine::loadSymbols(const QString &modulePath)
+void GdbEngine::loadSymbols(const FilePath &modulePath)
{
// FIXME: gdb does not understand quoted names here (tested with 6.8)
- runCommand({"sharedlibrary " + dotEscape(modulePath)});
+ runCommand({"sharedlibrary " + dotEscape(modulePath.path())});
reloadModulesInternal();
reloadStack();
updateLocals();
@@ -2811,7 +2815,7 @@ void GdbEngine::loadSymbolsForStack()
for (const Module &module : modules) {
if (module.startAddress <= frame.address
&& frame.address < module.endAddress) {
- runCommand({"sharedlibrary " + dotEscape(module.modulePath)});
+ runCommand({"sharedlibrary " + dotEscape(module.modulePath.path())});
needUpdate = true;
}
}
@@ -2824,7 +2828,7 @@ void GdbEngine::loadSymbolsForStack()
}
static void handleShowModuleSymbols(const DebuggerResponse &response,
- const QString &modulePath, const QString &fileName)
+ const FilePath &modulePath, const QString &fileName)
{
if (response.resultClass == ResultDone) {
Symbols symbols;
@@ -2883,21 +2887,21 @@ static void handleShowModuleSymbols(const DebuggerResponse &response,
}
}
-void GdbEngine::requestModuleSymbols(const QString &modulePath)
+void GdbEngine::requestModuleSymbols(const FilePath &modulePath)
{
- Utils::TemporaryFile tf("gdbsymbols");
+ TemporaryFile tf("gdbsymbols");
if (!tf.open())
return;
QString fileName = tf.fileName();
tf.close();
- DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath, NeedsTemporaryStop);
+ DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath.path(), NeedsTemporaryStop);
cmd.callback = [modulePath, fileName](const DebuggerResponse &r) {
handleShowModuleSymbols(r, modulePath, fileName);
};
runCommand(cmd);
}
-void GdbEngine::requestModuleSections(const QString &moduleName)
+void GdbEngine::requestModuleSections(const FilePath &moduleName)
{
// There seems to be no way to get the symbols from a single .so.
DebuggerCommand cmd("maint info section ALLOBJ", NeedsTemporaryStop);
@@ -2908,14 +2912,14 @@ void GdbEngine::requestModuleSections(const QString &moduleName)
}
void GdbEngine::handleShowModuleSections(const DebuggerResponse &response,
- const QString &moduleName)
+ const FilePath &moduleName)
{
// ~" Object file: /usr/lib/i386-linux-gnu/libffi.so.6\n"
// ~" 0xb44a6114->0xb44a6138 at 0x00000114: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS\n"
if (response.resultClass == ResultDone) {
const QStringList lines = response.consoleStreamOutput.split('\n');
const QString prefix = " Object file: ";
- const QString needle = prefix + moduleName;
+ const QString needle = prefix + moduleName.path();
Sections sections;
bool active = false;
for (const QString &line : std::as_const(lines)) {
@@ -2956,11 +2960,6 @@ void GdbEngine::reloadModulesInternal()
runCommand({"info shared", NeedsTemporaryStop, CB(handleModulesList)});
}
-static QString nameFromPath(const QString &path)
-{
- return QFileInfo(path).baseName();
-}
-
void GdbEngine::handleModulesList(const DebuggerResponse &response)
{
if (response.resultClass == ResultDone) {
@@ -2971,14 +2970,15 @@ void GdbEngine::handleModulesList(const DebuggerResponse &response)
QString data = response.consoleStreamOutput;
QTextStream ts(&data, QIODevice::ReadOnly);
bool found = false;
+ const FilePath inferior = runParameters().inferior.command.executable();
while (!ts.atEnd()) {
QString line = ts.readLine();
QString symbolsRead;
QTextStream ts(&line, QIODevice::ReadOnly);
if (line.startsWith("0x")) {
ts >> module.startAddress >> module.endAddress >> symbolsRead;
- module.modulePath = ts.readLine().trimmed();
- module.moduleName = nameFromPath(module.modulePath);
+ module.modulePath = inferior.withNewPath(ts.readLine().trimmed());
+ module.moduleName = module.modulePath.baseName();
module.symbolsRead =
(symbolsRead == "Yes" ? Module::ReadOk : Module::ReadFailed);
handler->updateModule(module);
@@ -2989,8 +2989,8 @@ void GdbEngine::handleModulesList(const DebuggerResponse &response)
QTC_ASSERT(symbolsRead == "No", continue);
module.startAddress = 0;
module.endAddress = 0;
- module.modulePath = ts.readLine().trimmed();
- module.moduleName = nameFromPath(module.modulePath);
+ module.modulePath = inferior.withNewPath(ts.readLine().trimmed());
+ module.moduleName = module.modulePath.baseName();
handler->updateModule(module);
found = true;
}
@@ -3002,8 +3002,8 @@ void GdbEngine::handleModulesList(const DebuggerResponse &response)
// loaded_addr="0x8fe00000",slide="0x0",prefix="__dyld_"},
// shlib-info={...}...
for (const GdbMi &item : response.data) {
- module.modulePath = item["path"].data();
- module.moduleName = nameFromPath(module.modulePath);
+ module.modulePath = inferior.withNewPath(item["path"].data());
+ module.moduleName = module.modulePath.baseName();
module.symbolsRead = (item["state"].data() == "Y")
? Module::ReadOk : Module::ReadFailed;
module.startAddress =
@@ -3038,7 +3038,7 @@ void GdbEngine::reloadSourceFiles()
cmd.callback = [this](const DebuggerResponse &response) {
m_sourcesListUpdating = false;
if (response.resultClass == ResultDone) {
- QMap<QString, QString> oldShortToFull = m_shortToFullName;
+ QMap<QString, FilePath> oldShortToFull = m_shortToFullName;
m_shortToFullName.clear();
m_fullToShortName.clear();
// "^done,files=[{file="../../../../bin/dumper/dumper.cpp",
@@ -3049,7 +3049,7 @@ void GdbEngine::reloadSourceFiles()
continue;
GdbMi fullName = item["fullname"];
QString file = fileName.data();
- QString full;
+ FilePath full;
if (fullName.isValid()) {
full = cleanupFullName(fullName.data());
m_fullToShortName[full] = file;
@@ -3922,7 +3922,7 @@ void GdbEngine::handleGdbStarted()
Module module;
module.startAddress = 0;
module.endAddress = 0;
- module.modulePath = rp.inferior.command.executable().toString();
+ module.modulePath = rp.inferior.command.executable();
module.moduleName = "<executable>";
modulesHandler()->updateModule(module);
@@ -3970,12 +3970,61 @@ void GdbEngine::handleGdbStarted()
//if (terminal()->isUsable())
// runCommand({"set inferior-tty " + QString::fromUtf8(terminal()->slaveDevice())});
- const QString uninstalledData = rp.debugger.command.executable().parentDir()
- .pathAppended("data-directory/python").path();
+ const FilePath dumperPath = ICore::resourcePath("debugger");
+ if (rp.debugger.command.executable().needsDevice()) {
+ // Gdb itself running remotely.
+ const FilePath loadOrderFile = dumperPath / "loadorder.txt";
+ const expected_str<QByteArray> toLoad = loadOrderFile.fileContents();
+ if (!toLoad) {
+ AsynchronousMessageBox::critical(
+ Tr::tr("Cannot Find Debugger Initialization Script"),
+ Tr::tr("Cannot read %1: %2").arg(loadOrderFile.toUserOutput(), toLoad.error()));
+ notifyEngineSetupFailed();
+ return;
+ }
+
+ runCommand({"python import sys, types"});
+ QStringList moduleList;
+ for (const QByteArray &rawModuleName : toLoad->split('\n')) {
+ QString module = QString::fromUtf8(rawModuleName).trimmed();
+ if (module.startsWith('#') || module.isEmpty())
+ continue;
+ if (module == "***bridge***")
+ module = "gdbbridge";
+
+ const FilePath codeFile = dumperPath / (module + ".py");
+ const expected_str<QByteArray> code = codeFile.fileContents();
+ if (!code) {
+ qDebug() << Tr::tr("Cannot read %1: %2").arg(codeFile.toUserOutput(), code.error());
+ continue;
+ }
- runCommand({"python sys.path.insert(1, '" + rp.dumperPath.path() + "')"});
- runCommand({"python sys.path.append('" + uninstalledData + "')"});
- runCommand({"python from gdbbridge import *"});
+ showMessage("Reading " + codeFile.toUserOutput(), LogInput);
+ runCommand({QString("python module = types.ModuleType('%1')").arg(module)});
+ runCommand({QString("python code = bytes.fromhex('%1').decode('utf-8')")
+ .arg(QString::fromUtf8(code->toHex()))});
+ runCommand({QString("python exec(code, module.__dict__)")});
+ runCommand({QString("python sys.modules['%1'] = module").arg(module)});
+ runCommand({QString("python import %1").arg(module)});
+
+ if (module.endsWith("types"))
+ moduleList.append('"' + module + '"');
+ }
+
+ runCommand({"python from gdbbridge import *"});
+ runCommand(QString("python theDumper.dumpermodules = [%1]").arg(moduleList.join(',')));
+
+ } else {
+ // Gdb on local host
+ // This is useful (only) in custom gdb builds that did not run 'make install'
+ const FilePath uninstalledData = rp.debugger.command.executable().parentDir()
+ / "data-directory/python";
+ if (uninstalledData.exists())
+ runCommand({"python sys.path.append('" + uninstalledData.path() + "')"});
+
+ runCommand({"python sys.path.insert(1, '" + dumperPath.path() + "')"});
+ runCommand({"python from gdbbridge import *"});
+ }
const QString path = debuggerSettings()->extraDumperFile.value();
if (!path.isEmpty() && QFileInfo(path).isReadable()) {
@@ -4256,7 +4305,7 @@ void GdbEngine::interruptLocalInferior(qint64 pid)
if (runParameters().runAsRoot) {
Environment env = Environment::systemEnvironment();
RunControl::provideAskPassEntry(env);
- QtcProcess proc;
+ Process proc;
proc.setCommand(CommandLine{"sudo", {"-A", "kill", "-s", "SIGINT", QString::number(pid)}});
proc.setEnvironment(env);
proc.start();
@@ -4369,7 +4418,7 @@ void GdbEngine::setupInferior()
setLinuxOsAbi();
QString symbolFile;
if (!rp.symbolFile.isEmpty())
- symbolFile = rp.symbolFile.toFileInfo().absoluteFilePath();
+ symbolFile = rp.symbolFile.absoluteFilePath().path();
//const QByteArray sysroot = sp.sysroot.toLocal8Bit();
//const QByteArray remoteArch = sp.remoteArchitecture.toLatin1();
@@ -4558,7 +4607,13 @@ void GdbEngine::handleLocalAttach(const DebuggerResponse &response)
switch (response.resultClass) {
case ResultDone:
case ResultRunning:
+ {
showMessage("INFERIOR ATTACHED");
+
+ QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value());
+ if (!commands.isEmpty())
+ runCommand({commands, NativeCommand});
+
if (state() == EngineRunRequested) {
// Happens e.g. for "Attach to unstarted application"
// We will get a '*stopped' later that we'll interpret as 'spontaneous'
@@ -4578,6 +4633,7 @@ void GdbEngine::handleLocalAttach(const DebuggerResponse &response)
updateAll();
}
break;
+ }
case ResultError:
if (response.data["msg"].data() == "ptrace: Operation not permitted.") {
QString msg = msgPtraceError(runParameters().startMode);
@@ -4732,6 +4788,13 @@ void GdbEngine::handleExecRun(const DebuggerResponse &response)
CHECK_STATE(EngineRunRequested);
if (response.resultClass == ResultRunning) {
+
+ if (isLocalRunEngine()) {
+ QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value());
+ if (!commands.isEmpty())
+ runCommand({commands, NativeCommand});
+ }
+
notifyEngineRunAndInferiorRunOk();
showMessage("INFERIOR STARTED");
showMessage(msgInferiorSetupOk(), StatusBar);
@@ -4983,7 +5046,7 @@ CoreInfo CoreInfo::readExecutableNameFromCore(const Runnable &debugger, const Fi
args += {"-ex", "set osabi GNU/Linux"};
args += {"-ex", "core " + coreFile.toUserOutput()};
- QtcProcess proc;
+ Process proc;
Environment envLang(Environment::systemEnvironment());
envLang.setupEnglishOutput();
proc.setEnvironment(envLang);
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index cd3a21ffbd..64a2eddd4e 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -15,7 +15,7 @@
#include <debugger/outputcollector.h>
#include <utils/id.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QProcess>
#include <QTextCodec>
@@ -210,23 +210,23 @@ private: ////////// General Interface //////////
void handleBkpt(const GdbMi &bkpt, const Breakpoint &bp);
QString breakpointLocation(const BreakpointParameters &data); // For gdb/MI.
QString breakpointLocation2(const BreakpointParameters &data); // For gdb/CLI fallback.
- QString breakLocation(const QString &file) const;
+ QString breakLocation(const Utils::FilePath &file) const;
void updateTracepointCaptures(const Breakpoint &bp);
//
// Modules specific stuff
//
- void loadSymbols(const QString &moduleName) final;
+ void loadSymbols(const Utils::FilePath &moduleName) final;
void loadAllSymbols() final;
void loadSymbolsForStack() final;
- void requestModuleSymbols(const QString &moduleName) final;
- void requestModuleSections(const QString &moduleName) final;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) final;
+ void requestModuleSections(const Utils::FilePath &moduleName) final;
void reloadModules() final;
void examineModules() final;
void reloadModulesInternal();
void handleModulesList(const DebuggerResponse &response);
- void handleShowModuleSections(const DebuggerResponse &response, const QString &moduleName);
+ void handleShowModuleSections(const DebuggerResponse &response, const Utils::FilePath &moduleName);
//
// Snapshot specific stuff
@@ -265,13 +265,13 @@ private: ////////// General Interface //////////
void reloadSourceFilesInternal();
void handleQuerySources(const DebuggerResponse &response);
- QString fullName(const QString &fileName);
- QString cleanupFullName(const QString &fileName);
+ Utils::FilePath fullName(const QString &fileName);
+ Utils::FilePath cleanupFullName(const QString &fileName);
// awful hack to keep track of used files
- QMap<QString, QString> m_shortToFullName;
- QMap<QString, QString> m_fullToShortName;
- QMultiMap<QString, QString> m_baseNameToFullName;
+ QMap<QString, Utils::FilePath> m_shortToFullName;
+ QMap<Utils::FilePath, QString> m_fullToShortName;
+ QMultiMap<QString, Utils::FilePath> m_baseNameToFullName;
bool m_sourcesListUpdating = false;
@@ -405,7 +405,7 @@ private: ////////// General Interface //////////
bool usesOutputCollector() const;
- Utils::QtcProcess m_gdbProc;
+ Utils::Process m_gdbProc;
OutputCollector m_outputCollector;
QString m_errorString;
};
diff --git a/src/plugins/debugger/images/pin.xpm b/src/plugins/debugger/images/pin.xpm
deleted file mode 100644
index 0759becb67..0000000000
--- a/src/plugins/debugger/images/pin.xpm
+++ /dev/null
@@ -1,19 +0,0 @@
-/* XPM */
-static const char * pin_xpm[] = {
-"12 9 7 1",
-" c None",
-". c #000000",
-"+ c #515151",
-"@ c #A8A8A8",
-"# c #A9A9A9",
-"$ c #999999",
-"% c #696969",
-" . ",
-" ......+",
-" .@@@@@.",
-" .#####.",
-"+.....$$$$$.",
-" .%%%%%.",
-" .......",
-" ......+",
-" . "};
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 3283d5ff7d..ef4d7a4354 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -29,9 +29,9 @@
#include <coreplugin/icore.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QApplication>
#include <QDateTime>
@@ -77,11 +77,11 @@ LldbEngine::LldbEngine()
connect(&ds.useDynamicType, &BaseAspect::changed, this, &LldbEngine::updateLocals);
connect(&ds.intelFlavor, &BaseAspect::changed, this, &LldbEngine::updateAll);
- connect(&m_lldbProc, &QtcProcess::started, this, &LldbEngine::handleLldbStarted);
- connect(&m_lldbProc, &QtcProcess::done, this, &LldbEngine::handleLldbDone);
- connect(&m_lldbProc, &QtcProcess::readyReadStandardOutput,
+ connect(&m_lldbProc, &Process::started, this, &LldbEngine::handleLldbStarted);
+ connect(&m_lldbProc, &Process::done, this, &LldbEngine::handleLldbDone);
+ connect(&m_lldbProc, &Process::readyReadStandardOutput,
this, &LldbEngine::readLldbStandardOutput);
- connect(&m_lldbProc, &QtcProcess::readyReadStandardError,
+ connect(&m_lldbProc, &Process::readyReadStandardError,
this, &LldbEngine::readLldbStandardError);
connect(this, &LldbEngine::outputReady,
@@ -187,7 +187,7 @@ void LldbEngine::setupEngine()
// LLDB 14 installation on Ubuntu 22.04 is broken:
// https://bugs.launchpad.net/ubuntu/+source/llvm-defaults/+bug/1972855
// Brush over it:
- QtcProcess lldbPythonPathFinder;
+ Process lldbPythonPathFinder;
lldbPythonPathFinder.setCommand({lldbCmd, {"-P"}});
lldbPythonPathFinder.start();
lldbPythonPathFinder.waitForFinished();
@@ -219,7 +219,8 @@ void LldbEngine::handleLldbStarted()
const DebuggerRunParameters &rp = runParameters();
- executeCommand("script sys.path.insert(1, '" + rp.dumperPath.path() + "')");
+ QString dumperPath = ICore::resourcePath("debugger").path();
+ executeCommand("script sys.path.insert(1, '" + dumperPath + "')");
// This triggers reportState("enginesetupok") or "enginesetupfailed":
executeCommand("script from lldbbridge import *");
@@ -267,7 +268,8 @@ void LldbEngine::handleLldbStarted()
cmd2.arg("nativemixed", isNativeMixedActive());
cmd2.arg("workingdirectory", rp.inferior.workingDirectory.path());
cmd2.arg("environment", rp.inferior.environment.toStringList());
- cmd2.arg("processargs", toHex(ProcessArgs::splitArgs(rp.inferior.command.arguments()).join(QChar(0))));
+ cmd2.arg("processargs", toHex(ProcessArgs::splitArgs(rp.inferior.command.arguments(),
+ HostOsInfo::hostOs()).join(QChar(0))));
cmd2.arg("platform", rp.platform);
cmd2.arg("symbolfile", rp.symbolFile.path());
@@ -278,8 +280,8 @@ void LldbEngine::handleLldbStarted()
? QString::fromLatin1("Attaching to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID)
: QString::fromLatin1("Attaching to %1").arg(attachedPID);
showMessage(msg, LogMisc);
+ cmd2.arg("startmode", DebuggerStartMode::AttachToLocalProcess);
cmd2.arg("attachpid", attachedPID);
-
} else {
cmd2.arg("startmode", rp.startMode);
// it is better not to check the start mode on the python sid (as we would have to duplicate the
@@ -467,7 +469,7 @@ void LldbEngine::selectThread(const Thread &thread)
DebuggerCommand cmd("selectThread");
cmd.arg("id", thread->id());
cmd.callback = [this](const DebuggerResponse &) {
- fetchStack(debuggerSettings()->maximalStackDepth.value());
+ fetchStack(debuggerSettings()->maximalStackDepth());
};
runCommand(cmd);
}
@@ -629,7 +631,7 @@ void LldbEngine::handleInterpreterBreakpointModified(const GdbMi &bpItem)
updateBreakpointData(bp, bpItem, false);
}
-void LldbEngine::loadSymbols(const QString &moduleName)
+void LldbEngine::loadSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -642,12 +644,13 @@ void LldbEngine::reloadModules()
{
DebuggerCommand cmd("fetchModules");
cmd.callback = [this](const DebuggerResponse &response) {
+ const FilePath inferior = runParameters().inferior.command.executable();
const GdbMi &modules = response.data["modules"];
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
for (const GdbMi &item : modules) {
Module module;
- module.modulePath = item["file"].data();
+ module.modulePath = inferior.withNewPath(item["file"].data());
module.moduleName = item["name"].data();
module.symbolsRead = Module::UnknownReadState;
module.startAddress = item["loaded_addr"].toAddress();
@@ -659,13 +662,13 @@ void LldbEngine::reloadModules()
runCommand(cmd);
}
-void LldbEngine::requestModuleSymbols(const QString &moduleName)
+void LldbEngine::requestModuleSymbols(const FilePath &moduleName)
{
DebuggerCommand cmd("fetchSymbols");
- cmd.arg("module", moduleName);
+ cmd.arg("module", moduleName.path());
cmd.callback = [moduleName](const DebuggerResponse &response) {
const GdbMi &symbols = response.data["symbols"];
- QString moduleName = response.data["module"].data();
+ const QString module = response.data["module"].data();
Symbols syms;
for (const GdbMi &item : symbols) {
Symbol symbol;
@@ -676,7 +679,7 @@ void LldbEngine::requestModuleSymbols(const QString &moduleName)
symbol.demangled = item["demangled"].data();
syms.append(symbol);
}
- showModuleSymbols(moduleName, syms);
+ showModuleSymbols(moduleName.withNewPath(module), syms);
};
runCommand(cmd);
}
@@ -698,7 +701,7 @@ void LldbEngine::updateAll()
DebuggerCommand cmd("fetchThreads");
cmd.callback = [this](const DebuggerResponse &response) {
threadsHandler()->setThreads(response.data);
- fetchStack(debuggerSettings()->maximalStackDepth.value());
+ fetchStack(debuggerSettings()->maximalStackDepth());
reloadRegisters();
};
runCommand(cmd);
diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h
index 0a0d9adfb5..db11af9c10 100644
--- a/src/plugins/debugger/lldb/lldbengine.h
+++ b/src/plugins/debugger/lldb/lldbengine.h
@@ -11,7 +11,7 @@
#include <debugger/debuggertooltipmanager.h>
#include <debugger/debuggerprotocol.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QPointer>
#include <QProcess>
@@ -68,9 +68,9 @@ private:
void assignValueInDebugger(WatchItem *item, const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command) override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadModules() override;
void reloadRegisters() override;
void reloadSourceFiles() override {}
@@ -113,7 +113,7 @@ private:
QString m_inbuffer;
QString m_scriptFileName;
- Utils::QtcProcess m_lldbProc;
+ Utils::Process m_lldbProc;
// FIXME: Make generic.
int m_lastAgentId = 0;
diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp
index 86c9c738dc..4a0fea3f16 100644
--- a/src/plugins/debugger/loadcoredialog.cpp
+++ b/src/plugins/debugger/loadcoredialog.cpp
@@ -11,13 +11,12 @@
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/processinterface.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <utils/temporaryfile.h>
#include <QCheckBox>
@@ -34,6 +33,7 @@
using namespace Core;
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace Debugger::Internal {
@@ -217,8 +217,6 @@ void AttachCoreDialog::accepted()
const DebuggerItem *debuggerItem = Debugger::DebuggerKitAspect::debugger(kit());
const FilePath debuggerCommand = debuggerItem->command();
- using namespace Tasking;
-
const auto copyFile = [debuggerCommand](const FilePath &srcPath) -> expected_str<FilePath> {
if (!srcPath.isSameDevice(debuggerCommand)) {
const expected_str<FilePath> tmpPath = debuggerCommand.tmpDir();
@@ -243,18 +241,18 @@ void AttachCoreDialog::accepted()
using ResultType = expected_str<FilePath>;
- const auto copyFileAsync = [=](QFutureInterface<ResultType> &fi, const FilePath &srcPath) {
- fi.reportResult(copyFile(srcPath));
+ const auto copyFileAsync = [=](QPromise<ResultType> &promise, const FilePath &srcPath) {
+ promise.addResult(copyFile(srcPath));
};
const Group root = {
parallel,
- Async<ResultType>{[=](auto &task) {
- task.setAsyncCallData(copyFileAsync, this->coreFile());
+ AsyncTask<ResultType>{[=](auto &task) {
+ task.setConcurrentCallData(copyFileAsync, this->coreFile());
},
[=](const auto &task) { d->coreFileResult = task.result(); }},
- Async<ResultType>{[=](auto &task) {
- task.setAsyncCallData(copyFileAsync, this->symbolFile());
+ AsyncTask<ResultType>{[=](auto &task) {
+ task.setConcurrentCallData(copyFileAsync, this->symbolFile());
},
[=](const auto &task) { d->symbolFileResult = task.result(); }},
};
diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp
index 3451b9b572..4f063b5728 100644
--- a/src/plugins/debugger/moduleshandler.cpp
+++ b/src/plugins/debugger/moduleshandler.cpp
@@ -11,8 +11,8 @@
#include <utils/basetreeview.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <QCoreApplication>
@@ -48,7 +48,7 @@ QVariant ModuleItem::data(int column, int role) const
break;
case 1:
if (role == Qt::DisplayRole)
- return module.modulePath;
+ return module.modulePath.toUserOutput();
if (role == Qt::ToolTipRole) {
QString msg;
if (!module.elfData.buildId.isEmpty())
@@ -140,6 +140,12 @@ public:
DebuggerEngine *engine;
};
+static bool dependsCanBeFound()
+{
+ static bool dependsInPath = Environment::systemEnvironment().searchInPath("depends").isEmpty();
+ return dependsInPath;
+}
+
bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
{
ModuleItem *item = itemForIndexAtLevel<1>(ev.sourceModelIndex());
@@ -150,7 +156,7 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
const bool canShowSymbols = engine->hasCapability(ShowModuleSymbolsCapability);
const bool moduleNameValid = item && !item->module.moduleName.isEmpty();
const QString moduleName = item ? item->module.moduleName : QString();
- const QString modulePath = item ? item->module.modulePath : QString();
+ const FilePath modulePath = item ? item->module.modulePath : FilePath();
auto menu = new QMenu;
@@ -163,11 +169,13 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
moduleNameValid && enabled && canReload,
[this, modulePath] { engine->loadSymbols(modulePath); });
- // FIXME: Dependencies only available on Windows, when "depends" is installed.
addAction(this, menu, Tr::tr("Show Dependencies of \"%1\"").arg(moduleName),
Tr::tr("Show Dependencies"),
- moduleNameValid && !moduleName.isEmpty() && HostOsInfo::isWindowsHost(),
- [modulePath] { QtcProcess::startDetached({{"depends"}, {modulePath}}); });
+ moduleNameValid && !modulePath.needsDevice() && modulePath.exists()
+ && dependsCanBeFound(),
+ [modulePath] {
+ Process::startDetached({{"depends"}, {modulePath.toString()}});
+ });
addAction(this, menu, Tr::tr("Load Symbols for All Modules"),
enabled && canLoadSymbols,
@@ -185,7 +193,7 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
addAction(this, menu, Tr::tr("Edit File \"%1\"").arg(moduleName),
Tr::tr("Edit File"),
moduleNameValid,
- [this, modulePath] { engine->gotoLocation(FilePath::fromString(modulePath)); });
+ [this, modulePath] { engine->gotoLocation(modulePath); });
addAction(this, menu, Tr::tr("Show Symbols in File \"%1\"").arg(moduleName),
Tr::tr("Show Symbols"),
@@ -239,7 +247,7 @@ QAbstractItemModel *ModulesHandler::model() const
return m_proxyModel;
}
-ModuleItem *ModulesHandler::moduleFromPath(const QString &modulePath) const
+ModuleItem *ModulesHandler::moduleFromPath(const FilePath &modulePath) const
{
// Recent modules are more likely to be unloaded first.
return m_model->findItemAtLevel<1>([modulePath](ModuleItem *item) {
@@ -259,7 +267,7 @@ const Modules ModulesHandler::modules() const
return mods;
}
-void ModulesHandler::removeModule(const QString &modulePath)
+void ModulesHandler::removeModule(const FilePath &modulePath)
{
if (ModuleItem *item = moduleFromPath(modulePath))
m_model->destroyItem(item);
@@ -267,7 +275,7 @@ void ModulesHandler::removeModule(const QString &modulePath)
void ModulesHandler::updateModule(const Module &module)
{
- const QString path = module.modulePath;
+ const FilePath path = module.modulePath;
if (path.isEmpty())
return;
@@ -281,12 +289,12 @@ void ModulesHandler::updateModule(const Module &module)
}
try { // MinGW occasionallly throws std::bad_alloc.
- ElfReader reader(FilePath::fromUserInput(path));
+ ElfReader reader(path);
item->module.elfData = reader.readHeaders();
item->update();
} catch(...) {
qWarning("%s: An exception occurred while reading module '%s'",
- Q_FUNC_INFO, qPrintable(module.modulePath));
+ Q_FUNC_INFO, qPrintable(module.modulePath.toUserOutput()));
}
item->updated = true;
}
diff --git a/src/plugins/debugger/moduleshandler.h b/src/plugins/debugger/moduleshandler.h
index 082a66f2ca..03434edab2 100644
--- a/src/plugins/debugger/moduleshandler.h
+++ b/src/plugins/debugger/moduleshandler.h
@@ -70,7 +70,7 @@ public:
ReadOk // Dwarf index available.
};
QString moduleName;
- QString modulePath;
+ Utils::FilePath modulePath;
QString hostPath;
SymbolReadState symbolsRead = UnknownReadState;
quint64 startAddress = 0;
@@ -99,7 +99,7 @@ public:
QAbstractItemModel *model() const;
- void removeModule(const QString &modulePath);
+ void removeModule(const Utils::FilePath &modulePath);
void updateModule(const Module &module);
void beginUpdateAll();
@@ -109,7 +109,7 @@ public:
const Modules modules() const;
private:
- ModuleItem *moduleFromPath(const QString &modulePath) const;
+ ModuleItem *moduleFromPath(const Utils::FilePath &modulePath) const;
ModulesModel *m_model;
QSortFilterProxyModel *m_proxyModel;
diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp
index fd749a2d0c..accf32d0e6 100644
--- a/src/plugins/debugger/pdb/pdbengine.cpp
+++ b/src/plugins/debugger/pdb/pdbengine.cpp
@@ -22,8 +22,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <coreplugin/idocument.h>
#include <coreplugin/icore.h>
@@ -99,10 +99,10 @@ void PdbEngine::setupEngine()
m_interpreter = runParameters().interpreter;
QString bridge = ICore::resourcePath("debugger/pdbbridge.py").toString();
- connect(&m_proc, &QtcProcess::started, this, &PdbEngine::handlePdbStarted);
- connect(&m_proc, &QtcProcess::done, this, &PdbEngine::handlePdbDone);
- connect(&m_proc, &QtcProcess::readyReadStandardOutput, this, &PdbEngine::readPdbStandardOutput);
- connect(&m_proc, &QtcProcess::readyReadStandardError, this, &PdbEngine::readPdbStandardError);
+ connect(&m_proc, &Process::started, this, &PdbEngine::handlePdbStarted);
+ connect(&m_proc, &Process::done, this, &PdbEngine::handlePdbDone);
+ connect(&m_proc, &Process::readyReadStandardOutput, this, &PdbEngine::readPdbStandardOutput);
+ connect(&m_proc, &Process::readyReadStandardError, this, &PdbEngine::readPdbStandardError);
const FilePath scriptFile = runParameters().mainScript;
if (!scriptFile.isReadableFile()) {
@@ -263,7 +263,7 @@ void PdbEngine::removeBreakpoint(const Breakpoint &bp)
notifyBreakpointRemoveOk(bp);
}
-void PdbEngine::loadSymbols(const QString &moduleName)
+void PdbEngine::loadSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -294,16 +294,16 @@ void PdbEngine::refreshModules(const GdbMi &modules)
&& path.endsWith("' (built-in)>")) {
path = "(builtin)";
}
- module.modulePath = path;
+ module.modulePath = FilePath::fromString(path);
handler->updateModule(module);
}
handler->endUpdateAll();
}
-void PdbEngine::requestModuleSymbols(const QString &moduleName)
+void PdbEngine::requestModuleSymbols(const FilePath &moduleName)
{
DebuggerCommand cmd("listSymbols");
- cmd.arg("module", moduleName);
+ cmd.arg("module", moduleName.path());
runCommand(cmd);
}
@@ -341,7 +341,7 @@ void PdbEngine::refreshSymbols(const GdbMi &symbols)
symbol.name = item["name"].data();
syms.append(symbol);
}
- showModuleSymbols(moduleName, syms);
+ showModuleSymbols(runParameters().inferior.command.executable().withNewPath(moduleName), syms);
}
bool PdbEngine::canHandleToolTip(const DebuggerToolTipContext &) const
diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h
index d65f0e33b1..1e365b36a7 100644
--- a/src/plugins/debugger/pdb/pdbengine.h
+++ b/src/plugins/debugger/pdb/pdbengine.h
@@ -4,7 +4,7 @@
#pragma once
#include <debugger/debuggerengine.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QVariant>
@@ -52,9 +52,9 @@ private:
const QString &expr, const QVariant &value) override;
void executeDebuggerCommand(const QString &command) override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadModules() override;
void reloadRegisters() override {}
void reloadSourceFiles() override {}
@@ -87,7 +87,7 @@ private:
void updateLocals() override;
QString m_inbuffer;
- Utils::QtcProcess m_proc;
+ Utils::Process m_proc;
Utils::FilePath m_interpreter;
};
diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp
index 204d6c4437..033069c116 100644
--- a/src/plugins/debugger/qml/qmlengine.cpp
+++ b/src/plugins/debugger/qml/qmlengine.cpp
@@ -40,8 +40,8 @@
#include <utils/basetreeview.h>
#include <utils/fileinprojectfinder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <QDebug>
@@ -200,7 +200,7 @@ public:
QHash<QString, QTextDocument*> sourceDocuments;
InteractiveInterpreter interpreter;
- QtcProcess process;
+ Process process;
QmlInspectorAgent inspectorAgent;
QList<quint32> queryIds;
@@ -249,17 +249,17 @@ QmlEngine::QmlEngine()
connect(stackHandler(), &StackHandler::currentIndexChanged,
this, &QmlEngine::updateCurrentContext);
- connect(&d->process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&d->process, &Process::readyReadStandardOutput, this, [this] {
// FIXME: Redirect to RunControl
showMessage(d->process.readAllStandardOutput(), AppOutput);
});
- connect(&d->process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&d->process, &Process::readyReadStandardError, this, [this] {
// FIXME: Redirect to RunControl
showMessage(d->process.readAllStandardError(), AppOutput);
});
- connect(&d->process, &QtcProcess::done, this, &QmlEngine::disconnected);
- connect(&d->process, &QtcProcess::started, this, &QmlEngine::handleLauncherStarted);
+ connect(&d->process, &Process::done, this, &QmlEngine::disconnected);
+ connect(&d->process, &Process::started, this, &QmlEngine::handleLauncherStarted);
debuggerConsole()->populateFileFinder();
debuggerConsole()->setScriptEvaluator([this](const QString &expr) {
@@ -736,7 +736,7 @@ bool QmlEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
return bp.isQmlFileAndLineBreakpoint();
}
-void QmlEngine::loadSymbols(const QString &moduleName)
+void QmlEngine::loadSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -759,7 +759,7 @@ void QmlEngine::updateAll()
d->updateLocals();
}
-void QmlEngine::requestModuleSymbols(const QString &moduleName)
+void QmlEngine::requestModuleSymbols(const FilePath &moduleName)
{
Q_UNUSED(moduleName)
}
@@ -1802,10 +1802,10 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
updateScriptSource(name, lineOffset, columnOffset, source);
}
- QMap<QString,QString> files;
+ QMap<QString, FilePath> files;
for (const QString &file : std::as_const(sourceFiles)) {
QString shortName = file;
- QString fullName = engine->toFileInProject(file);
+ FilePath fullName = engine->toFileInProject(file);
files.insert(shortName, fullName);
}
@@ -1915,7 +1915,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data)
const QVariantMap script = body.value("script").toMap();
QUrl fileUrl(script.value(NAME).toString());
- QString filePath = engine->toFileInProject(fileUrl);
+ FilePath filePath = engine->toFileInProject(fileUrl);
const QVariantMap exception = body.value("exception").toMap();
QString errorMessage = exception.value("text").toString();
@@ -2045,8 +2045,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
stackFrame.function = extractString(body.value("func"));
if (stackFrame.function.isEmpty())
stackFrame.function = Tr::tr("Anonymous Function");
- stackFrame.file = FilePath::fromString(
- engine->toFileInProject(extractString(body.value("script"))));
+ stackFrame.file = engine->toFileInProject(extractString(body.value("script")));
stackFrame.usable = stackFrame.file.isReadableFile();
stackFrame.receiver = extractString(body.value("receiver"));
stackFrame.line = body.value("line").toInt() + 1;
@@ -2321,7 +2320,6 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro
QTC_ASSERT(parent, return);
LookupItems itemsToLookup;
- const QSet<QString> expandedINames = engine->watchHandler()->expandedINames();
for (const QVariant &property : properties) {
QmlV8ObjectData propertyData = extractData(property);
std::unique_ptr<WatchItem> item(new WatchItem);
@@ -2343,7 +2341,7 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro
item->id = propertyData.handle;
item->type = propertyData.type;
item->value = propertyData.value.toString();
- if (item->type.isEmpty() || expandedINames.contains(item->iname))
+ if (item->type.isEmpty() || engine->watchHandler()->isExpandedIName(item->iname))
itemsToLookup.insert(propertyData.handle, {item->iname, item->name, item->exp});
setWatchItemHasChildren(item.get(), propertyData.hasChildren());
parent->appendChild(item.release());
@@ -2445,7 +2443,7 @@ void QmlEnginePrivate::flushSendBuffer()
sendBuffer.clear();
}
-QString QmlEngine::toFileInProject(const QUrl &fileUrl)
+FilePath QmlEngine::toFileInProject(const QUrl &fileUrl)
{
// make sure file finder is properly initialized
const DebuggerRunParameters &rp = runParameters();
@@ -2454,7 +2452,7 @@ QString QmlEngine::toFileInProject(const QUrl &fileUrl)
d->fileFinder.setAdditionalSearchDirectories(rp.additionalSearchDirectories);
d->fileFinder.setSysroot(rp.sysRoot);
- return d->fileFinder.findFile(fileUrl).constFirst().toString();
+ return d->fileFinder.findFile(fileUrl).constFirst();
}
DebuggerEngine *createQmlEngine()
diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h
index 331176d426..2006ce081f 100644
--- a/src/plugins/debugger/qml/qmlengine.h
+++ b/src/plugins/debugger/qml/qmlengine.h
@@ -25,7 +25,7 @@ public:
void logServiceActivity(const QString &service, const QString &logMessage);
void expressionEvaluated(quint32 queryId, const QVariant &result);
- QString toFileInProject(const QUrl &fileUrl);
+ Utils::FilePath toFileInProject(const QUrl &fileUrl);
private:
void disconnected();
@@ -75,9 +75,9 @@ private:
void assignValueInDebugger(WatchItem *item,
const QString &expr, const QVariant &value) override;
- void loadSymbols(const QString &moduleName) override;
+ void loadSymbols(const Utils::FilePath &moduleName) override;
void loadAllSymbols() override;
- void requestModuleSymbols(const QString &moduleName) override;
+ void requestModuleSymbols(const Utils::FilePath &moduleName) override;
void reloadModules() override;
void reloadRegisters() override {}
void reloadSourceFiles() override;
diff --git a/src/plugins/debugger/qml/qmlengineutils.cpp b/src/plugins/debugger/qml/qmlengineutils.cpp
index 7e304d3c92..80e0ac98f1 100644
--- a/src/plugins/debugger/qml/qmlengineutils.cpp
+++ b/src/plugins/debugger/qml/qmlengineutils.cpp
@@ -20,6 +20,7 @@ using namespace QmlDebug;
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace TextEditor;
+using namespace Utils;
namespace Debugger::Internal {
@@ -218,11 +219,10 @@ void clearExceptionSelection()
}
}
-QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage)
+QStringList highlightExceptionCode(int lineNumber, const FilePath &filePath, const QString &errorMessage)
{
QStringList messages;
- const QList<IEditor *> editors = DocumentModel::editorsForFilePath(
- Utils::FilePath::fromString(filePath));
+ const QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath);
const TextEditor::FontSettings &fontSettings = TextEditor::TextEditorSettings::fontSettings();
QTextCharFormat errorFormat = fontSettings.toTextCharFormat(TextEditor::C_ERROR);
@@ -251,7 +251,7 @@ QStringList highlightExceptionCode(int lineNumber, const QString &filePath, cons
selections.append(sel);
ed->setExtraSelections(TextEditorWidget::DebuggerExceptionSelection, selections);
- messages.append(QString::fromLatin1("%1: %2: %3").arg(filePath).arg(lineNumber).arg(errorMessage));
+ messages.append(QString::fromLatin1("%1: %2: %3").arg(filePath.toUserOutput()).arg(lineNumber).arg(errorMessage));
}
return messages;
}
diff --git a/src/plugins/debugger/qml/qmlengineutils.h b/src/plugins/debugger/qml/qmlengineutils.h
index 7cf55067b6..325c8f28e9 100644
--- a/src/plugins/debugger/qml/qmlengineutils.h
+++ b/src/plugins/debugger/qml/qmlengineutils.h
@@ -6,11 +6,13 @@
#include <qmldebug/qdebugmessageclient.h>
#include <qmldebug/qmloutputparser.h>
+namespace Utils { class FilePath; }
+
namespace Debugger::Internal {
void appendDebugOutput(QtMsgType type, const QString &message, const QmlDebug::QDebugContextInfo &info);
void clearExceptionSelection();
-QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage);
+QStringList highlightExceptionCode(int lineNumber, const Utils::FilePath &filePath, const QString &errorMessage);
} // Debugger::Internal
diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp
index 983e38adc0..4b632c0f0d 100644
--- a/src/plugins/debugger/qml/qmlinspectoragent.cpp
+++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp
@@ -32,6 +32,7 @@
using namespace QmlDebug;
using namespace QmlDebug::Constants;
+using namespace Utils;
namespace Debugger::Internal {
@@ -541,8 +542,8 @@ void QmlInspectorAgent::buildDebugIdHashRecursive(const ObjectReference &ref)
lineNum += match.captured(3).toInt() - 1;
}
- const QString filePath = m_qmlEngine->toFileInProject(fileUrl);
- m_debugIdLocations.insert(ref.debugId(), FileReference(filePath, lineNum, colNum));
+ const FilePath filePath = m_qmlEngine->toFileInProject(fileUrl);
+ m_debugIdLocations.insert(ref.debugId(), FileReference(filePath.toFSPathString(), lineNum, colNum));
const auto children = ref.children();
for (const ObjectReference &it : children)
@@ -735,7 +736,7 @@ void QmlInspectorAgent::onShowAppOnTopChanged(bool checked)
void QmlInspectorAgent::jumpToObjectDefinitionInEditor(const FileReference &objSource)
{
- const auto filePath = Utils::FilePath::fromString(m_qmlEngine->toFileInProject(objSource.url()));
+ const FilePath filePath = m_qmlEngine->toFileInProject(objSource.url());
Core::EditorManager::openEditorAt({filePath, objSource.lineNumber()});
}
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
index eb17cba3de..9f3824ffce 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
@@ -9,13 +9,13 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
-#include <utils/checkablemessagebox.h>
#include <utils/pathchooser.h>
#include <utils/temporarydirectory.h>
#include <QAction>
#include <QCheckBox>
#include <QDebug>
+#include <QDialogButtonBox>
#include <QFormLayout>
#include <QLabel>
#include <QPushButton>
diff --git a/src/plugins/debugger/sourcefileshandler.cpp b/src/plugins/debugger/sourcefileshandler.cpp
index 412f550482..bec0305417 100644
--- a/src/plugins/debugger/sourcefileshandler.cpp
+++ b/src/plugins/debugger/sourcefileshandler.cpp
@@ -55,8 +55,8 @@ Qt::ItemFlags SourceFilesHandler::flags(const QModelIndex &index) const
{
if (index.row() >= m_fullNames.size())
return {};
- QFileInfo fi(m_fullNames.at(index.row()));
- return fi.isReadable() ? QAbstractItemModel::flags(index) : Qt::ItemFlags({});
+ FilePath filePath = m_fullNames.at(index.row());
+ return filePath.isReadableFile() ? QAbstractItemModel::flags(index) : Qt::ItemFlags({});
}
QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
@@ -75,7 +75,7 @@ QVariant SourceFilesHandler::data(const QModelIndex &index, int role) const
break;
case 1:
if (role == Qt::DisplayRole)
- return m_fullNames.at(row);
+ return m_fullNames.at(row).toUserOutput();
//if (role == Qt::DecorationRole)
// return module.symbolsRead ? icon2 : icon;
break;
@@ -123,13 +123,13 @@ bool SourceFilesHandler::setData(const QModelIndex &idx, const QVariant &data, i
return false;
}
-void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFiles)
+void SourceFilesHandler::setSourceFiles(const QMap<QString, FilePath> &sourceFiles)
{
beginResetModel();
m_shortNames.clear();
m_fullNames.clear();
- QMap<QString, QString>::ConstIterator it = sourceFiles.begin();
- QMap<QString, QString>::ConstIterator et = sourceFiles.end();
+ auto it = sourceFiles.begin();
+ const auto et = sourceFiles.end();
for (; it != et; ++it) {
m_shortNames.append(it.key());
m_fullNames.append(it.value());
@@ -139,7 +139,7 @@ void SourceFilesHandler::setSourceFiles(const QMap<QString, QString> &sourceFile
void SourceFilesHandler::removeAll()
{
- setSourceFiles(QMap<QString, QString>());
+ setSourceFiles({});
//header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
}
diff --git a/src/plugins/debugger/sourcefileshandler.h b/src/plugins/debugger/sourcefileshandler.h
index dabbdbdad0..d714eda2e3 100644
--- a/src/plugins/debugger/sourcefileshandler.h
+++ b/src/plugins/debugger/sourcefileshandler.h
@@ -3,6 +3,8 @@
#pragma once
+#include <utils/filepath.h>
+
#include <QAbstractItemModel>
#include <QStringList>
@@ -29,7 +31,7 @@ public:
void clearModel();
- void setSourceFiles(const QMap<QString, QString> &sourceFiles);
+ void setSourceFiles(const QMap<QString, Utils::FilePath> &sourceFiles);
void removeAll();
QAbstractItemModel *model() { return m_proxyModel; }
@@ -37,7 +39,7 @@ public:
private:
DebuggerEngine *m_engine;
QStringList m_shortNames;
- QStringList m_fullNames;
+ Utils::FilePaths m_fullNames;
QAbstractItemModel *m_proxyModel;
};
diff --git a/src/plugins/debugger/stackframe.cpp b/src/plugins/debugger/stackframe.cpp
index 7008900aab..7ba504d5ca 100644
--- a/src/plugins/debugger/stackframe.cpp
+++ b/src/plugins/debugger/stackframe.cpp
@@ -74,7 +74,7 @@ StackFrame StackFrame::parseFrame(const GdbMi &frameMi, const DebuggerRunParamet
frame.function = frameMi["function"].data();
frame.module = frameMi["module"].data();
const FilePath debugger = rp.debugger.command.executable();
- const FilePath onDevicePath = FilePath::fromUserInput(frameMi["file"].data()).onDevice(debugger);
+ const FilePath onDevicePath = debugger.withNewPath(frameMi["file"].data()).cleanPath();
frame.file = onDevicePath.localSource().value_or(onDevicePath);
frame.line = frameMi["line"].toInt();
frame.address = frameMi["address"].toAddress();
diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp
index 6a2390fcad..1fc6f04582 100644
--- a/src/plugins/debugger/stackhandler.cpp
+++ b/src/plugins/debugger/stackhandler.cpp
@@ -234,7 +234,7 @@ void StackHandler::setFramesAndCurrentIndex(const GdbMi &frames, bool isFull)
targetFrame = i;
}
- bool canExpand = !isFull && (n >= debuggerSettings()->maximalStackDepth.value());
+ bool canExpand = !isFull && (n >= debuggerSettings()->maximalStackDepth());
debuggerSettings()->expandStack.setEnabled(canExpand);
setFrames(stackFrames, canExpand);
diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp
index 5de7740380..ac8a043df8 100644
--- a/src/plugins/debugger/terminal.cpp
+++ b/src/plugins/debugger/terminal.cpp
@@ -11,8 +11,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QIODevice>
@@ -172,13 +172,12 @@ void TerminalRunner::start()
QTC_ASSERT(!m_stubProc, reportFailure({}); return);
Runnable stub = m_stubRunnable();
- m_stubProc = new QtcProcess(this);
- m_stubProc->setTerminalMode(HostOsInfo::isWindowsHost()
- ? TerminalMode::Suspend : TerminalMode::Debug);
+ m_stubProc = new Process(this);
+ m_stubProc->setTerminalMode(TerminalMode::Debug);
- connect(m_stubProc, &QtcProcess::started,
+ connect(m_stubProc, &Process::started,
this, &TerminalRunner::stubStarted);
- connect(m_stubProc, &QtcProcess::done,
+ connect(m_stubProc, &Process::done,
this, &TerminalRunner::stubDone);
m_stubProc->setEnvironment(stub.environment);
diff --git a/src/plugins/debugger/terminal.h b/src/plugins/debugger/terminal.h
index b4fa1317c5..cd57e9eea2 100644
--- a/src/plugins/debugger/terminal.h
+++ b/src/plugins/debugger/terminal.h
@@ -8,7 +8,7 @@
#include <projectexplorer/runcontrol.h>
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace Debugger {
@@ -65,7 +65,7 @@ private:
void stubStarted();
void stubDone();
- Utils::QtcProcess *m_stubProc = nullptr;
+ Utils::Process *m_stubProc = nullptr;
std::function<ProjectExplorer::Runnable()> m_stubRunnable;
qint64 m_applicationPid = 0;
qint64 m_applicationMainThreadId = 0;
diff --git a/src/plugins/debugger/uvsc/uvscengine.cpp b/src/plugins/debugger/uvsc/uvscengine.cpp
index 2bb0bef8e7..06450651de 100644
--- a/src/plugins/debugger/uvsc/uvscengine.cpp
+++ b/src/plugins/debugger/uvsc/uvscengine.cpp
@@ -600,7 +600,7 @@ void UvscEngine::handleProjectClosed()
Module module;
module.startAddress = 0;
module.endAddress = 0;
- module.modulePath = rp.inferior.command.executable().toString();
+ module.modulePath = rp.inferior.command.executable();
module.moduleName = "<executable>";
modulesHandler()->updateModule(module);
diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp
index 329e58b88b..8d0af0e4d3 100644
--- a/src/plugins/debugger/watchdata.cpp
+++ b/src/plugins/debugger/watchdata.cpp
@@ -216,6 +216,13 @@ public:
child->valueEditable = true;
item->appendChild(child);
}
+ if (childrenElided) {
+ auto child = new WatchItem;
+ child->name = WatchItem::loadMoreName;
+ child->iname = item->iname + "." + WatchItem::loadMoreName;
+ child->wantsChildren = true;
+ item->appendChild(child);
+ }
}
void decode()
@@ -264,6 +271,7 @@ public:
QString rawData;
QString childType;
DebuggerEncoding encoding;
+ int childrenElided;
quint64 addrbase;
quint64 addrstep;
Endian endian;
@@ -379,6 +387,7 @@ void WatchItem::parseHelper(const GdbMi &input, bool maySort)
decoder.item = this;
decoder.rawData = mi.data();
decoder.childType = input["childtype"].data();
+ decoder.childrenElided = input["childrenelided"].toInt();
decoder.addrbase = input["addrbase"].toAddress();
decoder.addrstep = input["addrstep"].toAddress();
decoder.endian = input["endian"].data() == ">" ? Endian::Big : Endian::Little;
@@ -504,6 +513,11 @@ QString WatchItem::toToolTip() const
return res;
}
+bool WatchItem::isLoadMore() const
+{
+ return name == loadMoreName;
+}
+
bool WatchItem::isLocal() const
{
if (arrayIndex >= 0)
diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h
index 4ef57eacf1..42d046f2a1 100644
--- a/src/plugins/debugger/watchdata.h
+++ b/src/plugins/debugger/watchdata.h
@@ -36,8 +36,10 @@ public:
int editType() const;
static const qint64 InvalidId = -1;
+ constexpr static char loadMoreName[] = "<load more>";
void setHasChildren(bool c) { wantsChildren = c; }
+ bool isLoadMore() const;
bool isValid() const { return !iname.isEmpty(); }
bool isVTablePointer() const;
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 17ce07bee0..d533a0d4f6 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -40,6 +40,7 @@
#include <QApplication>
#include <QDebug>
+#include <QDialogButtonBox>
#include <QFile>
#include <QFloat16>
#include <QItemDelegate>
@@ -52,8 +53,8 @@
#include <QPainter>
#include <QSet>
#include <QStringDecoder>
-#include <QTableWidget>
#include <QTabWidget>
+#include <QTableWidget>
#include <QTextEdit>
#include <QTimer>
#include <QToolTip>
@@ -410,6 +411,7 @@ public:
bool hasChildren(const QModelIndex &idx) const override;
bool canFetchMore(const QModelIndex &idx) const override;
void fetchMore(const QModelIndex &idx) override;
+ void expand(WatchItem *item, bool requestEngineUpdate);
QString displayForAutoTest(const QByteArray &iname) const;
void reinitialize(bool includeInspectData = false);
@@ -468,6 +470,7 @@ public:
SeparatedView *m_separatedView; // Not owned.
QSet<QString> m_expandedINames;
+ QHash<QString, int> m_maxArrayCount;
QTimer m_requestUpdateTimer;
QTimer m_localsWindowsTimer;
@@ -1226,7 +1229,8 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
if (value.toBool()) {
// Should already have been triggered by fetchMore()
//QTC_CHECK(m_expandedINames.contains(item->iname));
- m_expandedINames.insert(item->iname);
+ if (!item->isLoadMore())
+ m_expandedINames.insert(item->iname);
} else {
m_expandedINames.remove(item->iname);
}
@@ -1336,13 +1340,23 @@ bool WatchModel::canFetchMore(const QModelIndex &idx) const
void WatchModel::fetchMore(const QModelIndex &idx)
{
- if (!idx.isValid())
- return;
+ if (idx.isValid())
+ expand(nonRootItemForIndex(idx), true);
+}
- WatchItem *item = nonRootItemForIndex(idx);
- if (item) {
+void WatchModel::expand(WatchItem *item, bool requestEngineUpdate)
+{
+ if (!item)
+ return;
+ if (item->isLoadMore()) {
+ item = item->parent();
+ m_maxArrayCount[item->iname]
+ = m_maxArrayCount.value(item->iname, debuggerSettings()->defaultArraySize.value()) * 10;
+ if (requestEngineUpdate)
+ m_engine->updateItem(item->iname);
+ } else {
m_expandedINames.insert(item->iname);
- if (item->childCount() == 0)
+ if (requestEngineUpdate && item->childCount() == 0)
m_engine->expandItem(item->iname);
}
}
@@ -1765,10 +1779,14 @@ bool WatchModel::contextMenuEvent(const ItemViewEvent &ev)
menu->addSeparator();
addAction(this, menu, Tr::tr("Expand All Children"), item, [this, name = item ? item->iname : QString()] {
- m_expandedINames.insert(name);
- if (auto item = findItem(name)) {
- item->forFirstLevelChildren(
- [this](WatchItem *child) { m_expandedINames.insert(child->iname); });
+ if (name.isEmpty())
+ return;
+ if (WatchItem *item = findItem(name)) {
+ expand(item, false);
+ item->forFirstLevelChildren([this](WatchItem *child) {
+ if (!child->isLoadMore())
+ expand(child, false);
+ });
m_engine->updateLocals();
}
});
@@ -2226,7 +2244,7 @@ bool WatchHandler::insertItem(WatchItem *item)
void WatchModel::reexpandItems()
{
- for (const QString &iname : std::as_const(m_expandedINames)) {
+ for (const QString &iname: m_expandedINames) {
if (WatchItem *item = findItem(iname)) {
emit itemIsExpanded(indexForItem(item));
emit inameIsExpanded(iname);
@@ -2308,7 +2326,8 @@ void WatchHandler::notifyUpdateFinished()
m_model->destroyItem(item);
m_model->forAllItems([this](WatchItem *item) {
- if (item->wantsChildren && isExpandedIName(item->iname)) {
+ if (item->wantsChildren && isExpandedIName(item->iname)
+ && item->name != WatchItem::loadMoreName) {
// m_model->m_engine->showMessage(QString("ADJUSTING CHILD EXPECTATION FOR " + item->iname));
item->wantsChildren = false;
}
@@ -2553,11 +2572,13 @@ void WatchModel::clearWatches()
if (theWatcherNames.isEmpty())
return;
- const QDialogButtonBox::StandardButton ret = CheckableMessageBox::doNotAskAgainQuestion(
- ICore::dialogParent(), Tr::tr("Remove All Expression Evaluators"),
- Tr::tr("Are you sure you want to remove all expression evaluators?"),
- ICore::settings(), "RemoveAllWatchers");
- if (ret != QDialogButtonBox::Yes)
+ const QMessageBox::StandardButton ret = CheckableMessageBox::question(
+ ICore::dialogParent(),
+ Tr::tr("Remove All Expression Evaluators"),
+ Tr::tr("Are you sure you want to remove all expression evaluators?"),
+ ICore::settings(),
+ "RemoveAllWatchers");
+ if (ret != QMessageBox::Yes)
return;
m_watchRoot->removeChildren();
@@ -2607,11 +2628,8 @@ const WatchItem *WatchHandler::watchItem(const QModelIndex &idx) const
void WatchHandler::fetchMore(const QString &iname) const
{
- if (WatchItem *item = m_model->findItem(iname)) {
- m_model->m_expandedINames.insert(iname);
- if (item->childCount() == 0)
- m_model->m_engine->expandItem(iname);
- }
+ if (WatchItem *item = m_model->findItem(iname))
+ m_model->expand(item, true);
}
WatchItem *WatchHandler::findItem(const QString &iname) const
@@ -2725,9 +2743,9 @@ QString WatchHandler::individualFormatRequests() const
void WatchHandler::appendFormatRequests(DebuggerCommand *cmd) const
{
- QJsonArray expanded;
- for (const QString &name : std::as_const(m_model->m_expandedINames))
- expanded.append(name);
+ QJsonObject expanded;
+ for (const QString &iname : std::as_const(m_model->m_expandedINames))
+ expanded.insert(iname, maxArrayCount(iname));
cmd->arg("expanded", expanded);
@@ -2831,6 +2849,11 @@ QSet<QString> WatchHandler::expandedINames() const
return m_model->m_expandedINames;
}
+int WatchHandler::maxArrayCount(const QString &iname) const
+{
+ return m_model->m_maxArrayCount.value(iname, debuggerSettings()->defaultArraySize());
+}
+
void WatchHandler::recordTypeInfo(const GdbMi &typeInfo)
{
if (typeInfo.type() == GdbMi::List) {
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index 0683d73ce9..58a64cea4a 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -60,6 +60,7 @@ public:
bool isExpandedIName(const QString &iname) const;
QSet<QString> expandedINames() const;
+ int maxArrayCount(const QString &iname) const;
static QStringList watchedExpressions();
static QMap<QString, int> watcherNames();
diff --git a/src/plugins/designer/CMakeLists.txt b/src/plugins/designer/CMakeLists.txt
index f6304e4a67..18b1cf9769 100644
--- a/src/plugins/designer/CMakeLists.txt
+++ b/src/plugins/designer/CMakeLists.txt
@@ -22,7 +22,7 @@ add_qtc_plugin(Designer
formeditorfactory.cpp formeditorfactory.h
formeditorplugin.cpp formeditorplugin.h
formeditorstack.cpp formeditorstack.h
- formeditorw.cpp formeditorw.h
+ formeditor.cpp formeditor.h
formtemplatewizardpage.cpp formtemplatewizardpage.h
formwindoweditor.cpp formwindoweditor.h
formwindowfile.cpp formwindowfile.h
diff --git a/src/plugins/designer/codemodelhelpers.cpp b/src/plugins/designer/codemodelhelpers.cpp
index beddb3714e..8428fd9817 100644
--- a/src/plugins/designer/codemodelhelpers.cpp
+++ b/src/plugins/designer/codemodelhelpers.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <QCoreApplication>
@@ -30,7 +30,7 @@ static const char setupUiC[] = "setupUi";
// Find the generated "ui_form.h" header of the form via project.
static FilePath generatedHeaderOf(const FilePath &uiFileName)
{
- if (const Project *uiProject = SessionManager::projectForFile(uiFileName)) {
+ if (const Project *uiProject = ProjectManager::projectForFile(uiFileName)) {
if (Target *t = uiProject->activeTarget()) {
if (BuildSystem *bs = t->buildSystem()) {
FilePaths files = bs->filesGeneratedFrom(uiFileName);
diff --git a/src/plugins/designer/cpp/newclasswidget.cpp b/src/plugins/designer/cpp/newclasswidget.cpp
index b659854777..924700b59c 100644
--- a/src/plugins/designer/cpp/newclasswidget.cpp
+++ b/src/plugins/designer/cpp/newclasswidget.cpp
@@ -66,14 +66,15 @@ NewClassWidget::NewClassWidget(QWidget *parent) :
setNamesDelimiter(QLatin1String("::"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
Tr::tr("&Class name:"), d->m_classLineEdit, br,
Tr::tr("&Header file:"), d->m_headerFileLineEdit, br,
Tr::tr("&Source file:"), d->m_sourceFileLineEdit, br,
Tr::tr("&Form file:"), d->m_formFileLineEdit, br,
Tr::tr("&Path:"), d->m_pathChooser, br,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(d->m_classLineEdit, &ClassNameValidatingLineEdit::updateFileName,
this, &NewClassWidget::slotUpdateFileNames);
diff --git a/src/plugins/designer/designer.qbs b/src/plugins/designer/designer.qbs
index 411be43283..8b97a494a7 100644
--- a/src/plugins/designer/designer.qbs
+++ b/src/plugins/designer/designer.qbs
@@ -41,7 +41,7 @@ QtcPlugin {
"formeditorfactory.cpp", "formeditorfactory.h",
"formeditorplugin.cpp", "formeditorplugin.h",
"formeditorstack.cpp", "formeditorstack.h",
- "formeditorw.cpp", "formeditorw.h",
+ "formeditor.cpp", "formeditor.h",
"formtemplatewizardpage.cpp", "formtemplatewizardpage.h",
"formwindoweditor.cpp", "formwindoweditor.h",
"formwindowfile.cpp", "formwindowfile.h",
@@ -77,9 +77,7 @@ QtcPlugin {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [ "gotoslot_test.cpp" ]
cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
diff --git a/src/plugins/designer/designercontext.cpp b/src/plugins/designer/designercontext.cpp
index de65df8a20..266a347bbb 100644
--- a/src/plugins/designer/designercontext.cpp
+++ b/src/plugins/designer/designercontext.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "designercontext.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include <QDesignerFormEditorInterface>
#include <QDesignerIntegration>
@@ -25,7 +25,7 @@ DesignerContext::DesignerContext(const Core::Context &context,
void DesignerContext::contextHelp(const HelpCallback &callback) const
{
- const QDesignerFormEditorInterface *core = FormEditorW::designerEditor();
+ const QDesignerFormEditorInterface *core = designerEditor();
callback(core->integration()->contextHelpId());
}
diff --git a/src/plugins/designer/editorwidget.cpp b/src/plugins/designer/editorwidget.cpp
index f1b1772fc2..664e953b3b 100644
--- a/src/plugins/designer/editorwidget.cpp
+++ b/src/plugins/designer/editorwidget.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "editorwidget.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formeditorstack.h"
#include <coreplugin/editormanager/ieditor.h>
@@ -28,7 +28,7 @@ EditorWidget::EditorWidget(QWidget *parent) :
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
- QWidget * const*subs = FormEditorW::designerSubWindows();
+ QWidget * const * subs = designerSubWindows();
for (int i = 0; i < DesignerSubWindowCount; i++) {
QWidget *subWindow = subs[i];
subWindow->setWindowTitle(subs[i]->windowTitle());
diff --git a/src/plugins/designer/formeditorw.cpp b/src/plugins/designer/formeditor.cpp
index a55b93d71f..96e6579650 100644
--- a/src/plugins/designer/formeditorw.cpp
+++ b/src/plugins/designer/formeditor.cpp
@@ -5,7 +5,7 @@
#include "designertr.h"
#include "editordata.h"
#include "editorwidget.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formwindoweditor.h"
#include "formwindowfile.h"
#include "qtcreatorintegration.h"
@@ -123,9 +123,9 @@ public:
}
};
-// --------- FormEditorW
+// FormEditorData
-class FormEditorData
+class FormEditorData : public QObject
{
public:
FormEditorData();
@@ -173,7 +173,7 @@ public:
QDesignerFormEditorInterface *m_formeditor = nullptr;
QtCreatorIntegration *m_integration = nullptr;
QDesignerFormWindowManagerInterface *m_fwm = nullptr;
- FormEditorW::InitializationStage m_initStage = FormEditorW::RegisterPlugins;
+ InitializationStage m_initStage = RegisterPlugins;
QWidget *m_designerSubWindows[DesignerSubWindowCount];
@@ -202,7 +202,6 @@ public:
};
static FormEditorData *d = nullptr;
-static FormEditorW *m_instance = nullptr;
FormEditorData::FormEditorData() :
m_formeditor(QDesignerComponents::createFormEditor(nullptr))
@@ -238,7 +237,7 @@ FormEditorData::FormEditorData() :
if (editor && editor->document()->id() == Constants::K_DESIGNER_XML_EDITOR_ID) {
FormWindowEditor *xmlEditor = qobject_cast<FormWindowEditor *>(editor);
QTC_ASSERT(xmlEditor, return);
- FormEditorW::ensureInitStage(FormEditorW::FullyInitialized);
+ ensureInitStage(FullyInitialized);
SharedTools::WidgetHost *fw = m_editorWidget->formWindowEditorForXmlEditor(xmlEditor);
QTC_ASSERT(fw, return);
m_editorWidget->setVisibleEditor(xmlEditor);
@@ -251,7 +250,7 @@ FormEditorData::FormEditorData() :
FormEditorData::~FormEditorData()
{
- if (m_initStage == FormEditorW::FullyInitialized) {
+ if (m_initStage == FullyInitialized) {
QSettings *s = ICore::settings();
s->beginGroup(settingsGroupC);
m_editorWidget->saveSettings(s);
@@ -324,18 +323,18 @@ void FormEditorData::setupViewActions()
void FormEditorData::fullInit()
{
- QTC_ASSERT(m_initStage == FormEditorW::RegisterPlugins, return);
+ QTC_ASSERT(m_initStage == RegisterPlugins, return);
QElapsedTimer *initTime = nullptr;
if (Designer::Constants::Internal::debug) {
initTime = new QElapsedTimer;
initTime->start();
}
- QDesignerComponents::createTaskMenu(m_formeditor, m_instance);
+ QDesignerComponents::createTaskMenu(m_formeditor, this);
QDesignerComponents::initializePlugins(m_formeditor);
QDesignerComponents::initializeResources();
initDesignerSubWindows();
- m_integration = new QtCreatorIntegration(m_formeditor, m_instance);
+ m_integration = new QtCreatorIntegration(m_formeditor, this);
m_formeditor->setIntegration(m_integration);
// Connect Qt Designer help request to HelpManager.
QObject::connect(m_integration, &QtCreatorIntegration::creatorHelpRequested,
@@ -397,13 +396,13 @@ void FormEditorData::fullInit()
Context designerContexts = m_contexts;
designerContexts.add(Core::Constants::C_EDITORMANAGER);
- ICore::addContextObject(new DesignerContext(designerContexts, m_modeWidget, m_instance));
+ ICore::addContextObject(new DesignerContext(designerContexts, m_modeWidget, this));
DesignMode::registerDesignWidget(m_modeWidget, QStringList(FORM_MIMETYPE), m_contexts);
setupViewActions();
- m_initStage = FormEditorW::FullyInitialized;
+ m_initStage = FullyInitialized;
}
void FormEditorData::initDesignerSubWindows()
@@ -438,22 +437,21 @@ void FormEditorData::initDesignerSubWindows()
ae->setObjectName("ActionEditor");
m_formeditor->setActionEditor(ae);
m_designerSubWindows[ActionEditorSubWindow] = ae;
- m_initStage = FormEditorW::SubwindowsInitialized;
+ m_initStage = SubwindowsInitialized;
}
-QList<IOptionsPage *> FormEditorW::optionsPages()
+QList<IOptionsPage *> optionsPages()
{
return d->m_settingsPages;
}
-void FormEditorW::ensureInitStage(InitializationStage s)
+void ensureInitStage(InitializationStage s)
{
if (Designer::Constants::Internal::debug)
qDebug() << Q_FUNC_INFO << s;
- if (!d) {
- m_instance = new FormEditorW;
+ if (!d)
d = new FormEditorData;
- }
+
if (d->m_initStage >= s)
return;
QApplication::setOverrideCursor(Qt::WaitCursor);
@@ -461,15 +459,13 @@ void FormEditorW::ensureInitStage(InitializationStage s)
QApplication::restoreOverrideCursor();
}
-void FormEditorW::deleteInstance()
+void deleteInstance()
{
delete d;
d = nullptr;
- delete m_instance;
- m_instance = nullptr;
}
-IEditor *FormEditorW::createEditor()
+IEditor *createEditor()
{
ensureInitStage(FullyInitialized);
return d->createEditor();
@@ -489,7 +485,7 @@ void FormEditorData::setupActions()
bindShortcut(ActionManager::registerAction(m_fwm->actionPaste(), Core::Constants::PASTE, m_contexts), m_fwm->actionPaste());
bindShortcut(ActionManager::registerAction(m_fwm->actionSelectAll(), Core::Constants::SELECTALL, m_contexts), m_fwm->actionSelectAll());
- m_actionPrint = new QAction(m_instance);
+ m_actionPrint = new QAction(this);
bindShortcut(ActionManager::registerAction(m_actionPrint, Core::Constants::PRINT, m_contexts), m_actionPrint);
QObject::connect(m_actionPrint, &QAction::triggered, [this]() { print(); });
@@ -502,7 +498,7 @@ void FormEditorData::setupActions()
command->setAttribute(Command::CA_Hide);
medit->addAction(command, Core::Constants::G_EDIT_COPYPASTE);
- m_actionGroupEditMode = new QActionGroup(m_instance);
+ m_actionGroupEditMode = new QActionGroup(this);
m_actionGroupEditMode->setExclusive(true);
QObject::connect(m_actionGroupEditMode, &QActionGroup::triggered,
[this](QAction *a) { activateEditMode(a->data().toInt()); });
@@ -601,7 +597,7 @@ void FormEditorData::setupActions()
QString(), Core::Constants::G_DEFAULT_THREE);
mformtools->addSeparator(m_contexts, Core::Constants::G_DEFAULT_THREE);
- m_actionAboutPlugins = new QAction(Tr::tr("About Qt Designer Plugins..."), m_instance);
+ m_actionAboutPlugins = new QAction(Tr::tr("About Qt Designer Plugins..."), d);
addToolAction(m_actionAboutPlugins, m_contexts, "FormEditor.AboutPlugins", mformtools,
QString(), Core::Constants::G_DEFAULT_THREE);
QObject::connect(m_actionAboutPlugins, &QAction::triggered,
@@ -760,19 +756,19 @@ IEditor *FormEditorData::createEditor()
return formWindowEditor;
}
-QDesignerFormEditorInterface *FormEditorW::designerEditor()
+QDesignerFormEditorInterface *designerEditor()
{
ensureInitStage(FullyInitialized);
return d->m_formeditor;
}
-QWidget * const *FormEditorW::designerSubWindows()
+QWidget * const *designerSubWindows()
{
ensureInitStage(SubwindowsInitialized);
return d->m_designerSubWindows;
}
-SharedTools::WidgetHost *FormEditorW::activeWidgetHost()
+SharedTools::WidgetHost *activeWidgetHost()
{
ensureInitStage(FullyInitialized);
if (d->m_editorWidget)
@@ -780,7 +776,7 @@ SharedTools::WidgetHost *FormEditorW::activeWidgetHost()
return nullptr;
}
-FormWindowEditor *FormEditorW::activeEditor()
+FormWindowEditor *activeEditor()
{
ensureInitStage(FullyInitialized);
if (d->m_editorWidget)
diff --git a/src/plugins/designer/formeditor.h b/src/plugins/designer/formeditor.h
new file mode 100644
index 0000000000..0d069e2322
--- /dev/null
+++ b/src/plugins/designer/formeditor.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QDesignerFormEditorInterface;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Core {
+class IEditor;
+class IOptionsPage;
+}
+
+namespace SharedTools { class WidgetHost; }
+
+namespace Designer {
+
+class FormWindowEditor;
+
+namespace Internal {
+
+/** This is an interface to the Designer CoreInterface to
+ * performs centralized operations.
+ * Since fully initializing Designer at startup is expensive, the
+ * setup has an internal partial initialization stage "RegisterPlugins"
+ * which is there to register the Creator plugin objects
+ * that must be present at startup (settings pages, actions).
+ * The plugin uses this stage at first by calling ensureInitStage().
+ * Requesting an editor via instance() will fully initialize the class.
+ * This is based on the assumption that the Designer settings work with
+ * no plugins loaded.
+ *
+ * The form editor shows a read-only XML editor in edit mode and Qt Designer
+ * in Design mode. */
+
+enum InitializationStage {
+ // Register Creator plugins (settings pages, actions)
+ RegisterPlugins,
+ // Subwindows of the designer are initialized
+ SubwindowsInitialized,
+ // Fully initialized for handling editor requests
+ FullyInitialized
+};
+
+// Create an instance and initialize up to stage s
+void ensureInitStage(InitializationStage s);
+// Deletes an existing instance if there is one.
+void deleteInstance();
+
+Core::IEditor *createEditor();
+
+QDesignerFormEditorInterface *designerEditor();
+QWidget * const *designerSubWindows();
+
+SharedTools::WidgetHost *activeWidgetHost();
+FormWindowEditor *activeEditor();
+QList<Core::IOptionsPage *> optionsPages();
+
+} // namespace Internal
+} // namespace Designer
diff --git a/src/plugins/designer/formeditorfactory.cpp b/src/plugins/designer/formeditorfactory.cpp
index beace0702e..278cb065f4 100644
--- a/src/plugins/designer/formeditorfactory.cpp
+++ b/src/plugins/designer/formeditorfactory.cpp
@@ -1,18 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "designertr.h"
#include "formeditorfactory.h"
-#include "formeditorw.h"
-#include "formwindoweditor.h"
+
+#include "designerconstants.h"
+#include "designertr.h"
+#include "formeditor.h"
#include <coreplugin/coreconstants.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/fsengine/fileiconprovider.h>
-#include <QCoreApplication>
-#include <QDebug>
-
using namespace Core;
using namespace Designer::Constants;
using namespace Utils;
@@ -25,7 +23,7 @@ FormEditorFactory::FormEditorFactory()
setId(K_DESIGNER_XML_EDITOR_ID);
setDisplayName(Tr::tr(C_DESIGNER_XML_DISPLAY_NAME));
addMimeType(FORM_MIMETYPE);
- setEditorCreator([] { return FormEditorW::createEditor(); });
+ setEditorCreator([] { return Designer::Internal::createEditor(); });
FileIconProvider::registerIconOverlayForSuffix(ProjectExplorer::Constants::FILEOVERLAY_UI, "ui");
}
diff --git a/src/plugins/designer/formeditorplugin.cpp b/src/plugins/designer/formeditorplugin.cpp
index 8d26f27f0f..881e6a30eb 100644
--- a/src/plugins/designer/formeditorplugin.cpp
+++ b/src/plugins/designer/formeditorplugin.cpp
@@ -1,10 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "formeditorplugin.h"
+
+#include "designerconstants.h"
#include "designertr.h"
#include "formeditorfactory.h"
-#include "formeditorplugin.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formtemplatewizardpage.h"
#ifdef CPP_ENABLED
@@ -54,7 +56,7 @@ public:
FormEditorPlugin::~FormEditorPlugin()
{
- FormEditorW::deleteInstance();
+ deleteInstance();
delete d;
}
diff --git a/src/plugins/designer/formeditorstack.cpp b/src/plugins/designer/formeditorstack.cpp
index 9325827f87..1bccd34178 100644
--- a/src/plugins/designer/formeditorstack.cpp
+++ b/src/plugins/designer/formeditorstack.cpp
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "formeditorstack.h"
+
+#include "designerconstants.h"
#include "formwindoweditor.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formwindowfile.h"
#include <widgethost.h>
diff --git a/src/plugins/designer/formeditorw.h b/src/plugins/designer/formeditorw.h
deleted file mode 100644
index 50462c2aa1..0000000000
--- a/src/plugins/designer/formeditorw.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "designerconstants.h"
-
-#include <QObject>
-
-QT_BEGIN_NAMESPACE
-class QDesignerFormEditorInterface;
-QT_END_NAMESPACE
-
-namespace Core {
-class IEditor;
-class IOptionsPage;
-}
-
-namespace SharedTools { class WidgetHost; }
-
-namespace Designer {
-
-class FormWindowEditor;
-
-namespace Internal {
-
-/** FormEditorW is a singleton that stores the Designer CoreInterface and
- * performs centralized operations. The instance() function will return an
- * instance. However, it must be manually deleted when unloading the
- * plugin. Since fully initializing Designer at startup is expensive, the
- * class has an internal partial initialisation stage "RegisterPlugins"
- * which is there to register the Creator plugin objects
- * that must be present at startup (settings pages, actions).
- * The plugin uses this stage at first by calling ensureInitStage().
- * Requesting an editor via instance() will fully initialize the class.
- * This is based on the assumption that the Designer settings work with
- * no plugins loaded.
- *
- * The form editor shows a read-only XML editor in edit mode and Qt Designer
- * in Design mode. */
-class FormEditorW : public QObject
-{
-public:
- enum InitializationStage {
- // Register Creator plugins (settings pages, actions)
- RegisterPlugins,
- // Subwindows of the designer are initialized
- SubwindowsInitialized,
- // Fully initialized for handling editor requests
- FullyInitialized
- };
-
- // Create an instance and initialize up to stage s
- static void ensureInitStage(InitializationStage s);
- // Deletes an existing instance if there is one.
- static void deleteInstance();
-
- static Core::IEditor *createEditor();
-
- static QDesignerFormEditorInterface *designerEditor();
- static QWidget * const *designerSubWindows();
-
- static SharedTools::WidgetHost *activeWidgetHost();
- static FormWindowEditor *activeEditor();
- static QList<Core::IOptionsPage *> optionsPages();
-};
-
-} // namespace Internal
-} // namespace Designer
diff --git a/src/plugins/designer/formtemplatewizardpage.cpp b/src/plugins/designer/formtemplatewizardpage.cpp
index c6417522f4..68e3fb1711 100644
--- a/src/plugins/designer/formtemplatewizardpage.cpp
+++ b/src/plugins/designer/formtemplatewizardpage.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "designertr.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formtemplatewizardpage.h"
#include <projectexplorer/jsonwizard/jsonwizardpagefactory.h>
@@ -58,7 +58,7 @@ bool FormPageFactory::validateData(Utils::Id typeId, const QVariant &data, QStri
FormTemplateWizardPage::FormTemplateWizardPage(QWidget * parent) :
Utils::WizardPage(parent),
- m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(FormEditorW::designerEditor())),
+ m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(designerEditor())),
m_templateSelected(m_newFormWidget->hasCurrentTemplate())
{
setTitle(Tr::tr("Choose a Form Template"));
diff --git a/src/plugins/designer/gotoslot_test.cpp b/src/plugins/designer/gotoslot_test.cpp
index f4562a41e7..f804c2259e 100644
--- a/src/plugins/designer/gotoslot_test.cpp
+++ b/src/plugins/designer/gotoslot_test.cpp
@@ -3,7 +3,7 @@
#include "formeditorplugin.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/testdatadir.h>
@@ -147,7 +147,7 @@ public:
waitForFilesInGlobalSnapshot({cppFile, hFile});
// Execute "Go To Slot"
- QDesignerIntegrationInterface *integration = FormEditorW::designerEditor()->integration();
+ QDesignerIntegrationInterface *integration = designerEditor()->integration();
QVERIFY(integration);
integration->emitNavigateToSlot("pushButton", "clicked()", QStringList());
diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp
index db96523c36..010ab85a82 100644
--- a/src/plugins/designer/qtcreatorintegration.cpp
+++ b/src/plugins/designer/qtcreatorintegration.cpp
@@ -1,10 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "qtcreatorintegration.h"
+
+#include "designerconstants.h"
#include "designertr.h"
-#include "formeditorw.h"
+#include "formeditor.h"
#include "formwindoweditor.h"
-#include "qtcreatorintegration.h"
+
#include <widgethost.h>
#include <designer/cpp/formclasswizardpage.h>
@@ -16,20 +19,24 @@
#include <cppeditor/cppworkingcopy.h>
#include <cppeditor/insertionpointlocator.h>
#include <cppeditor/symbolfinder.h>
+
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
+
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/messagemanager.h>
-#include <texteditor/texteditor.h>
-#include <texteditor/textdocument.h>
+
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/extracompiler.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
+#include <texteditor/texteditor.h>
+#include <texteditor/textdocument.h>
+
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
@@ -152,14 +159,14 @@ void QtCreatorIntegration::slotDesignerHelpRequested(const QString &manual, cons
void QtCreatorIntegration::updateSelection()
{
- if (SharedTools::WidgetHost *host = FormEditorW::activeWidgetHost())
+ if (SharedTools::WidgetHost *host = activeWidgetHost())
host->updateFormWindowSelectionHandles(true);
QDesignerIntegration::updateSelection();
}
QWidget *QtCreatorIntegration::containerWindow(QWidget * /*widget*/) const
{
- if (SharedTools::WidgetHost *host = FormEditorW::activeWidgetHost())
+ if (SharedTools::WidgetHost *host = activeWidgetHost())
return host->integrationContainer();
return nullptr;
}
@@ -431,7 +438,7 @@ void QtCreatorIntegration::slotNavigateToSlot(const QString &objectName, const Q
{
QString errorMessage;
if (!navigateToSlot(objectName, signalSignature, parameterNames, &errorMessage) && !errorMessage.isEmpty())
- QMessageBox::warning(FormEditorW::designerEditor()->topLevel(), Tr::tr("Error finding/adding a slot."), errorMessage);
+ QMessageBox::warning(designerEditor()->topLevel(), Tr::tr("Error finding/adding a slot."), errorMessage);
}
// Build name of the class as generated by uic, insert Ui namespace
@@ -452,8 +459,8 @@ static Document::Ptr getParsedDocument(const FilePath &filePath,
Snapshot &snapshot)
{
QByteArray src;
- if (workingCopy.contains(filePath)) {
- src = workingCopy.source(filePath);
+ if (const auto source = workingCopy.source(filePath)) {
+ src = *source;
} else {
Utils::FileReader reader;
if (reader.fetch(filePath)) // ### FIXME error reporting
@@ -476,7 +483,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
{
using DocumentMap = QMap<int, Document::Ptr>;
- const Utils::FilePath currentUiFile = FormEditorW::activeEditor()->document()->filePath();
+ const Utils::FilePath currentUiFile = activeEditor()->document()->filePath();
#if 0
return Designer::Internal::navigateToSlot(currentUiFile.toString(), objectName,
signalSignature, parameterNames, errorMessage);
@@ -493,10 +500,10 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
// Retrieve code model snapshot restricted to project of ui file or the working copy.
Snapshot docTable = CppEditor::CppModelManager::instance()->snapshot();
Snapshot newDocTable;
- const Project *uiProject = SessionManager::projectForFile(currentUiFile);
+ const Project *uiProject = ProjectManager::projectForFile(currentUiFile);
if (uiProject) {
for (Snapshot::const_iterator i = docTable.begin(), ei = docTable.end(); i != ei; ++i) {
- const Project *project = SessionManager::projectForFile(i.key());
+ const Project *project = ProjectManager::projectForFile(i.key());
if (project == uiProject)
newDocTable.insert(i.value());
}
@@ -529,7 +536,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName,
return false;
}
- QDesignerFormWindowInterface *fwi = FormEditorW::activeWidgetHost()->formWindow();
+ QDesignerFormWindowInterface *fwi = activeWidgetHost()->formWindow();
QString uiClass;
const Class *cl = nullptr;
@@ -639,7 +646,7 @@ void QtCreatorIntegration::handleSymbolRenameStage1(
return;
// Get ExtraCompiler.
- const Project * const project = SessionManager::projectForFile(uiFile);
+ const Project * const project = ProjectManager::projectForFile(uiFile);
if (!project) {
return reportRenamingError(oldName, Designer::Tr::tr("File \"%1\" not found in project.")
.arg(uiFile.toUserOutput()));
diff --git a/src/plugins/designer/resourcehandler.cpp b/src/plugins/designer/resourcehandler.cpp
index 29838283d5..fe2cdd28c6 100644
--- a/src/plugins/designer/resourcehandler.cpp
+++ b/src/plugins/designer/resourcehandler.cpp
@@ -2,13 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "resourcehandler.h"
+
#include "designerconstants.h"
+#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+
#include <resourceeditor/resourcenode.h>
+
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -42,10 +45,10 @@ void ResourceHandler::ensureInitialized()
Qt::QueuedConnection);
};
- for (Project *p : SessionManager::projects())
+ for (Project *p : ProjectManager::projects())
connector(p);
- connect(SessionManager::instance(), &SessionManager::projectAdded, this, connector);
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, connector);
m_originalUiQrcPaths = m_form->activeResourceFilePaths();
if (Designer::Constants::Internal::debug)
@@ -68,7 +71,7 @@ void ResourceHandler::updateResourcesHelper(bool updateProjectResources)
qDebug() << "ResourceHandler::updateResources()" << fileName;
// Filename could change in the meantime.
- Project *project = SessionManager::projectForFile(Utils::FilePath::fromUserInput(fileName));
+ Project *project = ProjectManager::projectForFile(Utils::FilePath::fromUserInput(fileName));
const bool dirty = m_form->property("_q_resourcepathchanged").toBool();
if (dirty)
m_form->setDirty(true);
diff --git a/src/plugins/designer/settingspage.cpp b/src/plugins/designer/settingspage.cpp
index a3e3ce3808..e1998ffd5e 100644
--- a/src/plugins/designer/settingspage.cpp
+++ b/src/plugins/designer/settingspage.cpp
@@ -1,48 +1,46 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "designertr.h"
-#include "formeditorw.h"
#include "settingspage.h"
+#include "designerconstants.h"
+#include "designertr.h"
+#include "formeditor.h"
+
#include <coreplugin/icontext.h>
#include <utils/stringutils.h>
#include <QDesignerOptionsPageInterface>
#include <QCoreApplication>
+#include <QVBoxLayout>
-using namespace Designer::Internal;
+namespace Designer::Internal {
-SettingsPage::SettingsPage(QDesignerOptionsPageInterface *designerPage) :
- Core::IOptionsPage(nullptr, false),
- m_designerPage(designerPage)
+class SettingsPageWidget : public Core::IOptionsPageWidget
{
- setId(Utils::Id::fromString(m_designerPage->name()));
- setDisplayName(m_designerPage->name());
- setCategory(Designer::Constants::SETTINGS_CATEGORY);
-}
+public:
+ explicit SettingsPageWidget(QDesignerOptionsPageInterface *designerPage)
+ : m_designerPage(designerPage)
+ {
+ auto vbox = new QVBoxLayout(this);
+ vbox->addWidget(m_designerPage->createPage(nullptr));
+ }
-QWidget *SettingsPage::widget()
-{
- m_initialized = true;
- if (!m_widget)
- m_widget = m_designerPage->createPage(nullptr);
- return m_widget;
+ void apply() { m_designerPage->apply(); }
+ void finish() { m_designerPage->finish(); }
-}
+ QDesignerOptionsPageInterface *m_designerPage;
+};
-void SettingsPage::apply()
-{
- if (m_initialized)
- m_designerPage->apply();
-}
-void SettingsPage::finish()
+SettingsPage::SettingsPage(QDesignerOptionsPageInterface *designerPage)
+ : Core::IOptionsPage(false)
{
- if (m_initialized)
- m_designerPage->finish();
- delete m_widget;
+ setId(Utils::Id::fromString(designerPage->name()));
+ setDisplayName(designerPage->name());
+ setCategory(Designer::Constants::SETTINGS_CATEGORY);
+ setWidgetCreator([designerPage] { return new SettingsPageWidget(designerPage); });
}
SettingsPageProvider::SettingsPageProvider()
@@ -58,9 +56,9 @@ QList<Core::IOptionsPage *> SettingsPageProvider::pages() const
if (!m_initialized) {
// get options pages from designer
m_initialized = true;
- FormEditorW::ensureInitStage(FormEditorW::RegisterPlugins);
+ ensureInitStage(RegisterPlugins);
}
- return FormEditorW::optionsPages();
+ return optionsPages();
}
bool SettingsPageProvider::matches(const QRegularExpression &regex) const
@@ -102,3 +100,5 @@ bool SettingsPageProvider::matches(const QRegularExpression &regex) const
}
return false;
}
+
+} // Designer::Internal
diff --git a/src/plugins/designer/settingspage.h b/src/plugins/designer/settingspage.h
index cdfb32c003..03206109db 100644
--- a/src/plugins/designer/settingspage.h
+++ b/src/plugins/designer/settingspage.h
@@ -5,38 +5,20 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
QT_BEGIN_NAMESPACE
class QDesignerOptionsPageInterface;
QT_END_NAMESPACE
-namespace Designer {
-namespace Internal {
-
-class SettingsPageWidget;
+namespace Designer::Internal {
class SettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
explicit SettingsPage(QDesignerOptionsPageInterface *designerPage);
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QDesignerOptionsPageInterface *m_designerPage;
- bool m_initialized = false;
- QPointer<QWidget> m_widget;
};
class SettingsPageProvider : public Core::IOptionsPageProvider
{
- Q_OBJECT
-
public:
SettingsPageProvider();
@@ -48,5 +30,4 @@ private:
mutable QStringList m_keywords;
};
-} // namespace Internal
-} // namespace Designer
+} // Designer::Internal
diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp
index 5274214f92..929c30c339 100644
--- a/src/plugins/diffeditor/diffeditor.cpp
+++ b/src/plugins/diffeditor/diffeditor.cpp
@@ -185,7 +185,7 @@ DiffEditor::DiffEditor()
policy.setHorizontalPolicy(QSizePolicy::Expanding);
m_entriesComboBox->setSizePolicy(policy);
connect(m_entriesComboBox, &QComboBox::currentIndexChanged,
- this, &DiffEditor::setCurrentDiffFileIndex);
+ this, &DiffEditor::currentIndexChanged);
m_toolBar->addWidget(m_entriesComboBox);
QLabel *contextLabel = new QLabel(m_toolBar);
@@ -309,7 +309,6 @@ TextEditorWidget *DiffEditor::sideEditorWidget(DiffSide side) const
return m_sideBySideView->sideEditorWidget(side);
}
-
void DiffEditor::documentHasChanged()
{
GuardLocker guard(m_ignoreChanges);
@@ -319,7 +318,10 @@ void DiffEditor::documentHasChanged()
currentView()->setDiff(diffFileList);
m_entriesComboBox->clear();
- for (const FileData &diffFile : diffFileList) {
+ const QString startupFile = m_document->startupFile();
+ int startupFileIndex = -1;
+ for (int i = 0, total = diffFileList.count(); i < total; ++i) {
+ const FileData &diffFile = diffFileList.at(i);
const DiffFileInfo &leftEntry = diffFile.fileInfo[LeftSide];
const DiffFileInfo &rightEntry = diffFile.fileInfo[RightSide];
const QString leftShortFileName = FilePath::fromString(leftEntry.fileName).fileName();
@@ -332,30 +334,20 @@ void DiffEditor::documentHasChanged()
if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
itemToolTip = leftEntry.fileName;
} else {
- itemToolTip = Tr::tr("[%1] vs. [%2] %3")
- .arg(leftEntry.typeInfo,
- rightEntry.typeInfo,
- leftEntry.fileName);
+ itemToolTip = Tr::tr("[%1] vs. [%2] %3").arg(
+ leftEntry.typeInfo, rightEntry.typeInfo, leftEntry.fileName);
}
} else {
- if (leftShortFileName == rightShortFileName) {
+ if (leftShortFileName == rightShortFileName)
itemText = leftShortFileName;
- } else {
- itemText = Tr::tr("%1 vs. %2")
- .arg(leftShortFileName,
- rightShortFileName);
- }
+ else
+ itemText = Tr::tr("%1 vs. %2").arg(leftShortFileName, rightShortFileName);
if (leftEntry.typeInfo.isEmpty() && rightEntry.typeInfo.isEmpty()) {
- itemToolTip = Tr::tr("%1 vs. %2")
- .arg(leftEntry.fileName,
- rightEntry.fileName);
+ itemToolTip = Tr::tr("%1 vs. %2").arg(leftEntry.fileName, rightEntry.fileName);
} else {
- itemToolTip = Tr::tr("[%1] %2 vs. [%3] %4")
- .arg(leftEntry.typeInfo,
- leftEntry.fileName,
- rightEntry.typeInfo,
- rightEntry.fileName);
+ itemToolTip = Tr::tr("[%1] %2 vs. [%3] %4").arg(leftEntry.typeInfo,
+ leftEntry.fileName, rightEntry.typeInfo, rightEntry.fileName);
}
}
m_entriesComboBox->addItem(itemText);
@@ -365,7 +357,21 @@ void DiffEditor::documentHasChanged()
rightEntry.fileName, Qt::UserRole + 1);
m_entriesComboBox->setItemData(m_entriesComboBox->count() - 1,
itemToolTip, Qt::ToolTipRole);
+ if (startupFileIndex < 0) {
+ const bool isStartup = m_currentFileChunk.first.isEmpty()
+ && m_currentFileChunk.second.isEmpty()
+ && startupFile.endsWith(rightEntry.fileName);
+ const bool isSame = m_currentFileChunk.first == leftEntry.fileName
+ && m_currentFileChunk.second == rightEntry.fileName;
+ if (isStartup || isSame)
+ startupFileIndex = i;
+ }
}
+
+ currentView()->endOperation();
+ m_currentFileChunk = {};
+ if (startupFileIndex >= 0)
+ setCurrentDiffFileIndex(startupFileIndex);
}
void DiffEditor::toggleDescription()
@@ -439,6 +445,7 @@ void DiffEditor::prepareForReload()
m_whitespaceButtonAction->setChecked(m_document->ignoreWhitespace());
}
currentView()->beginOperation();
+ currentView()->setMessage(Tr::tr("Waiting for data..."));
}
void DiffEditor::reloadHasFinished(bool success)
@@ -446,29 +453,8 @@ void DiffEditor::reloadHasFinished(bool success)
if (!currentView())
return;
- currentView()->endOperation(success);
-
- int index = -1;
- const QString startupFile = m_document->startupFile();
- const QList<FileData> &diffFileList = m_document->diffFiles();
- const int count = diffFileList.count();
- for (int i = 0; i < count; i++) {
- const FileData &diffFile = diffFileList.at(i);
- const DiffFileInfo &leftEntry = diffFile.fileInfo[LeftSide];
- const DiffFileInfo &rightEntry = diffFile.fileInfo[RightSide];
- if ((m_currentFileChunk.first.isEmpty()
- && m_currentFileChunk.second.isEmpty()
- && startupFile.endsWith(rightEntry.fileName))
- || (m_currentFileChunk.first == leftEntry.fileName
- && m_currentFileChunk.second == rightEntry.fileName)) {
- index = i;
- break;
- }
- }
-
- m_currentFileChunk = {};
- if (index >= 0)
- setCurrentDiffFileIndex(index);
+ if (!success)
+ currentView()->setMessage(Tr::tr("Retrieving data failed."));
}
void DiffEditor::updateEntryToolTip()
@@ -478,14 +464,19 @@ void DiffEditor::updateEntryToolTip()
m_entriesComboBox->setToolTip(toolTip);
}
-void DiffEditor::setCurrentDiffFileIndex(int index)
+void DiffEditor::currentIndexChanged(int index)
{
if (m_ignoreChanges.isLocked())
return;
+ GuardLocker guard(m_ignoreChanges);
+ setCurrentDiffFileIndex(index);
+}
+
+void DiffEditor::setCurrentDiffFileIndex(int index)
+{
QTC_ASSERT((index < 0) != (m_entriesComboBox->count() > 0), return);
- GuardLocker guard(m_ignoreChanges);
m_currentDiffFileIndex = index;
currentView()->setCurrentDiffFileIndex(index);
@@ -563,7 +554,7 @@ void DiffEditor::addView(IDiffView *view)
if (m_views.count() == 1)
setCurrentView(view);
- connect(view, &IDiffView::currentDiffFileIndexChanged, this, &DiffEditor::setCurrentDiffFileIndex);
+ connect(view, &IDiffView::currentDiffFileIndexChanged, this, &DiffEditor::currentIndexChanged);
}
IDiffView *DiffEditor::currentView() const
diff --git a/src/plugins/diffeditor/diffeditor.h b/src/plugins/diffeditor/diffeditor.h
index a801ee953b..49922d323e 100644
--- a/src/plugins/diffeditor/diffeditor.h
+++ b/src/plugins/diffeditor/diffeditor.h
@@ -53,6 +53,7 @@ private:
void ignoreWhitespaceHasChanged();
void prepareForReload();
void reloadHasFinished(bool success);
+ void currentIndexChanged(int index);
void setCurrentDiffFileIndex(int index);
void documentStateChanged();
diff --git a/src/plugins/diffeditor/diffeditorcontroller.cpp b/src/plugins/diffeditor/diffeditorcontroller.cpp
index dcce09f0b2..1ab7bfccb2 100644
--- a/src/plugins/diffeditor/diffeditorcontroller.cpp
+++ b/src/plugins/diffeditor/diffeditorcontroller.cpp
@@ -12,6 +12,7 @@
#include <utils/qtcassert.h>
using namespace Core;
+using namespace Tasking;
using namespace Utils;
namespace DiffEditor {
diff --git a/src/plugins/diffeditor/diffeditorcontroller.h b/src/plugins/diffeditor/diffeditorcontroller.h
index f52f2af1a0..1f791b2dcb 100644
--- a/src/plugins/diffeditor/diffeditorcontroller.h
+++ b/src/plugins/diffeditor/diffeditorcontroller.h
@@ -6,7 +6,7 @@
#include "diffeditor_global.h"
#include "diffutils.h"
-#include <utils/tasktree.h>
+#include <solutions/tasking/tasktree.h>
#include <QObject>
@@ -61,7 +61,7 @@ signals:
protected:
// Core functions:
- void setReloadRecipe(const Utils::Tasking::Group &recipe) { m_reloadRecipe = recipe; }
+ void setReloadRecipe(const Tasking::Group &recipe) { m_reloadRecipe = recipe; }
void setDiffFiles(const QList<FileData> &diffFileList);
// Optional:
void setDisplayName(const QString &name) { m_displayName = name; }
@@ -74,8 +74,8 @@ private:
Internal::DiffEditorDocument *const m_document;
QString m_displayName;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
- Utils::Tasking::Group m_reloadRecipe;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
+ Tasking::Group m_reloadRecipe;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DiffEditorController::PatchOptions)
diff --git a/src/plugins/diffeditor/diffeditordocument.cpp b/src/plugins/diffeditor/diffeditordocument.cpp
index 05b1684c8a..1d43064d60 100644
--- a/src/plugins/diffeditor/diffeditordocument.cpp
+++ b/src/plugins/diffeditor/diffeditordocument.cpp
@@ -291,8 +291,8 @@ Core::IDocument::OpenResult DiffEditorDocument::open(QString *errorString, const
return OpenResult::ReadError;
}
- bool ok = false;
- QList<FileData> fileDataList = DiffUtils::readPatch(patch, &ok);
+ const std::optional<QList<FileData>> fileDataList = DiffUtils::readPatch(patch);
+ bool ok = fileDataList.has_value();
if (!ok) {
*errorString = Tr::tr("Could not parse patch file \"%1\". "
"The content is not of unified diff format.")
@@ -302,7 +302,7 @@ Core::IDocument::OpenResult DiffEditorDocument::open(QString *errorString, const
emit temporaryStateChanged();
setFilePath(filePath.absoluteFilePath());
setWorkingDirectory(filePath.absoluteFilePath());
- setDiffFiles(fileDataList);
+ setDiffFiles(*fileDataList);
}
endReload(ok);
if (!ok && readResult == TextFileFormat::ReadEncodingError)
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index 2a81320665..e5bed027a3 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -13,12 +13,13 @@
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <texteditor/textdocument.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/differ.h>
#include <utils/fileutils.h>
-#include <utils/futuresynchronizer.h>
#include <utils/qtcassert.h>
#include <QAction>
@@ -47,13 +48,12 @@ public:
m_ignoreWhitespace(ignoreWhitespace)
{}
- void operator()(QFutureInterface<FileData> &futureInterface,
- const ReloadInput &reloadInput) const
+ void operator()(QPromise<FileData> &promise, const ReloadInput &reloadInput) const
{
if (reloadInput.text[LeftSide] == reloadInput.text[RightSide])
return; // We show "No difference" in this case, regardless if it's binary or not
- Differ differ(&futureInterface);
+ Differ differ(QFuture<void>(promise.future()));
FileData fileData;
if (!reloadInput.binaryFiles) {
@@ -85,7 +85,7 @@ public:
fileData.fileInfo = reloadInput.fileInfo;
fileData.fileOperation = reloadInput.fileOperation;
fileData.binaryFiles = reloadInput.binaryFiles;
- futureInterface.reportResult(fileData);
+ promise.addResult(fileData);
}
private:
@@ -114,11 +114,12 @@ DiffFilesController::DiffFilesController(IDocument *document)
const auto setupTree = [this, storage](TaskTree &taskTree) {
QList<std::optional<FileData>> *outputList = storage.activeStorage();
- const auto setupDiff = [this](AsyncTask<FileData> &async, const ReloadInput &reloadInput) {
- async.setAsyncCallData(DiffFile(ignoreWhitespace(), contextLineCount()), reloadInput);
- async.setFutureSynchronizer(Internal::DiffEditorPlugin::futureSynchronizer());
+ const auto setupDiff = [this](Async<FileData> &async, const ReloadInput &reloadInput) {
+ async.setConcurrentCallData(
+ DiffFile(ignoreWhitespace(), contextLineCount()), reloadInput);
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
};
- const auto onDiffDone = [outputList](const AsyncTask<FileData> &async, int i) {
+ const auto onDiffDone = [outputList](const Async<FileData> &async, int i) {
if (async.isResultAvailable())
(*outputList)[i] = async.result();
};
@@ -129,13 +130,13 @@ DiffFilesController::DiffFilesController(IDocument *document)
using namespace std::placeholders;
QList<TaskItem> tasks {parallel, optional};
for (int i = 0; i < inputList.size(); ++i) {
- tasks.append(Async<FileData>(std::bind(setupDiff, _1, inputList.at(i)),
+ tasks.append(AsyncTask<FileData>(std::bind(setupDiff, _1, inputList.at(i)),
std::bind(onDiffDone, _1, i)));
}
taskTree.setupRoot(tasks);
};
const auto onTreeDone = [this, storage] {
- const QList<std::optional<FileData>> &results = *storage.activeStorage();
+ const QList<std::optional<FileData>> &results = *storage;
QList<FileData> finalList;
for (const std::optional<FileData> &result : results) {
if (result.has_value())
@@ -149,7 +150,7 @@ DiffFilesController::DiffFilesController(IDocument *document)
const Group root = {
Storage(storage),
- Tree(setupTree),
+ TaskTreeTask(setupTree),
OnGroupDone(onTreeDone),
OnGroupError(onTreeError)
};
@@ -416,12 +417,10 @@ public:
DiffEditorFactory m_editorFactory;
DiffEditorServiceImpl m_service;
- FutureSynchronizer m_futureSynchronizer;
};
DiffEditorPluginPrivate::DiffEditorPluginPrivate()
{
- m_futureSynchronizer.setCancelOnWait(true);
//register actions
ActionContainer *toolsContainer = ActionManager::actionContainer(Core::Constants::M_TOOLS);
toolsContainer->insertGroup(Core::Constants::G_TOOLS_DEBUG, Constants::G_TOOLS_DIFF);
@@ -536,12 +535,6 @@ void DiffEditorPlugin::initialize()
d = new DiffEditorPluginPrivate;
}
-FutureSynchronizer *DiffEditorPlugin::futureSynchronizer()
-{
- QTC_ASSERT(s_instance, return nullptr);
- return &s_instance->d->m_futureSynchronizer;
-}
-
} // namespace Internal
} // namespace DiffEditor
@@ -771,13 +764,13 @@ void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
QCOMPARE(result, patchText);
- bool ok;
- QList<FileData> resultList = DiffUtils::readPatch(result, &ok);
+ const std::optional<QList<FileData>> resultList = DiffUtils::readPatch(result);
+ const bool ok = resultList.has_value();
QVERIFY(ok);
- QCOMPARE(resultList.count(), 1);
- for (int i = 0; i < resultList.count(); i++) {
- const FileData &resultFileData = resultList.at(i);
+ QCOMPARE(resultList->count(), 1);
+ for (int i = 0; i < resultList->count(); i++) {
+ const FileData &resultFileData = resultList->at(i);
QCOMPARE(resultFileData.fileInfo[LeftSide].fileName, fileName);
QCOMPARE(resultFileData.fileInfo[RightSide].fileName, fileName);
QCOMPARE(resultFileData.chunks.count(), 1);
@@ -1335,14 +1328,14 @@ void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
QFETCH(QString, sourcePatch);
QFETCH(QList<FileData>, fileDataList);
- bool ok;
- const QList<FileData> &result = DiffUtils::readPatch(sourcePatch, &ok);
+ const std::optional<QList<FileData>> result = DiffUtils::readPatch(sourcePatch);
+ const bool ok = result.has_value();
QVERIFY(ok);
- QCOMPARE(result.count(), fileDataList.count());
+ QCOMPARE(result->count(), fileDataList.count());
for (int i = 0; i < fileDataList.count(); i++) {
const FileData &origFileData = fileDataList.at(i);
- const FileData &resultFileData = result.at(i);
+ const FileData &resultFileData = result->at(i);
QCOMPARE(resultFileData.fileInfo[LeftSide].fileName, origFileData.fileInfo[LeftSide].fileName);
QCOMPARE(resultFileData.fileInfo[LeftSide].typeInfo, origFileData.fileInfo[LeftSide].typeInfo);
QCOMPARE(resultFileData.fileInfo[RightSide].fileName, origFileData.fileInfo[RightSide].fileName);
diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h
index 66849a7387..17b6a2d430 100644
--- a/src/plugins/diffeditor/diffeditorplugin.h
+++ b/src/plugins/diffeditor/diffeditorplugin.h
@@ -6,8 +6,6 @@
#include <coreplugin/diffservice.h>
#include <extensionsystem/iplugin.h>
-namespace Utils { class FutureSynchronizer; }
-
namespace DiffEditor {
namespace Internal {
@@ -34,8 +32,6 @@ public:
void initialize() final;
- static Utils::FutureSynchronizer *futureSynchronizer();
-
private:
class DiffEditorPluginPrivate *d = nullptr;
diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp
index f9d7d6b264..afb7520303 100644
--- a/src/plugins/diffeditor/diffutils.cpp
+++ b/src/plugins/diffeditor/diffutils.cpp
@@ -6,7 +6,8 @@
#include <utils/algorithm.h>
#include <utils/differ.h>
-#include <QFutureInterfaceBase>
+#include <QFuture>
+#include <QPromise>
#include <QRegularExpression>
#include <QStringList>
#include <QTextStream>
@@ -556,7 +557,7 @@ static QList<RowData> readLines(QStringView patch, bool lastChunk, bool *lastChu
int noNewLineInDelete = -1;
int noNewLineInInsert = -1;
- const QVector<QStringView> lines = patch.split(newLine);
+ const QList<QStringView> lines = patch.split(newLine);
int i;
for (i = 0; i < lines.size(); i++) {
QStringView line = lines.at(i);
@@ -794,7 +795,7 @@ static QList<ChunkData> readChunks(QStringView patch, bool *lastChunkAtTheEndOfF
QList<ChunkData> chunkDataList;
int position = -1;
- QVector<int> startingPositions; // store starting positions of @@
+ QList<int> startingPositions; // store starting positions of @@
if (patch.startsWith(QStringLiteral("@@ -")))
startingPositions.append(position + 1);
@@ -892,7 +893,7 @@ static FileData readDiffHeaderAndChunks(QStringView headerAndChunks, bool *ok)
}
-static QList<FileData> readDiffPatch(QStringView patch, bool *ok, QFutureInterfaceBase *jobController)
+static void readDiffPatch(QPromise<QList<FileData>> &promise, QStringView patch)
{
const QRegularExpression diffRegExp("(?:\\n|^)" // new line of the beginning of a patch
"(" // either
@@ -911,23 +912,20 @@ static QList<FileData> readDiffPatch(QStringView patch, bool *ok, QFutureInterfa
")"); // end of or
bool readOk = false;
-
QList<FileData> fileDataList;
-
QRegularExpressionMatch diffMatch = diffRegExp.match(patch);
if (diffMatch.hasMatch()) {
readOk = true;
int lastPos = -1;
do {
- if (jobController && jobController->isCanceled())
- return {};
+ if (promise.isCanceled())
+ return;
int pos = diffMatch.capturedStart();
if (lastPos >= 0) {
QStringView headerAndChunks = patch.mid(lastPos, pos - lastPos);
- const FileData fileData = readDiffHeaderAndChunks(headerAndChunks,
- &readOk);
+ const FileData fileData = readDiffHeaderAndChunks(headerAndChunks, &readOk);
if (!readOk)
break;
@@ -942,21 +940,15 @@ static QList<FileData> readDiffPatch(QStringView patch, bool *ok, QFutureInterfa
if (readOk) {
QStringView headerAndChunks = patch.mid(lastPos, patch.size() - lastPos - 1);
- const FileData fileData = readDiffHeaderAndChunks(headerAndChunks,
- &readOk);
+ const FileData fileData = readDiffHeaderAndChunks(headerAndChunks, &readOk);
if (readOk)
fileDataList.append(fileData);
}
}
-
- if (ok)
- *ok = readOk;
-
if (!readOk)
- return {};
-
- return fileDataList;
+ return;
+ promise.addResult(fileDataList);
}
// The git diff patch format (ChangeFile, NewFile, DeleteFile)
@@ -1203,11 +1195,11 @@ static bool detectFileData(QStringView patch, FileData *fileData, QStringView *r
return detectIndexAndBinary(*remainingPatch, fileData, remainingPatch);
}
-static QList<FileData> readGitPatch(QStringView patch, bool *ok, QFutureInterfaceBase *jobController)
+static void readGitPatch(QPromise<QList<FileData>> &promise, QStringView patch)
{
int position = -1;
- QVector<int> startingPositions; // store starting positions of git headers
+ QList<int> startingPositions; // store starting positions of git headers
if (patch.startsWith(QStringLiteral("diff --git ")))
startingPositions.append(position + 1);
@@ -1221,13 +1213,12 @@ static QList<FileData> readGitPatch(QStringView patch, bool *ok, QFutureInterfac
};
const QChar newLine('\n');
- bool readOk = true;
- QVector<PatchInfo> patches;
+ QList<PatchInfo> patches;
const int count = startingPositions.size();
for (int i = 0; i < count; i++) {
- if (jobController && jobController->isCanceled())
- return {};
+ if (promise.isCanceled())
+ return;
const int diffStart = startingPositions.at(i);
const int diffEnd = (i < count - 1)
@@ -1242,65 +1233,53 @@ static QList<FileData> readGitPatch(QStringView patch, bool *ok, QFutureInterfac
FileData fileData;
QStringView remainingFileDiff;
- readOk = detectFileData(fileDiff, &fileData, &remainingFileDiff);
-
- if (!readOk)
- break;
+ if (!detectFileData(fileDiff, &fileData, &remainingFileDiff))
+ return;
patches.append(PatchInfo { remainingFileDiff, fileData });
}
- if (!readOk) {
- if (ok)
- *ok = readOk;
- return {};
- }
+ if (patches.isEmpty())
+ return;
- if (jobController)
- jobController->setProgressRange(0, patches.size());
+ promise.setProgressRange(0, patches.size());
QList<FileData> fileDataList;
- readOk = false;
int i = 0;
for (const auto &patchInfo : std::as_const(patches)) {
- if (jobController) {
- if (jobController->isCanceled())
- return {};
- jobController->setProgressValue(i++);
- }
+ if (promise.isCanceled())
+ return;
+ promise.setProgressValue(i++);
FileData fileData = patchInfo.fileData;
+ bool readOk = false;
if (!patchInfo.patch.isEmpty() || fileData.fileOperation == FileData::ChangeFile)
fileData.chunks = readChunks(patchInfo.patch, &fileData.lastChunkAtTheEndOfFile, &readOk);
else
readOk = true;
if (!readOk)
- break;
+ return;
fileDataList.append(fileData);
}
+ promise.addResult(fileDataList);
+}
- if (ok)
- *ok = readOk;
-
- if (!readOk)
+std::optional<QList<FileData>> DiffUtils::readPatch(const QString &patch)
+{
+ QPromise<QList<FileData>> promise;
+ promise.start();
+ readPatchWithPromise(promise, patch);
+ if (promise.future().resultCount() == 0)
return {};
-
- return fileDataList;
+ return promise.future().result();
}
-QList<FileData> DiffUtils::readPatch(const QString &patch, bool *ok,
- QFutureInterfaceBase *jobController)
+void DiffUtils::readPatchWithPromise(QPromise<QList<FileData>> &promise, const QString &patch)
{
- bool readOk = false;
-
- QList<FileData> fileDataList;
-
- if (jobController) {
- jobController->setProgressRange(0, 1);
- jobController->setProgressValue(0);
- }
+ promise.setProgressRange(0, 1);
+ promise.setProgressValue(0);
QStringView croppedPatch = QStringView(patch);
// Crop e.g. "-- \n2.10.2.windows.1\n\n" at end of file
const QRegularExpression formatPatchEndingRegExp("(\\n-- \\n\\S*\\n\\n$)");
@@ -1308,14 +1287,9 @@ QList<FileData> DiffUtils::readPatch(const QString &patch, bool *ok,
if (match.hasMatch())
croppedPatch = croppedPatch.left(match.capturedStart() + 1);
- fileDataList = readGitPatch(croppedPatch, &readOk, jobController);
- if (!readOk)
- fileDataList = readDiffPatch(croppedPatch, &readOk, jobController);
-
- if (ok)
- *ok = readOk;
-
- return fileDataList;
+ readGitPatch(promise, croppedPatch);
+ if (promise.future().resultCount() == 0)
+ readDiffPatch(promise, croppedPatch);
}
} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/diffutils.h b/src/plugins/diffeditor/diffutils.h
index 65fcda4678..e128f01012 100644
--- a/src/plugins/diffeditor/diffutils.h
+++ b/src/plugins/diffeditor/diffutils.h
@@ -14,7 +14,8 @@
#include <array>
QT_BEGIN_NAMESPACE
-class QFutureInterfaceBase;
+template <class T>
+class QPromise;
QT_END_NAMESPACE
namespace Utils { class Diff; }
@@ -142,9 +143,8 @@ public:
const QString &rightFileName,
bool lastChunk = false);
static QString makePatch(const QList<FileData> &fileDataList);
- static QList<FileData> readPatch(const QString &patch,
- bool *ok = nullptr,
- QFutureInterfaceBase *jobController = nullptr);
+ static std::optional<QList<FileData>> readPatch(const QString &patch);
+ static void readPatchWithPromise(QPromise<QList<FileData>> &promise, const QString &patch);
};
} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/diffview.cpp b/src/plugins/diffeditor/diffview.cpp
index 8ff444066a..686f9798b2 100644
--- a/src/plugins/diffeditor/diffview.cpp
+++ b/src/plugins/diffeditor/diffview.cpp
@@ -117,7 +117,6 @@ void UnifiedView::beginOperation()
DiffEditorDocument *document = m_widget->diffDocument();
if (document && document->state() == DiffEditorDocument::LoadOK)
m_widget->saveState();
- m_widget->clear(Tr::tr("Waiting for data..."));
}
void UnifiedView::setDiff(const QList<FileData> &diffFileList)
@@ -126,13 +125,15 @@ void UnifiedView::setDiff(const QList<FileData> &diffFileList)
m_widget->setDiff(diffFileList);
}
-void UnifiedView::endOperation(bool success)
+void UnifiedView::setMessage(const QString &message)
+{
+ m_widget->clear(message);
+}
+
+void UnifiedView::endOperation()
{
QTC_ASSERT(m_widget, return);
- if (success)
- m_widget->restoreState();
- else
- m_widget->clear(Tr::tr("Retrieving data failed."));
+ m_widget->restoreState();
}
void UnifiedView::setCurrentDiffFileIndex(int index)
@@ -197,7 +198,6 @@ void SideBySideView::beginOperation()
DiffEditorDocument *document = m_widget->diffDocument();
if (document && document->state() == DiffEditorDocument::LoadOK)
m_widget->saveState();
- m_widget->clear(Tr::tr("Waiting for data..."));
}
void SideBySideView::setCurrentDiffFileIndex(int index)
@@ -212,13 +212,16 @@ void SideBySideView::setDiff(const QList<FileData> &diffFileList)
m_widget->setDiff(diffFileList);
}
-void SideBySideView::endOperation(bool success)
+void SideBySideView::setMessage(const QString &message)
{
QTC_ASSERT(m_widget, return);
- if (success)
- m_widget->restoreState();
- else
- m_widget->clear(Tr::tr("Retrieving data failed."));
+ m_widget->clear(message);
+}
+
+void SideBySideView::endOperation()
+{
+ QTC_ASSERT(m_widget, return);
+ m_widget->restoreState();
}
void SideBySideView::setSync(bool sync)
diff --git a/src/plugins/diffeditor/diffview.h b/src/plugins/diffeditor/diffview.h
index b1e4f21227..9028f83c63 100644
--- a/src/plugins/diffeditor/diffview.h
+++ b/src/plugins/diffeditor/diffview.h
@@ -44,7 +44,8 @@ public:
virtual void beginOperation() = 0;
virtual void setCurrentDiffFileIndex(int index) = 0;
virtual void setDiff(const QList<FileData> &diffFileList) = 0;
- virtual void endOperation(bool success) = 0;
+ virtual void setMessage(const QString &message) = 0;
+ virtual void endOperation() = 0;
virtual void setSync(bool) = 0;
@@ -81,7 +82,8 @@ public:
void beginOperation() override;
void setCurrentDiffFileIndex(int index) override;
void setDiff(const QList<FileData> &diffFileList) override;
- void endOperation(bool success) override;
+ void setMessage(const QString &message) override;
+ void endOperation() override;
void setSync(bool sync) override;
@@ -104,7 +106,8 @@ public:
void beginOperation() override;
void setCurrentDiffFileIndex(int index) override;
void setDiff(const QList<FileData> &diffFileList) override;
- void endOperation(bool success) override;
+ void setMessage(const QString &message) override;
+ void endOperation() override;
void setSync(bool sync) override;
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
index da69e60784..d2f58f83ac 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp
@@ -5,30 +5,30 @@
#include "diffeditorconstants.h"
#include "diffeditordocument.h"
-#include "diffeditorplugin.h"
#include "diffeditortr.h"
-#include <QMenu>
-#include <QPainter>
-#include <QScrollBar>
-#include <QTextBlock>
-#include <QVBoxLayout>
-
-#include <coreplugin/icore.h>
#include <coreplugin/find/highlightscrollbarcontroller.h>
+#include <coreplugin/icore.h>
#include <coreplugin/minisplitter.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <texteditor/displaysettings.h>
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/mathutils.h>
#include <utils/tooltip/tooltip.h>
+#include <QMenu>
+#include <QPainter>
+#include <QScrollBar>
+#include <QVBoxLayout>
+
using namespace Core;
using namespace TextEditor;
using namespace Utils;
@@ -245,8 +245,8 @@ QString SideDiffEditorWidget::plainTextFromSelection(const QTextCursor &cursor)
return TextDocument::convertToPlainText(text);
}
-SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int progressMin,
- int progressMax, const DiffEditorInput &input)
+static SideBySideDiffOutput diffOutput(QPromise<SideBySideShowResults> &promise, int progressMin,
+ int progressMax, const DiffEditorInput &input)
{
SideBySideDiffOutput output;
@@ -366,8 +366,8 @@ SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int pr
diffText[RightSide].replace('\r', ' ');
output.side[LeftSide].diffText += diffText[LeftSide];
output.side[RightSide].diffText += diffText[RightSide];
- fi.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
- if (fi.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
+ if (promise.isCanceled())
return {};
}
output.side[LeftSide].selections = SelectableTextEditorWidget::polishedSelections(
@@ -869,16 +869,16 @@ void SideBySideDiffEditorWidget::restoreState()
void SideBySideDiffEditorWidget::showDiff()
{
- m_asyncTask.reset(new AsyncTask<ShowResults>());
- m_asyncTask->setFutureSynchronizer(DiffEditorPlugin::futureSynchronizer());
+ m_asyncTask.reset(new Async<SideBySideShowResults>());
+ m_asyncTask->setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
m_controller.setBusyShowing(true);
- connect(m_asyncTask.get(), &AsyncTaskBase::done, this, [this] {
+ connect(m_asyncTask.get(), &AsyncBase::done, this, [this] {
if (m_asyncTask->isCanceled() || !m_asyncTask->isResultAvailable()) {
for (SideDiffEditorWidget *editor : m_editor)
editor->clearAll(Tr::tr("Retrieving data failed."));
} else {
- const ShowResults results = m_asyncTask->result();
+ const SideBySideShowResults results = m_asyncTask->result();
m_editor[LeftSide]->setDiffData(results[LeftSide].diffData);
m_editor[RightSide]->setDiffData(results[RightSide].diffData);
TextDocumentPtr leftDoc(results[LeftSide].textDocument);
@@ -914,28 +914,23 @@ void SideBySideDiffEditorWidget::showDiff()
const DiffEditorInput input(&m_controller);
- auto getDocument = [input](QFutureInterface<ShowResults> &futureInterface) {
- auto cleanup = qScopeGuard([&futureInterface] {
- if (futureInterface.isCanceled())
- futureInterface.reportCanceled();
- });
+ auto getDocument = [input](QPromise<SideBySideShowResults> &promise) {
const int firstPartMax = 20; // diffOutput is about 4 times quicker than filling document
const int leftPartMax = 60;
const int rightPartMax = 100;
- futureInterface.setProgressRange(0, rightPartMax);
- futureInterface.setProgressValue(0);
- QFutureInterface<void> fi = futureInterface;
- const SideBySideDiffOutput output = SideDiffData::diffOutput(fi, 0, firstPartMax, input);
- if (futureInterface.isCanceled())
+ promise.setProgressRange(0, rightPartMax);
+ promise.setProgressValue(0);
+ const SideBySideDiffOutput output = diffOutput(promise, 0, firstPartMax, input);
+ if (promise.isCanceled())
return;
- const ShowResult leftResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
+ const SideBySideShowResult leftResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
output.side[LeftSide].diffData, output.side[LeftSide].selections};
- const ShowResult rightResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
+ const SideBySideShowResult rightResult{TextDocumentPtr(new TextDocument("DiffEditor.SideDiffEditor")),
output.side[RightSide].diffData, output.side[RightSide].selections};
- const ShowResults result{leftResult, rightResult};
+ const SideBySideShowResults result{leftResult, rightResult};
- auto propagateDocument = [&output, &fi](DiffSide side, const ShowResult &result,
+ auto propagateDocument = [&output, &promise](DiffSide side, const SideBySideShowResult &result,
int progressMin, int progressMax) {
// No need to store the change history
result.textDocument->document()->setUndoRedoEnabled(false);
@@ -952,8 +947,9 @@ void SideBySideDiffEditorWidget::showDiff()
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
cursor.insertText(package);
currentPos += package.size();
- fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, progressMin, progressMax));
- if (fi.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize,
+ progressMin, progressMax));
+ if (promise.isCanceled())
return;
}
@@ -968,16 +964,16 @@ void SideBySideDiffEditorWidget::showDiff()
};
propagateDocument(LeftSide, leftResult, firstPartMax, leftPartMax);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
propagateDocument(RightSide, rightResult, leftPartMax, rightPartMax);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
- futureInterface.reportResult(result);
+ promise.addResult(result);
};
- m_asyncTask->setAsyncCallData(getDocument);
+ m_asyncTask->setConcurrentCallData(getDocument);
m_asyncTask->start();
ProgressManager::addTask(m_asyncTask->future(), Tr::tr("Rendering diff"), "DiffEditor");
}
diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.h b/src/plugins/diffeditor/sidebysidediffeditorwidget.h
index 21225fa558..b56fea7da0 100644
--- a/src/plugins/diffeditor/sidebysidediffeditorwidget.h
+++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.h
@@ -7,7 +7,6 @@
#include "diffeditorwidgetcontroller.h"
#include "selectabletexteditorwidget.h" // TODO: we need DiffSelections here only
-#include <QFutureInterface>
#include <QWidget>
namespace Core { class IContext; }
@@ -19,7 +18,7 @@ class TextEditorWidget;
namespace Utils {
template <typename R>
-class AsyncTask;
+class Async;
}
QT_BEGIN_NAMESPACE
@@ -40,9 +39,6 @@ class SideBySideDiffOutput;
class SideDiffData
{
public:
- static SideBySideDiffOutput diffOutput(QFutureInterface<void> &fi, int progressMin,
- int progressMax, const DiffEditorInput &input);
-
DiffChunkInfo m_chunkInfo;
// block number, fileInfo. Set for file lines only.
QMap<int, DiffFileInfo> m_fileInfo;
@@ -60,7 +56,6 @@ public:
int blockNumberForFileIndex(int fileIndex) const;
int fileIndexForBlockNumber(int blockNumber) const;
-private:
void setLineNumber(int blockNumber, int lineNumber);
void setFileInfo(int blockNumber, const DiffFileInfo &fileInfo);
void setSkippedLines(int blockNumber, int skippedLines, const QString &contextInfo = {}) {
@@ -88,6 +83,16 @@ public:
QHash<int, int> foldingIndent;
};
+class SideBySideShowResult
+{
+public:
+ QSharedPointer<TextEditor::TextDocument> textDocument{};
+ SideDiffData diffData;
+ DiffSelections selections;
+};
+
+using SideBySideShowResults = std::array<SideBySideShowResult, SideCount>;
+
class SideBySideDiffEditorWidget : public QWidget
{
Q_OBJECT
@@ -135,15 +140,7 @@ private:
bool m_horizontalSync = false;
- struct ShowResult
- {
- QSharedPointer<TextEditor::TextDocument> textDocument{};
- SideDiffData diffData;
- DiffSelections selections;
- };
- using ShowResults = std::array<ShowResult, SideCount>;
-
- std::unique_ptr<Utils::AsyncTask<ShowResults>> m_asyncTask;
+ std::unique_ptr<Utils::Async<SideBySideShowResults>> m_asyncTask;
};
} // namespace Internal
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
index b937b919f7..bb64e61d5b 100644
--- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp
@@ -5,26 +5,24 @@
#include "diffeditorconstants.h"
#include "diffeditordocument.h"
-#include "diffeditorplugin.h"
#include "diffeditortr.h"
-#include <QMenu>
-#include <QPainter>
-#include <QScrollBar>
-#include <QTextBlock>
-
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
-#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/mathutils.h>
#include <utils/qtcassert.h>
-#include <utils/tooltip/tooltip.h>
+
+#include <QMenu>
+#include <QScrollBar>
+#include <QTextBlock>
using namespace Core;
using namespace TextEditor;
@@ -48,10 +46,10 @@ UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent)
connect(this, &QPlainTextEdit::cursorPositionChanged,
this, &UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor);
- auto context = new Core::IContext(this);
+ auto context = new IContext(this);
context->setWidget(this);
- context->setContext(Core::Context(Constants::UNIFIED_VIEW_ID));
- Core::ICore::addContextObject(context);
+ context->setContext(Context(Constants::UNIFIED_VIEW_ID));
+ ICore::addContextObject(context);
}
UnifiedDiffEditorWidget::~UnifiedDiffEditorWidget() = default;
@@ -68,6 +66,14 @@ DiffEditorDocument *UnifiedDiffEditorWidget::diffDocument() const
return m_controller.document();
}
+void UnifiedDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
+{
+ const GuardLocker locker(m_controller.m_ignoreChanges);
+ clear(Tr::tr("Waiting for data..."));
+ m_controller.m_contextFileData = diffFileList;
+ showDiff();
+}
+
void UnifiedDiffEditorWidget::saveState()
{
if (!m_state.isNull())
@@ -260,14 +266,6 @@ void UnifiedDiffData::setLineNumber(DiffSide side, int blockNumber, int lineNumb
m_lineNumberDigits[side] = qMax(m_lineNumberDigits[side], lineNumberString.count());
}
-void UnifiedDiffEditorWidget::setDiff(const QList<FileData> &diffFileList)
-{
- const GuardLocker locker(m_controller.m_ignoreChanges);
- clear(Tr::tr("Waiting for data..."));
- m_controller.m_contextFileData = diffFileList;
- showDiff();
-}
-
QString UnifiedDiffData::setChunk(const DiffEditorInput &input, const ChunkData &chunkData,
bool lastChunk, int *blockNumber, DiffSelections *selections)
{
@@ -391,8 +389,8 @@ QString UnifiedDiffData::setChunk(const DiffEditorInput &input, const ChunkData
return diffText;
}
-UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface<void> &fi, int progressMin,
- int progressMax, const DiffEditorInput &input)
+static UnifiedDiffOutput diffOutput(QPromise<UnifiedShowResult> &promise, int progressMin,
+ int progressMax, const DiffEditorInput &input)
{
UnifiedDiffOutput output;
@@ -437,8 +435,8 @@ UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface<void> &fi, int pr
output.diffData.m_chunkInfo.setChunkIndex(oldBlock, blockNumber - oldBlock, j);
}
}
- fi.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
- if (fi.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
+ if (promise.isCanceled())
return {};
}
@@ -454,14 +452,14 @@ void UnifiedDiffEditorWidget::showDiff()
return;
}
- m_asyncTask.reset(new AsyncTask<ShowResult>());
- m_asyncTask->setFutureSynchronizer(DiffEditorPlugin::futureSynchronizer());
+ m_asyncTask.reset(new Async<UnifiedShowResult>());
+ m_asyncTask->setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
m_controller.setBusyShowing(true);
- connect(m_asyncTask.get(), &AsyncTaskBase::done, this, [this] {
+ connect(m_asyncTask.get(), &AsyncBase::done, this, [this] {
if (m_asyncTask->isCanceled() || !m_asyncTask->isResultAvailable()) {
setPlainText(Tr::tr("Retrieving data failed."));
} else {
- const ShowResult result = m_asyncTask->result();
+ const UnifiedShowResult result = m_asyncTask->result();
m_data = result.diffData;
TextDocumentPtr doc(result.textDocument);
{
@@ -481,21 +479,16 @@ void UnifiedDiffEditorWidget::showDiff()
const DiffEditorInput input(&m_controller);
- auto getDocument = [input](QFutureInterface<ShowResult> &futureInterface) {
- auto cleanup = qScopeGuard([&futureInterface] {
- if (futureInterface.isCanceled())
- futureInterface.reportCanceled();
- });
+ auto getDocument = [input](QPromise<UnifiedShowResult> &promise) {
const int progressMax = 100;
const int firstPartMax = 20; // diffOutput is about 4 times quicker than filling document
- futureInterface.setProgressRange(0, progressMax);
- futureInterface.setProgressValue(0);
- QFutureInterface<void> fi = futureInterface;
- const UnifiedDiffOutput output = UnifiedDiffData::diffOutput(fi, 0, firstPartMax, input);
- if (futureInterface.isCanceled())
+ promise.setProgressRange(0, progressMax);
+ promise.setProgressValue(0);
+ const UnifiedDiffOutput output = diffOutput(promise, 0, firstPartMax, input);
+ if (promise.isCanceled())
return;
- const ShowResult result = {TextDocumentPtr(new TextDocument("DiffEditor.UnifiedDiffEditor")),
+ const UnifiedShowResult result = {TextDocumentPtr(new TextDocument("DiffEditor.UnifiedDiffEditor")),
output.diffData, output.selections};
// No need to store the change history
result.textDocument->document()->setUndoRedoEnabled(false);
@@ -512,8 +505,9 @@ void UnifiedDiffEditorWidget::showDiff()
const QString package = output.diffText.mid(currentPos, packageSize);
cursor.insertText(package);
currentPos += package.size();
- fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, firstPartMax, progressMax));
- if (futureInterface.isCanceled())
+ promise.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize,
+ firstPartMax, progressMax));
+ if (promise.isCanceled())
return;
}
@@ -525,10 +519,10 @@ void UnifiedDiffEditorWidget::showDiff()
// to caller's thread. We push it to no thread (make object to have no thread affinity),
// and later, in the caller's thread, we pull it back to the caller's thread.
result.textDocument->moveToThread(nullptr);
- futureInterface.reportResult(result);
+ promise.addResult(result);
};
- m_asyncTask->setAsyncCallData(getDocument);
+ m_asyncTask->setConcurrentCallData(getDocument);
m_asyncTask->start();
ProgressManager::addTask(m_asyncTask->future(), Tr::tr("Rendering diff"), "DiffEditor");
}
diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.h b/src/plugins/diffeditor/unifieddiffeditorwidget.h
index 842eec9ea3..e303a75717 100644
--- a/src/plugins/diffeditor/unifieddiffeditorwidget.h
+++ b/src/plugins/diffeditor/unifieddiffeditorwidget.h
@@ -6,15 +6,13 @@
#include "diffeditorwidgetcontroller.h"
#include "selectabletexteditorwidget.h"
-#include <QFutureInterface>
-
namespace Core { class IContext; }
namespace TextEditor { class FontSettings; }
namespace Utils {
template <typename R>
-class AsyncTask;
+class Async;
}
namespace DiffEditor {
@@ -31,9 +29,6 @@ class UnifiedDiffOutput;
class UnifiedDiffData
{
public:
- static UnifiedDiffOutput diffOutput(QFutureInterface<void> &fi, int progressMin, int progressMax,
- const DiffEditorInput &input);
-
DiffChunkInfo m_chunkInfo;
// block number, visual line number.
QMap<int, DiffFileInfoArray> m_fileInfo;
@@ -45,10 +40,10 @@ public:
int blockNumberForFileIndex(int fileIndex) const;
int fileIndexForBlockNumber(int blockNumber) const;
-private:
- void setLineNumber(DiffSide side, int blockNumber, int lineNumber, int rowNumberInChunk);
QString setChunk(const DiffEditorInput &input, const ChunkData &chunkData,
bool lastChunk, int *blockNumber, DiffSelections *selections);
+private:
+ void setLineNumber(DiffSide side, int blockNumber, int lineNumber, int rowNumberInChunk);
};
class UnifiedDiffOutput
@@ -63,6 +58,14 @@ public:
DiffSelections selections;
};
+class UnifiedShowResult
+{
+public:
+ QSharedPointer<TextEditor::TextDocument> textDocument;
+ UnifiedDiffData diffData;
+ DiffSelections selections;
+};
+
class UnifiedDiffEditorWidget final : public SelectableTextEditorWidget
{
Q_OBJECT
@@ -105,14 +108,7 @@ private:
DiffEditorWidgetController m_controller;
QByteArray m_state;
- struct ShowResult
- {
- QSharedPointer<TextEditor::TextDocument> textDocument;
- UnifiedDiffData diffData;
- DiffSelections selections;
- };
-
- std::unique_ptr<Utils::AsyncTask<ShowResult>> m_asyncTask;
+ std::unique_ptr<Utils::Async<UnifiedShowResult>> m_asyncTask;
};
} // namespace Internal
diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp
index 75256f9473..ffc1414859 100644
--- a/src/plugins/docker/dockerapi.cpp
+++ b/src/plugins/docker/dockerapi.cpp
@@ -6,9 +6,9 @@
#include "dockertr.h"
#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QLoggingCategory>
@@ -35,7 +35,7 @@ DockerApi *DockerApi::instance()
bool DockerApi::canConnect()
{
- QtcProcess process;
+ Process process;
FilePath dockerExe = dockerClient();
if (dockerExe.isEmpty() || !dockerExe.isExecutableFile())
return false;
@@ -43,7 +43,7 @@ bool DockerApi::canConnect()
bool result = false;
process.setCommand(CommandLine(dockerExe, QStringList{"info"}));
- connect(&process, &QtcProcess::done, [&process, &result] {
+ connect(&process, &Process::done, [&process, &result] {
qCInfo(dockerApiLog) << "'docker info' result:\n" << qPrintable(process.allOutput());
if (process.result() == ProcessResult::FinishedWithSuccess)
result = true;
@@ -65,7 +65,7 @@ void DockerApi::checkCanConnect(bool async)
m_dockerDaemonAvailable = std::nullopt;
emit dockerDaemonAvailableChanged();
- auto future = Utils::runAsync([lk = std::move(lk), this] {
+ auto future = Utils::asyncRun([lk = std::move(lk), this] {
m_dockerDaemonAvailable = canConnect();
emit dockerDaemonAvailableChanged();
});
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index 2a955f131f..b87c198555 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -17,6 +17,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/idevicewidget.h>
+#include <projectexplorer/devicesupport/processlist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
@@ -43,11 +44,12 @@
#include <utils/overridecursor.h>
#include <utils/pathlisteditor.h>
#include <utils/port.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/sortfiltermodel.h>
#include <utils/temporaryfile.h>
+#include <utils/terminalhooks.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
@@ -95,7 +97,7 @@ public:
{}
private:
- void setupShellProcess(QtcProcess *shellProcess) final
+ void setupShellProcess(Process *shellProcess) final
{
shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(),
{"container", "start", "-i", "-a", m_containerId}});
@@ -104,7 +106,7 @@ private:
CommandLine createFallbackCommand(const CommandLine &cmdLine)
{
CommandLine result = cmdLine;
- result.setExecutable(cmdLine.executable().onDevice(m_devicePath));
+ result.setExecutable(m_devicePath.withNewPath(cmdLine.executable().path()));
return result;
}
@@ -124,7 +126,6 @@ public:
RunResult runInShell(const CommandLine &cmdLine,
const QByteArray &stdInData) const override;
QString mapToDevicePath(const QString &hostPath) const override;
- OsType osType(const FilePath &filePath) const override;
DockerDevicePrivate *m_dev = nullptr;
};
@@ -142,7 +143,7 @@ public:
RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {});
- void updateContainerAccess();
+ bool updateContainerAccess();
void changeMounts(QStringList newMounts);
bool ensureReachable(const FilePath &other);
void shutdown();
@@ -160,15 +161,17 @@ public:
Environment environment();
CommandLine withDockerExecCmd(const CommandLine &cmd,
- Environment *env = nullptr,
- FilePath *workDir = nullptr,
- bool interactive = false);
+ const std::optional<Environment> &env = std::nullopt,
+ const std::optional<FilePath> &workDir = std::nullopt,
+ bool interactive = false,
+ bool withPty = false,
+ bool withMarker = true);
bool prepareForBuild(const Target *target);
Tasks validateMounts() const;
bool createContainer();
- void startContainer();
+ bool startContainer();
void stopCurrentContainer();
void fetchSystemEnviroment();
@@ -183,6 +186,8 @@ public:
QStringList createMountArgs() const;
+ bool isImageAvailable() const;
+
DockerDevice *const q;
DockerDeviceData m_data;
DockerSettings *m_settings;
@@ -221,7 +226,7 @@ private:
// as this object is alive.
IDevice::ConstPtr m_device;
- QtcProcess m_process;
+ Process m_process;
qint64 m_remotePID = 0;
bool m_hasReceivedFirstOutput = false;
};
@@ -231,43 +236,66 @@ DockerProcessImpl::DockerProcessImpl(IDevice::ConstPtr device, DockerDevicePriva
, m_device(std::move(device))
, m_process(this)
{
- connect(&m_process, &QtcProcess::started, this, [this] {
+ connect(&m_process, &Process::started, this, [this] {
qCDebug(dockerDeviceLog) << "Process started:" << m_process.commandLine();
- });
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
- if (!m_hasReceivedFirstOutput) {
- QByteArray output = m_process.readAllRawStandardOutput();
- qsizetype idx = output.indexOf('\n');
- QByteArray firstLine = output.left(idx);
- QByteArray rest = output.mid(idx + 1);
- qCDebug(dockerDeviceLog)
- << "Process first line received:" << m_process.commandLine() << firstLine;
- if (firstLine.startsWith("__qtc")) {
- bool ok = false;
- m_remotePID = firstLine.mid(5, firstLine.size() - 5 - 5).toLongLong(&ok);
-
- if (ok)
- emit started(m_remotePID);
-
- if (rest.size() > 0)
- emit readyRead(rest, {});
-
- m_hasReceivedFirstOutput = true;
- return;
- }
+ if (m_setup.m_ptyData.has_value()) {
+ m_hasReceivedFirstOutput = true;
+ emit started(m_process.processId(), m_process.applicationMainThreadId());
}
- emit readyRead(m_process.readAllRawStandardOutput(), {});
});
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
- emit readyRead({}, m_process.readAllRawStandardError());
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
+ if (m_hasReceivedFirstOutput)
+ emit readyRead(m_process.readAllRawStandardOutput(), {});
+
+ QByteArray output = m_process.readAllRawStandardOutput();
+ qsizetype idx = output.indexOf('\n');
+ QByteArray firstLine = output.left(idx).trimmed();
+ QByteArray rest = output.mid(idx + 1);
+ qCDebug(dockerDeviceLog) << "Process first line received:" << m_process.commandLine()
+ << firstLine;
+
+ if (!firstLine.startsWith("__qtc"))
+ return;
+
+ bool ok = false;
+ m_remotePID = firstLine.mid(5, firstLine.size() - 5 - 5).toLongLong(&ok);
+
+ if (ok)
+ emit started(m_remotePID);
+
+ // In case we already received some error output, send it now.
+ const QByteArray stdErr = m_process.readAllRawStandardError();
+ if (rest.size() > 0 || stdErr.size() > 0)
+ emit readyRead(rest, stdErr);
+
+ m_hasReceivedFirstOutput = true;
});
- connect(&m_process, &QtcProcess::done, this, [this] {
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
+ if (m_remotePID)
+ emit readyRead({}, m_process.readAllRawStandardError());
+ });
+
+ connect(&m_process, &Process::done, this, [this] {
qCDebug(dockerDeviceLog) << "Process exited:" << m_process.commandLine()
<< "with code:" << m_process.resultData().m_exitCode;
- emit done(m_process.resultData());
+
+ Utils::ProcessResultData resultData = m_process.resultData();
+
+ if (m_remotePID == 0 && !m_hasReceivedFirstOutput) {
+ resultData.m_error = QProcess::FailedToStart;
+ qCWarning(dockerDeviceLog) << "Process failed to start:" << m_process.commandLine();
+ QByteArray stdOut = m_process.readAllRawStandardOutput();
+ QByteArray stdErr = m_process.readAllRawStandardError();
+ if (!stdOut.isEmpty())
+ qCWarning(dockerDeviceLog) << "stdout:" << stdOut;
+ if (!stdErr.isEmpty())
+ qCWarning(dockerDeviceLog) << "stderr:" << stdErr;
+ }
+
+ emit done(resultData);
});
}
@@ -282,23 +310,30 @@ void DockerProcessImpl::start()
m_process.setProcessImpl(m_setup.m_processImpl);
m_process.setProcessMode(m_setup.m_processMode);
m_process.setTerminalMode(m_setup.m_terminalMode);
+ m_process.setPtyData(m_setup.m_ptyData);
m_process.setReaperTimeout(m_setup.m_reaperTimeout);
m_process.setWriteData(m_setup.m_writeData);
m_process.setProcessChannelMode(m_setup.m_processChannelMode);
m_process.setExtraData(m_setup.m_extraData);
m_process.setStandardInputFile(m_setup.m_standardInputFile);
m_process.setAbortOnMetaChars(m_setup.m_abortOnMetaChars);
+ m_process.setCreateConsoleOnWindows(m_setup.m_createConsoleOnWindows);
if (m_setup.m_lowPriority)
m_process.setLowPriority();
+ const bool inTerminal = m_setup.m_terminalMode != TerminalMode::Off
+ || m_setup.m_ptyData.has_value();
+
const bool interactive = m_setup.m_processMode == ProcessMode::Writer
- || !m_setup.m_writeData.isEmpty();
+ || !m_setup.m_writeData.isEmpty() || inTerminal;
- const CommandLine fullCommandLine = m_devicePrivate
- ->withDockerExecCmd(m_setup.m_commandLine,
- &m_setup.m_environment,
- &m_setup.m_workingDirectory,
- interactive);
+ const CommandLine fullCommandLine
+ = m_devicePrivate->withDockerExecCmd(m_setup.m_commandLine,
+ m_setup.m_environment,
+ m_setup.m_workingDirectory,
+ interactive,
+ inTerminal,
+ !m_process.ptyData().has_value());
m_process.setCommand(fullCommandLine);
m_process.start();
@@ -311,14 +346,26 @@ qint64 DockerProcessImpl::write(const QByteArray &data)
void DockerProcessImpl::sendControlSignal(ControlSignal controlSignal)
{
- QTC_ASSERT(m_remotePID, return);
- if (controlSignal == ControlSignal::CloseWriteChannel) {
- m_process.closeWriteChannel();
- return;
+ if (!m_setup.m_ptyData.has_value()) {
+ QTC_ASSERT(m_remotePID, return);
+ if (controlSignal == ControlSignal::CloseWriteChannel) {
+ m_process.closeWriteChannel();
+ return;
+ }
+ const int signal = controlSignalToInt(controlSignal);
+ m_devicePrivate->runInShell(
+ {"kill", {QString("-%1").arg(signal), QString("%2").arg(m_remotePID)}});
+ } else {
+ // clang-format off
+ switch (controlSignal) {
+ case ControlSignal::Terminate: m_process.terminate(); break;
+ case ControlSignal::Kill: m_process.kill(); break;
+ case ControlSignal::Interrupt: m_process.interrupt(); break;
+ case ControlSignal::KickOff: m_process.kickoffProcess(); break;
+ case ControlSignal::CloseWriteChannel: break;
+ }
+ // clang-format on
}
- const int signal = controlSignalToInt(controlSignal);
- m_devicePrivate->runInShell(
- {"kill", {QString("-%1").arg(signal), QString("%2").arg(m_remotePID)}});
}
IDeviceWidget *DockerDevice::createWidget()
@@ -367,47 +414,40 @@ QString DockerDeviceFileAccess::mapToDevicePath(const QString &hostPath) const
return newPath;
}
-OsType DockerDeviceFileAccess::osType(const FilePath &filePath) const
-{
- QTC_ASSERT(m_dev, return UnixDeviceFileAccess::osType(filePath));
- return m_dev->q->osType();
-}
-
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
: d(new DockerDevicePrivate(this, settings, data))
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Docker"));
- setOsType(OsTypeOtherUnix);
+ setOsType(OsTypeLinux);
setDefaultDisplayName(Tr::tr("Docker Image"));
-
+ setupId(IDevice::ManuallyAdded);
+ setType(Constants::DOCKER_DEVICE_TYPE);
+ setMachineType(IDevice::Hardware);
setDisplayName(Tr::tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId));
setAllowEmptyCommand(true);
- setOpenTerminal([this, settings](const Environment &env, const FilePath &workingDir) {
+ setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below.
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return;
+
if (d->containerId().isEmpty()) {
MessageManager::writeDisrupting(Tr::tr("Error starting remote shell. No container."));
return;
}
- QtcProcess *proc = new QtcProcess(d);
- proc->setTerminalMode(TerminalMode::On);
+ Process proc;
+ proc.setTerminalMode(TerminalMode::Detached);
+ proc.setEnvironment(env);
+ proc.setWorkingDirectory(workingDir);
+ proc.setCommand({Terminal::defaultShellForDevice(rootPath()), {}});
+ proc.start();
- QObject::connect(proc, &QtcProcess::done, [proc] {
- if (proc->error() != QProcess::UnknownError && MessageManager::instance()) {
- MessageManager::writeDisrupting(
- Tr::tr("Error starting remote shell: %1").arg(proc->errorString()));
- }
- proc->deleteLater();
- });
-
- const QString wd = workingDir.isEmpty() ? "/" : workingDir.path();
- proc->setCommand({settings->dockerBinaryPath.filePath(),
- {"exec", "-it", "-w", wd, d->containerId(), "/bin/sh"}});
- proc->setEnvironment(Environment::systemEnvironment()); // The host system env. Intentional.
- proc->start();
+ if (proc.error() != QProcess::UnknownError && MessageManager::instance()) {
+ MessageManager::writeDisrupting(
+ Tr::tr("Error starting remote shell: %1").arg(proc.errorString()));
+ }
});
addDeviceAction({Tr::tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) {
@@ -440,47 +480,58 @@ void DockerDevice::setData(const DockerDeviceData &data)
d->setData(data);
}
-void DockerDevice::updateContainerAccess() const
+bool DockerDevice::updateContainerAccess() const
{
- d->updateContainerAccess();
+ return d->updateContainerAccess();
}
CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
- Environment *env,
- FilePath *workDir,
- bool interactive)
+ const std::optional<Environment> &env,
+ const std::optional<FilePath> &workDir,
+ bool interactive,
+ bool withPty,
+ bool withMarker)
{
if (!m_settings)
return {};
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return {};
CommandLine dockerCmd{m_settings->dockerBinaryPath.filePath(), {"exec"}};
if (interactive)
dockerCmd.addArg("-i");
+ if (withPty)
+ dockerCmd.addArg("-t");
+
if (env) {
- for (auto it = env->constBegin(); it != env->constEnd(); ++it) {
+ env->forEachEntry([&](const QString &key, const QString &value, bool) {
dockerCmd.addArg("-e");
- dockerCmd.addArg(env->key(it) + "=" + env->expandedValueForKey(env->key(it)));
- }
+ dockerCmd.addArg(key + "=" + env->expandVariables(value));
+ });
}
if (workDir && !workDir->isEmpty())
- dockerCmd.addArgs({"-w", workDir->path()});
+ dockerCmd.addArgs({"-w", q->rootPath().withNewMappedPath(*workDir).nativePath()});
dockerCmd.addArg(m_container);
+
dockerCmd.addArgs({"/bin/sh", "-c"});
CommandLine exec("exec");
- exec.addCommandLineAsArgs(cmd);
+ exec.addCommandLineAsArgs(cmd, CommandLine::Raw);
- CommandLine echo("echo");
- echo.addArgs("__qtc$$qtc__", CommandLine::Raw);
- echo.addCommandLineWithAnd(exec);
+ if (withMarker) {
+ CommandLine echo("echo");
+ echo.addArgs("__qtc$$qtc__", CommandLine::Raw);
+ echo.addCommandLineWithAnd(exec);
- dockerCmd.addCommandLineAsSingleArg(echo);
+ dockerCmd.addCommandLineAsSingleArg(echo);
+ } else {
+ dockerCmd.addCommandLineAsSingleArg(exec);
+ }
return dockerCmd;
}
@@ -494,9 +545,16 @@ void DockerDevicePrivate::stopCurrentContainer()
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return;
- m_shell.reset();
+ if (m_shell) {
+ // We have to disconnect the shell from the device, otherwise it will try to
+ // tell us about the container being stopped. Since that signal is emitted in a different
+ // thread, it would be delayed received by us when we might already have started
+ // a new shell.
+ m_shell->disconnect(this);
+ m_shell.reset();
+ }
- QtcProcess proc;
+ Process proc;
proc.setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "stop", m_container}});
m_container.clear();
@@ -598,11 +656,30 @@ QStringList DockerDevicePrivate::createMountArgs() const
return cmds;
}
+bool DockerDevicePrivate::isImageAvailable() const
+{
+ Process proc;
+ proc.setCommand(
+ {m_settings->dockerBinaryPath.filePath(),
+ {"image", "list", m_data.repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
+ proc.runBlocking();
+ if (proc.result() != ProcessResult::FinishedWithSuccess)
+ return false;
+
+ if (proc.stdOut().trimmed() == m_data.repoAndTag())
+ return true;
+
+ return false;
+}
+
bool DockerDevicePrivate::createContainer()
{
if (!m_settings)
return false;
+ if (!isImageAvailable())
+ return false;
+
const QString display = HostOsInfo::isLinuxHost() ? QString(":0")
: QString("host.docker.internal:0");
CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(),
@@ -621,9 +698,6 @@ bool DockerDevicePrivate::createContainer()
if (m_data.useLocalUidGid)
dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
#endif
- FilePath dumperPath = FilePath::fromString("/tmp/qtcreator/debugger");
- addTemporaryMount(Core::ICore::resourcePath("debugger/"), dumperPath);
- q->setDebugDumperPath(dumperPath);
dockerCreate.addArgs(createMountArgs());
@@ -636,7 +710,7 @@ bool DockerDevicePrivate::createContainer()
dockerCreate.addArg(m_data.repoAndTag());
qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << dockerCreate.toUserOutput();
- QtcProcess createProcess;
+ Process createProcess;
createProcess.setCommand(dockerCreate);
createProcess.runBlocking();
@@ -655,20 +729,22 @@ bool DockerDevicePrivate::createContainer()
return true;
}
-void DockerDevicePrivate::startContainer()
+bool DockerDevicePrivate::startContainer()
{
if (!createContainer())
- return;
+ return false;
m_shell = std::make_unique<ContainerShell>(m_settings, m_container, q->rootPath());
connect(m_shell.get(), &DeviceShell::done, this, [this](const ProcessResultData &resultData) {
+ if (m_shell)
+ m_shell.release()->deleteLater();
+
if (resultData.m_error != QProcess::UnknownError
|| resultData.m_exitStatus == QProcess::NormalExit)
return;
qCWarning(dockerDeviceLog) << "Container shell encountered error:" << resultData.m_error;
- m_shell.release()->deleteLater();
DockerApi::recheckDockerDaemon();
MessageManager::writeFlashing(Tr::tr("Docker daemon appears to be not running. "
@@ -677,23 +753,25 @@ void DockerDevicePrivate::startContainer()
"or restart Qt Creator."));
});
- if (!m_shell->start()) {
- qCWarning(dockerDeviceLog) << "Container shell failed to start";
- }
+ if (m_shell->start())
+ return true;
+
+ qCWarning(dockerDeviceLog) << "Container shell failed to start";
+ return false;
}
-void DockerDevicePrivate::updateContainerAccess()
+bool DockerDevicePrivate::updateContainerAccess()
{
if (m_isShutdown)
- return;
+ return false;
if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false)
- return;
+ return false;
if (m_shell)
- return;
+ return true;
- startContainer();
+ return startContainer();
}
void DockerDevice::setMounts(const QStringList &mounts) const
@@ -750,36 +828,9 @@ ProcessInterface *DockerDevice::createProcessInterface() const
return new DockerProcessImpl(this->sharedFromThis(), d);
}
-bool DockerDevice::canAutoDetectPorts() const
+DeviceProcessList *DockerDevice::createProcessListModel(QObject *parent) const
{
- return true;
-}
-
-PortsGatheringMethod DockerDevice::portsGatheringMethod() const
-{
- return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
- return {filePath("sed"),
- "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
- CommandLine::Raw};
- },
-
- &Port::parseFromSedOutput};
-};
-
-DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
-{
- return nullptr;
+ return new ProcessList(sharedFromThis(), parent);
}
DeviceTester *DockerDevice::createDeviceTester() const
@@ -865,7 +916,8 @@ void DockerDevice::aboutToBeRemoved() const
void DockerDevicePrivate::fetchSystemEnviroment()
{
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return;
if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) {
const RunResult result = runInShell({"env", {}});
@@ -874,7 +926,7 @@ void DockerDevicePrivate::fetchSystemEnviroment()
return;
}
- QtcProcess proc;
+ Process proc;
proc.setCommand(withDockerExecCmd({"env", {}}));
proc.start();
proc.waitForFinished();
@@ -889,7 +941,8 @@ void DockerDevicePrivate::fetchSystemEnviroment()
RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData)
{
- updateContainerAccess();
+ if (!updateContainerAccess())
+ return {};
QTC_ASSERT(m_shell, return {});
return m_shell->runInShell(cmd, stdInData);
}
@@ -985,6 +1038,7 @@ public:
using namespace Layouting;
+ // clang-format off
Column {
Stack {
statusLabel,
@@ -993,9 +1047,8 @@ public:
m_log,
errorLabel,
Row{showUnnamedContainers, m_buttons},
- }
- .attachTo(this);
-
+ }.attachTo(this);
+ // clang-format on
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
@@ -1004,10 +1057,10 @@ public:
{"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
m_log->append(Tr::tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
- m_process = new QtcProcess(this);
+ m_process = new Process(this);
m_process->setCommand(cmd);
- connect(m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(m_process, &Process::readyReadStandardOutput, this, [this] {
const QString out = m_process->readAllStandardOutput().trimmed();
m_log->append(out);
for (const QString &line : out.split('\n')) {
@@ -1026,12 +1079,12 @@ public:
m_log->append(Tr::tr("Done."));
});
- connect(m_process, &Utils::QtcProcess::readyReadStandardError, this, [this] {
+ connect(m_process, &Utils::Process::readyReadStandardError, this, [this] {
const QString out = Tr::tr("Error: %1").arg(m_process->cleanedStdErr());
m_log->append(Tr::tr("Error: %1").arg(out));
});
- connect(m_process, &QtcProcess::done, errorLabel, [errorLabel, this, statusLabel] {
+ connect(m_process, &Process::done, errorLabel, [errorLabel, this, statusLabel] {
delete statusLabel;
if (m_process->result() == ProcessResult::FinishedWithSuccess)
m_view->setEnabled(true);
@@ -1057,9 +1110,6 @@ public:
QTC_ASSERT(item, return {});
auto device = DockerDevice::create(m_settings, *item);
- device->setupId(IDevice::ManuallyAdded);
- device->setType(Constants::DOCKER_DEVICE_TYPE);
- device->setMachineType(IDevice::Hardware);
return device;
}
@@ -1072,7 +1122,7 @@ public:
QDialogButtonBox *m_buttons;
DockerSettings *m_settings;
- QtcProcess *m_process = nullptr;
+ Process *m_process = nullptr;
QString m_selectedId;
};
@@ -1186,16 +1236,25 @@ bool DockerDevicePrivate::ensureReachable(const FilePath &other)
const FilePath fMount = FilePath::fromString(mount);
if (other.isChildOf(fMount))
return true;
+
+ if (fMount == other)
+ return true;
}
for (const auto &[path, containerPath] : m_temporaryMounts) {
if (path.path() != containerPath.path())
continue;
+ if (path == other)
+ return true;
+
if (other.isChildOf(path))
return true;
}
+ if (q->filePath(other.path()).exists())
+ return false;
+
addTemporaryMount(other, other);
return true;
}
diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h
index 3ecc0118d4..bd0ce29d9d 100644
--- a/src/plugins/docker/dockerdevice.h
+++ b/src/plugins/docker/dockerdevice.h
@@ -73,9 +73,7 @@ public:
Utils::ProcessInterface *createProcessInterface() const override;
- bool canAutoDetectPorts() const override;
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
- bool canCreateProcessModel() const override { return false; }
+ bool canCreateProcessModel() const override { return true; }
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
bool hasDeviceTester() const override { return false; }
ProjectExplorer::DeviceTester *createDeviceTester() const override;
@@ -95,7 +93,7 @@ public:
void setData(const DockerDeviceData &data);
- void updateContainerAccess() const;
+ bool updateContainerAccess() const;
void setMounts(const QStringList &mounts) const;
bool prepareForBuild(const ProjectExplorer::Target *target) override;
diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp
index bc9c76f5b2..cd32fead6f 100644
--- a/src/plugins/docker/dockerdevicewidget.cpp
+++ b/src/plugins/docker/dockerdevicewidget.cpp
@@ -198,6 +198,8 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
logView->append(Tr::tr("Docker daemon appears to be not running."));
else
logView->append(Tr::tr("Docker daemon appears to be running."));
+
+ logView->append(Tr::tr("Detection complete."));
updateDaemonStateTexts();
});
@@ -245,7 +247,8 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
m_pathsListEdit,
}, br,
(dockerDevice->isAutoDetected() ? Column {} : std::move(detectionControls)),
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
// clang-format on
searchDirsLineEdit->setVisible(false);
diff --git a/src/plugins/docker/dockerplugin.cpp b/src/plugins/docker/dockerplugin.cpp
index d7addc894d..cd2dd623bc 100644
--- a/src/plugins/docker/dockerplugin.cpp
+++ b/src/plugins/docker/dockerplugin.cpp
@@ -28,7 +28,6 @@ public:
DockerSettings m_settings;
DockerDeviceFactory m_deviceFactory{&m_settings};
- DockerSettingsPage m_settingPage{&m_settings};
DockerApi m_dockerApi{&m_settings};
};
diff --git a/src/plugins/docker/dockersettings.cpp b/src/plugins/docker/dockersettings.cpp
index bfec44f243..a43e7a20d3 100644
--- a/src/plugins/docker/dockersettings.cpp
+++ b/src/plugins/docker/dockersettings.cpp
@@ -5,13 +5,11 @@
#include "dockerconstants.h"
#include "dockertr.h"
-#include "utils/hostosinfo.h"
-
-#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/filepath.h>
+#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
using namespace Utils;
@@ -21,7 +19,23 @@ namespace Docker::Internal {
DockerSettings::DockerSettings()
{
setSettingsGroup(Constants::DOCKER);
- setAutoApply(false);
+ setId(Docker::Constants::DOCKER_SETTINGS_ID);
+ setDisplayName(Tr::tr("Docker"));
+ setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+
+ // clang-format off
+ Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Row { dockerBinaryPath }
+ },
+ st
+ }.attachTo(widget);
+ // clang-format on
+ });
FilePaths additionalPaths;
if (HostOsInfo::isWindowsHost())
@@ -39,32 +53,7 @@ DockerSettings::DockerSettings()
dockerBinaryPath.setLabelText(Tr::tr("Command:"));
dockerBinaryPath.setSettingsKey("cli");
- readSettings(Core::ICore::settings());
-}
-
-// DockerSettingsPage
-
-DockerSettingsPage::DockerSettingsPage(DockerSettings *settings)
-{
- setId(Docker::Constants::DOCKER_SETTINGS_ID);
- setDisplayName(Tr::tr("Docker"));
- setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- DockerSettings &s = *settings;
- using namespace Layouting;
-
- // clang-format off
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Row { s.dockerBinaryPath }
- },
- st
- }.attachTo(widget);
- // clang-format on
- });
+ readSettings();
}
} // Docker::Internal
diff --git a/src/plugins/docker/dockersettings.h b/src/plugins/docker/dockersettings.h
index 4980a0a6b0..e6f8f1ec1f 100644
--- a/src/plugins/docker/dockersettings.h
+++ b/src/plugins/docker/dockersettings.h
@@ -5,11 +5,9 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace Docker::Internal {
-class DockerSettings final : public Utils::AspectContainer
+class DockerSettings final : public Core::PagedSettings
{
public:
DockerSettings();
@@ -17,10 +15,4 @@ public:
Utils::StringAspect dockerBinaryPath;
};
-class DockerSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit DockerSettingsPage(DockerSettings *settings);
-};
-
} // Docker::Internal
diff --git a/src/plugins/fakevim/fakevim.qbs b/src/plugins/fakevim/fakevim.qbs
index cdfb9e24dd..a4dad38428 100644
--- a/src/plugins/fakevim/fakevim.qbs
+++ b/src/plugins/fakevim/fakevim.qbs
@@ -25,9 +25,7 @@ QtcPlugin {
"fakevimtr.h",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: ["fakevim_test.cpp"]
}
}
diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h
index 6f7e303f0f..582630e83d 100644
--- a/src/plugins/fakevim/fakevimactions.h
+++ b/src/plugins/fakevim/fakevimactions.h
@@ -44,18 +44,21 @@ class FvBoolAspect : public FvBaseAspect
{
public:
bool value() const { return FvBaseAspect::value().toBool(); }
+ bool operator()() const { return value(); }
};
class FvIntegerAspect : public FvBaseAspect
{
public:
qint64 value() const { return FvBaseAspect::value().toLongLong(); }
+ qint64 operator()() const { return value(); }
};
class FvStringAspect : public FvBaseAspect
{
public:
QString value() const { return FvBaseAspect::value().toString(); }
+ QString operator()() const { return value(); }
};
class FvAspectContainer : public FvBaseAspect
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 23a52793e8..2eb052432c 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -36,8 +36,6 @@
#include "fakevimactions.h"
#include "fakevimtr.h"
-#include <utils/qtcprocess.h>
-
#include <QDebug>
#include <QFile>
#include <QObject>
@@ -833,29 +831,6 @@ static void setClipboardData(const QString &content, RangeMode mode,
clipboard->setMimeData(data, clipboardMode);
}
-static QByteArray toLocalEncoding(const QString &text)
-{
-#if defined(Q_OS_WIN)
- return QString(text).replace("\n", "\r\n").toLocal8Bit();
-#else
- return text.toLocal8Bit();
-#endif
-}
-
-static QString getProcessOutput(const QString &command, const QString &input)
-{
- Utils::QtcProcess proc;
- proc.setCommand(Utils::CommandLine::fromUserInput(command));
- proc.setWriteData(toLocalEncoding(input));
- proc.start();
-
- // FIXME: Process should be interruptable by user.
- // Solution is to create a QObject for each process and emit finished state.
- proc.waitForFinished();
-
- return proc.cleanedStdOut();
-}
-
static const QMap<QString, int> &vimKeyNames()
{
static const QMap<QString, int> k = {
@@ -2811,7 +2786,7 @@ void FakeVimHandler::Private::ensureCursorVisible()
void FakeVimHandler::Private::updateEditor()
{
- setTabSize(s.tabStop.value());
+ setTabSize(s.tabStop());
setupCharClass();
}
@@ -5199,6 +5174,7 @@ void FakeVimHandler::Private::handleReplaceMode(const Input &input)
moveDown();
} else if (input.isKey(Key_Insert)) {
g.mode = InsertMode;
+ q->modeChanged(isInsertMode());
} else if (input.isControl('o')) {
enterCommandMode(ReplaceMode);
} else {
@@ -5396,6 +5372,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
removeText(range);
} else if (input.isKey(Key_Insert)) {
g.mode = ReplaceMode;
+ q->modeChanged(isInsertMode());
} else if (input.isKey(Key_Left)) {
moveLeft();
} else if (input.isShift(Key_Left) || input.isControl(Key_Left)) {
@@ -5435,7 +5412,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
const Column ind = indentation(data);
if (col.logical <= ind.logical && col.logical
&& startsWithWhitespace(data, col.physical)) {
- const int ts = s.tabStop.value();
+ const int ts = s.tabStop();
const int newl = col.logical - 1 - (col.logical - 1) % ts;
const QString prefix = tabExpand(newl);
setLineContents(line, prefix + data.mid(col.physical));
@@ -5459,20 +5436,22 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else if (input.isKey(Key_PageUp) || input.isControl('b')) {
movePageUp();
} else if (input.isKey(Key_Tab)) {
- m_buffer->insertState.insertingSpaces = true;
- if (s.expandTab.value()) {
- const int ts = s.tabStop.value();
- const int col = logicalCursorColumn();
- QString str = QString(ts - col % ts, ' ');
- insertText(str);
- } else {
- insertInInsertMode(input.raw());
+ if (q->tabPressedInInsertMode()) {
+ m_buffer->insertState.insertingSpaces = true;
+ if (s.expandTab.value()) {
+ const int ts = s.tabStop();
+ const int col = logicalCursorColumn();
+ QString str = QString(ts - col % ts, ' ');
+ insertText(str);
+ } else {
+ insertInInsertMode(input.raw());
+ }
+ m_buffer->insertState.insertingSpaces = false;
}
- m_buffer->insertState.insertingSpaces = false;
} else if (input.isControl('d')) {
// remove one level of indentation from the current line
- const int shift = s.shiftWidth.value();
- const int tab = s.tabStop.value();
+ const int shift = s.shiftWidth();
+ const int tab = s.tabStop();
int line = cursorLine() + 1;
int pos = firstPositionInLine(line);
QString text = lineContents(line);
@@ -6430,7 +6409,8 @@ bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
const QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
const QString input = replaceText ? selectText(cmd.range) : QString();
- const QString result = getProcessOutput(command, input);
+ QString result;
+ q->processOutput(command, input, &result);
if (replaceText) {
setCurrentRange(cmd.range);
@@ -6977,7 +6957,7 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat)
if (s.startOfLine.value())
targetPos = firstPositionInLine(beginLine);
- const int sw = s.shiftWidth.value();
+ const int sw = s.shiftWidth();
g.movetype = MoveLineWise;
beginEditBlock();
QTextBlock block = document()->findBlockByLineNumber(beginLine - 1);
@@ -7315,7 +7295,7 @@ int FakeVimHandler::Private::physicalCursorColumn() const
int FakeVimHandler::Private::physicalToLogicalColumn
(const int physical, const QString &line) const
{
- const int ts = s.tabStop.value();
+ const int ts = s.tabStop();
int p = 0;
int logical = 0;
while (p < physical) {
@@ -7336,7 +7316,7 @@ int FakeVimHandler::Private::physicalToLogicalColumn
int FakeVimHandler::Private::logicalToPhysicalColumn
(const int logical, const QString &line) const
{
- const int ts = s.tabStop.value();
+ const int ts = s.tabStop();
int physical = 0;
for (int l = 0; l < logical && physical < line.size(); ++physical) {
QChar c = line.at(physical);
@@ -7350,7 +7330,7 @@ int FakeVimHandler::Private::logicalToPhysicalColumn
int FakeVimHandler::Private::windowScrollOffset() const
{
- return qMin(static_cast<int>(s.scrollOff.value()), linesOnScreen() / 2);
+ return qMin(static_cast<int>(s.scrollOff()), linesOnScreen() / 2);
}
int FakeVimHandler::Private::logicalCursorColumn() const
@@ -8577,6 +8557,8 @@ void FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode)
g.returnToMode = mode;
clearLastInsertion();
}
+
+ q->modeChanged(isInsertMode());
}
void FakeVimHandler::Private::enterVisualInsertMode(QChar command)
@@ -8652,6 +8634,8 @@ void FakeVimHandler::Private::enterCommandMode(Mode returnToMode)
g.returnToMode = returnToMode;
m_positionPastEnd = false;
m_anchorPastEnd = false;
+
+ q->modeChanged(isInsertMode());
}
void FakeVimHandler::Private::enterExMode(const QString &contents)
@@ -8666,6 +8650,8 @@ void FakeVimHandler::Private::enterExMode(const QString &contents)
g.submode = NoSubMode;
g.subsubmode = NoSubSubMode;
unfocus();
+
+ q->modeChanged(isInsertMode());
}
void FakeVimHandler::Private::recordJump(int position)
@@ -8698,7 +8684,7 @@ void FakeVimHandler::Private::jump(int distance)
Column FakeVimHandler::Private::indentation(const QString &line) const
{
- int ts = s.tabStop.value();
+ int ts = s.tabStop();
int physical = 0;
int logical = 0;
int n = line.size();
@@ -8717,8 +8703,8 @@ Column FakeVimHandler::Private::indentation(const QString &line) const
QString FakeVimHandler::Private::tabExpand(int n) const
{
- int ts = s.tabStop.value();
- if (s.expandTab.value() || ts < 1)
+ int ts = s.tabStop();
+ if (s.expandTab() || ts < 1)
return QString(n, ' ');
return QString(n / ts, '\t')
+ QString(n % ts, ' ');
@@ -9560,6 +9546,11 @@ bool FakeVimHandler::jumpToLocalMark(QChar mark, bool backTickMode)
return d->jumpToMark(mark, backTickMode);
}
+bool FakeVimHandler::inFakeVimMode()
+{
+ return d->m_inFakeVim;
+}
+
} // namespace Internal
} // namespace FakeVim
diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h
index 57955cd2cb..0b615e1657 100644
--- a/src/plugins/fakevim/fakevimhandler.h
+++ b/src/plugins/fakevim/fakevimhandler.h
@@ -61,23 +61,30 @@ enum MessageLevel
MessageShowCmd // partial command
};
-template <typename Type>
-class Signal
+template<typename>
+class Callback;
+
+template<typename R, typename... Params>
+class Callback<R(Params...)>
{
public:
- using Callable = std::function<Type>;
-
- void connect(const Callable &callable) { m_callables.push_back(callable); }
+ static constexpr auto IsVoidReturnType = std::is_same_v<R, void>;
+ using Function = std::function<R(Params...)>;
+ void set(const Function &callable) { m_callable = callable; }
- template <typename ...Args>
- void operator()(Args ...args) const
+ R operator()(Params... params)
{
- for (const Callable &callable : m_callables)
- callable(args...);
- }
+ if (!m_callable)
+ return R();
+
+ if constexpr (IsVoidReturnType)
+ m_callable(std::forward<Params>(params)...);
+ else
+ return m_callable(std::forward<Params>(params)...);
+ }
private:
- std::vector<Callable> m_callables;
+ Function m_callable;
};
class FakeVimHandler : public QObject
@@ -129,34 +136,40 @@ public:
bool jumpToLocalMark(QChar mark, bool backTickMode);
+ bool inFakeVimMode();
+
bool eventFilter(QObject *ob, QEvent *ev) override;
- Signal<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)> commandBufferChanged;
- Signal<void(const QString &msg)> statusDataChanged;
- Signal<void(const QString &msg)> extraInformationChanged;
- Signal<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
- Signal<void(const QString &needle)> highlightMatches;
- Signal<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
- Signal<void(bool *result, QChar c)> checkForElectricCharacter;
- Signal<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
- Signal<void(const QString &needle, bool forward)> simpleCompletionRequested;
- Signal<void(const QString &key, int count)> windowCommandRequested;
- Signal<void(bool reverse)> findRequested;
- Signal<void(bool reverse)> findNextRequested;
- Signal<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
- Signal<void()> requestDisableBlockSelection;
- Signal<void(const QTextCursor &cursor)> requestSetBlockSelection;
- Signal<void(QTextCursor *cursor)> requestBlockSelection;
- Signal<void(bool *on)> requestHasBlockSelection;
- Signal<void(int depth)> foldToggle;
- Signal<void(bool fold)> foldAll;
- Signal<void(int depth, bool dofold)> fold;
- Signal<void(int count, bool current)> foldGoTo;
- Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
- Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
- Signal<void()> completionRequested;
- Signal<void()> tabPreviousRequested;
- Signal<void()> tabNextRequested;
+ Callback<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)>
+ commandBufferChanged;
+ Callback<void(const QString &msg)> statusDataChanged;
+ Callback<void(const QString &msg)> extraInformationChanged;
+ Callback<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
+ Callback<void(const QString &needle)> highlightMatches;
+ Callback<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
+ Callback<void(bool *result, QChar c)> checkForElectricCharacter;
+ Callback<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
+ Callback<void(const QString &needle, bool forward)> simpleCompletionRequested;
+ Callback<void(const QString &key, int count)> windowCommandRequested;
+ Callback<void(bool reverse)> findRequested;
+ Callback<void(bool reverse)> findNextRequested;
+ Callback<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
+ Callback<void()> requestDisableBlockSelection;
+ Callback<void(const QTextCursor &cursor)> requestSetBlockSelection;
+ Callback<void(QTextCursor *cursor)> requestBlockSelection;
+ Callback<void(bool *on)> requestHasBlockSelection;
+ Callback<void(int depth)> foldToggle;
+ Callback<void(bool fold)> foldAll;
+ Callback<void(int depth, bool dofold)> fold;
+ Callback<void(int count, bool current)> foldGoTo;
+ Callback<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
+ Callback<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
+ Callback<void()> completionRequested;
+ Callback<void()> tabPreviousRequested;
+ Callback<void()> tabNextRequested;
+ Callback<void(bool insertMode)> modeChanged;
+ Callback<bool()> tabPressedInInsertMode;
+ Callback<void(const QString &, const QString &, QString *)> processOutput;
public:
class Private;
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 02207de872..be06ece7f4 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -50,7 +50,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcassert.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
@@ -340,33 +340,8 @@ private:
using ExCommandMap = QMap<QString, QRegularExpression>;
using UserCommandMap = QMap<int, QString>;
-class FakeVimOptionPage : public IOptionsPage
-{
-public:
- FakeVimOptionPage()
- {
- setId(SETTINGS_ID);
- setDisplayName(Tr::tr("General"));
- setCategory(SETTINGS_CATEGORY);
- setDisplayCategory(Tr::tr("FakeVim"));
- setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
- setLayouter([this](QWidget *widget) { return layoutPage(widget); });
- setSettings(fakeVimSettings());
- }
-
-private:
- void layoutPage(QWidget *);
- void copyTextEditorSettings();
- void setQtStyle();
- void setPlainStyle();
-};
-
-void FakeVimOptionPage::layoutPage(QWidget *widget)
+static void layoutPage(QWidget *widget)
{
- auto copyTextEditorSettings = new QPushButton(Tr::tr("Copy Text Editor Settings"));
- auto setQtStyle = new QPushButton(Tr::tr("Set Qt Style"));
- auto setPlainStyle = new QPushButton(Tr::tr("Set Plain Style"));
-
using namespace Layouting;
FakeVimSettings &s = *fakeVimSettings();
@@ -426,63 +401,73 @@ void FakeVimOptionPage::layoutPage(QWidget *widget)
}
},
- Row { copyTextEditorSettings, setQtStyle, setPlainStyle, st },
+ Row {
+ PushButton {
+ text(Tr::tr("Copy Text Editor Settings")),
+ onClicked([&s] {
+ TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
+ TypingSettings tps = TextEditorSettings::typingSettings();
+ s.expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
+ s.tabStop.setValue(ts.m_tabSize);
+ s.shiftWidth.setValue(ts.m_indentSize);
+ s.smartTab.setValue(tps.m_smartBackspaceBehavior
+ == TypingSettings::BackspaceFollowsPreviousIndents);
+ s.autoIndent.setValue(true);
+ s.smartIndent.setValue(tps.m_autoIndent);
+ s.incSearch.setValue(true);
+ }),
+ },
+ PushButton {
+ text(Tr::tr("Set Qt Style")),
+ onClicked([&s] {
+ s.expandTab.setVolatileValue(true);
+ s.tabStop.setVolatileValue(4);
+ s.shiftWidth.setVolatileValue(4);
+ s.smartTab.setVolatileValue(true);
+ s.autoIndent.setVolatileValue(true);
+ s.smartIndent.setVolatileValue(true);
+ s.incSearch.setVolatileValue(true);
+ s.backspace.setVolatileValue(QString("indent,eol,start"));
+ s.passKeys.setVolatileValue(true);
+ }),
+ },
+ PushButton {
+ text(Tr::tr("Set Plain Style")),
+ onClicked([&s] {
+ s.expandTab.setVolatileValue(false);
+ s.tabStop.setVolatileValue(8);
+ s.shiftWidth.setVolatileValue(8);
+ s.smartTab.setVolatileValue(false);
+ s.autoIndent.setVolatileValue(false);
+ s.smartIndent.setVolatileValue(false);
+ s.incSearch.setVolatileValue(false);
+ s.backspace.setVolatileValue(QString());
+ s.passKeys.setVolatileValue(false);
+ }),
+ },
+ st
+ },
st
}.attachTo(widget);
s.vimRcPath.setEnabler(&s.readVimRc);
-
- connect(copyTextEditorSettings, &QAbstractButton::clicked,
- this, &FakeVimOptionPage::copyTextEditorSettings);
- connect(setQtStyle, &QAbstractButton::clicked,
- this, &FakeVimOptionPage::setQtStyle);
- connect(setPlainStyle, &QAbstractButton::clicked,
- this, &FakeVimOptionPage::setPlainStyle);
}
-void FakeVimOptionPage::copyTextEditorSettings()
-{
- FakeVimSettings &s = *fakeVimSettings();
- TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
- TypingSettings tps = TextEditorSettings::typingSettings();
- s.expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
- s.tabStop.setValue(ts.m_tabSize);
- s.shiftWidth.setValue(ts.m_indentSize);
- s.smartTab.setValue(tps.m_smartBackspaceBehavior
- == TypingSettings::BackspaceFollowsPreviousIndents);
- s.autoIndent.setValue(true);
- s.smartIndent.setValue(tps.m_autoIndent);
- s.incSearch.setValue(true);
-}
-
-void FakeVimOptionPage::setQtStyle()
-{
- FakeVimSettings &s = *fakeVimSettings();
- s.expandTab.setVolatileValue(true);
- s.tabStop.setVolatileValue(4);
- s.shiftWidth.setVolatileValue(4);
- s.smartTab.setVolatileValue(true);
- s.autoIndent.setVolatileValue(true);
- s.smartIndent.setVolatileValue(true);
- s.incSearch.setVolatileValue(true);
- s.backspace.setVolatileValue(QString("indent,eol,start"));
- s.passKeys.setVolatileValue(true);
-}
-
-void FakeVimOptionPage::setPlainStyle()
+class FakeVimOptionPage : public IOptionsPage
{
- FakeVimSettings &s = *fakeVimSettings();
- s.expandTab.setVolatileValue(false);
- s.tabStop.setVolatileValue(8);
- s.shiftWidth.setVolatileValue(8);
- s.smartTab.setVolatileValue(false);
- s.autoIndent.setVolatileValue(false);
- s.smartIndent.setVolatileValue(false);
- s.incSearch.setVolatileValue(false);
- s.backspace.setVolatileValue(QString());
- s.passKeys.setVolatileValue(false);
-}
+public:
+ FakeVimOptionPage()
+ {
+ setId(SETTINGS_ID);
+ setDisplayName(Tr::tr("General"));
+ setCategory(SETTINGS_CATEGORY);
+ setDisplayCategory(Tr::tr("FakeVim"));
+ setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
+ setLayouter(&layoutPage);
+ setSettings(fakeVimSettings());
+ }
+};
///////////////////////////////////////////////////////////////////////
//
@@ -539,7 +524,22 @@ signals:
void delayedQuitAllRequested(bool forced);
public:
- QHash<IEditor *, FakeVimHandler *> m_editorToHandler;
+ struct HandlerAndData
+ {
+#ifdef Q_OS_WIN
+ // We need to declare a constructor here, otherwise the MSVC 17.5.x compiler fails to parse
+ // the "{nullptr}" initializer in the definition below.
+ // This seems to be a compiler bug,
+ // see: https://developercommunity.visualstudio.com/t/10351118
+ HandlerAndData()
+ : handler(nullptr)
+ {}
+#endif
+ FakeVimHandler *handler{nullptr};
+ TextEditorWidget::SuggestionBlocker suggestionBlocker;
+ };
+
+ QHash<IEditor *, HandlerAndData> m_editorToHandler;
void setActionChecked(Id id, bool check);
@@ -568,12 +568,21 @@ public:
enum { CommandRole = Qt::UserRole };
-class FakeVimExCommandsWidget : public CommandMappings
+const char exCommandMapGroup[] = "FakeVimExCommand";
+const char userCommandMapGroup[] = "FakeVimUserCommand";
+const char reKey[] = "RegEx";
+const char cmdKey[] = "Cmd";
+const char idKey[] = "Command";
+
+class FakeVimExCommandsMappings : public CommandMappings
{
public:
- FakeVimExCommandsWidget();
+ FakeVimExCommandsMappings();
+ void apply();
protected:
+ ExCommandMap exCommandMapFromWidget();
+
void commandChanged();
void resetToDefault();
void defaultAction() override;
@@ -581,113 +590,48 @@ protected:
void handleCurrentCommandChanged(QTreeWidgetItem *current);
private:
- void initialize();
-
- ExCommandMap exCommandMapFromWidget();
-
QGroupBox *m_commandBox;
FancyLineEdit *m_commandEdit;
-
- friend class FakeVimExCommandsPage; // allow the page accessing the ExCommandMaps
};
-FakeVimExCommandsWidget::FakeVimExCommandsWidget()
+FakeVimExCommandsMappings::FakeVimExCommandsMappings()
{
setPageTitle(Tr::tr("Ex Command Mapping"));
setTargetHeader(Tr::tr("Ex Trigger Expression"));
setImportExportEnabled(false);
- connect(this, &FakeVimExCommandsWidget::currentCommandChanged,
- this, &FakeVimExCommandsWidget::handleCurrentCommandChanged);
+ connect(this, &FakeVimExCommandsMappings::currentCommandChanged,
+ this, &FakeVimExCommandsMappings::handleCurrentCommandChanged);
m_commandBox = new QGroupBox(Tr::tr("Ex Command"), this);
m_commandBox->setEnabled(false);
- auto boxLayout = new QHBoxLayout(m_commandBox);
+ auto commandBoxLayout = new QVBoxLayout(m_commandBox);
+ auto boxLayout = new QHBoxLayout;
+ commandBoxLayout->addLayout(boxLayout);
m_commandEdit = new FancyLineEdit(m_commandBox);
m_commandEdit->setFiltering(true);
m_commandEdit->setPlaceholderText(QString());
connect(m_commandEdit, &FancyLineEdit::textChanged,
- this, &FakeVimExCommandsWidget::commandChanged);
+ this, &FakeVimExCommandsMappings::commandChanged);
+ m_commandEdit->setValidationFunction([](FancyLineEdit *e, QString *){
+ return QRegularExpression(e->text()).isValid();
+ });
auto resetButton = new QPushButton(Tr::tr("Reset"), m_commandBox);
resetButton->setToolTip(Tr::tr("Reset to default."));
connect(resetButton, &QPushButton::clicked,
- this, &FakeVimExCommandsWidget::resetToDefault);
+ this, &FakeVimExCommandsMappings::resetToDefault);
boxLayout->addWidget(new QLabel(Tr::tr("Regular expression:")));
boxLayout->addWidget(m_commandEdit);
boxLayout->addWidget(resetButton);
+ auto infoLabel = new InfoLabel(Tr::tr("Invalid regular expression."), InfoLabel::Error);
+ infoLabel->setVisible(false);
+ connect(m_commandEdit, &FancyLineEdit::validChanged, [infoLabel](bool valid){
+ infoLabel->setVisible(!valid);
+ });
+ commandBoxLayout->addWidget(infoLabel);
layout()->addWidget(m_commandBox);
- initialize();
-}
-
-class FakeVimExCommandsPage : public IOptionsPage
-{
-public:
- FakeVimExCommandsPage()
- {
- setId(SETTINGS_EX_CMDS_ID);
- setDisplayName(Tr::tr("Ex Command Mapping"));
- setCategory(SETTINGS_CATEGORY);
- }
-
- QWidget *widget() override
- {
- if (!m_widget)
- m_widget = new FakeVimExCommandsWidget;
- return m_widget;
- }
-
- void apply() override;
- void finish() override {}
-
-private:
- QPointer<FakeVimExCommandsWidget> m_widget;
-};
-
-
-const char exCommandMapGroup[] = "FakeVimExCommand";
-const char userCommandMapGroup[] = "FakeVimUserCommand";
-const char reKey[] = "RegEx";
-const char cmdKey[] = "Cmd";
-const char idKey[] = "Command";
-
-void FakeVimExCommandsPage::apply()
-{
- if (!m_widget) // page has not been shown at all
- return;
- // now save the mappings if necessary
- const ExCommandMap &newMapping = m_widget->exCommandMapFromWidget();
- ExCommandMap &globalCommandMapping = dd->m_exCommandMap;
-
- if (newMapping != globalCommandMapping) {
- const ExCommandMap &defaultMap = dd->m_defaultExCommandMap;
- QSettings *settings = ICore::settings();
- settings->beginWriteArray(exCommandMapGroup);
- int count = 0;
- using Iterator = ExCommandMap::const_iterator;
- const Iterator end = newMapping.constEnd();
- for (Iterator it = newMapping.constBegin(); it != end; ++it) {
- const QString id = it.key();
- const QRegularExpression re = it.value();
-
- if ((defaultMap.contains(id) && defaultMap[id] != re)
- || (!defaultMap.contains(id) && !re.pattern().isEmpty())) {
- settings->setArrayIndex(count);
- settings->setValue(idKey, id);
- settings->setValue(reKey, re.pattern());
- ++count;
- }
- }
- settings->endArray();
- globalCommandMapping.clear();
- globalCommandMapping.insert(defaultMap);
- globalCommandMapping.insert(newMapping);
- }
-}
-
-void FakeVimExCommandsWidget::initialize()
-{
QMap<QString, QTreeWidgetItem *> sections;
const QList<Command *> commands = ActionManager::commands();
@@ -727,7 +671,30 @@ void FakeVimExCommandsWidget::initialize()
handleCurrentCommandChanged(nullptr);
}
-void FakeVimExCommandsWidget::handleCurrentCommandChanged(QTreeWidgetItem *current)
+ExCommandMap FakeVimExCommandsMappings::exCommandMapFromWidget()
+{
+ ExCommandMap map;
+ int n = commandList()->topLevelItemCount();
+ for (int i = 0; i != n; ++i) {
+ QTreeWidgetItem *section = commandList()->topLevelItem(i);
+ int m = section->childCount();
+ for (int j = 0; j != m; ++j) {
+ QTreeWidgetItem *item = section->child(j);
+ const QString name = item->data(0, CommandRole).toString();
+ const QString regex = item->data(2, Qt::DisplayRole).toString();
+ const QString pattern = dd->m_defaultExCommandMap.value(name).pattern();
+ if ((regex.isEmpty() && pattern.isEmpty())
+ || (!regex.isEmpty() && pattern == regex))
+ continue;
+ const QRegularExpression expression(regex);
+ if (expression.isValid())
+ map[name] = expression;
+ }
+ }
+ return map;
+}
+
+void FakeVimExCommandsMappings::handleCurrentCommandChanged(QTreeWidgetItem *current)
{
if (current) {
m_commandEdit->setText(current->text(2));
@@ -738,7 +705,7 @@ void FakeVimExCommandsWidget::handleCurrentCommandChanged(QTreeWidgetItem *curre
}
}
-void FakeVimExCommandsWidget::commandChanged()
+void FakeVimExCommandsMappings::commandChanged()
{
QTreeWidgetItem *current = commandList()->currentItem();
if (!current)
@@ -753,7 +720,7 @@ void FakeVimExCommandsWidget::commandChanged()
setModified(current, regex != dd->m_defaultExCommandMap[name].pattern());
}
-void FakeVimExCommandsWidget::resetToDefault()
+void FakeVimExCommandsMappings::resetToDefault()
{
QTreeWidgetItem *current = commandList()->currentItem();
if (!current)
@@ -765,12 +732,12 @@ void FakeVimExCommandsWidget::resetToDefault()
m_commandEdit->setText(regex);
}
-void FakeVimExCommandsWidget::defaultAction()
+void FakeVimExCommandsMappings::defaultAction()
{
- int n = commandList()->topLevelItemCount();
+ const int n = commandList()->topLevelItemCount();
for (int i = 0; i != n; ++i) {
QTreeWidgetItem *section = commandList()->topLevelItem(i);
- int m = section->childCount();
+ const int m = section->childCount();
for (int j = 0; j != m; ++j) {
QTreeWidgetItem *item = section->child(j);
const QString name = item->data(0, CommandRole).toString();
@@ -785,6 +752,61 @@ void FakeVimExCommandsWidget::defaultAction()
}
}
+void FakeVimExCommandsMappings::apply()
+{
+ // now save the mappings if necessary
+ const ExCommandMap &newMapping = exCommandMapFromWidget();
+ ExCommandMap &globalCommandMapping = dd->m_exCommandMap;
+
+ if (newMapping != globalCommandMapping) {
+ const ExCommandMap &defaultMap = dd->m_defaultExCommandMap;
+ QSettings *settings = ICore::settings();
+ settings->beginWriteArray(exCommandMapGroup);
+ int count = 0;
+ using Iterator = ExCommandMap::const_iterator;
+ const Iterator end = newMapping.constEnd();
+ for (Iterator it = newMapping.constBegin(); it != end; ++it) {
+ const QString id = it.key();
+ const QRegularExpression re = it.value();
+
+ if ((defaultMap.contains(id) && defaultMap[id] != re)
+ || (!defaultMap.contains(id) && !re.pattern().isEmpty())) {
+ settings->setArrayIndex(count);
+ settings->setValue(idKey, id);
+ settings->setValue(reKey, re.pattern());
+ ++count;
+ }
+ }
+ settings->endArray();
+ globalCommandMapping.clear();
+ globalCommandMapping.insert(defaultMap);
+ globalCommandMapping.insert(newMapping);
+ }
+}
+
+class FakeVimExCommandsPageWidget : public IOptionsPageWidget
+{
+public:
+ FakeVimExCommandsPageWidget()
+ {
+ auto exCommands = new FakeVimExCommandsMappings;
+ setOnApply([exCommands] { exCommands->apply(); });
+ Layouting::Column { exCommands, Layouting::noMargin }.attachTo(this);
+ }
+};
+
+class FakeVimExCommandsPage : public IOptionsPage
+{
+public:
+ FakeVimExCommandsPage()
+ {
+ setId(SETTINGS_EX_CMDS_ID);
+ setDisplayName(Tr::tr("Ex Command Mapping"));
+ setCategory(SETTINGS_CATEGORY);
+ setWidgetCreator([] { return new FakeVimExCommandsPageWidget; });
+ }
+};
+
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
@@ -797,28 +819,17 @@ public:
FakeVimUserCommandsModel() { m_commandMap = dd->m_userCommandMap; }
UserCommandMap commandMap() const { return m_commandMap; }
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
- bool setData(const QModelIndex &index, const QVariant &data, int role) override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- Qt::ItemFlags flags(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent) const final { return parent.isValid() ? 0 : 9; }
+ int columnCount(const QModelIndex &parent) const final { return parent.isValid() ? 0 : 2; }
+ QVariant data(const QModelIndex &index, int role) const final;
+ bool setData(const QModelIndex &index, const QVariant &data, int role) final;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const final;
+ Qt::ItemFlags flags(const QModelIndex &index) const final;
private:
UserCommandMap m_commandMap;
};
-int FakeVimUserCommandsModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 9;
-}
-
-int FakeVimUserCommandsModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 2;
-}
-
-
QVariant FakeVimUserCommandsModel::headerData(int section,
Qt::Orientation orient, int role) const
{
@@ -862,83 +873,74 @@ public:
}
};
-class FakeVimUserCommandsPage : public IOptionsPage
+class FakeVimUserCommandsPageWidget : public IOptionsPageWidget
{
public:
- FakeVimUserCommandsPage()
+ FakeVimUserCommandsPageWidget(FakeVimUserCommandsModel *model)
+ : m_model(model)
{
- setId(SETTINGS_USER_CMDS_ID);
- setDisplayName(Tr::tr("User Command Mapping"));
- setCategory(SETTINGS_CATEGORY);
- }
-
- void apply() override;
- void finish() override {}
-
- QWidget *widget() override;
- void initialize() {}
- UserCommandMap currentCommandMap() { return m_model->commandMap(); }
-
-private:
- QPointer<QWidget> m_widget;
- FakeVimUserCommandsModel *m_model = nullptr;
-};
-
-QWidget *FakeVimUserCommandsPage::widget()
-{
- if (!m_widget) {
- m_widget = new QWidget;
-
- m_model = new FakeVimUserCommandsModel;
auto widget = new QTreeView;
- m_model->setParent(widget);
widget->setModel(m_model);
widget->resizeColumnToContents(0);
auto delegate = new FakeVimUserCommandsDelegate(widget);
widget->setItemDelegateForColumn(1, delegate);
- auto layout = new QGridLayout(m_widget);
+ auto layout = new QGridLayout(this);
layout->addWidget(widget, 0, 0);
- m_widget->setLayout(layout);
+ setLayout(layout);
}
- return m_widget;
-}
-void FakeVimUserCommandsPage::apply()
-{
- if (!m_widget) // page has not been shown at all
- return;
-
- // now save the mappings if necessary
- const UserCommandMap &current = currentCommandMap();
- UserCommandMap &userMap = dd->m_userCommandMap;
-
- if (current != userMap) {
- QSettings *settings = ICore::settings();
- settings->beginWriteArray(userCommandMapGroup);
- int count = 0;
- using Iterator = UserCommandMap::const_iterator;
- const Iterator end = current.constEnd();
- for (Iterator it = current.constBegin(); it != end; ++it) {
- const int key = it.key();
- const QString cmd = it.value();
-
- if ((dd->m_defaultUserCommandMap.contains(key)
- && dd->m_defaultUserCommandMap[key] != cmd)
- || (!dd->m_defaultUserCommandMap.contains(key) && !cmd.isEmpty())) {
- settings->setArrayIndex(count);
- settings->setValue(idKey, key);
- settings->setValue(cmdKey, cmd);
- ++count;
+private:
+ void apply() final
+ {
+ // now save the mappings if necessary
+ const UserCommandMap &current = m_model->commandMap();
+ UserCommandMap &userMap = dd->m_userCommandMap;
+
+ if (current != userMap) {
+ QSettings *settings = ICore::settings();
+ settings->beginWriteArray(userCommandMapGroup);
+ int count = 0;
+ using Iterator = UserCommandMap::const_iterator;
+ const Iterator end = current.constEnd();
+ for (Iterator it = current.constBegin(); it != end; ++it) {
+ const int key = it.key();
+ const QString cmd = it.value();
+
+ if ((dd->m_defaultUserCommandMap.contains(key)
+ && dd->m_defaultUserCommandMap[key] != cmd)
+ || (!dd->m_defaultUserCommandMap.contains(key) && !cmd.isEmpty())) {
+ settings->setArrayIndex(count);
+ settings->setValue(idKey, key);
+ settings->setValue(cmdKey, cmd);
+ ++count;
+ }
}
+ settings->endArray();
+ userMap.clear();
+ userMap.insert(dd->m_defaultUserCommandMap);
+ userMap.insert(current);
}
- settings->endArray();
- userMap.clear();
- userMap.insert(dd->m_defaultUserCommandMap);
- userMap.insert(current);
}
-}
+
+ FakeVimUserCommandsModel *m_model;
+};
+
+class FakeVimUserCommandsPage : public IOptionsPage
+{
+public:
+ FakeVimUserCommandsPage()
+ {
+ setId(SETTINGS_USER_CMDS_ID);
+ setDisplayName(Tr::tr("User Command Mapping"));
+ setCategory(SETTINGS_CATEGORY);
+ setWidgetCreator([this] { return new FakeVimUserCommandsPageWidget(&m_model); });
+ }
+
+private:
+ FakeVimUserCommandsModel m_model;
+};
///////////////////////////////////////////////////////////////////////
@@ -1235,7 +1237,7 @@ void FakeVimPluginPrivate::initialize()
void FakeVimPluginPrivate::userActionTriggered(int key)
{
IEditor *editor = EditorManager::currentEditor();
- FakeVimHandler *handler = m_editorToHandler[editor];
+ FakeVimHandler *handler = m_editorToHandler[editor].handler;
if (handler) {
// If disabled, enable FakeVim mode just for single user command.
bool enableFakeVim = !fakeVimSettings()->useFakeVim.value();
@@ -1280,7 +1282,9 @@ void FakeVimPluginPrivate::readSettings()
settings->setArrayIndex(i);
const QString id = settings->value(idKey).toString();
const QString re = settings->value(reKey).toString();
- m_exCommandMap[id] = QRegularExpression(re);
+ const QRegularExpression regEx(re);
+ if (regEx.isValid())
+ m_exCommandMap[id] = regEx;
}
settings->endArray();
@@ -1549,29 +1553,52 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
// the handler might have triggered the deletion of the editor:
// make sure that it can return before being deleted itself
new DeferredDeleter(widget, handler);
- m_editorToHandler[editor] = handler;
+ m_editorToHandler[editor].handler = handler;
- handler->extraInformationChanged.connect([this](const QString &text) {
+ handler->extraInformationChanged.set([this](const QString &text) {
EditorManager::splitSideBySide();
QString title = "stdout.txt";
IEditor *iedit = EditorManager::openEditorWithContents(Id(), &title, text.toUtf8());
EditorManager::activateEditor(iedit);
- FakeVimHandler *handler = m_editorToHandler.value(iedit, nullptr);
+ FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler;
QTC_ASSERT(handler, return);
handler->handleCommand("0");
});
- handler->commandBufferChanged
- .connect([this, handler](const QString &contents, int cursorPos, int anchorPos, int messageLevel) {
- showCommandBuffer(handler, contents, cursorPos, anchorPos, messageLevel);
- });
+ handler->commandBufferChanged.set(
+ [this, handler](const QString &contents, int cursorPos, int anchorPos, int messageLevel) {
+ showCommandBuffer(handler, contents, cursorPos, anchorPos, messageLevel);
+ });
- handler->selectionChanged.connect([tew](const QList<QTextEdit::ExtraSelection> &selection) {
+ handler->selectionChanged.set([tew](const QList<QTextEdit::ExtraSelection> &selection) {
if (tew)
tew->setExtraSelections(TextEditorWidget::FakeVimSelection, selection);
});
- handler->highlightMatches.connect([](const QString &needle) {
+ handler->tabPressedInInsertMode.set([tew]() {
+ auto suggestion = tew->currentSuggestion();
+ if (suggestion) {
+ suggestion->apply();
+ return false;
+ }
+
+ return true;
+ });
+
+ handler->modeChanged.set([tew, this, editor](bool insertMode) {
+ HandlerAndData &handlerAndData = m_editorToHandler[editor];
+ if (!handlerAndData.handler->inFakeVimMode())
+ return;
+
+ // We don't want to show suggestions unless we are in insert mode.
+ if (insertMode != (handlerAndData.suggestionBlocker == nullptr))
+ handlerAndData.suggestionBlocker = insertMode ? nullptr : tew->blockSuggestions();
+
+ if (tew)
+ tew->clearSuggestion();
+ });
+
+ handler->highlightMatches.set([](const QString &needle) {
for (IEditor *editor : EditorManager::visibleEditors()) {
QWidget *w = editor->widget();
if (auto find = Aggregation::query<IFindSupport>(w))
@@ -1579,7 +1606,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->moveToMatchingParenthesis.connect([](bool *moved, bool *forward, QTextCursor *cursor) {
+ handler->moveToMatchingParenthesis.set([](bool *moved, bool *forward, QTextCursor *cursor) {
*moved = false;
bool undoFakeEOL = false;
@@ -1612,14 +1639,14 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->indentRegion.connect([tew](int beginBlock, int endBlock, QChar typedChar) {
+ handler->indentRegion.set([tew](int beginBlock, int endBlock, QChar typedChar) {
if (!tew)
return;
TabSettings tabSettings;
- tabSettings.m_indentSize = fakeVimSettings()->shiftWidth.value();
- tabSettings.m_tabSize = fakeVimSettings()->tabStop.value();
- tabSettings.m_tabPolicy = fakeVimSettings()->expandTab.value()
+ tabSettings.m_indentSize = fakeVimSettings()->shiftWidth();
+ tabSettings.m_tabSize = fakeVimSettings()->tabStop();
+ tabSettings.m_tabPolicy = fakeVimSettings()->expandTab()
? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy;
tabSettings.m_continuationAlignBehavior =
tew->textDocument()->tabSettings().m_continuationAlignBehavior;
@@ -1645,17 +1672,17 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->checkForElectricCharacter.connect([tew](bool *result, QChar c) {
+ handler->checkForElectricCharacter.set([tew](bool *result, QChar c) {
if (tew)
*result = tew->textDocument()->indenter()->isElectricCharacter(c);
});
- handler->requestDisableBlockSelection.connect([tew] {
+ handler->requestDisableBlockSelection.set([tew] {
if (tew)
tew->setTextCursor(tew->textCursor());
});
- handler->requestSetBlockSelection.connect([tew](const QTextCursor &cursor) {
+ handler->requestSetBlockSelection.set([tew](const QTextCursor &cursor) {
if (tew) {
const TabSettings &tabs = tew->textDocument()->tabSettings();
MultiTextCursor mtc;
@@ -1679,7 +1706,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->requestBlockSelection.connect([tew](QTextCursor *cursor) {
+ handler->requestBlockSelection.set([tew](QTextCursor *cursor) {
if (tew && cursor) {
MultiTextCursor mtc = tew->multiTextCursor();
*cursor = mtc.cursors().first();
@@ -1687,16 +1714,16 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->requestHasBlockSelection.connect([tew](bool *on) {
+ handler->requestHasBlockSelection.set([tew](bool *on) {
if (tew && on)
*on = tew->multiTextCursor().hasMultipleCursors();
});
- handler->simpleCompletionRequested.connect([this, handler](const QString &needle, bool forward) {
+ handler->simpleCompletionRequested.set([this, handler](const QString &needle, bool forward) {
runData->wordProvider.setActive(needle, forward, handler);
});
- handler->windowCommandRequested.connect([this, handler](const QString &map, int count) {
+ handler->windowCommandRequested.set([this, handler](const QString &map, int count) {
// normalize mapping
const QString key = map.toUpper();
@@ -1726,22 +1753,22 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
qDebug() << "UNKNOWN WINDOW COMMAND: <C-W>" << map;
});
- handler->findRequested.connect([](bool reverse) {
+ handler->findRequested.set([](bool reverse) {
Find::setUseFakeVim(true);
Find::openFindToolBar(reverse ? Find::FindBackwardDirection
: Find::FindForwardDirection);
});
- handler->findNextRequested.connect([](bool reverse) {
+ handler->findNextRequested.set([](bool reverse) {
triggerAction(reverse ? Core::Constants::FIND_PREVIOUS : Core::Constants::FIND_NEXT);
});
- handler->foldToggle.connect([this, handler](int depth) {
+ handler->foldToggle.set([this, handler](int depth) {
QTextBlock block = handler->textCursor().block();
fold(handler, depth, !TextDocumentLayout::isFolded(block));
});
- handler->foldAll.connect([handler](bool fold) {
+ handler->foldAll.set([handler](bool fold) {
QTextDocument *document = handler->textCursor().document();
auto documentLayout = qobject_cast<TextDocumentLayout*>(document->documentLayout());
QTC_ASSERT(documentLayout, return);
@@ -1756,11 +1783,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
documentLayout->emitDocumentSizeChanged();
});
- handler->fold.connect([this, handler](int depth, bool dofold) {
- fold(handler, depth, dofold);
- });
+ handler->fold.set([this, handler](int depth, bool dofold) { fold(handler, depth, dofold); });
- handler->foldGoTo.connect([handler](int count, bool current) {
+ handler->foldGoTo.set([handler](int count, bool current) {
QTextCursor tc = handler->textCursor();
QTextBlock block = tc.block();
@@ -1812,31 +1837,39 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
}
});
- handler->requestJumpToGlobalMark.connect(
+ handler->requestJumpToGlobalMark.set(
[this](QChar mark, bool backTickMode, const QString &fileName) {
if (IEditor *iedit = EditorManager::openEditor(FilePath::fromString(fileName))) {
- if (FakeVimHandler *handler = m_editorToHandler.value(iedit, nullptr))
+ if (FakeVimHandler *handler = m_editorToHandler.value(iedit, {}).handler)
handler->jumpToLocalMark(mark, backTickMode);
}
});
- handler->handleExCommandRequested.connect([this, handler](bool *handled, const ExCommand &cmd) {
+ handler->handleExCommandRequested.set([this, handler](bool *handled, const ExCommand &cmd) {
handleExCommand(handler, handled, cmd);
});
- handler->tabNextRequested.connect([] {
- triggerAction(Core::Constants::GOTONEXTINHISTORY);
- });
+ handler->tabNextRequested.set([] { triggerAction(Core::Constants::GOTONEXTINHISTORY); });
- handler->tabPreviousRequested.connect([] {
- triggerAction(Core::Constants::GOTOPREVINHISTORY);
- });
+ handler->tabPreviousRequested.set([] { triggerAction(Core::Constants::GOTOPREVINHISTORY); });
- handler->completionRequested.connect([this, tew] {
+ handler->completionRequested.set([this, tew] {
if (tew)
tew->invokeAssist(Completion, &runData->wordProvider);
});
+ handler->processOutput.set([](const QString &command, const QString &input, QString *output) {
+ Process proc;
+ proc.setCommand(Utils::CommandLine::fromUserInput(command));
+ proc.setWriteData(input.toLocal8Bit());
+ proc.start();
+
+ // FIXME: Process should be interruptable by user.
+ // Solution is to create a QObject for each process and emit finished state.
+ proc.waitForFinished();
+ *output = proc.cleanedStdOut();
+ });
+
connect(ICore::instance(), &ICore::saveSettingsRequested,
this, &FakeVimPluginPrivate::writeSettings);
@@ -1862,7 +1895,7 @@ void FakeVimPluginPrivate::editorAboutToClose(IEditor *editor)
void FakeVimPluginPrivate::currentEditorAboutToChange(IEditor *editor)
{
- if (FakeVimHandler *handler = m_editorToHandler.value(editor, 0))
+ if (FakeVimHandler *handler = m_editorToHandler.value(editor, {}).handler)
handler->enterCommandMode();
}
@@ -1880,9 +1913,9 @@ void FakeVimPluginPrivate::documentRenamed(
void FakeVimPluginPrivate::renameFileNameInEditors(const FilePath &oldPath, const FilePath &newPath)
{
- for (FakeVimHandler *handler : m_editorToHandler) {
- if (handler->currentFileName() == oldPath.toString())
- handler->setCurrentFileName(newPath.toString());
+ for (const HandlerAndData &handlerAndData : m_editorToHandler) {
+ if (handlerAndData.handler->currentFileName() == oldPath.toString())
+ handlerAndData.handler->setCurrentFileName(newPath.toString());
}
}
@@ -1901,16 +1934,19 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
//ICore *core = ICore::instance();
//core->updateAdditionalContexts(Context(FAKEVIM_CONTEXT),
// Context());
- for (FakeVimHandler *handler : m_editorToHandler)
- handler->setupWidget();
+ for (const HandlerAndData &handlerAndData : m_editorToHandler)
+ handlerAndData.handler->setupWidget();
} else {
//ICore *core = ICore::instance();
//core->updateAdditionalContexts(Context(),
// Context(FAKEVIM_CONTEXT));
resetCommandBuffer();
- for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it) {
- if (auto textDocument = qobject_cast<const TextDocument *>(it.key()->document()))
- it.value()->restoreWidget(textDocument->tabSettings().m_tabSize);
+ for (auto it = m_editorToHandler.begin(); it != m_editorToHandler.end(); ++it) {
+ if (auto textDocument = qobject_cast<const TextDocument *>(it.key()->document())) {
+ HandlerAndData &handlerAndData = it.value();
+ handlerAndData.handler->restoreWidget(textDocument->tabSettings().m_tabSize);
+ handlerAndData.suggestionBlocker.reset();
+ }
}
}
}
@@ -1945,11 +1981,22 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle
if (editor)
editor->setFocus();
+ auto editorFromHandler = [this, handler]() -> Core::IEditor * {
+ auto itEditor = std::find_if(m_editorToHandler.cbegin(),
+ m_editorToHandler.cend(),
+ [handler](const HandlerAndData &handlerAndData) {
+ return handlerAndData.handler == handler;
+ });
+ if (itEditor != m_editorToHandler.cend())
+ return itEditor.key();
+ return nullptr;
+ };
+
*handled = true;
if ((cmd.matches("w", "write") || cmd.cmd == "wq") && cmd.args.isEmpty()) {
// :w[rite]
bool saved = false;
- IEditor *editor = m_editorToHandler.key(handler);
+ IEditor *editor = editorFromHandler();
const QString fileName = handler->currentFileName();
if (editor && editor->document()->filePath().toString() == fileName) {
triggerAction(Core::Constants::SAVE);
@@ -1961,7 +2008,7 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle
handler->showMessage(MessageInfo, Tr::tr("\"%1\" %2 %3L, %4C written")
.arg(fileName).arg(' ').arg(ba.count('\n')).arg(ba.size()));
if (cmd.cmd == "wq")
- emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+ emit delayedQuitRequested(cmd.hasBang, editor);
}
}
}
@@ -1980,7 +2027,7 @@ void FakeVimPluginPrivate::handleExCommand(FakeVimHandler *handler, bool *handle
emit delayedQuitAllRequested(cmd.hasBang);
} else if (cmd.matches("q", "quit")) {
// :q[uit]
- emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+ emit delayedQuitRequested(cmd.hasBang, editorFromHandler());
} else if (cmd.matches("qa", "qall")) {
// :qa[ll]
emit delayedQuitAllRequested(cmd.hasBang);
@@ -2096,28 +2143,6 @@ void FakeVimPluginPrivate::switchToFile(int n)
EditorManager::activateEditorForEntry(DocumentModel::entries().at(n));
}
-ExCommandMap FakeVimExCommandsWidget::exCommandMapFromWidget()
-{
- ExCommandMap map;
- int n = commandList()->topLevelItemCount();
- for (int i = 0; i != n; ++i) {
- QTreeWidgetItem *section = commandList()->topLevelItem(i);
- int m = section->childCount();
- for (int j = 0; j != m; ++j) {
- QTreeWidgetItem *item = section->child(j);
- const QString name = item->data(0, CommandRole).toString();
- const QString regex = item->data(2, Qt::DisplayRole).toString();
- const QString pattern = dd->m_defaultExCommandMap.value(name).pattern();
- if ((regex.isEmpty() && pattern.isEmpty())
- || (!regex.isEmpty() && pattern == regex))
- continue;
- map[name] = QRegularExpression(regex);
- }
- }
- return map;
-}
-
-
///////////////////////////////////////////////////////////////////////
//
@@ -2164,7 +2189,7 @@ void FakeVimPlugin::setupTest(QString *title, FakeVimHandler **handler, QWidget
IEditor *iedit = EditorManager::openEditorWithContents(Id(), title);
EditorManager::activateEditor(iedit);
*edit = iedit->widget();
- *handler = dd->m_editorToHandler.value(iedit, 0);
+ *handler = dd->m_editorToHandler.value(iedit, {}).handler;
(*handler)->setupWidget();
(*handler)->handleCommand("set startofline");
diff --git a/src/plugins/fossil/configuredialog.cpp b/src/plugins/fossil/configuredialog.cpp
index 69c74ad8c8..e5b6c8f3e7 100644
--- a/src/plugins/fossil/configuredialog.cpp
+++ b/src/plugins/fossil/configuredialog.cpp
@@ -67,7 +67,7 @@ ConfigureDialog::ConfigureDialog(QWidget *parent) : QDialog(parent),
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Repository User")),
diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp
index 16038150cb..83a2938f91 100644
--- a/src/plugins/fossil/fossilclient.cpp
+++ b/src/plugins/fossil/fossilclient.cpp
@@ -16,9 +16,9 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/processenums.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <QSyntaxHighlighter>
@@ -55,9 +55,9 @@ public:
addReloadButton();
if (features.testFlag(FossilClient::DiffIgnoreWhiteSpaceFeature)) {
mapSetting(addToggleButton("-w", Tr::tr("Ignore All Whitespace")),
- &client->settings().diffIgnoreAllWhiteSpace);
+ &settings().diffIgnoreAllWhiteSpace);
mapSetting(addToggleButton("--strip-trailing-cr", Tr::tr("Strip Trailing CR")),
- &client->settings().diffStripTrailingCR);
+ &settings().diffStripTrailingCR);
}
}
};
@@ -73,20 +73,19 @@ public:
{
QTC_ASSERT(client, return);
- FossilSettings &settings = client->settings();
FossilClient::SupportedFeatures features = client->supportedFeatures();
if (features.testFlag(FossilClient::AnnotateBlameFeature)) {
mapSetting(addToggleButton("|BLAME|", Tr::tr("Show Committers")),
- &settings.annotateShowCommitters);
+ &settings().annotateShowCommitters);
}
// Force listVersions setting to false by default.
// This way the annotated line number would not get offset by the version list.
- settings.annotateListVersions.setValue(false);
+ settings().annotateListVersions.setValue(false);
mapSetting(addToggleButton("--log", Tr::tr("List Versions")),
- &settings.annotateListVersions);
+ &settings().annotateListVersions);
}
};
@@ -122,8 +121,6 @@ public:
void addLineageComboBox()
{
- FossilSettings &settings = m_client->settings();
-
// ancestors/descendants filter
// This is a positional argument not an option.
// Normally it takes the checkin/branch/tag as an additional parameter
@@ -137,23 +134,19 @@ public:
ChoiceItem(Tr::tr("Unfiltered"), "")
};
mapSetting(addChoices(Tr::tr("Lineage"), QStringList("|LINEAGE|%1|current"), lineageFilterChoices),
- &settings.timelineLineageFilter);
+ &settings().timelineLineageFilter);
}
void addVerboseToggleButton()
{
- FossilSettings &settings = m_client->settings();
-
// show files
mapSetting(addToggleButton("-showfiles", Tr::tr("Verbose"),
Tr::tr("Show files changed in each revision")),
- &settings.timelineVerbose);
+ &settings().timelineVerbose);
}
void addItemTypeComboBox()
{
- FossilSettings &settings = m_client->settings();
-
// option: -t <val>
const QList<ChoiceItem> itemTypeChoices = {
ChoiceItem(Tr::tr("All Items"), "all"),
@@ -169,7 +162,7 @@ public:
// Fossil expects separate arguments for option and value ( i.e. "-t" "all")
// so we need to handle the splitting explicitly in arguments().
mapSetting(addChoices(Tr::tr("Item Types"), QStringList("-t %1"), itemTypeChoices),
- &settings.timelineItemType);
+ &settings().timelineItemType);
}
QStringList arguments() const final
@@ -224,17 +217,17 @@ QString FossilClient::makeVersionString(unsigned version)
.arg(versionPart(version));
}
-FossilClient::FossilClient(FossilSettings *settings)
- : VcsBaseClient(settings), m_settings(settings)
+FossilSettings &FossilClient::settings() const
{
- setDiffConfigCreator([this](QToolBar *toolBar) {
- return new FossilDiffConfig(this, toolBar);
- });
+ return Internal::settings();
}
-FossilSettings &FossilClient::settings() const
+FossilClient::FossilClient()
+ : VcsBaseClient(&Internal::settings())
{
- return *m_settings;
+ setDiffConfigCreator([this](QToolBar *toolBar) {
+ return new FossilDiffConfig(this, toolBar);
+ });
}
unsigned int FossilClient::synchronousBinaryVersion() const
diff --git a/src/plugins/fossil/fossilclient.h b/src/plugins/fossil/fossilclient.h
index 21dfeaae04..b301637846 100644
--- a/src/plugins/fossil/fossilclient.h
+++ b/src/plugins/fossil/fossilclient.h
@@ -41,7 +41,7 @@ public:
static unsigned makeVersionNumber(int major, int minor, int patch);
static QString makeVersionString(unsigned version);
- explicit FossilClient(FossilSettings *settings);
+ FossilClient();
FossilSettings &settings() const;
unsigned int synchronousBinaryVersion() const;
@@ -107,7 +107,6 @@ private:
VcsBase::VcsBaseEditorConfig *createLogEditor(VcsBase::VcsBaseEditorWidget *editor);
friend class FossilPluginPrivate;
- FossilSettings *m_settings = nullptr;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(FossilClient::SupportedFeatures)
diff --git a/src/plugins/fossil/fossilcommitwidget.cpp b/src/plugins/fossil/fossilcommitwidget.cpp
index 5658ed9f76..355a792bfd 100644
--- a/src/plugins/fossil/fossilcommitwidget.cpp
+++ b/src/plugins/fossil/fossilcommitwidget.cpp
@@ -120,8 +120,9 @@ FossilCommitWidget::FossilCommitWidget() : m_commitPanel(new QWidget)
Tr::tr("Tags:"), m_tagsLineEdit, br,
Tr::tr("Author:"), m_authorLineEdit, st,
}
- }
- }.attachTo(m_commitPanel, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(m_commitPanel);
insertTopWidget(m_commitPanel);
new FossilSubmitHighlighter(descriptionEdit());
diff --git a/src/plugins/fossil/fossilplugin.cpp b/src/plugins/fossil/fossilplugin.cpp
index 94f800e1ea..4979b1d120 100644
--- a/src/plugins/fossil/fossilplugin.cpp
+++ b/src/plugins/fossil/fossilplugin.cpp
@@ -187,10 +187,8 @@ public:
bool pullOrPush(SyncMode mode);
// Variables
- FossilSettings m_fossilSettings;
- FossilClient m_client{&m_fossilSettings};
-
- OptionsPage optionPage{[this] { configurationChanged(); }, &m_fossilSettings};
+ FossilSettings m_settings;
+ FossilClient m_client;
VcsSubmitEditorFactory submitEditorFactory {
submitEditorParameters,
@@ -274,11 +272,6 @@ void FossilPlugin::extensionsInitialized()
dd->extensionsInitialized();
}
-const FossilSettings &FossilPlugin::settings()
-{
- return dd->m_fossilSettings;
-}
-
FossilClient *FossilPlugin::client()
{
return &dd->m_client;
@@ -293,11 +286,13 @@ FossilPluginPrivate::FossilPluginPrivate()
connect(&m_client, &VcsBase::VcsBaseClient::changed, this, &FossilPluginPrivate::changed);
m_commandLocator = new Core::CommandLocator("Fossil", "fossil", "fossil", this);
+ m_commandLocator->setDescription(Tr::tr("Triggers a Fossil version control operation."));
ProjectExplorer::JsonWizardFactory::addWizardPath(Utils::FilePath::fromString(Constants::WIZARD_PATH));
- Core::JsExpander::registerGlobalObject("Fossil", [this] {
- return new FossilJsExtension(&m_fossilSettings);
- });
+ Core::JsExpander::registerGlobalObject("Fossil", [] { return new FossilJsExtension; });
+
+ connect(&settings(), &AspectContainer::changed,
+ this, &IVersionControl::configurationChanged);
createMenu(context);
}
@@ -417,10 +412,10 @@ void FossilPluginPrivate::logCurrentFile()
QTC_ASSERT(state.hasFile(), return);
FossilClient::SupportedFeatures features = m_client.supportedFeatures();
QStringList extraOptions;
- extraOptions << "-n" << QString::number(m_client.settings().logCount.value());
+ extraOptions << "-n" << QString::number(m_client.settings().logCount());
if (features.testFlag(FossilClient::TimelineWidthFeature))
- extraOptions << "-W" << QString::number(m_client.settings().timelineWidth.value());
+ extraOptions << "-W" << QString::number(m_client.settings().timelineWidth());
// disable annotate context menu for older client versions, used to be supported for current revision only
bool enableAnnotationContextMenu = features.testFlag(FossilClient::AnnotateRevisionFeature);
@@ -502,10 +497,10 @@ void FossilPluginPrivate::logRepository()
QTC_ASSERT(state.hasTopLevel(), return);
FossilClient::SupportedFeatures features = m_client.supportedFeatures();
QStringList extraOptions;
- extraOptions << "-n" << QString::number(m_client.settings().logCount.value());
+ extraOptions << "-n" << QString::number(m_client.settings().logCount());
if (features.testFlag(FossilClient::TimelineWidthFeature))
- extraOptions << "-W" << QString::number(m_client.settings().timelineWidth.value());
+ extraOptions << "-W" << QString::number(m_client.settings().timelineWidth());
m_client.log(state.topLevel(), {}, extraOptions);
}
@@ -1093,7 +1088,7 @@ RevertDialog::RevertDialog(const QString &title, QWidget *parent)
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
Tr::tr("Revision"), m_revisionLineEdit, br,
}.attachTo(groupBox);
diff --git a/src/plugins/fossil/fossilplugin.h b/src/plugins/fossil/fossilplugin.h
index b3a45f62db..7297cd05ae 100644
--- a/src/plugins/fossil/fossilplugin.h
+++ b/src/plugins/fossil/fossilplugin.h
@@ -3,8 +3,6 @@
#pragma once
-#include "fossilsettings.h"
-
#include <vcsbase/vcsbaseclient.h>
#include <vcsbase/vcsbaseplugin.h>
#include <coreplugin/icontext.h>
@@ -25,7 +23,6 @@ class FossilPlugin final : public ExtensionSystem::IPlugin
void extensionsInitialized() final;
public:
- static const FossilSettings &settings();
static FossilClient *client();
#ifdef WITH_TESTS
diff --git a/src/plugins/fossil/fossilsettings.cpp b/src/plugins/fossil/fossilsettings.cpp
index 130ff5c2c5..b72f6b7623 100644
--- a/src/plugins/fossil/fossilsettings.cpp
+++ b/src/plugins/fossil/fossilsettings.cpp
@@ -15,16 +15,25 @@
using namespace Utils;
-namespace Fossil {
-namespace Internal {
+namespace Fossil::Internal {
+
+static FossilSettings *theSettings;
+
+FossilSettings &settings()
+{
+ return *theSettings;
+}
FossilSettings::FossilSettings()
{
+ theSettings = this;
+
setSettingsGroup(Constants::FOSSIL);
- setAutoApply(false);
+ setId(Constants::VCS_ID_FOSSIL);
+ setDisplayName(Tr::tr("Fossil"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setDefaultValue(Constants::FOSSILDEFAULT);
binaryPath.setDisplayName(Tr::tr("Fossil Command"));
@@ -95,80 +104,38 @@ FossilSettings::FossilSettings()
logCount.setLabelText(Tr::tr("Log count:"));
logCount.setToolTip(Tr::tr("The number of recent commit log entries to show. "
"Choose 0 to see all entries."));
-};
-
-// OptionsPage
-class OptionsPageWidget final : public Core::IOptionsPageWidget
-{
-public:
- OptionsPageWidget(const std::function<void()> &onApply, FossilSettings *settings);
- void apply() final;
-
-private:
- const std::function<void()> m_onApply;
- FossilSettings *m_settings;
-};
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ Column {
+ Group {
+ title(Tr::tr("Configuration")),
+ Row { binaryPath }
+ },
-void OptionsPageWidget::apply()
-{
- if (!m_settings->isDirty())
- return;
+ Group {
+ title(Tr::tr("Local Repositories")),
+ Row { defaultRepoPath }
+ },
- m_settings->apply();
- m_onApply();
-}
+ Group {
+ title(Tr::tr("User")),
+ Form {
+ userName, br,
+ sslIdentityFile
+ }
+ },
-OptionsPageWidget::OptionsPageWidget(const std::function<void()> &onApply, FossilSettings *settings) :
- m_onApply(onApply),
- m_settings(settings)
-{
- FossilSettings &s = *m_settings;
-
- using namespace Layouting;
-
- Column {
- Group {
- title(Tr::tr("Configuration")),
- Row { s.binaryPath }
- },
-
- Group {
- title(Tr::tr("Local Repositories")),
- Row { s.defaultRepoPath }
- },
- Group {
- title(Tr::tr("User")),
- Form {
- s.userName, br,
- s.sslIdentityFile
- }
- },
-
- Group {
- title(Tr::tr("Miscellaneous")),
- Column {
- Row {
- s.logCount,
- s.timelineWidth,
- s.timeout,
- st
+ Group {
+ title(Tr::tr("Miscellaneous")),
+ Column {
+ Row { logCount, timelineWidth, timeout, st },
+ disableAutosync
},
- s.disableAutosync
},
- },
- st
-
- }.attachTo(this);
-}
-
-OptionsPage::OptionsPage(const std::function<void()> &onApply, FossilSettings *settings)
-{
- setId(Constants::VCS_ID_FOSSIL);
- setDisplayName(Tr::tr("Fossil"));
- setWidgetCreator([onApply, settings]() { return new OptionsPageWidget(onApply, settings); });
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+ st
+ }.attachTo(widget);
+ });
}
-} // Internal
-} // Fossil
+} // Fossil::Internal
diff --git a/src/plugins/fossil/fossilsettings.h b/src/plugins/fossil/fossilsettings.h
index e4133393f6..eeea6041bd 100644
--- a/src/plugins/fossil/fossilsettings.h
+++ b/src/plugins/fossil/fossilsettings.h
@@ -3,15 +3,15 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
#include <vcsbase/vcsbaseclientsettings.h>
-namespace Fossil {
-namespace Internal {
+namespace Fossil::Internal {
class FossilSettings : public VcsBase::VcsBaseSettings
{
public:
+ FossilSettings();
+
Utils::StringAspect defaultRepoPath;
Utils::StringAspect sslIdentityFile;
Utils::BoolAspect diffIgnoreAllWhiteSpace;
@@ -23,10 +23,10 @@ public:
Utils::BoolAspect timelineVerbose;
Utils::StringAspect timelineItemType;
Utils::BoolAspect disableAutosync;
-
- FossilSettings();
};
+FossilSettings &settings();
+
struct RepositorySettings
{
enum AutosyncMode {AutosyncOff, AutosyncOn, AutosyncPullOnly};
@@ -34,20 +34,13 @@ struct RepositorySettings
QString user;
QString sslIdentityFile;
AutosyncMode autosync = AutosyncOn;
-};
-
-inline bool operator==(const RepositorySettings &lh, const RepositorySettings &rh)
-{
- return (lh.user == rh.user &&
- lh.sslIdentityFile == rh.sslIdentityFile &&
- lh.autosync == rh.autosync);
-}
-class OptionsPage : public Core::IOptionsPage
-{
-public:
- OptionsPage(const std::function<void()> &onApply, FossilSettings *settings);
+ friend bool operator==(const RepositorySettings &lh, const RepositorySettings &rh)
+ {
+ return lh.user == rh.user
+ && lh.sslIdentityFile == rh.sslIdentityFile
+ && lh.autosync == rh.autosync;
+ }
};
-} // namespace Internal
-} // namespace Fossil
+} // Fossil::Internal
diff --git a/src/plugins/fossil/pullorpushdialog.cpp b/src/plugins/fossil/pullorpushdialog.cpp
index 05bdf136f1..cd72f2bd01 100644
--- a/src/plugins/fossil/pullorpushdialog.cpp
+++ b/src/plugins/fossil/pullorpushdialog.cpp
@@ -52,7 +52,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Remote Location")),
diff --git a/src/plugins/fossil/wizard/fossiljsextension.cpp b/src/plugins/fossil/wizard/fossiljsextension.cpp
index 66747db567..4c013b503a 100644
--- a/src/plugins/fossil/wizard/fossiljsextension.cpp
+++ b/src/plugins/fossil/wizard/fossiljsextension.cpp
@@ -17,19 +17,6 @@ using namespace Core;
namespace Fossil {
namespace Internal {
-class FossilJsExtensionPrivate {
-public:
- FossilJsExtensionPrivate(FossilSettings *settings) :
- m_vscId(Constants::VCS_ID_FOSSIL),
- m_settings(settings)
- {
- }
-
- Utils::Id m_vscId;
- FossilSettings *m_settings;
-};
-
-
QMap<QString, QString> FossilJsExtension::parseArgOptions(const QStringList &args)
{
QMap<QString, QString> options;
@@ -42,24 +29,19 @@ QMap<QString, QString> FossilJsExtension::parseArgOptions(const QStringList &arg
return options;
}
-FossilJsExtension::FossilJsExtension(FossilSettings *settings) :
- d(new FossilJsExtensionPrivate(settings))
-{ }
+FossilJsExtension::FossilJsExtension() = default;
-FossilJsExtension::~FossilJsExtension()
-{
- delete d;
-}
+FossilJsExtension::~FossilJsExtension() = default;
bool FossilJsExtension::isConfigured() const
{
- IVersionControl *vc = VcsManager::versionControl(d->m_vscId);
+ IVersionControl *vc = VcsManager::versionControl(Constants::VCS_ID_FOSSIL);
return vc && vc->isConfigured();
}
QString FossilJsExtension::displayName() const
{
- IVersionControl *vc = VcsManager::versionControl(d->m_vscId);
+ IVersionControl *vc = VcsManager::versionControl(Constants::VCS_ID_FOSSIL);
return vc ? vc->displayName() : QString();
}
@@ -68,7 +50,7 @@ QString FossilJsExtension::defaultAdminUser() const
if (!isConfigured())
return QString();
- return d->m_settings->userName.value();
+ return settings().userName.value();
}
QString FossilJsExtension::defaultSslIdentityFile() const
@@ -76,7 +58,7 @@ QString FossilJsExtension::defaultSslIdentityFile() const
if (!isConfigured())
return QString();
- return d->m_settings->sslIdentityFile.value();
+ return settings().sslIdentityFile.value();
}
QString FossilJsExtension::defaultLocalRepoPath() const
@@ -84,7 +66,7 @@ QString FossilJsExtension::defaultLocalRepoPath() const
if (!isConfigured())
return QString();
- return d->m_settings->defaultRepoPath.value();
+ return settings().defaultRepoPath.value();
}
bool FossilJsExtension::defaultDisableAutosync() const
@@ -92,7 +74,7 @@ bool FossilJsExtension::defaultDisableAutosync() const
if (!isConfigured())
return false;
- return d->m_settings->disableAutosync.value();
+ return settings().disableAutosync.value();
}
} // namespace Internal
diff --git a/src/plugins/fossil/wizard/fossiljsextension.h b/src/plugins/fossil/wizard/fossiljsextension.h
index 3a37fc5280..0947693d58 100644
--- a/src/plugins/fossil/wizard/fossiljsextension.h
+++ b/src/plugins/fossil/wizard/fossiljsextension.h
@@ -12,9 +12,6 @@
namespace Fossil {
namespace Internal {
-class FossilJsExtensionPrivate;
-class FossilSettings;
-
class FossilJsExtension : public QObject
{
Q_OBJECT
@@ -22,7 +19,7 @@ class FossilJsExtension : public QObject
public:
static QMap<QString, QString> parseArgOptions(const QStringList &args);
- FossilJsExtension(FossilSettings *settings);
+ FossilJsExtension();
~FossilJsExtension();
Q_INVOKABLE bool isConfigured() const;
@@ -31,9 +28,6 @@ public:
Q_INVOKABLE QString defaultSslIdentityFile() const;
Q_INVOKABLE QString defaultLocalRepoPath() const;
Q_INVOKABLE bool defaultDisableAutosync() const;
-
-private:
- FossilJsExtensionPrivate *d = nullptr;
};
} // namespace Internal
diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp
index fbe77e74a9..ef83f906a9 100644
--- a/src/plugins/genericprojectmanager/genericproject.cpp
+++ b/src/plugins/genericprojectmanager/genericproject.cpp
@@ -38,8 +38,8 @@
#include <utils/algorithm.h>
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
@@ -96,7 +96,7 @@ private:
////////////////////////////////////////////////////////////////////////////////////
//
-// GenericProjectNode
+// GenericBuildSystem
//
////////////////////////////////////////////////////////////////////////////////////
@@ -401,7 +401,7 @@ static QStringList readFlags(const QString &filePath)
return QStringList();
QStringList flags;
for (const auto &line : lines)
- flags.append(ProcessArgs::splitArgs(line));
+ flags.append(ProcessArgs::splitArgs(line, HostOsInfo::hostOs()));
return flags;
}
@@ -570,8 +570,8 @@ void GenericBuildSystem::refreshCppCodeModel()
rpp.setQtVersion(kitInfo.projectPartQtVersion);
rpp.setHeaderPaths(m_projectIncludePaths);
rpp.setConfigFileName(m_configFileName);
- rpp.setFlagsForCxx({nullptr, m_cxxflags, projectDirectory().toString()});
- rpp.setFlagsForC({nullptr, m_cflags, projectDirectory().toString()});
+ rpp.setFlagsForCxx({nullptr, m_cxxflags, projectDirectory()});
+ rpp.setFlagsForC({nullptr, m_cflags, projectDirectory()});
static const auto sourceFilesToStringList = [](const SourceFiles &sourceFiles) {
return Utils::transform(sourceFiles, [](const SourceFile &f) {
diff --git a/src/plugins/git/branchadddialog.cpp b/src/plugins/git/branchadddialog.cpp
index 3dfd7ad946..011a01ea1e 100644
--- a/src/plugins/git/branchadddialog.cpp
+++ b/src/plugins/git/branchadddialog.cpp
@@ -125,7 +125,7 @@ BranchAddDialog::BranchAddDialog(const QStringList &localBranches, Type type, QW
break;
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { branchNameLabel, m_branchNameEdit },
diff --git a/src/plugins/git/branchcheckoutdialog.cpp b/src/plugins/git/branchcheckoutdialog.cpp
index 9fa2e83a0b..9615c28ff6 100644
--- a/src/plugins/git/branchcheckoutdialog.cpp
+++ b/src/plugins/git/branchcheckoutdialog.cpp
@@ -45,7 +45,7 @@ BranchCheckoutDialog::BranchCheckoutDialog(QWidget *parent,
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_makeStashRadioButton,
diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp
index 3c4971fa73..8a495a8d8f 100644
--- a/src/plugins/git/branchmodel.cpp
+++ b/src/plugins/git/branchmodel.cpp
@@ -11,8 +11,8 @@
#include <vcsbase/vcsoutputwindow.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <QDateTime>
@@ -20,6 +20,7 @@
#include <set>
+using namespace Tasking;
using namespace Utils;
using namespace VcsBase;
@@ -229,6 +230,7 @@ public:
QString currentSha;
QDateTime currentDateTime;
QStringList obsoleteLocalBranches;
+ std::unique_ptr<TaskTree> refreshTask;
bool oldBranchesIncluded = false;
struct OldEntry
@@ -399,50 +401,82 @@ void BranchModel::clear()
d->obsoleteLocalBranches.clear();
}
-bool BranchModel::refresh(const FilePath &workingDirectory, QString *errorMessage)
+void BranchModel::refresh(const FilePath &workingDirectory, ShowError showError)
{
+ if (d->refreshTask) {
+ endResetModel(); // for the running task tree.
+ d->refreshTask.reset(); // old running tree is reset, no handlers are being called
+ }
beginResetModel();
clear();
if (workingDirectory.isEmpty()) {
endResetModel();
- return true;
+ return;
}
- d->currentSha = d->client->synchronousTopRevision(workingDirectory, &d->currentDateTime);
- QStringList args = {"--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
- "%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)",
- "refs/heads/**",
- "refs/remotes/**"};
- if (d->client->settings().showTags.value())
- args << "refs/tags/**";
- QString output;
- if (!d->client->synchronousForEachRefCmd(workingDirectory, args, &output, errorMessage)) {
- endResetModel();
- return false;
- }
+ const ProcessTask topRevisionProc =
+ d->client->topRevision(workingDirectory,
+ [=](const QString &ref, const QDateTime &dateTime) {
+ d->currentSha = ref;
+ d->currentDateTime = dateTime;
+ });
+
+ const auto setupForEachRef = [=](Process &process) {
+ d->workingDirectory = workingDirectory;
+ QStringList args = {"for-each-ref",
+ "--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
+ "%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)",
+ "refs/heads/**",
+ "refs/remotes/**"};
+ if (settings().showTags())
+ args << "refs/tags/**";
+ d->client->setupCommand(process, workingDirectory, args);
+ };
- d->workingDirectory = workingDirectory;
- const QStringList lines = output.split('\n');
- for (const QString &l : lines)
- d->parseOutputLine(l);
- d->flushOldEntries();
+ const auto forEachRefDone = [=](const Process &process) {
+ const QString output = process.stdOut();
+ const QStringList lines = output.split('\n');
+ for (const QString &l : lines)
+ d->parseOutputLine(l);
+ d->flushOldEntries();
+
+ d->updateAllUpstreamStatus(d->rootNode->children.at(LocalBranches));
+ if (d->currentBranch) {
+ if (d->currentBranch->isLocal())
+ d->currentBranch = nullptr;
+ setCurrentBranch();
+ }
+ if (!d->currentBranch) {
+ BranchNode *local = d->rootNode->children.at(LocalBranches);
+ d->currentBranch = d->headNode = new BranchNode(
+ Tr::tr("Detached HEAD"), "HEAD", {}, d->currentDateTime);
+ local->prepend(d->headNode);
+ }
+ };
- d->updateAllUpstreamStatus(d->rootNode->children.at(LocalBranches));
- if (d->currentBranch) {
- if (d->currentBranch->isLocal())
- d->currentBranch = nullptr;
- setCurrentBranch();
- }
- if (!d->currentBranch) {
- BranchNode *local = d->rootNode->children.at(LocalBranches);
- d->currentBranch = d->headNode = new BranchNode(Tr::tr("Detached HEAD"), "HEAD", QString(),
- d->currentDateTime);
- local->prepend(d->headNode);
- }
+ const auto forEachRefError = [=](const Process &process) {
+ if (showError == ShowError::No)
+ return;
+ const QString message = Tr::tr("Cannot run \"%1\" in \"%2\": %3")
+ .arg("git for-each-ref")
+ .arg(workingDirectory.toUserOutput())
+ .arg(process.cleanedStdErr());
+ VcsBase::VcsOutputWindow::appendError(message);
+ };
- endResetModel();
+ const auto finalize = [this] {
+ endResetModel();
+ d->refreshTask.release()->deleteLater();
+ };
- return true;
+ const Group root {
+ topRevisionProc,
+ ProcessTask(setupForEachRef, forEachRefDone, forEachRefError),
+ OnGroupDone(finalize),
+ OnGroupError(finalize)
+ };
+ d->refreshTask.reset(new TaskTree(root));
+ d->refreshTask->start();
}
void BranchModel::setCurrentBranch()
@@ -469,7 +503,7 @@ void BranchModel::renameBranch(const QString &oldName, const QString &newName)
&output, &errorMessage))
VcsOutputWindow::appendError(errorMessage);
else
- refresh(d->workingDirectory, &errorMessage);
+ refresh(d->workingDirectory);
}
void BranchModel::renameTag(const QString &oldName, const QString &newName)
@@ -482,7 +516,7 @@ void BranchModel::renameTag(const QString &oldName, const QString &newName)
&output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage);
} else {
- refresh(d->workingDirectory, &errorMessage);
+ refresh(d->workingDirectory);
}
}
@@ -771,7 +805,7 @@ void BranchModel::Private::parseOutputLine(const QString &line, bool force)
const qint64 age = dateTime.daysTo(QDateTime::currentDateTime());
isOld = age > Constants::OBSOLETE_COMMIT_AGE_IN_DAYS;
}
- const bool showTags = client->settings().showTags.value();
+ const bool showTags = settings().showTags();
// insert node into tree:
QStringList nameParts = fullName.split('/');
@@ -885,12 +919,12 @@ void BranchModel::updateUpstreamStatus(BranchNode *node)
if (node->tracking.isEmpty())
return;
- QtcProcess *process = new QtcProcess(node);
+ Process *process = new Process(node);
process->setEnvironment(d->client->processEnvironment());
process->setCommand({d->client->vcsBinary(), {"rev-list", "--no-color", "--left-right",
"--count", node->fullRef() + "..." + node->tracking}});
process->setWorkingDirectory(d->workingDirectory);
- connect(process, &QtcProcess::done, this, [this, process, node] {
+ connect(process, &Process::done, this, [this, process, node] {
process->deleteLater();
if (process->result() != ProcessResult::FinishedWithSuccess)
return;
diff --git a/src/plugins/git/branchmodel.h b/src/plugins/git/branchmodel.h
index 580af61298..de126e9a5e 100644
--- a/src/plugins/git/branchmodel.h
+++ b/src/plugins/git/branchmodel.h
@@ -34,7 +34,8 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const override;
void clear();
- bool refresh(const Utils::FilePath &workingDirectory, QString *errorMessage);
+ enum class ShowError { No, Yes };
+ void refresh(const Utils::FilePath &workingDirectory, ShowError showError = ShowError::No);
void renameBranch(const QString &oldName, const QString &newName);
void renameTag(const QString &oldName, const QString &newName);
diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp
index 528ec288f9..ff428b1b90 100644
--- a/src/plugins/git/branchview.cpp
+++ b/src/plugins/git/branchview.cpp
@@ -20,6 +20,7 @@
#include <utils/fancylineedit.h>
#include <utils/navigationtreeview.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <vcsbase/vcscommand.h>
@@ -36,6 +37,7 @@
#include <QVBoxLayout>
using namespace Core;
+using namespace Tasking;
using namespace Utils;
using namespace VcsBase;
@@ -113,7 +115,7 @@ BranchView::BranchView()
connect(m_includeOldEntriesAction, &QAction::toggled,
this, &BranchView::setIncludeOldEntries);
m_includeTagsAction->setCheckable(true);
- m_includeTagsAction->setChecked(GitClient::settings().showTags.value());
+ m_includeTagsAction->setChecked(settings().showTags.value());
connect(m_includeTagsAction, &QAction::toggled,
this, &BranchView::setIncludeTags);
@@ -160,9 +162,7 @@ void BranchView::refresh(const FilePath &repository, bool force)
if (!isVisible())
return;
- QString errorMessage;
- if (!m_model->refresh(m_repository, &errorMessage))
- VcsBase::VcsOutputWindow::appendError(errorMessage);
+ m_model->refresh(m_repository, BranchModel::ShowError::Yes);
}
void BranchView::refreshCurrentBranch()
@@ -181,7 +181,7 @@ QList<QToolButton *> BranchView::createToolButtons()
filter->setIcon(Utils::Icons::FILTER.icon());
filter->setToolTip(Tr::tr("Filter"));
filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
+ filter->setProperty(StyleHelper::C_NO_ARROW, true);
auto filterMenu = new QMenu(filter);
filterMenu->addAction(m_includeOldEntriesAction);
@@ -190,11 +190,11 @@ QList<QToolButton *> BranchView::createToolButtons()
auto addButton = new QToolButton;
addButton->setDefaultAction(m_addAction);
- addButton->setProperty("noArrow", true);
+ addButton->setProperty(StyleHelper::C_NO_ARROW, true);
auto refreshButton = new QToolButton;
refreshButton->setDefaultAction(m_refreshAction);
- refreshButton->setProperty("noArrow", true);
+ refreshButton->setProperty(StyleHelper::C_NO_ARROW, true);
return {filter, addButton, refreshButton};
}
@@ -225,6 +225,8 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
const bool isTag = m_model->isTag(index);
const bool hasActions = m_model->isLeaf(index);
const bool currentLocal = m_model->isLocal(currentBranch);
+ std::unique_ptr<TaskTree> taskTree;
+ QAction *mergeAction = nullptr;
QMenu contextMenu;
contextMenu.addAction(Tr::tr("&Add..."), this, &BranchView::add);
@@ -268,19 +270,20 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
resetMenu->addAction(Tr::tr("&Mixed"), this, [this] { reset("mixed"); });
resetMenu->addAction(Tr::tr("&Soft"), this, [this] { reset("soft"); });
contextMenu.addMenu(resetMenu);
- QString mergeTitle;
- if (isFastForwardMerge()) {
- contextMenu.addAction(Tr::tr("&Merge \"%1\" into \"%2\" (Fast-Forward)")
- .arg(indexName, currentName),
- this, [this] { merge(true); });
- mergeTitle = Tr::tr("Merge \"%1\" into \"%2\" (No &Fast-Forward)")
- .arg(indexName, currentName);
- } else {
- mergeTitle = Tr::tr("&Merge \"%1\" into \"%2\"")
- .arg(indexName, currentName);
- }
+ mergeAction = contextMenu.addAction(Tr::tr("&Merge \"%1\" into \"%2\"")
+ .arg(indexName, currentName),
+ this,
+ [this] { merge(false); });
+ taskTree.reset(onFastForwardMerge([&] {
+ auto ffMerge = new QAction(
+ Tr::tr("&Merge \"%1\" into \"%2\" (Fast-Forward)").arg(indexName, currentName));
+ connect(ffMerge, &QAction::triggered, this, [this] { merge(true); });
+ contextMenu.insertAction(mergeAction, ffMerge);
+ mergeAction->setText(Tr::tr("Merge \"%1\" into \"%2\" (No &Fast-Forward)")
+ .arg(indexName, currentName));
+ }));
+ connect(mergeAction, &QObject::destroyed, taskTree.get(), &TaskTree::stop);
- contextMenu.addAction(mergeTitle, this, [this] { merge(false); });
contextMenu.addAction(Tr::tr("&Rebase \"%1\" on \"%2\"")
.arg(currentName, indexName),
this, &BranchView::rebase);
@@ -316,7 +319,7 @@ void BranchView::setIncludeOldEntries(bool filter)
void BranchView::setIncludeTags(bool includeTags)
{
- GitClient::settings().showTags.setValue(includeTags);
+ settings().showTags.setValue(includeTags);
refreshCurrentRepository();
}
@@ -523,13 +526,48 @@ bool BranchView::reset(const QByteArray &resetType)
return false;
}
-bool BranchView::isFastForwardMerge()
+TaskTree *BranchView::onFastForwardMerge(const std::function<void()> &callback)
{
const QModelIndex selected = selectedIndex();
QTC_CHECK(selected != m_model->currentBranch());
const QString branch = m_model->fullName(selected, true);
- return GitClient::instance()->isFastForwardMerge(m_repository, branch);
+
+ struct FastForwardStorage
+ {
+ QString mergeBase;
+ QString topRevision;
+ };
+
+ const TreeStorage<FastForwardStorage> storage;
+
+ GitClient *client = GitClient::instance();
+ const auto setupMergeBase = [=](Process &process) {
+ client->setupCommand(process, m_repository, {"merge-base", "HEAD", branch});
+ };
+ const auto onMergeBaseDone = [storage](const Process &process) {
+ storage->mergeBase = process.cleanedStdOut().trimmed();
+ };
+
+ const ProcessTask topRevisionProc = client->topRevision(
+ m_repository,
+ [storage](const QString &revision, const QDateTime &) {
+ storage->topRevision = revision;
+ });
+
+ const Group root {
+ Storage(storage),
+ parallel,
+ ProcessTask(setupMergeBase, onMergeBaseDone),
+ topRevisionProc,
+ OnGroupDone([storage, callback] {
+ if (storage->mergeBase == storage->topRevision)
+ callback();
+ })
+ };
+ auto taskTree = new TaskTree(root);
+ taskTree->start();
+ return taskTree;
}
bool BranchView::merge(bool allowFastForward)
diff --git a/src/plugins/git/branchview.h b/src/plugins/git/branchview.h
index 9a1a3c1e45..d272a5e219 100644
--- a/src/plugins/git/branchview.h
+++ b/src/plugins/git/branchview.h
@@ -17,6 +17,8 @@ class QToolButton;
class QTreeView;
QT_END_NAMESPACE;
+namespace Tasking { class TaskTree; }
+
namespace Utils {
class ElidingLabel;
class NavigationTreeView;
@@ -54,7 +56,7 @@ private:
bool remove();
bool rename();
bool reset(const QByteArray &resetType);
- bool isFastForwardMerge();
+ Tasking::TaskTree *onFastForwardMerge(const std::function<void()> &callback);
bool merge(bool allowFastForward);
void rebase();
bool cherryPick();
diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp
index aba7be1ad2..8e4a6bdc99 100644
--- a/src/plugins/git/changeselectiondialog.cpp
+++ b/src/plugins/git/changeselectiondialog.cpp
@@ -12,7 +12,7 @@
#include <utils/completinglineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/theme/theme.h>
#include <QCompleter>
@@ -209,12 +209,12 @@ void ChangeSelectionDialog::recalculateCompletion()
return;
GitClient *client = GitClient::instance();
- QtcProcess *process = new QtcProcess(this);
+ Process *process = new Process(this);
process->setEnvironment(client->processEnvironment());
process->setCommand({client->vcsBinary(), {"for-each-ref", "--format=%(refname:short)"}});
process->setWorkingDirectory(workingDir);
process->setUseCtrlCStub(true);
- connect(process, &QtcProcess::done, this, [this, process] {
+ connect(process, &Process::done, this, [this, process] {
if (process->result() == ProcessResult::FinishedWithSuccess)
m_changeModel->setStringList(process->cleanedStdOut().split('\n'));
process->deleteLater();
@@ -238,8 +238,8 @@ void ChangeSelectionDialog::recalculateDetails()
return;
}
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &ChangeSelectionDialog::setDetails);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &ChangeSelectionDialog::setDetails);
m_process->setWorkingDirectory(workingDir);
m_process->setEnvironment(m_gitEnvironment);
m_process->setCommand({m_gitExecutable, {"show", "--decorate", "--stat=80", ref}});
diff --git a/src/plugins/git/changeselectiondialog.h b/src/plugins/git/changeselectiondialog.h
index 69da3b20d4..24bc3155dc 100644
--- a/src/plugins/git/changeselectiondialog.h
+++ b/src/plugins/git/changeselectiondialog.h
@@ -18,7 +18,7 @@ QT_END_NAMESPACE
namespace Utils {
class CompletingLineEdit;
class PathChooser;
-class QtcProcess;
+class Process;
} // Utils
namespace Git::Internal {
@@ -53,7 +53,7 @@ private:
void enableButtons(bool b);
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
Utils::FilePath m_gitExecutable;
Utils::Environment m_gitEnvironment;
ChangeCommand m_command = NoCommand;
diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp
index 8273fe0f09..6457117b32 100644
--- a/src/plugins/git/gerrit/gerritmodel.cpp
+++ b/src/plugins/git/gerrit/gerritmodel.cpp
@@ -10,8 +10,8 @@
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <QApplication>
#include <QDebug>
@@ -227,7 +227,7 @@ private:
void errorTermination(const QString &msg);
- QtcProcess m_process;
+ Process m_process;
QTimer m_timer;
FilePath m_binary;
QByteArray m_output;
@@ -259,15 +259,15 @@ QueryContext::QueryContext(const QString &query,
+ "&o=CURRENT_REVISION&o=DETAILED_LABELS&o=DETAILED_ACCOUNTS";
m_arguments = server.curlArguments() << url;
}
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
const QString text = QString::fromLocal8Bit(m_process.readAllRawStandardError());
VcsOutputWindow::appendError(text);
m_error.append(text);
});
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
m_output.append(m_process.readAllRawStandardOutput());
});
- connect(&m_process, &QtcProcess::done, this, &QueryContext::processDone);
+ connect(&m_process, &Process::done, this, &QueryContext::processDone);
m_process.setEnvironment(Git::Internal::GitClient::instance()->processEnvironment());
m_timer.setInterval(timeOutMS);
@@ -340,7 +340,7 @@ void QueryContext::timeout()
arg(timeOutMS / 1000), QMessageBox::NoButton, parent);
QPushButton *terminateButton = box.addButton(Git::Tr::tr("Terminate"), QMessageBox::YesRole);
box.addButton(Git::Tr::tr("Keep Running"), QMessageBox::NoRole);
- connect(&m_process, &QtcProcess::done, &box, &QDialog::reject);
+ connect(&m_process, &Process::done, &box, &QDialog::reject);
box.exec();
if (m_process.state() != QProcess::Running)
return;
diff --git a/src/plugins/git/gerrit/gerritoptionspage.cpp b/src/plugins/git/gerrit/gerritoptionspage.cpp
index 61aa7047b7..3f225d82eb 100644
--- a/src/plugins/git/gerrit/gerritoptionspage.cpp
+++ b/src/plugins/git/gerrit/gerritoptionspage.cpp
@@ -7,6 +7,8 @@
#include "../gittr.h"
#include <coreplugin/icore.h>
+
+#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <vcsbase/vcsbaseconstants.h>
@@ -16,108 +18,87 @@
#include <QCheckBox>
#include <QFormLayout>
-namespace Gerrit {
-namespace Internal {
+namespace Gerrit::Internal {
-GerritOptionsPage::GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
- QObject *parent)
- : Core::IOptionsPage(parent)
- , m_parameters(p)
+class GerritOptionsWidget : public Core::IOptionsPageWidget
{
- setId("Gerrit");
- setDisplayName(Git::Tr::tr("Gerrit"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
-}
+public:
+ GerritOptionsWidget(const QSharedPointer<GerritParameters> &p,
+ const std::function<void()> &onChanged)
+ : m_parameters(p)
+ {
+ auto hostLineEdit = new QLineEdit(p->server.host);
-GerritOptionsPage::~GerritOptionsPage()
-{
- delete m_widget;
-}
+ auto userLineEdit = new QLineEdit(p->server.user.userName);
-QWidget *GerritOptionsPage::widget()
-{
- if (!m_widget) {
- m_widget = new GerritOptionsWidget;
- m_widget->setParameters(*m_parameters);
- }
- return m_widget;
-}
+ auto sshChooser = new Utils::PathChooser;
+ sshChooser->setFilePath(p->ssh);
+ sshChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ sshChooser->setCommandVersionArguments({"-V"});
+ sshChooser->setHistoryCompleter("Git.SshCommand.History");
-void GerritOptionsPage::apply()
-{
- if (GerritOptionsWidget *w = m_widget.data()) {
- GerritParameters newParameters = w->parameters();
- if (newParameters != *m_parameters) {
- if (m_parameters->ssh == newParameters.ssh)
- newParameters.portFlag = m_parameters->portFlag;
- else
- newParameters.setPortFlagBySshType();
- *m_parameters = newParameters;
- m_parameters->toSettings(Core::ICore::settings());
- emit settingsChanged();
- }
- }
-}
+ auto curlChooser = new Utils::PathChooser;
+ curlChooser->setFilePath(p->curl);
+ curlChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ curlChooser->setCommandVersionArguments({"-V"});
-void GerritOptionsPage::finish()
-{
- delete m_widget;
-}
+ auto portSpinBox = new QSpinBox(this);
+ portSpinBox->setValue(p->server.port);
+ portSpinBox->setRange(1, 65535);
-GerritOptionsWidget::GerritOptionsWidget(QWidget *parent)
- : QWidget(parent)
- , m_hostLineEdit(new QLineEdit(this))
- , m_userLineEdit(new QLineEdit(this))
- , m_sshChooser(new Utils::PathChooser)
- , m_curlChooser(new Utils::PathChooser)
- , m_portSpinBox(new QSpinBox(this))
- , m_httpsCheckBox(new QCheckBox(Git::Tr::tr("HTTPS")))
-{
- auto formLayout = new QFormLayout(this);
- formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
- formLayout->addRow(Git::Tr::tr("&Host:"), m_hostLineEdit);
- formLayout->addRow(Git::Tr::tr("&User:"), m_userLineEdit);
- m_sshChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_sshChooser->setCommandVersionArguments({"-V"});
- m_sshChooser->setHistoryCompleter("Git.SshCommand.History");
- formLayout->addRow(Git::Tr::tr("&ssh:"), m_sshChooser);
- m_curlChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
- m_curlChooser->setCommandVersionArguments({"-V"});
- formLayout->addRow(Git::Tr::tr("cur&l:"), m_curlChooser);
- m_portSpinBox->setMinimum(1);
- m_portSpinBox->setMaximum(65535);
- formLayout->addRow(Git::Tr::tr("SSH &Port:"), m_portSpinBox);
- formLayout->addRow(Git::Tr::tr("P&rotocol:"), m_httpsCheckBox);
- m_httpsCheckBox->setToolTip(Git::Tr::tr(
- "Determines the protocol used to form a URL in case\n"
- "\"canonicalWebUrl\" is not configured in the file\n"
- "\"gerrit.config\"."));
- setTabOrder(m_sshChooser, m_curlChooser);
- setTabOrder(m_curlChooser, m_portSpinBox);
-}
+ auto httpsCheckBox = new QCheckBox(Git::Tr::tr("HTTPS"));
+ httpsCheckBox->setChecked(p->https);
+ httpsCheckBox->setToolTip(Git::Tr::tr(
+ "Determines the protocol used to form a URL in case\n"
+ "\"canonicalWebUrl\" is not configured in the file\n"
+ "\"gerrit.config\"."));
-GerritParameters GerritOptionsWidget::parameters() const
-{
- GerritParameters result;
- result.server = GerritServer(m_hostLineEdit->text().trimmed(),
- static_cast<unsigned short>(m_portSpinBox->value()),
- m_userLineEdit->text().trimmed(),
- GerritServer::Ssh);
- result.ssh = m_sshChooser->filePath();
- result.curl = m_curlChooser->filePath();
- result.https = m_httpsCheckBox->isChecked();
- return result;
-}
+ using namespace Layouting;
+ Form {
+ Git::Tr::tr("&Host:"), hostLineEdit, br,
+ Git::Tr::tr("&User:"), userLineEdit, br,
+ Git::Tr::tr("&ssh:"), sshChooser, br,
+ Git::Tr::tr("cur&l:"), curlChooser, br,
+ Git::Tr::tr("SSH &Port:"), portSpinBox, br,
+ Git::Tr::tr("P&rotocol:"), httpsCheckBox
+ }.attachTo(this);
+
+ setOnApply([this, hostLineEdit, userLineEdit, sshChooser,
+ curlChooser, portSpinBox, httpsCheckBox, onChanged] {
+ GerritParameters newParameters;
+ newParameters.server = GerritServer(hostLineEdit->text().trimmed(),
+ static_cast<unsigned short>(portSpinBox->value()),
+ userLineEdit->text().trimmed(),
+ GerritServer::Ssh);
+ newParameters.ssh = sshChooser->filePath();
+ newParameters.curl = curlChooser->filePath();
+ newParameters.https = httpsCheckBox->isChecked();
+
+ if (newParameters != *m_parameters) {
+ if (m_parameters->ssh == newParameters.ssh)
+ newParameters.portFlag = m_parameters->portFlag;
+ else
+ newParameters.setPortFlagBySshType();
+ *m_parameters = newParameters;
+ m_parameters->toSettings(Core::ICore::settings());
+ emit onChanged();
+ }
+ });
+ }
+
+private:
+ const QSharedPointer<GerritParameters> &m_parameters;
+};
-void GerritOptionsWidget::setParameters(const GerritParameters &p)
+// GerritOptionsPage
+
+GerritOptionsPage::GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
+ const std::function<void()> &onChanged)
{
- m_hostLineEdit->setText(p.server.host);
- m_userLineEdit->setText(p.server.user.userName);
- m_sshChooser->setFilePath(p.ssh);
- m_curlChooser->setFilePath(p.curl);
- m_portSpinBox->setValue(p.server.port);
- m_httpsCheckBox->setChecked(p.https);
+ setId("Gerrit");
+ setDisplayName(Git::Tr::tr("Gerrit"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+ setWidgetCreator([p, onChanged] { return new GerritOptionsWidget(p, onChanged); });
}
-} // namespace Internal
-} // namespace Gerrit
+} // Gerrit::Internal
diff --git a/src/plugins/git/gerrit/gerritoptionspage.h b/src/plugins/git/gerrit/gerritoptionspage.h
index d5b220eb33..96caab0609 100644
--- a/src/plugins/git/gerrit/gerritoptionspage.h
+++ b/src/plugins/git/gerrit/gerritoptionspage.h
@@ -6,57 +6,16 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <QSharedPointer>
-#include <QPointer>
-QT_BEGIN_NAMESPACE
-class QLineEdit;
-class QSpinBox;
-class QCheckBox;
-QT_END_NAMESPACE
-
-namespace Utils { class PathChooser; }
-namespace Gerrit {
-namespace Internal {
+namespace Gerrit::Internal {
class GerritParameters;
-class GerritOptionsWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit GerritOptionsWidget(QWidget *parent = nullptr);
-
- GerritParameters parameters() const;
- void setParameters(const GerritParameters &);
-
-private:
- QLineEdit *m_hostLineEdit;
- QLineEdit *m_userLineEdit;
- Utils::PathChooser *m_sshChooser;
- Utils::PathChooser *m_curlChooser;
- QSpinBox *m_portSpinBox;
- QCheckBox *m_httpsCheckBox;
-};
-
class GerritOptionsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
- GerritOptionsPage(const QSharedPointer<GerritParameters> &p, QObject *parent = nullptr);
- ~GerritOptionsPage() override;
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-signals:
- void settingsChanged();
-
-private:
- const QSharedPointer<GerritParameters> &m_parameters;
- QPointer<GerritOptionsWidget> m_widget;
+ GerritOptionsPage(const QSharedPointer<GerritParameters> &p,
+ const std::function<void()> &onChanged);
};
-} // namespace Internal
-} // namespace Gerrit
+} // Gerrit::Internal
diff --git a/src/plugins/git/gerrit/gerritplugin.cpp b/src/plugins/git/gerrit/gerritplugin.cpp
index 41fa83731f..87ce5bfdac 100644
--- a/src/plugins/git/gerrit/gerritplugin.cpp
+++ b/src/plugins/git/gerrit/gerritplugin.cpp
@@ -22,8 +22,8 @@
#include <coreplugin/vcsmanager.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <vcsbase/vcsoutputwindow.h>
@@ -78,7 +78,7 @@ private:
const FetchMode m_fetchMode;
const Utils::FilePath m_git;
const GerritServer m_server;
- QtcProcess m_process;
+ Process m_process;
};
FetchContext::FetchContext(const QSharedPointer<GerritChange> &change,
@@ -93,11 +93,11 @@ FetchContext::FetchContext(const QSharedPointer<GerritChange> &change,
, m_server(server)
{
m_process.setUseCtrlCStub(true);
- connect(&m_process, &QtcProcess::done, this, &FetchContext::processDone);
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_process, &Process::done, this, &FetchContext::processDone);
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
VcsBase::VcsOutputWindow::append(QString::fromLocal8Bit(m_process.readAllRawStandardError()));
});
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
VcsBase::VcsOutputWindow::append(QString::fromLocal8Bit(m_process.readAllRawStandardOutput()));
});
m_process.setWorkingDirectory(repository);
@@ -152,18 +152,26 @@ void FetchContext::checkout()
GitClient::instance()->checkout(m_repository, "FETCH_HEAD");
}
-GerritPlugin::GerritPlugin(QObject *parent)
- : QObject(parent)
- , m_parameters(new GerritParameters)
+GerritPlugin::GerritPlugin()
+ : m_parameters(new GerritParameters)
, m_server(new GerritServer)
{
+ m_parameters->fromSettings(ICore::settings());
+
+ m_gerritOptionsPage = new GerritOptionsPage(m_parameters,
+ [this] {
+ if (m_dialog)
+ m_dialog->scheduleUpdateRemotes();
+ });
}
-GerritPlugin::~GerritPlugin() = default;
+GerritPlugin::~GerritPlugin()
+{
+ delete m_gerritOptionsPage;
+}
-void GerritPlugin::initialize(ActionContainer *ac)
+void GerritPlugin::addToMenu(ActionContainer *ac)
{
- m_parameters->fromSettings(ICore::settings());
QAction *openViewAction = new QAction(Git::Tr::tr("Gerrit..."), this);
@@ -178,13 +186,6 @@ void GerritPlugin::initialize(ActionContainer *ac)
ActionManager::registerAction(pushAction, Constants::GERRIT_PUSH);
connect(pushAction, &QAction::triggered, this, [this] { push(); });
ac->addAction(m_pushToGerritCommand);
-
- auto options = new GerritOptionsPage(m_parameters, this);
- connect(options, &GerritOptionsPage::settingsChanged,
- this, [this] {
- if (m_dialog)
- m_dialog->scheduleUpdateRemotes();
- });
}
void GerritPlugin::updateActions(const VcsBase::VcsBasePluginState &state)
diff --git a/src/plugins/git/gerrit/gerritplugin.h b/src/plugins/git/gerrit/gerritplugin.h
index 77d96d9f74..b7e0de29d8 100644
--- a/src/plugins/git/gerrit/gerritplugin.h
+++ b/src/plugins/git/gerrit/gerritplugin.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/fileutils.h>
+#include <utils/filepath.h>
#include <QObject>
#include <QPointer>
@@ -25,15 +25,17 @@ class GerritChange;
class GerritDialog;
class GerritParameters;
class GerritServer;
+class GerritOptionsPage;
class GerritPlugin : public QObject
{
Q_OBJECT
+
public:
- explicit GerritPlugin(QObject *parent = nullptr);
+ GerritPlugin();
~GerritPlugin() override;
- void initialize(Core::ActionContainer *ac);
+ void addToMenu(Core::ActionContainer *ac);
static Utils::FilePath gitBinDirectory();
static QString branch(const Utils::FilePath &repository);
@@ -59,6 +61,7 @@ private:
Core::Command *m_gerritCommand = nullptr;
Core::Command *m_pushToGerritCommand = nullptr;
QString m_reviewers;
+ GerritOptionsPage *m_gerritOptionsPage = nullptr;
};
} // namespace Internal
diff --git a/src/plugins/git/gerrit/gerritpushdialog.cpp b/src/plugins/git/gerrit/gerritpushdialog.cpp
index 057d4cd644..87961c5945 100644
--- a/src/plugins/git/gerrit/gerritpushdialog.cpp
+++ b/src/plugins/git/gerrit/gerritpushdialog.cpp
@@ -125,7 +125,7 @@ GerritPushDialog::GerritPushDialog(const Utils::FilePath &workingDir, const QStr
"Unchecked - Remove mark.\n"
"Partially checked - Do not change current state."));
m_commitView->setToolTip(::Git::Tr::tr(
- "Pushes the selected commit and all dependent commits."));
+ "Pushes the selected commit and all commits it depends on."));
m_reviewersLineEdit->setToolTip(::Git::Tr::tr("Comma-separated list of reviewers.\n"
"\n"
"Reviewers can be specified by nickname or email address. Spaces not allowed.\n"
@@ -136,7 +136,7 @@ GerritPushDialog::GerritPushDialog(const Utils::FilePath &workingDir, const QStr
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
::Git::Tr::tr("Push:"), workingDir.toUserOutput(), m_localBranchComboBox, br,
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index e419449fe0..bda248be63 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -19,7 +19,7 @@
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/commandline.h>
@@ -27,8 +27,8 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <utils/theme/theme.h>
@@ -60,7 +60,7 @@
const char GIT_DIRECTORY[] = ".git";
const char HEAD[] = "HEAD";
const char CHERRY_PICK_HEAD[] = "CHERRY_PICK_HEAD";
-const char BRANCHES_PREFIX[] = "Branches: ";
+[[maybe_unused]] const char BRANCHES_PREFIX[] = "Branches: ";
const char stashNamePrefix[] = "stash@{";
const char noColorOption[] = "--no-color";
const char colorOption[] = "--color=always";
@@ -76,6 +76,7 @@ const char showFormatC[] =
using namespace Core;
using namespace DiffEditor;
+using namespace Tasking;
using namespace Utils;
using namespace VcsBase;
@@ -92,7 +93,7 @@ static QString branchesDisplay(const QString &prefix, QStringList *branches, boo
if (*first)
*first = false;
else
- output += QString(sizeof(BRANCHES_PREFIX) - 1, ' '); // Align
+ output += QString(sizeof(BRANCHES_PREFIX) - 1 /* the \0 */, ' '); // Align
output += prefix + ": ";
// If there are more than 'limit' branches, list limit/2 (first limit/4 and last limit/4)
if (count > limit) {
@@ -165,18 +166,18 @@ GitDiffEditorController::GitDiffEditorController(IDocument *document,
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupDiff = [=](QtcProcess &process) {
+ const auto setupDiff = [=](Process &process) {
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), {}));
setupCommand(process, {addConfigurationArguments(diffArgs(leftCommit, rightCommit, extraArgs))});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
Storage(diffInputStorage),
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
};
setReloadRecipe(root);
@@ -231,7 +232,7 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
const TreeStorage<DiffStorage> storage;
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupStaged = [this, stagedFiles](QtcProcess &process) {
+ const auto setupStaged = [this, stagedFiles](Process &process) {
if (stagedFiles.isEmpty())
return TaskAction::StopWithError;
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), stagedFiles));
@@ -240,11 +241,11 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
return TaskAction::Continue;
};
- const auto onStagedDone = [storage](const QtcProcess &process) {
+ const auto onStagedDone = [storage](const Process &process) {
storage->m_stagedOutput = process.cleanedStdOut();
};
- const auto setupUnstaged = [this, unstagedFiles](QtcProcess &process) {
+ const auto setupUnstaged = [this, unstagedFiles](Process &process) {
if (unstagedFiles.isEmpty())
return TaskAction::StopWithError;
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), unstagedFiles));
@@ -253,12 +254,12 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
return TaskAction::Continue;
};
- const auto onUnstagedDone = [storage](const QtcProcess &process) {
+ const auto onUnstagedDone = [storage](const Process &process) {
storage->m_unstagedOutput = process.cleanedStdOut();
};
const auto onStagingDone = [storage, diffInputStorage] {
- *diffInputStorage.activeStorage() = storage->m_stagedOutput + storage->m_unstagedOutput;
+ *diffInputStorage = storage->m_stagedOutput + storage->m_unstagedOutput;
};
const Group root {
@@ -267,8 +268,8 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
Group {
parallel,
continueOnDone,
- Process(setupStaged, onStagedDone),
- Process(setupUnstaged, onUnstagedDone),
+ ProcessTask(setupStaged, onStagedDone),
+ ProcessTask(setupUnstaged, onUnstagedDone),
OnGroupDone(onStagingDone)
},
postProcessTask()
@@ -321,13 +322,13 @@ ShowController::ShowController(IDocument *document, const QString &id)
setDescription(desc);
};
- const auto setupDescription = [this, id](QtcProcess &process) {
+ const auto setupDescription = [this, id](Process &process) {
process.setCodec(m_instance->encoding(GitClient::EncodingCommit, workingDirectory()));
setupCommand(process, {"show", "-s", noColorOption, showFormatC, id});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
setDescription(Tr::tr("Waiting for data..."));
};
- const auto onDescriptionDone = [this, storage, updateDescription](const QtcProcess &process) {
+ const auto onDescriptionDone = [this, storage, updateDescription](const Process &process) {
ReloadStorage *data = storage.activeStorage();
const QString output = process.cleanedStdOut();
data->m_postProcessDescription = output.startsWith("commit ");
@@ -348,12 +349,12 @@ ShowController::ShowController(IDocument *document, const QString &id)
return TaskAction::Continue;
};
- const auto setupBranches = [this, storage](QtcProcess &process) {
+ const auto setupBranches = [this, storage](Process &process) {
storage->m_branches = busyMessage;
setupCommand(process, {"branch", noColorOption, "-a", "--contains", storage->m_commit});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onBranchesDone = [storage, updateDescription](const QtcProcess &process) {
+ const auto onBranchesDone = [storage, updateDescription](const Process &process) {
ReloadStorage *data = storage.activeStorage();
data->m_branches.clear();
const QString remotePrefix = "remotes/";
@@ -391,17 +392,17 @@ ShowController::ShowController(IDocument *document, const QString &id)
data->m_branches = data->m_branches.trimmed();
updateDescription(*data);
};
- const auto onBranchesError = [storage, updateDescription](const QtcProcess &) {
+ const auto onBranchesError = [storage, updateDescription](const Process &) {
ReloadStorage *data = storage.activeStorage();
data->m_branches.clear();
updateDescription(*data);
};
- const auto setupPrecedes = [this, storage](QtcProcess &process) {
+ const auto setupPrecedes = [this, storage](Process &process) {
storage->m_precedes = busyMessage;
setupCommand(process, {"describe", "--contains", storage->m_commit});
};
- const auto onPrecedesDone = [storage, updateDescription](const QtcProcess &process) {
+ const auto onPrecedesDone = [storage, updateDescription](const Process &process) {
ReloadStorage *data = storage.activeStorage();
data->m_precedes = process.cleanedStdOut().trimmed();
const int tilde = data->m_precedes.indexOf('~');
@@ -411,7 +412,7 @@ ShowController::ShowController(IDocument *document, const QString &id)
data->m_precedes.chop(2);
updateDescription(*data);
};
- const auto onPrecedesError = [storage, updateDescription](const QtcProcess &) {
+ const auto onPrecedesError = [storage, updateDescription](const Process &) {
ReloadStorage *data = storage.activeStorage();
data->m_precedes.clear();
updateDescription(*data);
@@ -427,10 +428,10 @@ ShowController::ShowController(IDocument *document, const QString &id)
data->m_follows = {busyMessage};
data->m_follows.resize(parents.size());
- const auto setupFollow = [this](QtcProcess &process, const QString &parent) {
+ const auto setupFollow = [this](Process &process, const QString &parent) {
setupCommand(process, {"describe", "--tags", "--abbrev=0", parent});
};
- const auto onFollowDone = [data, updateDescription](const QtcProcess &process, int index) {
+ const auto onFollowDone = [data, updateDescription](const Process &process, int index) {
data->m_follows[index] = process.cleanedStdOut().trimmed();
updateDescription(*data);
};
@@ -442,20 +443,20 @@ ShowController::ShowController(IDocument *document, const QString &id)
using namespace std::placeholders;
QList<TaskItem> tasks {parallel, continueOnDone, OnGroupError(onFollowsError)};
for (int i = 0, total = parents.size(); i < total; ++i) {
- tasks.append(Process(std::bind(setupFollow, _1, parents.at(i)),
+ tasks.append(ProcessTask(std::bind(setupFollow, _1, parents.at(i)),
std::bind(onFollowDone, _1, i)));
}
taskTree.setupRoot(tasks);
};
- const auto setupDiff = [this, id](QtcProcess &process) {
+ const auto setupDiff = [this, id](Process &process) {
setupCommand(process, addConfigurationArguments(
{"show", "--format=format:", // omit header, already generated
noColorOption, decorateOption, id}));
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
@@ -465,18 +466,18 @@ ShowController::ShowController(IDocument *document, const QString &id)
OnGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }),
Group {
optional,
- Process(setupDescription, onDescriptionDone),
+ ProcessTask(setupDescription, onDescriptionDone),
Group {
parallel,
optional,
OnGroupSetup(desciptionDetailsSetup),
- Process(setupBranches, onBranchesDone, onBranchesError),
- Process(setupPrecedes, onPrecedesDone, onPrecedesError),
- Tree(setupFollows)
+ ProcessTask(setupBranches, onBranchesDone, onBranchesError),
+ ProcessTask(setupPrecedes, onPrecedesDone, onPrecedesError),
+ TaskTreeTask(setupFollows)
}
},
Group {
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
}
};
@@ -490,16 +491,16 @@ class BaseGitDiffArgumentsWidget : public VcsBaseEditorConfig
Q_OBJECT
public:
- BaseGitDiffArgumentsWidget(GitSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit BaseGitDiffArgumentsWidget(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
m_patienceButton
= addToggleButton("--patience", Tr::tr("Patience"),
Tr::tr("Use the patience algorithm for calculating the differences."));
- mapSetting(m_patienceButton, &settings.diffPatience);
+ mapSetting(m_patienceButton, &settings().diffPatience);
m_ignoreWSButton = addToggleButton("--ignore-space-change", Tr::tr("Ignore Whitespace"),
Tr::tr("Ignore whitespace only changes."));
- mapSetting(m_ignoreWSButton, &settings.ignoreSpaceChangesInDiff);
+ mapSetting(m_ignoreWSButton, &settings().ignoreSpaceChangesInDiff);
}
protected:
@@ -512,15 +513,15 @@ class GitBlameArgumentsWidget : public VcsBaseEditorConfig
Q_OBJECT
public:
- GitBlameArgumentsWidget(GitSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit GitBlameArgumentsWidget(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton(QString(), Tr::tr("Omit Date"),
Tr::tr("Hide the date of a change from the output.")),
- &settings.omitAnnotationDate);
+ &settings().omitAnnotationDate);
mapSetting(addToggleButton("-w", Tr::tr("Ignore Whitespace"),
Tr::tr("Ignore whitespace only changes.")),
- &settings.ignoreSpaceChangesInBlame);
+ &settings().ignoreSpaceChangesInBlame);
const QList<ChoiceItem> logChoices = {
ChoiceItem(Tr::tr("No Move Detection"), ""),
@@ -529,7 +530,7 @@ public:
ChoiceItem(Tr::tr("Detect Moves and Copies Between Files"), "-M -C -C")
};
mapSetting(addChoices(Tr::tr("Move detection"), {}, logChoices),
- &settings.blameMoveDetection);
+ &settings().blameMoveDetection);
addReloadButton();
}
@@ -540,13 +541,13 @@ class BaseGitLogArgumentsWidget : public BaseGitDiffArgumentsWidget
Q_OBJECT
public:
- BaseGitLogArgumentsWidget(GitSettings &settings, GitEditorWidget *editor) :
- BaseGitDiffArgumentsWidget(settings, editor->toolBar())
+ BaseGitLogArgumentsWidget(GitEditorWidget *editor)
+ : BaseGitDiffArgumentsWidget(editor->toolBar())
{
QToolBar *toolBar = editor->toolBar();
QAction *diffButton = addToggleButton(patchOption, Tr::tr("Diff"),
Tr::tr("Show difference."));
- mapSetting(diffButton, &settings.logDiff);
+ mapSetting(diffButton, &settings().logDiff);
connect(diffButton, &QAction::toggled, m_patienceButton, &QAction::setVisible);
connect(diffButton, &QAction::toggled, m_ignoreWSButton, &QAction::setVisible);
m_patienceButton->setVisible(diffButton->isChecked());
@@ -581,27 +582,27 @@ class GitLogArgumentsWidget : public BaseGitLogArgumentsWidget
Q_OBJECT
public:
- GitLogArgumentsWidget(GitSettings &settings, bool fileRelated, GitEditorWidget *editor) :
- BaseGitLogArgumentsWidget(settings, editor)
+ GitLogArgumentsWidget(bool fileRelated, GitEditorWidget *editor)
+ : BaseGitLogArgumentsWidget(editor)
{
QAction *firstParentButton =
addToggleButton({"-m", "--first-parent"},
Tr::tr("First Parent"),
Tr::tr("Follow only the first parent on merge commits."));
- mapSetting(firstParentButton, &settings.firstParent);
+ mapSetting(firstParentButton, &settings().firstParent);
QAction *graphButton = addToggleButton(graphArguments(), Tr::tr("Graph"),
Tr::tr("Show textual graph log."));
- mapSetting(graphButton, &settings.graphLog);
+ mapSetting(graphButton, &settings().graphLog);
QAction *colorButton = addToggleButton(QStringList{colorOption},
Tr::tr("Color"), Tr::tr("Use colors in log."));
- mapSetting(colorButton, &settings.colorLog);
+ mapSetting(colorButton, &settings().colorLog);
if (fileRelated) {
QAction *followButton = addToggleButton(
"--follow", Tr::tr("Follow"),
Tr::tr("Show log also for previous names of the file."));
- mapSetting(followButton, &settings.followRenames);
+ mapSetting(followButton, &settings().followRenames);
}
addReloadButton();
@@ -640,14 +641,14 @@ class GitRefLogArgumentsWidget : public BaseGitLogArgumentsWidget
Q_OBJECT
public:
- GitRefLogArgumentsWidget(GitSettings &settings, GitEditorWidget *editor) :
- BaseGitLogArgumentsWidget(settings, editor)
+ explicit GitRefLogArgumentsWidget(GitEditorWidget *editor)
+ : BaseGitLogArgumentsWidget(editor)
{
QAction *showDateButton =
addToggleButton("--date=iso",
Tr::tr("Show Date"),
Tr::tr("Show date instead of sequence."));
- mapSetting(showDateButton, &settings.refLogShowDate);
+ mapSetting(showDateButton, &settings().refLogShowDate);
addReloadButton();
}
@@ -735,8 +736,8 @@ static inline void msgCannotRun(const QStringList &args, const FilePath &working
// ---------------- GitClient
-GitClient::GitClient(GitSettings *settings)
- : VcsBase::VcsBaseClientImpl(settings)
+GitClient::GitClient()
+ : VcsBase::VcsBaseClientImpl(&Internal::settings())
{
m_instance = this;
m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2")
@@ -751,7 +752,7 @@ GitClient *GitClient::instance()
GitSettings &GitClient::settings()
{
- return static_cast<GitSettings &>(m_instance->VcsBaseClientImpl::settings());
+ return Internal::settings();
}
FilePath GitClient::findRepositoryForDirectory(const FilePath &directory) const
@@ -1074,7 +1075,7 @@ void GitClient::log(const FilePath &workingDirectory, const QString &fileName,
encoding(EncodingLogOutput), "logTitle", msgArg));
VcsBaseEditorConfig *argWidget = editor->editorConfig();
if (!argWidget) {
- argWidget = new GitLogArgumentsWidget(settings(), !fileName.isEmpty(), editor);
+ argWidget = new GitLogArgumentsWidget(!fileName.isEmpty(), editor);
argWidget->setBaseArguments(args);
connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, this,
[=] { this->log(workingDir, fileName, enableAnnotationContextMenu, args); });
@@ -1084,7 +1085,7 @@ void GitClient::log(const FilePath &workingDirectory, const QString &fileName,
editor->setWorkingDirectory(workingDir);
QStringList arguments = {"log", decorateOption};
- int logCount = settings().logCount.value();
+ int logCount = settings().logCount();
if (logCount > 0)
arguments << "-n" << QString::number(logCount);
@@ -1130,7 +1131,7 @@ void GitClient::reflog(const FilePath &workingDirectory, const QString &ref)
"reflogRepository", workingDir.toString()));
VcsBaseEditorConfig *argWidget = editor->editorConfig();
if (!argWidget) {
- argWidget = new GitRefLogArgumentsWidget(settings(), editor);
+ argWidget = new GitRefLogArgumentsWidget(editor);
if (!ref.isEmpty())
argWidget->setBaseArguments({ref});
connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, this,
@@ -1141,7 +1142,7 @@ void GitClient::reflog(const FilePath &workingDirectory, const QString &ref)
QStringList arguments = {"reflog", noColorOption, decorateOption};
arguments << argWidget->arguments();
- int logCount = settings().logCount.value();
+ int logCount = settings().logCount();
if (logCount > 0)
arguments << "-n" << QString::number(logCount);
@@ -1242,7 +1243,7 @@ void GitClient::annotate(const Utils::FilePath &workingDir, const QString &file,
encoding(EncodingSource, sourceFile), "blameFileName", id);
VcsBaseEditorConfig *argWidget = editor->editorConfig();
if (!argWidget) {
- argWidget = new GitBlameArgumentsWidget(settings(), editor->toolBar());
+ argWidget = new GitBlameArgumentsWidget(editor->toolBar());
argWidget->setBaseArguments(extraOptions);
connect(argWidget, &VcsBaseEditorConfig::commandExecutionRequested, this, [=] {
const int line = VcsBaseEditor::lineNumberOfCurrentEditor();
@@ -1292,14 +1293,16 @@ QStringList GitClient::setupCheckoutArguments(const FilePath &workingDirectory,
if (localBranches.contains(ref))
return arguments;
- if (Utils::CheckableMessageBox::doNotAskAgainQuestion(
- ICore::dialogParent() /*parent*/,
- Tr::tr("Create Local Branch") /*title*/,
- Tr::tr("Would you like to create a local branch?") /*message*/,
- ICore::settings(), "Git.CreateLocalBranchOnCheckout" /*setting*/,
- QDialogButtonBox::Yes | QDialogButtonBox::No /*buttons*/,
- QDialogButtonBox::No /*default button*/,
- QDialogButtonBox::No /*button to save*/) != QDialogButtonBox::Yes) {
+ if (Utils::CheckableMessageBox::question(
+ ICore::dialogParent() /*parent*/,
+ Tr::tr("Create Local Branch") /*title*/,
+ Tr::tr("Would you like to create a local branch?") /*message*/,
+ ICore::settings(),
+ "Git.CreateLocalBranchOnCheckout" /*setting*/,
+ QMessageBox::Yes | QMessageBox::No /*buttons*/,
+ QMessageBox::No /*default button*/,
+ QMessageBox::No /*button to save*/)
+ != QMessageBox::Yes) {
return arguments;
}
@@ -1730,19 +1733,25 @@ bool GitClient::synchronousRevParseCmd(const FilePath &workingDirectory, const Q
}
// Retrieve head revision
-QString GitClient::synchronousTopRevision(const FilePath &workingDirectory, QDateTime *dateTime)
+ProcessTask GitClient::topRevision(const FilePath &workingDirectory,
+ const std::function<void(const QString &, const QDateTime &)> &callback)
{
- const QStringList arguments = {"show", "-s", "--pretty=format:%H:%ct", HEAD};
- const CommandResult result = vcsSynchronousExec(workingDirectory, arguments, RunFlags::NoOutput);
- if (result.result() != ProcessResult::FinishedWithSuccess)
- return QString();
- const QStringList output = result.cleanedStdOut().trimmed().split(':');
- if (dateTime && output.size() > 1) {
- bool ok = false;
- const qint64 timeT = output.at(1).toLongLong(&ok);
- *dateTime = ok ? QDateTime::fromSecsSinceEpoch(timeT) : QDateTime();
- }
- return output.first();
+ const auto setupProcess = [=](Process &process) {
+ setupCommand(process, workingDirectory, {"show", "-s", "--pretty=format:%H:%ct", HEAD});
+ };
+ const auto onProcessDone = [=](const Process &process) {
+ const QStringList output = process.cleanedStdOut().trimmed().split(':');
+ QDateTime dateTime;
+ if (output.size() > 1) {
+ bool ok = false;
+ const qint64 timeT = output.at(1).toLongLong(&ok);
+ if (ok)
+ dateTime = QDateTime::fromSecsSinceEpoch(timeT);
+ }
+ callback(output.first(), dateTime);
+ };
+
+ return ProcessTask(setupProcess, onProcessDone);
}
bool GitClient::isRemoteCommit(const FilePath &workingDirectory, const QString &commit)
@@ -1752,13 +1761,6 @@ bool GitClient::isRemoteCommit(const FilePath &workingDirectory, const QString &
return !result.rawStdOut().isEmpty();
}
-bool GitClient::isFastForwardMerge(const FilePath &workingDirectory, const QString &branch)
-{
- const CommandResult result = vcsSynchronousExec(workingDirectory,
- {"merge-base", HEAD, branch}, RunFlags::NoOutput);
- return result.cleanedStdOut().trimmed() == synchronousTopRevision(workingDirectory);
-}
-
// Format an entry in a one-liner for selection list using git log.
QString GitClient::synchronousShortDescription(const FilePath &workingDirectory, const QString &revision,
const QString &format) const
@@ -2422,7 +2424,7 @@ void GitClient::launchRepositoryBrowser(const FilePath &workingDirectory) const
{
const FilePath repBrowserBinary = settings().repositoryBrowserCmd.filePath();
if (!repBrowserBinary.isEmpty())
- QtcProcess::startDetached({repBrowserBinary, {workingDirectory.toString()}}, workingDirectory);
+ Process::startDetached({repBrowserBinary, {workingDirectory.toString()}}, workingDirectory);
}
static FilePath gitBinDir(const GitClient::GitKLaunchTrial trial, const FilePath &parentDir)
@@ -2466,21 +2468,21 @@ void GitClient::tryLaunchingGitK(const Environment &env,
arguments << "--" << fileName;
VcsOutputWindow::appendCommand(workingDirectory, {binary, arguments});
- // This should always use QtcProcess::startDetached (as not to kill
+ // This should always use Process::startDetached (as not to kill
// the child), but that does not have an environment parameter.
if (!settings().path.value().isEmpty()) {
- auto process = new QtcProcess(const_cast<GitClient*>(this));
+ auto process = new Process(const_cast<GitClient*>(this));
process->setWorkingDirectory(workingDirectory);
process->setEnvironment(env);
process->setCommand({binary, arguments});
- connect(process, &QtcProcess::done, this, [=] {
+ connect(process, &Process::done, this, [=] {
if (process->result() == ProcessResult::StartFailed)
handleGitKFailedToStart(env, workingDirectory, fileName, trial, gitBinDirectory);
process->deleteLater();
});
process->start();
} else {
- if (!QtcProcess::startDetached({binary, arguments}, workingDirectory))
+ if (!Process::startDetached({binary, arguments}, workingDirectory))
handleGitKFailedToStart(env, workingDirectory, fileName, trial, gitBinDirectory);
}
}
@@ -2517,7 +2519,7 @@ bool GitClient::launchGitGui(const FilePath &workingDirectory) {
if (gitBinary.isEmpty()) {
success = false;
} else {
- success = QtcProcess::startDetached({gitBinary, {"gui"}}, workingDirectory);
+ success = Process::startDetached({gitBinary, {"gui"}}, workingDirectory);
}
if (!success)
@@ -2562,7 +2564,7 @@ bool GitClient::launchGitBash(const FilePath &workingDirectory)
success = false;
} else {
const FilePath gitBash = git.absolutePath().parentDir() / "git-bash.exe";
- success = QtcProcess::startDetached({gitBash, {}}, workingDirectory);
+ success = Process::startDetached({gitBash, {}}, workingDirectory);
}
if (!success)
@@ -2574,7 +2576,7 @@ bool GitClient::launchGitBash(const FilePath &workingDirectory)
FilePath GitClient::vcsBinary() const
{
bool ok;
- Utils::FilePath binary = static_cast<GitSettings &>(settings()).gitExecutable(&ok);
+ Utils::FilePath binary = settings().gitExecutable(&ok);
if (!ok)
return Utils::FilePath();
return binary;
@@ -3120,7 +3122,7 @@ void GitClient::synchronousSubversionFetch(const FilePath &workingDirectory) con
void GitClient::subversionLog(const FilePath &workingDirectory) const
{
QStringList arguments = {"svn", "log"};
- int logCount = settings().logCount.value();
+ int logCount = settings().logCount();
if (logCount > 0)
arguments << ("--limit=" + QString::number(logCount));
@@ -3464,8 +3466,8 @@ QFuture<unsigned> GitClient::gitVersion() const
const FilePath newGitBinary = vcsBinary();
const bool needToRunGit = m_gitVersionForBinary != newGitBinary && !newGitBinary.isEmpty();
if (needToRunGit) {
- auto proc = new QtcProcess(const_cast<GitClient *>(this));
- connect(proc, &QtcProcess::done, this, [this, proc, fi, newGitBinary]() mutable {
+ auto proc = new Process(const_cast<GitClient *>(this));
+ connect(proc, &Process::done, this, [this, proc, fi, newGitBinary]() mutable {
if (proc->result() == ProcessResult::FinishedWithSuccess) {
m_cachedGitVersion = parseGitVersion(proc->cleanedStdOut());
m_gitVersionForBinary = newGitBinary;
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 2dfd6ddaaa..88c2f3870f 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -13,6 +13,7 @@
#include <utils/fileutils.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <QObject>
#include <QString>
@@ -114,9 +115,8 @@ public:
PushAction m_pushAction = NoPush;
};
- explicit GitClient(GitSettings *settings);
+ GitClient();
static GitClient *instance();
- static GitSettings &settings();
Utils::FilePath vcsBinary() const override;
QFuture<unsigned> gitVersion() const;
@@ -241,9 +241,9 @@ public:
QString synchronousTopic(const Utils::FilePath &workingDirectory) const;
bool synchronousRevParseCmd(const Utils::FilePath &workingDirectory, const QString &ref,
QString *output, QString *errorMessage = nullptr) const;
- QString synchronousTopRevision(const Utils::FilePath &workingDirectory, QDateTime *dateTime = nullptr);
+ Tasking::ProcessTask topRevision(const Utils::FilePath &workingDirectory,
+ const std::function<void(const QString &, const QDateTime &)> &callback);
bool isRemoteCommit(const Utils::FilePath &workingDirectory, const QString &commit);
- bool isFastForwardMerge(const Utils::FilePath &workingDirectory, const QString &branch);
void fetch(const Utils::FilePath &workingDirectory, const QString &remote);
void pull(const Utils::FilePath &workingDirectory, bool rebase);
@@ -349,6 +349,8 @@ public:
QTextCodec *encoding(EncodingType encodingType, const Utils::FilePath &source = {}) const;
private:
+ static GitSettings &settings();
+
void finishSubmoduleUpdate();
void chunkActionsRequested(DiffEditor::DiffEditorController *controller,
QMenu *menu, int fileIndex, int chunkIndex,
@@ -400,6 +402,7 @@ private:
QString m_diffCommit;
Utils::FilePaths m_updatedSubmodules;
bool m_disableEditor = false;
+ // The synchronizer has cancelOnWait set to true by default.
Utils::FutureSynchronizer m_synchronizer; // for commit updates
};
diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp
index 7f73842cb0..c7078dc2ba 100644
--- a/src/plugins/git/giteditor.cpp
+++ b/src/plugins/git/giteditor.cpp
@@ -132,7 +132,7 @@ static QString sanitizeBlameOutput(const QString &b)
if (b.isEmpty())
return b;
- const bool omitDate = GitClient::instance()->settings().omitAnnotationDate.value();
+ const bool omitDate = settings().omitAnnotationDate.value();
const QChar space(' ');
const int parenPos = b.indexOf(')');
if (parenPos == -1)
diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp
index 6baa635080..2161cbbf68 100644
--- a/src/plugins/git/gitgrep.cpp
+++ b/src/plugins/git/gitgrep.cpp
@@ -13,12 +13,12 @@
#include <vcsbase/vcsbaseconstants.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/fancylineedit.h>
#include <utils/filesearch.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QCheckBox>
#include <QFuture>
@@ -45,7 +45,7 @@ public:
class GitGrepRunner
{
- using FutureInterfaceType = QFutureInterface<FileSearchResultList>;
+ using PromiseType = QPromise<SearchResultItems>;
public:
GitGrepRunner(const TextEditor::FileFindParameters &parameters)
@@ -67,23 +67,23 @@ public:
QStringList regexpCapturedTexts;
};
- void processLine(const QString &line, FileSearchResultList *resultList) const
+ void processLine(const QString &line, SearchResultItems *resultList) const
{
if (line.isEmpty())
return;
static const QLatin1String boldRed("\x1b[1;31m");
static const QLatin1String resetColor("\x1b[m");
- FileSearchResult single;
+ SearchResultItem result;
const int lineSeparator = line.indexOf(QChar::Null);
QString filePath = line.left(lineSeparator);
if (!m_ref.isEmpty() && filePath.startsWith(m_ref))
filePath.remove(0, m_ref.length());
- single.fileName = m_directory.pathAppended(filePath);
+ result.setFilePath(m_directory.pathAppended(filePath));
const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1);
- single.lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
+ const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
QString text = line.mid(textSeparator + 1);
QRegularExpression regexp;
- QVector<Match> matches;
+ QList<Match> matches;
if (m_parameters.flags & FindRegularExpression) {
const QRegularExpression::PatternOptions patternOptions =
(m_parameters.flags & FindCaseSensitively)
@@ -106,28 +106,27 @@ public:
matches.append(match);
text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size());
}
- single.matchingLine = text;
+ result.setDisplayText(text);
for (const auto &match : std::as_const(matches)) {
- single.matchStart = match.matchStart;
- single.matchLength = match.matchLength;
- single.regexpCapturedTexts = match.regexpCapturedTexts;
- resultList->append(single);
+ result.setMainRange(lineNumber, match.matchStart, match.matchLength);
+ result.setUserData(match.regexpCapturedTexts);
+ resultList->append(result);
}
}
- void read(FutureInterfaceType &fi, const QString &text)
+ void read(PromiseType &fi, const QString &text)
{
- FileSearchResultList resultList;
+ SearchResultItems resultList;
QString t = text;
QTextStream stream(&t);
while (!stream.atEnd() && !fi.isCanceled())
processLine(stream.readLine(), &resultList);
if (!resultList.isEmpty() && !fi.isCanceled())
- fi.reportResult(resultList);
+ fi.addResult(resultList);
}
- void operator()(FutureInterfaceType &fi)
+ void operator()(PromiseType &promise)
{
QStringList arguments = {
"-c", "color.grep.match=bold red",
@@ -161,11 +160,11 @@ public:
});
arguments << "--" << filterArgs << exclusionArgs;
- QtcProcess process;
+ Process process;
process.setEnvironment(m_environment);
process.setCommand({m_vcsBinary, arguments});
process.setWorkingDirectory(m_directory);
- process.setStdOutCallback([this, &fi](const QString &text) { read(fi, text); });
+ process.setStdOutCallback([this, &promise](const QString &text) { read(promise, text); });
process.start();
process.waitForFinished();
@@ -173,7 +172,7 @@ public:
case ProcessResult::TerminatedAbnormally:
case ProcessResult::StartFailed:
case ProcessResult::Hang:
- fi.reportCanceled();
+ promise.future().cancel();
break;
case ProcessResult::FinishedWithSuccess:
case ProcessResult::FinishedWithError:
@@ -272,10 +271,10 @@ void GitGrep::writeSettings(QSettings *settings) const
settings->setValue(GitGrepRef, m_treeLineEdit->text());
}
-QFuture<FileSearchResultList> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters,
+QFuture<SearchResultItems> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind * /*baseFileFind*/)
{
- return Utils::runAsync(GitGrepRunner(parameters));
+ return Utils::asyncRun(GitGrepRunner(parameters));
}
IEditor *GitGrep::openEditor(const SearchResultItem &item,
diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h
index 119751102c..2fd114e36e 100644
--- a/src/plugins/git/gitgrep.h
+++ b/src/plugins/git/gitgrep.h
@@ -5,9 +5,14 @@
#include <texteditor/basefilefind.h>
-QT_FORWARD_DECLARE_CLASS(QCheckBox);
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+QT_END_NAMESPACE
-namespace Utils { class FancyLineEdit; }
+namespace Utils {
+class FancyLineEdit;
+class SearchResultItem;
+}
namespace Git::Internal {
@@ -25,10 +30,10 @@ public:
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
- QFuture<Utils::FileSearchResultList> executeSearch(
+ QFuture<Utils::SearchResultItems> executeSearch(
const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind *baseFileFind) override;
- Core::IEditor *openEditor(const Core::SearchResultItem &item,
+ Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;
private:
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index 9729dbe424..2b9f908ff8 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -42,12 +42,12 @@
#include <texteditor/textmark.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/commandline.h>
#include <utils/infobar.h>
#include <utils/parameteraction.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#include <utils/utilsicons.h>
@@ -394,9 +394,11 @@ public:
void instantBlameOnce();
void instantBlame();
void stopInstantBlame();
+ bool refreshWorkingDirectory(const FilePath &workingDirectory);
void onApplySettings();
+ GitSettings setting;
CommandLocator *m_commandLocator = nullptr;
QAction *m_menuAction = nullptr;
@@ -418,24 +420,23 @@ public:
QVector<ParameterAction *> m_projectActions;
QVector<QAction *> m_repositoryActions;
ParameterAction *m_applyCurrentFilePatchAction = nullptr;
- Gerrit::Internal::GerritPlugin *m_gerritPlugin = nullptr;
+ Gerrit::Internal::GerritPlugin m_gerritPlugin;
- GitSettings m_settings;
- GitClient m_gitClient{&m_settings};
+ GitClient m_gitClient;
QPointer<StashDialog> m_stashDialog;
BranchViewFactory m_branchViewFactory;
QPointer<RemoteDialog> m_remoteDialog;
FilePath m_submitRepository;
QString m_commitMessageFileName;
+ FilePath m_workingDirectory;
+ QTextCodec *m_codec = nullptr;
Author m_author;
int m_lastVisitedEditorLine = -1;
QTimer *m_cursorPositionChangedTimer = nullptr;
std::unique_ptr<BlameMark> m_blameMark;
QMetaObject::Connection m_blameCursorPosConn;
- GitSettingsPage settingPage{&m_settings};
-
GitGrep gitGrep{&m_gitClient};
VcsEditorFactory svnLogEditorFactory {
@@ -524,7 +525,7 @@ void GitPluginPrivate::onApplySettings()
updateRepositoryBrowserAction();
bool gitFoundOk;
QString errorMessage;
- m_settings.gitExecutable(&gitFoundOk, &errorMessage);
+ settings().gitExecutable(&gitFoundOk, &errorMessage);
if (!gitFoundOk) {
QTimer::singleShot(0, this, [errorMessage] {
AsynchronousMessageBox::warning(Tr::tr("Git Settings"), errorMessage);
@@ -555,11 +556,6 @@ IVersionControl *GitPlugin::versionControl()
return dd;
}
-const GitSettings &GitPlugin::settings()
-{
- return dd->m_settings;
-}
-
const VcsBasePluginState &GitPlugin::currentState()
{
return dd->currentState();
@@ -776,13 +772,11 @@ GitPluginPrivate::GitPluginPrivate()
createProjectAction(currentProjectMenu, Tr::tr("Diff Current Project", "Avoid translating \"Diff\""),
Tr::tr("Diff Project \"%1\"", "Avoid translating \"Diff\""),
- "Git.DiffProject", context, true, &GitPluginPrivate::diffCurrentProject,
- QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+Shift+D") : Tr::tr("Alt+G,Alt+Shift+D")));
+ "Git.DiffProject", context, true, &GitPluginPrivate::diffCurrentProject);
createProjectAction(currentProjectMenu, Tr::tr("Log Project", "Avoid translating \"Log\""),
Tr::tr("Log Project \"%1\"", "Avoid translating \"Log\""),
- "Git.LogProject", context, true, &GitPluginPrivate::logProject,
- QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+K") : Tr::tr("Alt+G,Alt+K")));
+ "Git.LogProject", context, true, &GitPluginPrivate::logProject);
createProjectAction(currentProjectMenu, Tr::tr("Clean Project...", "Avoid translating \"Clean\""),
Tr::tr("Clean Project \"%1\"...", "Avoid translating \"Clean\""),
@@ -795,10 +789,12 @@ GitPluginPrivate::GitPluginPrivate()
gitContainer->addMenu(localRepositoryMenu);
createRepositoryAction(localRepositoryMenu, "Diff", "Git.DiffRepository",
- context, true, &GitClient::diffRepository);
+ context, true, &GitClient::diffRepository,
+ QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+Shift+D") : Tr::tr("Alt+G,Alt+Shift+D")));
createRepositoryAction(localRepositoryMenu, "Log", "Git.LogRepository",
- context, true, std::bind(&GitPluginPrivate::logRepository, this));
+ context, true, std::bind(&GitPluginPrivate::logRepository, this),
+ QKeySequence(useMacShortcuts ? Tr::tr("Meta+G,Meta+K") : Tr::tr("Alt+G,Alt+K")));
createRepositoryAction(localRepositoryMenu, "Reflog", "Git.ReflogRepository",
context, true, std::bind(&GitPluginPrivate::reflogRepository, this));
@@ -1065,12 +1061,11 @@ GitPluginPrivate::GitPluginPrivate()
this, &GitPluginPrivate::updateBranches, Qt::QueuedConnection);
/* "Gerrit" */
- m_gerritPlugin = new Gerrit::Internal::GerritPlugin(this);
- m_gerritPlugin->initialize(remoteRepositoryMenu);
- m_gerritPlugin->updateActions(currentState());
- m_gerritPlugin->addToLocator(m_commandLocator);
+ m_gerritPlugin.addToMenu(remoteRepositoryMenu);
+ m_gerritPlugin.updateActions(currentState());
+ m_gerritPlugin.addToLocator(m_commandLocator);
- connect(&m_settings, &AspectContainer::applied, this, &GitPluginPrivate::onApplySettings);
+ connect(&settings(), &AspectContainer::applied, this, &GitPluginPrivate::onApplySettings);
setupInstantBlame();
}
@@ -1440,16 +1435,15 @@ void GitPluginPrivate::setupInstantBlame()
return;
}
- if (!GitClient::instance()->settings().instantBlame.value()) {
+ if (!settings().instantBlame.value()) {
m_lastVisitedEditorLine = -1;
stopInstantBlame();
return;
}
const Utils::FilePath workingDirectory = GitPlugin::currentState().currentFileTopLevel();
- if (workingDirectory.isEmpty())
+ if (!refreshWorkingDirectory(workingDirectory))
return;
- m_author = GitClient::instance()->getAuthor(workingDirectory);
const TextEditorWidget *widget = TextEditorWidget::fromEditor(editor);
if (!widget)
@@ -1460,7 +1454,7 @@ void GitPluginPrivate::setupInstantBlame()
m_blameCursorPosConn = connect(widget, &QPlainTextEdit::cursorPositionChanged, this,
[this] {
- if (!GitClient::instance()->settings().instantBlame.value()) {
+ if (!settings().instantBlame.value()) {
disconnect(m_blameCursorPosConn);
return;
}
@@ -1471,8 +1465,8 @@ void GitPluginPrivate::setupInstantBlame()
instantBlame();
};
- connect(&GitClient::instance()->settings().instantBlame,
- &BoolAspect::valueChanged, this, [this, setupBlameForEditor](bool enabled) {
+ connect(&settings().instantBlame, &BoolAspect::valueChanged, this,
+ [this, setupBlameForEditor](bool enabled) {
if (enabled)
setupBlameForEditor(EditorManager::currentEditor());
else
@@ -1521,7 +1515,7 @@ CommitInfo parseBlameOutput(const QStringList &blame, const Utils::FilePath &fil
void GitPluginPrivate::instantBlameOnce()
{
- if (!GitClient::instance()->settings().instantBlame.value()) {
+ if (!settings().instantBlame.value()) {
const TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget();
if (!widget)
return;
@@ -1532,9 +1526,8 @@ void GitPluginPrivate::instantBlameOnce()
this, [this] { m_blameMark.reset(); }, Qt::SingleShotConnection);
const Utils::FilePath workingDirectory = GitPlugin::currentState().topLevel();
- if (workingDirectory.isEmpty())
+ if (!refreshWorkingDirectory(workingDirectory))
return;
- m_author = GitClient::instance()->getAuthor(workingDirectory);
}
m_lastVisitedEditorLine = -1;
@@ -1556,7 +1549,7 @@ void GitPluginPrivate::instantBlame()
const QTextCursor cursor = widget->textCursor();
const QTextBlock block = cursor.block();
const int line = block.blockNumber() + 1;
- const int lines = widget->document()->lineCount();
+ const int lines = widget->document()->blockCount();
if (line >= lines) {
m_blameMark.reset();
@@ -1582,10 +1575,9 @@ void GitPluginPrivate::instantBlame()
const CommitInfo info = parseBlameOutput(output.split('\n'), filePath, m_author);
m_blameMark.reset(new BlameMark(filePath, line, info));
};
- QTextCodec *codec = GitClient::instance()->encoding(GitClient::EncodingCommit, workingDirectory);
GitClient::instance()->vcsExecWithHandler(workingDirectory,
{"blame", "-p", "-L", lineString, "--", filePath.toString()},
- this, commandHandler, RunFlags::NoOutput, codec);
+ this, commandHandler, RunFlags::NoOutput, m_codec);
}
void GitPluginPrivate::stopInstantBlame()
@@ -1595,6 +1587,21 @@ void GitPluginPrivate::stopInstantBlame()
disconnect(m_blameCursorPosConn);
}
+bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory)
+{
+ if (workingDirectory.isEmpty())
+ return false;
+
+ if (m_workingDirectory == workingDirectory)
+ return true;
+
+ m_workingDirectory = workingDirectory;
+ m_author = GitClient::instance()->getAuthor(workingDirectory);
+ m_codec = GitClient::instance()->encoding(GitClient::EncodingCommit, workingDirectory);
+
+ return true;
+}
+
IEditor *GitPluginPrivate::openSubmitEditor(const QString &fileName, const CommitData &cd)
{
IEditor *editor = EditorManager::openEditor(FilePath::fromString(fileName),
@@ -1683,7 +1690,7 @@ void GitPluginPrivate::pull()
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
FilePath topLevel = state.topLevel();
- bool rebase = m_settings.pullRebase.value();
+ bool rebase = settings().pullRebase.value();
if (!rebase) {
QString currentBranch = m_gitClient.synchronousCurrentLocalBranch(topLevel);
@@ -1927,7 +1934,7 @@ void GitPluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as)
updateContinueAndAbortCommands();
updateRepositoryBrowserAction();
- m_gerritPlugin->updateActions(state);
+ m_gerritPlugin.updateActions(state);
}
void GitPluginPrivate::updateContinueAndAbortCommands()
@@ -1965,7 +1972,7 @@ void GitPluginPrivate::updateContinueAndAbortCommands()
void GitPluginPrivate::delayedPushToGerrit()
{
- m_gerritPlugin->push(m_submitRepository);
+ m_gerritPlugin.push(m_submitRepository);
}
void GitPluginPrivate::updateBranches(const FilePath &repository)
@@ -1994,7 +2001,7 @@ QObject *GitPlugin::remoteCommand(const QStringList &options, const QString &wor
void GitPluginPrivate::updateRepositoryBrowserAction()
{
const bool repositoryEnabled = currentState().hasTopLevel();
- const bool hasRepositoryBrowserCmd = !m_settings.repositoryBrowserCmd.value().isEmpty();
+ const bool hasRepositoryBrowserCmd = !settings().repositoryBrowserCmd.value().isEmpty();
m_repositoryBrowserAction->setEnabled(repositoryEnabled && hasRepositoryBrowserCmd);
}
@@ -2100,7 +2107,7 @@ GitPluginPrivate::RepoUrl GitPluginPrivate::getRepoUrl(const QString &location)
FilePaths GitPluginPrivate::additionalToolsPath() const
{
- FilePaths res = m_gitClient.settings().searchPathList();
+ FilePaths res = settings().searchPathList();
const FilePath binaryPath = m_gitClient.gitBinDirectory();
if (!binaryPath.isEmpty() && !res.contains(binaryPath))
res << binaryPath;
@@ -2172,7 +2179,7 @@ void GitPlugin::updateBranches(const FilePath &repository)
void GitPlugin::gerritPush(const FilePath &topLevel)
{
- dd->m_gerritPlugin->push(topLevel);
+ dd->m_gerritPlugin.push(topLevel);
}
bool GitPlugin::isCommitEditorOpen()
diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h
index 0b0e751801..59d9a87fde 100644
--- a/src/plugins/git/gitplugin.h
+++ b/src/plugins/git/gitplugin.h
@@ -3,7 +3,6 @@
#pragma once
-#include "gitsettings.h"
#include "git_global.h"
#include <coreplugin/iversioncontrol.h>
@@ -36,7 +35,6 @@ public:
static GitClient *client();
static Core::IVersionControl *versionControl();
- static const GitSettings &settings();
static const VcsBase::VcsBasePluginState &currentState();
static QString msgRepositoryLabel(const Utils::FilePath &repository);
@@ -63,7 +61,6 @@ private slots:
void testGitRemote_data();
void testGitRemote();
#endif
-
};
} // Git::Internal
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 25a8f77988..f092dd74e9 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -17,8 +17,21 @@ using namespace VcsBase;
namespace Git::Internal {
+static GitSettings *theSettings;
+
+GitSettings &settings()
+{
+ return *theSettings;
+}
+
GitSettings::GitSettings()
{
+ theSettings = this;
+
+ setId(VcsBase::Constants::VCS_ID_GIT);
+ setDisplayName(Tr::tr("Git"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+
setSettingsGroup("Git");
path.setDisplayStyle(StringAspect::LineEditDisplay);
@@ -117,83 +130,71 @@ GitSettings::GitSettings()
timeout.setDefaultValue(Utils::HostOsInfo::isWindowsHost() ? 60 : 30);
- connect(&binaryPath, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
- connect(&path, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
-}
-
-FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const
-{
- // Locate binary in path if one is specified, otherwise default to pathless binary.
- if (ok)
- *ok = true;
- if (errorMessage)
- errorMessage->clear();
-
- if (tryResolve) {
- resolvedBinPath = binaryPath.filePath();
- if (!resolvedBinPath.isAbsolutePath())
- resolvedBinPath = resolvedBinPath.searchInPath({path.filePath()}, FilePath::PrependToPath);
- tryResolve = false;
- }
-
- if (resolvedBinPath.isEmpty()) {
- if (ok)
- *ok = false;
- if (errorMessage)
- *errorMessage = Tr::tr("The binary \"%1\" could not be located in the path \"%2\"")
- .arg(binaryPath.value(), path.value());
- }
- return resolvedBinPath;
-}
-
-// GitSettingsPage
-
-GitSettingsPage::GitSettingsPage(GitSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_GIT);
- setDisplayName(Tr::tr("Git"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- GitSettings &s = *settings;
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
Column {
Group {
title(Tr::tr("Configuration")),
Column {
- Row { s.path },
- s.winSetHomeEnvironment,
+ Row { path },
+ winSetHomeEnvironment,
}
},
Group {
title(Tr::tr("Miscellaneous")),
Column {
- Row { s.logCount, s.timeout, st },
- s.pullRebase
+ Row { logCount, timeout, st },
+ pullRebase
}
},
Group {
title(Tr::tr("Gitk")),
- Row { s.gitkOptions }
+ Row { gitkOptions }
},
Group {
title(Tr::tr("Repository Browser")),
- Row { s.repositoryBrowserCmd }
+ Row { repositoryBrowserCmd }
},
Group {
title(Tr::tr("Instant Blame")),
- Row { s.instantBlame }
+ Row { instantBlame }
},
st
}.attachTo(widget);
});
+ connect(&binaryPath, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
+ connect(&path, &StringAspect::valueChanged, this, [this] { tryResolve = true; });
+}
+
+FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const
+{
+ // Locate binary in path if one is specified, otherwise default to pathless binary.
+ if (ok)
+ *ok = true;
+ if (errorMessage)
+ errorMessage->clear();
+
+ if (tryResolve) {
+ resolvedBinPath = binaryPath.filePath();
+ if (!resolvedBinPath.isAbsolutePath())
+ resolvedBinPath = resolvedBinPath.searchInPath({path.filePath()}, FilePath::PrependToPath);
+ tryResolve = false;
+ }
+
+ if (resolvedBinPath.isEmpty()) {
+ if (ok)
+ *ok = false;
+ if (errorMessage)
+ *errorMessage = Tr::tr("The binary \"%1\" could not be located in the path \"%2\"")
+ .arg(binaryPath.value(), path.value());
+ }
+ return resolvedBinPath;
}
} // Git::Internal
diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h
index c03deadeb9..3899fdada5 100644
--- a/src/plugins/git/gitsettings.h
+++ b/src/plugins/git/gitsettings.h
@@ -3,7 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
#include <vcsbase/vcsbaseclientsettings.h>
namespace Git::Internal {
@@ -46,10 +45,6 @@ public:
Utils::FilePath gitExecutable(bool *ok = nullptr, QString *errorMessage = nullptr) const;
};
-class GitSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit GitSettingsPage(GitSettings *settings);
-};
+GitSettings &settings();
} // Git::Internal
diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp
index 23755d9875..9ff83c8c12 100644
--- a/src/plugins/git/gitsubmiteditor.cpp
+++ b/src/plugins/git/gitsubmiteditor.cpp
@@ -11,8 +11,8 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <vcsbase/submitfilemodel.h>
#include <vcsbase/vcsoutputwindow.h>
@@ -204,7 +204,7 @@ void GitSubmitEditor::updateFileModel()
return;
w->setUpdateInProgress(true);
// TODO: Check if fetch works OK from separate thread, refactor otherwise
- m_fetchWatcher.setFuture(Utils::runAsync(&CommitDataFetchResult::fetch,
+ m_fetchWatcher.setFuture(Utils::asyncRun(&CommitDataFetchResult::fetch,
m_commitType, m_workingDirectory));
Core::ProgressManager::addTask(m_fetchWatcher.future(), Tr::tr("Refreshing Commit Data"),
TASK_UPDATE_COMMIT);
diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp
index 65bc5beab4..ec09437e26 100644
--- a/src/plugins/git/gitsubmiteditorwidget.cpp
+++ b/src/plugins/git/gitsubmiteditorwidget.cpp
@@ -36,8 +36,6 @@ class GitSubmitPanel : public QWidget
public:
GitSubmitPanel()
{
- resize(364, 269);
-
repositoryLabel = new QLabel(Tr::tr("repository"));
branchLabel = new QLabel(Tr::tr("branch")); // FIXME: Isn't this overwritten soon?
showHeadLabel = new QLabel("<a href=\"head\">" + Tr::tr("Show HEAD") + "</a>");
@@ -81,7 +79,8 @@ public:
}
},
editGroup,
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
}
QLabel *repositoryLabel;
diff --git a/src/plugins/git/logchangedialog.cpp b/src/plugins/git/logchangedialog.cpp
index 55055cbf76..75660c1189 100644
--- a/src/plugins/git/logchangedialog.cpp
+++ b/src/plugins/git/logchangedialog.cpp
@@ -224,7 +224,7 @@ LogChangeDialog::LogChangeDialog(bool isReset, QWidget *parent) :
m_resetTypeComboBox->addItem(Tr::tr("Hard"), "--hard");
m_resetTypeComboBox->addItem(Tr::tr("Mixed"), "--mixed");
m_resetTypeComboBox->addItem(Tr::tr("Soft"), "--soft");
- m_resetTypeComboBox->setCurrentIndex(GitClient::settings().lastResetIndex.value());
+ m_resetTypeComboBox->setCurrentIndex(settings().lastResetIndex());
popUpLayout->addWidget(m_resetTypeComboBox);
popUpLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
}
@@ -250,7 +250,7 @@ bool LogChangeDialog::runDialog(const FilePath &repository,
if (QDialog::exec() == QDialog::Accepted) {
if (m_resetTypeComboBox)
- GitClient::settings().lastResetIndex.setValue(m_resetTypeComboBox->currentIndex());
+ settings().lastResetIndex.setValue(m_resetTypeComboBox->currentIndex());
return true;
}
return false;
diff --git a/src/plugins/git/mergetool.cpp b/src/plugins/git/mergetool.cpp
index f696bef124..6fbc9d5ea8 100644
--- a/src/plugins/git/mergetool.cpp
+++ b/src/plugins/git/mergetool.cpp
@@ -23,8 +23,8 @@ namespace Git::Internal {
MergeTool::MergeTool(QObject *parent) : QObject(parent)
{
- connect(&m_process, &QtcProcess::done, this, &MergeTool::done);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, &MergeTool::readData);
+ connect(&m_process, &Process::done, this, &MergeTool::done);
+ connect(&m_process, &Process::readyReadStandardOutput, this, &MergeTool::readData);
Environment env = Environment::systemEnvironment();
env.set("LANG", "C");
env.set("LANGUAGE", "C");
diff --git a/src/plugins/git/mergetool.h b/src/plugins/git/mergetool.h
index 290b50f10c..727f9a85df 100644
--- a/src/plugins/git/mergetool.h
+++ b/src/plugins/git/mergetool.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
#include <QStringList>
@@ -49,7 +49,7 @@ private:
void chooseAction();
void addButton(QMessageBox *msgBox, const QString &text, char key);
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
MergeType m_mergeType = NormalMerge;
QString m_fileName;
FileState m_localState = UnknownState;
diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp
index 4d54105e44..3a061b9cc5 100644
--- a/src/plugins/gitlab/gitlabclonedialog.cpp
+++ b/src/plugins/gitlab/gitlabclonedialog.cpp
@@ -23,8 +23,8 @@
#include <utils/infolabel.h>
#include <utils/mimeutils.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <vcsbase/vcsbaseplugin.h>
#include <vcsbase/vcscommand.h>
diff --git a/src/plugins/gitlab/gitlabdialog.cpp b/src/plugins/gitlab/gitlabdialog.cpp
index 4a10888c94..9375c057f5 100644
--- a/src/plugins/gitlab/gitlabdialog.cpp
+++ b/src/plugins/gitlab/gitlabdialog.cpp
@@ -9,7 +9,7 @@
#include "gitlabprojectsettings.h"
#include "gitlabtr.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
@@ -188,7 +188,7 @@ void GitLabDialog::requestMainViewUpdate()
bool linked = false;
m_currentServerId = Id();
- if (auto project = ProjectExplorer::SessionManager::startupProject()) {
+ if (auto project = ProjectExplorer::ProjectManager::startupProject()) {
GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
if (projSettings->isLinked()) {
m_currentServerId = projSettings->currentServer();
diff --git a/src/plugins/gitlab/gitlaboptionspage.cpp b/src/plugins/gitlab/gitlaboptionspage.cpp
index 4f519e7788..c2e56d5730 100644
--- a/src/plugins/gitlab/gitlaboptionspage.cpp
+++ b/src/plugins/gitlab/gitlaboptionspage.cpp
@@ -9,6 +9,7 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
@@ -44,6 +45,25 @@ static bool hostValid(const QString &host)
return (host == "localhost") || dn.match(host).hasMatch();
}
+class GitLabServerWidget : public QWidget
+{
+public:
+ enum Mode { Display, Edit };
+ explicit GitLabServerWidget(Mode m, QWidget *parent = nullptr);
+
+ GitLabServer gitLabServer() const;
+ void setGitLabServer(const GitLabServer &server);
+
+private:
+ Mode m_mode = Display;
+ Id m_id;
+ StringAspect m_host;
+ StringAspect m_description;
+ StringAspect m_token;
+ IntegerAspect m_port;
+ BoolAspect m_secure;
+};
+
GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent)
: QWidget(parent)
, m_mode(m)
@@ -77,13 +97,14 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent)
Row {
Form {
- m_host,
- m_description,
- m_token,
- m_port,
- m_secure
+ m_host, br,
+ m_description, br,
+ m_token, br,
+ m_port, br,
+ m_secure,
+ m == Edit ? normalMargin : noMargin
},
- }.attachTo(this, m == Edit ? WithMargins : WithoutMargins);
+ }.attachTo(this);
}
GitLabServer GitLabServerWidget::gitLabServer() const
@@ -108,8 +129,30 @@ void GitLabServerWidget::setGitLabServer(const GitLabServer &server)
m_secure.setValue(server.secure);
}
-GitLabOptionsWidget::GitLabOptionsWidget(QWidget *parent)
- : QWidget(parent)
+class GitLabOptionsWidget : public Core::IOptionsPageWidget
+{
+public:
+ explicit GitLabOptionsWidget(GitLabParameters *parameters);
+
+private:
+ void showEditServerDialog();
+ void showAddServerDialog();
+ void removeCurrentTriggered();
+ void addServer(const GitLabServer &newServer);
+ void modifyCurrentServer(const GitLabServer &newServer);
+ void updateButtonsState();
+
+ GitLabParameters *m_parameters = nullptr;
+ GitLabServerWidget *m_gitLabServerWidget = nullptr;
+ QPushButton *m_edit = nullptr;
+ QPushButton *m_remove = nullptr;
+ QPushButton *m_add = nullptr;
+ QComboBox *m_defaultGitLabServer = nullptr;
+ Utils::StringAspect m_curl;
+};
+
+GitLabOptionsWidget::GitLabOptionsWidget(GitLabParameters *params)
+ : m_parameters(params)
{
auto defaultLabel = new QLabel(Tr::tr("Default:"), this);
m_defaultGitLabServer = new QComboBox(this);
@@ -126,7 +169,7 @@ GitLabOptionsWidget::GitLabOptionsWidget(QWidget *parent)
m_add = new QPushButton(Tr::tr("Add..."), this);
m_add->setToolTip(Tr::tr("Add new GitLab server configuration."));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
Form {
@@ -136,42 +179,43 @@ GitLabOptionsWidget::GitLabOptionsWidget(QWidget *parent)
}, Column { m_add, m_edit, m_remove, st },
}.attachTo(this);
- connect(m_edit, &QPushButton::clicked, this, &GitLabOptionsWidget::showEditServerDialog);
- connect(m_remove, &QPushButton::clicked, this, &GitLabOptionsWidget::removeCurrentTriggered);
- connect(m_add, &QPushButton::clicked, this, &GitLabOptionsWidget::showAddServerDialog);
- connect(m_defaultGitLabServer, &QComboBox::currentIndexChanged, this, [this] {
- m_gitLabServerWidget->setGitLabServer(
- m_defaultGitLabServer->currentData().value<GitLabServer>());
- });
-}
-
-GitLabParameters GitLabOptionsWidget::parameters() const
-{
- GitLabParameters result;
- // get all configured gitlabservers
- for (int i = 0, end = m_defaultGitLabServer->count(); i < end; ++i)
- result.gitLabServers.append(m_defaultGitLabServer->itemData(i).value<GitLabServer>());
- if (m_defaultGitLabServer->count())
- result.defaultGitLabServer = m_defaultGitLabServer->currentData().value<GitLabServer>().id;
- result.curl = m_curl.filePath();
- return result;
-}
+ m_curl.setFilePath(params->curl);
-void GitLabOptionsWidget::setParameters(const GitLabParameters &params)
-{
- m_curl.setFilePath(params.curl);
-
- for (const auto &gitLabServer : params.gitLabServers) {
+ for (const auto &gitLabServer : params->gitLabServers) {
m_defaultGitLabServer->addItem(gitLabServer.displayString(),
QVariant::fromValue(gitLabServer));
}
- const GitLabServer found = params.currentDefaultServer();
+ const GitLabServer found = params->currentDefaultServer();
if (found.id.isValid()) {
m_defaultGitLabServer->setCurrentIndex(m_defaultGitLabServer->findData(
QVariant::fromValue(found)));
}
updateButtonsState();
+
+ connect(m_edit, &QPushButton::clicked, this, &GitLabOptionsWidget::showEditServerDialog);
+ connect(m_remove, &QPushButton::clicked, this, &GitLabOptionsWidget::removeCurrentTriggered);
+ connect(m_add, &QPushButton::clicked, this, &GitLabOptionsWidget::showAddServerDialog);
+ connect(m_defaultGitLabServer, &QComboBox::currentIndexChanged, this, [this] {
+ m_gitLabServerWidget->setGitLabServer(
+ m_defaultGitLabServer->currentData().value<GitLabServer>());
+ });
+
+ setOnApply([this] {
+ GitLabParameters result;
+ // get all configured gitlabservers
+ for (int i = 0, end = m_defaultGitLabServer->count(); i < end; ++i)
+ result.gitLabServers.append(m_defaultGitLabServer->itemData(i).value<GitLabServer>());
+ if (m_defaultGitLabServer->count())
+ result.defaultGitLabServer = m_defaultGitLabServer->currentData().value<GitLabServer>().id;
+ result.curl = m_curl.filePath();
+
+ if (result != *m_parameters) {
+ m_parameters->assign(result);
+ m_parameters->toSettings(Core::ICore::settings());
+ emit m_parameters->changed();
+ }
+ });
}
void GitLabOptionsWidget::showEditServerDialog()
@@ -253,39 +297,14 @@ void GitLabOptionsWidget::updateButtonsState()
m_remove->setEnabled(hasItems);
}
-GitLabOptionsPage::GitLabOptionsPage(GitLabParameters *p, QObject *parent)
- : Core::IOptionsPage{parent}
- , m_parameters(p)
+// GitLabOptionsPage
+
+GitLabOptionsPage::GitLabOptionsPage(GitLabParameters *p)
{
setId(Constants::GITLAB_SETTINGS);
setDisplayName(Tr::tr("GitLab"));
setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
-}
-
-QWidget *GitLabOptionsPage::widget()
-{
- if (!m_widget) {
- m_widget = new GitLabOptionsWidget;
- m_widget->setParameters(*m_parameters);
- }
- return m_widget;
-}
-
-void GitLabOptionsPage::apply()
-{
- if (GitLabOptionsWidget *w = m_widget.data()) {
- GitLabParameters newParameters = w->parameters();
- if (newParameters != *m_parameters) {
- *m_parameters = newParameters;
- m_parameters->toSettings(Core::ICore::settings());
- emit settingsChanged();
- }
- }
-}
-
-void GitLabOptionsPage::finish()
-{
- delete m_widget;
+ setWidgetCreator([p] { return new GitLabOptionsWidget(p); });
}
} // namespace GitLab
diff --git a/src/plugins/gitlab/gitlaboptionspage.h b/src/plugins/gitlab/gitlaboptionspage.h
index 327cc1128a..664f4a26fc 100644
--- a/src/plugins/gitlab/gitlaboptionspage.h
+++ b/src/plugins/gitlab/gitlaboptionspage.h
@@ -6,84 +6,15 @@
#include "gitlabparameters.h"
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
-#include <QPointer>
-
-QT_BEGIN_NAMESPACE
-class QComboBox;
-class QPushButton;
-QT_END_NAMESPACE
namespace GitLab {
-namespace Constants {
-const char GITLAB_SETTINGS[] = "GitLab";
-} // namespace Constants
-
-class GitLabServerWidget : public QWidget
-{
-public:
- enum Mode { Display, Edit };
- explicit GitLabServerWidget(Mode m, QWidget *parent = nullptr);
-
- GitLabServer gitLabServer() const;
- void setGitLabServer(const GitLabServer &server);
-
- bool isValid() const;
-private:
- Mode m_mode = Display;
- Utils::Id m_id;
- Utils::StringAspect m_host;
- Utils::StringAspect m_description;
- Utils::StringAspect m_token;
- Utils::IntegerAspect m_port;
- Utils::BoolAspect m_secure;
-};
-
-class GitLabOptionsWidget : public QWidget
-{
- Q_OBJECT
-public:
- explicit GitLabOptionsWidget(QWidget *parent = nullptr);
-
- GitLabParameters parameters() const;
- void setParameters(const GitLabParameters &params);
-
-private:
- void showEditServerDialog();
- void showAddServerDialog();
- void removeCurrentTriggered();
- void addServer(const GitLabServer &newServer);
- void modifyCurrentServer(const GitLabServer &newServer);
- void updateButtonsState();
-
- GitLabServerWidget *m_gitLabServerWidget = nullptr;
- QPushButton *m_edit = nullptr;
- QPushButton *m_remove = nullptr;
- QPushButton *m_add = nullptr;
- QComboBox *m_defaultGitLabServer = nullptr;
- Utils::StringAspect m_curl;
-};
+namespace Constants { const char GITLAB_SETTINGS[] = "GitLab"; }
class GitLabOptionsPage : public Core::IOptionsPage
{
- Q_OBJECT
public:
- explicit GitLabOptionsPage(GitLabParameters *p, QObject *parent = nullptr);
-
- QWidget *widget() final;
- void apply() final;
- void finish() final;
-
-signals:
- void settingsChanged();
-
-private:
- void addServer();
-
- GitLabParameters *m_parameters;
- QPointer<GitLabOptionsWidget> m_widget;
+ explicit GitLabOptionsPage(GitLabParameters *p);
};
-} // namespace GitLab
+} // GitLab
diff --git a/src/plugins/gitlab/gitlabparameters.cpp b/src/plugins/gitlab/gitlabparameters.cpp
index 1a3f970edb..79a6ce7da6 100644
--- a/src/plugins/gitlab/gitlabparameters.cpp
+++ b/src/plugins/gitlab/gitlabparameters.cpp
@@ -102,6 +102,13 @@ GitLabParameters::GitLabParameters()
{
}
+void GitLabParameters::assign(const GitLabParameters &other)
+{
+ curl = other.curl;
+ defaultGitLabServer = other.defaultGitLabServer;
+ gitLabServers = other.gitLabServers;
+}
+
bool GitLabParameters::equals(const GitLabParameters &other) const
{
return curl == other.curl && defaultGitLabServer == other.defaultGitLabServer
diff --git a/src/plugins/gitlab/gitlabparameters.h b/src/plugins/gitlab/gitlabparameters.h
index 57dda3667d..7a21976dc2 100644
--- a/src/plugins/gitlab/gitlabparameters.h
+++ b/src/plugins/gitlab/gitlabparameters.h
@@ -38,11 +38,14 @@ public:
bool validateCert = true;
};
-class GitLabParameters
+class GitLabParameters : public QObject
{
+ Q_OBJECT
+
public:
GitLabParameters();
+ void assign(const GitLabParameters &other);
bool equals(const GitLabParameters &other) const;
bool isValid() const;
@@ -52,6 +55,10 @@ public:
GitLabServer currentDefaultServer() const;
GitLabServer serverForId(const Utils::Id &id) const;
+signals:
+ void changed();
+
+public:
friend bool operator==(const GitLabParameters &p1, const GitLabParameters &p2)
{
return p1.equals(p2);
diff --git a/src/plugins/gitlab/gitlabplugin.cpp b/src/plugins/gitlab/gitlabplugin.cpp
index 2059ae4d84..5ae4df682b 100644
--- a/src/plugins/gitlab/gitlabplugin.cpp
+++ b/src/plugins/gitlab/gitlabplugin.cpp
@@ -14,11 +14,15 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
+
#include <git/gitplugin.h>
+
#include <projectexplorer/project.h>
#include <projectexplorer/projectpanelfactory.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <utils/qtcassert.h>
+
#include <vcsbase/vcsoutputwindow.h>
#include <QAction>
@@ -34,6 +38,18 @@ const char GITLAB_OPEN_VIEW[] = "GitLab.OpenView";
class GitLabPluginPrivate : public QObject
{
public:
+ void setupNotificationTimer();
+ void fetchEvents();
+ void fetchUser();
+ void createAndSendEventsRequest(const QDateTime timeStamp, int page = -1);
+ void handleUser(const User &user);
+ void handleEvents(const Events &events, const QDateTime &timeStamp);
+
+ void onSettingsChanged() {
+ if (dialog)
+ dialog->updateRemotes();
+ }
+
GitLabParameters parameters;
GitLabOptionsPage optionsPage{&parameters};
QHash<ProjectExplorer::Project *, GitLabProjectSettings *> projectSettings;
@@ -43,13 +59,6 @@ public:
QString projectName;
Utils::Id serverId;
bool runningQuery = false;
-
- void setupNotificationTimer();
- void fetchEvents();
- void fetchUser();
- void createAndSendEventsRequest(const QDateTime timeStamp, int page = -1);
- void handleUser(const User &user);
- void handleEvents(const Events &events, const QDateTime &timeStamp);
};
static GitLabPluginPrivate *dd = nullptr;
@@ -85,12 +94,8 @@ void GitLabPlugin::initialize()
connect(openViewAction, &QAction::triggered, this, &GitLabPlugin::openView);
Core::ActionContainer *ac = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
ac->addAction(gitlabCommand);
- connect(&dd->optionsPage, &GitLabOptionsPage::settingsChanged, this, [] {
- if (dd->dialog)
- dd->dialog->updateRemotes();
- });
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this, &GitLabPlugin::onStartupProjectChanged);
}
@@ -121,7 +126,7 @@ void GitLabPlugin::onStartupProjectChanged()
{
QTC_ASSERT(dd, return);
disconnect(&dd->notificationTimer);
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project) {
dd->notificationTimer.stop();
return;
@@ -147,7 +152,7 @@ void GitLabPluginPrivate::setupNotificationTimer()
void GitLabPluginPrivate::fetchEvents()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return);
if (runningQuery)
@@ -218,7 +223,7 @@ void GitLabPluginPrivate::handleEvents(const Events &events, const QDateTime &ti
{
runningQuery = false;
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(project, return);
GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
@@ -301,7 +306,7 @@ bool GitLabPlugin::handleCertificateIssue(const Utils::Id &serverId)
int index = dd->parameters.gitLabServers.indexOf(server);
server.validateCert = false;
dd->parameters.gitLabServers.replace(index, server);
- emit dd->optionsPage.settingsChanged();
+ dd->onSettingsChanged();
return true;
}
return false;
@@ -311,7 +316,7 @@ void GitLabPlugin::linkedStateChanged(bool enabled)
{
QTC_ASSERT(dd, return);
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (project) {
const GitLabProjectSettings *pSettings = projectSettings(project);
dd->serverId = pSettings->currentServer();
diff --git a/src/plugins/gitlab/gitlabprojectsettings.cpp b/src/plugins/gitlab/gitlabprojectsettings.cpp
index 73532e492f..240f5292e4 100644
--- a/src/plugins/gitlab/gitlabprojectsettings.cpp
+++ b/src/plugins/gitlab/gitlabprojectsettings.cpp
@@ -157,7 +157,7 @@ GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Projec
connect(m_hostCB, &QComboBox::currentIndexChanged, this, [this] {
m_infoLabel->setVisible(false);
});
- connect(GitLabPlugin::optionsPage(), &GitLabOptionsPage::settingsChanged,
+ connect(GitLabPlugin::globalParameters(), &GitLabParameters::changed,
this, &GitLabProjectSettingsWidget::updateUi);
updateUi();
}
diff --git a/src/plugins/gitlab/queryrunner.cpp b/src/plugins/gitlab/queryrunner.cpp
index 2ddc86cd09..fd56c68c29 100644
--- a/src/plugins/gitlab/queryrunner.cpp
+++ b/src/plugins/gitlab/queryrunner.cpp
@@ -96,7 +96,7 @@ QueryRunner::QueryRunner(const Query &query, const Id &id, QObject *parent)
url += query.toString();
args << url;
m_process.setCommand({p->curl, args});
- connect(&m_process, &QtcProcess::done, this, [this, id] {
+ connect(&m_process, &Process::done, this, [this, id] {
if (m_process.result() != ProcessResult::FinishedWithSuccess) {
const int exitCode = m_process.exitCode();
if (m_process.exitStatus() == QProcess::NormalExit
diff --git a/src/plugins/gitlab/queryrunner.h b/src/plugins/gitlab/queryrunner.h
index ee698e384f..08afc56e1b 100644
--- a/src/plugins/gitlab/queryrunner.h
+++ b/src/plugins/gitlab/queryrunner.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/id.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
@@ -47,7 +47,7 @@ signals:
void resultRetrieved(const QByteArray &json);
private:
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
};
} // namespace GitLab
diff --git a/src/plugins/haskell/CMakeLists.txt b/src/plugins/haskell/CMakeLists.txt
index 0a62921168..d45326695c 100644
--- a/src/plugins/haskell/CMakeLists.txt
+++ b/src/plugins/haskell/CMakeLists.txt
@@ -1,7 +1,6 @@
add_qtc_plugin(Haskell
PLUGIN_DEPENDS
QtCreator::Core QtCreator::TextEditor QtCreator::ProjectExplorer
- DEPENDS Qt5::Widgets
SOURCES
haskell.qrc
haskell_global.h
@@ -13,9 +12,9 @@ add_qtc_plugin(Haskell
haskellplugin.cpp haskellplugin.h
haskellproject.cpp haskellproject.h
haskellrunconfiguration.cpp haskellrunconfiguration.h
+ haskellsettings.cpp haskellsettings.h
haskelltokenizer.cpp haskelltokenizer.h
haskelltr.h
- optionspage.cpp optionspage.h
stackbuildstep.cpp stackbuildstep.h
)
diff --git a/src/plugins/haskell/haskell.qbs b/src/plugins/haskell/haskell.qbs
index 30eb8328f1..5c1be11538 100644
--- a/src/plugins/haskell/haskell.qbs
+++ b/src/plugins/haskell/haskell.qbs
@@ -21,8 +21,8 @@ QtcPlugin {
"haskellplugin.cpp", "haskellplugin.h",
"haskellproject.cpp", "haskellproject.h",
"haskellrunconfiguration.cpp", "haskellrunconfiguration.h",
+ "haskellsettings.cpp", "haskellsettings.h",
"haskelltokenizer.cpp", "haskelltokenizer.h",
- "optionspage.cpp", "optionspage.h",
"stackbuildstep.cpp", "stackbuildstep.h"
]
}
diff --git a/src/plugins/haskell/haskell.qrc b/src/plugins/haskell/haskell.qrc
index 654f45818c..3ddf22f76d 100644
--- a/src/plugins/haskell/haskell.qrc
+++ b/src/plugins/haskell/haskell.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/haskell">
- <file>images/category_haskell.png</file>
+ <file>images/settingscategory_haskell.png</file>
+ <file>images/settingscategory_haskell@2x.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/haskell/haskelleditorfactory.cpp b/src/plugins/haskell/haskelleditorfactory.cpp
index 4053efeba2..ba87f88ef7 100644
--- a/src/plugins/haskell/haskelleditorfactory.cpp
+++ b/src/plugins/haskell/haskelleditorfactory.cpp
@@ -14,17 +14,14 @@
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/textindenter.h>
-#include <QCoreApplication>
+namespace Haskell::Internal {
-namespace Haskell {
-namespace Internal {
-
-static QWidget *createEditorWidget()
+static QWidget *createEditorWidget(QObject *guard)
{
auto widget = new TextEditor::TextEditorWidget;
auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget);
ghciButton->setText(Tr::tr("GHCi"));
- QObject::connect(ghciButton, &QToolButton::clicked, HaskellManager::instance(), [widget] {
+ QObject::connect(ghciButton, &QToolButton::clicked, guard, [widget] {
HaskellManager::openGhci(widget->textDocument()->filePath());
});
widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, ghciButton);
@@ -40,12 +37,11 @@ HaskellEditorFactory::HaskellEditorFactory()
| TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_HASKELLEDITOR_ID); });
setIndenterCreator([](QTextDocument *doc) { return new TextEditor::TextIndenter(doc); });
- setEditorWidgetCreator(createEditorWidget);
+ setEditorWidgetCreator([this] { return createEditorWidget(this); });
setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}"));
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setSyntaxHighlighterCreator([] { return new HaskellHighlighter(); });
}
-} // Internal
-} // Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskelleditorfactory.h b/src/plugins/haskell/haskelleditorfactory.h
index c91f875df2..090a5b1278 100644
--- a/src/plugins/haskell/haskelleditorfactory.h
+++ b/src/plugins/haskell/haskelleditorfactory.h
@@ -5,8 +5,7 @@
#include <texteditor/texteditor.h>
-namespace Haskell {
-namespace Internal {
+namespace Haskell::Internal {
class HaskellEditorFactory : public TextEditor::TextEditorFactory
{
@@ -14,5 +13,4 @@ public:
HaskellEditorFactory();
};
-} // Internal
-} // Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellmanager.cpp b/src/plugins/haskell/haskellmanager.cpp
index ac3564f23e..94a03ec659 100644
--- a/src/plugins/haskell/haskellmanager.cpp
+++ b/src/plugins/haskell/haskellmanager.cpp
@@ -3,43 +3,22 @@
#include "haskellmanager.h"
+#include "haskellsettings.h"
#include "haskelltr.h"
-#include <coreplugin/messagemanager.h>
#include <utils/algorithm.h>
#include <utils/commandline.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/processenums.h>
-#include <utils/qtcprocess.h>
-#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
-#include <QSettings>
-
-#include <unordered_map>
-
-static const char kStackExecutableKey[] = "Haskell/StackExecutable";
using namespace Utils;
-namespace Haskell {
-namespace Internal {
-
-class HaskellManagerPrivate
-{
-public:
- FilePath stackExecutable;
-};
-
-Q_GLOBAL_STATIC(HaskellManagerPrivate, m_d)
-Q_GLOBAL_STATIC(HaskellManager, m_instance)
-
-HaskellManager *HaskellManager::instance()
-{
- return m_instance;
-}
+namespace Haskell::Internal {
FilePath HaskellManager::findProjectDirectory(const FilePath &filePath)
{
@@ -57,28 +36,6 @@ FilePath HaskellManager::findProjectDirectory(const FilePath &filePath)
return {};
}
-FilePath defaultStackExecutable()
-{
- // stack from brew or the installer script from https://docs.haskellstack.org
- // install to /usr/local/bin.
- if (HostOsInfo::isAnyUnixHost())
- return FilePath::fromString("/usr/local/bin/stack");
- return FilePath::fromString("stack");
-}
-
-FilePath HaskellManager::stackExecutable()
-{
- return m_d->stackExecutable;
-}
-
-void HaskellManager::setStackExecutable(const FilePath &filePath)
-{
- if (filePath == m_d->stackExecutable)
- return;
- m_d->stackExecutable = filePath;
- emit m_instance->stackExecutableChanged(m_d->stackExecutable);
-}
-
void HaskellManager::openGhci(const FilePath &haskellFile)
{
const QList<MimeType> mimeTypes = mimeTypesForFileName(haskellFile.toString());
@@ -87,35 +44,11 @@ void HaskellManager::openGhci(const FilePath &haskellFile)
});
const auto args = QStringList{"ghci"}
+ (isHaskell ? QStringList{haskellFile.fileName()} : QStringList());
- auto p = new QtcProcess(m_instance);
- p->setTerminalMode(TerminalMode::On);
- p->setCommand({stackExecutable(), args});
- p->setWorkingDirectory(haskellFile.absolutePath());
- connect(p, &QtcProcess::done, p, [p] {
- if (p->result() != ProcessResult::FinishedWithSuccess) {
- Core::MessageManager::writeDisrupting(
- Tr::tr("Failed to run GHCi: \"%1\".").arg(p->errorString()));
- }
- p->deleteLater();
- });
- p->start();
-}
-
-void HaskellManager::readSettings(QSettings *settings)
-{
- m_d->stackExecutable = FilePath::fromString(
- settings->value(kStackExecutableKey,
- defaultStackExecutable().toString()).toString());
- emit m_instance->stackExecutableChanged(m_d->stackExecutable);
-}
-
-void HaskellManager::writeSettings(QSettings *settings)
-{
- if (m_d->stackExecutable == defaultStackExecutable())
- settings->remove(kStackExecutableKey);
- else
- settings->setValue(kStackExecutableKey, m_d->stackExecutable.toString());
+ Process p;
+ p.setTerminalMode(TerminalMode::Detached);
+ p.setCommand({settings().stackPath.filePath(), args});
+ p.setWorkingDirectory(haskellFile.absolutePath());
+ p.start();
}
-} // namespace Internal
-} // namespace Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellmanager.h b/src/plugins/haskell/haskellmanager.h
index da4018930d..862f46460f 100644
--- a/src/plugins/haskell/haskellmanager.h
+++ b/src/plugins/haskell/haskellmanager.h
@@ -7,30 +7,15 @@
#include <utils/fileutils.h>
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
+namespace Haskell::Internal {
-namespace Haskell {
-namespace Internal {
-
-class HaskellManager : public QObject
+class HaskellManager
{
- Q_OBJECT
-
public:
static HaskellManager *instance();
static Utils::FilePath findProjectDirectory(const Utils::FilePath &filePath);
- static Utils::FilePath stackExecutable();
- static void setStackExecutable(const Utils::FilePath &filePath);
static void openGhci(const Utils::FilePath &haskellFile);
- static void readSettings(QSettings *settings);
- static void writeSettings(QSettings *settings);
-
-signals:
- void stackExecutableChanged(const Utils::FilePath &filePath);
};
-} // namespace Internal
-} // namespace Haskell
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellplugin.cpp b/src/plugins/haskell/haskellplugin.cpp
index 334c104911..84a86f7db4 100644
--- a/src/plugins/haskell/haskellplugin.cpp
+++ b/src/plugins/haskell/haskellplugin.cpp
@@ -9,8 +9,8 @@
#include "haskellmanager.h"
#include "haskellproject.h"
#include "haskellrunconfiguration.h"
+#include "haskellsettings.h"
#include "haskelltr.h"
-#include "optionspage.h"
#include "stackbuildstep.h"
#include <coreplugin/actionmanager/actionmanager.h>
@@ -28,8 +28,8 @@ namespace Internal {
class HaskellPluginPrivate
{
public:
+ HaskellSettings settings;
HaskellEditorFactory editorFactory;
- OptionsPage optionsPage;
HaskellBuildConfigurationFactory buildConfigFactory;
StackBuildStepFactory stackBuildStepFactory;
HaskellRunConfigurationFactory runConfigFactory;
@@ -41,11 +41,11 @@ HaskellPlugin::~HaskellPlugin()
delete d;
}
-static void registerGhciAction()
+static void registerGhciAction(QObject *guard)
{
- QAction *action = new QAction(Tr::tr("Run GHCi"), HaskellManager::instance());
+ QAction *action = new QAction(Tr::tr("Run GHCi"), guard);
Core::ActionManager::registerAction(action, Constants::A_RUN_GHCI);
- QObject::connect(action, &QAction::triggered, HaskellManager::instance(), [] {
+ QObject::connect(action, &QAction::triggered, guard, [] {
if (Core::IDocument *doc = Core::EditorManager::currentDocument())
HaskellManager::openGhci(doc->filePath());
});
@@ -63,13 +63,7 @@ bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorStrin
TextEditor::SnippetProvider::registerGroup(Constants::C_HASKELLSNIPPETSGROUP_ID,
Tr::tr("Haskell", "SnippetProvider"));
- connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [] {
- HaskellManager::writeSettings(Core::ICore::settings());
- });
-
- registerGhciAction();
-
- HaskellManager::readSettings(Core::ICore::settings());
+ registerGhciAction(this);
ProjectExplorer::JsonWizardFactory::addWizardPath(":/haskell/share/wizards/");
return true;
diff --git a/src/plugins/haskell/haskellproject.cpp b/src/plugins/haskell/haskellproject.cpp
index 916632c5dc..f19840a67f 100644
--- a/src/plugins/haskell/haskellproject.cpp
+++ b/src/plugins/haskell/haskellproject.cpp
@@ -14,7 +14,6 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QFile>
#include <QTextStream>
diff --git a/src/plugins/haskell/haskellrunconfiguration.cpp b/src/plugins/haskell/haskellrunconfiguration.cpp
index 45b6e684bc..9c236c65b5 100644
--- a/src/plugins/haskell/haskellrunconfiguration.cpp
+++ b/src/plugins/haskell/haskellrunconfiguration.cpp
@@ -4,9 +4,9 @@
#include "haskellrunconfiguration.h"
#include "haskellconstants.h"
-#include "haskellmanager.h"
#include "haskellproject.h"
#include "haskelltr.h"
+#include "haskellsettings.h"
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/localenvironmentaspect.h>
@@ -68,7 +68,7 @@ Runnable HaskellRunConfiguration::runnable() const
r.workingDirectory = projectDirectory;
r.environment = aspect<LocalEnvironmentAspect>()->environment();
- r.command = {r.environment.searchInPath(HaskellManager::stackExecutable().toString()), args};
+ r.command = {r.environment.searchInPath(settings().stackPath()), args};
return r;
}
diff --git a/src/plugins/haskell/haskellsettings.cpp b/src/plugins/haskell/haskellsettings.cpp
new file mode 100644
index 0000000000..6b0ee17036
--- /dev/null
+++ b/src/plugins/haskell/haskellsettings.cpp
@@ -0,0 +1,63 @@
+// Copyright (c) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "haskellsettings.h"
+
+#include "haskellconstants.h"
+#include "haskelltr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/hostosinfo.h>
+#include <utils/pathchooser.h>
+#include <utils/layoutbuilder.h>
+
+using namespace Utils;
+
+namespace Haskell::Internal {
+
+static HaskellSettings *theSettings;
+
+HaskellSettings &settings()
+{
+ return *theSettings;
+}
+
+HaskellSettings::HaskellSettings()
+{
+ theSettings = this;
+
+ setId(Constants::OPTIONS_GENERAL);
+ setDisplayName(Tr::tr("General"));
+ setCategory("J.Z.Haskell");
+ setDisplayCategory(Tr::tr("Haskell"));
+ setCategoryIconPath(":/haskell/images/settingscategory_haskell.png");
+
+ registerAspect(&stackPath);
+ stackPath.setSettingsKey("Haskell/StackExecutable");
+ stackPath.setDisplayStyle(StringAspect::PathChooserDisplay);
+ stackPath.setExpectedKind(PathChooser::ExistingCommand);
+ stackPath.setPromptDialogTitle(Tr::tr("Choose Stack Executable"));
+ stackPath.setCommandVersionArguments({"--version"});
+
+ // stack from brew or the installer script from https://docs.haskellstack.org
+ // install to /usr/local/bin.
+ stackPath.setDefaultFilePath(HostOsInfo::isAnyUnixHost()
+ ? FilePath::fromString("/usr/local/bin/stack")
+ : FilePath::fromString("stack"));
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ Column {
+ Group {
+ title(Tr::tr("General")),
+ Row { Tr::tr("Stack executable:"), stackPath }
+ },
+ st,
+ }.attachTo(widget);
+ });
+
+ readSettings(Core::ICore::settings());
+}
+
+} // Haskell::Internal
diff --git a/src/plugins/haskell/haskellsettings.h b/src/plugins/haskell/haskellsettings.h
new file mode 100644
index 0000000000..c697a154ee
--- /dev/null
+++ b/src/plugins/haskell/haskellsettings.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Haskell::Internal {
+
+class HaskellSettings : public Core::PagedSettings
+{
+public:
+ HaskellSettings();
+
+ Utils::StringAspect stackPath;
+};
+
+HaskellSettings &settings();
+
+} // Haskell::Internal
diff --git a/src/plugins/haskell/images/category_haskell.png b/src/plugins/haskell/images/category_haskell.png
deleted file mode 100644
index 947f948bd9..0000000000
--- a/src/plugins/haskell/images/category_haskell.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/haskell/images/settingscategory_haskell.png b/src/plugins/haskell/images/settingscategory_haskell.png
new file mode 100644
index 0000000000..5bd3f69fa8
--- /dev/null
+++ b/src/plugins/haskell/images/settingscategory_haskell.png
Binary files differ
diff --git a/src/plugins/haskell/images/settingscategory_haskell@2x.png b/src/plugins/haskell/images/settingscategory_haskell@2x.png
new file mode 100644
index 0000000000..1eb20959d5
--- /dev/null
+++ b/src/plugins/haskell/images/settingscategory_haskell@2x.png
Binary files differ
diff --git a/src/plugins/haskell/optionspage.cpp b/src/plugins/haskell/optionspage.cpp
deleted file mode 100644
index e64a601234..0000000000
--- a/src/plugins/haskell/optionspage.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "optionspage.h"
-
-#include "haskellconstants.h"
-#include "haskellmanager.h"
-#include "haskelltr.h"
-
-#include <QGroupBox>
-#include <QHBoxLayout>
-#include <QLabel>
-#include <QVBoxLayout>
-#include <QWidget>
-
-namespace Haskell {
-namespace Internal {
-
-OptionsPage::OptionsPage()
-{
- setId(Constants::OPTIONS_GENERAL);
- setDisplayName(Tr::tr("General"));
- setCategory("J.Z.Haskell");
- setDisplayCategory(Tr::tr("Haskell"));
- setCategoryIcon(Utils::Icon(":/haskell/images/category_haskell.png"));
-}
-
-QWidget *OptionsPage::widget()
-{
- using namespace Utils;
- if (!m_widget) {
- m_widget = new QWidget;
- auto topLayout = new QVBoxLayout;
- m_widget->setLayout(topLayout);
- auto generalBox = new QGroupBox(Tr::tr("General"));
- topLayout->addWidget(generalBox);
- topLayout->addStretch(10);
- auto boxLayout = new QHBoxLayout;
- generalBox->setLayout(boxLayout);
- boxLayout->addWidget(new QLabel(Tr::tr("Stack executable:")));
- m_stackPath = new PathChooser();
- m_stackPath->setExpectedKind(PathChooser::ExistingCommand);
- m_stackPath->setPromptDialogTitle(Tr::tr("Choose Stack Executable"));
- m_stackPath->setFilePath(HaskellManager::stackExecutable());
- m_stackPath->setCommandVersionArguments({"--version"});
- boxLayout->addWidget(m_stackPath);
- }
- return m_widget;
-}
-
-void OptionsPage::apply()
-{
- if (!m_widget)
- return;
- HaskellManager::setStackExecutable(m_stackPath->rawFilePath());
-}
-
-void OptionsPage::finish()
-{
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/src/plugins/haskell/optionspage.h b/src/plugins/haskell/optionspage.h
deleted file mode 100644
index a477d72dfc..0000000000
--- a/src/plugins/haskell/optionspage.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/pathchooser.h>
-
-#include <QPointer>
-
-namespace Haskell {
-namespace Internal {
-
-class OptionsPage : public Core::IOptionsPage
-{
- Q_OBJECT
-
-public:
- OptionsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<QWidget> m_widget;
- QPointer<Utils::PathChooser> m_stackPath;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/src/plugins/haskell/stackbuildstep.cpp b/src/plugins/haskell/stackbuildstep.cpp
index 084ba8af4a..860ef458c5 100644
--- a/src/plugins/haskell/stackbuildstep.cpp
+++ b/src/plugins/haskell/stackbuildstep.cpp
@@ -4,7 +4,7 @@
#include "stackbuildstep.h"
#include "haskellconstants.h"
-#include "haskellmanager.h"
+#include "haskellsettings.h"
#include "haskelltr.h"
#include <projectexplorer/buildconfiguration.h>
@@ -38,7 +38,7 @@ bool StackBuildStep::init()
if (AbstractProcessStep::init()) {
const auto projectDir = QDir(project()->projectDirectory().toString());
processParameters()->setCommandLine(
- {HaskellManager::stackExecutable(),
+ {settings().stackPath.filePath(),
{"build", "--work-dir", projectDir.relativeFilePath(buildDirectory().toString())}});
processParameters()->setEnvironment(buildEnvironment());
}
diff --git a/src/plugins/help/CMakeLists.txt b/src/plugins/help/CMakeLists.txt
index c37b5b0c31..3034781739 100644
--- a/src/plugins/help/CMakeLists.txt
+++ b/src/plugins/help/CMakeLists.txt
@@ -12,7 +12,6 @@ add_qtc_plugin(Help
helpfindsupport.cpp helpfindsupport.h
helpindexfilter.cpp helpindexfilter.h
helpmanager.cpp helpmanager.h
- helpmode.cpp helpmode.h
helpplugin.cpp helpplugin.h
helptr.h
helpviewer.cpp helpviewer.h
@@ -47,7 +46,7 @@ extend_qtc_plugin(Help
)
option(BUILD_HELPVIEWERBACKEND_QTWEBENGINE "Build QtWebEngine based help viewer backend." YES)
-find_package(Qt5 COMPONENTS WebEngineWidgets QUIET)
+find_package(Qt6 COMPONENTS WebEngineWidgets QUIET)
extend_qtc_plugin(Help
CONDITION BUILD_HELPVIEWERBACKEND_QTWEBENGINE AND TARGET Qt::WebEngineWidgets
FEATURE_INFO "QtWebEngine help viewer"
diff --git a/src/plugins/help/filtersettingspage.cpp b/src/plugins/help/filtersettingspage.cpp
index 771c39fc68..e227b025ef 100644
--- a/src/plugins/help/filtersettingspage.cpp
+++ b/src/plugins/help/filtersettingspage.cpp
@@ -7,56 +7,56 @@
#include "helptr.h"
#include "localhelpmanager.h"
+#include <utils/layoutbuilder.h>
+
#include <QHelpFilterEngine>
#include <QHelpFilterSettingsWidget>
#include <QVersionNumber>
-using namespace Help::Internal;
-
-FilterSettingsPage::FilterSettingsPage()
-{
- setId("D.Filters");
- setDisplayName(Tr::tr("Filters"));
- setCategory(Help::Constants::HELP_CATEGORY);
-}
+namespace Help::Internal {
-QWidget *FilterSettingsPage::widget()
+class FilterSettingsPageWidget : public Core::IOptionsPageWidget
{
- if (!m_widget) {
+public:
+ FilterSettingsPageWidget(const std::function<void()> &onChanged)
+ {
LocalHelpManager::setupGuiHelpEngine();
- m_widget = new QHelpFilterSettingsWidget(nullptr);
- m_widget->readSettings(LocalHelpManager::filterEngine());
- connect(Core::HelpManager::Signals::instance(),
- &Core::HelpManager::Signals::documentationChanged,
- this,
- &FilterSettingsPage::updateFilterPage);
+ auto widget = new QHelpFilterSettingsWidget;
+ widget->readSettings(LocalHelpManager::filterEngine());
- updateFilterPage();
- }
- return m_widget;
-}
+ Layouting::Column {
+ Layouting::noMargin,
+ widget
+ }.attachTo(this);
-void FilterSettingsPage::apply()
-{
- if (m_widget->applySettings(LocalHelpManager::filterEngine()))
- emit filtersChanged();
+ auto updateFilterPage = [widget] {
+ widget->setAvailableComponents(LocalHelpManager::filterEngine()->availableComponents());
+ widget->setAvailableVersions(LocalHelpManager::filterEngine()->availableVersions());
+ };
- m_widget->readSettings(LocalHelpManager::filterEngine());
-}
+ auto connection = connect(Core::HelpManager::Signals::instance(),
+ &Core::HelpManager::Signals::documentationChanged,
+ this,
+ updateFilterPage);
+ updateFilterPage();
-void FilterSettingsPage::finish()
-{
- disconnect(Core::HelpManager::Signals::instance(),
- &Core::HelpManager::Signals::documentationChanged,
- this,
- &FilterSettingsPage::updateFilterPage);
- delete m_widget;
-}
+ setOnApply([widget, onChanged] {
+ if (widget->applySettings(LocalHelpManager::filterEngine()))
+ onChanged();
+ widget->readSettings(LocalHelpManager::filterEngine());
+ });
-void FilterSettingsPage::updateFilterPage()
+ setOnFinish([connection] { disconnect(connection); });
+ }
+};
+
+FilterSettingsPage::FilterSettingsPage(const std::function<void ()> &onChanged)
{
- m_widget->setAvailableComponents(LocalHelpManager::filterEngine()->availableComponents());
- m_widget->setAvailableVersions(LocalHelpManager::filterEngine()->availableVersions());
+ setId("D.Filters");
+ setDisplayName(Tr::tr("Filters"));
+ setCategory(Help::Constants::HELP_CATEGORY);
+ setWidgetCreator([onChanged] { return new FilterSettingsPageWidget(onChanged); });
}
+} // Help::Internal
diff --git a/src/plugins/help/filtersettingspage.h b/src/plugins/help/filtersettingspage.h
index 308a489f26..54256b1d7f 100644
--- a/src/plugins/help/filtersettingspage.h
+++ b/src/plugins/help/filtersettingspage.h
@@ -5,35 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
-QT_BEGIN_NAMESPACE
-class QHelpFilterSettingsWidget;
-QT_END_NAMESPACE
-
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
class FilterSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
- FilterSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-signals:
- void filtersChanged();
-
-private:
-
- void updateFilterPage();
- QPointer<QHelpFilterSettingsWidget> m_widget;
-
+ explicit FilterSettingsPage(const std::function<void()> &onChanged);
};
-} // namespace Help
-} // namespace Internal
+} // Help::Internal
diff --git a/src/plugins/help/generalsettingspage.cpp b/src/plugins/help/generalsettingspage.cpp
index a05a0db3ee..d5f5756ed6 100644
--- a/src/plugins/help/generalsettingspage.cpp
+++ b/src/plugins/help/generalsettingspage.cpp
@@ -23,6 +23,7 @@
#include <QComboBox>
#include <QCoreApplication>
#include <QFontComboBox>
+#include <QFontDatabase>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
@@ -35,19 +36,42 @@
#include <QTextStream>
#include <QVBoxLayout>
-#include <QApplication>
-
using namespace Core;
using namespace Utils;
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
-class GeneralSettingsPageWidget : public QWidget
+class GeneralSettingsPageWidget : public IOptionsPageWidget
{
public:
GeneralSettingsPageWidget();
+private:
+ void apply() final;
+
+ void setCurrentPage();
+ void setBlankPage();
+ void setDefaultPage();
+ void importBookmarks();
+ void exportBookmarks();
+
+ void updateFontSizeSelector();
+ void updateFontStyleSelector();
+ void updateFontFamilySelector();
+ void updateFont();
+ int closestPointSizeIndex(int desiredPointSize) const;
+
+ QFont m_font;
+ int m_fontZoom = 100;
+ QFontDatabase m_fontDatabase;
+
+ QString m_homePage;
+ int m_contextOption;
+
+ int m_startOption;
+ bool m_returnOnClose;
+ bool m_scrollWheelZoomingEnabled;
+
QSpinBox *zoomSpinBox;
QFontComboBox *familyComboBox;
QComboBox *styleComboBox;
@@ -62,7 +86,7 @@ public:
QPushButton *importButton;
QPushButton *exportButton;
QCheckBox *scrollWheelZooming;
- QCheckBox *m_returnOnClose;
+ QCheckBox *m_returnOnCloseCheckBox;
QComboBox *viewerBackend;
};
@@ -152,8 +176,8 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget()
auto behaviourGroupBox = new QGroupBox(Tr::tr("Behaviour"));
scrollWheelZooming = new QCheckBox(Tr::tr("Enable scroll wheel zooming"));
- m_returnOnClose = new QCheckBox(Tr::tr("Return to editor on closing the last page"));
- m_returnOnClose->setToolTip(
+ m_returnOnCloseCheckBox = new QCheckBox(Tr::tr("Return to editor on closing the last page"));
+ m_returnOnCloseCheckBox->setToolTip(
Tr::tr("Switches to editor context after last help page is closed."));
auto viewerBackendLabel = new QLabel(Tr::tr("Viewer backend:"));
@@ -172,7 +196,7 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget()
auto behaviourGroupBoxLayout = new QVBoxLayout;
behaviourGroupBox->setLayout(behaviourGroupBoxLayout);
behaviourGroupBoxLayout->addWidget(scrollWheelZooming);
- behaviourGroupBoxLayout->addWidget(m_returnOnClose);
+ behaviourGroupBoxLayout->addWidget(m_returnOnCloseCheckBox);
behaviourGroupBoxLayout->addLayout(viewerBackendLayout);
// bookmarks
@@ -203,167 +227,145 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget()
mainLayout->addWidget(behaviourGroupBox);
mainLayout->addLayout(bookmarksLayout);
mainLayout->addStretch(1);
-}
-GeneralSettingsPage::GeneralSettingsPage()
-{
- setId("A.General settings");
- setDisplayName(Tr::tr("General"));
- setCategory(Help::Constants::HELP_CATEGORY);
- setDisplayCategory(Tr::tr("Help"));
- setCategoryIconPath(":/help/images/settingscategory_help.png");
-}
+ m_font = LocalHelpManager::fallbackFont();
+ m_fontZoom = LocalHelpManager::fontZoom();
+ zoomSpinBox->setValue(m_fontZoom);
-QWidget *GeneralSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new GeneralSettingsPageWidget;
+ updateFontSizeSelector();
+ updateFontStyleSelector();
+ updateFontFamilySelector();
- m_font = LocalHelpManager::fallbackFont();
- m_fontZoom = LocalHelpManager::fontZoom();
- m_widget->zoomSpinBox->setValue(m_fontZoom);
+ connect(familyComboBox, &QFontComboBox::currentFontChanged, this, [this] {
+ updateFont();
+ updateFontStyleSelector();
+ updateFontSizeSelector();
+ updateFont(); // changes that might have happened when updating the selectors
+ });
+ connect(styleComboBox, &QComboBox::currentIndexChanged, this, [this] {
+ updateFont();
updateFontSizeSelector();
- updateFontStyleSelector();
- updateFontFamilySelector();
-
- connect(m_widget->familyComboBox, &QFontComboBox::currentFontChanged, this, [this] {
- updateFont();
- updateFontStyleSelector();
- updateFontSizeSelector();
- updateFont(); // changes that might have happened when updating the selectors
- });
-
- connect(m_widget->styleComboBox, &QComboBox::currentIndexChanged, this, [this] {
- updateFont();
- updateFontSizeSelector();
- updateFont(); // changes that might have happened when updating the selectors
- });
-
- connect(m_widget->sizeComboBox, &QComboBox::currentIndexChanged,
- this, &GeneralSettingsPage::updateFont);
-
- connect(m_widget->zoomSpinBox, &QSpinBox::valueChanged,
- this, [this](int value) { m_fontZoom = value; });
-
- m_homePage = LocalHelpManager::homePage();
- m_widget->homePageLineEdit->setText(m_homePage);
-
- m_startOption = LocalHelpManager::startOption();
- m_widget->helpStartComboBox->setCurrentIndex(m_startOption);
-
- m_contextOption = LocalHelpManager::contextHelpOption();
- m_widget->contextHelpComboBox->setCurrentIndex(m_contextOption);
-
- connect(m_widget->currentPageButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::setCurrentPage);
- connect(m_widget->blankPageButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::setBlankPage);
- connect(m_widget->defaultPageButton,
- &QPushButton::clicked,
- this,
- &GeneralSettingsPage::setDefaultPage);
-
- HelpViewer *viewer = HelpPlugin::modeHelpWidget()->currentViewer();
- if (!viewer)
- m_widget->currentPageButton->setEnabled(false);
-
- m_widget->errorLabel->setVisible(false);
- connect(m_widget->importButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::importBookmarks);
- connect(m_widget->exportButton, &QPushButton::clicked,
- this, &GeneralSettingsPage::exportBookmarks);
-
- m_returnOnClose = LocalHelpManager::returnOnClose();
- m_widget->m_returnOnClose->setChecked(m_returnOnClose);
-
- m_scrollWheelZoomingEnabled = LocalHelpManager::isScrollWheelZoomingEnabled();
- m_widget->scrollWheelZooming->setChecked(m_scrollWheelZoomingEnabled);
-
- m_widget->viewerBackend->addItem(
- Tr::tr("Default (%1)", "Default viewer backend")
- .arg(LocalHelpManager::defaultViewerBackend().displayName));
- const QByteArray currentBackend = LocalHelpManager::viewerBackendId();
- const QVector<HelpViewerFactory> backends = LocalHelpManager::viewerBackends();
- for (const HelpViewerFactory &f : backends) {
- m_widget->viewerBackend->addItem(f.displayName, f.id);
- if (f.id == currentBackend)
- m_widget->viewerBackend->setCurrentIndex(m_widget->viewerBackend->count() - 1);
- }
- if (backends.size() == 1)
- m_widget->viewerBackend->setEnabled(false);
+ updateFont(); // changes that might have happened when updating the selectors
+ });
+
+ connect(sizeComboBox, &QComboBox::currentIndexChanged,
+ this, &GeneralSettingsPageWidget::updateFont);
+
+ connect(zoomSpinBox, &QSpinBox::valueChanged,
+ this, [this](int value) { m_fontZoom = value; });
+
+ m_homePage = LocalHelpManager::homePage();
+ homePageLineEdit->setText(m_homePage);
+
+ m_startOption = LocalHelpManager::startOption();
+ helpStartComboBox->setCurrentIndex(m_startOption);
+
+ m_contextOption = LocalHelpManager::contextHelpOption();
+ contextHelpComboBox->setCurrentIndex(m_contextOption);
+
+ connect(currentPageButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::setCurrentPage);
+ connect(blankPageButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::setBlankPage);
+ connect(defaultPageButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::setDefaultPage);
+
+ HelpViewer *viewer = HelpPlugin::modeHelpWidget()->currentViewer();
+ if (!viewer)
+ currentPageButton->setEnabled(false);
+
+ errorLabel->setVisible(false);
+ connect(importButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::importBookmarks);
+ connect(exportButton, &QPushButton::clicked,
+ this, &GeneralSettingsPageWidget::exportBookmarks);
+
+ m_returnOnClose = LocalHelpManager::returnOnClose();
+ m_returnOnCloseCheckBox->setChecked(m_returnOnClose);
+
+ m_scrollWheelZoomingEnabled = LocalHelpManager::isScrollWheelZoomingEnabled();
+ scrollWheelZooming->setChecked(m_scrollWheelZoomingEnabled);
+
+ viewerBackend->addItem(
+ Tr::tr("Default (%1)", "Default viewer backend")
+ .arg(LocalHelpManager::defaultViewerBackend().displayName));
+ const QByteArray currentBackend = LocalHelpManager::viewerBackendId();
+ const QVector<HelpViewerFactory> backends = LocalHelpManager::viewerBackends();
+ for (const HelpViewerFactory &f : backends) {
+ viewerBackend->addItem(f.displayName, f.id);
+ if (f.id == currentBackend)
+ viewerBackend->setCurrentIndex(viewerBackend->count() - 1);
}
- return m_widget;
+ if (backends.size() == 1)
+ viewerBackend->setEnabled(false);
}
-void GeneralSettingsPage::apply()
+void GeneralSettingsPageWidget::apply()
{
- if (!m_widget) // page was never shown
- return;
-
if (m_font != LocalHelpManager::fallbackFont())
LocalHelpManager::setFallbackFont(m_font);
if (m_fontZoom != LocalHelpManager::fontZoom())
LocalHelpManager::setFontZoom(m_fontZoom);
- QString homePage = QUrl::fromUserInput(m_widget->homePageLineEdit->text()).toString();
+ QString homePage = QUrl::fromUserInput(homePageLineEdit->text()).toString();
if (homePage.isEmpty())
homePage = Help::Constants::AboutBlank;
- m_widget->homePageLineEdit->setText(homePage);
+ homePageLineEdit->setText(homePage);
if (m_homePage != homePage) {
m_homePage = homePage;
LocalHelpManager::setHomePage(homePage);
}
- const int startOption = m_widget->helpStartComboBox->currentIndex();
+ const int startOption = helpStartComboBox->currentIndex();
if (m_startOption != startOption) {
m_startOption = startOption;
LocalHelpManager::setStartOption(LocalHelpManager::StartOption(m_startOption));
}
- const int helpOption = m_widget->contextHelpComboBox->currentIndex();
+ const int helpOption = contextHelpComboBox->currentIndex();
if (m_contextOption != helpOption) {
m_contextOption = helpOption;
LocalHelpManager::setContextHelpOption(HelpManager::HelpViewerLocation(m_contextOption));
}
- const bool close = m_widget->m_returnOnClose->isChecked();
+ const bool close = m_returnOnCloseCheckBox->isChecked();
if (m_returnOnClose != close) {
m_returnOnClose = close;
LocalHelpManager::setReturnOnClose(m_returnOnClose);
}
- const bool zoom = m_widget->scrollWheelZooming->isChecked();
+ const bool zoom = scrollWheelZooming->isChecked();
if (m_scrollWheelZoomingEnabled != zoom) {
m_scrollWheelZoomingEnabled = zoom;
LocalHelpManager::setScrollWheelZoomingEnabled(m_scrollWheelZoomingEnabled);
}
- const QByteArray viewerBackendId = m_widget->viewerBackend->currentData().toByteArray();
+ const QByteArray viewerBackendId = viewerBackend->currentData().toByteArray();
LocalHelpManager::setViewerBackendId(viewerBackendId);
}
-void GeneralSettingsPage::setCurrentPage()
+void GeneralSettingsPageWidget::setCurrentPage()
{
HelpViewer *viewer = HelpPlugin::modeHelpWidget()->currentViewer();
if (viewer)
- m_widget->homePageLineEdit->setText(viewer->source().toString());
+ homePageLineEdit->setText(viewer->source().toString());
}
-void GeneralSettingsPage::setBlankPage()
+void GeneralSettingsPageWidget::setBlankPage()
{
- m_widget->homePageLineEdit->setText(Help::Constants::AboutBlank);
+ homePageLineEdit->setText(Help::Constants::AboutBlank);
}
-void GeneralSettingsPage::setDefaultPage()
+void GeneralSettingsPageWidget::setDefaultPage()
{
- m_widget->homePageLineEdit->setText(LocalHelpManager::defaultHomePage());
+ homePageLineEdit->setText(LocalHelpManager::defaultHomePage());
}
-void GeneralSettingsPage::importBookmarks()
+void GeneralSettingsPageWidget::importBookmarks()
{
- m_widget->errorLabel->setVisible(false);
+ errorLabel->setVisible(false);
FilePath filePath = FileUtils::getOpenFilePath(nullptr,
Tr::tr("Import Bookmarks"),
@@ -381,13 +383,13 @@ void GeneralSettingsPage::importBookmarks()
return;
}
- m_widget->errorLabel->setVisible(true);
- m_widget->errorLabel->setText(Tr::tr("Cannot import bookmarks."));
+ errorLabel->setVisible(true);
+ errorLabel->setText(Tr::tr("Cannot import bookmarks."));
}
-void GeneralSettingsPage::exportBookmarks()
+void GeneralSettingsPageWidget::exportBookmarks()
{
- m_widget->errorLabel->setVisible(false);
+ errorLabel->setVisible(false);
FilePath filePath = FileUtils::getSaveFilePath(nullptr,
Tr::tr("Save File"),
@@ -405,12 +407,12 @@ void GeneralSettingsPage::exportBookmarks()
saver.setResult(&writer);
}
if (!saver.finalize()) {
- m_widget->errorLabel->setVisible(true);
- m_widget->errorLabel->setText(saver.errorString());
+ errorLabel->setVisible(true);
+ errorLabel->setText(saver.errorString());
}
}
-void GeneralSettingsPage::updateFontSizeSelector()
+void GeneralSettingsPageWidget::updateFontSizeSelector()
{
const QString &family = m_font.family();
const QString &fontStyle = m_fontDatabase.styleString(m_font);
@@ -419,81 +421,81 @@ void GeneralSettingsPage::updateFontSizeSelector()
if (pointSizes.empty())
pointSizes = QFontDatabase::standardSizes();
- QSignalBlocker blocker(m_widget->sizeComboBox);
- m_widget->sizeComboBox->clear();
- m_widget->sizeComboBox->setCurrentIndex(-1);
- m_widget->sizeComboBox->setEnabled(!pointSizes.empty());
+ QSignalBlocker blocker(sizeComboBox);
+ sizeComboBox->clear();
+ sizeComboBox->setCurrentIndex(-1);
+ sizeComboBox->setEnabled(!pointSizes.empty());
// try to maintain selection or select closest.
if (!pointSizes.empty()) {
QString n;
for (int pointSize : std::as_const(pointSizes))
- m_widget->sizeComboBox->addItem(n.setNum(pointSize), QVariant(pointSize));
+ sizeComboBox->addItem(n.setNum(pointSize), QVariant(pointSize));
const int closestIndex = closestPointSizeIndex(m_font.pointSize());
if (closestIndex != -1)
- m_widget->sizeComboBox->setCurrentIndex(closestIndex);
+ sizeComboBox->setCurrentIndex(closestIndex);
}
}
-void GeneralSettingsPage::updateFontStyleSelector()
+void GeneralSettingsPageWidget::updateFontStyleSelector()
{
const QString &fontStyle = m_fontDatabase.styleString(m_font);
const QStringList &styles = m_fontDatabase.styles(m_font.family());
- QSignalBlocker blocker(m_widget->styleComboBox);
- m_widget->styleComboBox->clear();
- m_widget->styleComboBox->setCurrentIndex(-1);
- m_widget->styleComboBox->setEnabled(!styles.empty());
+ QSignalBlocker blocker(styleComboBox);
+ styleComboBox->clear();
+ styleComboBox->setCurrentIndex(-1);
+ styleComboBox->setEnabled(!styles.empty());
if (!styles.empty()) {
int normalIndex = -1;
const QString normalStyle = "Normal";
for (const QString &style : styles) {
// try to maintain selection or select 'normal' preferably
- const int newIndex = m_widget->styleComboBox->count();
- m_widget->styleComboBox->addItem(style);
+ const int newIndex = styleComboBox->count();
+ styleComboBox->addItem(style);
if (fontStyle == style) {
- m_widget->styleComboBox->setCurrentIndex(newIndex);
+ styleComboBox->setCurrentIndex(newIndex);
} else {
if (fontStyle == normalStyle)
normalIndex = newIndex;
}
}
- if (m_widget->styleComboBox->currentIndex() == -1 && normalIndex != -1)
- m_widget->styleComboBox->setCurrentIndex(normalIndex);
+ if (styleComboBox->currentIndex() == -1 && normalIndex != -1)
+ styleComboBox->setCurrentIndex(normalIndex);
}
}
-void GeneralSettingsPage::updateFontFamilySelector()
+void GeneralSettingsPageWidget::updateFontFamilySelector()
{
- m_widget->familyComboBox->setCurrentFont(m_font);
+ familyComboBox->setCurrentFont(m_font);
}
-void GeneralSettingsPage::updateFont()
+void GeneralSettingsPageWidget::updateFont()
{
- const QString &family = m_widget->familyComboBox->currentFont().family();
+ const QString &family = familyComboBox->currentFont().family();
m_font.setFamily(family);
int fontSize = 14;
- int currentIndex = m_widget->sizeComboBox->currentIndex();
+ int currentIndex = sizeComboBox->currentIndex();
if (currentIndex != -1)
- fontSize = m_widget->sizeComboBox->itemData(currentIndex).toInt();
+ fontSize = sizeComboBox->itemData(currentIndex).toInt();
m_font.setPointSize(fontSize);
- currentIndex = m_widget->styleComboBox->currentIndex();
+ currentIndex = styleComboBox->currentIndex();
if (currentIndex != -1)
- m_font.setStyleName(m_widget->styleComboBox->itemText(currentIndex));
+ m_font.setStyleName(styleComboBox->itemText(currentIndex));
}
-int GeneralSettingsPage::closestPointSizeIndex(int desiredPointSize) const
+int GeneralSettingsPageWidget::closestPointSizeIndex(int desiredPointSize) const
{
// try to maintain selection or select closest.
int closestIndex = -1;
int closestAbsError = 0xFFFF;
- const int pointSizeCount = m_widget->sizeComboBox->count();
+ const int pointSizeCount = sizeComboBox->count();
for (int i = 0; i < pointSizeCount; i++) {
- const int itemPointSize = m_widget->sizeComboBox->itemData(i).toInt();
+ const int itemPointSize = sizeComboBox->itemData(i).toInt();
const int absError = qAbs(desiredPointSize - itemPointSize);
if (absError < closestAbsError) {
closestIndex = i;
@@ -508,10 +510,16 @@ int GeneralSettingsPage::closestPointSizeIndex(int desiredPointSize) const
return closestIndex;
}
-void GeneralSettingsPage::finish()
+// GeneralSettingPage
+
+GeneralSettingsPage::GeneralSettingsPage()
{
- delete m_widget;
+ setId("A.General settings");
+ setDisplayName(Tr::tr("General"));
+ setCategory(Help::Constants::HELP_CATEGORY);
+ setDisplayCategory(Tr::tr("Help"));
+ setCategoryIconPath(":/help/images/settingscategory_help.png");
+ setWidgetCreator([] { return new GeneralSettingsPageWidget; });
}
-} // Internal
-} // Help
+} // Help::Interal
diff --git a/src/plugins/help/generalsettingspage.h b/src/plugins/help/generalsettingspage.h
index 4965cac9ea..ff5f6313ac 100644
--- a/src/plugins/help/generalsettingspage.h
+++ b/src/plugins/help/generalsettingspage.h
@@ -5,51 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QFontDatabase>
-#include <QPointer>
-
-namespace Help {
-namespace Internal {
-
-class GeneralSettingsPageWidget;
+namespace Help::Internal {
class GeneralSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
GeneralSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- void setCurrentPage();
- void setBlankPage();
- void setDefaultPage();
- void importBookmarks();
- void exportBookmarks();
-
- void updateFontSizeSelector();
- void updateFontStyleSelector();
- void updateFontFamilySelector();
- void updateFont();
- int closestPointSizeIndex(int desiredPointSize) const;
-
- QFont m_font;
- int m_fontZoom = 100;
- QFontDatabase m_fontDatabase;
-
- QString m_homePage;
- int m_contextOption;
-
- int m_startOption;
- bool m_returnOnClose;
- bool m_scrollWheelZoomingEnabled;
-
- QPointer<GeneralSettingsPageWidget> m_widget;
};
- } // Internal
-} // Help
+} // Help::Internal
diff --git a/src/plugins/help/help.qbs b/src/plugins/help/help.qbs
index 25d85bdc37..d96cfa154e 100644
--- a/src/plugins/help/help.qbs
+++ b/src/plugins/help/help.qbs
@@ -43,7 +43,6 @@ Project {
"helpfindsupport.cpp", "helpfindsupport.h",
"helpindexfilter.cpp", "helpindexfilter.h",
"helpmanager.cpp", "helpmanager.h",
- "helpmode.cpp", "helpmode.h",
"helpplugin.cpp", "helpplugin.h",
"helpviewer.cpp", "helpviewer.h",
"helpwidget.cpp", "helpwidget.h",
diff --git a/src/plugins/help/helpindexfilter.cpp b/src/plugins/help/helpindexfilter.cpp
index fe1c34a48e..9da1921480 100644
--- a/src/plugins/help/helpindexfilter.cpp
+++ b/src/plugins/help/helpindexfilter.cpp
@@ -3,30 +3,34 @@
#include "helpindexfilter.h"
+#include "localhelpmanager.h"
#include "helpmanager.h"
+#include "helpplugin.h"
#include "helptr.h"
-#include "localhelpmanager.h"
-#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
+#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
+#include <utils/async.h>
#include <utils/utilsicons.h>
#include <QHelpEngine>
#include <QHelpFilterEngine>
#include <QHelpLink>
-#include <QIcon>
using namespace Core;
using namespace Help;
using namespace Help::Internal;
+using namespace Tasking;
+using namespace Utils;
HelpIndexFilter::HelpIndexFilter()
{
setId("HelpIndexFilter");
setDisplayName(Tr::tr("Help Index"));
- setDefaultIncludedByDefault(false);
+ setDescription(Tr::tr("Locates help topics, for example in the Qt documentation."));
setDefaultShortcutString("?");
+ setRefreshRecipe(Sync([this] { invalidateCache(); }));
m_icon = Utils::Icons::BOOKMARK.icon();
connect(Core::HelpManager::Signals::instance(), &Core::HelpManager::Signals::setupFinished,
@@ -39,82 +43,69 @@ HelpIndexFilter::HelpIndexFilter()
this, &HelpIndexFilter::invalidateCache);
}
-HelpIndexFilter::~HelpIndexFilter() = default;
-
-bool HelpIndexFilter::updateCache(QFutureInterface<LocatorFilterEntry> &future,
- const QStringList &cache, const QString &entry)
+static void matches(QPromise<QStringList> &promise, const LocatorStorage &storage,
+ const QStringList &cache, const QIcon &icon)
{
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
+ const QString input = storage.input();
+ const Qt::CaseSensitivity cs = ILocatorFilter::caseSensitivity(input);
QStringList bestKeywords;
QStringList worseKeywords;
bestKeywords.reserve(cache.size());
worseKeywords.reserve(cache.size());
for (const QString &keyword : cache) {
- if (future.isCanceled())
- return false;
- if (keyword.startsWith(entry, cs))
+ if (promise.isCanceled())
+ return;
+ if (keyword.startsWith(input, cs))
bestKeywords.append(keyword);
- else if (keyword.contains(entry, cs))
+ else if (keyword.contains(input, cs))
worseKeywords.append(keyword);
}
- bestKeywords << worseKeywords;
- m_lastIndicesCache = bestKeywords;
- m_lastEntry = entry;
-
- return true;
-}
-
-QList<LocatorFilterEntry> HelpIndexFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
-{
- if (m_needsUpdate.exchange(false)) {
- QStringList indices;
- QMetaObject::invokeMethod(this, [this] { return allIndices(); },
- Qt::BlockingQueuedConnection, &indices);
- m_allIndicesCache = indices;
- // force updating the cache taking the m_allIndicesCache
- m_lastIndicesCache = QStringList();
- m_lastEntry = QString();
- }
-
- const QStringList cacheBase = m_lastEntry.isEmpty() || !entry.contains(m_lastEntry)
- ? m_allIndicesCache : m_lastIndicesCache;
-
- if (!updateCache(future, cacheBase, entry))
- return QList<LocatorFilterEntry>();
-
- const Qt::CaseSensitivity cs = caseSensitivity(entry);
- QList<LocatorFilterEntry> entries;
- for (const QString &keyword : std::as_const(m_lastIndicesCache)) {
- const int index = keyword.indexOf(entry, 0, cs);
- LocatorFilterEntry filterEntry(this, keyword, {}, m_icon);
- filterEntry.highlightInfo = {index, int(entry.length())};
+ const QStringList lastIndicesCache = bestKeywords + worseKeywords;
+
+ LocatorFilterEntries entries;
+ for (const QString &key : lastIndicesCache) {
+ if (promise.isCanceled())
+ return;
+ const int index = key.indexOf(input, 0, cs);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = key;
+ filterEntry.acceptor = [key] {
+ HelpPlugin::showLinksInCurrentViewer(LocalHelpManager::linksForKeyword(key), key);
+ return AcceptResult();
+ };
+ filterEntry.displayIcon = icon;
+ filterEntry.highlightInfo = {index, int(input.length())};
entries.append(filterEntry);
}
-
- return entries;
-}
-
-void HelpIndexFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- const QString &key = selection.displayName;
- const QMultiMap<QString, QUrl> links = LocalHelpManager::linksForKeyword(key);
- emit linksActivated(links, key);
-}
-
-void HelpIndexFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- invalidateCache();
+ storage.reportOutput(entries);
+ promise.addResult(lastIndicesCache);
}
-QStringList HelpIndexFilter::allIndices() const
+LocatorMatcherTasks HelpIndexFilter::matchers()
{
- LocalHelpManager::setupGuiHelpEngine();
- return LocalHelpManager::filterEngine()->indices(QString());
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [this, storage](Async<QStringList> &async) {
+ if (m_needsUpdate) {
+ m_needsUpdate = false;
+ LocalHelpManager::setupGuiHelpEngine();
+ m_allIndicesCache = LocalHelpManager::filterEngine()->indices({});
+ m_lastIndicesCache.clear();
+ m_lastEntry.clear();
+ }
+ const QStringList cache = m_lastEntry.isEmpty() || !storage->input().contains(m_lastEntry)
+ ? m_allIndicesCache : m_lastIndicesCache;
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, cache, m_icon);
+ };
+ const auto onDone = [this, storage](const Async<QStringList> &async) {
+ if (async.isResultAvailable()) {
+ m_lastIndicesCache = async.result();
+ m_lastEntry = storage->input();
+ }
+ };
+
+ return {{AsyncTask<QStringList>(onSetup, onDone), storage}};
}
void HelpIndexFilter::invalidateCache()
diff --git a/src/plugins/help/helpindexfilter.h b/src/plugins/help/helpindexfilter.h
index 1dfc6f5995..e980f49ab0 100644
--- a/src/plugins/help/helpindexfilter.h
+++ b/src/plugins/help/helpindexfilter.h
@@ -5,48 +5,22 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QIcon>
-#include <QMultiMap>
-#include <QSet>
-#include <QUrl>
-
-#include <atomic>
-
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
class HelpIndexFilter final : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
HelpIndexFilter();
- ~HelpIndexFilter() final;
-
- // ILocatorFilter
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
- void refresh(QFutureInterface<void> &future) override;
-
- QStringList allIndices() const;
-
-signals:
- void linksActivated(const QMultiMap<QString, QUrl> &links, const QString &key) const;
private:
+ Core::LocatorMatcherTasks matchers() final;
void invalidateCache();
- bool updateCache(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QStringList &cache, const QString &entry);
-
QStringList m_allIndicesCache;
QStringList m_lastIndicesCache;
QString m_lastEntry;
- std::atomic_bool m_needsUpdate = true;
+ bool m_needsUpdate = true;
QIcon m_icon;
};
-} // namespace Internal
-} // namespace Help
+} // namespace Help::Internal
diff --git a/src/plugins/help/helpmanager.cpp b/src/plugins/help/helpmanager.cpp
index c3e0bd3151..3c053662d5 100644
--- a/src/plugins/help/helpmanager.cpp
+++ b/src/plugins/help/helpmanager.cpp
@@ -7,16 +7,18 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesystemwatcher.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QDateTime>
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QFileInfo>
+#include <QPromise>
#include <QStringList>
#include <QUrl>
@@ -99,7 +101,7 @@ void HelpManager::registerDocumentation(const QStringList &files)
return;
}
- QFuture<bool> future = Utils::runAsync(&HelpManager::registerDocumentationNow, files);
+ QFuture<bool> future = Utils::asyncRun(&HelpManager::registerDocumentationNow, files);
Utils::onResultReady(future, this, [](bool docsChanged){
if (docsChanged) {
d->m_helpEngine->setupData();
@@ -122,13 +124,12 @@ void HelpManager::unregisterDocumentation(const QStringList &fileNames)
unregisterNamespaces(getNamespaces(fileNames));
}
-void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterface,
- const QStringList &files)
+void HelpManager::registerDocumentationNow(QPromise<bool> &promise, const QStringList &files)
{
QMutexLocker locker(&d->m_helpengineMutex);
- futureInterface.setProgressRange(0, files.count());
- futureInterface.setProgressValue(0);
+ promise.setProgressRange(0, files.count());
+ promise.setProgressValue(0);
QHelpEngineCore helpEngine(collectionFilePath());
helpEngine.setReadOnly(false);
@@ -136,9 +137,9 @@ void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterfa
bool docsChanged = false;
QStringList nameSpaces = helpEngine.registeredDocumentations();
for (const QString &file : files) {
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
break;
- futureInterface.setProgressValue(futureInterface.progressValue() + 1);
+ promise.setProgressValue(promise.future().progressValue() + 1);
const QString &nameSpace = QHelpEngineCore::namespaceName(file);
if (nameSpace.isEmpty())
continue;
@@ -152,7 +153,7 @@ void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterfa
}
}
}
- futureInterface.reportResult(docsChanged);
+ promise.addResult(docsChanged);
}
void HelpManager::unregisterNamespaces(const QStringList &nameSpaces)
diff --git a/src/plugins/help/helpmanager.h b/src/plugins/help/helpmanager.h
index d868e96256..739d5a6876 100644
--- a/src/plugins/help/helpmanager.h
+++ b/src/plugins/help/helpmanager.h
@@ -5,12 +5,15 @@
#include <coreplugin/helpmanager_implementation.h>
-QT_FORWARD_DECLARE_CLASS(QUrl)
-
-#include <QFutureInterface>
#include <QHelpEngineCore>
#include <QVariant>
+QT_BEGIN_NAMESPACE
+template <typename T>
+class QPromise;
+class QUrl;
+QT_END_NAMESPACE
+
namespace Help {
namespace Internal {
@@ -55,10 +58,9 @@ public:
const QUrl &url,
Core::HelpManager::HelpViewerLocation location = Core::HelpManager::HelpModeAlways) override;
-
static void setupHelpManager();
- static void registerDocumentationNow(QFutureInterface<bool> &futureInterface,
- const QStringList &fileNames);
+ static void registerDocumentationNow(QPromise<bool> &promise, const QStringList &fileNames);
+
signals:
void collectionFileChanged();
void helpRequested(const QUrl &url, Core::HelpManager::HelpViewerLocation location);
diff --git a/src/plugins/help/helpmode.cpp b/src/plugins/help/helpmode.cpp
deleted file mode 100644
index 2e2ee46893..0000000000
--- a/src/plugins/help/helpmode.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "helpmode.h"
-#include "helpconstants.h"
-#include "helpicons.h"
-#include "helptr.h"
-
-#include <QCoreApplication>
-
-using namespace Help;
-using namespace Help::Internal;
-
-HelpMode::HelpMode(QObject *parent)
- : Core::IMode(parent)
-{
- setObjectName("HelpMode");
- setContext(Core::Context(Constants::C_MODE_HELP));
- setIcon(Utils::Icon::modeIcon(Icons::MODE_HELP_CLASSIC,
- Icons::MODE_HELP_FLAT, Icons::MODE_HELP_FLAT_ACTIVE));
- setDisplayName(Tr::tr("Help"));
- setPriority(Constants::P_MODE_HELP);
- setId(Constants::ID_MODE_HELP);
-}
diff --git a/src/plugins/help/helpmode.h b/src/plugins/help/helpmode.h
deleted file mode 100644
index fbe69f08bc..0000000000
--- a/src/plugins/help/helpmode.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/imode.h>
-
-#include <QString>
-#include <QIcon>
-
-namespace Help {
-namespace Internal {
-
-class HelpMode : public Core::IMode
-{
-public:
- explicit HelpMode(QObject *parent = nullptr);
-};
-
-} // namespace Internal
-} // namespace Help
diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp
index 5612637247..7329b3dad6 100644
--- a/src/plugins/help/helpplugin.cpp
+++ b/src/plugins/help/helpplugin.cpp
@@ -4,7 +4,6 @@
#include "helpplugin.h"
#include "bookmarkmanager.h"
-#include "contentwindow.h"
#include "docsettingspage.h"
#include "filtersettingspage.h"
#include "generalsettingspage.h"
@@ -13,11 +12,9 @@
#include "helpicons.h"
#include "helpindexfilter.h"
#include "helpmanager.h"
-#include "helpmode.h"
#include "helptr.h"
#include "helpviewer.h"
#include "helpwidget.h"
-#include "indexwindow.h"
#include "localhelpmanager.h"
#include "openpagesmanager.h"
#include "searchtaskhandler.h"
@@ -35,6 +32,7 @@
#include <coreplugin/findplaceholder.h>
#include <coreplugin/helpitem.h>
#include <coreplugin/icore.h>
+#include <coreplugin/imode.h>
#include <coreplugin/minisplitter.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/rightpane.h>
@@ -52,45 +50,54 @@
#include <utils/theme/theme.h>
#include <utils/tooltip/tooltip.h>
+#include <QAction>
#include <QApplication>
+#include <QComboBox>
+#include <QDesktopServices>
#include <QDialog>
#include <QDialogButtonBox>
-#include <QDir>
-#include <QFileInfo>
+#include <QHelpEngine>
#include <QLabel>
#include <QLibraryInfo>
+#include <QMenu>
#include <QPlainTextEdit>
-#include <QTimer>
-#include <QTranslator>
-#include <qplugin.h>
#include <QRegularExpression>
-
-#include <QAction>
-#include <QComboBox>
-#include <QDesktopServices>
-#include <QMenu>
-#include <QStackedLayout>
#include <QSplitter>
-
-#include <QHelpEngine>
+#include <QStackedLayout>
+#include <QTimer>
+#include <QTranslator>
#include <functional>
-static const char kExternalWindowStateKey[] = "Help/ExternalWindowState";
-static const char kToolTipHelpContext[] = "Help.ToolTip";
-
using namespace Core;
using namespace Utils;
-namespace Help {
-namespace Internal {
+namespace Help::Internal {
+
+const char kExternalWindowStateKey[] = "Help/ExternalWindowState";
+const char kToolTipHelpContext[] = "Help.ToolTip";
+
+class HelpMode : public IMode
+{
+public:
+ HelpMode()
+ {
+ setObjectName("HelpMode");
+ setContext(Core::Context(Constants::C_MODE_HELP));
+ setIcon(Icon::modeIcon(Icons::MODE_HELP_CLASSIC,
+ Icons::MODE_HELP_FLAT, Icons::MODE_HELP_FLAT_ACTIVE));
+ setDisplayName(Tr::tr("Help"));
+ setPriority(Constants::P_MODE_HELP);
+ setId(Constants::ID_MODE_HELP);
+ }
+};
class HelpPluginPrivate : public QObject
{
public:
HelpPluginPrivate();
- void modeChanged(Utils::Id mode, Utils::Id old);
+ void modeChanged(Id mode, Id old);
void requestContextHelp();
void showContextHelp(const HelpItem &contextHelp);
@@ -127,7 +134,7 @@ public:
QRect m_externalWindowState;
DocSettingsPage m_docSettingsPage;
- FilterSettingsPage m_filterSettingsPage;
+ FilterSettingsPage m_filterSettingsPage{[this] {setupHelpEngineIfNeeded(); }};
SearchTaskHandler m_searchTaskHandler;
GeneralSettingsPage m_generalSettingsPage;
@@ -158,6 +165,11 @@ void HelpPlugin::showHelpUrl(const QUrl &url, Core::HelpManager::HelpViewerLocat
dd->showHelpUrl(url, location);
}
+void HelpPlugin::showLinksInCurrentViewer(const QMultiMap<QString, QUrl> &links, const QString &key)
+{
+ dd->showLinksInCurrentViewer(links, key);
+}
+
void HelpPlugin::initialize()
{
dd = new HelpPluginPrivate;
@@ -185,8 +197,6 @@ HelpPluginPrivate::HelpPluginPrivate()
connect(&m_searchTaskHandler, &SearchTaskHandler::search,
this, &QDesktopServices::openUrl);
- connect(&m_filterSettingsPage, &FilterSettingsPage::filtersChanged,
- this, &HelpPluginPrivate::setupHelpEngineIfNeeded);
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
@@ -258,9 +268,6 @@ HelpPluginPrivate::HelpPluginPrivate()
ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
connect(action, &QAction::triggered, this, &HelpPluginPrivate::slotSystemInformation);
- connect(&helpIndexFilter, &HelpIndexFilter::linksActivated,
- this, &HelpPluginPrivate::showLinksInCurrentViewer);
-
connect(ModeManager::instance(), &ModeManager::currentModeChanged,
this, &HelpPluginPrivate::modeChanged);
@@ -394,7 +401,7 @@ void HelpPluginPrivate::showLinksInCurrentViewer(const QMultiMap<QString, QUrl>
widget->showLinks(links, key);
}
-void HelpPluginPrivate::modeChanged(Utils::Id mode, Utils::Id old)
+void HelpPluginPrivate::modeChanged(Id mode, Id old)
{
Q_UNUSED(old)
if (mode == m_mode.id()) {
@@ -494,7 +501,7 @@ HelpViewer *HelpPluginPrivate::viewerForContextHelp()
void HelpPluginPrivate::requestContextHelp()
{
// Find out what to show
- const QVariant tipHelpValue = Utils::ToolTip::contextHelp();
+ const QVariant tipHelpValue = ToolTip::contextHelp();
const HelpItem tipHelp = tipHelpValue.canConvert<HelpItem>()
? tipHelpValue.value<HelpItem>()
: HelpItem(tipHelpValue.toString());
@@ -641,5 +648,4 @@ void HelpPluginPrivate::doSetupIfNeeded()
}
}
-} // Internal
-} // Help
+} // Help::Internal
diff --git a/src/plugins/help/helpplugin.h b/src/plugins/help/helpplugin.h
index d3a3e0b980..96d14a152b 100644
--- a/src/plugins/help/helpplugin.h
+++ b/src/plugins/help/helpplugin.h
@@ -26,6 +26,8 @@ public:
~HelpPlugin() final;
static void showHelpUrl(const QUrl &url, Core::HelpManager::HelpViewerLocation location);
+ static void showLinksInCurrentViewer(const QMultiMap<QString, QUrl> &links,
+ const QString &key);
static HelpViewer *createHelpViewer();
static HelpWidget *modeHelpWidget();
diff --git a/src/plugins/help/helpwidget.cpp b/src/plugins/help/helpwidget.cpp
index 571964d0b3..67b42232c9 100644
--- a/src/plugins/help/helpwidget.cpp
+++ b/src/plugins/help/helpwidget.cpp
@@ -30,6 +30,7 @@
#include <texteditor/texteditorconstants.h>
#include <utils/qtcassert.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QComboBox>
@@ -319,7 +320,7 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget
cmd = Core::ActionManager::registerAction(helpTargetAction, "Help.OpenContextHelpHere", context);
QToolButton *helpTargetButton = Core::Command::toolButtonWithAppendedShortcut(helpTargetAction,
cmd);
- helpTargetButton->setProperty("noArrow", true);
+ helpTargetButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
helpTargetButton->setPopupMode(QToolButton::DelayedPopup);
helpTargetButton->setMenu(createHelpTargetMenu(helpTargetButton));
connect(LocalHelpManager::instance(),
@@ -416,7 +417,7 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget
auto openButton = new QToolButton;
openButton->setIcon(Utils::Icons::SPLIT_HORIZONTAL_TOOLBAR.icon());
openButton->setPopupMode(QToolButton::InstantPopup);
- openButton->setProperty("noArrow", true);
+ openButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
layout->addWidget(openButton);
auto openMenu = new QMenu(openButton);
openButton->setMenu(openMenu);
diff --git a/src/plugins/imageviewer/CMakeLists.txt b/src/plugins/imageviewer/CMakeLists.txt
index b55ef9931e..38659e8417 100644
--- a/src/plugins/imageviewer/CMakeLists.txt
+++ b/src/plugins/imageviewer/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(Qt5 COMPONENTS SvgWidgets QUIET)
+find_package(Qt6 COMPONENTS SvgWidgets QUIET)
if (TARGET Qt::SvgWidgets)
set(SVG_WIDGETS Qt::SvgWidgets)
endif()
diff --git a/src/plugins/imageviewer/imageview.cpp b/src/plugins/imageviewer/imageview.cpp
index 34b74d4908..9a2af284f4 100644
--- a/src/plugins/imageviewer/imageview.cpp
+++ b/src/plugins/imageviewer/imageview.cpp
@@ -8,11 +8,11 @@
#include "imageviewerfile.h"
#include "imageviewertr.h"
#include "multiexportdialog.h"
-#include "utils/mimeutils.h"
#include <coreplugin/messagemanager.h>
#include <utils/fileutils.h>
+#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
#include <utils/qtcsettings.h>
diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp
index 9c19b0ffbf..5d4a068e71 100644
--- a/src/plugins/imageviewer/imageviewer.cpp
+++ b/src/plugins/imageviewer/imageviewer.cpp
@@ -17,8 +17,9 @@
#include <utils/filepath.h>
#include <utils/qtcassert.h>
-#include <utils/utilsicons.h>
#include <utils/styledbar.h>
+#include <utils/stylehelper.h>
+#include <utils/utilsicons.h>
#include <QAction>
#include <QDebug>
@@ -112,7 +113,7 @@ void ImageViewer::ctor()
d->shareButton->setToolTip(Tr::tr("Export"));
d->shareButton->setPopupMode(QToolButton::InstantPopup);
d->shareButton->setIcon(Icons::EXPORTFILE_TOOLBAR.icon());
- d->shareButton->setProperty("noArrow", true);
+ d->shareButton->setProperty(StyleHelper::C_NO_ARROW, true);
auto shareMenu = new QMenu(d->shareButton);
shareMenu->addAction(d->actionExportImage);
shareMenu->addAction(d->actionMultiExportImages);
diff --git a/src/plugins/incredibuild/cmakecommandbuilder.cpp b/src/plugins/incredibuild/cmakecommandbuilder.cpp
index 8732601cbc..2f0dd751ae 100644
--- a/src/plugins/incredibuild/cmakecommandbuilder.cpp
+++ b/src/plugins/incredibuild/cmakecommandbuilder.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <cmakeprojectmanager/cmakeprojectconstants.h> // Compile-time only
diff --git a/src/plugins/incredibuild/commandbuilderaspect.cpp b/src/plugins/incredibuild/commandbuilderaspect.cpp
index 6a16cabc6d..5cfbac953b 100644
--- a/src/plugins/incredibuild/commandbuilderaspect.cpp
+++ b/src/plugins/incredibuild/commandbuilderaspect.cpp
@@ -110,7 +110,7 @@ void CommandBuilderAspectPrivate::tryToMigrate()
}
}
-void CommandBuilderAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void CommandBuilderAspect::addToLayout(Layouting::LayoutItem &parent)
{
if (!d->commandBuilder) {
d->commandBuilder = new QComboBox;
@@ -151,9 +151,9 @@ void CommandBuilderAspect::addToLayout(Layouting::LayoutBuilder &builder)
if (!d->m_loadedFromMap)
d->tryToMigrate();
- builder.addRow({d->label.data(), d->commandBuilder.data()});
- builder.addRow({Tr::tr("Make command:"), d->makePathChooser.data()});
- builder.addRow({Tr::tr("Make arguments:"), d->makeArgumentsLineEdit.data()});
+ parent.addRow({d->label.data(), d->commandBuilder.data()});
+ parent.addRow({Tr::tr("Make command:"), d->makePathChooser.data()});
+ parent.addRow({Tr::tr("Make arguments:"), d->makeArgumentsLineEdit.data()});
updateGui();
}
diff --git a/src/plugins/incredibuild/commandbuilderaspect.h b/src/plugins/incredibuild/commandbuilderaspect.h
index 7c6d04c93b..6033e9036e 100644
--- a/src/plugins/incredibuild/commandbuilderaspect.h
+++ b/src/plugins/incredibuild/commandbuilderaspect.h
@@ -23,7 +23,7 @@ public:
QString fullCommandFlag(bool keepJobNum) const;
private:
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) final;
+ void addToLayout(Layouting::LayoutItem &parent) final;
void fromMap(const QVariantMap &map) final;
void toMap(QVariantMap &map) const final;
diff --git a/src/plugins/ios/createsimulatordialog.cpp b/src/plugins/ios/createsimulatordialog.cpp
index 784c33742f..aff8a673df 100644
--- a/src/plugins/ios/createsimulatordialog.cpp
+++ b/src/plugins/ios/createsimulatordialog.cpp
@@ -7,8 +7,8 @@
#include "simulatorcontrol.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
-#include <utils/runextensions.h>
#include <QApplication>
#include <QComboBox>
@@ -32,7 +32,7 @@ CreateSimulatorDialog::CreateSimulatorDialog(QWidget *parent)
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
@@ -60,11 +60,10 @@ CreateSimulatorDialog::CreateSimulatorDialog(QWidget *parent)
enableOk();
});
- m_futureSync.setCancelOnWait(true);
- m_futureSync.addFuture(Utils::onResultReady(SimulatorControl::updateDeviceTypes(), this,
+ m_futureSync.addFuture(Utils::onResultReady(SimulatorControl::updateDeviceTypes(this), this,
&CreateSimulatorDialog::populateDeviceTypes));
- QFuture<QList<RuntimeInfo>> runtimesfuture = SimulatorControl::updateRuntimes();
+ QFuture<QList<RuntimeInfo>> runtimesfuture = SimulatorControl::updateRuntimes(this);
Utils::onResultReady(runtimesfuture, this, [this](const QList<RuntimeInfo> &runtimes) {
m_runtimes = runtimes;
});
diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp
index eb6664c155..7831d84e23 100644
--- a/src/plugins/ios/iosbuildstep.cpp
+++ b/src/plugins/ios/iosbuildstep.cpp
@@ -20,8 +20,8 @@
#include <projectexplorer/toolchain.h>
#include <utils/filepath.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QGridLayout>
#include <QLabel>
@@ -101,7 +101,8 @@ QWidget *IosBuildStep::createConfigWidget()
updateDetails();
connect(buildArgumentsTextEdit, &QPlainTextEdit::textChanged, this, [=] {
- setBaseArguments(ProcessArgs::splitArgs(buildArgumentsTextEdit->toPlainText()));
+ setBaseArguments(ProcessArgs::splitArgs(buildArgumentsTextEdit->toPlainText(),
+ HostOsInfo::hostOs()));
resetDefaultsButton->setEnabled(!m_useDefaultArguments);
updateDetails();
});
@@ -113,7 +114,8 @@ QWidget *IosBuildStep::createConfigWidget()
});
connect(extraArgumentsLineEdit, &QLineEdit::editingFinished, this, [=] {
- setExtraArguments(ProcessArgs::splitArgs(extraArgumentsLineEdit->text()));
+ setExtraArguments(ProcessArgs::splitArgs(extraArgumentsLineEdit->text(),
+ HostOsInfo::hostOs()));
});
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp
index 54674de8ec..04e4363a2a 100644
--- a/src/plugins/ios/iosconfigurations.cpp
+++ b/src/plugins/ios/iosconfigurations.cpp
@@ -15,6 +15,7 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/gcctoolchain.h>
@@ -30,8 +31,8 @@
#include <qtsupport/qtversionfactory.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QDomDocument>
@@ -211,7 +212,7 @@ static QByteArray decodeProvisioningProfile(const QString &path)
{
QTC_ASSERT(!path.isEmpty(), return QByteArray());
- QtcProcess p;
+ Process p;
p.setTimeoutS(3);
// path is assumed to be valid file path to .mobileprovision
p.setCommand({"openssl", {"smime", "-inform", "der", "-verify", "-in", path}});
@@ -404,7 +405,7 @@ void IosConfigurations::updateSimulators()
dev = IDevice::ConstPtr(new IosSimulator(devId));
devManager->addDevice(dev);
}
- SimulatorControl::updateAvailableSimulators();
+ SimulatorControl::updateAvailableSimulators(this);
}
void IosConfigurations::setDeveloperPath(const FilePath &devPath)
@@ -573,7 +574,7 @@ IosToolChainFactory::IosToolChainFactory()
Toolchains IosToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
- if (detector.device)
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
return {};
QList<ClangToolChain *> existingClangToolChains = clangToolChains(detector.alreadyKnown);
diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp
index 49a07a2b10..326092fccc 100644
--- a/src/plugins/ios/iosdevice.cpp
+++ b/src/plugins/ios/iosdevice.cpp
@@ -185,12 +185,6 @@ Utils::Port IosDevice::nextPort() const
return Utils::Port(m_lastPort);
}
-bool IosDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
-
// IosDeviceManager
IosDeviceManager::TranslationMap IosDeviceManager::translationMap()
diff --git a/src/plugins/ios/iosdevice.h b/src/plugins/ios/iosdevice.h
index 9f4e66c502..ada952037d 100644
--- a/src/plugins/ios/iosdevice.h
+++ b/src/plugins/ios/iosdevice.h
@@ -36,7 +36,6 @@ public:
QString osVersion() const;
QString cpuArchitecture() const;
Utils::Port nextPort() const;
- bool canAutoDetectPorts() const override;
static QString name();
diff --git a/src/plugins/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp
index 54481ded9b..2d0e2b841f 100644
--- a/src/plugins/ios/iosdsymbuildstep.cpp
+++ b/src/plugins/ios/iosdsymbuildstep.cpp
@@ -9,21 +9,22 @@
#include "iostr.h"
#include <extensionsystem/pluginmanager.h>
-#include <projectexplorer/target.h>
-#include <projectexplorer/project.h>
+
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/processparameters.h>
+#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtparser.h>
-#include <utils/stringutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/stringutils.h>
#include <QGridLayout>
#include <QLabel>
@@ -242,7 +243,8 @@ QWidget *IosDsymBuildStep::createConfigWidget()
connect(argumentsTextEdit, &QPlainTextEdit::textChanged, this,
[this, argumentsTextEdit, resetDefaultsButton, updateDetails] {
- setArguments(Utils::ProcessArgs::splitArgs(argumentsTextEdit->toPlainText()));
+ setArguments(ProcessArgs::splitArgs(argumentsTextEdit->toPlainText(),
+ HostOsInfo::hostOs()));
resetDefaultsButton->setEnabled(!isDefault());
updateDetails();
});
diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp
index c835e48390..e6fca977b2 100644
--- a/src/plugins/ios/iosprobe.cpp
+++ b/src/plugins/ios/iosprobe.cpp
@@ -4,7 +4,7 @@
#include "iosprobe.h"
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFileInfo>
#include <QLoggingCategory>
@@ -40,7 +40,7 @@ void XcodeProbe::addDeveloperPath(const QString &path)
void XcodeProbe::detectDeveloperPaths()
{
- Utils::QtcProcess selectedXcode;
+ Utils::Process selectedXcode;
selectedXcode.setTimeoutS(5);
selectedXcode.setCommand({"/usr/bin/xcode-select", {"--print-path"}});
selectedXcode.runBlocking();
diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp
index 9a9acedb29..8a0a3e62dc 100644
--- a/src/plugins/ios/iosrunconfiguration.cpp
+++ b/src/plugins/ios/iosrunconfiguration.cpp
@@ -21,9 +21,9 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
-#include <utils/qtcassert.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
#include <QAction>
#include <QApplication>
@@ -324,14 +324,14 @@ IosDeviceTypeAspect::IosDeviceTypeAspect(IosRunConfiguration *runConfiguration)
this, &IosDeviceTypeAspect::deviceChanges);
}
-void IosDeviceTypeAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void IosDeviceTypeAspect::addToLayout(Layouting::LayoutItem &parent)
{
m_deviceTypeComboBox = new QComboBox;
m_deviceTypeComboBox->setModel(&m_deviceTypeModel);
m_deviceTypeLabel = new QLabel(Tr::tr("Device type:"));
- builder.addItems({m_deviceTypeLabel, m_deviceTypeComboBox});
+ parent.addItems({m_deviceTypeLabel, m_deviceTypeComboBox});
updateValues();
diff --git a/src/plugins/ios/iosrunconfiguration.h b/src/plugins/ios/iosrunconfiguration.h
index 161b0703c5..1ebe79efa5 100644
--- a/src/plugins/ios/iosrunconfiguration.h
+++ b/src/plugins/ios/iosrunconfiguration.h
@@ -27,7 +27,7 @@ public:
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
IosDeviceType deviceType() const;
void setDeviceType(const IosDeviceType &deviceType);
diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp
index c01c3c859d..828bb65c00 100644
--- a/src/plugins/ios/iosrunner.cpp
+++ b/src/plugins/ios/iosrunner.cpp
@@ -27,7 +27,7 @@
#include <qmldebug/qmloutputparser.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/url.h>
#include <utils/utilsicons.h>
diff --git a/src/plugins/ios/iossettingswidget.cpp b/src/plugins/ios/iossettingswidget.cpp
index 8c4149a91b..bb0bc36d61 100644
--- a/src/plugins/ios/iossettingswidget.cpp
+++ b/src/plugins/ios/iossettingswidget.cpp
@@ -12,9 +12,9 @@
#include "simulatoroperationdialog.h"
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/runextensions.h>
#include <QCheckBox>
#include <QDateTime>
@@ -51,7 +51,6 @@ static void onSimOperation(const SimulatorInfo &simInfo, SimulatorOperationDialo
IosSettingsWidget::IosSettingsWidget()
{
- resize(622, 456);
setWindowTitle(Tr::tr("iOS Configuration"));
m_deviceAskCheckBox = new QCheckBox(Tr::tr("Ask about devices not in developer mode"));
@@ -94,7 +93,7 @@ IosSettingsWidget::IosSettingsWidget()
m_pathWidget->addButton(Tr::tr("Screenshot"), this,
std::bind(&IosSettingsWidget::onScreenshot, this));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Devices")),
@@ -175,7 +174,7 @@ void IosSettingsWidget::onStart()
Utils::StdErrFormat);
} else {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::startSimulator(info.identifier),
+ SimulatorControl::startSimulator(info.identifier), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator start"), _1)));
}
}
@@ -207,11 +206,9 @@ void IosSettingsWidget::onCreate()
CreateSimulatorDialog createDialog(this);
if (createDialog.exec() == QDialog::Accepted) {
- QFuture<void> f = QFuture<void>(
- Utils::onResultReady(SimulatorControl::createSimulator(createDialog.name(),
- createDialog.deviceType(),
- createDialog.runtime()),
- std::bind(onSimulatorCreate, createDialog.name(), _1)));
+ QFuture<void> f = QFuture<void>(Utils::onResultReady(SimulatorControl::createSimulator(
+ createDialog.name(), createDialog.deviceType(), createDialog.runtime()),
+ this, std::bind(onSimulatorCreate, createDialog.name(), _1)));
statusDialog->addFutures({ f });
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
}
@@ -242,7 +239,7 @@ void IosSettingsWidget::onReset()
QList<QFuture<void>> futureList;
for (const SimulatorInfo &info : simulatorInfoList) {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::resetSimulator(info.identifier),
+ SimulatorControl::resetSimulator(info.identifier), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator reset"), _1)));
}
@@ -270,7 +267,7 @@ void IosSettingsWidget::onRename()
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
statusDialog->addMessage(Tr::tr("Renaming simulator device..."), Utils::NormalMessageFormat);
QFuture<void> f = QFuture<void>(Utils::onResultReady(
- SimulatorControl::renameSimulator(simInfo.identifier, newName),
+ SimulatorControl::renameSimulator(simInfo.identifier, newName), this,
std::bind(onSimOperation, simInfo, statusDialog, Tr::tr("simulator rename"), _1)));
statusDialog->addFutures({f});
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
@@ -300,7 +297,7 @@ void IosSettingsWidget::onDelete()
QList<QFuture<void>> futureList;
for (const SimulatorInfo &info : simulatorInfoList) {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::deleteSimulator(info.identifier),
+ SimulatorControl::deleteSimulator(info.identifier), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator delete"), _1)));
}
@@ -331,7 +328,7 @@ void IosSettingsWidget::onScreenshot()
QList<QFuture<void>> futureList;
for (const SimulatorInfo &info : simulatorInfoList) {
futureList << QFuture<void>(Utils::onResultReady(
- SimulatorControl::takeSceenshot(info.identifier, generatePath(info)),
+ SimulatorControl::takeSceenshot(info.identifier, generatePath(info)), this,
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator screenshot"), _1)));
}
diff --git a/src/plugins/ios/iossimulator.cpp b/src/plugins/ios/iossimulator.cpp
index b58e451400..fa68c4031e 100644
--- a/src/plugins/ios/iossimulator.cpp
+++ b/src/plugins/ios/iossimulator.cpp
@@ -8,7 +8,7 @@
#include <projectexplorer/kitinformation.h>
#include <utils/port.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QMapIterator>
@@ -52,7 +52,7 @@ Utils::Port IosSimulator::nextPort() const
// use qrand instead?
if (++m_lastPort >= Constants::IOS_SIMULATOR_PORT_END)
m_lastPort = Constants::IOS_SIMULATOR_PORT_START;
- Utils::QtcProcess portVerifier;
+ Utils::Process portVerifier;
// this is a bit too broad (it does not check just listening sockets, but also connections
// to that port from this computer)
portVerifier.setCommand({"lsof", {"-n", "-P", "-i", QString(":%1").arg(m_lastPort)}});
@@ -66,11 +66,6 @@ Utils::Port IosSimulator::nextPort() const
return Utils::Port(m_lastPort);
}
-bool IosSimulator::canAutoDetectPorts() const
-{
- return true;
-}
-
// IosDeviceType
IosDeviceType::IosDeviceType(IosDeviceType::Type type, const QString &identifier, const QString &displayName) :
diff --git a/src/plugins/ios/iossimulator.h b/src/plugins/ios/iossimulator.h
index d2d69f1d0e..988776e508 100644
--- a/src/plugins/ios/iossimulator.h
+++ b/src/plugins/ios/iossimulator.h
@@ -48,7 +48,6 @@ public:
ProjectExplorer::IDeviceWidget *createWidget() override;
Utils::Port nextPort() const;
- bool canAutoDetectPorts() const override;
protected:
friend class IosSimulatorFactory;
diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp
index 0331ba2cca..0e4545bf1d 100644
--- a/src/plugins/ios/iostoolhandler.cpp
+++ b/src/plugins/ios/iostoolhandler.cpp
@@ -12,11 +12,11 @@
#include <debugger/debuggerconstants.h>
+#include <utils/async.h>
#include <utils/filepath.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <QDir>
@@ -63,22 +63,22 @@ class LogTailFiles : public QObject
Q_OBJECT
public:
- void exec(QFutureInterface<void> &fi, std::shared_ptr<QTemporaryFile> stdoutFile,
- std::shared_ptr<QTemporaryFile> stderrFile)
+ void exec(QPromise<void> &promise, std::shared_ptr<QTemporaryFile> stdoutFile,
+ std::shared_ptr<QTemporaryFile> stderrFile)
{
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
// The future is canceled when app on simulator is stoped.
QEventLoop loop;
QFutureWatcher<void> watcher;
connect(&watcher, &QFutureWatcher<void>::canceled, &loop, [&] { loop.quit(); });
- watcher.setFuture(fi.future());
+ watcher.setFuture(promise.future());
// Process to print the console output while app is running.
auto logProcess = [&](QProcess *tailProcess, std::shared_ptr<QTemporaryFile> file) {
- QObject::connect(tailProcess, &QProcess::readyReadStandardOutput, &loop, [=] {
- if (!fi.isCanceled())
+ QObject::connect(tailProcess, &QProcess::readyReadStandardOutput, &loop, [&, tailProcess] {
+ if (!promise.isCanceled())
emit logMessage(QString::fromLocal8Bit(tailProcess->readAll()));
});
tailProcess->start(QStringLiteral("tail"), {"-f", file->fileName()});
@@ -787,7 +787,6 @@ IosSimulatorToolHandlerPrivate::IosSimulatorToolHandlerPrivate(const IosDeviceTy
{
QObject::connect(&outputLogger, &LogTailFiles::logMessage,
std::bind(&IosToolHandlerPrivate::appOutput, this, _1));
- futureSynchronizer.setCancelOnWait(true);
}
void IosSimulatorToolHandlerPrivate::requestTransferApp(const QString &appBundlePath,
@@ -814,8 +813,8 @@ void IosSimulatorToolHandlerPrivate::requestTransferApp(const QString &appBundle
if (SimulatorControl::isSimulatorRunning(m_deviceId))
installAppOnSimulator();
else
- futureSynchronizer.addFuture(
- Utils::onResultReady(SimulatorControl::startSimulator(m_deviceId), onSimulatorStart));
+ futureSynchronizer.addFuture(Utils::onResultReady(
+ SimulatorControl::startSimulator(m_deviceId), q, onSimulatorStart));
}
void IosSimulatorToolHandlerPrivate::requestRunApp(const QString &appBundlePath,
@@ -851,8 +850,8 @@ void IosSimulatorToolHandlerPrivate::requestRunApp(const QString &appBundlePath,
if (SimulatorControl::isSimulatorRunning(m_deviceId))
launchAppOnSimulator(extraArgs);
else
- futureSynchronizer.addFuture(
- Utils::onResultReady(SimulatorControl::startSimulator(m_deviceId), onSimulatorStart));
+ futureSynchronizer.addFuture(Utils::onResultReady(
+ SimulatorControl::startSimulator(m_deviceId), q, onSimulatorStart));
}
void IosSimulatorToolHandlerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
@@ -904,7 +903,7 @@ void IosSimulatorToolHandlerPrivate::installAppOnSimulator()
isTransferringApp(m_bundlePath, m_deviceId, 20, 100, "");
auto installFuture = SimulatorControl::installApp(m_deviceId,
Utils::FilePath::fromString(m_bundlePath));
- futureSynchronizer.addFuture(Utils::onResultReady(installFuture, onResponseAppInstall));
+ futureSynchronizer.addFuture(Utils::onResultReady(installFuture, q, onResponseAppInstall));
}
void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &extraArgs)
@@ -931,17 +930,17 @@ void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &ext
"Install Xcode 8 or later.").arg(bundleId));
}
- auto monitorPid = [this](QFutureInterface<void> &fi, qint64 pid) {
+ auto monitorPid = [this](QPromise<void> &promise, qint64 pid) {
#ifdef Q_OS_UNIX
do {
// Poll every 1 sec to check whether the app is running.
QThread::msleep(1000);
- } while (!fi.isCanceled() && kill(pid, 0) == 0);
+ } while (!promise.isCanceled() && kill(pid, 0) == 0);
#else
Q_UNUSED(pid)
#endif
// Future is cancelled if the app is stopped from the qt creator.
- if (!fi.isCanceled())
+ if (!promise.isCanceled())
stop(0);
};
@@ -953,9 +952,9 @@ void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &ext
gotInferiorPid(m_bundlePath, m_deviceId, response.pID);
didStartApp(m_bundlePath, m_deviceId, Ios::IosToolHandler::Success);
// Start monitoring app's life signs.
- futureSynchronizer.addFuture(Utils::runAsync(monitorPid, response.pID));
+ futureSynchronizer.addFuture(Utils::asyncRun(monitorPid, response.pID));
if (captureConsole)
- futureSynchronizer.addFuture(Utils::runAsync(&LogTailFiles::exec, &outputLogger,
+ futureSynchronizer.addFuture(Utils::asyncRun(&LogTailFiles::exec, &outputLogger,
stdoutFile, stderrFile));
} else {
m_pid = -1;
@@ -967,16 +966,11 @@ void IosSimulatorToolHandlerPrivate::launchAppOnSimulator(const QStringList &ext
}
};
- futureSynchronizer.addFuture(
- Utils::onResultReady(SimulatorControl::launchApp(m_deviceId,
- bundleId,
- debugRun,
- extraArgs,
- captureConsole ? stdoutFile->fileName()
- : QString(),
- captureConsole ? stderrFile->fileName()
- : QString()),
- onResponseAppLaunch));
+ futureSynchronizer.addFuture(Utils::onResultReady(SimulatorControl::launchApp(
+ m_deviceId, bundleId, debugRun, extraArgs,
+ captureConsole ? stdoutFile->fileName() : QString(),
+ captureConsole ? stderrFile->fileName() : QString()),
+ q, onResponseAppLaunch));
}
bool IosSimulatorToolHandlerPrivate::isResponseValid(const SimulatorControl::ResponseData &responseData)
diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp
index 6e090b6188..0832a5faf3 100644
--- a/src/plugins/ios/simulatorcontrol.cpp
+++ b/src/plugins/ios/simulatorcontrol.cpp
@@ -5,9 +5,9 @@
#include "iosconfigurations.h"
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#ifdef Q_OS_MAC
#include <CoreFoundation/CoreFoundation.h>
@@ -57,7 +57,7 @@ static bool checkForTimeout(const chrono::high_resolution_clock::time_point &sta
static bool runCommand(const CommandLine &command, QString *stdOutput, QString *allOutput = nullptr)
{
- QtcProcess p;
+ Process p;
p.setTimeoutS(-1);
p.setCommand(command);
p.runBlocking();
@@ -98,7 +98,7 @@ static bool launchSimulator(const QString &simUdid) {
}
}
- return QtcProcess::startDetached({simulatorAppPath, {"--args", "-CurrentDeviceUDID", simUdid}});
+ return Process::startDetached({simulatorAppPath, {"--args", "-CurrentDeviceUDID", simUdid}});
}
static bool isAvailable(const QJsonObject &object)
@@ -162,30 +162,30 @@ static SimulatorInfo deviceInfo(const QString &simUdid);
static QString bundleIdentifier(const Utils::FilePath &bundlePath);
static QString bundleExecutable(const Utils::FilePath &bundlePath);
-static void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void startSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid);
-static void installApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void installApp(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const Utils::FilePath &bundlePath);
-static void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void launchApp(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &bundleIdentifier,
bool waitForDebugger,
const QStringList &extraArgs,
const QString &stdoutPath,
const QString &stderrPath);
-static void deleteSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void deleteSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid);
-static void resetSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void resetSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid);
-static void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void renameSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &newName);
-static void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void createSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &name,
const DeviceTypeInfo &deviceType,
const RuntimeInfo &runtime);
-static void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
+static void takeSceenshot(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &filePath);
@@ -234,10 +234,10 @@ static QList<SimulatorInfo> getAvailableSimulators()
return availableDevices;
}
-QFuture<QList<DeviceTypeInfo> > SimulatorControl::updateDeviceTypes()
+QFuture<QList<DeviceTypeInfo>> SimulatorControl::updateDeviceTypes(QObject *context)
{
- QFuture< QList<DeviceTypeInfo> > future = Utils::runAsync(getAvailableDeviceTypes);
- Utils::onResultReady(future, [](const QList<DeviceTypeInfo> &deviceTypes) {
+ QFuture<QList<DeviceTypeInfo>> future = Utils::asyncRun(getAvailableDeviceTypes);
+ Utils::onResultReady(future, context, [](const QList<DeviceTypeInfo> &deviceTypes) {
s_availableDeviceTypes = deviceTypes;
});
return future;
@@ -248,20 +248,21 @@ QList<RuntimeInfo> SimulatorControl::availableRuntimes()
return s_availableRuntimes;
}
-QFuture<QList<RuntimeInfo> > SimulatorControl::updateRuntimes()
+QFuture<QList<RuntimeInfo>> SimulatorControl::updateRuntimes(QObject *context)
{
- QFuture< QList<RuntimeInfo> > future = Utils::runAsync(getAvailableRuntimes);
- Utils::onResultReady(future, [](const QList<RuntimeInfo> &runtimes) {
+ QFuture<QList<RuntimeInfo>> future = Utils::asyncRun(getAvailableRuntimes);
+ Utils::onResultReady(future, context, [](const QList<RuntimeInfo> &runtimes) {
s_availableRuntimes = runtimes;
});
return future;
}
-QFuture< QList<SimulatorInfo> > SimulatorControl::updateAvailableSimulators()
+QFuture<QList<SimulatorInfo>> SimulatorControl::updateAvailableSimulators(QObject *context)
{
- QFuture< QList<SimulatorInfo> > future = Utils::runAsync(getAvailableSimulators);
- Utils::onResultReady(future,
- [](const QList<SimulatorInfo> &devices) { s_availableDevices = devices; });
+ QFuture<QList<SimulatorInfo>> future = Utils::asyncRun(getAvailableSimulators);
+ Utils::onResultReady(future, context, [](const QList<SimulatorInfo> &devices) {
+ s_availableDevices = devices;
+ });
return future;
}
@@ -284,13 +285,13 @@ QString SimulatorControl::bundleExecutable(const Utils::FilePath &bundlePath)
QFuture<SimulatorControl::ResponseData> SimulatorControl::startSimulator(const QString &simUdid)
{
- return Utils::runAsync(Internal::startSimulator, simUdid);
+ return Utils::asyncRun(Internal::startSimulator, simUdid);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::installApp(
const QString &simUdid, const Utils::FilePath &bundlePath)
{
- return Utils::runAsync(Internal::installApp, simUdid, bundlePath);
+ return Utils::asyncRun(Internal::installApp, simUdid, bundlePath);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::launchApp(const QString &simUdid,
@@ -300,7 +301,7 @@ QFuture<SimulatorControl::ResponseData> SimulatorControl::launchApp(const QStrin
const QString &stdoutPath,
const QString &stderrPath)
{
- return Utils::runAsync(Internal::launchApp,
+ return Utils::asyncRun(Internal::launchApp,
simUdid,
bundleIdentifier,
waitForDebugger,
@@ -311,18 +312,18 @@ QFuture<SimulatorControl::ResponseData> SimulatorControl::launchApp(const QStrin
QFuture<SimulatorControl::ResponseData> SimulatorControl::deleteSimulator(const QString &simUdid)
{
- return Utils::runAsync(Internal::deleteSimulator, simUdid);
+ return Utils::asyncRun(Internal::deleteSimulator, simUdid);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::resetSimulator(const QString &simUdid)
{
- return Utils::runAsync(Internal::resetSimulator, simUdid);
+ return Utils::asyncRun(Internal::resetSimulator, simUdid);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::renameSimulator(const QString &simUdid,
const QString &newName)
{
- return Utils::runAsync(Internal::renameSimulator, simUdid, newName);
+ return Utils::asyncRun(Internal::renameSimulator, simUdid, newName);
}
QFuture<SimulatorControl::ResponseData>
@@ -330,13 +331,13 @@ SimulatorControl::createSimulator(const QString &name,
const DeviceTypeInfo &deviceType,
const RuntimeInfo &runtime)
{
- return Utils::runAsync(Internal::createSimulator, name, deviceType, runtime);
+ return Utils::asyncRun(Internal::createSimulator, name, deviceType, runtime);
}
QFuture<SimulatorControl::ResponseData> SimulatorControl::takeSceenshot(const QString &simUdid,
const QString &filePath)
{
- return Utils::runAsync(Internal::takeSceenshot, simUdid, filePath);
+ return Utils::asyncRun(Internal::takeSceenshot, simUdid, filePath);
}
// Static members
@@ -392,7 +393,7 @@ QString bundleExecutable(const Utils::FilePath &bundlePath)
return executable;
}
-void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid)
+void startSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QString &simUdid)
{
SimulatorControl::ResponseData response(simUdid);
SimulatorInfo simInfo = deviceInfo(simUdid);
@@ -420,7 +421,7 @@ void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const
if (simInfo.isShutdown()) {
if (launchSimulator(simUdid)) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
// At this point the sim device exists, available and was not running.
// So the simulator is started and we'll wait for it to reach to a state
@@ -429,13 +430,11 @@ void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const
SimulatorInfo info;
do {
info = deviceInfo(simUdid);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
- } while (!info.isBooted()
- && !checkForTimeout(start, simulatorStartTimeout));
- if (info.isBooted()) {
+ } while (!info.isBooted() && !checkForTimeout(start, simulatorStartTimeout));
+ if (info.isBooted())
response.success = true;
- }
} else {
qCDebug(simulatorLog) << "Error starting simulator.";
}
@@ -444,14 +443,12 @@ void startSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const
<< simInfo;
}
- if (!fi.isCanceled()) {
- fi.reportResult(response);
- }
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void installApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
- const QString &simUdid,
- const Utils::FilePath &bundlePath)
+void installApp(QPromise<SimulatorControl::ResponseData> &promise,
+ const QString &simUdid, const Utils::FilePath &bundlePath)
{
QTC_CHECK(bundlePath.exists());
@@ -459,11 +456,11 @@ void installApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.success = runSimCtlCommand({"install", simUdid, bundlePath.toString()},
nullptr,
&response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void launchApp(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &bundleIdentifier,
bool waitForDebugger,
@@ -472,7 +469,7 @@ void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
const QString &stderrPath)
{
SimulatorControl::ResponseData response(simUdid);
- if (!bundleIdentifier.isEmpty() && !fi.isCanceled()) {
+ if (!bundleIdentifier.isEmpty() && !promise.isCanceled()) {
QStringList args({"launch", simUdid, bundleIdentifier});
// simctl usage documentation : Note: Log output is often directed to stderr, not stdout.
@@ -499,30 +496,29 @@ void launchApp(QFutureInterface<SimulatorControl::ResponseData> &fi,
}
}
- if (!fi.isCanceled()) {
- fi.reportResult(response);
- }
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void deleteSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid)
+void deleteSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QString &simUdid)
{
SimulatorControl::ResponseData response(simUdid);
response.success = runSimCtlCommand({"delete", simUdid}, nullptr, &response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void resetSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid)
+void resetSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QString &simUdid)
{
SimulatorControl::ResponseData response(simUdid);
response.success = runSimCtlCommand({"erase", simUdid}, nullptr, &response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void renameSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &newName)
{
@@ -530,12 +526,11 @@ void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.success = runSimCtlCommand({"rename", simUdid, newName},
nullptr,
&response.commandOutput);
-
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void createSimulator(QPromise<SimulatorControl::ResponseData> &promise,
const QString &name,
const DeviceTypeInfo &deviceType,
const RuntimeInfo &runtime)
@@ -550,11 +545,11 @@ void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.simUdid = response.success ? stdOutput.trimmed() : QString();
}
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
-void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
+void takeSceenshot(QPromise<SimulatorControl::ResponseData> &promise,
const QString &simUdid,
const QString &filePath)
{
@@ -562,8 +557,8 @@ void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
response.success = runSimCtlCommand({"io", simUdid, "screenshot", filePath},
nullptr,
&response.commandOutput);
- if (!fi.isCanceled())
- fi.reportResult(response);
+ if (!promise.isCanceled())
+ promise.addResult(response);
}
QDebug &operator<<(QDebug &stream, const SimulatorInfo &info)
diff --git a/src/plugins/ios/simulatorcontrol.h b/src/plugins/ios/simulatorcontrol.h
index 15fea7b27b..402100fbea 100644
--- a/src/plugins/ios/simulatorcontrol.h
+++ b/src/plugins/ios/simulatorcontrol.h
@@ -65,11 +65,11 @@ public:
public:
static QList<DeviceTypeInfo> availableDeviceTypes();
- static QFuture<QList<DeviceTypeInfo> > updateDeviceTypes();
+ static QFuture<QList<DeviceTypeInfo>> updateDeviceTypes(QObject *context);
static QList<RuntimeInfo> availableRuntimes();
- static QFuture<QList<RuntimeInfo> > updateRuntimes();
+ static QFuture<QList<RuntimeInfo>> updateRuntimes(QObject *context);
static QList<SimulatorInfo> availableSimulators();
- static QFuture<QList<SimulatorInfo> > updateAvailableSimulators();
+ static QFuture<QList<SimulatorInfo>> updateAvailableSimulators(QObject *context);
static bool isSimulatorRunning(const QString &simUdid);
static QString bundleIdentifier(const Utils::FilePath &bundlePath);
static QString bundleExecutable(const Utils::FilePath &bundlePath);
diff --git a/src/plugins/ios/simulatorinfomodel.cpp b/src/plugins/ios/simulatorinfomodel.cpp
index df58dc98cb..dce5646713 100644
--- a/src/plugins/ios/simulatorinfomodel.cpp
+++ b/src/plugins/ios/simulatorinfomodel.cpp
@@ -6,7 +6,7 @@
#include "iostr.h"
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <QTimer>
@@ -23,8 +23,6 @@ const int deviceUpdateInterval = 1000; // Update simulator state every 1 sec.
SimulatorInfoModel::SimulatorInfoModel(QObject *parent) :
QAbstractItemModel(parent)
{
- m_fetchFuture.setCancelOnWait(true);
-
requestSimulatorInfo();
auto updateTimer = new QTimer(this);
@@ -109,7 +107,7 @@ void SimulatorInfoModel::requestSimulatorInfo()
if (!m_fetchFuture.isEmpty())
return; // Ignore the request if the last request is still pending.
- m_fetchFuture.addFuture(Utils::onResultReady(SimulatorControl::updateAvailableSimulators(),
+ m_fetchFuture.addFuture(Utils::onResultReady(SimulatorControl::updateAvailableSimulators(this),
this, &SimulatorInfoModel::populateSimulators));
}
diff --git a/src/plugins/ios/simulatoroperationdialog.cpp b/src/plugins/ios/simulatoroperationdialog.cpp
index 56e2d3b69f..180b28d76e 100644
--- a/src/plugins/ios/simulatoroperationdialog.cpp
+++ b/src/plugins/ios/simulatoroperationdialog.cpp
@@ -40,7 +40,7 @@ SimulatorOperationDialog::SimulatorOperationDialog(QWidget *parent) :
m_formatter = new Utils::OutputFormatter;
m_formatter->setPlainTextEdit(messageEdit);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
messageEdit,
diff --git a/src/plugins/languageclient/CMakeLists.txt b/src/plugins/languageclient/CMakeLists.txt
index 8e1748a04a..5af221f7f8 100644
--- a/src/plugins/languageclient/CMakeLists.txt
+++ b/src/plugins/languageclient/CMakeLists.txt
@@ -1,9 +1,17 @@
+if (MSVC)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
+elseif (MINGW)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
+endif()
+
add_qtc_plugin(LanguageClient
PUBLIC_DEPENDS LanguageServerProtocol Qt::Core app_version
PLUGIN_DEPENDS ProjectExplorer Core TextEditor
SOURCES
callhierarchy.cpp callhierarchy.h
client.cpp client.h
+ clientrequesttask.cpp clientrequesttask.h
+ currentdocumentsymbolsrequest.cpp currentdocumentsymbolsrequest.h
diagnosticmanager.cpp diagnosticmanager.h
documentsymbolcache.cpp documentsymbolcache.h
dynamiccapabilities.cpp dynamiccapabilities.h
diff --git a/src/plugins/languageclient/callhierarchy.cpp b/src/plugins/languageclient/callhierarchy.cpp
index 3e0c6589aa..a05922cbe5 100644
--- a/src/plugins/languageclient/callhierarchy.cpp
+++ b/src/plugins/languageclient/callhierarchy.cpp
@@ -23,8 +23,6 @@ using namespace LanguageServerProtocol;
namespace LanguageClient {
-const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
-
namespace {
enum Direction { Incoming, Outgoing };
@@ -186,6 +184,9 @@ public:
layout()->setSpacing(0);
connect(m_view, &NavigationTreeView::activated, this, &CallHierarchy::onItemActivated);
+
+ connect(LanguageClientManager::instance(), &LanguageClientManager::openCallHierarchy,
+ this, &CallHierarchy::updateHierarchyAtCursorPosition);
}
void onItemActivated(const QModelIndex &index)
@@ -211,26 +212,14 @@ void CallHierarchy::updateHierarchyAtCursorPosition()
BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
if (!editor)
return;
- Client *client = LanguageClientManager::clientForFilePath(editor->document()->filePath());
+
+ Core::IDocument *document = editor->document();
+
+ Client *client = LanguageClientManager::clientForFilePath(document->filePath());
if (!client)
return;
- const QString methodName = PrepareCallHierarchyRequest::methodName;
- std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
- bool supported = registered.value_or(false);
- const Core::IDocument *document = editor->document();
- if (registered) {
- if (supported) {
- const QJsonValue &options = client->dynamicCapabilities().option(methodName);
- const TextDocumentRegistrationOptions docOptions(options);
- supported = docOptions.filterApplies(document->filePath(),
- Utils::mimeTypeForName(document->mimeType()));
- }
- } else {
- supported = client->capabilities().callHierarchyProvider().has_value();
- }
-
- if (!supported)
+ if (!CallHierarchyFactory::supportsCallHierarchy(client, document))
return;
TextDocumentPositionParams params;
@@ -273,7 +262,25 @@ CallHierarchyFactory::CallHierarchyFactory()
{
setDisplayName(Tr::tr("Call Hierarchy"));
setPriority(650);
- setId(CALL_HIERARCHY_FACTORY_ID);
+ setId(Constants::CALL_HIERARCHY_FACTORY_ID);
+}
+
+bool CallHierarchyFactory::supportsCallHierarchy(Client *client, const Core::IDocument *document)
+{
+ const QString methodName = PrepareCallHierarchyRequest::methodName;
+ std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
+ bool supported = registered.value_or(false);
+ if (registered) {
+ if (supported) {
+ const QJsonValue &options = client->dynamicCapabilities().option(methodName);
+ const TextDocumentRegistrationOptions docOptions(options);
+ supported = docOptions.filterApplies(document->filePath(),
+ Utils::mimeTypeForName(document->mimeType()));
+ }
+ } else {
+ supported = client->capabilities().callHierarchyProvider().has_value();
+ }
+ return supported;
}
Core::NavigationView CallHierarchyFactory::createWidget()
@@ -284,6 +291,7 @@ Core::NavigationView CallHierarchyFactory::createWidget()
Icons::RELOAD_TOOLBAR.icon();
auto button = new QToolButton;
button->setIcon(Icons::RELOAD_TOOLBAR.icon());
+ button->setToolTip(Tr::tr("Reloads the call hierarchy for the symbol under cursor position."));
connect(button, &QToolButton::clicked, [h](){
h->updateHierarchyAtCursorPosition();
});
diff --git a/src/plugins/languageclient/callhierarchy.h b/src/plugins/languageclient/callhierarchy.h
index f707c4fbcb..bbc15b0971 100644
--- a/src/plugins/languageclient/callhierarchy.h
+++ b/src/plugins/languageclient/callhierarchy.h
@@ -5,8 +5,12 @@
#pragma once
+namespace Core { class IDocument; }
+
namespace LanguageClient {
+class Client;
+
class CallHierarchyFactory : public Core::INavigationWidgetFactory
{
Q_OBJECT
@@ -14,6 +18,8 @@ class CallHierarchyFactory : public Core::INavigationWidgetFactory
public:
CallHierarchyFactory();
+ static bool supportsCallHierarchy(Client *client, const Core::IDocument *document);
+
Core::NavigationView createWidget() override;
};
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index 0efaacd350..77b2cbfdee 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -3,6 +3,7 @@
#include "client.h"
+#include "callhierarchy.h"
#include "diagnosticmanager.h"
#include "documentsymbolcache.h"
#include "languageclientcompletionassist.h"
@@ -11,6 +12,7 @@
#include "languageclienthoverhandler.h"
#include "languageclientinterface.h"
#include "languageclientmanager.h"
+#include "languageclientoutline.h"
#include "languageclientquickfix.h"
#include "languageclientsymbolsupport.h"
#include "languageclientutils.h"
@@ -38,7 +40,7 @@
#include <languageserverprotocol/workspace.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/codeassist/documentcontentcompletion.h>
#include <texteditor/codeassist/iassistprocessor.h>
@@ -51,7 +53,7 @@
#include <texteditor/texteditorsettings.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDebug>
#include <QJsonDocument>
@@ -155,7 +157,7 @@ public:
m_documentUpdateTimer.setInterval(500);
connect(&m_documentUpdateTimer, &QTimer::timeout, this,
[this] { sendPostponedDocumentUpdates(Schedule::Now); });
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
q, &Client::projectClosed);
QTC_ASSERT(clientInterface, return);
@@ -325,7 +327,6 @@ public:
SemanticTokenSupport m_tokenSupport;
QString m_serverName;
QString m_serverVersion;
- LanguageServerProtocol::SymbolStringifier m_symbolStringifier;
Client::LogTarget m_logTarget = Client::LogTarget::Ui;
bool m_locatorsEnabled = true;
bool m_autoRequestCodeActions = true;
@@ -509,7 +510,7 @@ void Client::initialize()
params.setRootUri(hostPathToServerUri(d->m_project->projectDirectory()));
const QList<WorkSpaceFolder> workspaces
- = Utils::transform(SessionManager::projects(), [this](Project *pro) {
+ = Utils::transform(ProjectManager::projects(), [this](Project *pro) {
return WorkSpaceFolder(hostPathToServerUri(pro->projectDirectory()),
pro->displayName());
});
@@ -554,7 +555,7 @@ QString Client::stateString() const
case InitializeRequested: return Tr::tr("initialize requested");
case Initialized: return Tr::tr("initialized");
case ShutdownRequested: return Tr::tr("shutdown requested");
- case Shutdown: return Tr::tr("shutdown");
+ case Shutdown: return Tr::tr("shut down");
case Error: return Tr::tr("error");
}
return {};
@@ -617,7 +618,7 @@ void Client::openDocument(TextEditor::TextDocument *document)
}
}
- d->m_openedDocument[document].document = document->document()->clone(this);
+ d->m_openedDocument[document].document = new QTextDocument(document->document()->toPlainText());
d->m_openedDocument[document].contentsChangedConnection
= connect(document,
&TextDocument::contentsChangedWithPosition,
@@ -879,6 +880,8 @@ void Client::activateEditor(Core::IEditor *editor)
optionalActions |= TextEditor::TextEditorActionHandler::FindUsage;
if (symbolSupport().supportsRename(widget->textDocument()))
optionalActions |= TextEditor::TextEditorActionHandler::RenameSymbol;
+ if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document()))
+ optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy;
widget->setOptionalActions(optionalActions);
}
}
@@ -1484,16 +1487,6 @@ void Client::setSemanticTokensHandler(const SemanticTokensHandler &handler)
d->m_tokenSupport.setTokensHandler(handler);
}
-void Client::setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier)
-{
- d->m_symbolStringifier = stringifier;
-}
-
-SymbolStringifier Client::symbolStringifier() const
-{
- return d->m_symbolStringifier;
-}
-
void Client::setSnippetsGroup(const QString &group)
{
if (const auto provider = qobject_cast<LanguageClientCompletionAssistProvider *>(
@@ -1679,10 +1672,15 @@ LanguageClientValue<MessageActionItem> ClientPrivate::showMessageBox(
}
QHash<QAbstractButton *, MessageActionItem> itemForButton;
if (const std::optional<QList<MessageActionItem>> actions = message.actions()) {
- for (const MessageActionItem &action : *actions)
- itemForButton.insert(box->addButton(action.title(), QMessageBox::InvalidRole), action);
+ auto button = box->addButton(QMessageBox::Close);
+ connect(button, &QPushButton::clicked, box, &QMessageBox::reject);
+ for (const MessageActionItem &action : *actions) {
+ connect(button, &QPushButton::clicked, box, &QMessageBox::accept);
+ itemForButton.insert(button, action);
+ }
}
- box->exec();
+ if (box->exec() == QDialog::Rejected)
+ return {};
const MessageActionItem &item = itemForButton.value(box->clickedButton());
return item.isValid() ? LanguageClientValue<MessageActionItem>(item)
: LanguageClientValue<MessageActionItem>();
@@ -1872,7 +1870,7 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con
} else if (method == WorkSpaceFolderRequest::methodName) {
WorkSpaceFolderRequest::Response response(id);
const QList<ProjectExplorer::Project *> projects
- = ProjectExplorer::SessionManager::projects();
+ = ProjectExplorer::ProjectManager::projects();
if (projects.isEmpty()) {
response.setResult(nullptr);
} else {
@@ -2089,6 +2087,12 @@ bool Client::fileBelongsToProject(const Utils::FilePath &filePath) const
return project() && project()->isKnownFile(filePath);
}
+LanguageClientOutlineItem *Client::createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol)
+{
+ return new LanguageClientOutlineItem(this, symbol);
+}
+
FilePath toHostPath(const FilePath serverDeviceTemplate, const FilePath localClientPath)
{
const FilePath onDevice = serverDeviceTemplate.withNewPath(localClientPath.path());
@@ -2110,7 +2114,7 @@ FilePath Client::serverUriToHostPath(const LanguageServerProtocol::DocumentUri &
DocumentUri Client::hostPathToServerUri(const Utils::FilePath &path) const
{
return DocumentUri::fromFilePath(path, [&](const Utils::FilePath &clientPath) {
- return clientPath.onDevice(d->m_serverDeviceTemplate);
+ return d->m_serverDeviceTemplate.withNewPath(clientPath.path());
});
}
diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h
index 525739d7a6..27f48abc41 100644
--- a/src/plugins/languageclient/client.h
+++ b/src/plugins/languageclient/client.h
@@ -31,7 +31,6 @@ class Unregistration;
} // namespace LanguageServerProtocol
namespace LanguageClient {
-
class BaseClientInterface;
class ClientPrivate;
class DiagnosticManager;
@@ -40,6 +39,7 @@ class DynamicCapabilities;
class HoverHandler;
class InterfaceController;
class LanguageClientCompletionAssistProvider;
+class LanguageClientOutlineItem;
class LanguageClientQuickFixProvider;
class LanguageFilter;
class ProgressManager;
@@ -48,16 +48,12 @@ class SymbolSupport;
class LANGUAGECLIENT_EXPORT Client : public QObject
{
Q_OBJECT
+ Q_DISABLE_COPY_MOVE(Client)
public:
explicit Client(BaseClientInterface *clientInterface, const Utils::Id &id = {}); // takes ownership
~Client() override;
- Client(const Client &) = delete;
- Client(Client &&) = delete;
- Client &operator=(const Client &) = delete;
- Client &operator=(Client &&) = delete;
-
// basic properties
Utils::Id id() const;
void setName(const QString &name);
@@ -160,13 +156,13 @@ public:
const LanguageServerProtocol::Diagnostic &diag) const;
bool hasDiagnostics(const TextEditor::TextDocument *document) const;
void setSemanticTokensHandler(const SemanticTokensHandler &handler);
- void setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier);
- LanguageServerProtocol::SymbolStringifier symbolStringifier() const;
void setSnippetsGroup(const QString &group);
void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider);
void setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider);
virtual bool supportsDocumentSymbols(const TextEditor::TextDocument *doc) const;
virtual bool fileBelongsToProject(const Utils::FilePath &filePath) const;
+ virtual LanguageClientOutlineItem *createOutlineItem(
+ const LanguageServerProtocol::DocumentSymbol &symbol);
LanguageServerProtocol::DocumentUri::PathMapper hostPathMapper() const;
Utils::FilePath serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const;
diff --git a/src/plugins/languageclient/clientrequesttask.cpp b/src/plugins/languageclient/clientrequesttask.cpp
new file mode 100644
index 0000000000..ed3ddaff2c
--- /dev/null
+++ b/src/plugins/languageclient/clientrequesttask.cpp
@@ -0,0 +1,39 @@
+// 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 "clientrequesttask.h"
+
+#include <QScopeGuard>
+
+using namespace LanguageServerProtocol;
+
+namespace LanguageClient {
+
+WorkspaceSymbolRequestTaskAdapter::WorkspaceSymbolRequestTaskAdapter()
+{
+ task()->setResponseCallback([this](const WorkspaceSymbolRequest::Response &response){
+ emit done(response.result().has_value());
+ });
+}
+
+void WorkspaceSymbolRequestTaskAdapter::start()
+{
+ task()->start();
+}
+
+bool WorkspaceSymbolRequestTask::preStartCheck()
+{
+ if (!ClientRequestTask::preStartCheck())
+ return false;
+
+ const std::optional<std::variant<bool, WorkDoneProgressOptions>> capability
+ = client()->capabilities().workspaceSymbolProvider();
+ if (!capability.has_value())
+ return false;
+ if (std::holds_alternative<bool>(*capability) && !std::get<bool>(*capability))
+ return false;
+
+ return true;
+}
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/clientrequesttask.h b/src/plugins/languageclient/clientrequesttask.h
new file mode 100644
index 0000000000..3e08e1a287
--- /dev/null
+++ b/src/plugins/languageclient/clientrequesttask.h
@@ -0,0 +1,80 @@
+// 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 "languageclient_global.h"
+
+#include "client.h"
+
+#include <languageserverprotocol/lsptypes.h>
+#include <languageserverprotocol/lsputils.h>
+#include <languageserverprotocol/workspace.h>
+
+#include <solutions/tasking/tasktree.h>
+
+namespace LanguageClient {
+
+template <typename Request>
+class LANGUAGECLIENT_EXPORT ClientRequestTask
+{
+public:
+ virtual ~ClientRequestTask()
+ {
+ if (m_id)
+ m_client->cancelRequest(*m_id); // In order to not to invoke a response callback anymore
+ }
+
+ void setClient(Client *client) { m_client = client; }
+ Client *client() const { return m_client; }
+ void setParams(const typename Request::Parameters &params) { m_params = params; }
+
+ void start()
+ {
+ QTC_ASSERT(!isRunning(), return);
+ if (!preStartCheck()) {
+ m_callback({});
+ return;
+ }
+ Request request(m_params);
+ request.setResponseCallback([this](const typename Request::Response &response) {
+ m_response = response;
+ m_id = {};
+ m_callback(response);
+ });
+ m_id = request.id();
+ m_client->sendMessage(request);
+ }
+
+ bool isRunning() const { return m_id.has_value(); }
+ virtual bool preStartCheck() { return m_client && m_client->reachable() && m_params.isValid(); }
+
+ typename Request::Response response() const { return m_response; }
+ void setResponseCallback(typename Request::ResponseCallback callback) { m_callback = callback; }
+
+private:
+ Client *m_client = nullptr;
+ typename Request::Parameters m_params;
+ typename Request::ResponseCallback m_callback;
+ std::optional<LanguageServerProtocol::MessageId> m_id;
+ typename Request::Response m_response;
+};
+
+class LANGUAGECLIENT_EXPORT WorkspaceSymbolRequestTask
+ : public ClientRequestTask<LanguageServerProtocol::WorkspaceSymbolRequest>
+{
+public:
+ bool preStartCheck() override;
+};
+
+class LANGUAGECLIENT_EXPORT WorkspaceSymbolRequestTaskAdapter
+ : public Tasking::TaskAdapter<WorkspaceSymbolRequestTask>
+{
+public:
+ WorkspaceSymbolRequestTaskAdapter();
+ void start() final;
+};
+
+} // namespace LanguageClient
+
+TASKING_DECLARE_TASK(SymbolRequest, LanguageClient::WorkspaceSymbolRequestTaskAdapter);
diff --git a/src/plugins/languageclient/currentdocumentsymbolsrequest.cpp b/src/plugins/languageclient/currentdocumentsymbolsrequest.cpp
new file mode 100644
index 0000000000..2d272a7216
--- /dev/null
+++ b/src/plugins/languageclient/currentdocumentsymbolsrequest.cpp
@@ -0,0 +1,83 @@
+// 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 "currentdocumentsymbolsrequest.h"
+
+#include "documentsymbolcache.h"
+#include "languageclientmanager.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+
+using namespace Core;
+using namespace LanguageServerProtocol;
+using namespace TextEditor;
+using namespace Utils;
+
+namespace LanguageClient {
+
+void CurrentDocumentSymbolsRequest::start()
+{
+ QTC_ASSERT(!isRunning(), return);
+
+ m_currentDocumentSymbolsData = {};
+
+ TextDocument *document = TextDocument::currentTextDocument();
+ Client *client = LanguageClientManager::clientForDocument(document);
+ if (!client) {
+ emit done(false);
+ return;
+ }
+
+ DocumentSymbolCache *symbolCache = client->documentSymbolCache();
+ DocumentUri currentUri = client->hostPathToServerUri(document->filePath());
+ DocumentUri::PathMapper pathMapper = client->hostPathMapper();
+
+ const auto reportFailure = [this] {
+ clearConnections();
+ emit done(false);
+ };
+
+ const auto updateSymbols = [this, currentUri, pathMapper](const DocumentUri &uri,
+ const DocumentSymbolsResult &symbols)
+ {
+ if (uri != currentUri) // We might get updates for not current editor
+ return;
+
+ const FilePath filePath = pathMapper ? currentUri.toFilePath(pathMapper) : FilePath();
+ m_currentDocumentSymbolsData = {filePath, pathMapper, symbols};
+ clearConnections();
+ emit done(true);
+ };
+
+ m_connections.append(connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
+ this, reportFailure));
+ m_connections.append(connect(client, &Client::finished, this, reportFailure));
+ m_connections.append(connect(document, &IDocument::contentsChanged, this, reportFailure));
+ m_connections.append(connect(symbolCache, &DocumentSymbolCache::gotSymbols,
+ this, updateSymbols));
+ symbolCache->requestSymbols(currentUri, Schedule::Now);
+}
+
+bool CurrentDocumentSymbolsRequest::isRunning() const
+{
+ return !m_connections.isEmpty();
+}
+
+void CurrentDocumentSymbolsRequest::clearConnections()
+{
+ for (const QMetaObject::Connection &connection : std::as_const(m_connections))
+ disconnect(connection);
+ m_connections.clear();
+}
+
+CurrentDocumentSymbolsRequestTaskAdapter::CurrentDocumentSymbolsRequestTaskAdapter()
+{
+ connect(task(), &CurrentDocumentSymbolsRequest::done, this, &TaskInterface::done);
+}
+
+void CurrentDocumentSymbolsRequestTaskAdapter::start()
+{
+ task()->start();
+}
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/currentdocumentsymbolsrequest.h b/src/plugins/languageclient/currentdocumentsymbolsrequest.h
new file mode 100644
index 0000000000..0b9c11a221
--- /dev/null
+++ b/src/plugins/languageclient/currentdocumentsymbolsrequest.h
@@ -0,0 +1,53 @@
+// 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 "languageclient_global.h"
+
+#include <languageserverprotocol/languagefeatures.h>
+#include <languageserverprotocol/lsptypes.h>
+
+#include <solutions/tasking/tasktree.h>
+
+namespace LanguageClient {
+
+class LANGUAGECLIENT_EXPORT CurrentDocumentSymbolsData
+{
+public:
+ Utils::FilePath m_filePath;
+ LanguageServerProtocol::DocumentUri::PathMapper m_pathMapper;
+ LanguageServerProtocol::DocumentSymbolsResult m_symbols;
+};
+
+class LANGUAGECLIENT_EXPORT CurrentDocumentSymbolsRequest : public QObject
+{
+ Q_OBJECT
+
+public:
+ void start();
+ bool isRunning() const;
+ CurrentDocumentSymbolsData currentDocumentSymbolsData() const { return m_currentDocumentSymbolsData; }
+
+signals:
+ void done(bool success);
+
+private:
+ void clearConnections();
+
+ CurrentDocumentSymbolsData m_currentDocumentSymbolsData;
+ QList<QMetaObject::Connection> m_connections;
+};
+
+class LANGUAGECLIENT_EXPORT CurrentDocumentSymbolsRequestTaskAdapter
+ : public Tasking::TaskAdapter<CurrentDocumentSymbolsRequest>
+{
+public:
+ CurrentDocumentSymbolsRequestTaskAdapter();
+ void start() final;
+};
+
+} // namespace LanguageClient
+
+TASKING_DECLARE_TASK(CurrentDocumentSymbolsRequestTask,
+ LanguageClient::CurrentDocumentSymbolsRequestTaskAdapter);
diff --git a/src/plugins/languageclient/documentsymbolcache.cpp b/src/plugins/languageclient/documentsymbolcache.cpp
index 113b22695a..b08e70d291 100644
--- a/src/plugins/languageclient/documentsymbolcache.cpp
+++ b/src/plugins/languageclient/documentsymbolcache.cpp
@@ -40,6 +40,8 @@ DocumentSymbolCache::DocumentSymbolCache(Client *client)
void DocumentSymbolCache::requestSymbols(const DocumentUri &uri, Schedule schedule)
{
+ if (m_runningRequests.contains(uri))
+ return;
m_compressedUris.insert(uri);
switch (schedule) {
case Schedule::Now:
diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs
index 9a48caf9e3..286f319a3c 100644
--- a/src/plugins/languageclient/languageclient.qbs
+++ b/src/plugins/languageclient/languageclient.qbs
@@ -24,6 +24,10 @@ QtcPlugin {
"callhierarchy.h",
"client.cpp",
"client.h",
+ "clientrequesttask.cpp",
+ "clientrequesttask.h",
+ "currentdocumentsymbolsrequest.cpp",
+ "currentdocumentsymbolsrequest.h",
"diagnosticmanager.cpp",
"diagnosticmanager.h",
"documentsymbolcache.cpp",
diff --git a/src/plugins/languageclient/languageclient_global.h b/src/plugins/languageclient/languageclient_global.h
index 0dca3e6bbf..2abb395d86 100644
--- a/src/plugins/languageclient/languageclient_global.h
+++ b/src/plugins/languageclient/languageclient_global.h
@@ -22,12 +22,25 @@ const char LANGUAGECLIENT_STDIO_SETTINGS_ID[] = "LanguageClient::StdIOSettingsID
const char LANGUAGECLIENT_SETTINGS_TR[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Language Client");
const char LANGUAGECLIENT_DOCUMENT_FILTER_ID[] = "Current Document Symbols";
const char LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Symbols in Current Document");
+const char LANGUAGECLIENT_DOCUMENT_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient",
+ "Locates symbols in the current document, based on a language server.");
const char LANGUAGECLIENT_WORKSPACE_FILTER_ID[] = "Workspace Symbols";
const char LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Symbols in Workspace");
+const char LANGUAGECLIENT_WORKSPACE_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Locates symbols in the language server workspace.");
const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_ID[] = "Workspace Classes and Structs";
const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Classes and Structs in Workspace");
+const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient",
+ "Locates classes and structs in the language server workspace.");
const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID[] = "Workspace Functions and Methods";
const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Functions and Methods in Workspace");
+const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DESCRIPTION[]
+ = QT_TRANSLATE_NOOP("QtC::LanguageClient",
+ "Locates functions and methods in the language server workspace.");
+
+const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
} // namespace Constants
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp
index 541d1c3ab0..84f4b5ed0a 100644
--- a/src/plugins/languageclient/languageclientinterface.cpp
+++ b/src/plugins/languageclient/languageclientinterface.cpp
@@ -95,14 +95,14 @@ void StdIOClientInterface::startImpl()
QTC_CHECK(!m_process->isRunning());
delete m_process;
}
- m_process = new QtcProcess;
+ m_process = new Process;
m_process->setProcessMode(ProcessMode::Writer);
- connect(m_process, &QtcProcess::readyReadStandardError,
+ connect(m_process, &Process::readyReadStandardError,
this, &StdIOClientInterface::readError);
- connect(m_process, &QtcProcess::readyReadStandardOutput,
+ connect(m_process, &Process::readyReadStandardOutput,
this, &StdIOClientInterface::readOutput);
- connect(m_process, &QtcProcess::started, this, &StdIOClientInterface::started);
- connect(m_process, &QtcProcess::done, this, [this] {
+ connect(m_process, &Process::started, this, &StdIOClientInterface::started);
+ connect(m_process, &Process::done, this, [this] {
m_logFile.flush();
if (m_process->result() != ProcessResult::FinishedWithSuccess)
emit error(QString("%1 (see logs in \"%2\")")
diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h
index 79401f889a..7577f13691 100644
--- a/src/plugins/languageclient/languageclientinterface.h
+++ b/src/plugins/languageclient/languageclientinterface.h
@@ -8,7 +8,7 @@
#include <languageserverprotocol/jsonrpcmessages.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporaryfile.h>
#include <QBuffer>
@@ -52,15 +52,12 @@ private:
class LANGUAGECLIENT_EXPORT StdIOClientInterface : public BaseClientInterface
{
Q_OBJECT
+ Q_DISABLE_COPY_MOVE(StdIOClientInterface)
+
public:
StdIOClientInterface();
~StdIOClientInterface() override;
- StdIOClientInterface(const StdIOClientInterface &) = delete;
- StdIOClientInterface(StdIOClientInterface &&) = delete;
- StdIOClientInterface &operator=(const StdIOClientInterface &) = delete;
- StdIOClientInterface &operator=(StdIOClientInterface &&) = delete;
-
void startImpl() override;
// These functions only have an effect if they are called before start
@@ -74,7 +71,7 @@ protected:
void sendData(const QByteArray &data) final;
Utils::CommandLine m_cmd;
Utils::FilePath m_workingDirectory;
- Utils::QtcProcess *m_process = nullptr;
+ Utils::Process *m_process = nullptr;
Utils::Environment m_env;
private:
diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp
index a41c4f01d6..4de4a0de46 100644
--- a/src/plugins/languageclient/languageclientmanager.cpp
+++ b/src/plugins/languageclient/languageclientmanager.cpp
@@ -6,18 +6,20 @@
#include "languageclientplugin.h"
#include "languageclientsymbolsupport.h"
#include "languageclienttr.h"
+#include "locatorfilter.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/find/searchresultwindow.h>
#include <coreplugin/icore.h>
+#include <coreplugin/navigationwidget.h>
#include <languageserverprotocol/messages.h>
#include <languageserverprotocol/progresssupport.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
@@ -28,7 +30,6 @@
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
-#include <QTextBlock>
#include <QTimer>
using namespace LanguageServerProtocol;
@@ -40,9 +41,19 @@ static Q_LOGGING_CATEGORY(Log, "qtc.languageclient.manager", QtWarningMsg)
static LanguageClientManager *managerInstance = nullptr;
static bool g_shuttingDown = false;
+class LanguageClientManagerPrivate
+{
+ LanguageCurrentDocumentFilter m_currentDocumentFilter;
+ LanguageAllSymbolsFilter m_allSymbolsFilter;
+ LanguageClassesFilter m_classFilter;
+ LanguageFunctionsFilter m_functionFilter;
+};
+
LanguageClientManager::LanguageClientManager(QObject *parent)
- : QObject (parent)
+ : QObject(parent)
{
+ managerInstance = this;
+ d.reset(new LanguageClientManagerPrivate);
using namespace Core;
using namespace ProjectExplorer;
connect(EditorManager::instance(), &EditorManager::editorOpened,
@@ -55,9 +66,9 @@ LanguageClientManager::LanguageClientManager(QObject *parent)
this, &LanguageClientManager::documentContentsSaved);
connect(EditorManager::instance(), &EditorManager::aboutToSave,
this, &LanguageClientManager::documentWillSave);
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &LanguageClientManager::projectAdded);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, [&](Project *project) { project->disconnect(this); });
}
@@ -73,7 +84,7 @@ void LanguageClientManager::init()
if (managerInstance)
return;
QTC_ASSERT(LanguageClientPlugin::instance(), return);
- managerInstance = new LanguageClientManager(LanguageClientPlugin::instance());
+ new LanguageClientManager(LanguageClientPlugin::instance());
}
void LanguageClient::LanguageClientManager::addClient(Client *client)
@@ -91,7 +102,7 @@ void LanguageClient::LanguageClientManager::addClient(Client *client)
&Client::initialized,
managerInstance,
[client](const LanguageServerProtocol::ServerCapabilities &capabilities) {
- managerInstance->m_currentDocumentLocatorFilter.updateCurrentClient();
+ emit managerInstance->clientInitialized(client);
managerInstance->m_inspector.clientInitialized(client->name(), capabilities);
});
connect(client,
@@ -317,7 +328,7 @@ void LanguageClientManager::applySettings()
continue;
const Utils::FilePath filePath = textDocument->filePath();
for (ProjectExplorer::Project *project :
- ProjectExplorer::SessionManager::projects()) {
+ ProjectExplorer::ProjectManager::projects()) {
if (project->isKnownFile(filePath)) {
Client *client = clientForProject[project];
if (!client) {
@@ -448,6 +459,8 @@ QList<Client *> LanguageClientManager::reachableClients()
void LanguageClientManager::editorOpened(Core::IEditor *editor)
{
using namespace TextEditor;
+ using namespace Core;
+
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
if (TextEditorWidget *widget = textEditor->editorWidget()) {
connect(widget, &TextEditorWidget::requestLinkAt, this,
@@ -466,6 +479,14 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
if (auto client = clientForDocument(document))
client->symbolSupport().renameSymbol(document, cursor);
});
+ connect(widget, &TextEditorWidget::requestCallHierarchy, this,
+ [this, document = textEditor->textDocument()]() {
+ if (clientForDocument(document)) {
+ emit openCallHierarchy();
+ NavigationWidget::activateSubWidget(Constants::CALL_HIERARCHY_FACTORY_ID,
+ Side::Left);
+ }
+ });
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() {
if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable())
@@ -494,7 +515,7 @@ void LanguageClientManager::documentOpened(Core::IDocument *document)
if (setting->m_startBehavior == BaseSettings::RequiresProject) {
const Utils::FilePath &filePath = document->filePath();
for (ProjectExplorer::Project *project :
- ProjectExplorer::SessionManager::projects()) {
+ ProjectExplorer::ProjectManager::projects()) {
// check whether file is part of this project
if (!project->isKnownFile(filePath))
continue;
diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h
index 6ceca307a6..fdbbec98b6 100644
--- a/src/plugins/languageclient/languageclientmanager.h
+++ b/src/plugins/languageclient/languageclientmanager.h
@@ -6,7 +6,6 @@
#include "client.h"
#include "languageclient_global.h"
#include "languageclientsettings.h"
-#include "locatorfilter.h"
#include "lspinspector.h"
#include <utils/algorithm.h>
@@ -25,14 +24,15 @@ namespace ProjectExplorer { class Project; }
namespace LanguageClient {
+class LanguageClientManagerPrivate;
class LanguageClientMark;
class LANGUAGECLIENT_EXPORT LanguageClientManager : public QObject
{
Q_OBJECT
+ Q_DISABLE_COPY_MOVE(LanguageClientManager)
+
public:
- LanguageClientManager(const LanguageClientManager &other) = delete;
- LanguageClientManager(LanguageClientManager &&other) = delete;
~LanguageClientManager() override;
static void init();
@@ -80,8 +80,10 @@ public:
signals:
void clientAdded(Client *client);
+ void clientInitialized(Client *client);
void clientRemoved(Client *client);
void shutdownFinished();
+ void openCallHierarchy();
private:
LanguageClientManager(QObject *parent);
@@ -102,10 +104,7 @@ private:
QList<BaseSettings *> m_currentSettings; // owned
QMap<QString, QList<Client *>> m_clientsForSetting;
QHash<TextEditor::TextDocument *, QPointer<Client>> m_clientForDocument;
- DocumentLocatorFilter m_currentDocumentLocatorFilter;
- WorkspaceLocatorFilter m_workspaceLocatorFilter;
- WorkspaceClassLocatorFilter m_workspaceClassLocatorFilter;
- WorkspaceMethodLocatorFilter m_workspaceMethodLocatorFilter;
+ std::unique_ptr<LanguageClientManagerPrivate> d;
LspInspector m_inspector;
};
diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp
index 0d7ae8c371..b065754ec0 100644
--- a/src/plugins/languageclient/languageclientoutline.cpp
+++ b/src/plugins/languageclient/languageclientoutline.cpp
@@ -42,67 +42,10 @@ const QList<DocumentSymbol> sortedSymbols(const QList<DocumentSymbol> &symbols)
});
}
-class LanguageClientOutlineItem : public Utils::TypedTreeItem<LanguageClientOutlineItem>
-{
-public:
- LanguageClientOutlineItem() = default;
- LanguageClientOutlineItem(const SymbolInformation &info)
- : m_name(info.name())
- , m_range(info.location().range())
- , m_type(info.kind())
- { }
-
- LanguageClientOutlineItem(const DocumentSymbol &info, const SymbolStringifier &stringifier)
- : m_name(info.name())
- , m_detail(info.detail().value_or(QString()))
- , m_range(info.range())
- , m_symbolStringifier(stringifier)
- , m_type(info.kind())
- {
- const QList<LanguageServerProtocol::DocumentSymbol> children = sortedSymbols(
- info.children().value_or(QList<DocumentSymbol>()));
- for (const DocumentSymbol &child : children)
- appendChild(new LanguageClientOutlineItem(child, stringifier));
- }
-
- // TreeItem interface
- QVariant data(int column, int role) const override
- {
- switch (role) {
- case Qt::DecorationRole:
- return symbolIcon(m_type);
- case Qt::DisplayRole:
- return m_symbolStringifier
- ? m_symbolStringifier(static_cast<SymbolKind>(m_type), m_name, m_detail)
- : m_name;
- default:
- return Utils::TreeItem::data(column, role);
- }
- }
-
- Qt::ItemFlags flags(int column) const override
- {
- Q_UNUSED(column)
- return Utils::TypedTreeItem<LanguageClientOutlineItem>::flags(column)
- | Qt::ItemIsDragEnabled;
- }
-
- Range range() const { return m_range; }
- Position pos() const { return m_range.start(); }
- bool contains(const Position &pos) const { return m_range.contains(pos); }
-
-private:
- QString m_name;
- QString m_detail;
- Range m_range;
- SymbolStringifier m_symbolStringifier;
- int m_type = -1;
-};
-
class LanguageClientOutlineModel : public Utils::TreeModel<LanguageClientOutlineItem>
{
public:
- using Utils::TreeModel<LanguageClientOutlineItem>::TreeModel;
+ LanguageClientOutlineModel(Client *client) : m_client(client) {}
void setFilePath(const Utils::FilePath &filePath) { m_filePath = filePath; }
void setInfo(const QList<SymbolInformation> &info)
@@ -115,12 +58,7 @@ public:
{
clear();
for (const DocumentSymbol &symbol : sortedSymbols(info))
- rootItem()->appendChild(new LanguageClientOutlineItem(symbol, m_symbolStringifier));
- }
-
- void setSymbolStringifier(const SymbolStringifier &stringifier)
- {
- m_symbolStringifier = stringifier;
+ rootItem()->appendChild(m_client->createOutlineItem(symbol));
}
Qt::DropActions supportedDragActions() const override
@@ -146,7 +84,7 @@ public:
}
private:
- SymbolStringifier m_symbolStringifier;
+ Client * const m_client;
Utils::FilePath m_filePath;
};
@@ -195,6 +133,7 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
TextEditor::BaseTextEditor *editor)
: m_client(client)
, m_editor(editor)
+ , m_model(client)
, m_view(this)
, m_uri(m_client->hostPathToServerUri(editor->textDocument()->filePath()))
{
@@ -214,7 +153,6 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
layout->setSpacing(0);
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view));
setLayout(layout);
- m_model.setSymbolStringifier(m_client->symbolStringifier());
m_model.setFilePath(editor->textDocument()->filePath());
m_proxyModel.setSourceModel(&m_model);
m_view.setModel(&m_proxyModel);
@@ -373,11 +311,11 @@ Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox(
}
OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor)
- : m_client(client)
+ : m_model(client)
+ , m_client(client)
, m_editorWidget(editor->editorWidget())
, m_uri(m_client->hostPathToServerUri(editor->document()->filePath()))
{
- m_model.setSymbolStringifier(client->symbolStringifier());
m_proxyModel.setSourceModel(&m_model);
const bool sorted = LanguageClientSettings::outlineComboBoxIsSorted();
m_proxyModel.sort(sorted ? 0 : -1);
@@ -455,4 +393,40 @@ void OutlineComboBox::setSorted(bool sorted)
m_proxyModel.sort(sorted ? 0 : -1);
}
+LanguageClientOutlineItem::LanguageClientOutlineItem(const SymbolInformation &info)
+ : m_name(info.name())
+ , m_range(info.location().range())
+ , m_type(info.kind())
+{ }
+
+LanguageClientOutlineItem::LanguageClientOutlineItem(Client *client, const DocumentSymbol &info)
+ : m_client(client)
+ , m_name(info.name())
+ , m_detail(info.detail().value_or(QString()))
+ , m_range(info.range())
+ , m_selectionRange(info.selectionRange())
+ , m_type(info.kind())
+{
+ const QList<LanguageServerProtocol::DocumentSymbol> children = sortedSymbols(
+ info.children().value_or(QList<DocumentSymbol>()));
+ for (const DocumentSymbol &child : children)
+ appendChild(m_client->createOutlineItem(child));
+}
+
+QVariant LanguageClientOutlineItem::data(int column, int role) const
+{
+ switch (role) {
+ case Qt::DecorationRole:
+ return symbolIcon(m_type);
+ case Qt::DisplayRole:
+ return m_name;
+ default:
+ return Utils::TreeItem::data(column, role);
+ }
+}
+Qt::ItemFlags LanguageClientOutlineItem::flags(int column) const
+{
+ Q_UNUSED(column)
+ return Utils::TypedTreeItem<LanguageClientOutlineItem>::flags(column) | Qt::ItemIsDragEnabled;
+}
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientoutline.h b/src/plugins/languageclient/languageclientoutline.h
index e333563dc2..4ddc9c79c0 100644
--- a/src/plugins/languageclient/languageclientoutline.h
+++ b/src/plugins/languageclient/languageclientoutline.h
@@ -3,7 +3,11 @@
#pragma once
+#include "languageclient_global.h"
+
+#include <languageserverprotocol/lsptypes.h>
#include <texteditor/ioutlinewidget.h>
+#include <utils/treemodel.h>
namespace TextEditor {
class TextDocument;
@@ -12,6 +16,40 @@ class BaseTextEditor;
namespace Utils { class TreeViewComboBox; }
namespace LanguageClient {
+class Client;
+
+class LANGUAGECLIENT_EXPORT LanguageClientOutlineItem
+ : public Utils::TypedTreeItem<LanguageClientOutlineItem>
+{
+public:
+ LanguageClientOutlineItem() = default;
+ LanguageClientOutlineItem(const LanguageServerProtocol::SymbolInformation &info);
+ LanguageClientOutlineItem(Client *client, const LanguageServerProtocol::DocumentSymbol &info);
+
+ LanguageServerProtocol::Range range() const { return m_range; }
+ LanguageServerProtocol::Range selectionRange() const { return m_selectionRange; }
+ LanguageServerProtocol::Position pos() const { return m_range.start(); }
+ bool contains(const LanguageServerProtocol::Position &pos) const {
+ return m_range.contains(pos);
+ }
+
+protected:
+ // TreeItem interface
+ QVariant data(int column, int role) const override;
+ Qt::ItemFlags flags(int column) const override;
+
+ QString name() const { return m_name; }
+ QString detail() const { return m_detail; }
+ int type() const { return m_type; }
+
+private:
+ Client * const m_client = nullptr;
+ QString m_name;
+ QString m_detail;
+ LanguageServerProtocol::Range m_range;
+ LanguageServerProtocol::Range m_selectionRange;
+ int m_type = -1;
+};
class Client;
diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp
index 6f2cf7dafe..650b450a5d 100644
--- a/src/plugins/languageclient/languageclientsettings.cpp
+++ b/src/plugins/languageclient/languageclientsettings.cpp
@@ -14,7 +14,7 @@
#include <coreplugin/idocument.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/plaintexteditorfactory.h>
#include <texteditor/textmark.h>
@@ -104,17 +104,39 @@ private:
QList<BaseSettings *> m_removed;
};
-class LanguageClientSettingsPageWidget : public QWidget
+class LanguageClientSettingsPageWidget : public Core::IOptionsPageWidget
{
public:
- LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings);
+ LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings,
+ QSet<QString> &changedSettings);
+
void currentChanged(const QModelIndex &index);
int currentRow() const;
void resetCurrentSettings(int row);
void applyCurrentSettings();
+ void apply() final
+ {
+ applyCurrentSettings();
+ LanguageClientManager::applySettings();
+
+ for (BaseSettings *setting : m_model.removed()) {
+ for (Client *client : LanguageClientManager::clientsForSetting(setting))
+ LanguageClientManager::shutdownClient(client);
+ }
+
+ int row = currentRow();
+ m_model.reset(LanguageClientManager::currentSettings());
+ resetCurrentSettings(row);
+ }
+
+ void finish()
+ {
+ m_settings.reset(LanguageClientManager::currentSettings());
+ m_changedSettings.clear();
+ }
+
private:
- LanguageClientSettingsModel &m_settings;
QTreeView *m_view = nullptr;
struct CurrentSettings {
BaseSettings *setting = nullptr;
@@ -123,30 +145,10 @@ private:
void addItem(const Utils::Id &clientTypeId);
void deleteItem();
-};
-class LanguageClientSettingsPage : public Core::IOptionsPage
-{
-public:
- LanguageClientSettingsPage();
- ~LanguageClientSettingsPage() override;
-
- void init();
-
- // IOptionsPage interface
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
- QList<BaseSettings *> settings() const;
- QList<BaseSettings *> changedSettings() const;
- void addSettings(BaseSettings *settings);
- void enableSettings(const QString &id, bool enable = true);
-
-private:
+ LanguageClientSettingsModel &m_settings;
+ QSet<QString> &m_changedSettings;
LanguageClientSettingsModel m_model;
- QSet<QString> m_changedSettings;
- QPointer<LanguageClientSettingsPageWidget> m_widget;
};
QMap<Utils::Id, ClientType> &clientTypes()
@@ -155,9 +157,11 @@ QMap<Utils::Id, ClientType> &clientTypes()
return types;
}
-LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings)
- : m_settings(settings)
- , m_view(new QTreeView())
+LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings,
+ QSet<QString> &changedSettings)
+ : m_view(new QTreeView())
+ , m_settings(settings)
+ , m_changedSettings(changedSettings)
{
auto mainLayout = new QVBoxLayout();
auto layout = new QHBoxLayout();
@@ -264,6 +268,23 @@ void LanguageClientSettingsPageWidget::deleteItem()
m_settings.removeRows(index.row());
}
+class LanguageClientSettingsPage : public Core::IOptionsPage
+{
+public:
+ LanguageClientSettingsPage();
+
+ void init();
+
+ QList<BaseSettings *> settings() const;
+ QList<BaseSettings *> changedSettings() const;
+ void addSettings(BaseSettings *settings);
+ void enableSettings(const QString &id, bool enable = true);
+
+private:
+ LanguageClientSettingsModel m_model;
+ QSet<QString> m_changedSettings;
+};
+
LanguageClientSettingsPage::LanguageClientSettingsPage()
{
setId(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
@@ -271,18 +292,13 @@ LanguageClientSettingsPage::LanguageClientSettingsPage()
setCategory(Constants::LANGUAGECLIENT_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr(Constants::LANGUAGECLIENT_SETTINGS_TR));
setCategoryIconPath(":/languageclient/images/settingscategory_languageclient.png");
- connect(&m_model, &LanguageClientSettingsModel::dataChanged, [this](const QModelIndex &index) {
+ setWidgetCreator([this] { return new LanguageClientSettingsPageWidget(m_model, m_changedSettings); });
+ QObject::connect(&m_model, &LanguageClientSettingsModel::dataChanged, [this](const QModelIndex &index) {
if (BaseSettings *setting = m_model.settingForIndex(index))
m_changedSettings << setting->m_id;
});
}
-LanguageClientSettingsPage::~LanguageClientSettingsPage()
-{
- if (m_widget)
- delete m_widget;
-}
-
void LanguageClientSettingsPage::init()
{
m_model.reset(LanguageClientSettings::fromSettings(Core::ICore::settings()));
@@ -290,39 +306,6 @@ void LanguageClientSettingsPage::init()
finish();
}
-QWidget *LanguageClientSettingsPage::widget()
-{
- if (!m_widget)
- m_widget = new LanguageClientSettingsPageWidget(m_model);
- return m_widget;
-}
-
-void LanguageClientSettingsPage::apply()
-{
- if (m_widget)
- m_widget->applyCurrentSettings();
- LanguageClientManager::applySettings();
-
- for (BaseSettings *setting : m_model.removed()) {
- for (Client *client : LanguageClientManager::clientsForSetting(setting))
- LanguageClientManager::shutdownClient(client);
- }
-
- if (m_widget) {
- int row = m_widget->currentRow();
- m_model.reset(LanguageClientManager::currentSettings());
- m_widget->resetCurrentSettings(row);
- } else {
- m_model.reset(LanguageClientManager::currentSettings());
- }
-}
-
-void LanguageClientSettingsPage::finish()
-{
- m_model.reset(LanguageClientManager::currentSettings());
- m_changedSettings.clear();
-}
-
QList<BaseSettings *> LanguageClientSettingsPage::settings() const
{
return m_model.settings();
@@ -1047,6 +1030,7 @@ bool LanguageFilter::operator!=(const LanguageFilter &other) const
TextEditor::BaseTextEditor *jsonEditor()
{
using namespace TextEditor;
+ using namespace Utils::Text;
BaseTextEditor *editor = PlainTextEditorFactory::createPlainTextEditor();
TextDocument *document = editor->textDocument();
TextEditorWidget *widget = editor->editorWidget();
@@ -1069,12 +1053,11 @@ TextEditor::BaseTextEditor *jsonEditor()
QJsonDocument::fromJson(content.toUtf8(), &error);
if (error.error == QJsonParseError::NoError)
return;
- const Utils::OptionalLineColumn lineColumn
- = Utils::Text::convertPosition(document->document(), error.offset);
- if (!lineColumn.has_value())
+ const Position pos = Position::fromPositionInDocument(document->document(), error.offset);
+ if (!pos.isValid())
return;
auto mark = new TextMark(Utils::FilePath(),
- lineColumn->line,
+ pos.line,
{::LanguageClient::Tr::tr("JSON Error"), jsonMarkId});
mark->setLineAnnotation(error.errorString());
mark->setColor(Utils::Theme::CodeModel_Error_TextMarkColor);
diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp
index b5f301d443..7069360b02 100644
--- a/src/plugins/languageclient/languageclientsymbolsupport.cpp
+++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp
@@ -13,7 +13,7 @@
#include <coreplugin/find/searchresultwindow.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
@@ -193,7 +193,7 @@ bool SymbolSupport::supportsFindUsages(TextEditor::TextDocument *document) const
struct ItemData
{
- Core::Search::TextRange range;
+ Utils::Text::Range range;
QVariant userData;
};
@@ -216,12 +216,12 @@ QStringList SymbolSupport::getFileContents(const Utils::FilePath &filePath)
return fileContent.split("\n");
}
-QList<Core::SearchResultItem> generateSearchResultItems(
+Utils::SearchResultItems generateSearchResultItems(
const QMap<Utils::FilePath, QList<ItemData>> &rangesInDocument,
Core::SearchResult *search = nullptr,
bool limitToProjects = false)
{
- QList<Core::SearchResultItem> result;
+ Utils::SearchResultItems result;
const bool renaming = search && search->supportsReplace();
QString oldSymbolName;
QVariantList userData;
@@ -233,11 +233,11 @@ QList<Core::SearchResultItem> generateSearchResultItems(
for (auto it = rangesInDocument.begin(); it != rangesInDocument.end(); ++it) {
const Utils::FilePath &filePath = it.key();
- Core::SearchResultItem item;
+ Utils::SearchResultItem item;
item.setFilePath(filePath);
item.setUseTextEditorFont(true);
if (renaming && limitToProjects) {
- const bool fileBelongsToProject = ProjectExplorer::SessionManager::projectForFile(
+ const bool fileBelongsToProject = ProjectExplorer::ProjectManager::projectForFile(
filePath);
item.setSelectForReplacement(fileBelongsToProject);
if (fileBelongsToProject
@@ -264,7 +264,7 @@ QList<Core::SearchResultItem> generateSearchResultItems(
return result;
}
-QList<Core::SearchResultItem> generateSearchResultItems(
+Utils::SearchResultItems generateSearchResultItems(
const LanguageClientArray<Location> &locations, const DocumentUri::PathMapper &pathMapper)
{
if (locations.isNull())
@@ -291,7 +291,7 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re
Tr::tr("Find References with %1 for:").arg(m_client->name()), "", wordUnderCursor);
search->addResults(generateSearchResultItems(*result, m_client->hostPathMapper()),
Core::SearchResult::AddOrdered);
- connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
+ connect(search, &Core::SearchResult::activated, [](const Utils::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
search->finishSearch(false);
@@ -463,7 +463,7 @@ void SymbolSupport::requestRename(const TextDocumentPositionParams &positionPara
search->popup();
}
-QList<Core::SearchResultItem> generateReplaceItems(const WorkspaceEdit &edits,
+Utils::SearchResultItems generateReplaceItems(const WorkspaceEdit &edits,
Core::SearchResult *search,
bool limitToProjects,
const DocumentUri::PathMapper &pathMapper)
@@ -506,7 +506,7 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams
if (callback)
search->makeNonInteractive(callback);
- connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) {
+ connect(search, &Core::SearchResult::activated, [](const Utils::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
connect(search, &Core::SearchResult::replaceTextChanged, this, [this, search, positionParams]() {
@@ -524,7 +524,7 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams
connect(search, &Core::SearchResult::replaceButtonClicked, this,
[this, search, resetConnection](const QString & /*replaceText*/,
- const QList<Core::SearchResultItem> &checkedItems) {
+ const Utils::SearchResultItems &checkedItems) {
applyRename(checkedItems, search);
disconnect(resetConnection);
});
@@ -571,12 +571,12 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search,
}
}
-void SymbolSupport::applyRename(const QList<Core::SearchResultItem> &checkedItems,
+void SymbolSupport::applyRename(const Utils::SearchResultItems &checkedItems,
Core::SearchResult *search)
{
QSet<Utils::FilePath> affectedNonOpenFilePaths;
QMap<Utils::FilePath, QList<TextEdit>> editsForDocuments;
- for (const Core::SearchResultItem &item : checkedItems) {
+ for (const Utils::SearchResultItem &item : checkedItems) {
const auto filePath = Utils::FilePath::fromString(item.path().value(0));
if (!m_client->documentForFilePath(filePath))
affectedNonOpenFilePaths << filePath;
@@ -616,12 +616,12 @@ QString SymbolSupport::derivePlaceholder(const QString &oldSymbol, const QString
return m_defaultSymbolMapper ? m_defaultSymbolMapper(oldSymbol) : oldSymbol;
}
-Core::Search::TextRange SymbolSupport::convertRange(const Range &range)
+Utils::Text::Range SymbolSupport::convertRange(const Range &range)
{
- auto convertPosition = [](const Position &pos) {
- return Core::Search::TextPosition(pos.line() + 1, pos.character());
+ const auto convertPosition = [](const Position &pos) {
+ return Utils::Text::Position{pos.line() + 1, pos.character()};
};
- return Core::Search::TextRange(convertPosition(range.start()), convertPosition(range.end()));
+ return {convertPosition(range.start()), convertPosition(range.end())};
}
void SymbolSupport::setDefaultRenamingSymbolMapper(const SymbolMapper &mapper)
diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h
index a4b910a9db..3dcc7b0ddc 100644
--- a/src/plugins/languageclient/languageclientsymbolsupport.h
+++ b/src/plugins/languageclient/languageclientsymbolsupport.h
@@ -5,18 +5,15 @@
#include "languageclient_global.h"
-#include <coreplugin/find/searchresultitem.h>
#include <texteditor/textdocument.h>
#include <languageserverprotocol/languagefeatures.h>
-#include <functional>
+#include <utils/searchresultitem.h>
-namespace Core {
-class SearchResult;
-class SearchResultItem;
-}
+#include <functional>
+namespace Core { class SearchResult; }
namespace LanguageServerProtocol { class MessageId; }
namespace LanguageClient {
@@ -46,7 +43,7 @@ public:
const std::function<void()> &callback = {},
bool preferLowerCaseFileNames = true);
- static Core::Search::TextRange convertRange(const LanguageServerProtocol::Range &range);
+ static Utils::Text::Range convertRange(const LanguageServerProtocol::Range &range);
static QStringList getFileContents(const Utils::FilePath &filePath);
using SymbolMapper = std::function<QString(const QString &)>;
@@ -76,7 +73,7 @@ private:
const std::function<void()> &callback, bool preferLowerCaseFileNames);
void handleRenameResponse(Core::SearchResult *search,
const LanguageServerProtocol::RenameRequest::Response &response);
- void applyRename(const QList<Core::SearchResultItem> &checkedItems, Core::SearchResult *search);
+ void applyRename(const Utils::SearchResultItems &checkedItems, Core::SearchResult *search);
QString derivePlaceholder(const QString &oldSymbol, const QString &newSymbol);
Client *m_client = nullptr;
diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp
index 44168d1dd2..c421b2a937 100644
--- a/src/plugins/languageclient/languageclientutils.cpp
+++ b/src/plugins/languageclient/languageclientutils.cpp
@@ -97,11 +97,10 @@ void applyTextEdit(TextDocumentManipulatorInterface &manipulator,
const TextEdit &edit,
bool newTextIsSnippet)
{
- using namespace Utils::Text;
const Range range = edit.range();
const QTextDocument *doc = manipulator.textCursorAt(manipulator.currentPosition()).document();
- const int start = positionInText(doc, range.start().line() + 1, range.start().character() + 1);
- const int end = positionInText(doc, range.end().line() + 1, range.end().character() + 1);
+ const int start = Text::positionInText(doc, range.start().line() + 1, range.start().character() + 1);
+ const int end = Text::positionInText(doc, range.end().line() + 1, range.end().character() + 1);
if (newTextIsSnippet) {
manipulator.replace(start, end - start, {});
manipulator.insertCodeSnippet(start, edit.newText(), &parseSnippet);
diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp
index ddcfc6b43b..da03b2e7c5 100644
--- a/src/plugins/languageclient/locatorfilter.cpp
+++ b/src/plugins/languageclient/locatorfilter.cpp
@@ -3,358 +3,290 @@
#include "locatorfilter.h"
-#include "documentsymbolcache.h"
-#include "languageclient_global.h"
+#include "clientrequesttask.h"
+#include "currentdocumentsymbolsrequest.h"
#include "languageclientmanager.h"
#include "languageclienttr.h"
-#include "languageclientutils.h"
-#include <coreplugin/editormanager/editormanager.h>
-
-#include <languageserverprotocol/lsptypes.h>
-#include <languageserverprotocol/servercapabilities.h>
-
-#include <texteditor/textdocument.h>
-#include <texteditor/texteditor.h>
+#include <extensionsystem/pluginmanager.h>
+#include <utils/async.h>
#include <utils/fuzzymatcher.h>
-#include <utils/linecolumn.h>
-#include <QFutureWatcher>
#include <QRegularExpression>
+using namespace Core;
using namespace LanguageServerProtocol;
+using namespace Utils;
namespace LanguageClient {
-DocumentLocatorFilter::DocumentLocatorFilter()
+void filterResults(QPromise<void> &promise, const LocatorStorage &storage, Client *client,
+ const QList<SymbolInformation> &results, const QList<SymbolKind> &filter)
{
- setId(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_ID);
- setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME));
- setDescription(
- Tr::tr("Matches all symbols from the current document, based on a language server."));
- setDefaultShortcutString(".");
- setDefaultIncludedByDefault(false);
- setPriority(ILocatorFilter::Low);
- connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
- this, &DocumentLocatorFilter::updateCurrentClient);
-}
-
-void DocumentLocatorFilter::updateCurrentClient()
-{
- resetSymbols();
- disconnect(m_resetSymbolsConnection);
-
- TextEditor::TextDocument *document = TextEditor::TextDocument::currentTextDocument();
- if (Client *client = LanguageClientManager::clientForDocument(document);
- client && (client->locatorsEnabled() || m_forced)) {
-
- setEnabled(!m_forced);
- if (m_symbolCache != client->documentSymbolCache()) {
- disconnect(m_updateSymbolsConnection);
- m_symbolCache = client->documentSymbolCache();
- m_updateSymbolsConnection = connect(m_symbolCache, &DocumentSymbolCache::gotSymbols,
- this, &DocumentLocatorFilter::updateSymbols);
- }
- m_resetSymbolsConnection = connect(document, &Core::IDocument::contentsChanged,
- this, &DocumentLocatorFilter::resetSymbols);
- m_currentUri = client->hostPathToServerUri(document->filePath());
- m_pathMapper = client->hostPathMapper();
- } else {
- disconnect(m_updateSymbolsConnection);
- m_symbolCache.clear();
- m_currentUri.clear();
- setEnabled(false);
- m_pathMapper = DocumentUri::PathMapper();
- }
-}
-
-void DocumentLocatorFilter::updateSymbols(const DocumentUri &uri,
- const DocumentSymbolsResult &symbols)
-{
- if (uri != m_currentUri)
+ const auto doFilter = [&](const SymbolInformation &info) {
+ return filter.contains(SymbolKind(info.kind()));
+ };
+ if (promise.isCanceled())
return;
- QMutexLocker locker(&m_mutex);
- m_currentSymbols = symbols;
- emit symbolsUpToDate(QPrivateSignal());
+ const QList<SymbolInformation> filteredResults = filter.isEmpty() ? results
+ : Utils::filtered(results, doFilter);
+ const auto generateEntry = [client](const SymbolInformation &info) {
+ LocatorFilterEntry entry;
+ entry.displayName = info.name();
+ if (std::optional<QString> container = info.containerName())
+ entry.extraInfo = container.value_or(QString());
+ entry.displayIcon = symbolIcon(info.kind());
+ entry.linkForEditor = info.location().toLink(client->hostPathMapper());
+ return entry;
+ };
+ storage.reportOutput(Utils::transform(filteredResults, generateEntry));
}
-void DocumentLocatorFilter::resetSymbols()
+LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount,
+ const QList<SymbolKind> &filter)
{
- QMutexLocker locker(&m_mutex);
- m_currentSymbols.reset();
-}
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+ TreeStorage<QList<SymbolInformation>> resultStorage;
+
+ const auto onQuerySetup = [storage, client, maxResultCount](WorkspaceSymbolRequestTask &request) {
+ request.setClient(client);
+ WorkspaceSymbolParams params;
+ params.setQuery(storage->input());
+ if (maxResultCount > 0)
+ params.setLimit(maxResultCount);
+ request.setParams(params);
+ };
+ const auto onQueryDone = [resultStorage](const WorkspaceSymbolRequestTask &request) {
+ const std::optional<LanguageClientArray<SymbolInformation>> result
+ = request.response().result();
+ if (result.has_value())
+ *resultStorage = result->toList();
+ };
-static Core::LocatorFilterEntry generateLocatorEntry(const SymbolInformation &info,
- Core::ILocatorFilter *filter,
- DocumentUri::PathMapper pathMapper)
-{
- Core::LocatorFilterEntry entry;
- entry.filter = filter;
- entry.displayName = info.name();
- if (std::optional<QString> container = info.containerName())
- entry.extraInfo = container.value_or(QString());
- entry.displayIcon = symbolIcon(info.kind());
- entry.internalData = QVariant::fromValue(info.location().toLink(pathMapper));
- return entry;
-}
+ const auto onFilterSetup = [storage, resultStorage, client, filter](Async<void> &async) {
+ const QList<SymbolInformation> results = *resultStorage;
+ if (results.isEmpty())
+ return TaskAction::StopWithDone;
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(filterResults, *storage, client, results, filter);
+ return TaskAction::Continue;
+ };
-Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(const SymbolInformation &info)
-{
- QTC_ASSERT(m_pathMapper, return {});
- return LanguageClient::generateLocatorEntry(info, this, m_pathMapper);
+ const Group root {
+ Storage(resultStorage),
+ SymbolRequest(onQuerySetup, onQueryDone),
+ AsyncTask<void>(onFilterSetup)
+ };
+ return {root, storage};
}
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateLocatorEntries(
- const SymbolInformation &info, const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent)
+LocatorMatcherTask allSymbolsMatcher(Client *client, int maxResultCount)
{
- Q_UNUSED(parent)
- if (regexp.match(info.name()).hasMatch())
- return {generateLocatorEntry(info)};
- return {};
+ return locatorMatcher(client, maxResultCount, {});
}
-Core::LocatorFilterEntry DocumentLocatorFilter::generateLocatorEntry(
- const DocumentSymbol &info,
- const Core::LocatorFilterEntry &parent)
+LocatorMatcherTask classMatcher(Client *client, int maxResultCount)
{
- Q_UNUSED(parent)
- Core::LocatorFilterEntry entry;
- entry.filter = this;
- entry.displayName = info.name();
- if (std::optional<QString> detail = info.detail())
- entry.extraInfo = detail.value_or(QString());
- entry.displayIcon = symbolIcon(info.kind());
- const Position &pos = info.range().start();
- entry.internalData = QVariant::fromValue(Utils::LineColumn(pos.line(), pos.character()));
- return entry;
+ return locatorMatcher(client, maxResultCount, {SymbolKind::Class, SymbolKind::Struct});
}
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateLocatorEntries(
- const DocumentSymbol &info, const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent)
+LocatorMatcherTask functionMatcher(Client *client, int maxResultCount)
{
- QList<Core::LocatorFilterEntry> entries;
- const QList<DocumentSymbol> children = info.children().value_or(QList<DocumentSymbol>());
- const bool hasMatch = regexp.match(info.name()).hasMatch();
- Core::LocatorFilterEntry entry;
- if (hasMatch || !children.isEmpty())
- entry = generateLocatorEntry(info, parent);
- if (hasMatch)
- entries << entry;
- for (const DocumentSymbol &child : children)
- entries << generateLocatorEntries(child, regexp, entry);
- return entries;
+ return locatorMatcher(client, maxResultCount,
+ {SymbolKind::Method, SymbolKind::Function, SymbolKind::Constructor});
}
-template<class T>
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::generateEntries(const QList<T> &list,
- const QString &filter)
+static void filterCurrentResults(QPromise<void> &promise, const LocatorStorage &storage,
+ const CurrentDocumentSymbolsData &currentSymbolsData)
{
- QList<Core::LocatorFilterEntry> entries;
- FuzzyMatcher::CaseSensitivity caseSensitivity
- = ILocatorFilter::caseSensitivity(filter) == Qt::CaseSensitive
- ? FuzzyMatcher::CaseSensitivity::CaseSensitive
- : FuzzyMatcher::CaseSensitivity::CaseInsensitive;
- const QRegularExpression regexp = FuzzyMatcher::createRegExp(filter, caseSensitivity);
- if (!regexp.isValid())
- return entries;
-
- for (const T &item : list)
- entries << generateLocatorEntries(item, regexp, {});
- return entries;
+ Q_UNUSED(promise)
+ const auto docSymbolModifier = [](LocatorFilterEntry &entry, const DocumentSymbol &info,
+ const LocatorFilterEntry &parent) {
+ Q_UNUSED(parent)
+ entry.displayName = info.name();
+ if (std::optional<QString> detail = info.detail())
+ entry.extraInfo = *detail;
+ };
+ // TODO: Pass promise into currentSymbols
+ storage.reportOutput(LanguageClient::currentDocumentSymbols(storage.input(), currentSymbolsData,
+ docSymbolModifier));
}
-void DocumentLocatorFilter::prepareSearch(const QString &/*entry*/)
+LocatorMatcherTask currentDocumentMatcher()
{
- QMutexLocker locker(&m_mutex);
- if (m_symbolCache && !m_currentSymbols.has_value()) {
- locker.unlock();
- m_symbolCache->requestSymbols(m_currentUri, Schedule::Now);
- }
-}
+ using namespace Tasking;
-QList<Core::LocatorFilterEntry> DocumentLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
-{
- QMutexLocker locker(&m_mutex);
- if (!m_symbolCache)
- return {};
- if (!m_currentSymbols.has_value()) {
- QEventLoop loop;
- connect(this, &DocumentLocatorFilter::symbolsUpToDate, &loop, [&]() { loop.exit(1); });
- QFutureWatcher<Core::LocatorFilterEntry> watcher;
- connect(&watcher,
- &QFutureWatcher<Core::LocatorFilterEntry>::canceled,
- &loop,
- &QEventLoop::quit);
- watcher.setFuture(future.future());
- locker.unlock();
- if (!loop.exec())
- return {};
- locker.relock();
- }
+ TreeStorage<LocatorStorage> storage;
+ TreeStorage<CurrentDocumentSymbolsData> resultStorage;
- QTC_ASSERT(m_currentSymbols.has_value(), return {});
+ const auto onQuerySetup = [](CurrentDocumentSymbolsRequest &request) {
+ Q_UNUSED(request)
+ };
+ const auto onQueryDone = [resultStorage](const CurrentDocumentSymbolsRequest &request) {
+ *resultStorage = request.currentDocumentSymbolsData();
+ };
- if (auto list = std::get_if<QList<DocumentSymbol>>(&*m_currentSymbols))
- return generateEntries(*list, entry);
- else if (auto list = std::get_if<QList<SymbolInformation>>(&*m_currentSymbols))
- return generateEntries(*list, entry);
+ const auto onFilterSetup = [storage, resultStorage](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage);
+ };
- return {};
+ const Group root {
+ Storage(resultStorage),
+ CurrentDocumentSymbolsRequestTask(onQuerySetup, onQueryDone),
+ AsyncTask<void>(onFilterSetup)
+ };
+ return {root, storage};
}
-void DocumentLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString * /*newText*/,
- int * /*selectionStart*/,
- int * /*selectionLength*/) const
+using MatcherCreator = std::function<Core::LocatorMatcherTask(Client *, int)>;
+
+static MatcherCreator creatorForType(MatcherType type)
{
- if (selection.internalData.canConvert<Utils::LineColumn>()) {
- QTC_ASSERT(m_pathMapper, return);
- auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData);
- const Utils::Link link(m_currentUri.toFilePath(m_pathMapper),
- lineColumn.line + 1,
- lineColumn.column);
- Core::EditorManager::openEditorAt(link, {}, Core::EditorManager::AllowExternalEditor);
- } else if (selection.internalData.canConvert<Utils::Link>()) {
- Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
- {},
- Core::EditorManager::AllowExternalEditor);
+ switch (type) {
+ case MatcherType::AllSymbols: return &allSymbolsMatcher;
+ case MatcherType::Classes: return &classMatcher;
+ case MatcherType::Functions: return &functionMatcher;
+ case MatcherType::CurrentDocumentSymbols: QTC_CHECK(false); return {};
}
+ return {};
}
-WorkspaceLocatorFilter::WorkspaceLocatorFilter()
- : WorkspaceLocatorFilter(QVector<SymbolKind>())
-{}
+LocatorMatcherTasks languageClientMatchers(MatcherType type, const QList<Client *> &clients,
+ int maxResultCount)
+{
+ if (type == MatcherType::CurrentDocumentSymbols)
+ return {currentDocumentMatcher()};
+ const MatcherCreator creator = creatorForType(type);
+ if (!creator)
+ return {};
+ LocatorMatcherTasks matchers;
+ for (Client *client : clients)
+ matchers << creator(client, maxResultCount);
+ return matchers;
+}
-WorkspaceLocatorFilter::WorkspaceLocatorFilter(const QVector<SymbolKind> &filter)
- : m_filterKinds(filter)
+LanguageCurrentDocumentFilter::LanguageCurrentDocumentFilter()
{
- setId(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_ID);
- setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME));
- setDefaultShortcutString(":");
- setDefaultIncludedByDefault(false);
+ setId(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_DOCUMENT_FILTER_DESCRIPTION));
+ setDefaultShortcutString(".");
setPriority(ILocatorFilter::Low);
}
-void WorkspaceLocatorFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks LanguageCurrentDocumentFilter::matchers()
{
- prepareSearch(entry, LanguageClientManager::clients(), false);
+ return {currentDocumentMatcher()};
}
-void WorkspaceLocatorFilter::prepareSearch(const QString &entry, const QList<Client *> &clients)
+static LocatorFilterEntry entryForSymbolInfo(const SymbolInformation &info,
+ const DocumentUri::PathMapper &pathMapper)
{
- prepareSearch(entry, clients, true);
+ LocatorFilterEntry entry;
+ entry.displayName = info.name();
+ if (std::optional<QString> container = info.containerName())
+ entry.extraInfo = container.value_or(QString());
+ entry.displayIcon = symbolIcon(info.kind());
+ entry.linkForEditor = info.location().toLink(pathMapper);
+ return entry;
}
-void WorkspaceLocatorFilter::prepareSearch(const QString &entry,
- const QList<Client *> &clients,
- bool force)
+LocatorFilterEntries entriesForSymbolsInfo(const QList<SymbolInformation> &infoList,
+ const QRegularExpression &regexp, const DocumentUri::PathMapper &pathMapper)
{
- m_pendingRequests.clear();
- m_results.clear();
-
- WorkspaceSymbolParams params;
- params.setQuery(entry);
- if (m_maxResultCount > 0)
- params.setLimit(m_maxResultCount);
-
- QMutexLocker locker(&m_mutex);
- for (auto client : std::as_const(clients)) {
- if (!client->reachable())
- continue;
- if (!(force || client->locatorsEnabled()))
- continue;
- std::optional<std::variant<bool, WorkDoneProgressOptions>> capability
- = client->capabilities().workspaceSymbolProvider();
- if (!capability.has_value())
- continue;
- if (std::holds_alternative<bool>(*capability) && !std::get<bool>(*capability))
- continue;
- WorkspaceSymbolRequest request(params);
- request.setResponseCallback(
- [this, client](const WorkspaceSymbolRequest::Response &response) {
- handleResponse(client, response);
- });
- m_pendingRequests[client] = request.id();
- client->sendMessage(request);
+ QTC_ASSERT(pathMapper, return {});
+ LocatorFilterEntries entries;
+ for (const SymbolInformation &info : infoList) {
+ if (regexp.match(info.name()).hasMatch())
+ entries << LanguageClient::entryForSymbolInfo(info, pathMapper);
}
+ return entries;
}
-QList<Core::LocatorFilterEntry> WorkspaceLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString & /*entry*/)
+LocatorFilterEntries entriesForDocSymbols(const QList<DocumentSymbol> &infoList,
+ const QRegularExpression &regexp, const FilePath &filePath,
+ const DocSymbolModifier &docSymbolModifier, const LocatorFilterEntry &parent = {})
{
- QMutexLocker locker(&m_mutex);
- if (!m_pendingRequests.isEmpty()) {
- QEventLoop loop;
- connect(this, &WorkspaceLocatorFilter::allRequestsFinished, &loop, [&]() { loop.exit(1); });
- QFutureWatcher<Core::LocatorFilterEntry> watcher;
- connect(&watcher,
- &QFutureWatcher<Core::LocatorFilterEntry>::canceled,
- &loop,
- &QEventLoop::quit);
- watcher.setFuture(future.future());
- locker.unlock();
- if (!loop.exec())
- return {};
-
- locker.relock();
+ LocatorFilterEntries entries;
+ for (const DocumentSymbol &info : infoList) {
+ const QList<DocumentSymbol> children = info.children().value_or(QList<DocumentSymbol>());
+ const bool hasMatch = regexp.match(info.name()).hasMatch();
+ LocatorFilterEntry entry;
+ if (hasMatch) {
+ entry.displayIcon = LanguageClient::symbolIcon(info.kind());
+ const Position &pos = info.range().start();
+ entry.linkForEditor = {filePath, pos.line() + 1, pos.character()};
+ docSymbolModifier(entry, info, parent);
+ entries << entry;
+ } else {
+ entry = parent;
+ }
+ entries << entriesForDocSymbols(children, regexp, filePath, docSymbolModifier, entry);
}
+ return entries;
+}
+Core::LocatorFilterEntries currentDocumentSymbols(const QString &input,
+ const CurrentDocumentSymbolsData &currentSymbolsData,
+ const DocSymbolModifier &docSymbolModifier)
+{
+ const Qt::CaseSensitivity caseSensitivity = ILocatorFilter::caseSensitivity(input);
+ const QRegularExpression regExp = ILocatorFilter::createRegExp(input, caseSensitivity);
+ if (!regExp.isValid())
+ return {};
- if (!m_filterKinds.isEmpty()) {
- m_results = Utils::filtered(m_results, [&](const SymbolInfoWithPathMapper &info) {
- return m_filterKinds.contains(SymbolKind(info.symbol.kind()));
- });
- }
- auto generateEntry = [this](const SymbolInfoWithPathMapper &info) {
- return generateLocatorEntry(info.symbol, this, info.mapper);
- };
- return Utils::transform(m_results, generateEntry).toList();
+ if (auto list = std::get_if<QList<DocumentSymbol>>(&currentSymbolsData.m_symbols))
+ return entriesForDocSymbols(*list, regExp, currentSymbolsData.m_filePath, docSymbolModifier);
+ else if (auto list = std::get_if<QList<SymbolInformation>>(&currentSymbolsData.m_symbols))
+ return entriesForSymbolsInfo(*list, regExp, currentSymbolsData.m_pathMapper);
+ return {};
}
-void WorkspaceLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString * /*newText*/,
- int * /*selectionStart*/,
- int * /*selectionLength*/) const
+LanguageAllSymbolsFilter::LanguageAllSymbolsFilter()
{
- if (selection.internalData.canConvert<Utils::Link>())
- Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
- {},
- Core::EditorManager::AllowExternalEditor);
+ setId(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_ID);
+ setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_FILTER_DESCRIPTION));
+ setDefaultShortcutString(":");
+ setPriority(ILocatorFilter::Low);
}
-void WorkspaceLocatorFilter::handleResponse(Client *client,
- const WorkspaceSymbolRequest::Response &response)
+LocatorMatcherTasks LanguageAllSymbolsFilter::matchers()
{
- QMutexLocker locker(&m_mutex);
- m_pendingRequests.remove(client);
- auto result = response.result().value_or(LanguageClientArray<SymbolInformation>());
- if (!result.isNull())
- m_results.append(
- Utils::transform(result.toList().toVector(), [client](const SymbolInformation &info) {
- return SymbolInfoWithPathMapper{info, client->hostPathMapper()};
- }));
- if (m_pendingRequests.isEmpty())
- emit allRequestsFinished(QPrivateSignal());
+ return languageClientMatchers(MatcherType::AllSymbols,
+ Utils::filtered(LanguageClientManager::clients(), &Client::locatorsEnabled));
}
-WorkspaceClassLocatorFilter::WorkspaceClassLocatorFilter()
- : WorkspaceLocatorFilter({SymbolKind::Class, SymbolKind::Struct})
+LanguageClassesFilter::LanguageClassesFilter()
{
setId(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_ID);
setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DESCRIPTION));
setDefaultShortcutString("c");
}
-WorkspaceMethodLocatorFilter::WorkspaceMethodLocatorFilter()
- : WorkspaceLocatorFilter({SymbolKind::Method, SymbolKind::Function, SymbolKind::Constructor})
+LocatorMatcherTasks LanguageClassesFilter::matchers()
+{
+ return languageClientMatchers(MatcherType::Classes,
+ Utils::filtered(LanguageClientManager::clients(), &Client::locatorsEnabled));
+}
+
+LanguageFunctionsFilter::LanguageFunctionsFilter()
{
setId(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID);
setDisplayName(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME));
+ setDescription(Tr::tr(Constants::LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DESCRIPTION));
setDefaultShortcutString("m");
}
+LocatorMatcherTasks LanguageFunctionsFilter::matchers()
+{
+ return languageClientMatchers(MatcherType::Functions,
+ Utils::filtered(LanguageClientManager::clients(), &Client::locatorsEnabled));
+}
+
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/locatorfilter.h b/src/plugins/languageclient/locatorfilter.h
index 3eebd907a2..39d135723c 100644
--- a/src/plugins/languageclient/locatorfilter.h
+++ b/src/plugins/languageclient/locatorfilter.h
@@ -3,129 +3,60 @@
#pragma once
-#include "client.h"
#include "languageclient_global.h"
#include <coreplugin/locator/ilocatorfilter.h>
-#include <languageserverprotocol/languagefeatures.h>
-#include <languageserverprotocol/lsptypes.h>
-#include <languageserverprotocol/workspace.h>
-
-#include <QPointer>
-#include <QVector>
-
-namespace Core { class IEditor; }
+namespace LanguageServerProtocol { class DocumentSymbol; };
namespace LanguageClient {
-class LANGUAGECLIENT_EXPORT DocumentLocatorFilter : public Core::ILocatorFilter
-{
- Q_OBJECT
-public:
- DocumentLocatorFilter();
+class Client;
+class CurrentDocumentSymbolsData;
- void updateCurrentClient();
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const override;
+using DocSymbolModifier = std::function<void(Core::LocatorFilterEntry &,
+ const LanguageServerProtocol::DocumentSymbol &, const Core::LocatorFilterEntry &)>;
-signals:
- void symbolsUpToDate(QPrivateSignal);
+Core::LocatorFilterEntries LANGUAGECLIENT_EXPORT currentDocumentSymbols(const QString &input,
+ const CurrentDocumentSymbolsData &currentSymbolsData, const DocSymbolModifier &docSymbolModifier);
-protected:
- void forceUse() { m_forced = true; }
+Core::LocatorMatcherTasks LANGUAGECLIENT_EXPORT languageClientMatchers(
+ Core::MatcherType type, const QList<Client *> &clients = {}, int maxResultCount = 0);
- QPointer<DocumentSymbolCache> m_symbolCache;
- LanguageServerProtocol::DocumentUri m_currentUri;
+class LanguageAllSymbolsFilter : public Core::ILocatorFilter
+{
+public:
+ LanguageAllSymbolsFilter();
private:
- void updateSymbols(const LanguageServerProtocol::DocumentUri &uri,
- const LanguageServerProtocol::DocumentSymbolsResult &symbols);
- void resetSymbols();
-
- template<class T>
- QList<Core::LocatorFilterEntry> generateEntries(const QList<T> &list, const QString &filter);
- QList<Core::LocatorFilterEntry> generateLocatorEntries(
- const LanguageServerProtocol::SymbolInformation &info,
- const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent);
- QList<Core::LocatorFilterEntry> generateLocatorEntries(
- const LanguageServerProtocol::DocumentSymbol &info,
- const QRegularExpression &regexp,
- const Core::LocatorFilterEntry &parent);
- virtual Core::LocatorFilterEntry generateLocatorEntry(
- const LanguageServerProtocol::DocumentSymbol &info,
- const Core::LocatorFilterEntry &parent);
- virtual Core::LocatorFilterEntry generateLocatorEntry(
- const LanguageServerProtocol::SymbolInformation &info);
-
- QMutex m_mutex;
- QMetaObject::Connection m_updateSymbolsConnection;
- QMetaObject::Connection m_resetSymbolsConnection;
- std::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols;
- LanguageServerProtocol::DocumentUri::PathMapper m_pathMapper;
- bool m_forced = false;
+ Core::LocatorMatcherTasks matchers() final;
};
-class LANGUAGECLIENT_EXPORT WorkspaceLocatorFilter : public Core::ILocatorFilter
+class LanguageClassesFilter : public Core::ILocatorFilter
{
- Q_OBJECT
public:
- WorkspaceLocatorFilter();
-
- /// request workspace symbols for all clients with enabled locator
- void prepareSearch(const QString &entry) override;
- /// force request workspace symbols for all given clients
- void prepareSearch(const QString &entry, const QList<Client *> &clients);
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const override;
-
-signals:
- void allRequestsFinished(QPrivateSignal);
-
-protected:
- explicit WorkspaceLocatorFilter(const QVector<LanguageServerProtocol::SymbolKind> &filter);
-
- void setMaxResultCount(qint64 limit) { m_maxResultCount = limit; }
+ LanguageClassesFilter();
private:
- void prepareSearch(const QString &entry, const QList<Client *> &clients, bool force);
- void handleResponse(Client *client,
- const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response);
-
- QMutex m_mutex;
-
- struct SymbolInfoWithPathMapper
- {
- LanguageServerProtocol::SymbolInformation symbol;
- LanguageServerProtocol::DocumentUri::PathMapper mapper;
- };
-
- QMap<Client *, LanguageServerProtocol::MessageId> m_pendingRequests;
- QVector<SymbolInfoWithPathMapper> m_results;
- QVector<LanguageServerProtocol::SymbolKind> m_filterKinds;
- qint64 m_maxResultCount = 0;
+ Core::LocatorMatcherTasks matchers() final;
};
-class LANGUAGECLIENT_EXPORT WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter
+class LanguageFunctionsFilter : public Core::ILocatorFilter
{
public:
- WorkspaceClassLocatorFilter();
+ LanguageFunctionsFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
-class LANGUAGECLIENT_EXPORT WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter
+class LanguageCurrentDocumentFilter : public Core::ILocatorFilter
{
public:
- WorkspaceMethodLocatorFilter();
+ LanguageCurrentDocumentFilter();
+
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
} // namespace LanguageClient
diff --git a/src/plugins/macros/macrolocatorfilter.cpp b/src/plugins/macros/macrolocatorfilter.cpp
index 43a9a48945..f43758bd03 100644
--- a/src/plugins/macros/macrolocatorfilter.cpp
+++ b/src/plugins/macros/macrolocatorfilter.cpp
@@ -8,13 +8,13 @@
#include "macrostr.h"
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/editormanager/ieditor.h>
-#include <coreplugin/icore.h>
#include <QPixmap>
+using namespace Core;
using namespace Macros;
using namespace Macros::Internal;
+using namespace Utils;
MacroLocatorFilter::MacroLocatorFilter()
: m_icon(QPixmap(":/macros/images/macro.png"))
@@ -26,54 +26,50 @@ MacroLocatorFilter::MacroLocatorFilter()
setDefaultShortcutString("rm");
}
-MacroLocatorFilter::~MacroLocatorFilter() = default;
-
-QList<Core::LocatorFilterEntry> MacroLocatorFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+LocatorMatcherTasks MacroLocatorFilter::matchers()
{
- Q_UNUSED(future)
- QList<Core::LocatorFilterEntry> goodEntries;
- QList<Core::LocatorFilterEntry> betterEntries;
-
- const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(entry);
-
- const QMap<QString, Macro*> &macros = MacroManager::macros();
-
- for (auto it = macros.cbegin(), end = macros.cend(); it != end; ++it) {
- const QString displayName = it.key();
- const QString description = it.value()->description();
-
- int index = displayName.indexOf(entry, 0, entryCaseSensitivity);
- Core::LocatorFilterEntry::HighlightInfo::DataType hDataType = Core::LocatorFilterEntry::HighlightInfo::DisplayName;
- if (index < 0) {
- index = description.indexOf(entry, 0, entryCaseSensitivity);
- hDataType = Core::LocatorFilterEntry::HighlightInfo::ExtraInfo;
- }
-
- if (index >= 0) {
- Core::LocatorFilterEntry filterEntry(this, displayName, {}, m_icon);
- filterEntry.extraInfo = description;
- filterEntry.highlightInfo = Core::LocatorFilterEntry::HighlightInfo(index, entry.length(), hDataType);
-
- if (index == 0)
- betterEntries.append(filterEntry);
- else
- goodEntries.append(filterEntry);
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, icon = m_icon] {
+ const QString input = storage->input();
+ const Qt::CaseSensitivity entryCaseSensitivity = caseSensitivity(input);
+ const QMap<QString, Macro *> &macros = MacroManager::macros();
+ LocatorFilterEntries goodEntries;
+ LocatorFilterEntries betterEntries;
+ for (auto it = macros.cbegin(); it != macros.cend(); ++it) {
+ const QString displayName = it.key();
+ const QString description = it.value()->description();
+ int index = displayName.indexOf(input, 0, entryCaseSensitivity);
+ LocatorFilterEntry::HighlightInfo::DataType hDataType
+ = LocatorFilterEntry::HighlightInfo::DisplayName;
+ if (index < 0) {
+ index = description.indexOf(input, 0, entryCaseSensitivity);
+ hDataType = LocatorFilterEntry::HighlightInfo::ExtraInfo;
+ }
+
+ if (index >= 0) {
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = displayName;
+ filterEntry.acceptor = [displayName] {
+ IEditor *editor = EditorManager::currentEditor();
+ if (editor)
+ editor->widget()->setFocus(Qt::OtherFocusReason);
+ MacroManager::instance()->executeMacro(displayName);
+ return AcceptResult();
+ };
+ filterEntry.displayIcon = icon;
+ filterEntry.extraInfo = description;
+ filterEntry.highlightInfo = LocatorFilterEntry::HighlightInfo(index, input.length(),
+ hDataType);
+ if (index == 0)
+ betterEntries.append(filterEntry);
+ else
+ goodEntries.append(filterEntry);
+ }
}
- }
- betterEntries.append(goodEntries);
- return betterEntries;
-}
-
-void MacroLocatorFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- // Give the focus back to the editor
- Core::IEditor *editor = Core::EditorManager::currentEditor();
- if (editor)
- editor->widget()->setFocus(Qt::OtherFocusReason);
-
- MacroManager::instance()->executeMacro(selection.displayName);
+ storage->reportOutput(betterEntries + goodEntries);
+ };
+ return {{Sync(onSetup), storage}};
}
diff --git a/src/plugins/macros/macrolocatorfilter.h b/src/plugins/macros/macrolocatorfilter.h
index 61fa7a34db..b6acc6d2c7 100644
--- a/src/plugins/macros/macrolocatorfilter.h
+++ b/src/plugins/macros/macrolocatorfilter.h
@@ -5,27 +5,17 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QIcon>
-
-namespace Macros {
-namespace Internal {
+namespace Macros::Internal {
class MacroLocatorFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
MacroLocatorFilter();
- ~MacroLocatorFilter() override;
-
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
private:
+ Core::LocatorMatcherTasks matchers() final;
+
const QIcon m_icon;
};
-} // namespace Internal
-} // namespace Macros
+} // namespace Macros::Internal
diff --git a/src/plugins/macros/macrooptionswidget.cpp b/src/plugins/macros/macrooptionswidget.cpp
index c6a94d4498..cd11ab64ef 100644
--- a/src/plugins/macros/macrooptionswidget.cpp
+++ b/src/plugins/macros/macrooptionswidget.cpp
@@ -49,7 +49,7 @@ MacroOptionsWidget::MacroOptionsWidget()
m_macroGroup = new QGroupBox(Tr::tr("Macro"), this);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Tr::tr("Description:"), m_description
diff --git a/src/plugins/macros/savedialog.cpp b/src/plugins/macros/savedialog.cpp
index 3d8f7d6ada..cb45f96da0 100644
--- a/src/plugins/macros/savedialog.cpp
+++ b/src/plugins/macros/savedialog.cpp
@@ -12,8 +12,6 @@
#include <QLineEdit>
#include <QRegularExpressionValidator>
-using namespace Utils;
-
namespace Macros::Internal {
SaveDialog::SaveDialog(QWidget *parent) :
diff --git a/src/plugins/marketplace/productlistmodel.cpp b/src/plugins/marketplace/productlistmodel.cpp
index cdfb8d79e5..36b047abc2 100644
--- a/src/plugins/marketplace/productlistmodel.cpp
+++ b/src/plugins/marketplace/productlistmodel.cpp
@@ -269,7 +269,7 @@ void SectionedProducts::onImageDownloadFinished(QNetworkReply *reply)
if (pixmap.loadFromData(data, imageFormat.toLatin1())) {
const QString url = imageUrl.toString();
const int dpr = qApp->devicePixelRatio();
- pixmap = pixmap.scaled(ListModel::defaultImageSize * dpr,
+ pixmap = pixmap.scaled(WelcomePageHelpers::GridItemImageSize * dpr,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
pixmap.setDevicePixelRatio(dpr);
diff --git a/src/plugins/mcusupport/mcukitinformation.cpp b/src/plugins/mcusupport/mcukitinformation.cpp
index 801b7cdecc..2fd6f846e1 100644
--- a/src/plugins/mcusupport/mcukitinformation.cpp
+++ b/src/plugins/mcusupport/mcukitinformation.cpp
@@ -22,7 +22,7 @@ public:
void makeReadOnly() override {}
void refresh() override {}
- void addToLayout(Utils::Layouting::LayoutBuilder &) override {}
+ void addToLayout(Layouting::LayoutItem &) override {}
};
} // anonymous namespace
diff --git a/src/plugins/mcusupport/mcuqmlprojectnode.h b/src/plugins/mcusupport/mcuqmlprojectnode.h
index 2a06e5cde5..bf40a696f6 100644
--- a/src/plugins/mcusupport/mcuqmlprojectnode.h
+++ b/src/plugins/mcusupport/mcuqmlprojectnode.h
@@ -9,7 +9,7 @@
#include <utils/filepath.h>
#include <utils/osspecificaspects.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs
index e6aec151a0..b0fa090b41 100644
--- a/src/plugins/mcusupport/mcusupport.qbs
+++ b/src/plugins/mcusupport/mcusupport.qbs
@@ -58,8 +58,7 @@ QtcPlugin {
"settingshandler.cpp",
]
- Group {
- name: "McuSupport test files"
+ QtcTestFiles {
condition: qtc.testsEnabled && (qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent)
prefix: "test/"
files: [
diff --git a/src/plugins/mcusupport/mcusupportdevice.h b/src/plugins/mcusupport/mcusupportdevice.h
index 262edc39ab..78bf6b68e3 100644
--- a/src/plugins/mcusupport/mcusupportdevice.h
+++ b/src/plugins/mcusupport/mcusupportdevice.h
@@ -4,6 +4,7 @@
#pragma once
#include <projectexplorer/devicesupport/desktopdevice.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace McuSupport {
namespace Internal {
diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp
index 6b4e851c47..8c1d2ea35f 100644
--- a/src/plugins/mcusupport/mcusupportplugin.cpp
+++ b/src/plugins/mcusupport/mcusupportplugin.cpp
@@ -27,8 +27,8 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <cmakeprojectmanager/cmakeprojectconstants.h>
@@ -113,8 +113,8 @@ void McuSupportPlugin::initialize()
setObjectName("McuSupportPlugin");
dd = new McuSupportPluginPrivate;
- connect(SessionManager::instance(),
- &SessionManager::projectFinishedParsing,
+ connect(ProjectManager::instance(),
+ &ProjectManager::projectFinishedParsing,
updateMCUProjectTree);
dd->m_options.registerQchFiles();
diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp
index b31d01d8bc..f92a443c4f 100644
--- a/src/plugins/mcusupport/mcusupportversiondetection.cpp
+++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp
@@ -3,7 +3,7 @@
#include "mcusupportversiondetection.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
#include <QRegularExpression>
@@ -41,7 +41,7 @@ QString McuPackageExecutableVersionDetector::parseVersion(const FilePath &packag
return {};
const int timeout = 3000; // usually runs below 1s, but we want to be on the safe side
- QtcProcess process;
+ Process process;
process.setCommand({binaryPath, m_detectionArgs});
process.start();
if (!process.waitForFinished(timeout) || process.result() != ProcessResult::FinishedWithSuccess)
diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp
index b9fc108ef7..a8a8f09139 100644
--- a/src/plugins/mcusupport/test/unittest.cpp
+++ b/src/plugins/mcusupport/test/unittest.cpp
@@ -152,11 +152,7 @@ const QString renesasProgrammerEnvVar{"RenesasFlashProgrammer_PATH"};
const char renesasProgrammerLabel[]{"Renesas Flash Programmer"};
const QString renesasProgrammerDetectionPath{HostOsInfo::withExecutableSuffix("rfp-cli")};
-const char renesasE2StudioCmakeVar[]{"EK_RA6M3G_E2_PROJECT_PATH"};
-const char renesasE2StudioDefaultPath[]{"%{Env:HOME}/e2_studio/workspace"};
const QString renesasE2StudioPath{(FileUtils::homePath() / "/e2_studio/workspace").toUserOutput()};
-const char renesasE2StudioLabel[]{"Path to project for Renesas e2 Studio"};
-const char renesasE2StudioSetting[]{"RenesasE2StudioPath"};
const char cypressProgrammerSetting[]{"CypressAutoFlashUtil"};
const char cypressProgrammerCmakeVar[]{"INFINEON_AUTO_FLASH_UTILITY_DIR"};
@@ -1760,9 +1756,9 @@ void McuSupportTest::test_nonemptyVersionDetector()
// pkgDesc.versionDetection.xmlAttribute left empty
pkgDesc.shouldAddToSystemPath = false;
const auto package = targetFactory.createPackage(pkgDesc);
- QVERIFY(package->getVersionDetector() != nullptr);
- QCOMPARE(typeid(*package->getVersionDetector()).name(),
- typeid(McuPackageExecutableVersionDetector).name());
+ const McuPackageVersionDetector *detector = package->getVersionDetector();
+ QVERIFY(detector != nullptr);
+ QCOMPARE(typeid(*detector).name(), typeid(McuPackageExecutableVersionDetector).name());
}
void McuSupportTest::test_emptyVersionDetector()
diff --git a/src/plugins/mcusupport/wizards/qmlproject/wizard.json b/src/plugins/mcusupport/wizards/qmlproject/wizard.json
index 173c430eeb..4fcf4f6fd3 100644
--- a/src/plugins/mcusupport/wizards/qmlproject/wizard.json
+++ b/src/plugins/mcusupport/wizards/qmlproject/wizard.json
@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject" ],
"id": "M.McuSupportApplication",
"category": "D.ApplicationMCU",
- "trDescription": "Suitable for Qt for MCUs versions 2.4 and later. Creates a Qt for MCUs application with a simple UI, based on qmlproject.",
+ "trDescription": "Suitable for Qt for MCUs versions 2.4 and later. Creates an application that uses a subset of Qt QML and Qt Quick Controls types (as supported by Qt for MCUs) that you can deploy, run, and debug on MCU boards.",
"trDisplayName": "Qt for MCUs Application",
"trDisplayCategory": "QmlProject Application (Qt for MCUs)",
"icon": "../icon.png",
diff --git a/src/plugins/mercurial/authenticationdialog.cpp b/src/plugins/mercurial/authenticationdialog.cpp
index 8538fd2812..d9d552274d 100644
--- a/src/plugins/mercurial/authenticationdialog.cpp
+++ b/src/plugins/mercurial/authenticationdialog.cpp
@@ -24,7 +24,7 @@ AuthenticationDialog::AuthenticationDialog(const QString &username, const QStrin
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp
index 43f942a167..6a0c2574d4 100644
--- a/src/plugins/mercurial/mercurialclient.cpp
+++ b/src/plugins/mercurial/mercurialclient.cpp
@@ -12,8 +12,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <vcsbase/vcsbasediffeditorcontroller.h>
#include <vcsbase/vcsbaseeditor.h>
@@ -55,17 +55,17 @@ MercurialDiffEditorController::MercurialDiffEditorController(IDocument *document
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupDiff = [=](QtcProcess &process) {
+ const auto setupDiff = [=](Process &process) {
setupCommand(process, {addConfigurationArguments(args)});
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
Storage(diffInputStorage),
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
};
setReloadRecipe(root);
@@ -81,7 +81,8 @@ QStringList MercurialDiffEditorController::addConfigurationArguments(const QStri
/////////////////////////////////////////////////////////////
-MercurialClient::MercurialClient(MercurialSettings *settings) : VcsBaseClient(settings)
+MercurialClient::MercurialClient()
+ : VcsBaseClient(&Internal::settings())
{
}
@@ -427,7 +428,7 @@ void MercurialClient::requestReload(const QString &documentId, const FilePath &s
IDocument *document = DiffEditorController::findOrCreateDocument(documentId, title);
QTC_ASSERT(document, return);
auto controller = new MercurialDiffEditorController(document, args);
- controller->setVcsBinary(settings().binaryPath.filePath());
+ controller->setVcsBinary(settings().binaryPath());
controller->setProcessEnvironment(processEnvironment());
controller->setWorkingDirectory(workingDirectory);
diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h
index 15c31845db..27badd45d0 100644
--- a/src/plugins/mercurial/mercurialclient.h
+++ b/src/plugins/mercurial/mercurialclient.h
@@ -16,8 +16,9 @@ class MercurialDiffEditorController;
class MercurialClient : public VcsBase::VcsBaseClient
{
Q_OBJECT
+
public:
- explicit MercurialClient(MercurialSettings *settings);
+ MercurialClient();
bool synchronousClone(const Utils::FilePath &workingDir,
const QString &srcLocation,
diff --git a/src/plugins/mercurial/mercurialcommitwidget.cpp b/src/plugins/mercurial/mercurialcommitwidget.cpp
index 7fcd333930..c6b35a67d0 100644
--- a/src/plugins/mercurial/mercurialcommitwidget.cpp
+++ b/src/plugins/mercurial/mercurialcommitwidget.cpp
@@ -101,7 +101,7 @@ public:
m_authorLineEdit = new QLineEdit;
m_emailLineEdit = new QLineEdit;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
@@ -119,8 +119,9 @@ public:
Tr::tr("Email:"), m_emailLineEdit,
},
}
- }
- }.attachTo(this, Utils::Layouting::WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
}
QLabel *m_repositoryLabel;
diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp
index e84c26a6ca..05b3524b4d 100644
--- a/src/plugins/mercurial/mercurialplugin.cpp
+++ b/src/plugins/mercurial/mercurialplugin.cpp
@@ -170,8 +170,7 @@ private:
// Variables
MercurialSettings m_settings;
- MercurialClient m_client{&m_settings};
- MercurialSettingsPage m_settingsPage{&m_settings};
+ MercurialClient m_client;
Core::CommandLocator *m_commandLocator = nullptr;
Core::ActionContainer *m_mercurialContainer = nullptr;
@@ -254,7 +253,7 @@ MercurialPluginPrivate::MercurialPluginPrivate()
createMenu(context);
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
void MercurialPluginPrivate::createMenu(const Core::Context &context)
@@ -633,8 +632,8 @@ void MercurialPluginPrivate::showCommitWidget(const QList<VcsBaseClient::StatusI
const QString branch = vcsTopic(m_submitRepository);
commitEditor->setFields(m_submitRepository, branch,
- m_settings.userName.value(),
- m_settings.userEmail.value(), status);
+ settings().userName(),
+ settings().userEmail(), status);
}
void MercurialPluginPrivate::diffFromEditorSelected(const QStringList &files)
@@ -716,7 +715,7 @@ bool MercurialPluginPrivate::managesFile(const FilePath &workingDirectory, const
bool MercurialPluginPrivate::isConfigured() const
{
- const FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath();
if (binary.isEmpty())
return false;
QFileInfo fi = binary.toFileInfo();
@@ -784,7 +783,7 @@ VcsCommand *MercurialPluginPrivate::createInitialCheckoutCommand(const QString &
QStringList args;
args << QLatin1String("clone") << extraArgs << url << localName;
auto command = VcsBaseClient::createVcsCommand(baseDirectory, m_client.processEnvironment());
- command->addJob({m_settings.binaryPath.filePath(), args}, -1);
+ command->addJob({settings().binaryPath.filePath(), args}, -1);
return command;
}
diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp
index 6d21550bc7..0b84187b1b 100644
--- a/src/plugins/mercurial/mercurialsettings.cpp
+++ b/src/plugins/mercurial/mercurialsettings.cpp
@@ -14,13 +14,26 @@ using namespace Utils;
namespace Mercurial::Internal {
+static MercurialSettings *theSettings;
+
+MercurialSettings &settings()
+{
+ return *theSettings;
+}
+
MercurialSettings::MercurialSettings()
{
+ theSettings = this;
+
+ setId(VcsBase::Constants::VCS_ID_MERCURIAL);
+ setDisplayName(Tr::tr("Mercurial"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
+ setSettings(&settings());
+
setSettingsGroup("Mercurial");
setAutoApply(false);
registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setDefaultValue(Constants::MERCURIALDEFAULT);
binaryPath.setDisplayName(Tr::tr("Mercurial Command"));
@@ -42,42 +55,27 @@ MercurialSettings::MercurialSettings()
registerAspect(&diffIgnoreBlankLines);
diffIgnoreBlankLines.setSettingsKey("diffIgnoreBlankLines");
-}
-
-// MercurialSettingsPage
-
-MercurialSettingsPage::MercurialSettingsPage(MercurialSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_MERCURIAL);
- setDisplayName(Tr::tr("Mercurial"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
- setLayouter([settings](QWidget *widget) {
- MercurialSettings &s = *settings;
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
Column {
Group {
title(Tr::tr("Configuration")),
- Row { s.binaryPath }
+ Row { binaryPath }
},
Group {
title(Tr::tr("User")),
Form {
- s.userName,
- s.userEmail
+ userName, br,
+ userEmail
}
},
Group {
title(Tr::tr("Miscellaneous")),
- Row {
- s.logCount,
- s.timeout,
- st
- }
+ Row { logCount, timeout, st }
},
st
diff --git a/src/plugins/mercurial/mercurialsettings.h b/src/plugins/mercurial/mercurialsettings.h
index 881bcd0447..08c8fa4154 100644
--- a/src/plugins/mercurial/mercurialsettings.h
+++ b/src/plugins/mercurial/mercurialsettings.h
@@ -3,8 +3,6 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
-
#include <vcsbase/vcsbaseclientsettings.h>
namespace Mercurial::Internal {
@@ -18,10 +16,6 @@ public:
Utils::StringAspect diffIgnoreBlankLines;
};
-class MercurialSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit MercurialSettingsPage(MercurialSettings *settings);
-};
+MercurialSettings &settings();
} // Mercurial::Internal
diff --git a/src/plugins/mercurial/revertdialog.cpp b/src/plugins/mercurial/revertdialog.cpp
index c36eb85fe7..29ee250e55 100644
--- a/src/plugins/mercurial/revertdialog.cpp
+++ b/src/plugins/mercurial/revertdialog.cpp
@@ -11,8 +11,6 @@
#include <QGroupBox>
#include <QLineEdit>
-using namespace Utils;
-
namespace Mercurial::Internal {
RevertDialog::RevertDialog(QWidget *parent)
@@ -32,8 +30,8 @@ RevertDialog::RevertDialog(QWidget *parent)
using namespace Layouting;
Form {
- Tr::tr("Revision:"), m_revisionLineEdit,
- }.attachTo(groupBox, WithMargins);
+ Tr::tr("Revision:"), m_revisionLineEdit, normalMargin
+ }.attachTo(groupBox);
Column {
groupBox,
diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
index a24a1f52bf..514c529212 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp
@@ -19,7 +19,7 @@
#include <projectexplorer/projectexplorer.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDir>
@@ -91,7 +91,8 @@ void MesonBuildConfiguration::build(const QString &target)
QStringList MesonBuildConfiguration::mesonConfigArgs()
{
- return Utils::ProcessArgs::splitArgs(m_parameters) + QStringList{QString("-Dbuildtype=%1").arg(mesonBuildTypeName(m_buildType))};
+ return Utils::ProcessArgs::splitArgs(m_parameters, HostOsInfo::hostOs())
+ + QStringList{QString("-Dbuildtype=%1").arg(mesonBuildTypeName(m_buildType))};
}
const QString &MesonBuildConfiguration::parameters() const
diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h
index 6bc4a96afe..a405909189 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h
+++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "projectexplorer/buildconfiguration.h"
-#include "projectexplorer/target.h"
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/target.h>
namespace MesonProjectManager {
namespace Internal {
diff --git a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp b/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp
index cedd9c5f3e..32719c08d6 100644
--- a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp
+++ b/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp
@@ -67,16 +67,18 @@ MesonBuildSettingsWidget::MesonBuildSettingsWidget(MesonBuildConfiguration *buil
buildDirWidget,
optionsFilterLineEdit,
optionsTreeView,
- }.attachTo(details, WithoutMargins);
+ noMargin
+ }.attachTo(details);
Column {
container,
- Row { configureButton, wipeButton, }
- }.attachTo(this, WithoutMargins);
+ Row { configureButton, wipeButton, noMargin }
+ }.attachTo(this);
- Form buildDirWBuilder;
- buildCfg->buildDirectoryAspect()->addToLayout(buildDirWBuilder);
- buildDirWBuilder.attachTo(buildDirWidget, WithoutMargins);
+ Form {
+ buildCfg->buildDirectoryAspect(),
+ noMargin
+ }.attachTo(buildDirWidget);
parametersLineEdit->setText(buildCfg->parameters());
optionsFilterLineEdit->setFiltering(true);
diff --git a/src/plugins/mesonprojectmanager/mesonprocess.cpp b/src/plugins/mesonprojectmanager/mesonprocess.cpp
index 0c828588c2..2e4ba02455 100644
--- a/src/plugins/mesonprojectmanager/mesonprocess.cpp
+++ b/src/plugins/mesonprojectmanager/mesonprocess.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/taskhub.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
#include <QLoggingCategory>
@@ -63,12 +63,12 @@ void MesonProcess::setupProcess(const Command &command, const Environment &env,
{
if (m_process)
m_process.release()->deleteLater();
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &MesonProcess::handleProcessDone);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &MesonProcess::handleProcessDone);
if (!captureStdo) {
- connect(m_process.get(), &QtcProcess::readyReadStandardOutput,
+ connect(m_process.get(), &Process::readyReadStandardOutput,
this, &MesonProcess::processStandardOutput);
- connect(m_process.get(), &QtcProcess::readyReadStandardError,
+ connect(m_process.get(), &Process::readyReadStandardError,
this, &MesonProcess::processStandardError);
}
diff --git a/src/plugins/mesonprojectmanager/mesonprocess.h b/src/plugins/mesonprojectmanager/mesonprocess.h
index 949af3339f..c01427c8fb 100644
--- a/src/plugins/mesonprojectmanager/mesonprocess.h
+++ b/src/plugins/mesonprojectmanager/mesonprocess.h
@@ -12,7 +12,7 @@
namespace Utils {
class Environment;
-class QtcProcess;
+class Process;
}
namespace MesonProjectManager {
@@ -44,7 +44,7 @@ private:
void processStandardOutput();
void processStandardError();
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
QElapsedTimer m_elapsed;
QByteArray m_stdo;
QByteArray m_stderr;
diff --git a/src/plugins/mesonprojectmanager/mesonprojectparser.cpp b/src/plugins/mesonprojectmanager/mesonprojectparser.cpp
index 5a9babeedf..c84b5e4b3d 100644
--- a/src/plugins/mesonprojectmanager/mesonprojectparser.cpp
+++ b/src/plugins/mesonprojectmanager/mesonprojectparser.cpp
@@ -12,8 +12,8 @@
#include <projectexplorer/projectexplorer.h>
+#include <utils/async.h>
#include <utils/fileinprojectfinder.h>
-#include <utils/runextensions.h>
#include <QStringList>
#include <QTextStream>
@@ -197,7 +197,7 @@ QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
bool MesonProjectParser::startParser()
{
- m_parserFutureResult = Utils::runAsync(
+ m_parserFutureResult = Utils::asyncRun(
ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(),
[processOutput = m_process.stdOut(), introType = m_introType,
buildDir = m_buildDir.toString(), srcDir = m_srcDir] {
diff --git a/src/plugins/mesonprojectmanager/mesonwrapper.h b/src/plugins/mesonprojectmanager/mesonwrapper.h
index b229ab6555..6ec1e57a0f 100644
--- a/src/plugins/mesonprojectmanager/mesonwrapper.h
+++ b/src/plugins/mesonprojectmanager/mesonwrapper.h
@@ -9,7 +9,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/id.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFile>
#include <QFileInfo>
@@ -35,7 +35,7 @@ bool containsFiles(const QString &path, const File_t &file, const T &...files)
inline bool run_meson(const Command &command, QIODevice *output = nullptr)
{
- Utils::QtcProcess process;
+ Utils::Process process;
process.setWorkingDirectory(command.workDir());
process.setCommand(command.cmdLine());
process.start();
diff --git a/src/plugins/mesonprojectmanager/settings.cpp b/src/plugins/mesonprojectmanager/settings.cpp
index b26e09f29b..6a211a375a 100644
--- a/src/plugins/mesonprojectmanager/settings.cpp
+++ b/src/plugins/mesonprojectmanager/settings.cpp
@@ -45,7 +45,7 @@ GeneralSettingsPage::GeneralSettingsPage()
setLayouter([](QWidget *widget) {
Settings &s = *Settings::instance();
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
s.autorunMeson,
diff --git a/src/plugins/mesonprojectmanager/toolitemsettings.cpp b/src/plugins/mesonprojectmanager/toolitemsettings.cpp
index 148e3e5fae..a0591b905c 100644
--- a/src/plugins/mesonprojectmanager/toolitemsettings.cpp
+++ b/src/plugins/mesonprojectmanager/toolitemsettings.cpp
@@ -29,7 +29,8 @@ ToolItemSettings::ToolItemSettings(QWidget *parent)
Form {
Tr::tr("Name:"), m_mesonNameLineEdit, br,
Tr::tr("Path:"), m_mesonPathChooser, br,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(m_mesonPathChooser, &PathChooser::rawPathChanged, this, &ToolItemSettings::store);
connect(m_mesonNameLineEdit, &QLineEdit::textChanged, this, &ToolItemSettings::store);
diff --git a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
index b403afd057..c327fb4e2b 100644
--- a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
+++ b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h
@@ -36,11 +36,11 @@ private:
void makeReadOnly() override { m_toolsComboBox->setEnabled(false); }
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_toolsComboBox);
- builder.addItem(m_toolsComboBox);
- builder.addItem(m_manageButton);
+ parent.addItem(m_toolsComboBox);
+ parent.addItem(m_manageButton);
}
void refresh() override
diff --git a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp
index fff0259741..a67175742b 100644
--- a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp
+++ b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp
@@ -21,15 +21,13 @@ namespace Internal {
static QString entryName(int index)
{
- using namespace Constants;
- return QString("%1%2").arg(ToolsSettings::ENTRY_KEY).arg(index);
+ return QString("%1%2").arg(Constants::ToolsSettings::ENTRY_KEY).arg(index);
}
ToolsSettingsAccessor::ToolsSettingsAccessor()
- : UpgradingSettingsAccessor("QtCreatorMesonTools",
- Tr::tr("Meson"),
- Core::Constants::IDE_DISPLAY_NAME)
{
+ setDocType("QtCreatorMesonTools");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
setBaseFilePath(Core::ICore::userResourcePath(Constants::ToolsSettings::FILENAME));
}
diff --git a/src/plugins/mesonprojectmanager/toolwrapper.cpp b/src/plugins/mesonprojectmanager/toolwrapper.cpp
index 9864fd030b..83ed27f495 100644
--- a/src/plugins/mesonprojectmanager/toolwrapper.cpp
+++ b/src/plugins/mesonprojectmanager/toolwrapper.cpp
@@ -3,7 +3,7 @@
#include "toolwrapper.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace MesonProjectManager {
namespace Internal {
@@ -40,7 +40,7 @@ void ToolWrapper::setExe(const Utils::FilePath &newExe)
Version ToolWrapper::read_version(const Utils::FilePath &toolPath)
{
if (toolPath.toFileInfo().isExecutable()) {
- Utils::QtcProcess process;
+ Utils::Process process;
process.setCommand({ toolPath, { "--version" } });
process.start();
if (process.waitForFinished())
diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp
index 4ce7cc16ca..4071c5b015 100644
--- a/src/plugins/modeleditor/componentviewcontroller.cpp
+++ b/src/plugins/modeleditor/componentviewcontroller.cpp
@@ -18,9 +18,9 @@
#include <cppeditor/cppmodelmanager.h>
#include <cplusplus/CppDocument.h>
-#include <projectexplorer/session.h>
-#include <projectexplorer/projectnodes.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectnodes.h>
#include <utils/qtcassert.h>
@@ -29,6 +29,7 @@
// TODO implement removing include dependencies that are not longer used
// TODO refactor add/remove relations between ancestor packages into extra controller class
+using namespace ProjectExplorer;
using namespace Utils;
namespace ModelEditor {
@@ -136,7 +137,7 @@ void UpdateIncludeDependenciesVisitor::setModelUtilities(ModelUtilities *modelUt
void UpdateIncludeDependenciesVisitor::updateFilePaths()
{
m_filePaths.clear();
- for (const ProjectExplorer::Project *project : ProjectExplorer::SessionManager::projects()) {
+ for (const ProjectExplorer::Project *project : ProjectExplorer::ProjectManager::projects()) {
ProjectExplorer::ProjectNode *projectNode = project->rootProjectNode();
if (projectNode)
collectElementPaths(projectNode, &m_filePaths);
@@ -217,17 +218,16 @@ QStringList UpdateIncludeDependenciesVisitor::findFilePathOfComponent(const qmt:
void UpdateIncludeDependenciesVisitor::collectElementPaths(const ProjectExplorer::FolderNode *folderNode,
QMultiHash<QString, Node> *filePathsMap)
{
- const QList<ProjectExplorer::FileNode *> fileNodes = folderNode->fileNodes();
- for (const ProjectExplorer::FileNode *fileNode : fileNodes) {
+ folderNode->forEachFileNode([&](FileNode *fileNode) {
QString elementName = qmt::NameController::convertFileNameToElementName(fileNode->filePath().toString());
QFileInfo fileInfo = fileNode->filePath().toFileInfo();
QString nodePath = fileInfo.path();
QStringList elementsPath = qmt::NameController::buildElementsPath(nodePath, false);
filePathsMap->insert(elementName, Node(fileNode->filePath().toString(), elementsPath));
- }
- const QList<ProjectExplorer::FolderNode *> subNodes = folderNode->folderNodes();
- for (const ProjectExplorer::FolderNode *subNode : subNodes)
+ });
+ folderNode->forEachFolderNode([&](FolderNode *subNode) {
collectElementPaths(subNode, filePathsMap);
+ });
}
qmt::MComponent *UpdateIncludeDependenciesVisitor::findComponentFromFilePath(const QString &filePath)
diff --git a/src/plugins/modeleditor/elementtasks.cpp b/src/plugins/modeleditor/elementtasks.cpp
index e0efd6914a..6413686f61 100644
--- a/src/plugins/modeleditor/elementtasks.cpp
+++ b/src/plugins/modeleditor/elementtasks.cpp
@@ -24,15 +24,17 @@
#include "qmt/project/project.h"
#include <extensionsystem/pluginmanager.h>
-#include <cppeditor/cpplocatorfilter.h>
+#include <cppeditor/cpplocatordata.h>
#include <cppeditor/indexitem.h>
#include <cppeditor/searchsymbols.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/locator/ilocatorfilter.h>
#include <utils/qtcassert.h>
#include <QMenu>
+using namespace Core;
+using namespace CppEditor;
+
namespace ModelEditor {
namespace Internal {
@@ -83,23 +85,16 @@ void ElementTasks::openElement(const qmt::DElement *element, const qmt::MDiagram
bool ElementTasks::hasClassDefinition(const qmt::MElement *element) const
{
if (auto klass = dynamic_cast<const qmt::MClass *>(element)) {
- QString qualifiedClassName = klass->umlNamespace().isEmpty()
- ? klass->name()
- : klass->umlNamespace() + "::" + klass->name();
-
- Core::ILocatorFilter *classesFilter
- = CppEditor::CppModelManager::instance()->classesFilter();
- if (!classesFilter)
+ const QString qualifiedClassName = klass->umlNamespace().isEmpty() ? klass->name()
+ : klass->umlNamespace() + "::" + klass->name();
+ auto *locatorData = CppModelManager::instance()->locatorData();
+ if (!locatorData)
return false;
-
- QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
- const QList<Core::LocatorFilterEntry> matches
- = classesFilter->matchesFor(dummyInterface, qualifiedClassName);
- for (const Core::LocatorFilterEntry &entry : matches) {
- CppEditor::IndexItem::Ptr info = qvariant_cast<CppEditor::IndexItem::Ptr>(entry.internalData);
- if (info->scopedSymbolName() != qualifiedClassName)
- continue;
- return true;
+ const QList<IndexItem::Ptr> matches = locatorData->findSymbols(IndexItem::Class,
+ qualifiedClassName);
+ for (const IndexItem::Ptr &info : matches) {
+ if (info->scopedSymbolName() == qualifiedClassName)
+ return true;
}
}
return false;
@@ -120,26 +115,19 @@ bool ElementTasks::hasClassDefinition(const qmt::DElement *element,
void ElementTasks::openClassDefinition(const qmt::MElement *element)
{
if (auto klass = dynamic_cast<const qmt::MClass *>(element)) {
- QString qualifiedClassName = klass->umlNamespace().isEmpty()
- ? klass->name()
- : klass->umlNamespace() + "::" + klass->name();
+ const QString qualifiedClassName = klass->umlNamespace().isEmpty() ? klass->name()
+ : klass->umlNamespace() + "::" + klass->name();
- Core::ILocatorFilter *classesFilter
- = CppEditor::CppModelManager::instance()->classesFilter();
- if (!classesFilter)
+ auto *locatorData = CppModelManager::instance()->locatorData();
+ if (!locatorData)
return;
-
- QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
- const QList<Core::LocatorFilterEntry> matches
- = classesFilter->matchesFor(dummyInterface, qualifiedClassName);
- for (const Core::LocatorFilterEntry &entry : matches) {
- CppEditor::IndexItem::Ptr info = qvariant_cast<CppEditor::IndexItem::Ptr>(entry.internalData);
+ const QList<IndexItem::Ptr> matches = locatorData->findSymbols(IndexItem::Class,
+ qualifiedClassName);
+ for (const IndexItem::Ptr &info : matches) {
if (info->scopedSymbolName() != qualifiedClassName)
continue;
- if (Core::EditorManager::instance()->openEditorAt(
- {info->filePath(), info->line(), info->column()})) {
+ if (EditorManager::openEditorAt({info->filePath(), info->line(), info->column()}))
return;
- }
}
}
}
diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp
index c6ace66024..d4471fe0ec 100644
--- a/src/plugins/modeleditor/modelindexer.cpp
+++ b/src/plugins/modeleditor/modelindexer.cpp
@@ -18,7 +18,7 @@
#include "qmt/tasks/findrootdiagramvisitor.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <utils/mimeutils.h>
@@ -34,6 +34,8 @@
#include <QDebug>
#include <QPointer>
+using namespace ProjectExplorer;
+
namespace ModelEditor {
namespace Internal {
@@ -308,9 +310,9 @@ ModelIndexer::ModelIndexer(QObject *parent)
connect(this, &ModelIndexer::filesQueued,
d->indexerThread, &ModelIndexer::IndexerThread::onFilesQueued);
d->indexerThread->start();
- connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::projectAdded,
+ connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectAdded,
this, &ModelIndexer::onProjectAdded);
- connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::aboutToRemoveProject,
+ connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::aboutToRemoveProject,
this, &ModelIndexer::onAboutToRemoveProject);
}
@@ -447,18 +449,20 @@ QString ModelIndexer::findFirstModel(ProjectExplorer::FolderNode *folderNode,
{
if (!mimeType.isValid())
return QString();
- const QList<ProjectExplorer::FileNode *> fileNodes = folderNode->fileNodes();
- for (const ProjectExplorer::FileNode *fileNode : fileNodes) {
- if (mimeType.suffixes().contains(fileNode->filePath().completeSuffix()))
- return fileNode->filePath().toString();
- }
- const QList<ProjectExplorer::FolderNode *> subFolderNodes = folderNode->folderNodes();
- for (ProjectExplorer::FolderNode *subFolderNode : subFolderNodes) {
- QString modelFileName = findFirstModel(subFolderNode, mimeType);
- if (!modelFileName.isEmpty())
- return modelFileName;
- }
- return QString();
+
+ const QStringList suffixes = mimeType.suffixes();
+ FileNode *foundFileNode = folderNode->findChildFileNode([&](FileNode *fn) {
+ return suffixes.contains(fn->filePath().completeSuffix());
+ });
+ if (foundFileNode)
+ return foundFileNode->filePath().toString();
+
+ QString modelFileName;
+ folderNode->findChildFolderNode([&](FolderNode *fn) {
+ modelFileName = findFirstModel(fn, mimeType);
+ return !modelFileName.isEmpty();
+ });
+ return modelFileName;
}
void ModelIndexer::forgetProject(ProjectExplorer::Project *project)
diff --git a/src/plugins/nim/editor/nimcompletionassistprovider.cpp b/src/plugins/nim/editor/nimcompletionassistprovider.cpp
index 7179a8a64e..c94001b348 100644
--- a/src/plugins/nim/editor/nimcompletionassistprovider.cpp
+++ b/src/plugins/nim/editor/nimcompletionassistprovider.cpp
@@ -5,7 +5,7 @@
#include "suggest/nimsuggestcache.h"
#include "suggest/nimsuggest.h"
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposal.h>
diff --git a/src/plugins/nim/images/settingscategory_nim.png b/src/plugins/nim/images/settingscategory_nim.png
index 016c570eb4..bee1123cff 100644
--- a/src/plugins/nim/images/settingscategory_nim.png
+++ b/src/plugins/nim/images/settingscategory_nim.png
Binary files differ
diff --git a/src/plugins/nim/images/settingscategory_nim@2x.png b/src/plugins/nim/images/settingscategory_nim@2x.png
index 4fb9c10188..2cbe6cfdc1 100644
--- a/src/plugins/nim/images/settingscategory_nim@2x.png
+++ b/src/plugins/nim/images/settingscategory_nim@2x.png
Binary files differ
diff --git a/src/plugins/nim/nimplugin.cpp b/src/plugins/nim/nimplugin.cpp
index fcca3ef8d3..b3ef6fc49f 100644
--- a/src/plugins/nim/nimplugin.cpp
+++ b/src/plugins/nim/nimplugin.cpp
@@ -62,7 +62,6 @@ public:
NimCompilerBuildStepFactory buildStepFactory;
NimCompilerCleanStepFactory cleanStepFactory;
NimCodeStyleSettingsPage codeStyleSettingsPage;
- NimToolsSettingsPage toolsSettingsPage{&settings};
NimCodeStylePreferencesFactory codeStylePreferencesPage;
NimToolChainFactory toolChainFactory;
diff --git a/src/plugins/nim/project/nimblebuildsystem.cpp b/src/plugins/nim/project/nimblebuildsystem.cpp
index b1317153a8..3a8ccb08d6 100644
--- a/src/plugins/nim/project/nimblebuildsystem.cpp
+++ b/src/plugins/nim/project/nimblebuildsystem.cpp
@@ -10,8 +10,8 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -20,7 +20,7 @@ namespace Nim {
const char C_NIMBLEPROJECT_TASKS[] = "Nim.NimbleProject.Tasks";
-static QList<QByteArray> linesFromProcessOutput(QtcProcess *process)
+static QList<QByteArray> linesFromProcessOutput(Process *process)
{
QList<QByteArray> lines = process->readAllRawStandardOutput().split('\n');
lines = Utils::transform(lines, [](const QByteArray &line){ return line.trimmed(); });
@@ -30,7 +30,7 @@ static QList<QByteArray> linesFromProcessOutput(QtcProcess *process)
static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const FilePath &workingDirectory)
{
- QtcProcess process;
+ Process process;
process.setCommand({nimblePath, {"tasks"}});
process.setWorkingDirectory(workingDirectory);
process.start();
@@ -58,7 +58,7 @@ static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const File
static NimbleMetadata parseMetadata(const FilePath &nimblePath, const FilePath &workingDirectory)
{
- QtcProcess process;
+ Process process;
process.setCommand({nimblePath, {"dump"}});
process.setWorkingDirectory(workingDirectory);
process.start();
diff --git a/src/plugins/nim/project/nimbletaskstep.cpp b/src/plugins/nim/project/nimbletaskstep.cpp
index 06cf14a107..e072b9422e 100644
--- a/src/plugins/nim/project/nimbletaskstep.cpp
+++ b/src/plugins/nim/project/nimbletaskstep.cpp
@@ -88,8 +88,9 @@ QWidget *NimbleTaskStep::createConfigWidget()
using namespace Layouting;
auto widget = Form {
m_taskArgs,
- Tr::tr("Tasks:"), taskList
- }.emerge(WithoutMargins);
+ Tr::tr("Tasks:"), taskList,
+ noMargin
+ }.emerge();
auto buildSystem = dynamic_cast<NimbleBuildSystem *>(this->buildSystem());
QTC_ASSERT(buildSystem, return widget);
diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp
index 3ca151def7..588098619a 100644
--- a/src/plugins/nim/project/nimcompilerbuildstep.cpp
+++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp
@@ -16,8 +16,8 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QFormLayout>
@@ -76,7 +76,7 @@ QWidget *NimCompilerBuildStep::createConfigWidget()
auto updateUi = [=] {
const CommandLine cmd = commandLine();
- const QStringList parts = ProcessArgs::splitArgs(cmd.toUserOutput());
+ const QStringList parts = ProcessArgs::splitArgs(cmd.toUserOutput(), HostOsInfo::hostOs());
commandTextEdit->setText(parts.join(QChar::LineFeed));
diff --git a/src/plugins/nim/project/nimcompilercleanstep.cpp b/src/plugins/nim/project/nimcompilercleanstep.cpp
index c17f9437a5..8e13c06813 100644
--- a/src/plugins/nim/project/nimcompilercleanstep.cpp
+++ b/src/plugins/nim/project/nimcompilercleanstep.cpp
@@ -110,7 +110,7 @@ bool NimCompilerCleanStep::removeOutFilePath()
NimCompilerCleanStepFactory::NimCompilerCleanStepFactory()
{
registerStep<NimCompilerCleanStep>(Constants::C_NIMCOMPILERCLEANSTEP_ID);
- setFlags(BuildStepInfo::Unclonable);
+ setFlags(BuildStep::Unclonable);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_CLEAN);
setSupportedConfiguration(Constants::C_NIMBUILDCONFIGURATION_ID);
setRepeatable(false);
diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp
index b373fc9418..21b441fa6c 100644
--- a/src/plugins/nim/project/nimtoolchain.cpp
+++ b/src/plugins/nim/project/nimtoolchain.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/abi.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QRegularExpression>
@@ -95,7 +95,7 @@ bool NimToolChain::fromMap(const QVariantMap &data)
bool NimToolChain::parseVersion(const FilePath &path, std::tuple<int, int, int> &result)
{
- QtcProcess process;
+ Process process;
process.setCommand({path, {"--version"}});
process.start();
if (!process.waitForFinished())
diff --git a/src/plugins/nim/project/nimtoolchainfactory.cpp b/src/plugins/nim/project/nimtoolchainfactory.cpp
index 4301140865..01ec5373b5 100644
--- a/src/plugins/nim/project/nimtoolchainfactory.cpp
+++ b/src/plugins/nim/project/nimtoolchainfactory.cpp
@@ -35,10 +35,7 @@ Toolchains NimToolChainFactory::autoDetect(const ToolchainDetector &detector) co
{
Toolchains result;
- IDevice::ConstPtr dev =
- detector.device ? detector.device : DeviceManager::defaultDesktopDevice();
-
- const FilePath compilerPath = dev->searchExecutableInPath("nim");
+ const FilePath compilerPath = detector.device->searchExecutableInPath("nim");
if (compilerPath.isEmpty())
return result;
diff --git a/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp b/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp
index beefeb56ac..ec9cfa170e 100644
--- a/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp
+++ b/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp
@@ -37,14 +37,15 @@ NimCodeStylePreferencesWidget::NimCodeStylePreferencesWidget(ICodeStylePreferenc
m_previewTextEdit = new SnippetEditorWidget;
m_previewTextEdit->setPlainText(Nim::Constants::C_NIMCODESTYLEPREVIEWSNIPPET);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Column {
tabPreferencesWidget,
st,
},
m_previewTextEdit,
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
decorateEditor(TextEditorSettings::fontSettings());
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
diff --git a/src/plugins/nim/settings/nimcodestylesettingspage.cpp b/src/plugins/nim/settings/nimcodestylesettingspage.cpp
index 79c3634cf2..56919a6c15 100644
--- a/src/plugins/nim/settings/nimcodestylesettingspage.cpp
+++ b/src/plugins/nim/settings/nimcodestylesettingspage.cpp
@@ -36,13 +36,9 @@ public:
auto layout = new QVBoxLayout(this);
layout->addWidget(editor);
- layout->setContentsMargins(0, 0, 0, 0);
}
private:
- void apply() final {}
- void finish() final {}
-
TextEditor::SimpleCodeStylePreferences *m_nimCodeStylePreferences;
};
diff --git a/src/plugins/nim/settings/nimsettings.cpp b/src/plugins/nim/settings/nimsettings.cpp
index 5904b8b6e6..5e98b0fd53 100644
--- a/src/plugins/nim/settings/nimsettings.cpp
+++ b/src/plugins/nim/settings/nimsettings.cpp
@@ -26,32 +26,24 @@ static SimpleCodeStylePreferences *m_globalCodeStyle = nullptr;
NimSettings::NimSettings()
{
- setAutoApply(false);
setSettingsGroups("Nim", "NimSuggest");
+ setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID);
+ setDisplayName(Tr::tr("Tools"));
+ setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY);
+ setDisplayCategory(Tr::tr("Nim"));
+ setCategoryIconPath(":/nim/images/settingscategory_nim.png");
- InitializeCodeStyleSettings();
-
- registerAspect(&nimSuggestPath);
- nimSuggestPath.setSettingsKey("Command");
- nimSuggestPath.setDisplayStyle(StringAspect::PathChooserDisplay);
- nimSuggestPath.setExpectedKind(PathChooser::ExistingCommand);
- nimSuggestPath.setLabelText(Tr::tr("Path:"));
-
- readSettings(Core::ICore::settings());
-}
-
-NimSettings::~NimSettings()
-{
- TerminateCodeStyleSettings();
-}
-
-SimpleCodeStylePreferences *NimSettings::globalCodeStyle()
-{
- return m_globalCodeStyle;
-}
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ Column {
+ Group {
+ title("Nimsuggest"),
+ Column { nimSuggestPath }
+ },
+ st
+ }.attachTo(widget);
+ });
-void NimSettings::InitializeCodeStyleSettings()
-{
// code style factory
auto factory = new NimCodeStylePreferencesFactory();
TextEditorSettings::registerCodeStyleFactory(factory);
@@ -93,9 +85,17 @@ void NimSettings::InitializeCodeStyleSettings()
Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_SCRIPT_MIMETYPE,
Nim::Constants::C_NIMLANGUAGE_ID);
+
+ registerAspect(&nimSuggestPath);
+ nimSuggestPath.setSettingsKey("Command");
+ nimSuggestPath.setDisplayStyle(StringAspect::PathChooserDisplay);
+ nimSuggestPath.setExpectedKind(PathChooser::ExistingCommand);
+ nimSuggestPath.setLabelText(Tr::tr("Path:"));
+
+ readSettings();
}
-void NimSettings::TerminateCodeStyleSettings()
+NimSettings::~NimSettings()
{
TextEditorSettings::unregisterCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID);
TextEditorSettings::unregisterCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID);
@@ -105,28 +105,9 @@ void NimSettings::TerminateCodeStyleSettings()
m_globalCodeStyle = nullptr;
}
-
-// NimToolSettingsPage
-
-NimToolsSettingsPage::NimToolsSettingsPage(NimSettings *settings)
+SimpleCodeStylePreferences *NimSettings::globalCodeStyle()
{
- setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID);
- setDisplayName(Tr::tr("Tools"));
- setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY);
- setDisplayCategory(Tr::tr("Nim"));
- setCategoryIconPath(":/nim/images/settingscategory_nim.png");
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- using namespace Layouting;
- Column {
- Group {
- title("Nimsuggest"),
- Column { settings->nimSuggestPath }
- },
- st
- }.attachTo(widget);
- });
+ return m_globalCodeStyle;
}
} // namespace Nim
diff --git a/src/plugins/nim/settings/nimsettings.h b/src/plugins/nim/settings/nimsettings.h
index d144618f7d..57beb64f45 100644
--- a/src/plugins/nim/settings/nimsettings.h
+++ b/src/plugins/nim/settings/nimsettings.h
@@ -4,13 +4,12 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
namespace TextEditor { class SimpleCodeStylePreferences; }
namespace Nim {
-class NimSettings : public Utils::AspectContainer
+class NimSettings : public Core::PagedSettings
{
public:
NimSettings();
@@ -19,16 +18,6 @@ public:
Utils::StringAspect nimSuggestPath;
static TextEditor::SimpleCodeStylePreferences *globalCodeStyle();
-
-private:
- void InitializeCodeStyleSettings();
- void TerminateCodeStyleSettings();
-};
-
-class NimToolsSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit NimToolsSettingsPage(NimSettings *settings);
};
} // Nim
diff --git a/src/plugins/nim/suggest/server.cpp b/src/plugins/nim/suggest/server.cpp
index b891404fe8..7573505f72 100644
--- a/src/plugins/nim/suggest/server.cpp
+++ b/src/plugins/nim/suggest/server.cpp
@@ -10,8 +10,8 @@ namespace Suggest {
NimSuggestServer::NimSuggestServer(QObject *parent) : QObject(parent)
{
- connect(&m_process, &QtcProcess::done, this, &NimSuggestServer::onDone);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this,
+ connect(&m_process, &Process::done, this, &NimSuggestServer::onDone);
+ connect(&m_process, &Process::readyReadStandardOutput, this,
&NimSuggestServer::onStandardOutputAvailable);
}
diff --git a/src/plugins/nim/suggest/server.h b/src/plugins/nim/suggest/server.h
index 9d8e880663..9eb2bac141 100644
--- a/src/plugins/nim/suggest/server.h
+++ b/src/plugins/nim/suggest/server.h
@@ -7,7 +7,7 @@
#include <QFile>
#include <QObject>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Nim {
namespace Suggest {
@@ -36,7 +36,7 @@ private:
void clearState();
bool m_portAvailable = false;
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
quint16 m_port = 0;
QString m_projectFilePath;
QString m_executablePath;
diff --git a/src/plugins/perforce/changenumberdialog.cpp b/src/plugins/perforce/changenumberdialog.cpp
index b5a0aa2787..5ba8dcced0 100644
--- a/src/plugins/perforce/changenumberdialog.cpp
+++ b/src/plugins/perforce/changenumberdialog.cpp
@@ -27,7 +27,7 @@ ChangeNumberDialog::ChangeNumberDialog(QWidget *parent)
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { Tr::tr("Change number:"), m_lineEdit },
diff --git a/src/plugins/perforce/pendingchangesdialog.cpp b/src/plugins/perforce/pendingchangesdialog.cpp
index 589d17b276..84b10a8a07 100644
--- a/src/plugins/perforce/pendingchangesdialog.cpp
+++ b/src/plugins/perforce/pendingchangesdialog.cpp
@@ -48,7 +48,7 @@ PendingChangesDialog::PendingChangesDialog(const QString &data, QWidget *parent)
submitButton->setEnabled(false);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_listWidget,
diff --git a/src/plugins/perforce/perforcechecker.cpp b/src/plugins/perforce/perforcechecker.cpp
index fc2aeae3e1..cf63d9e691 100644
--- a/src/plugins/perforce/perforcechecker.cpp
+++ b/src/plugins/perforce/perforcechecker.cpp
@@ -21,13 +21,15 @@ namespace Internal {
PerforceChecker::PerforceChecker(QObject *parent) : QObject(parent)
{
- connect(&m_process, &QtcProcess::done, this, &PerforceChecker::slotDone);
+ connect(&m_process, &Process::done, this, &PerforceChecker::slotDone);
}
PerforceChecker::~PerforceChecker()
{
- m_process.kill();
- m_process.waitForFinished();
+ if (m_process.isRunning()) {
+ m_process.kill();
+ m_process.waitForFinished();
+ }
resetOverrideCursor();
}
diff --git a/src/plugins/perforce/perforcechecker.h b/src/plugins/perforce/perforcechecker.h
index e9325b4ced..d0f608de70 100644
--- a/src/plugins/perforce/perforcechecker.h
+++ b/src/plugins/perforce/perforcechecker.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Perforce::Internal {
@@ -44,7 +44,7 @@ private:
void parseOutput(const QString &);
inline void resetOverrideCursor();
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
Utils::FilePath m_binary;
int m_timeOutMS = -1;
bool m_timedOut = false;
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index ccd4faec16..5a7e74450d 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -27,8 +27,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/parameteraction.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <vcsbase/basevcseditorfactory.h>
@@ -889,8 +889,8 @@ void PerforcePluginPrivate::filelog(const FilePath &workingDir, const QString &f
QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, QStringList(fileName));
QStringList args;
args << QLatin1String("filelog") << QLatin1String("-li");
- if (m_settings.logCount.value() > 0)
- args << "-m" << QString::number(m_settings.logCount.value());
+ if (m_settings.logCount() > 0)
+ args << "-m" << QString::number(m_settings.logCount());
if (!fileName.isEmpty())
args.append(fileName);
const PerforceResponse result = runP4Cmd(workingDir, args,
@@ -911,8 +911,8 @@ void PerforcePluginPrivate::changelists(const FilePath &workingDir, const QStrin
QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, QStringList(fileName));
QStringList args;
args << QLatin1String("changelists") << QLatin1String("-lit");
- if (m_settings.logCount.value() > 0)
- args << "-m" << QString::number(m_settings.logCount.value());
+ if (m_settings.logCount() > 0)
+ args << "-m" << QString::number(m_settings.logCount());
if (!fileName.isEmpty())
args.append(fileName);
const PerforceResponse result = runP4Cmd(workingDir, args,
@@ -1225,8 +1225,8 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki
QTC_ASSERT(stdInput.isEmpty(), return PerforceResponse()); // Not supported here
// Run, connect stderr to the output window
- QtcProcess process;
- const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS.value();
+ Process process;
+ const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS();
process.setTimeoutS(timeOutS);
if (outputCodec)
process.setCodec(outputCodec);
@@ -1283,7 +1283,7 @@ PerforceResponse PerforcePluginPrivate::fullySynchronousProcess(const FilePath &
const QByteArray &stdInput,
QTextCodec *outputCodec) const
{
- QtcProcess process;
+ Process process;
if (flags & OverrideDiffEnvironment)
process.setEnvironment(overrideDiffEnvironmentVariable());
@@ -1303,7 +1303,7 @@ PerforceResponse PerforcePluginPrivate::fullySynchronousProcess(const FilePath &
QByteArray stdOut;
QByteArray stdErr;
- const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS.value();
+ const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS();
if (!process.readDataFromProcess(&stdOut, &stdErr, timeOutS)) {
process.stop();
process.waitForFinished();
diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp
index ac9d0b26b0..e62fdade89 100644
--- a/src/plugins/perforce/perforcesettings.cpp
+++ b/src/plugins/perforce/perforcesettings.cpp
@@ -106,6 +106,23 @@ QStringList PerforceSettings::commonP4Arguments() const
return lst;
}
+QStringList PerforceSettings::commonP4Arguments_volatile() const
+{
+ QStringList lst;
+ if (customEnv.volatileValue().toBool()) {
+ auto p4C = p4Client.volatileValue().toString();
+ if (!p4C.isEmpty())
+ lst << "-c" << p4C;
+ auto p4P = p4Port.volatileValue().toString();
+ if (!p4P.isEmpty())
+ lst << "-p" << p4P;
+ auto p4U = p4User.volatileValue().toString();
+ if (!p4U.isEmpty())
+ lst << "-u" << p4U;
+ }
+ return lst;
+}
+
bool PerforceSettings::isValid() const
{
return !m_topLevel.isEmpty() && !p4BinaryPath.value().isEmpty();
@@ -206,24 +223,24 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings)
setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
setSettings(settings);
- setLayouter([settings, this](QWidget *widget) {
+ setLayouter([settings](QWidget *widget) {
PerforceSettings &s = *settings;
using namespace Layouting;
auto errorLabel = new QLabel;
auto testButton = new QPushButton(Tr::tr("Test"));
- connect(testButton, &QPushButton::clicked, this, [settings, errorLabel, testButton] {
+ QObject::connect(testButton, &QPushButton::clicked, widget, [settings, errorLabel, testButton] {
testButton->setEnabled(false);
auto checker = new PerforceChecker(errorLabel);
checker->setUseOverideCursor(true);
- connect(checker, &PerforceChecker::failed, errorLabel,
+ QObject::connect(checker, &PerforceChecker::failed, errorLabel,
[errorLabel, testButton, checker](const QString &t) {
errorLabel->setStyleSheet("background-color: red");
errorLabel->setText(t);
testButton->setEnabled(true);
checker->deleteLater();
});
- connect(checker, &PerforceChecker::succeeded, errorLabel,
+ QObject::connect(checker, &PerforceChecker::succeeded, errorLabel,
[errorLabel, testButton, checker](const FilePath &repo) {
errorLabel->setStyleSheet({});
errorLabel->setText(Tr::tr("Test succeeded (%1).")
@@ -234,7 +251,10 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings)
errorLabel->setStyleSheet(QString());
errorLabel->setText(Tr::tr("Testing..."));
- checker->start(settings->p4BinaryPath.filePath(), {}, settings->commonP4Arguments(), 10000);
+
+ const FilePath p4Bin = FilePath::fromUserInput(
+ settings->p4BinaryPath.volatileValue().toString());
+ checker->start(p4Bin, {}, settings->commonP4Arguments_volatile(), 10000);
});
Group config {
@@ -243,7 +263,8 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings)
};
Group environment {
- title(Tr::tr("Environment Variables"), &s.customEnv),
+ title(Tr::tr("Environment Variables")),
+ s.customEnv.groupChecker(),
Row { s.p4Port, s.p4Client, s.p4User }
};
diff --git a/src/plugins/perforce/perforcesettings.h b/src/plugins/perforce/perforcesettings.h
index f6c52957e6..b632d15325 100644
--- a/src/plugins/perforce/perforcesettings.h
+++ b/src/plugins/perforce/perforcesettings.h
@@ -41,8 +41,8 @@ public:
QString *repositoryRoot /* = 0 */,
QString *errorMessage);
- int longTimeOutS() const { return timeOutS.value() * 10; }
- int timeOutMS() const { return timeOutS.value() * 1000; }
+ int longTimeOutS() const { return timeOutS() * 10; }
+ int timeOutMS() const { return timeOutS() * 1000; }
Utils::FilePath topLevel() const;
Utils::FilePath topLevelSymLinkTarget() const;
@@ -64,6 +64,7 @@ public:
// Return basic arguments, including -d and server connection parameters.
QStringList commonP4Arguments() const;
QStringList commonP4Arguments(const QString &workingDir) const;
+ QStringList commonP4Arguments_volatile() const; // remove when auto apply is done
void clearTopLevel();
diff --git a/src/plugins/perforce/perforcesubmiteditorwidget.cpp b/src/plugins/perforce/perforcesubmiteditorwidget.cpp
index ddf4f1feb1..6887472822 100644
--- a/src/plugins/perforce/perforcesubmiteditorwidget.cpp
+++ b/src/plugins/perforce/perforcesubmiteditorwidget.cpp
@@ -22,7 +22,6 @@ public:
, m_clientName(createLabel())
, m_userName(createLabel())
{
- resize(402, 134);
setFlat(true);
setTitle(Tr::tr("Submit"));
diff --git a/src/plugins/perfprofiler/perfconfigwidget.cpp b/src/plugins/perfprofiler/perfconfigwidget.cpp
index 68c86ff720..e5e3fd3725 100644
--- a/src/plugins/perfprofiler/perfconfigwidget.cpp
+++ b/src/plugins/perfprofiler/perfconfigwidget.cpp
@@ -14,8 +14,8 @@
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QHeaderView>
@@ -120,9 +120,9 @@ void PerfConfigWidget::setTarget(ProjectExplorer::Target *target)
QTC_ASSERT(device, return);
QTC_CHECK(!m_process || m_process->state() == QProcess::NotRunning);
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setCommand({device->filePath("perf"), {"probe", "-l"}});
- connect(m_process.get(), &QtcProcess::done,
+ connect(m_process.get(), &Process::done,
this, &PerfConfigWidget::handleProcessDone);
useTracePointsButton->setEnabled(true);
diff --git a/src/plugins/perfprofiler/perfconfigwidget.h b/src/plugins/perfprofiler/perfconfigwidget.h
index 3fc65c4753..5372647064 100644
--- a/src/plugins/perfprofiler/perfconfigwidget.h
+++ b/src/plugins/perfprofiler/perfconfigwidget.h
@@ -14,7 +14,7 @@ class QPushButton;
class QTableView;
QT_END_NAMESPACE
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace PerfProfiler {
namespace Internal {
@@ -37,7 +37,7 @@ private:
void handleProcessDone();
PerfSettings *m_settings;
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
QTableView *eventsView;
QPushButton *useTracePointsButton;
diff --git a/src/plugins/perfprofiler/perfdatareader.cpp b/src/plugins/perfprofiler/perfdatareader.cpp
index 603d2ad016..a545dff913 100644
--- a/src/plugins/perfprofiler/perfdatareader.cpp
+++ b/src/plugins/perfprofiler/perfdatareader.cpp
@@ -18,7 +18,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
diff --git a/src/plugins/perfprofiler/perfloaddialog.cpp b/src/plugins/perfprofiler/perfloaddialog.cpp
index 8bf34316fe..8f162ed596 100644
--- a/src/plugins/perfprofiler/perfloaddialog.cpp
+++ b/src/plugins/perfprofiler/perfloaddialog.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/kit.h>
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/layoutbuilder.h>
@@ -109,7 +109,7 @@ void PerfLoadDialog::on_browseExecutableDirButton_pressed()
void PerfLoadDialog::chooseDefaults()
{
- ProjectExplorer::Target *target = ProjectExplorer::SessionManager::startupTarget();
+ ProjectExplorer::Target *target = ProjectExplorer::ProjectManager::startupTarget();
if (!target)
return;
diff --git a/src/plugins/perfprofiler/perfprofiler.qbs b/src/plugins/perfprofiler/perfprofiler.qbs
index f3151ae0a6..7a037e7fa4 100644
--- a/src/plugins/perfprofiler/perfprofiler.qbs
+++ b/src/plugins/perfprofiler/perfprofiler.qbs
@@ -75,9 +75,7 @@ QtcPlugin {
files: [ "PerfProfilerFlameGraphView.qml" ]
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "tests/"
files: [
"perfprofilertracefile_test.cpp",
diff --git a/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h b/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h
index d213bba099..4347728536 100644
--- a/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h
+++ b/src/plugins/perfprofiler/perfprofilerflamegraphmodel.h
@@ -19,10 +19,9 @@ class PerfProfilerFlameGraphModel : public QAbstractItemModel
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("use the context property")
- Q_DISABLE_COPY(PerfProfilerFlameGraphModel);
+ Q_DISABLE_COPY_MOVE(PerfProfilerFlameGraphModel);
+
public:
- PerfProfilerFlameGraphModel(PerfProfilerFlameGraphModel &&) = delete;
- PerfProfilerFlameGraphModel &operator=(PerfProfilerFlameGraphModel &&) = delete;
enum Role {
TypeIdRole = Qt::UserRole + 1, // Sort by data, not by displayed string
diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
index d14bfb87cd..8579a9cf7c 100644
--- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
+++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp
@@ -17,7 +17,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QAction>
#include <QMessageBox>
@@ -105,10 +105,10 @@ public:
void start() override
{
- m_process = new QtcProcess(this);
+ m_process = new Process(this);
- connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted);
- connect(m_process, &QtcProcess::done, this, [this] {
+ connect(m_process, &Process::started, this, &RunWorker::reportStarted);
+ connect(m_process, &Process::done, this, [this] {
// The terminate() below will frequently lead to QProcess::Crashed. We're not interested
// in that. FailedToStart is the only actual failure.
if (m_process->error() == QProcess::FailedToStart) {
@@ -141,10 +141,10 @@ public:
m_process->terminate();
}
- QtcProcess *recorder() { return m_process; }
+ Process *recorder() { return m_process; }
private:
- QPointer<QtcProcess> m_process;
+ QPointer<Process> m_process;
QStringList m_perfRecordArguments;
};
@@ -193,12 +193,12 @@ void PerfProfilerRunner::start()
PerfDataReader *reader = m_perfParserWorker->reader();
if (auto prw = qobject_cast<LocalPerfRecordWorker *>(m_perfRecordWorker)) {
// That's the local case.
- QtcProcess *recorder = prw->recorder();
- connect(recorder, &QtcProcess::readyReadStandardError, this, [this, recorder] {
+ Process *recorder = prw->recorder();
+ connect(recorder, &Process::readyReadStandardError, this, [this, recorder] {
appendMessage(QString::fromLocal8Bit(recorder->readAllRawStandardError()),
Utils::StdErrFormat);
});
- connect(recorder, &QtcProcess::readyReadStandardOutput, this, [this, reader, recorder] {
+ connect(recorder, &Process::readyReadStandardOutput, this, [this, reader, recorder] {
if (!reader->feedParser(recorder->readAllRawStandardOutput()))
reportFailure(Tr::tr("Failed to transfer Perf data to perfparser."));
});
diff --git a/src/plugins/perfprofiler/perfprofilertool.cpp b/src/plugins/perfprofiler/perfprofilertool.cpp
index d4515f79b8..60bbcc3ae3 100644
--- a/src/plugins/perfprofiler/perfprofilertool.cpp
+++ b/src/plugins/perfprofiler/perfprofilertool.cpp
@@ -27,13 +27,14 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
#include <utils/fancymainwindow.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QFileDialog>
@@ -120,6 +121,7 @@ PerfProfilerTool::PerfProfilerTool()
options->addAction(command);
m_tracePointsButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_tracePointsButton);
m_tracePointsButton->setDefaultAction(tracePointsAction);
m_objectsToDelete << m_tracePointsButton;
@@ -146,14 +148,18 @@ PerfProfilerTool::PerfProfilerTool()
this, &PerfProfilerTool::updateRunActions);
m_recordButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_recordButton);
m_clearButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_clearButton);
m_filterButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_filterButton);
m_filterMenu = new QMenu(m_filterButton);
m_aggregateButton = new QToolButton;
+ StyleHelper::setPanelWidget(m_aggregateButton);
m_recordedLabel = new QLabel;
- m_recordedLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_recordedLabel);
m_delayLabel = new QLabel;
- m_delayLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(m_delayLabel);
m_objectsToDelete << m_recordButton << m_clearButton << m_filterButton << m_aggregateButton
<< m_recordedLabel << m_delayLabel;
@@ -224,7 +230,7 @@ void PerfProfilerTool::createViews()
connect(recordMenu, &QMenu::aboutToShow, recordMenu, [recordMenu] {
recordMenu->hide();
PerfSettings *settings = nullptr;
- Target *target = SessionManager::startupTarget();
+ Target *target = ProjectManager::startupTarget();
if (target) {
if (auto runConfig = target->activeRunConfiguration())
settings = runConfig->currentSettings<PerfSettings>(Constants::PerfSettingsId);
@@ -250,7 +256,7 @@ void PerfProfilerTool::createViews()
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setPopupMode(QToolButton::InstantPopup);
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterButton->setMenu(m_filterMenu);
m_aggregateButton->setIcon(Utils::Icons::EXPAND_ALL_TOOLBAR.icon());
@@ -572,7 +578,7 @@ static Utils::FilePaths sourceFiles(const Project *currentProject = nullptr)
if (currentProject)
sourceFiles.append(currentProject->files(Project::SourceFiles));
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
for (const Project *project : projects) {
if (project != currentProject)
sourceFiles.append(project->files(Project::SourceFiles));
@@ -609,7 +615,7 @@ void PerfProfilerTool::showLoadTraceDialog()
startLoading();
- const Project *currentProject = SessionManager::startupProject();
+ const Project *currentProject = ProjectManager::startupProject();
const Target *target = currentProject ? currentProject->activeTarget() : nullptr;
const Kit *kit = target ? target->kit() : nullptr;
populateFileFinder(currentProject, kit);
diff --git a/src/plugins/perfprofiler/perfsettings.cpp b/src/plugins/perfprofiler/perfsettings.cpp
index 8175dece0e..e5f307b85a 100644
--- a/src/plugins/perfprofiler/perfsettings.cpp
+++ b/src/plugins/perfprofiler/perfsettings.cpp
@@ -10,7 +10,7 @@
#include <QSettings>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace Utils;
@@ -122,7 +122,7 @@ QStringList PerfSettings::perfRecordArguments() const
"--call-graph", callgraphArg,
sampleMode.itemValue().toString(),
QString::number(period.value())})
- + ProcessArgs::splitArgs(extraArguments.value());
+ + ProcessArgs::splitArgs(extraArguments.value(), HostOsInfo::hostOs());
}
void PerfSettings::resetToDefault()
diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp
index da3b4c38c3..3920b526b8 100644
--- a/src/plugins/perfprofiler/perftracepointdialog.cpp
+++ b/src/plugins/perfprofiler/perftracepointdialog.cpp
@@ -8,12 +8,12 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
#include <QComboBox>
#include <QDialogButtonBox>
@@ -41,7 +41,7 @@ PerfTracePointDialog::PerfTracePointDialog()
m_privilegesChooser->addItems({ELEVATE_METHOD_NA, ELEVATE_METHOD_PKEXEC, ELEVATE_METHOD_SUDO});
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_label,
m_textEdit,
@@ -51,7 +51,7 @@ PerfTracePointDialog::PerfTracePointDialog()
m_buttonBox,
}.attachTo(this);
- if (const Target *target = SessionManager::startupTarget()) {
+ if (const Target *target = ProjectManager::startupTarget()) {
const Kit *kit = target->kit();
QTC_ASSERT(kit, return);
@@ -93,7 +93,7 @@ void PerfTracePointDialog::runScript()
m_privilegesChooser->setEnabled(false);
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
- m_process.reset(new QtcProcess(this));
+ m_process.reset(new Process(this));
m_process->setWriteData(m_textEdit->toPlainText().toUtf8());
m_textEdit->clear();
@@ -103,7 +103,7 @@ void PerfTracePointDialog::runScript()
else
m_process->setCommand({m_device->filePath("sh"), {}});
- connect(m_process.get(), &QtcProcess::done, this, &PerfTracePointDialog::handleProcessDone);
+ connect(m_process.get(), &Process::done, this, &PerfTracePointDialog::handleProcessDone);
m_process->start();
}
diff --git a/src/plugins/perfprofiler/perftracepointdialog.h b/src/plugins/perfprofiler/perftracepointdialog.h
index 427220e64e..d5affdcc7c 100644
--- a/src/plugins/perfprofiler/perftracepointdialog.h
+++ b/src/plugins/perfprofiler/perftracepointdialog.h
@@ -15,7 +15,7 @@ class QLabel;
class QTextEdit;
QT_END_NAMESPACE
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace PerfProfiler {
namespace Internal {
@@ -40,7 +40,7 @@ private:
QComboBox *m_privilegesChooser;
QDialogButtonBox *m_buttonBox;
ProjectExplorer::IDeviceConstPtr m_device;
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
void accept() final;
void reject() final;
diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs
index b878e76c3e..b199706481 100644
--- a/src/plugins/plugins.qbs
+++ b/src/plugins/plugins.qbs
@@ -23,6 +23,7 @@ Project {
"coco/coco.qbs",
"compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs",
"conan/conan.qbs",
+ "copilot/copilot.qbs",
"coreplugin/coreplugin.qbs",
"coreplugin/images/logo/logo.qbs",
"cpaster/cpaster.qbs",
@@ -43,6 +44,7 @@ Project {
"git/git.qbs",
"gitlab/gitlab.qbs",
"glsleditor/glsleditor.qbs",
+ "haskell/haskell.qbs",
"helloworld/helloworld.qbs",
"help/help.qbs",
"imageviewer/imageviewer.qbs",
@@ -68,6 +70,7 @@ Project {
"qmlprojectmanager/qmlprojectmanager.qbs",
"qnx/qnx.qbs",
"qmakeprojectmanager/qmakeprojectmanager.qbs",
+ "qmldesignerbase/qmldesignerbase.qbs",
"qtsupport/qtsupport.qbs",
"remotelinux/remotelinux.qbs",
"resourceeditor/resourceeditor.qbs",
@@ -78,12 +81,14 @@ Project {
"squish/squish.qbs",
"studiowelcome/studiowelcome.qbs",
"subversion/subversion.qbs",
+ "terminal/terminal.qbs",
"texteditor/texteditor.qbs",
"todo/todo.qbs",
"updateinfo/updateinfo.qbs",
"valgrind/valgrind.qbs",
+ "vcpkg/vcpkg.qbs",
"vcsbase/vcsbase.qbs",
"webassembly/webassembly.qbs",
- "welcome/welcome.qbs"
+ "welcome/welcome.qbs",
].concat(project.additionalPlugins)
}
diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt
index 58f8c73d1d..1ed25adada 100644
--- a/src/plugins/projectexplorer/CMakeLists.txt
+++ b/src/plugins/projectexplorer/CMakeLists.txt
@@ -28,6 +28,7 @@ add_qtc_plugin(ProjectExplorer
codestylesettingspropertiespage.cpp codestylesettingspropertiespage.h
compileoutputwindow.cpp compileoutputwindow.h
configtaskhandler.cpp configtaskhandler.h
+ copystep.cpp copystep.h
copytaskhandler.cpp copytaskhandler.h
currentprojectfilter.cpp currentprojectfilter.h
currentprojectfind.cpp currentprojectfind.h
@@ -65,8 +66,7 @@ add_qtc_plugin(ProjectExplorer
devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h
devicesupport/idevicefwd.h
devicesupport/idevicewidget.h
- devicesupport/localprocesslist.cpp devicesupport/localprocesslist.h
- devicesupport/sshdeviceprocesslist.cpp devicesupport/sshdeviceprocesslist.h
+ devicesupport/processlist.cpp devicesupport/processlist.h
devicesupport/sshparameters.cpp devicesupport/sshparameters.h
devicesupport/sshsettings.cpp devicesupport/sshsettings.h
devicesupport/sshsettingspage.cpp devicesupport/sshsettingspage.h
@@ -141,7 +141,7 @@ add_qtc_plugin(ProjectExplorer
projectfilewizardextension.cpp projectfilewizardextension.h
projectimporter.cpp projectimporter.h
projectmacro.cpp projectmacro.h
- projectmanager.h
+ projectmanager.cpp projectmanager.h
projectmodels.cpp projectmodels.h
projectnodes.cpp projectnodes.h
projectpanelfactory.cpp projectpanelfactory.h
@@ -159,7 +159,7 @@ add_qtc_plugin(ProjectExplorer
runsettingspropertiespage.cpp runsettingspropertiespage.h
sanitizerparser.cpp sanitizerparser.h
selectablefilesmodel.cpp selectablefilesmodel.h
- session.cpp session.h
+ session.cpp session.h session_p.h
sessiondialog.cpp sessiondialog.h
sessionmodel.cpp sessionmodel.h
sessionview.cpp sessionview.h
@@ -216,9 +216,14 @@ extend_qtc_plugin(ProjectExplorer
extend_qtc_plugin(ProjectExplorer
CONDITION WITH_TESTS
SOURCES
- jsonwizard/jsonwizard_test.cpp
outputparser_test.cpp outputparser_test.h
)
+extend_qtc_plugin(ProjectExplorer
+ CONDITION WITH_TESTS
+ SOURCES
+ jsonwizard/jsonwizard_test.cpp
+ SOURCES_PROPERTIES HEADER_FILE_ONLY ON
+)
file(GLOB_RECURSE test_resources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} testdata/*)
qtc_add_resources(ProjectExplorer "testdata"
@@ -227,11 +232,3 @@ qtc_add_resources(ProjectExplorer "testdata"
BASE "."
FILES ${test_resources}
)
-
-qtc_plugin_enabled(_projectexplorer_enabled ProjectExplorer)
-if (WITH_TESTS AND _projectexplorer_enabled)
- set_source_files_properties(jsonwizard/jsonwizard_test.cpp
- PROPERTIES HEADER_FILE_ONLY ON
- )
-endif()
-
diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp
index c838458aa5..806d9159f9 100644
--- a/src/plugins/projectexplorer/abstractprocessstep.cpp
+++ b/src/plugins/projectexplorer/abstractprocessstep.cpp
@@ -12,14 +12,15 @@
#include <utils/fileutils.h>
#include <utils/outputformatter.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QTextDecoder>
#include <algorithm>
#include <memory>
+using namespace Tasking;
using namespace Utils;
namespace ProjectExplorer {
@@ -76,7 +77,7 @@ public:
void cleanUp(int exitCode, QProcess::ExitStatus status);
AbstractProcessStep *q;
- std::unique_ptr<QtcProcess> m_process;
+ std::unique_ptr<Process> m_process;
std::unique_ptr<TaskTree> m_taskTree;
ProcessParameters m_param;
ProcessParameters *m_displayedParams = &m_param;
@@ -182,9 +183,9 @@ void AbstractProcessStep::doRun()
setupStreams();
- d->m_process.reset(new QtcProcess);
+ d->m_process.reset(new Process);
setupProcess(d->m_process.get());
- connect(d->m_process.get(), &QtcProcess::done, this, &AbstractProcessStep::handleProcessDone);
+ connect(d->m_process.get(), &Process::done, this, &AbstractProcessStep::handleProcessDone);
d->m_process->start();
}
@@ -209,7 +210,7 @@ void AbstractProcessStep::setupStreams()
d->stderrStream = std::make_unique<QTextDecoder>(QTextCodec::codecForLocale());
}
-void AbstractProcessStep::setupProcess(QtcProcess *process)
+void AbstractProcessStep::setupProcess(Process *process)
{
process->setUseCtrlCStub(HostOsInfo::isWindowsHost());
process->setWorkingDirectory(d->m_param.effectiveWorkingDirectory());
@@ -224,15 +225,15 @@ void AbstractProcessStep::setupProcess(QtcProcess *process)
if (d->m_lowPriority && ProjectExplorerPlugin::projectExplorerSettings().lowBuildPriority)
process->setLowPriority();
- connect(process, &QtcProcess::readyReadStandardOutput, this, [this, process] {
+ connect(process, &Process::readyReadStandardOutput, this, [this, process] {
emit addOutput(d->stdoutStream->toUnicode(process->readAllRawStandardOutput()),
OutputFormat::Stdout, DontAppendNewline);
});
- connect(process, &QtcProcess::readyReadStandardError, this, [this, process] {
+ connect(process, &Process::readyReadStandardError, this, [this, process] {
emit addOutput(d->stderrStream->toUnicode(process->readAllRawStandardError()),
OutputFormat::Stderr, DontAppendNewline);
});
- connect(process, &QtcProcess::started, this, [this] {
+ connect(process, &Process::started, this, [this] {
ProcessParameters *params = displayedParameters();
emit addOutput(Tr::tr("Starting: \"%1\" %2")
.arg(params->effectiveCommand().toUserOutput(), params->prettyArguments()),
@@ -240,7 +241,7 @@ void AbstractProcessStep::setupProcess(QtcProcess *process)
});
}
-void AbstractProcessStep::runTaskTree(const Tasking::Group &recipe)
+void AbstractProcessStep::runTaskTree(const Group &recipe)
{
setupStreams();
@@ -306,7 +307,7 @@ bool AbstractProcessStep::setupProcessParameters(ProcessParameters *params) cons
const bool looksGood = executable.isEmpty() || executable.ensureReachable(workingDirectory);
QTC_ASSERT(looksGood, return false);
- params->setWorkingDirectory(workingDirectory.onDevice(executable));
+ params->setWorkingDirectory(executable.withNewPath(workingDirectory.path()));
return true;
}
diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h
index 8112541447..fc1ed13135 100644
--- a/src/plugins/projectexplorer/abstractprocessstep.h
+++ b/src/plugins/projectexplorer/abstractprocessstep.h
@@ -10,10 +10,11 @@
namespace Utils {
class CommandLine;
enum class ProcessResult;
-class QtcProcess;
-namespace Tasking { class Group; }
+class Process;
}
+namespace Tasking { class Group; }
+
namespace ProjectExplorer {
class ProcessParameters;
@@ -51,8 +52,8 @@ protected:
virtual void finish(Utils::ProcessResult result);
bool checkWorkingDirectory();
- void setupProcess(Utils::QtcProcess *process);
- void runTaskTree(const Utils::Tasking::Group &recipe);
+ void setupProcess(Utils::Process *process);
+ void runTaskTree(const Tasking::Group &recipe);
ProcessParameters *displayedParameters() const;
private:
diff --git a/src/plugins/projectexplorer/allprojectsfilter.cpp b/src/plugins/projectexplorer/allprojectsfilter.cpp
index ea4d86dba7..42e110d350 100644
--- a/src/plugins/projectexplorer/allprojectsfilter.cpp
+++ b/src/plugins/projectexplorer/allprojectsfilter.cpp
@@ -3,14 +3,17 @@
#include "allprojectsfilter.h"
+#include "project.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
-#include "session.h"
-#include "project.h"
+#include "projectmanager.h"
#include <utils/algorithm.h>
+#include <QFuture>
+
using namespace Core;
+using namespace Utils;
namespace ProjectExplorer::Internal {
@@ -18,38 +21,29 @@ AllProjectsFilter::AllProjectsFilter()
{
setId("Files in any project");
setDisplayName(Tr::tr("Files in Any Project"));
- setDescription(Tr::tr("Matches all files of all open projects. Append \"+<number>\" or "
- "\":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ setDescription(Tr::tr("Locates files of all open projects. Append \"+<number>\" or "
+ "\":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
setDefaultShortcutString("a");
setDefaultIncludedByDefault(true);
+ setRefreshRecipe(Tasking::Sync([this] { m_cache.invalidate(); }));
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
- this, &AllProjectsFilter::markFilesAsOutOfDate);
-}
-
-void AllProjectsFilter::markFilesAsOutOfDate()
-{
- setFileIterator(nullptr);
-}
-
-void AllProjectsFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- if (!fileIterator()) {
- Utils::FilePaths paths;
- for (Project *project : SessionManager::projects())
- paths.append(project->files(Project::SourceFiles));
- Utils::sort(paths);
- setFileIterator(new BaseFileFilter::ListIterator(paths));
- }
- BaseFileFilter::prepareSearch(entry);
-}
-
-void AllProjectsFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- QMetaObject::invokeMethod(this, &AllProjectsFilter::markFilesAsOutOfDate, Qt::QueuedConnection);
+ this, [this] { m_cache.invalidate(); });
+ m_cache.setGeneratorProvider([] {
+ // This body runs in main thread
+ FilePaths filePaths;
+ for (Project *project : ProjectManager::projects())
+ filePaths.append(project->files(Project::SourceFiles));
+ return [filePaths](const QFuture<void> &future) {
+ // This body runs in non-main thread
+ FilePaths sortedPaths = filePaths;
+ if (future.isCanceled())
+ return FilePaths();
+ Utils::sort(sortedPaths);
+ return sortedPaths;
+ };
+ });
}
-} // ProjectExplorer::Internal
+} // namespace ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/allprojectsfilter.h b/src/plugins/projectexplorer/allprojectsfilter.h
index ae9fdffe92..6782ef4611 100644
--- a/src/plugins/projectexplorer/allprojectsfilter.h
+++ b/src/plugins/projectexplorer/allprojectsfilter.h
@@ -3,25 +3,18 @@
#pragma once
-#include <coreplugin/locator/basefilefilter.h>
+#include <coreplugin/locator/ilocatorfilter.h>
-#include <QFutureInterface>
+namespace ProjectExplorer::Internal {
-namespace ProjectExplorer {
-namespace Internal {
-
-class AllProjectsFilter : public Core::BaseFileFilter
+class AllProjectsFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
AllProjectsFilter();
- void refresh(QFutureInterface<void> &future) override;
- void prepareSearch(const QString &entry) override;
private:
- void markFilesAsOutOfDate();
+ Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
+ Core::LocatorFileCache m_cache;
};
-} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp
index ac4afc1d6e..d61b156052 100644
--- a/src/plugins/projectexplorer/allprojectsfind.cpp
+++ b/src/plugins/projectexplorer/allprojectsfind.cpp
@@ -7,7 +7,7 @@
#include "project.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <coreplugin/editormanager/editormanager.h>
@@ -44,7 +44,7 @@ QString AllProjectsFind::displayName() const
bool AllProjectsFind::isEnabled() const
{
- return BaseFileFind::isEnabled() && SessionManager::hasProjects();
+ return BaseFileFind::isEnabled() && ProjectManager::hasProjects();
}
FileIterator *AllProjectsFind::files(const QStringList &nameFilters,
@@ -52,7 +52,7 @@ FileIterator *AllProjectsFind::files(const QStringList &nameFilters,
const QVariant &additionalParameters) const
{
Q_UNUSED(additionalParameters)
- return filesForProjects(nameFilters, exclusionFilters, SessionManager::projects());
+ return filesForProjects(nameFilters, exclusionFilters, ProjectManager::projects());
}
FileIterator *AllProjectsFind::filesForProjects(const QStringList &nameFilters,
diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp
index adf37a3362..e4cbf6903b 100644
--- a/src/plugins/projectexplorer/buildaspects.cpp
+++ b/src/plugins/projectexplorer/buildaspects.cpp
@@ -107,12 +107,12 @@ void BuildDirectoryAspect::fromMap(const QVariantMap &map)
}
}
-void BuildDirectoryAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void BuildDirectoryAspect::addToLayout(Layouting::LayoutItem &parent)
{
- StringAspect::addToLayout(builder);
+ StringAspect::addToLayout(parent);
d->problemLabel = new InfoLabel({}, InfoLabel::Warning);
d->problemLabel->setElideMode(Qt::ElideNone);
- builder.addRow({{}, d->problemLabel.data()});
+ parent.addItems({{}, d->problemLabel.data()});
updateProblemLabel();
if (!d->sourceDir.isEmpty()) {
connect(this, &StringAspect::checkedChanged, this, [this] {
diff --git a/src/plugins/projectexplorer/buildaspects.h b/src/plugins/projectexplorer/buildaspects.h
index 9788cc69ca..96c6db9071 100644
--- a/src/plugins/projectexplorer/buildaspects.h
+++ b/src/plugins/projectexplorer/buildaspects.h
@@ -23,7 +23,7 @@ public:
bool isShadowBuild() const;
void setProblem(const QString &description);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
static Utils::FilePath fixupDir(const Utils::FilePath &dir);
diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp
index ff14b7ab07..70d5a348c1 100644
--- a/src/plugins/projectexplorer/buildconfiguration.cpp
+++ b/src/plugins/projectexplorer/buildconfiguration.cpp
@@ -17,8 +17,8 @@
#include "projectexplorer.h"
#include "projectexplorertr.h"
#include "project.h"
+#include "projectmanager.h"
#include "projecttree.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/fileutils.h>
@@ -167,6 +167,10 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id)
expander->registerVariable("buildDir", Tr::tr("Build directory"),
[this] { return buildDirectory().toUserOutput(); });
+ expander->registerFileVariables("BuildConfig:BuildDirectory",
+ Tr::tr("Build directory"),
+ [this] { return buildDirectory(); });
+
expander->registerVariable("BuildConfig:Name", Tr::tr("Name of the build configuration"),
[this] { return displayName(); });
@@ -209,7 +213,7 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id)
connect(target, &Target::parsingStarted, this, &BuildConfiguration::enabledChanged);
connect(target, &Target::parsingFinished, this, &BuildConfiguration::enabledChanged);
connect(this, &BuildConfiguration::enabledChanged, this, [this] {
- if (isActive() && project() == SessionManager::startupProject()) {
+ if (isActive() && project() == ProjectManager::startupProject()) {
ProjectExplorerPlugin::updateActions();
ProjectExplorerPlugin::updateRunActions();
}
@@ -318,12 +322,15 @@ NamedWidget *BuildConfiguration::createConfigWidget()
widget = named;
}
- Layouting::Form builder;
+ Layouting::Form form;
for (BaseAspect *aspect : aspects()) {
- if (aspect->isVisible())
- aspect->addToLayout(builder.finishRow());
+ if (aspect->isVisible()) {
+ form.addItem(aspect);
+ form.addItem(Layouting::br);
+ }
}
- builder.attachTo(widget, Layouting::WithoutMargins);
+ form.addItem(Layouting::noMargin);
+ form.attachTo(widget);
return named;
}
@@ -612,13 +619,27 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD
[buildType] { return buildTypeName(buildType); });
exp.registerSubProvider([kit] { return kit->macroExpander(); });
- QString buildDir = ProjectExplorerPlugin::buildDirectoryTemplate();
- qCDebug(bcLog) << "build dir template:" << buildDir;
+ FilePath buildDir = FilePath::fromUserInput(ProjectExplorerPlugin::buildDirectoryTemplate());
+ qCDebug(bcLog) << "build dir template:" << buildDir.toUserOutput();
buildDir = exp.expand(buildDir);
- qCDebug(bcLog) << "expanded build:" << buildDir;
- buildDir.replace(" ", "-");
+ qCDebug(bcLog) << "expanded build:" << buildDir.toUserOutput();
+ buildDir = buildDir.withNewPath(buildDir.path().replace(" ", "-"));
+
+ auto buildDevice = BuildDeviceKitAspect::device(kit);
+
+ if (buildDir.isAbsolutePath()) {
+ bool isReachable = buildDevice->ensureReachable(buildDir);
+ if (!isReachable)
+ return {};
+ return buildDevice->rootPath().withNewMappedPath(buildDir);
+ }
+
+ bool isReachable = buildDevice->ensureReachable(projectDir);
+ if (!isReachable)
+ return {};
- return projectDir.resolvePath(buildDir);
+ const FilePath baseDir = buildDevice->rootPath().withNewMappedPath(projectDir);
+ return baseDir.resolvePath(buildDir);
}
///
// IBuildConfigurationFactory
diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp
index 57bfde6e46..6284a96041 100644
--- a/src/plugins/projectexplorer/buildmanager.cpp
+++ b/src/plugins/projectexplorer/buildmanager.cpp
@@ -16,8 +16,8 @@
#include "projectexplorerconstants.h"
#include "projectexplorersettings.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runcontrol.h"
-#include "session.h"
#include "target.h"
#include "task.h"
#include "taskhub.h"
@@ -258,7 +258,7 @@ BuildManager::BuildManager(QObject *parent, QAction *cancelBuildAction)
m_instance = this;
d = new BuildManagerPrivate;
- connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
+ connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject,
this, &BuildManager::aboutToRemoveProject);
d->m_outputWindow = new Internal::CompileOutputWindow(cancelBuildAction);
@@ -318,19 +318,19 @@ void BuildManager::rebuildProjectWithoutDependencies(Project *project)
void BuildManager::buildProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
- queue(SessionManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)},
+ queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)},
configSelection);
}
void BuildManager::cleanProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
- queue(SessionManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)},
+ queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_CLEAN)},
configSelection);
}
void BuildManager::rebuildProjectWithDependencies(Project *project, ConfigSelection configSelection)
{
- queue(SessionManager::projectOrder(project),
+ queue(ProjectManager::projectOrder(project),
{Id(Constants::BUILDSTEPS_CLEAN), Id(Constants::BUILDSTEPS_BUILD)},
configSelection);
}
@@ -384,7 +384,7 @@ BuildForRunConfigStatus BuildManager::potentiallyBuildForRunConfig(RunConfigurat
}
Project * const pro = rc->target()->project();
- const int queueCount = queue(SessionManager::projectOrder(pro), stepIds,
+ const int queueCount = queue(ProjectManager::projectOrder(pro), stepIds,
ConfigSelection::Active, rc);
if (rc->target()->activeBuildConfiguration())
rc->target()->activeBuildConfiguration()->restrictNextBuild(nullptr);
diff --git a/src/plugins/projectexplorer/buildpropertiessettings.cpp b/src/plugins/projectexplorer/buildpropertiessettings.cpp
index a9126551e3..be0a699707 100644
--- a/src/plugins/projectexplorer/buildpropertiessettings.cpp
+++ b/src/plugins/projectexplorer/buildpropertiessettings.cpp
@@ -24,6 +24,25 @@ BuildPropertiesSettings::BuildPropertiesSettings()
{
setAutoApply(false);
+ setId("AB.ProjectExplorer.BuildPropertiesSettingsPage");
+ setDisplayName(Tr::tr("Default Build Properties"));
+ setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
+ setSettings(this);
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+
+ Column {
+ Form {
+ buildDirectoryTemplate, br,
+ separateDebugInfo, br,
+ qmlDebugging, br,
+ qtQuickCompiler
+ },
+ st
+ }.attachTo(widget);
+ });
+
registerAspect(&buildDirectoryTemplate);
buildDirectoryTemplate.setDisplayStyle(StringAspect::LineEditDisplay);
buildDirectoryTemplate.setSettingsKey("Directories/BuildDirectory.TemplateV2");
@@ -75,30 +94,4 @@ QString BuildPropertiesSettings::defaultBuildDirectoryTemplate()
return QString(DEFAULT_BUILD_DIRECTORY_TEMPLATE);
}
-namespace Internal {
-
-BuildPropertiesSettingsPage::BuildPropertiesSettingsPage(BuildPropertiesSettings *settings)
-{
- setId("AB.ProjectExplorer.BuildPropertiesSettingsPage");
- setDisplayName(Tr::tr("Default Build Properties"));
- setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- BuildPropertiesSettings &s = *settings;
- using namespace Layouting;
-
- Column {
- Form {
- s.buildDirectoryTemplate,
- s.separateDebugInfo,
- s.qmlDebugging,
- s.qtQuickCompiler
- },
- st
- }.attachTo(widget);
- });
-}
-
-} // Internal
} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/buildpropertiessettings.h b/src/plugins/projectexplorer/buildpropertiessettings.h
index 75ec957a4e..5edf60d666 100644
--- a/src/plugins/projectexplorer/buildpropertiessettings.h
+++ b/src/plugins/projectexplorer/buildpropertiessettings.h
@@ -7,11 +7,9 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
-
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Utils::AspectContainer
+class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Core::PagedSettings
{
public:
BuildPropertiesSettings();
@@ -34,13 +32,4 @@ public:
QString defaultBuildDirectoryTemplate();
};
-namespace Internal {
-
-class BuildPropertiesSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit BuildPropertiesSettingsPage(BuildPropertiesSettings *settings);
-};
-
-} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp
index 410449083c..c715cff6d0 100644
--- a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp
@@ -185,7 +185,7 @@ void BuildSettingsWidget::currentIndexChanged(int index)
{
auto buildConfiguration = qobject_cast<BuildConfiguration *>(
m_target->buildConfigurationModel()->projectConfigurationAt(index));
- SessionManager::setActiveBuildConfiguration(m_target, buildConfiguration, SetActive::Cascade);
+ m_target->setActiveBuildConfiguration(buildConfiguration, SetActive::Cascade);
}
void BuildSettingsWidget::updateActiveConfiguration()
@@ -222,7 +222,7 @@ void BuildSettingsWidget::createConfiguration(const BuildInfo &info_)
return;
m_target->addBuildConfiguration(bc);
- SessionManager::setActiveBuildConfiguration(m_target, bc, SetActive::Cascade);
+ m_target->setActiveBuildConfiguration(bc, SetActive::Cascade);
}
QString BuildSettingsWidget::uniqueName(const QString & name)
@@ -286,7 +286,7 @@ void BuildSettingsWidget::cloneConfiguration()
bc->setDisplayName(name);
const FilePath buildDirectory = bc->buildDirectory();
if (buildDirectory != m_target->project()->projectDirectory()) {
- const std::function<bool(const FilePath &)> isBuildDirOk = [this](const FilePath &candidate) {
+ const FilePathPredicate isBuildDirOk = [this](const FilePath &candidate) {
if (candidate.exists())
return false;
return !anyOf(m_target->buildConfigurations(), [&candidate](const BuildConfiguration *bc) {
@@ -295,7 +295,7 @@ void BuildSettingsWidget::cloneConfiguration()
bc->setBuildDirectory(makeUniquelyNumbered(buildDirectory, isBuildDirOk));
}
m_target->addBuildConfiguration(bc);
- SessionManager::setActiveBuildConfiguration(m_target, bc, SetActive::Cascade);
+ m_target->setActiveBuildConfiguration(bc, SetActive::Cascade);
}
void BuildSettingsWidget::deleteConfiguration(BuildConfiguration *deleteConfiguration)
diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp
index c078a73d82..46283b4c9f 100644
--- a/src/plugins/projectexplorer/buildstep.cpp
+++ b/src/plugins/projectexplorer/buildstep.cpp
@@ -160,12 +160,13 @@ QWidget *BuildStep::doCreateConfigWidget()
QWidget *BuildStep::createConfigWidget()
{
- Layouting::Form builder;
+ Layouting::Form form;
for (BaseAspect *aspect : std::as_const(m_aspects)) {
if (aspect->isVisible())
- aspect->addToLayout(builder.finishRow());
+ form.addItem(aspect);
}
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ form.addItem(Layouting::noMargin);
+ auto widget = form.emerge();
if (m_addMacroExpander)
VariableChooser::addSupportForChildWidgets(widget, macroExpander());
@@ -361,7 +362,7 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const
return false;
}
- if (!m_isRepeatable && bsl->contains(m_info.id))
+ if (!m_isRepeatable && bsl->contains(m_stepId))
return false;
if (m_supportedConfiguration.isValid()) {
@@ -375,14 +376,42 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const
return true;
}
+QString BuildStepFactory::displayName() const
+{
+ return m_displayName;
+}
+
+void BuildStepFactory::cloneStepCreator(Id exitstingStepId, Id overrideNewStepId)
+{
+ m_stepId = {};
+ m_creator = {};
+ for (BuildStepFactory *factory : BuildStepFactory::allBuildStepFactories()) {
+ if (factory->m_stepId == exitstingStepId) {
+ m_creator = factory->m_creator;
+ m_stepId = factory->m_stepId;
+ m_displayName = factory->m_displayName;
+ // Other bits are intentionally not copied as they are unlikely to be
+ // useful in the cloner's context. The cloner can/has to finish the
+ // setup on its own.
+ break;
+ }
+ }
+ // Existence should be guaranteed by plugin dependencies. In case it fails,
+ // bark and keep the factory in a state where the invalid m_stepId keeps it
+ // inaction.
+ QTC_ASSERT(m_creator, return);
+ if (overrideNewStepId.isValid())
+ m_stepId = overrideNewStepId;
+}
+
void BuildStepFactory::setDisplayName(const QString &displayName)
{
- m_info.displayName = displayName;
+ m_displayName = displayName;
}
-void BuildStepFactory::setFlags(BuildStepInfo::Flags flags)
+void BuildStepFactory::setFlags(BuildStep::Flags flags)
{
- m_info.flags = flags;
+ m_flags = flags;
}
void BuildStepFactory::setSupportedStepList(Id id)
@@ -415,20 +444,21 @@ void BuildStepFactory::setSupportedDeviceTypes(const QList<Id> &ids)
m_supportedDeviceTypes = ids;
}
-BuildStepInfo BuildStepFactory::stepInfo() const
+BuildStep::Flags BuildStepFactory::stepFlags() const
{
- return m_info;
+ return m_flags;
}
Id BuildStepFactory::stepId() const
{
- return m_info.id;
+ return m_stepId;
}
BuildStep *BuildStepFactory::create(BuildStepList *parent)
{
- BuildStep *step = m_info.creator(parent);
- step->setDefaultDisplayName(m_info.displayName);
+ QTC_ASSERT(m_creator, return nullptr);
+ BuildStep *step = m_creator(parent);
+ step->setDefaultDisplayName(m_displayName);
return step;
}
diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h
index 547a5e2027..46be75f563 100644
--- a/src/plugins/projectexplorer/buildstep.h
+++ b/src/plugins/projectexplorer/buildstep.h
@@ -76,6 +76,12 @@ public:
enum OutputNewlineSetting { DoAppendNewline, DontAppendNewline };
+ enum Flags {
+ Uncreatable = 1 << 0,
+ Unclonable = 1 << 1,
+ UniqueStep = 1 << 8 // Can't be used twice in a BuildStepList
+ };
+
bool widgetExpandedByDefault() const;
void setWidgetExpandedByDefault(bool widgetExpandedByDefault);
@@ -136,23 +142,6 @@ private:
QString m_summaryText;
};
-class PROJECTEXPLORER_EXPORT BuildStepInfo
-{
-public:
- enum Flags {
- Uncreatable = 1 << 0,
- Unclonable = 1 << 1,
- UniqueStep = 1 << 8 // Can't be used twice in a BuildStepList
- };
-
- using BuildStepCreator = std::function<BuildStep *(BuildStepList *)>;
-
- Utils::Id id;
- QString displayName;
- Flags flags = Flags();
- BuildStepCreator creator;
-};
-
class PROJECTEXPLORER_EXPORT BuildStepFactory
{
public:
@@ -163,23 +152,26 @@ public:
static const QList<BuildStepFactory *> allBuildStepFactories();
- BuildStepInfo stepInfo() const;
+ BuildStep::Flags stepFlags() const;
Utils::Id stepId() const;
BuildStep *create(BuildStepList *parent);
BuildStep *restore(BuildStepList *parent, const QVariantMap &map);
bool canHandle(BuildStepList *bsl) const;
+ QString displayName() const;
+
protected:
using BuildStepCreator = std::function<BuildStep *(BuildStepList *)>;
template <class BuildStepType>
void registerStep(Utils::Id id)
{
- QTC_CHECK(!m_info.creator);
- m_info.id = id;
- m_info.creator = [id](BuildStepList *bsl) { return new BuildStepType(bsl, id); };
+ QTC_CHECK(!m_creator);
+ m_stepId = id;
+ m_creator = [id](BuildStepList *bsl) { return new BuildStepType(bsl, id); };
}
+ void cloneStepCreator(Utils::Id exitstingStepId, Utils::Id overrideNewStepId = {});
void setSupportedStepList(Utils::Id id);
void setSupportedStepLists(const QList<Utils::Id> &ids);
@@ -189,10 +181,13 @@ protected:
void setSupportedDeviceTypes(const QList<Utils::Id> &ids);
void setRepeatable(bool on) { m_isRepeatable = on; }
void setDisplayName(const QString &displayName);
- void setFlags(BuildStepInfo::Flags flags);
+ void setFlags(BuildStep::Flags flags);
private:
- BuildStepInfo m_info;
+ Utils::Id m_stepId;
+ QString m_displayName;
+ BuildStep::Flags m_flags = {};
+ BuildStepCreator m_creator;
Utils::Id m_supportedProjectType;
QList<Utils::Id> m_supportedDeviceTypes;
diff --git a/src/plugins/projectexplorer/buildstepspage.cpp b/src/plugins/projectexplorer/buildstepspage.cpp
index 241d2a9835..21fb4c2093 100644
--- a/src/plugins/projectexplorer/buildstepspage.cpp
+++ b/src/plugins/projectexplorer/buildstepspage.cpp
@@ -209,14 +209,14 @@ void BuildStepListWidget::updateAddBuildStepMenu()
if (!factory->canHandle(m_buildStepList))
continue;
- const BuildStepInfo &info = factory->stepInfo();
- if (info.flags & BuildStepInfo::Uncreatable)
+ const BuildStep::Flags flags = factory->stepFlags();
+ if (flags & BuildStep::Uncreatable)
continue;
- if ((info.flags & BuildStepInfo::UniqueStep) && m_buildStepList->contains(info.id))
+ if ((flags & BuildStep::UniqueStep) && m_buildStepList->contains(factory->stepId()))
continue;
- QAction *action = menu->addAction(info.displayName);
+ QAction *action = menu->addAction(factory->displayName());
connect(action, &QAction::triggered, this, [factory, this] {
BuildStep *newStep = factory->create(m_buildStepList);
QTC_ASSERT(newStep, return);
diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp
index 0a2c2f3a85..9a120a9863 100644
--- a/src/plugins/projectexplorer/buildsystem.cpp
+++ b/src/plugins/projectexplorer/buildsystem.cpp
@@ -7,9 +7,9 @@
#include "extracompiler.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runconfiguration.h"
#include "runcontrol.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/messagemanager.h>
@@ -64,7 +64,7 @@ BuildSystem::BuildSystem(Target *target)
connect(&d->m_delayedParsingTimer, &QTimer::timeout, this,
[this] {
- if (SessionManager::hasProject(project()))
+ if (ProjectManager::hasProject(project()))
triggerParsing();
else
requestDelayedParse();
@@ -325,7 +325,6 @@ void BuildSystem::setDeploymentData(const DeploymentData &deploymentData)
if (d->m_deploymentData != deploymentData) {
d->m_deploymentData = deploymentData;
emit deploymentDataChanged();
- emit applicationTargetsChanged();
emit target()->deploymentDataChanged();
}
}
@@ -337,10 +336,7 @@ DeploymentData BuildSystem::deploymentData() const
void BuildSystem::setApplicationTargets(const QList<BuildTargetInfo> &appTargets)
{
- if (Utils::toSet(appTargets) != Utils::toSet(d->m_appTargets)) {
- d->m_appTargets = appTargets;
- emit applicationTargetsChanged();
- }
+ d->m_appTargets = appTargets;
}
const QList<BuildTargetInfo> BuildSystem::applicationTargets() const
diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h
index 0d19c9110e..1cd1e41a06 100644
--- a/src/plugins/projectexplorer/buildsystem.h
+++ b/src/plugins/projectexplorer/buildsystem.h
@@ -152,7 +152,6 @@ signals:
void parsingStarted();
void parsingFinished(bool success);
void deploymentDataChanged();
- void applicationTargetsChanged();
void testInformationUpdated();
protected:
diff --git a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
index 2ff0285a61..f7ed131e57 100644
--- a/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/codestylesettingspropertiespage.cpp
@@ -48,12 +48,13 @@ CodeStyleSettingsWidget::CodeStyleSettingsWidget(Project *project)
connect(languageComboBox, &QComboBox::currentIndexChanged,
stackedWidget, &QStackedWidget::setCurrentIndex);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { new QLabel(Tr::tr("Language:")), languageComboBox, st },
- stackedWidget
- }.attachTo(this, WithoutMargins);
+ stackedWidget,
+ noMargin
+ }.attachTo(this);
}
} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/copystep.cpp b/src/plugins/projectexplorer/copystep.cpp
new file mode 100644
index 0000000000..d152671c70
--- /dev/null
+++ b/src/plugins/projectexplorer/copystep.cpp
@@ -0,0 +1,118 @@
+// 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 "copystep.h"
+
+#include "projectexplorerconstants.h"
+#include "projectexplorertr.h"
+
+#include <utils/aspects.h>
+
+using namespace Utils;
+
+namespace ProjectExplorer::Internal {
+
+const char SOURCE_KEY[] = "ProjectExplorer.CopyStep.Source";
+const char TARGET_KEY[] = "ProjectExplorer.CopyStep.Target";
+
+class CopyStepBase : public BuildStep
+{
+public:
+ CopyStepBase(BuildStepList *bsl, Id id)
+ : BuildStep(bsl, id)
+ {
+ m_sourceAspect = addAspect<StringAspect>();
+ m_sourceAspect->setSettingsKey(SOURCE_KEY);
+ m_sourceAspect->setDisplayStyle(StringAspect::PathChooserDisplay);
+ m_sourceAspect->setLabelText(Tr::tr("Source:"));
+
+ m_targetAspect = addAspect<StringAspect>();
+ m_targetAspect->setSettingsKey(TARGET_KEY);
+ m_targetAspect->setDisplayStyle(StringAspect::PathChooserDisplay);
+ m_targetAspect->setLabelText(Tr::tr("Target:"));
+
+ addMacroExpander();
+ }
+
+protected:
+ bool init() final
+ {
+ m_source = m_sourceAspect->filePath();
+ m_target = m_targetAspect->filePath();
+ return m_source.exists();
+ }
+
+ void doRun() final
+ {
+ // FIXME: asyncCopy does not handle directories yet.
+ QTC_ASSERT(m_source.isFile(), emit finished(false));
+ m_source.asyncCopy(m_target, this, [this](const expected_str<void> &cont) {
+ if (!cont) {
+ addOutput(cont.error(), OutputFormat::ErrorMessage);
+ addOutput(Tr::tr("Copying failed"), OutputFormat::ErrorMessage);
+ emit finished(false);
+ } else {
+ addOutput(Tr::tr("Copying finished"), OutputFormat::NormalMessage);
+ emit finished(true);
+ }
+ });
+ }
+
+ StringAspect *m_sourceAspect;
+ StringAspect *m_targetAspect;
+
+private:
+ FilePath m_source;
+ FilePath m_target;
+};
+
+class CopyFileStep final : public CopyStepBase
+{
+public:
+ CopyFileStep(BuildStepList *bsl, Id id)
+ : CopyStepBase(bsl, id)
+ {
+ // Expected kind could be stricter in theory, but since this here is
+ // a last stand fallback, better not impose extra "nice to have"
+ // work on the system.
+ m_sourceAspect->setExpectedKind(PathChooser::Any); // "File"
+ m_targetAspect->setExpectedKind(PathChooser::Any); // "SaveFile"
+
+ setSummaryUpdater([] {
+ return QString("<b>" + Tr::tr("Copy file") + "</b>");
+ });
+ }
+};
+
+class CopyDirectoryStep final : public CopyStepBase
+{
+public:
+ CopyDirectoryStep(BuildStepList *bsl, Id id)
+ : CopyStepBase(bsl, id)
+ {
+ m_sourceAspect->setExpectedKind(PathChooser::Directory);
+ m_targetAspect->setExpectedKind(PathChooser::Directory);
+
+ setSummaryUpdater([] {
+ return QString("<b>" + Tr::tr("Copy directory recursively") + "</b>");
+ });
+ }
+};
+
+// Factories
+
+CopyFileStepFactory::CopyFileStepFactory()
+{
+ registerStep<CopyFileStep>(Constants::COPY_FILE_STEP);
+ //: Default CopyStep display name
+ setDisplayName(Tr::tr("Copy file"));
+}
+
+CopyDirectoryStepFactory::CopyDirectoryStepFactory()
+{
+ registerStep<CopyDirectoryStep>(Constants::COPY_DIRECTORY_STEP);
+ //: Default CopyStep display name
+ setDisplayName(Tr::tr("Copy directory recursively"));
+}
+
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/copystep.h b/src/plugins/projectexplorer/copystep.h
new file mode 100644
index 0000000000..07940f3a89
--- /dev/null
+++ b/src/plugins/projectexplorer/copystep.h
@@ -0,0 +1,22 @@
+// 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 "buildstep.h"
+
+namespace ProjectExplorer::Internal {
+
+class CopyFileStepFactory final : public BuildStepFactory
+{
+public:
+ CopyFileStepFactory();
+};
+
+class CopyDirectoryStepFactory final : public BuildStepFactory
+{
+public:
+ CopyDirectoryStepFactory();
+};
+
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/currentprojectfilter.cpp b/src/plugins/projectexplorer/currentprojectfilter.cpp
index 69b88ddf46..d0f9ed99b0 100644
--- a/src/plugins/projectexplorer/currentprojectfilter.cpp
+++ b/src/plugins/projectexplorer/currentprojectfilter.cpp
@@ -7,42 +7,28 @@
#include "projectexplorertr.h"
#include "projecttree.h"
-#include <utils/algorithm.h>
-
using namespace Core;
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
+using namespace Utils;
CurrentProjectFilter::CurrentProjectFilter()
- : BaseFileFilter()
{
setId("Files in current project");
setDisplayName(Tr::tr("Files in Current Project"));
- setDescription(Tr::tr("Matches all files from the current document's project. Append \"+<number>\" "
- "or \":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ setDescription(Tr::tr("Locates files from the current document's project. Append \"+<number>\" "
+ "or \":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
setDefaultShortcutString("p");
- setDefaultIncludedByDefault(false);
+ setRefreshRecipe(Tasking::Sync([this] { invalidate(); }));
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &CurrentProjectFilter::currentProjectChanged);
-}
-
-void CurrentProjectFilter::markFilesAsOutOfDate()
-{
- setFileIterator(nullptr);
-}
-void CurrentProjectFilter::prepareSearch(const QString &entry)
-{
- Q_UNUSED(entry)
- if (!fileIterator()) {
- Utils::FilePaths paths;
- if (m_project)
- paths = m_project->files(Project::SourceFiles);
- setFileIterator(new BaseFileFilter::ListIterator(paths));
- }
- BaseFileFilter::prepareSearch(entry);
+ m_cache.setGeneratorProvider([this] {
+ const FilePaths paths = m_project ? m_project->files(Project::SourceFiles) : FilePaths();
+ return LocatorFileCache::filePathsGenerator(paths);
+ });
}
void CurrentProjectFilter::currentProjectChanged()
@@ -50,21 +36,12 @@ void CurrentProjectFilter::currentProjectChanged()
Project *project = ProjectTree::currentProject();
if (project == m_project)
return;
- if (m_project)
- disconnect(m_project, &Project::fileListChanged,
- this, &CurrentProjectFilter::markFilesAsOutOfDate);
-
- if (project)
- connect(project, &Project::fileListChanged,
- this, &CurrentProjectFilter::markFilesAsOutOfDate);
+ if (m_project)
+ disconnect(m_project, &Project::fileListChanged, this, &CurrentProjectFilter::invalidate);
m_project = project;
- markFilesAsOutOfDate();
-}
+ if (m_project)
+ connect(m_project, &Project::fileListChanged, this, &CurrentProjectFilter::invalidate);
-void CurrentProjectFilter::refresh(QFutureInterface<void> &future)
-{
- Q_UNUSED(future)
- QMetaObject::invokeMethod(this, &CurrentProjectFilter::markFilesAsOutOfDate,
- Qt::QueuedConnection);
+ invalidate();
}
diff --git a/src/plugins/projectexplorer/currentprojectfilter.h b/src/plugins/projectexplorer/currentprojectfilter.h
index b9d63db293..6c50070790 100644
--- a/src/plugins/projectexplorer/currentprojectfilter.h
+++ b/src/plugins/projectexplorer/currentprojectfilter.h
@@ -3,31 +3,24 @@
#pragma once
-#include <coreplugin/locator/basefilefilter.h>
+#include <coreplugin/locator/ilocatorfilter.h>
-#include <QFutureInterface>
+namespace ProjectExplorer { class Project; }
-namespace ProjectExplorer {
+namespace ProjectExplorer::Internal {
-class Project;
-
-namespace Internal {
-
-class CurrentProjectFilter : public Core::BaseFileFilter
+class CurrentProjectFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
CurrentProjectFilter();
- void refresh(QFutureInterface<void> &future) override;
- void prepareSearch(const QString &entry) override;
private:
+ Core::LocatorMatcherTasks matchers() final { return {m_cache.matcher()}; }
void currentProjectChanged();
- void markFilesAsOutOfDate();
+ void invalidate() { m_cache.invalidate(); }
+ Core::LocatorFileCache m_cache;
Project *m_project = nullptr;
};
-} // namespace Internal
-} // namespace ProjectExplorer
+} // namespace ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp
index 7ecbd99069..3bde2bb62f 100644
--- a/src/plugins/projectexplorer/currentprojectfind.cpp
+++ b/src/plugins/projectexplorer/currentprojectfind.cpp
@@ -5,8 +5,8 @@
#include "project.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projecttree.h"
-#include "session.h"
#include <utils/qtcassert.h>
#include <utils/filesearch.h>
@@ -23,7 +23,7 @@ CurrentProjectFind::CurrentProjectFind()
{
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &CurrentProjectFind::handleProjectChanged);
- connect(SessionManager::instance(), &SessionManager::projectDisplayNameChanged,
+ connect(ProjectManager::instance(), &ProjectManager::projectDisplayNameChanged,
this, [this](ProjectExplorer::Project *p) {
if (p == ProjectTree::currentProject())
emit displayNameChanged();
@@ -61,14 +61,13 @@ FileIterator *CurrentProjectFind::files(const QStringList &nameFilters,
const QStringList &exclusionFilters,
const QVariant &additionalParameters) const
{
- QTC_ASSERT(additionalParameters.isValid(),
- return new FileListIterator(FilePaths(), QList<QTextCodec *>()));
+ QTC_ASSERT(additionalParameters.isValid(), return new FileListIterator);
const FilePath projectFile = FilePath::fromVariant(additionalParameters);
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (project && projectFile == project->projectFilePath())
return filesForProjects(nameFilters, exclusionFilters, {project});
}
- return new FileListIterator(FilePaths(), QList<QTextCodec *>());
+ return new FileListIterator;
}
QString CurrentProjectFind::label() const
@@ -87,7 +86,7 @@ void CurrentProjectFind::handleProjectChanged()
void CurrentProjectFind::recheckEnabled(Core::SearchResult *search)
{
const FilePath projectFile = FilePath::fromVariant(getAdditionalParameters(search));
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (projectFile == project->projectFilePath()) {
search->setSearchAgainEnabled(true);
return;
diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp
index 4ebc880c14..cbfd1adfbc 100644
--- a/src/plugins/projectexplorer/customparserconfigdialog.cpp
+++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp
@@ -101,7 +101,7 @@ CustomParserConfigDialog::CustomParserConfigDialog(QWidget *parent)
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
auto tabWarning = new QWidget;
Column {
diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
index f2b33378b5..7d1174839d 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
@@ -8,7 +8,7 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporarydirectory.h>
#include <QFileInfo>
@@ -64,7 +64,7 @@ static bool
const QMap<QString, QString> &fieldMap,
QString *stdOut /* = 0 */, QString *errorMessage)
{
- Utils::QtcProcess process;
+ Utils::Process process;
const QString binary = script.front();
QStringList arguments;
const int binarySize = script.size();
diff --git a/src/plugins/projectexplorer/dependenciespanel.cpp b/src/plugins/projectexplorer/dependenciespanel.cpp
index 1799126a5b..c08ae7b523 100644
--- a/src/plugins/projectexplorer/dependenciespanel.cpp
+++ b/src/plugins/projectexplorer/dependenciespanel.cpp
@@ -5,6 +5,7 @@
#include "project.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "session.h"
#include <coreplugin/icore.h>
@@ -32,19 +33,18 @@ DependenciesModel::DependenciesModel(Project *project, QObject *parent)
{
resetModel();
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, &DependenciesModel::resetModel);
- connect(sessionManager, &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &DependenciesModel::resetModel);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
this, &DependenciesModel::resetModel);
}
void DependenciesModel::resetModel()
{
beginResetModel();
- m_projects = SessionManager::projects();
+ m_projects = ProjectManager::projects();
m_projects.removeAll(m_project);
Utils::sort(m_projects, [](Project *a, Project *b) {
return a->displayName() < b->displayName();
@@ -77,7 +77,7 @@ QVariant DependenciesModel::data(const QModelIndex &index, int role) const
case Qt::ToolTipRole:
return p->projectFilePath().toUserOutput();
case Qt::CheckStateRole:
- return SessionManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked;
+ return ProjectManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked;
case Qt::DecorationRole:
return Utils::FileIconProvider::icon(p->projectFilePath());
default:
@@ -92,7 +92,7 @@ bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value,
const auto c = static_cast<Qt::CheckState>(value.toInt());
if (c == Qt::Checked) {
- if (SessionManager::addDependency(m_project, p)) {
+ if (ProjectManager::addDependency(m_project, p)) {
emit dataChanged(index, index);
return true;
} else {
@@ -100,8 +100,8 @@ bool DependenciesModel::setData(const QModelIndex &index, const QVariant &value,
Tr::tr("This would create a circular dependency."));
}
} else if (c == Qt::Unchecked) {
- if (SessionManager::hasDependency(m_project, p)) {
- SessionManager::removeDependency(m_project, p);
+ if (ProjectManager::hasDependency(m_project, p)) {
+ ProjectManager::removeDependency(m_project, p);
emit dataChanged(index, index);
return true;
}
@@ -215,9 +215,9 @@ DependenciesWidget::DependenciesWidget(Project *project, QWidget *parent) : Proj
m_cascadeSetActiveCheckBox = new QCheckBox;
m_cascadeSetActiveCheckBox->setText(Tr::tr("Synchronize configuration"));
m_cascadeSetActiveCheckBox->setToolTip(Tr::tr("Synchronize active kit, build, and deploy configuration between projects."));
- m_cascadeSetActiveCheckBox->setChecked(SessionManager::isProjectConfigurationCascading());
+ m_cascadeSetActiveCheckBox->setChecked(ProjectManager::isProjectConfigurationCascading());
connect(m_cascadeSetActiveCheckBox, &QCheckBox::toggled,
- SessionManager::instance(), &SessionManager::setProjectConfigurationCascading);
+ ProjectManager::instance(), &ProjectManager::setProjectConfigurationCascading);
layout->addWidget(m_cascadeSetActiveCheckBox, 1, 0, 2, 1);
}
diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.cpp b/src/plugins/projectexplorer/desktoprunconfiguration.cpp
index 82d0123b11..286f5c4b6e 100644
--- a/src/plugins/projectexplorer/desktoprunconfiguration.cpp
+++ b/src/plugins/projectexplorer/desktoprunconfiguration.cpp
@@ -87,7 +87,8 @@ void DesktopRunConfiguration::updateTargetInformation()
BuildTargetInfo bti = buildTargetInfo();
auto terminalAspect = aspect<TerminalAspect>();
- terminalAspect->setUseTerminalHint(bti.usesTerminal);
+ terminalAspect->setUseTerminalHint(bti.targetFilePath.needsDevice() ? false : bti.usesTerminal);
+ terminalAspect->setEnabled(!bti.targetFilePath.needsDevice());
if (m_kind == Qmake) {
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
index a09c8bac76..099040d6b2 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
@@ -3,11 +3,11 @@
#include "desktopdevice.h"
-#include "desktopprocesssignaloperation.h"
-#include "deviceprocesslist.h"
-#include "localprocesslist.h"
#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
+#include "desktopprocesssignaloperation.h"
+#include "deviceprocesslist.h"
+#include "processlist.h"
#include <coreplugin/fileutils.h>
@@ -15,18 +15,19 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/terminalcommand.h>
+#include <utils/terminalhooks.h>
#include <utils/url.h>
#include <QCoreApplication>
#include <QDateTime>
#ifdef Q_OS_WIN
-#include <windows.h>
-#include <stdlib.h>
#include <cstring>
+#include <stdlib.h>
+#include <windows.h>
#endif
using namespace ProjectExplorer::Constants;
@@ -34,58 +35,11 @@ using namespace Utils;
namespace ProjectExplorer {
-static void startTerminalEmulator(const QString &workingDir, const Environment &env)
-{
-#ifdef Q_OS_WIN
- STARTUPINFO si;
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- PROCESS_INFORMATION pinfo;
- ZeroMemory(&pinfo, sizeof(pinfo));
-
- static const auto quoteWinCommand = [](const QString &program) {
- const QChar doubleQuote = QLatin1Char('"');
-
- // add the program as the first arg ... it works better
- QString programName = program;
- programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
- if (!programName.startsWith(doubleQuote) && !programName.endsWith(doubleQuote)
- && programName.contains(QLatin1Char(' '))) {
- programName.prepend(doubleQuote);
- programName.append(doubleQuote);
- }
- return programName;
- };
- const QString cmdLine = quoteWinCommand(qtcEnvironmentVariable("COMSPEC"));
- // cmdLine is assumed to be detached -
- // https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083
-
- const QString totalEnvironment = env.toStringList().join(QChar(QChar::Null)) + QChar(QChar::Null);
- LPVOID envPtr = (env != Environment::systemEnvironment())
- ? (WCHAR *)(totalEnvironment.utf16()) : nullptr;
-
- const bool success = CreateProcessW(0, (WCHAR *)cmdLine.utf16(),
- 0, 0, FALSE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
- envPtr, workingDir.isEmpty() ? 0 : (WCHAR *)workingDir.utf16(),
- &si, &pinfo);
-
- if (success) {
- CloseHandle(pinfo.hThread);
- CloseHandle(pinfo.hProcess);
- }
-#else
- const TerminalCommand term = TerminalCommand::terminalEmulator();
- QProcess process;
- process.setProgram(term.command.nativePath());
- process.setArguments(ProcessArgs::splitArgs(term.openArgs));
- process.setProcessEnvironment(env.toProcessEnvironment());
- process.setWorkingDirectory(workingDir);
- process.startDetached();
-#endif
-}
+class DesktopDevicePrivate : public QObject
+{};
DesktopDevice::DesktopDevice()
+ : d(new DesktopDevicePrivate())
{
setFileAccess(DesktopDeviceFileAccess::instance());
@@ -98,20 +52,26 @@ DesktopDevice::DesktopDevice()
setMachineType(IDevice::Hardware);
setOsType(HostOsInfo::hostOs());
- const QString portRange =
- QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END);
+ const QString portRange
+ = QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END);
setFreePorts(Utils::PortList::fromString(portRange));
setOpenTerminal([](const Environment &env, const FilePath &path) {
- const QFileInfo fileInfo = path.toFileInfo();
- const QString workingDir = QDir::toNativeSeparators(fileInfo.isDir() ?
- fileInfo.absoluteFilePath() :
- fileInfo.absolutePath());
const Environment realEnv = env.hasChanges() ? env : Environment::systemEnvironment();
- startTerminalEmulator(workingDir, realEnv);
+
+ const FilePath shell = Terminal::defaultShellForDevice(path);
+
+ Process process;
+ process.setTerminalMode(TerminalMode::Detached);
+ process.setEnvironment(realEnv);
+ process.setCommand({shell, {}});
+ process.setWorkingDirectory(path);
+ process.start();
});
}
+DesktopDevice::~DesktopDevice() = default;
+
IDevice::DeviceInfo DesktopDevice::deviceInformation() const
{
return DeviceInfo();
@@ -125,11 +85,6 @@ IDeviceWidget *DesktopDevice::createWidget()
// range can be confusing to the user. Hence, disabling the widget for now.
}
-bool DesktopDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
bool DesktopDevice::canCreateProcessModel() const
{
return true;
@@ -137,7 +92,7 @@ bool DesktopDevice::canCreateProcessModel() const
DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const
{
- return new Internal::LocalProcessList(sharedFromThis(), parent);
+ return new ProcessList(sharedFromThis(), parent);
}
DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
@@ -145,31 +100,6 @@ DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
return DeviceProcessSignalOperation::Ptr(new DesktopProcessSignalOperation());
}
-PortsGatheringMethod DesktopDevice::portsGatheringMethod() const
-{
- return {
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
- return {filePath("netstat"), {"-a", "-n"}};
- if (HostOsInfo::isLinuxHost())
- return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
- return {};
- },
-
- &Port::parseFromNetstatOutput
- };
-}
-
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
{
QUrl url;
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
index ee5ac1ca5e..d9cafbfda9 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
@@ -6,25 +6,27 @@
#include "../projectexplorer_export.h"
#include "idevice.h"
-#include "idevicefactory.h"
#include <QApplication>
+#include <memory>
+
namespace ProjectExplorer {
class ProjectExplorerPlugin;
+class DesktopDevicePrivate;
namespace Internal { class DesktopDeviceFactory; }
class PROJECTEXPLORER_EXPORT DesktopDevice : public IDevice
{
public:
+ ~DesktopDevice() override;
+
IDevice::DeviceInfo deviceInformation() const override;
IDeviceWidget *createWidget() override;
- bool canAutoDetectPorts() const override;
bool canCreateProcessModel() const override;
DeviceProcessList *createProcessListModel(QObject *parent) const override;
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;
bool usableAsBuildDevice() const override;
@@ -40,6 +42,8 @@ protected:
friend class ProjectExplorerPlugin;
friend class Internal::DesktopDeviceFactory;
+
+ std::unique_ptr<DesktopDevicePrivate> d;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp
index acce8bf29b..84e3b27408 100644
--- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp
@@ -4,6 +4,7 @@
#include "devicecheckbuildstep.h"
#include "../kitinformation.h"
+#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
#include "devicemanager.h"
@@ -12,60 +13,61 @@
#include <QMessageBox>
-using namespace ProjectExplorer;
+namespace ProjectExplorer {
-DeviceCheckBuildStep::DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id)
- : BuildStep(bsl, id)
+class DeviceCheckBuildStep : public BuildStep
{
- setWidgetExpandedByDefault(false);
-}
+public:
+ DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id)
+ : BuildStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+ }
-bool DeviceCheckBuildStep::init()
-{
- IDevice::ConstPtr device = DeviceKitAspect::device(kit());
- if (!device) {
- Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit());
- IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId);
- if (!factory || !factory->canCreate()) {
- emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
- return false;
- }
+ bool init() override
+ {
+ IDevice::ConstPtr device = DeviceKitAspect::device(kit());
+ if (!device) {
+ Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit());
+ IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId);
+ if (!factory || !factory->canCreate()) {
+ emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
+ return false;
+ }
- QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"),
- Tr::tr("There is no device set up for this kit. Do you want to add a device?"),
- QMessageBox::Yes|QMessageBox::No);
- msgBox.setDefaultButton(QMessageBox::Yes);
- if (msgBox.exec() == QMessageBox::No) {
- emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
- return false;
- }
+ QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"),
+ Tr::tr("There is no device set up for this kit. Do you want to add a device?"),
+ QMessageBox::Yes|QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::Yes);
+ if (msgBox.exec() == QMessageBox::No) {
+ emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
+ return false;
+ }
- IDevice::Ptr newDevice = factory->create();
- if (newDevice.isNull()) {
- emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
- return false;
- }
+ IDevice::Ptr newDevice = factory->create();
+ if (newDevice.isNull()) {
+ emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage);
+ return false;
+ }
- DeviceManager *dm = DeviceManager::instance();
- dm->addDevice(newDevice);
+ DeviceManager *dm = DeviceManager::instance();
+ dm->addDevice(newDevice);
- DeviceKitAspect::setDevice(kit(), newDevice);
+ DeviceKitAspect::setDevice(kit(), newDevice);
+ }
+
+ return true;
}
- return true;
-}
+ void doRun() override { emit finished(true); }
+};
-void DeviceCheckBuildStep::doRun()
-{
- emit finished(true);
-}
+// Factory
-Utils::Id DeviceCheckBuildStep::stepId()
+DeviceCheckBuildStepFactory::DeviceCheckBuildStepFactory()
{
- return "ProjectExplorer.DeviceCheckBuildStep";
+ registerStep<DeviceCheckBuildStep>(Constants::DEVICE_CHECK_STEP);
+ setDisplayName(Tr::tr("Check for a configured device"));
}
-QString DeviceCheckBuildStep::displayName()
-{
- return Tr::tr("Check for a configured device");
-}
+} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h
index 8b7d3687b0..6b7a8edb11 100644
--- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h
+++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h
@@ -8,18 +8,10 @@
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT DeviceCheckBuildStep : public BuildStep
+class PROJECTEXPLORER_EXPORT DeviceCheckBuildStepFactory : public BuildStepFactory
{
- Q_OBJECT
-
public:
- DeviceCheckBuildStep(BuildStepList *bsl, Utils::Id id);
-
- bool init() override;
- void doRun() override;
-
- static Utils::Id stepId();
- static QString displayName();
+ DeviceCheckBuildStepFactory();
};
-} // namespace ProjectExplorer
+} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
index ed47999432..987f2e0c39 100644
--- a/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicefactoryselectiondialog.cpp
@@ -24,7 +24,7 @@ DeviceFactorySelectionDialog::DeviceFactorySelectionDialog(QWidget *parent) :
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
m_buttonBox->button(QDialogButtonBox::Ok)->setText(Tr::tr("Start Wizard"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Available device types:"),
m_listWidget,
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index 9f8e0594fd..aa017df46b 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -14,9 +14,10 @@
#include <utils/environment.h>
#include <utils/fsengine/fsengine.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
+#include <utils/terminalhooks.h>
#include <QHash>
#include <QMutex>
@@ -442,6 +443,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
device->openTerminal(env, filePath);
};
+ deviceHooks.osType = [](const FilePath &filePath) {
+ auto device = DeviceManager::deviceForPath(filePath);
+ if (!device)
+ return OsTypeLinux;
+ return device->osType();
+ };
+
DeviceProcessHooks processHooks;
processHooks.processImplHook = [](const FilePath &filePath) -> ProcessInterface * {
@@ -456,7 +464,23 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->systemEnvironment();
};
- QtcProcess::setRemoteProcessHooks(processHooks);
+ Process::setRemoteProcessHooks(processHooks);
+
+ Terminal::Hooks::instance().getTerminalCommandsForDevicesHook().set(
+ [this]() -> QList<Terminal::NameAndCommandLine> {
+ QList<Terminal::NameAndCommandLine> result;
+ for (const IDevice::ConstPtr device : d->devices) {
+ if (device->type() == Constants::DESKTOP_DEVICE_TYPE)
+ continue;
+
+ const FilePath shell = Terminal::defaultShellForDevice(device->rootPath());
+
+ if (!shell.isEmpty())
+ result << Terminal::NameAndCommandLine{device->displayName(),
+ CommandLine{shell, {}}};
+ }
+ return result;
+ });
}
DeviceManager::~DeviceManager()
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
index 39af471345..a35387a3c2 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
@@ -17,13 +17,16 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
+#include <utils/optionpushbutton.h>
#include <utils/qtcassert.h>
#include <QComboBox>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
+#include <QMenu>
#include <QPushButton>
#include <QScrollArea>
#include <QTextStream>
@@ -100,21 +103,47 @@ void DeviceSettingsWidget::initGui()
m_deviceStateTextLabel = new QLabel;
m_osSpecificGroupBox = new QGroupBox(Tr::tr("Type Specific"));
m_osSpecificGroupBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
- m_addConfigButton = new QPushButton(Tr::tr("&Add..."));
m_removeConfigButton = new QPushButton(Tr::tr("&Remove"));
m_defaultDeviceButton = new QPushButton(Tr::tr("Set As Default"));
- auto line = new QFrame;
- line->setFrameShape(QFrame::HLine);
- line->setFrameShadow(QFrame::Sunken);
- auto customButtonsContainer = new QWidget;
- m_buttonsLayout = new QVBoxLayout(customButtonsContainer);
+
+ OptionPushButton *addButton = new OptionPushButton(Tr::tr("&Add..."));
+ connect(addButton, &OptionPushButton::clicked, this, &DeviceSettingsWidget::addDevice);
+
+ QMenu *deviceTypeMenu = new QMenu(addButton);
+ QAction *defaultAction = new QAction(Tr::tr("&Start Wizard to Add Device..."));
+ connect(defaultAction, &QAction::triggered, this, &DeviceSettingsWidget::addDevice);
+ deviceTypeMenu->addAction(defaultAction);
+ deviceTypeMenu->addSeparator();
+
+ for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) {
+ if (!factory->canCreate())
+ continue;
+ if (!factory->quickCreationAllowed())
+ continue;
+
+ QAction *action = new QAction(Tr::tr("Add %1").arg(factory->displayName()));
+ deviceTypeMenu->addAction(action);
+
+ connect(action, &QAction::triggered, this, [factory, this] {
+ IDevice::Ptr device = factory->construct();
+ QTC_ASSERT(device, return);
+ m_deviceManager->addDevice(device);
+ m_removeConfigButton->setEnabled(true);
+ m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device));
+ saveSettings();
+ });
+ }
+
+ addButton->setOptionalMenu(deviceTypeMenu);
+
+ m_buttonsLayout = new QVBoxLayout;
m_buttonsLayout->setContentsMargins({});
auto scrollAreaWidget = new QWidget;
auto scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(scrollAreaWidget);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_generalGroupBox,
m_osSpecificGroupBox,
@@ -127,25 +156,27 @@ void DeviceSettingsWidget::initGui()
Tr::tr("Current state:"), Row { m_deviceStateIconLabel, m_deviceStateTextLabel, st, }, br,
}.attachTo(m_generalGroupBox);
+ // clang-format off
Row {
Column {
Form { m_configurationLabel, m_configurationComboBox, br, },
scrollArea,
},
Column {
- m_addConfigButton,
+ addButton,
+ Space(30),
m_removeConfigButton,
m_defaultDeviceButton,
- line,
- customButtonsContainer,
+ m_buttonsLayout,
st,
},
}.attachTo(this);
+ // clang-format on
bool hasDeviceFactories = Utils::anyOf(IDeviceFactory::allDeviceFactories(),
&IDeviceFactory::canCreate);
- m_addConfigButton->setEnabled(hasDeviceFactories);
+ addButton->setEnabled(hasDeviceFactories);
int lastIndex = ICore::settings()
->value(QLatin1String(LastDeviceIndexKey), 0).toInt();
@@ -160,10 +191,10 @@ void DeviceSettingsWidget::initGui()
this, &DeviceSettingsWidget::setDefaultDevice);
connect(m_removeConfigButton, &QAbstractButton::clicked,
this, &DeviceSettingsWidget::removeDevice);
- connect(m_nameLineEdit, &QLineEdit::editingFinished,
- this, &DeviceSettingsWidget::deviceNameEditingFinished);
- connect(m_addConfigButton, &QAbstractButton::clicked,
- this, &DeviceSettingsWidget::addDevice);
+ connect(m_nameLineEdit,
+ &QLineEdit::editingFinished,
+ this,
+ &DeviceSettingsWidget::deviceNameEditingFinished);
}
void DeviceSettingsWidget::addDevice()
@@ -182,6 +213,8 @@ void DeviceSettingsWidget::addDevice()
if (device.isNull())
return;
+ Utils::asyncRun([device] { device->checkOsType(); });
+
m_deviceManager->addDevice(device);
m_removeConfigButton->setEnabled(true);
m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device));
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
index 5cd5cec85e..207b60cf74 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h
@@ -76,7 +76,6 @@ private:
QLabel *m_deviceStateIconLabel;
QLabel *m_deviceStateTextLabel;
QGroupBox *m_osSpecificGroupBox;
- QPushButton *m_addConfigButton;
QPushButton *m_removeConfigButton;
QPushButton *m_defaultDeviceButton;
QVBoxLayout *m_buttonsLayout;
diff --git a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
index 91a83531b4..0277fc827a 100644
--- a/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicetestdialog.cpp
@@ -43,7 +43,7 @@ DeviceTestDialog::DeviceTestDialog(const IDevice::Ptr &deviceConfiguration,
d->textEdit->setReadOnly(true);
d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
d->textEdit,
d->buttonBox,
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
index 8081c9745d..fbe300bf26 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
@@ -9,8 +9,8 @@
#include <utils/port.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/url.h>
@@ -22,7 +22,7 @@ namespace Internal {
class DeviceUsedPortsGathererPrivate
{
public:
- std::unique_ptr<QtcProcess> process;
+ std::unique_ptr<Process> process;
QList<Port> usedPorts;
IDevice::ConstPtr device;
PortsGatheringMethod portsGatheringMethod;
@@ -54,10 +54,10 @@ void DeviceUsedPortsGatherer::start()
const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol;
- d->process.reset(new QtcProcess);
+ d->process.reset(new Process);
d->process->setCommand(d->portsGatheringMethod.commandLine(protocol));
- connect(d->process.get(), &QtcProcess::done, this, &DeviceUsedPortsGatherer::handleProcessDone);
+ connect(d->process.get(), &Process::done, this, &DeviceUsedPortsGatherer::handleProcessDone);
d->process->start();
}
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
index 3519c7d22f..7be6f9c485 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
@@ -7,8 +7,9 @@
#include <projectexplorer/runcontrol.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/portlist.h>
-#include <utils/tasktree.h>
namespace ProjectExplorer {
@@ -44,7 +45,7 @@ private:
};
class PROJECTEXPLORER_EXPORT DeviceUsedPortsGathererAdapter
- : public Utils::Tasking::TaskAdapter<DeviceUsedPortsGatherer>
+ : public Tasking::TaskAdapter<DeviceUsedPortsGatherer>
{
public:
DeviceUsedPortsGathererAdapter();
@@ -85,4 +86,4 @@ private:
} // namespace ProjectExplorer
-QTC_DECLARE_CUSTOM_TASK(PortGatherer, ProjectExplorer::DeviceUsedPortsGathererAdapter);
+TASKING_DECLARE_TASK(PortGatherer, ProjectExplorer::DeviceUsedPortsGathererAdapter);
diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp
index bd386db0d1..0581579767 100644
--- a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp
+++ b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp
@@ -16,43 +16,18 @@ using namespace Utils;
namespace ProjectExplorer {
-FileTransferDirection FileToTransfer::direction() const
-{
- if (m_source.needsDevice() == m_target.needsDevice())
- return FileTransferDirection::Invalid;
- return m_source.needsDevice() ? FileTransferDirection::Download : FileTransferDirection::Upload;
-}
-
QString FileTransferSetupData::defaultRsyncFlags()
{
return "-av";
}
-static FileTransferDirection transferDirection(const FilesToTransfer &files)
-{
- if (files.isEmpty())
- return FileTransferDirection::Invalid;
-
- const FileTransferDirection direction = files.first().direction();
- for (const FileToTransfer &file : files) {
- if (file.direction() != direction)
- return FileTransferDirection::Invalid;
- }
- return direction;
-}
-
-static const FilePath &remoteFile(FileTransferDirection direction, const FileToTransfer &file)
-{
- return direction == FileTransferDirection::Upload ? file.m_target : file.m_source;
-}
-
-static IDeviceConstPtr matchedDevice(FileTransferDirection direction, const FilesToTransfer &files)
+static IDeviceConstPtr matchedDevice(const FilesToTransfer &files)
{
if (files.isEmpty())
return {};
- const FilePath &filePath = remoteFile(direction, files.first());
+ const FilePath filePath = files.first().m_target;
for (const FileToTransfer &file : files) {
- if (!filePath.isSameDevice(remoteFile(direction, file)))
+ if (!filePath.isSameDevice(file.m_target))
return {};
}
return DeviceManager::deviceForPath(filePath);
@@ -102,15 +77,11 @@ void FileTransferPrivate::start()
if (m_setup.m_files.isEmpty())
return startFailed(Tr::tr("No files to transfer."));
- const FileTransferDirection direction = transferDirection(m_setup.m_files);
-
- IDeviceConstPtr device;
- if (direction != FileTransferDirection::Invalid)
- device = matchedDevice(direction, m_setup.m_files);
+ IDeviceConstPtr device = matchedDevice(m_setup.m_files);
if (!device) {
// Fall back to generic copy.
- const FilePath &filePath = m_setup.m_files.first().m_target;
+ const FilePath filePath = m_setup.m_files.first().m_target;
device = DeviceManager::deviceForPath(filePath);
m_setup.m_method = FileTransferMethod::GenericCopy;
}
@@ -222,7 +193,7 @@ QString FileTransfer::transferMethodName(FileTransferMethod method)
return {};
}
-FileTransferAdapter::FileTransferAdapter()
+FileTransferTaskAdapter::FileTransferTaskAdapter()
{
connect(task(), &FileTransfer::done, this, [this](const ProcessResultData &result) {
emit done(result.m_exitStatus == QProcess::NormalExit
diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.h b/src/plugins/projectexplorer/devicesupport/filetransfer.h
index dcc04f05c0..f73a374168 100644
--- a/src/plugins/projectexplorer/devicesupport/filetransfer.h
+++ b/src/plugins/projectexplorer/devicesupport/filetransfer.h
@@ -7,7 +7,7 @@
#include "filetransferinterface.h"
#include "idevicefwd.h"
-#include <utils/tasktree.h>
+#include <solutions/tasking/tasktree.h>
namespace Utils { class ProcessResultData; }
@@ -46,14 +46,14 @@ private:
FileTransferPrivate *d;
};
-class PROJECTEXPLORER_EXPORT FileTransferAdapter : public Utils::Tasking::TaskAdapter<FileTransfer>
+class PROJECTEXPLORER_EXPORT FileTransferTaskAdapter : public Tasking::TaskAdapter<FileTransfer>
{
public:
- FileTransferAdapter();
+ FileTransferTaskAdapter();
void start() override { task()->start(); }
};
-class PROJECTEXPLORER_EXPORT FileTransferTestAdapter : public FileTransferAdapter
+class PROJECTEXPLORER_EXPORT FileTransferTestTaskAdapter : public FileTransferTaskAdapter
{
public:
void start() final { task()->test(); }
@@ -61,5 +61,5 @@ public:
} // namespace ProjectExplorer
-QTC_DECLARE_CUSTOM_TASK(Transfer, ProjectExplorer::FileTransferAdapter);
-QTC_DECLARE_CUSTOM_TASK(TransferTest, ProjectExplorer::FileTransferTestAdapter);
+TASKING_DECLARE_TASK(FileTransferTask, ProjectExplorer::FileTransferTaskAdapter);
+TASKING_DECLARE_TASK(FileTransferTestTask, ProjectExplorer::FileTransferTestTaskAdapter);
diff --git a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h
index 2907f9c257..ce4e662db4 100644
--- a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h
+++ b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h
@@ -11,12 +11,6 @@ namespace Utils { class ProcessResultData; }
namespace ProjectExplorer {
-enum class FileTransferDirection {
- Invalid,
- Upload,
- Download
-};
-
enum class FileTransferMethod {
Sftp,
Rsync,
@@ -29,8 +23,6 @@ class PROJECTEXPLORER_EXPORT FileToTransfer
public:
Utils::FilePath m_source;
Utils::FilePath m_target;
-
- FileTransferDirection direction() const;
};
using FilesToTransfer = QList<FileToTransfer>;
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index b884237b3b..775f60e396 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -15,6 +15,7 @@
#include <coreplugin/icore.h>
+#include <utils/commandline.h>
#include <utils/devicefileaccess.h>
#include <utils/displayname.h>
#include <utils/icon.h>
@@ -92,6 +93,7 @@ static Id newId()
const char DisplayNameKey[] = "Name";
const char TypeKey[] = "OsType";
+const char ClientOsTypeKey[] = "ClientOsType";
const char IdKey[] = "InternalId";
const char OriginKey[] = "Origin";
const char MachineTypeKey[] = "Type";
@@ -369,6 +371,27 @@ const QList<IDevice::DeviceAction> IDevice::deviceActions() const
return d->deviceActions;
}
+PortsGatheringMethod IDevice::portsGatheringMethod() const
+{
+ return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
+ // We might encounter the situation that protocol is given IPv6
+ // but the consumer of the free port information decides to open
+ // an IPv4(only) port. As a result the next IPv6 scan will
+ // report the port again as open (in IPv6 namespace), while the
+ // same port in IPv4 namespace might still be blocked, and
+ // re-use of this port fails.
+ // GDBserver behaves exactly like this.
+
+ Q_UNUSED(protocol)
+
+ if (filePath("/proc/net").isReadableDir())
+ return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
+
+ return {filePath("netstat"), {"-a", "-n"}};
+ },
+ &Port::parseFromCommandOutput};
+};
+
DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const
{
Q_UNUSED(parent)
@@ -425,6 +448,8 @@ void IDevice::fromMap(const QVariantMap &map)
d->type = typeFromMap(map);
d->displayName.fromMap(map, DisplayNameKey);
d->id = Id::fromSetting(map.value(QLatin1String(IdKey)));
+ d->osType = osTypeFromString(
+ map.value(QLatin1String(ClientOsTypeKey), osTypeToString(OsTypeLinux)).toString());
if (!d->id.isValid())
d->id = newId();
d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt());
@@ -471,6 +496,7 @@ QVariantMap IDevice::toMap() const
QVariantMap map;
d->displayName.toMap(map, DisplayNameKey);
map.insert(QLatin1String(TypeKey), d->type.toString());
+ map.insert(QLatin1String(ClientOsTypeKey), osTypeToString(d->osType));
map.insert(QLatin1String(IdKey), d->id.toSetting());
map.insert(QLatin1String(OriginKey), d->origin);
@@ -503,9 +529,6 @@ IDevice::Ptr IDevice::clone() const
device->d->deviceState = d->deviceState;
device->d->deviceActions = d->deviceActions;
device->d->deviceIcons = d->deviceIcons;
- // Os type is only set in the constructor, always to the same value.
- // But make sure we notice if that changes in the future (which it shouldn't).
- QTC_CHECK(device->d->osType == d->osType);
device->d->osType = d->osType;
device->fromMap(toMap());
return device;
@@ -578,16 +601,6 @@ void IDevice::setDebugServerPath(const FilePath &path)
d->debugServerPath = path;
}
-FilePath IDevice::debugDumperPath() const
-{
- return d->debugDumperPath;
-}
-
-void IDevice::setDebugDumperPath(const FilePath &path)
-{
- d->debugDumperPath = path;
-}
-
FilePath IDevice::qmlRunCommand() const
{
return d->qmlRunCommand;
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 6323b8589b..f0a902227a 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -6,11 +6,12 @@
#include "../projectexplorer_export.h"
#include "idevicefwd.h"
+#include <solutions/tasking/tasktree.h>
+
#include <utils/id.h>
#include <utils/expected.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
-#include <utils/tasktree.h>
#include <QAbstractSocket>
#include <QCoreApplication>
@@ -35,7 +36,7 @@ class Icon;
class PortList;
class Port;
class ProcessInterface;
-class QtcProcess;
+class Process;
} // Utils
namespace ProjectExplorer {
@@ -139,10 +140,7 @@ public:
void addDeviceAction(const DeviceAction &deviceAction);
const QList<DeviceAction> deviceActions() const;
- // Devices that can auto detect ports need not return a ports gathering method. Such devices can
- // obtain a free port on demand. eg: Desktop device.
- virtual bool canAutoDetectPorts() const { return false; }
- virtual PortsGatheringMethod portsGatheringMethod() const { return {}; }
+ virtual PortsGatheringMethod portsGatheringMethod() const;
virtual bool canCreateProcessModel() const { return false; }
virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const;
virtual bool hasDeviceTester() const { return false; }
@@ -179,9 +177,6 @@ public:
Utils::FilePath debugServerPath() const;
void setDebugServerPath(const Utils::FilePath &path);
- Utils::FilePath debugDumperPath() const;
- void setDebugDumperPath(const Utils::FilePath &path);
-
Utils::FilePath qmlRunCommand() const;
void setQmlRunCommand(const Utils::FilePath &path);
@@ -221,6 +216,8 @@ public:
virtual bool prepareForBuild(const Target *target);
virtual std::optional<Utils::FilePath> clangdExecutable() const;
+ virtual void checkOsType() {}
+
protected:
IDevice();
@@ -280,7 +277,7 @@ private:
QString m_errorString;
};
-class PROJECTEXPLORER_EXPORT KillerAdapter : public Utils::Tasking::TaskAdapter<DeviceProcessKiller>
+class PROJECTEXPLORER_EXPORT KillerAdapter : public Tasking::TaskAdapter<DeviceProcessKiller>
{
public:
KillerAdapter();
@@ -289,4 +286,4 @@ public:
} // namespace ProjectExplorer
-QTC_DECLARE_CUSTOM_TASK(Killer, ProjectExplorer::KillerAdapter);
+TASKING_DECLARE_TASK(Killer, ProjectExplorer::KillerAdapter);
diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp
index c33bc125bb..b517844570 100644
--- a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp
@@ -63,12 +63,25 @@ bool IDeviceFactory::canCreate() const
IDevice::Ptr IDeviceFactory::create() const
{
- return m_creator ? m_creator() : IDevice::Ptr();
+ if (!m_creator)
+ return {};
+
+ IDevice::Ptr device = m_creator();
+ if (!device) // e.g. Cancel used on the dialog to create a device
+ return {};
+ device->setDefaultDisplayName(displayName());
+ return device;
}
IDevice::Ptr IDeviceFactory::construct() const
{
- return m_constructor ? m_constructor() : IDevice::Ptr();
+ if (!m_constructor)
+ return {};
+
+ IDevice::Ptr device = m_constructor();
+ QTC_ASSERT(device, return {});
+ device->setDefaultDisplayName(displayName());
+ return device;
}
static QList<IDeviceFactory *> g_deviceFactories;
@@ -105,6 +118,16 @@ void IDeviceFactory::setCreator(const std::function<IDevice::Ptr ()> &creator)
m_creator = creator;
}
+void IDeviceFactory::setQuickCreationAllowed(bool on)
+{
+ m_quickCreationAllowed = on;
+}
+
+bool IDeviceFactory::quickCreationAllowed() const
+{
+ return m_quickCreationAllowed;
+}
+
void IDeviceFactory::setConstructionFunction(const std::function<IDevice::Ptr ()> &constructor)
{
m_constructor = constructor;
diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.h b/src/plugins/projectexplorer/devicesupport/idevicefactory.h
index 87a54244ad..665059f5b2 100644
--- a/src/plugins/projectexplorer/devicesupport/idevicefactory.h
+++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.h
@@ -26,6 +26,7 @@ public:
bool canCreate() const;
IDevicePtr construct() const;
IDevicePtr create() const;
+ bool quickCreationAllowed() const;
virtual bool canRestore(const QVariantMap &) const { return true; }
@@ -41,6 +42,7 @@ protected:
void setCombinedIcon(const Utils::FilePath &smallIcon, const Utils::FilePath &largeIcon);
void setConstructionFunction(const std::function<IDevicePtr ()> &constructor);
void setCreator(const std::function<IDevicePtr()> &creator);
+ void setQuickCreationAllowed(bool on);
private:
std::function<IDevicePtr()> m_creator;
@@ -48,6 +50,7 @@ private:
QString m_displayName;
QIcon m_icon;
std::function<IDevicePtr()> m_constructor;
+ bool m_quickCreationAllowed = false;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp
deleted file mode 100644
index c6fd49012f..0000000000
--- a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "localprocesslist.h"
-
-#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/processinfo.h>
-
-#include <QTimer>
-
-#if defined(Q_OS_UNIX)
-#include <unistd.h>
-#elif defined(Q_OS_WIN)
-#include <windows.h>
-#endif
-
-using namespace Utils;
-
-namespace ProjectExplorer {
-namespace Internal {
-
-LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent)
- : DeviceProcessList(device, parent)
-{
-#if defined(Q_OS_UNIX)
- setOwnPid(getpid());
-#elif defined(Q_OS_WIN)
- setOwnPid(GetCurrentProcessId());
-#endif
-}
-
-void LocalProcessList::doKillProcess(const ProcessInfo &processInfo)
-{
- DeviceProcessSignalOperation::Ptr signalOperation = device()->signalOperation();
- connect(signalOperation.data(), &DeviceProcessSignalOperation::finished,
- this, &LocalProcessList::reportDelayedKillStatus);
- signalOperation->killProcess(processInfo.processId);
-}
-
-void LocalProcessList::handleUpdate()
-{
- reportProcessListUpdated(ProcessInfo::processInfoList());
-}
-
-void LocalProcessList::doUpdate()
-{
- QTimer::singleShot(0, this, &LocalProcessList::handleUpdate);
-}
-
-void LocalProcessList::reportDelayedKillStatus(const QString &errorMessage)
-{
- if (errorMessage.isEmpty())
- reportProcessKilled();
- else
- reportError(errorMessage);
-}
-
-} // namespace Internal
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/processlist.cpp b/src/plugins/projectexplorer/devicesupport/processlist.cpp
new file mode 100644
index 0000000000..11e0932832
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/processlist.cpp
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "processlist.h"
+
+#include <projectexplorer/devicesupport/idevice.h>
+#include <utils/processinfo.h>
+
+#include <QTimer>
+
+#if defined(Q_OS_UNIX)
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <windows.h>
+#endif
+
+using namespace Utils;
+
+namespace ProjectExplorer {
+
+ProcessList::ProcessList(const IDevice::ConstPtr &device, QObject *parent)
+ : DeviceProcessList(device, parent)
+{
+#if defined(Q_OS_UNIX)
+ setOwnPid(getpid());
+#elif defined(Q_OS_WIN)
+ setOwnPid(GetCurrentProcessId());
+#endif
+}
+
+void ProcessList::doKillProcess(const ProcessInfo &processInfo)
+{
+ m_signalOperation = device()->signalOperation();
+ connect(m_signalOperation.data(),
+ &DeviceProcessSignalOperation::finished,
+ this,
+ &ProcessList::reportDelayedKillStatus);
+ m_signalOperation->killProcess(processInfo.processId);
+}
+
+void ProcessList::handleUpdate()
+{
+ reportProcessListUpdated(ProcessInfo::processInfoList(DeviceProcessList::device()->rootPath()));
+}
+
+void ProcessList::doUpdate()
+{
+ QTimer::singleShot(0, this, &ProcessList::handleUpdate);
+}
+
+void ProcessList::reportDelayedKillStatus(const QString &errorMessage)
+{
+ if (errorMessage.isEmpty())
+ reportProcessKilled();
+ else
+ reportError(errorMessage);
+
+ m_signalOperation.reset();
+}
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.h b/src/plugins/projectexplorer/devicesupport/processlist.h
index e3445f47b2..caebaf22f9 100644
--- a/src/plugins/projectexplorer/devicesupport/localprocesslist.h
+++ b/src/plugins/projectexplorer/devicesupport/processlist.h
@@ -4,16 +4,16 @@
#pragma once
#include "deviceprocesslist.h"
+#include "idevice.h"
namespace ProjectExplorer {
-namespace Internal {
-class LocalProcessList : public DeviceProcessList
+class PROJECTEXPLORER_EXPORT ProcessList : public DeviceProcessList
{
Q_OBJECT
public:
- explicit LocalProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
+ explicit ProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
private:
void doUpdate() override;
@@ -22,7 +22,9 @@ private:
private:
void handleUpdate();
void reportDelayedKillStatus(const QString &errorMessage);
+
+private:
+ DeviceProcessSignalOperation::Ptr m_signalOperation;
};
-} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp
deleted file mode 100644
index d64cde6e3e..0000000000
--- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "sshdeviceprocesslist.h"
-
-#include "idevice.h"
-#include "../projectexplorertr.h"
-
-#include <utils/processinfo.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/stringutils.h>
-
-using namespace Utils;
-
-namespace ProjectExplorer {
-
-class SshDeviceProcessListPrivate
-{
-public:
- QtcProcess m_process;
- DeviceProcessSignalOperation::Ptr m_signalOperation;
-};
-
-SshDeviceProcessList::SshDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) :
- DeviceProcessList(device, parent), d(std::make_unique<SshDeviceProcessListPrivate>())
-{
- connect(&d->m_process, &QtcProcess::done, this, &SshDeviceProcessList::handleProcessDone);
-}
-
-SshDeviceProcessList::~SshDeviceProcessList() = default;
-
-void SshDeviceProcessList::doUpdate()
-{
- d->m_process.close();
- d->m_process.setCommand({device()->filePath("/bin/sh"), {"-c", listProcessesCommandLine()}});
- d->m_process.start();
-}
-
-void SshDeviceProcessList::doKillProcess(const ProcessInfo &process)
-{
- d->m_signalOperation = device()->signalOperation();
- QTC_ASSERT(d->m_signalOperation, return);
- connect(d->m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
- this, &SshDeviceProcessList::handleKillProcessFinished);
- d->m_signalOperation->killProcess(process.processId);
-}
-
-void SshDeviceProcessList::handleProcessDone()
-{
- if (d->m_process.result() == ProcessResult::FinishedWithSuccess) {
- reportProcessListUpdated(buildProcessList(d->m_process.cleanedStdOut()));
- } else {
- const QString errorString = d->m_process.exitStatus() == QProcess::NormalExit
- ? Tr::tr("Process listing command failed with exit code %1.").arg(d->m_process.exitCode())
- : d->m_process.errorString();
- const QString stdErr = d->m_process.cleanedStdErr();
- const QString outputString
- = stdErr.isEmpty() ? stdErr : Tr::tr("Remote stderr was: %1").arg(stdErr);
- reportError(Utils::joinStrings({errorString, outputString}, '\n'));
- }
- setFinished();
-}
-
-void SshDeviceProcessList::handleKillProcessFinished(const QString &errorString)
-{
- if (errorString.isEmpty())
- reportProcessKilled();
- else
- reportError(Tr::tr("Error: Kill process failed: %1").arg(errorString));
- setFinished();
-}
-
-void SshDeviceProcessList::setFinished()
-{
- d->m_process.close();
- if (d->m_signalOperation) {
- d->m_signalOperation->disconnect(this);
- d->m_signalOperation.clear();
- }
-}
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h
deleted file mode 100644
index fd560f5375..0000000000
--- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "deviceprocesslist.h"
-
-#include <memory>
-
-namespace ProjectExplorer {
-
-class SshDeviceProcessListPrivate;
-
-class PROJECTEXPLORER_EXPORT SshDeviceProcessList : public DeviceProcessList
-{
- Q_OBJECT
-public:
- explicit SshDeviceProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
- ~SshDeviceProcessList() override;
-
-private:
- void handleProcessDone();
- void handleKillProcessFinished(const QString &errorString);
-
- virtual QString listProcessesCommandLine() const = 0;
- virtual QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const = 0;
-
- void doUpdate() override;
- void doKillProcess(const Utils::ProcessInfo &process) override;
-
- void setFinished();
-
- const std::unique_ptr<SshDeviceProcessListPrivate> d;
-};
-
-} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
index 3cc6364a67..744e350eae 100644
--- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
+++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp
@@ -8,8 +8,8 @@
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
@@ -19,9 +19,17 @@ using namespace Utils;
namespace ProjectExplorer {
-SshParameters::SshParameters()
+SshParameters::SshParameters() = default;
+
+QString SshParameters::userAtHost() const
{
- url.setPort(0);
+ QString res;
+ if (!m_userName.isEmpty())
+ res = m_userName + '@';
+ res += m_host;
+ if (m_port != 22)
+ res += QString(":%1").arg(m_port);
+ return res;
}
QStringList SshParameters::connectionOptions(const FilePath &binary) const
@@ -61,7 +69,7 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const
return args;
}
-bool SshParameters::setupSshEnvironment(QtcProcess *process)
+bool SshParameters::setupSshEnvironment(Process *process)
{
Environment env = process->controlEnvironment();
if (!env.hasChanges())
@@ -81,10 +89,11 @@ bool SshParameters::setupSshEnvironment(QtcProcess *process)
return hasDisplay;
}
-
-static inline bool equals(const SshParameters &p1, const SshParameters &p2)
+bool operator==(const SshParameters &p1, const SshParameters &p2)
{
- return p1.url == p2.url
+ return p1.m_host == p2.m_host
+ && p1.m_port == p2.m_port
+ && p1.m_userName == p2.m_userName
&& p1.authenticationType == p2.authenticationType
&& p1.privateKeyFile == p2.privateKeyFile
&& p1.hostKeyCheckingMode == p2.hostKeyCheckingMode
@@ -92,16 +101,6 @@ static inline bool equals(const SshParameters &p1, const SshParameters &p2)
&& p1.timeout == p2.timeout;
}
-bool operator==(const SshParameters &p1, const SshParameters &p2)
-{
- return equals(p1, p2);
-}
-
-bool operator!=(const SshParameters &p1, const SshParameters &p2)
-{
- return !equals(p1, p2);
-}
-
#ifdef WITH_TESTS
namespace SshTest {
const QString getHostFromEnvironment()
diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.h b/src/plugins/projectexplorer/devicesupport/sshparameters.h
index 00b63e3aac..3ee483d5f3 100644
--- a/src/plugins/projectexplorer/devicesupport/sshparameters.h
+++ b/src/plugins/projectexplorer/devicesupport/sshparameters.h
@@ -7,9 +7,7 @@
#include <utils/filepath.h>
-#include <QUrl>
-
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
namespace ProjectExplorer {
@@ -29,28 +27,34 @@ public:
SshParameters();
- QString host() const { return url.host(); }
- quint16 port() const { return url.port(); }
- QString userName() const { return url.userName(); }
- QString userAtHost() const { return userName().isEmpty() ? host() : userName() + '@' + host(); }
- void setHost(const QString &host) { url.setHost(host); }
- void setPort(int port) { url.setPort(port); }
- void setUserName(const QString &name) { url.setUserName(name); }
+ QString host() const { return m_host; }
+ quint16 port() const { return m_port; }
+ QString userName() const { return m_userName; }
+
+ QString userAtHost() const;
+
+ void setHost(const QString &host) { m_host = host; }
+ void setPort(int port) { m_port = port; }
+ void setUserName(const QString &name) { m_userName = name; }
QStringList connectionOptions(const Utils::FilePath &binary) const;
- QUrl url;
Utils::FilePath privateKeyFile;
QString x11DisplayName;
int timeout = 0; // In seconds.
AuthenticationType authenticationType = AuthenticationTypeAll;
SshHostKeyCheckingMode hostKeyCheckingMode = SshHostKeyCheckingAllowNoMatch;
- static bool setupSshEnvironment(Utils::QtcProcess *process);
-};
+ static bool setupSshEnvironment(Utils::Process *process);
+
+ friend PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2);
+ friend bool operator!=(const SshParameters &p1, const SshParameters &p2) { return !(p1 == p2); }
-PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2);
-PROJECTEXPLORER_EXPORT bool operator!=(const SshParameters &p1, const SshParameters &p2);
+private:
+ QString m_host;
+ quint16 m_port = 22;
+ QString m_userName;
+};
#ifdef WITH_TESTS
namespace SshTest {
diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp
index fc3933d734..ff99bc25e8 100644
--- a/src/plugins/projectexplorer/editorconfiguration.cpp
+++ b/src/plugins/projectexplorer/editorconfiguration.cpp
@@ -5,7 +5,7 @@
#include "project.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <utils/algorithm.h>
@@ -88,7 +88,7 @@ EditorConfiguration::EditorConfiguration() : d(std::make_unique<EditorConfigurat
// if setCurrentDelegate is 0 values are read from *this prefs
d->m_defaultCodeStyle->setCurrentDelegate(TextEditorSettings::codeStyle());
- connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
+ connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject,
this, &EditorConfiguration::slotAboutToRemoveProject);
}
@@ -263,7 +263,7 @@ void EditorConfiguration::setUseGlobalSettings(bool use)
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForOpenedDocuments();
for (Core::IEditor *editor : editors) {
if (auto widget = TextEditorWidget::fromEditor(editor)) {
- Project *project = SessionManager::projectForFile(editor->document()->filePath());
+ Project *project = ProjectManager::projectForFile(editor->document()->filePath());
if (project && project->editorConfiguration() == this)
switchSettings(widget);
}
@@ -399,7 +399,7 @@ TabSettings actualTabSettings(const Utils::FilePath &file,
{
if (baseTextdocument)
return baseTextdocument->tabSettings();
- if (Project *project = SessionManager::projectForFile(file))
+ if (Project *project = ProjectManager::projectForFile(file))
return project->editorConfiguration()->codeStyle()->tabSettings();
return TextEditorSettings::codeStyle()->tabSettings();
}
diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp
index 52450b2bc8..e0c2b93bb2 100644
--- a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp
@@ -48,7 +48,7 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : m_project(project
m_behaviorSettings = new TextEditor::BehaviorSettingsWidget(this);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_showWrapColumn,
@@ -63,7 +63,8 @@ EditorSettingsWidget::EditorSettingsWidget(Project *project) : m_project(project
m_displaySettings,
m_behaviorSettings,
st,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
const EditorConfiguration *config = m_project->editorConfiguration();
settingsToUi(config);
diff --git a/src/plugins/projectexplorer/environmentaspect.cpp b/src/plugins/projectexplorer/environmentaspect.cpp
index 66a2b3bc99..dc3abedbe7 100644
--- a/src/plugins/projectexplorer/environmentaspect.cpp
+++ b/src/plugins/projectexplorer/environmentaspect.cpp
@@ -12,6 +12,7 @@
using namespace Utils;
namespace ProjectExplorer {
+const char PRINT_ON_RUN_KEY[] = "PE.EnvironmentAspect.PrintOnRun";
// --------------------------------------------------------------------
// EnvironmentAspect:
@@ -105,12 +106,14 @@ void EnvironmentAspect::fromMap(const QVariantMap &map)
{
m_base = map.value(QLatin1String(BASE_KEY), -1).toInt();
m_userChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(CHANGES_KEY)).toStringList());
+ m_printOnRun = map.value(PRINT_ON_RUN_KEY).toBool();
}
void EnvironmentAspect::toMap(QVariantMap &data) const
{
data.insert(QLatin1String(BASE_KEY), m_base);
data.insert(QLatin1String(CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_userChanges));
+ data.insert(PRINT_ON_RUN_KEY, m_printOnRun);
}
QString EnvironmentAspect::currentDisplayName() const
diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h
index 5b1d0d1c41..9ba2f8a983 100644
--- a/src/plugins/projectexplorer/environmentaspect.h
+++ b/src/plugins/projectexplorer/environmentaspect.h
@@ -48,6 +48,10 @@ public:
bool isLocal() const { return m_isLocal; }
+ bool isPrintOnRunAllowed() const { return m_allowPrintOnRun; }
+ bool isPrintOnRunEnabled() const { return m_printOnRun; }
+ void setPrintOnRun(bool enabled) { m_printOnRun = enabled; }
+
struct Data : BaseAspect::Data
{
Utils::Environment environment;
@@ -66,6 +70,7 @@ protected:
void toMap(QVariantMap &map) const override;
void setIsLocal(bool local) { m_isLocal = local; }
+ void setAllowPrintOnRun(bool allow) { m_allowPrintOnRun = allow; }
static constexpr char BASE_KEY[] = "PE.EnvironmentAspect.Base";
static constexpr char CHANGES_KEY[] = "PE.EnvironmentAspect.Changes";
@@ -84,6 +89,8 @@ private:
QList<BaseEnvironment> m_baseEnvironments;
int m_base = -1;
bool m_isLocal = false;
+ bool m_allowPrintOnRun = true;
+ bool m_printOnRun = false;
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp
index 18babfcf61..34e48af42c 100644
--- a/src/plugins/projectexplorer/environmentaspectwidget.cpp
+++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp
@@ -9,6 +9,7 @@
#include <utils/environment.h>
#include <utils/qtcassert.h>
+#include <QCheckBox>
#include <QComboBox>
#include <QHBoxLayout>
#include <QLabel>
@@ -63,6 +64,14 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect)
m_environmentWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
topLayout->addWidget(m_environmentWidget);
+ if (m_aspect->isPrintOnRunAllowed()) {
+ const auto printOnRunCheckBox = new QCheckBox(Tr::tr("Show in output pane when running"));
+ printOnRunCheckBox->setChecked(m_aspect->isPrintOnRunEnabled());
+ connect(printOnRunCheckBox, &QCheckBox::toggled,
+ m_aspect, &EnvironmentAspect::setPrintOnRun);
+ topLayout->addWidget(printOnRunCheckBox);
+ }
+
connect(m_environmentWidget, &EnvironmentWidget::userChangesChanged,
this, &EnvironmentAspectWidget::userChangesEdited);
diff --git a/src/plugins/projectexplorer/extraabi.cpp b/src/plugins/projectexplorer/extraabi.cpp
index 24a2861103..19f970b11a 100644
--- a/src/plugins/projectexplorer/extraabi.cpp
+++ b/src/plugins/projectexplorer/extraabi.cpp
@@ -4,7 +4,6 @@
#include "extraabi.h"
#include "abi.h"
-#include "projectexplorertr.h"
#include <coreplugin/icore.h>
@@ -36,17 +35,15 @@ public:
class AbiFlavorAccessor : public UpgradingSettingsAccessor
{
public:
- AbiFlavorAccessor();
+ AbiFlavorAccessor()
+ {
+ setDocType("QtCreatorExtraAbi");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
+ setBaseFilePath(Core::ICore::installerResourcePath("abi.xml"));
+ addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>());
+ }
};
-AbiFlavorAccessor::AbiFlavorAccessor() :
- UpgradingSettingsAccessor("QtCreatorExtraAbi", Tr::tr("ABI"),
- Core::Constants::IDE_DISPLAY_NAME)
-{
- setBaseFilePath(Core::ICore::installerResourcePath("abi.xml"));
-
- addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>());
-}
// --------------------------------------------------------------------
// ExtraAbi:
diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp
index ab72d3608f..67bb4a63fc 100644
--- a/src/plugins/projectexplorer/extracompiler.cpp
+++ b/src/plugins/projectexplorer/extracompiler.cpp
@@ -5,23 +5,26 @@
#include "buildmanager.h"
#include "kitinformation.h"
-#include "session.h"
+#include "projectmanager.h"
#include "target.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
-#include <utils/asynctask.h>
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/async.h>
#include <utils/expected.h>
#include <utils/guard.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDateTime>
-#include <QFutureInterface>
#include <QLoggingCategory>
#include <QThreadPool>
#include <QTimer>
+using namespace Core;
+using namespace Tasking;
using namespace Utils;
namespace ProjectExplorer {
@@ -37,15 +40,14 @@ public:
FilePath source;
FileNameToContentsHash contents;
QDateTime compileTime;
- Core::IEditor *lastEditor = nullptr;
+ IEditor *lastEditor = nullptr;
QMetaObject::Connection activeBuildConfigConnection;
QMetaObject::Connection activeEnvironmentConnection;
- Utils::Guard lock;
+ Guard lock;
bool dirty = false;
QTimer timer;
- FutureSynchronizer m_futureSynchronizer;
std::unique_ptr<TaskTree> m_taskTree;
};
@@ -63,16 +65,16 @@ ExtraCompiler::ExtraCompiler(const Project *project, const FilePath &source,
connect(BuildManager::instance(), &BuildManager::buildStateChanged,
this, &ExtraCompiler::onTargetsBuilt);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, [this](Project *project) {
if (project == d->project)
deleteLater();
});
- Core::EditorManager *editorManager = Core::EditorManager::instance();
- connect(editorManager, &Core::EditorManager::currentEditorChanged,
+ EditorManager *editorManager = EditorManager::instance();
+ connect(editorManager, &EditorManager::currentEditorChanged,
this, &ExtraCompiler::onEditorChanged);
- connect(editorManager, &Core::EditorManager::editorAboutToClose,
+ connect(editorManager, &EditorManager::editorAboutToClose,
this, &ExtraCompiler::onEditorAboutToClose);
// Use existing target files, where possible. Otherwise run the compiler.
@@ -135,7 +137,7 @@ QThreadPool *ExtraCompiler::extraCompilerThreadPool()
return s_extraCompilerThreadPool();
}
-Tasking::TaskItem ExtraCompiler::compileFileItem()
+TaskItem ExtraCompiler::compileFileItem()
{
return taskItemImpl(fromFileProvider());
}
@@ -228,12 +230,12 @@ void ExtraCompiler::onTargetsBuilt(Project *project)
});
}
-void ExtraCompiler::onEditorChanged(Core::IEditor *editor)
+void ExtraCompiler::onEditorChanged(IEditor *editor)
{
// Handle old editor
if (d->lastEditor) {
- Core::IDocument *doc = d->lastEditor->document();
- disconnect(doc, &Core::IDocument::contentsChanged,
+ IDocument *doc = d->lastEditor->document();
+ disconnect(doc, &IDocument::contentsChanged,
this, &ExtraCompiler::setDirty);
if (d->dirty) {
@@ -246,7 +248,7 @@ void ExtraCompiler::onEditorChanged(Core::IEditor *editor)
d->lastEditor = editor;
// Handle new editor
- connect(d->lastEditor->document(), &Core::IDocument::contentsChanged,
+ connect(d->lastEditor->document(), &IDocument::contentsChanged,
this, &ExtraCompiler::setDirty);
} else {
d->lastEditor = nullptr;
@@ -259,15 +261,15 @@ void ExtraCompiler::setDirty()
d->timer.start(1000);
}
-void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor)
+void ExtraCompiler::onEditorAboutToClose(IEditor *editor)
{
if (d->lastEditor != editor)
return;
// Oh no our editor is going to be closed
// get the content first
- Core::IDocument *doc = d->lastEditor->document();
- disconnect(doc, &Core::IDocument::contentsChanged,
+ IDocument *doc = d->lastEditor->document();
+ disconnect(doc, &IDocument::contentsChanged,
this, &ExtraCompiler::setDirty);
if (d->dirty) {
d->dirty = false;
@@ -278,24 +280,17 @@ void ExtraCompiler::onEditorAboutToClose(Core::IEditor *editor)
Environment ExtraCompiler::buildEnvironment() const
{
- if (Target *target = project()->activeTarget()) {
- if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
- return bc->environment();
- } else {
- EnvironmentItems changes =
- EnvironmentKitAspect::environmentChanges(target->kit());
- Environment env = Environment::systemEnvironment();
- env.modify(changes);
- return env;
- }
- }
+ Target *target = project()->activeTarget();
+ if (!target)
+ return Environment::systemEnvironment();
- return Environment::systemEnvironment();
-}
+ if (BuildConfiguration *bc = target->activeBuildConfiguration())
+ return bc->environment();
-Utils::FutureSynchronizer *ExtraCompiler::futureSynchronizer() const
-{
- return &d->m_futureSynchronizer;
+ const EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(target->kit());
+ Environment env = Environment::systemEnvironment();
+ env.modify(changes);
+ return env;
}
void ExtraCompiler::setContent(const FilePath &file, const QByteArray &contents)
@@ -331,15 +326,16 @@ ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const FilePat
ExtraCompiler(project, source, targets, parent)
{ }
-Tasking::TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider)
+TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider)
{
- const auto setupTask = [=](AsyncTask<FileNameToContentsHash> &async) {
+ const auto setupTask = [=](Async<FileNameToContentsHash> &async) {
async.setThreadPool(extraCompilerThreadPool());
- async.setAsyncCallData(&ProcessExtraCompiler::runInThread, this, command(),
- workingDirectory(), arguments(), provider, buildEnvironment());
- async.setFutureSynchronizer(futureSynchronizer());
+ // The passed synchronizer has cancelOnWait set to true by default.
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(&ProcessExtraCompiler::runInThread, this, command(),
+ workingDirectory(), arguments(), provider, buildEnvironment());
};
- const auto taskDone = [=](const AsyncTask<FileNameToContentsHash> &async) {
+ const auto taskDone = [=](const Async<FileNameToContentsHash> &async) {
if (!async.isResultAvailable())
return;
const FileNameToContentsHash data = async.result();
@@ -349,7 +345,7 @@ Tasking::TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &prov
setContent(it.key(), it.value());
updateCompileTime();
};
- return Tasking::Async<FileNameToContentsHash>(setupTask, taskDone);
+ return AsyncTask<FileNameToContentsHash>(setupTask, taskDone);
}
FilePath ProcessExtraCompiler::workingDirectory() const
@@ -374,7 +370,7 @@ Tasks ProcessExtraCompiler::parseIssues(const QByteArray &stdErr)
return {};
}
-void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface,
+void ProcessExtraCompiler::runInThread(QPromise<FileNameToContentsHash> &promise,
const FilePath &cmd, const FilePath &workDir,
const QStringList &args, const ContentProvider &provider,
const Environment &env)
@@ -386,7 +382,7 @@ void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash>
if (sourceContents.isNull() || !prepareToRun(sourceContents))
return;
- QtcProcess process;
+ Process process;
process.setEnvironment(env);
if (!workDir.isEmpty())
@@ -397,15 +393,15 @@ void ProcessExtraCompiler::runInThread(QFutureInterface<FileNameToContentsHash>
if (!process.waitForStarted())
return;
- while (!futureInterface.isCanceled()) {
+ while (!promise.isCanceled()) {
if (process.waitForFinished(200))
break;
}
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
- futureInterface.reportResult(handleProcessFinished(&process));
+ promise.addResult(handleProcessFinished(&process));
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h
index 34993f3940..1dbda0e424 100644
--- a/src/plugins/projectexplorer/extracompiler.h
+++ b/src/plugins/projectexplorer/extracompiler.h
@@ -10,7 +10,6 @@
#include <utils/environment.h>
#include <utils/filepath.h>
-#include <utils/tasktree.h>
#include <QByteArray>
#include <QHash>
@@ -21,14 +20,12 @@
QT_BEGIN_NAMESPACE
template <typename T>
-class QFutureInterface;
+class QPromise;
class QThreadPool;
QT_END_NAMESPACE
-namespace Utils {
-class FutureSynchronizer;
-class QtcProcess;
-}
+namespace Tasking { class TaskItem; }
+namespace Utils { class Process; }
namespace ProjectExplorer {
@@ -52,7 +49,7 @@ public:
Utils::FilePaths targets() const;
void forEachTarget(std::function<void(const Utils::FilePath &)> func) const;
- Utils::Tasking::TaskItem compileFileItem();
+ Tasking::TaskItem compileFileItem();
void compileFile();
bool isDirty() const;
void block();
@@ -64,7 +61,6 @@ signals:
protected:
static QThreadPool *extraCompilerThreadPool();
- Utils::FutureSynchronizer *futureSynchronizer() const;
void setContent(const Utils::FilePath &file, const QByteArray &content);
void updateCompileTime();
Utils::Environment buildEnvironment() const;
@@ -79,7 +75,7 @@ private:
void compileContent(const QByteArray &content);
void compileImpl(const ContentProvider &provider);
void compileIfDirty();
- virtual Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0;
+ virtual Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0;
const std::unique_ptr<ExtraCompilerPrivate> d;
};
@@ -100,13 +96,13 @@ protected:
virtual bool prepareToRun(const QByteArray &sourceContents);
- virtual FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) = 0;
+ virtual FileNameToContentsHash handleProcessFinished(Utils::Process *process) = 0;
virtual Tasks parseIssues(const QByteArray &stdErr);
private:
- Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final;
- void runInThread(QFutureInterface<FileNameToContentsHash> &futureInterface,
+ Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final;
+ void runInThread(QPromise<FileNameToContentsHash> &promise,
const Utils::FilePath &cmd, const Utils::FilePath &workDir,
const QStringList &args, const ContentProvider &provider,
const Utils::Environment &env);
diff --git a/src/plugins/projectexplorer/fileinsessionfinder.cpp b/src/plugins/projectexplorer/fileinsessionfinder.cpp
index 78f75ce1d5..d8df6c7430 100644
--- a/src/plugins/projectexplorer/fileinsessionfinder.cpp
+++ b/src/plugins/projectexplorer/fileinsessionfinder.cpp
@@ -4,7 +4,7 @@
#include "fileinsessionfinder.h"
#include "project.h"
-#include "session.h"
+#include "projectmanager.h"
#include <utils/fileinprojectfinder.h>
@@ -30,12 +30,12 @@ private:
FileInSessionFinder::FileInSessionFinder()
{
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this](const Project *p) {
invalidateFinder();
connect(p, &Project::fileListChanged, this, &FileInSessionFinder::invalidateFinder);
});
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, [this](const Project *p) {
invalidateFinder();
p->disconnect(this);
@@ -45,11 +45,11 @@ FileInSessionFinder::FileInSessionFinder()
FilePaths FileInSessionFinder::doFindFile(const FilePath &filePath)
{
if (!m_finderIsUpToDate) {
- m_finder.setProjectDirectory(SessionManager::startupProject()
- ? SessionManager::startupProject()->projectDirectory()
+ m_finder.setProjectDirectory(ProjectManager::startupProject()
+ ? ProjectManager::startupProject()->projectDirectory()
: FilePath());
FilePaths allFiles;
- for (const Project * const p : SessionManager::projects())
+ for (const Project * const p : ProjectManager::projects())
allFiles << p->files(Project::SourceFiles);
m_finder.setProjectFiles(allFiles);
m_finderIsUpToDate = true;
diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp
index 720cfbabae..0f9d6c40e2 100644
--- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp
+++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp
@@ -5,7 +5,7 @@
#include "project.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include <coreplugin/editormanager/editormanager.h>
#include <utils/algorithm.h>
@@ -52,7 +52,7 @@ Utils::FileIterator *FilesInAllProjectsFind::files(const QStringList &nameFilter
const QVariant &additionalParameters) const
{
Q_UNUSED(additionalParameters)
- const QSet<FilePath> dirs = Utils::transform<QSet>(SessionManager::projects(), [](Project *p) {
+ const QSet<FilePath> dirs = Utils::transform<QSet>(ProjectManager::projects(), [](Project *p) {
return p->projectFilePath().parentDir();
});
return new SubDirFileIterator(FilePaths(dirs.constBegin(), dirs.constEnd()),
diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp
index d2449bd69a..4012fbd669 100644
--- a/src/plugins/projectexplorer/gcctoolchain.cpp
+++ b/src/plugins/projectexplorer/gcctoolchain.cpp
@@ -20,8 +20,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QBuffer>
#include <QCheckBox>
@@ -119,7 +119,7 @@ static QString runGcc(const FilePath &gcc, const QStringList &arguments, const E
if (!gcc.isExecutableFile())
return {};
- QtcProcess cpp;
+ Process cpp;
Environment environment(env);
environment.setupEnglishOutput();
@@ -196,7 +196,7 @@ HeaderPaths GccToolChain::gccHeaderPaths(const FilePath &gcc,
}
const FilePath headerPath
- = FilePath::fromString(QString::fromUtf8(line)).onDevice(gcc).canonicalPath();
+ = gcc.withNewPath(QString::fromUtf8(line)).canonicalPath();
if (!headerPath.isEmpty())
builtInHeaderPaths.append({headerPath, thisHeaderKind});
@@ -569,7 +569,7 @@ WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const
return flags;
}
-QStringList GccToolChain::includedFiles(const QStringList &flags, const QString &directoryPath) const
+FilePaths GccToolChain::includedFiles(const QStringList &flags, const FilePath &directoryPath) const
{
return ToolChain::includedFiles("-include", flags, directoryPath, PossiblyConcatenatedFlag::No);
}
@@ -1040,10 +1040,9 @@ GccToolChainFactory::GccToolChainFactory()
Toolchains GccToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
// GCC is almost never what you want on macOS, but it is by default found in /usr/bin
- if (HostOsInfo::isMacHost()
- && (!detector.device || detector.device->type() == Constants::DESKTOP_DEVICE_TYPE)) {
+ if (HostOsInfo::isMacHost() && detector.device->type() == Constants::DESKTOP_DEVICE_TYPE)
return {};
- }
+
Toolchains tcs;
static const auto tcChecker = [](const ToolChain *tc) {
return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor
@@ -1086,7 +1085,7 @@ static FilePaths findCompilerCandidates(const ToolchainDetector &detector,
{
const IDevice::ConstPtr device = detector.device;
const QFileInfo fi(compilerName);
- if (device.isNull() && fi.isAbsolute() && fi.isFile())
+ if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && fi.isAbsolute() && fi.isFile())
return {FilePath::fromString(compilerName)};
QStringList nameFilters(compilerName);
@@ -1385,8 +1384,10 @@ void GccToolChainConfigWidget::setFromToolchain()
QSignalBlocker blocker(this);
auto tc = static_cast<GccToolChain *>(toolChain());
m_compilerCommand->setFilePath(tc->compilerCommand());
- m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags()));
- m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags()));
+ m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags(),
+ HostOsInfo::hostOs()));
+ m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags(),
+ HostOsInfo::hostOs()));
if (m_abiWidget) {
m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi());
if (!m_isReadOnly && !m_compilerCommand->filePath().toString().isEmpty())
@@ -1569,7 +1570,7 @@ bool ClangToolChain::matchesCompilerCommand(const FilePath &command) const
m_resolvedCompilerCommand = FilePath();
if (HostOsInfo::isMacHost()
&& compilerCommand().parentDir() == FilePath::fromString("/usr/bin")) {
- QtcProcess xcrun;
+ Process xcrun;
xcrun.setCommand({"/usr/bin/xcrun", {"-f", compilerCommand().fileName()}});
xcrun.runBlocking();
const FilePath output = FilePath::fromString(xcrun.cleanedStdOut().trimmed());
diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h
index 55ba61e9fb..1e6353d0a7 100644
--- a/src/plugins/projectexplorer/gcctoolchain.h
+++ b/src/plugins/projectexplorer/gcctoolchain.h
@@ -55,8 +55,8 @@ public:
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
Utils::WarningFlags warningFlags(const QStringList &cflags) const override;
- QStringList includedFiles(const QStringList &flags,
- const QString &directoryPath) const override;
+ Utils::FilePaths includedFiles(const QStringList &flags,
+ const Utils::FilePath &directoryPath) const override;
MacroInspectionRunner createMacroInspectionRunner() const override;
BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Utils::Environment &env) const override;
diff --git a/src/plugins/projectexplorer/ipotentialkit.h b/src/plugins/projectexplorer/ipotentialkit.h
index ae032fb9e3..038a3a9fac 100644
--- a/src/plugins/projectexplorer/ipotentialkit.h
+++ b/src/plugins/projectexplorer/ipotentialkit.h
@@ -4,18 +4,16 @@
#pragma once
#include <QObject>
-#include <QMetaType>
+
#include "projectexplorer_export.h"
namespace ProjectExplorer {
-class PROJECTEXPLORER_EXPORT IPotentialKit : public QObject
+class PROJECTEXPLORER_EXPORT IPotentialKit
{
- Q_OBJECT
-
public:
IPotentialKit();
- ~IPotentialKit() override;
+ virtual ~IPotentialKit();
virtual QString displayName() const = 0;
virtual void executeFromMenu() = 0;
@@ -23,4 +21,4 @@ public:
virtual bool isEnabled() const = 0;
};
-}
+} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
index 25a897bd79..49a9571189 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp
@@ -16,10 +16,7 @@
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
-#include <utils/fileutils.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <utils/stringutils.h>
#include <utils/theme/theme.h>
#include <QApplication>
@@ -29,7 +26,6 @@
#include <QDebug>
#include <QDir>
#include <QFormLayout>
-#include <QFutureWatcher>
#include <QItemSelectionModel>
#include <QLabel>
#include <QListView>
@@ -603,25 +599,21 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
using namespace Utils;
if (m_completion == Completion::None)
return;
- ILocatorFilter * const classesFilter = findOrDefault(
- ILocatorFilter::allLocatorFilters(),
- equal(&ILocatorFilter::id, Id("Classes")));
- if (!classesFilter)
- return;
- classesFilter->prepareSearch({});
- const auto watcher = new QFutureWatcher<LocatorFilterEntry>;
- const auto handleResults = [this, lineEdit, watcher](int firstIndex, int endIndex) {
+ LocatorMatcher *matcher = new LocatorMatcher;
+ matcher->setParent(lineEdit);
+ matcher->setTasks(LocatorMatcher::matchers(MatcherType::Classes));
+ const auto handleResults = [lineEdit, matcher, completion = m_completion] {
QSet<QString> namespaces;
QStringList classes;
Project * const project = ProjectTree::currentProject();
- for (int i = firstIndex; i < endIndex; ++i) {
+ const LocatorFilterEntries entries = matcher->outputData();
+ for (const LocatorFilterEntry &entry : entries) {
static const auto isReservedName = [](const QString &name) {
static const QRegularExpression rx1("^_[A-Z].*");
static const QRegularExpression rx2(".*::_[A-Z].*");
return name.contains("__") || rx1.match(name).hasMatch()
|| rx2.match(name).hasMatch();
};
- const LocatorFilterEntry &entry = watcher->resultAt(i);
const bool hasNamespace = !entry.extraInfo.isEmpty()
&& !entry.extraInfo.startsWith('<') && !entry.extraInfo.contains("::<")
&& !isReservedName(entry.extraInfo)
@@ -635,7 +627,7 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
if (hasNamespace) {
if (isBaseClassCandidate)
classes << (entry.extraInfo + "::" + entry.displayName);
- if (m_completion == Completion::Namespaces) {
+ if (completion == Completion::Namespaces) {
if (!project
|| entry.filePath.startsWith(project->projectDirectory().toString())) {
namespaces << entry.extraInfo;
@@ -644,7 +636,7 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
}
}
QStringList completionList;
- if (m_completion == Completion::Namespaces) {
+ if (completion == Completion::Namespaces) {
completionList = toList(namespaces);
completionList = filtered(completionList, [&classes](const QString &ns) {
return !classes.contains(ns);
@@ -658,16 +650,9 @@ void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
completionList.sort();
lineEdit->setSpecialCompleter(new QCompleter(completionList, lineEdit));
};
- QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt, lineEdit,
- handleResults);
- QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::finished,
- watcher, &QFutureWatcher<LocatorFilterEntry>::deleteLater);
- watcher->setFuture(runAsync([classesFilter](QFutureInterface<LocatorFilterEntry> &f) {
- const QList<LocatorFilterEntry> matches = classesFilter->matchesFor(f, {});
- if (!matches.isEmpty())
- f.reportResults(QVector<LocatorFilterEntry>(matches.cbegin(), matches.cend()));
- f.reportFinished();
- }));
+ QObject::connect(matcher, &LocatorMatcher::done, lineEdit, handleResults);
+ QObject::connect(matcher, &LocatorMatcher::done, matcher, &QObject::deleteLater);
+ matcher->start();
}
void LineEditField::setText(const QString &text)
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp
index 024044d7a0..ec55e3218f 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonsummarypage.cpp
@@ -8,8 +8,8 @@
#include "../projectexplorerconstants.h"
#include "../projectexplorertr.h"
#include "../projectnodes.h"
+#include "../projectmanager.h"
#include "../projecttree.h"
-#include "../session.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/iversioncontrol.h>
@@ -209,7 +209,7 @@ Node *JsonSummaryPage::findWizardContextNode(Node *contextNode) const
// Static cast from void * to avoid qobject_cast (which needs a valid object) in value().
auto project = static_cast<Project *>(m_wizard->value(Constants::PROJECT_POINTER).value<void *>());
- if (SessionManager::projects().contains(project) && project->rootProjectNode()) {
+ if (ProjectManager::projects().contains(project) && project->rootProjectNode()) {
const FilePath path = FilePath::fromVariant(m_wizard->value(Constants::PREFERRED_PROJECT_NODE_PATH));
contextNode = project->rootProjectNode()->findNode([path](const Node *n) {
return path == n->filePath();
diff --git a/src/plugins/projectexplorer/kitchooser.cpp b/src/plugins/projectexplorer/kitchooser.cpp
index 14c848da74..9e45cacd7d 100644
--- a/src/plugins/projectexplorer/kitchooser.cpp
+++ b/src/plugins/projectexplorer/kitchooser.cpp
@@ -6,7 +6,7 @@
#include "kitmanager.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
-#include "session.h"
+#include "projectmanager.h"
#include "target.h"
#include <coreplugin/icore.h>
@@ -88,7 +88,7 @@ void KitChooser::populate()
const Id lastKit = Id::fromSetting(ICore::settings()->value(lastKitKey));
bool didActivate = false;
- if (Target *target = SessionManager::startupTarget()) {
+ if (Target *target = ProjectManager::startupTarget()) {
Kit *kit = target->kit();
if (m_kitPredicate(kit)) {
QString display = Tr::tr("Kit of Active Project: %1").arg(kitText(kit));
diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp
index dcb490b70d..fc8039b402 100644
--- a/src/plugins/projectexplorer/kitinformation.cpp
+++ b/src/plugins/projectexplorer/kitinformation.cpp
@@ -34,7 +34,6 @@
#include <QVBoxLayout>
using namespace Utils;
-using namespace Utils::Layouting;
namespace ProjectExplorer {
@@ -65,7 +64,7 @@ public:
private:
void makeReadOnly() override { m_chooser->setReadOnly(true); }
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_chooser);
builder.addItem(Layouting::Span(2, m_chooser));
@@ -142,7 +141,7 @@ void SysRootKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) con
});
}
-Id SysRootKitAspect::id()
+Utils::Id SysRootKitAspect::id()
{
return "PE.Profile.SysRoot";
}
@@ -231,7 +230,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_mainWidget);
builder.addItem(m_mainWidget);
@@ -760,7 +759,7 @@ public:
~DeviceTypeKitAspectWidget() override { delete m_comboBox; }
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -795,7 +794,7 @@ DeviceTypeKitAspect::DeviceTypeKitAspect()
{
setObjectName(QLatin1String("DeviceTypeInformation"));
setId(DeviceTypeKitAspect::id());
- setDisplayName(Tr::tr("Device type"));
+ setDisplayName(Tr::tr("Run device type"));
setDescription(Tr::tr("The type of device to run applications on."));
setPriority(33000);
makeEssential();
@@ -896,7 +895,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -942,7 +941,7 @@ DeviceKitAspect::DeviceKitAspect()
{
setObjectName(QLatin1String("DeviceInformation"));
setId(DeviceKitAspect::id());
- setDisplayName(Tr::tr("Device"));
+ setDisplayName(Tr::tr("Run device"));
setDescription(Tr::tr("The device to run the applications on."));
setPriority(32000);
@@ -1023,30 +1022,29 @@ KitAspect::ItemList DeviceKitAspect::toUserOutput(const Kit *k) const
void DeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const
{
QTC_ASSERT(kit, return);
- expander->registerVariable("Device:HostAddress", Tr::tr("Host address"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->sshParameters().host() : QString();
+ expander->registerVariable("Device:HostAddress", Tr::tr("Host address"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->sshParameters().host() : QString();
});
- expander->registerVariable("Device:SshPort", Tr::tr("SSH port"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? QString::number(device->sshParameters().port()) : QString();
+ expander->registerVariable("Device:SshPort", Tr::tr("SSH port"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? QString::number(device->sshParameters().port()) : QString();
});
- expander->registerVariable("Device:UserName", Tr::tr("User name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->sshParameters().userName() : QString();
+ expander->registerVariable("Device:UserName", Tr::tr("User name"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->sshParameters().userName() : QString();
});
- expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->sshParameters().privateKeyFile.toString() : QString();
+ expander->registerVariable("Device:KeyFile", Tr::tr("Private key file"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->sshParameters().privateKeyFile.toString() : QString();
});
- expander->registerVariable("Device:Name", Tr::tr("Device name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
- return device ? device->displayName() : QString();
+ expander->registerVariable("Device:Name", Tr::tr("Device name"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->displayName() : QString();
+ });
+ expander->registerFileVariables("Device::Root", Tr::tr("Device root directory"), [kit] {
+ const IDevice::ConstPtr device = DeviceKitAspect::device(kit);
+ return device ? device->rootPath() : FilePath{};
});
}
@@ -1157,7 +1155,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_comboBox);
builder.addItem(m_comboBox);
@@ -1266,31 +1264,31 @@ KitAspect::ItemList BuildDeviceKitAspect::toUserOutput(const Kit *k) const
void BuildDeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const
{
QTC_ASSERT(kit, return);
- expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->sshParameters().host() : QString();
+ expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->sshParameters().host() : QString();
});
- expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? QString::number(device->sshParameters().port()) : QString();
+ expander->registerVariable("BuildDevice:SshPort", Tr::tr("Build SSH port"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? QString::number(device->sshParameters().port()) : QString();
});
- expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->sshParameters().userName() : QString();
+ expander->registerVariable("BuildDevice:UserName", Tr::tr("Build user name"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->sshParameters().userName() : QString();
});
- expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->sshParameters().privateKeyFile.toString() : QString();
+ expander->registerVariable("BuildDevice:KeyFile", Tr::tr("Build private key file"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->sshParameters().privateKeyFile.toString() : QString();
});
- expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"),
- [kit]() -> QString {
- const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
- return device ? device->displayName() : QString();
+ expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->displayName() : QString();
});
+ expander
+ ->registerFileVariables("BuildDevice::Root", Tr::tr("Build device root directory"), [kit] {
+ const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit);
+ return device ? device->rootPath() : FilePath{};
+ });
}
Id BuildDeviceKitAspect::id()
@@ -1388,7 +1386,7 @@ public:
}
private:
- void addToLayout(LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &builder) override
{
addMutableAction(m_mainWidget);
builder.addItem(m_mainWidget);
diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp
index cb67e11fac..86a3b533c1 100644
--- a/src/plugins/projectexplorer/kitmanager.cpp
+++ b/src/plugins/projectexplorer/kitmanager.cpp
@@ -747,7 +747,7 @@ KitAspectWidget::~KitAspectWidget()
delete m_mutableAction;
}
-void KitAspectWidget::addToLayoutWithLabel(QWidget *parent)
+void KitAspectWidget::addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent)
{
QTC_ASSERT(parent, return);
auto label = createSubWidget<QLabel>(m_kitInformation->displayName() + ':');
@@ -756,10 +756,9 @@ void KitAspectWidget::addToLayoutWithLabel(QWidget *parent)
emit labelLinkActivated(link);
});
- Layouting::LayoutExtender builder(parent->layout(), Layouting::WithFormAlignment);
- builder.finishRow();
- builder.addItem(label);
- addToLayout(builder);
+ parentItem.addItem(label);
+ addToLayout(parentItem);
+ parentItem.addItem(Layouting::br);
}
void KitAspectWidget::addMutableAction(QWidget *child)
diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h
index a494fde323..f25f83ca7f 100644
--- a/src/plugins/projectexplorer/kitmanager.h
+++ b/src/plugins/projectexplorer/kitmanager.h
@@ -117,7 +117,7 @@ public:
virtual void makeReadOnly() = 0;
virtual void refresh() = 0;
- void addToLayoutWithLabel(QWidget *parent);
+ void addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent);
static QString msgManage();
diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
index 908238d3a7..822844f57d 100644
--- a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
+++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp
@@ -23,6 +23,7 @@
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#include <QFileDialog>
+#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
@@ -36,12 +37,14 @@ using namespace Utils;
namespace ProjectExplorer {
namespace Internal {
-KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) :
+KitManagerConfigWidget::KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName) :
m_iconButton(new QToolButton),
m_nameEdit(new QLineEdit),
m_fileSystemFriendlyNameLineEdit(new QLineEdit),
m_kit(k),
- m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID)))
+ m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID))),
+ m_isDefaultKit(isDefaultKit),
+ m_hasUniqueName(hasUniqueName)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
@@ -64,10 +67,13 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) :
this, &KitManagerConfigWidget::setFileSystemFriendlyName);
using namespace Layouting;
- Grid {
+ Grid page {
+ withFormAlignment,
+ columnStretch(1, 2),
label, m_nameEdit, m_iconButton, br,
- fsLabel, m_fileSystemFriendlyNameLineEdit
- }.attachTo(this, WithFormAlignment);
+ fsLabel, m_fileSystemFriendlyNameLineEdit, br,
+ noMargin
+ };
m_iconButton->setToolTip(Tr::tr("Kit icon."));
auto setIconAction = new QAction(Tr::tr("Select Icon..."), this);
@@ -97,7 +103,9 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k) :
chooser->addMacroExpanderProvider([this] { return m_modifiedKit->macroExpander(); });
for (KitAspect *aspect : KitManager::kitAspects())
- addAspectToWorkingCopy(aspect);
+ addAspectToWorkingCopy(page, aspect);
+
+ page.attachTo(this);
updateVisibility();
@@ -187,14 +195,14 @@ QString KitManagerConfigWidget::validityMessage() const
return m_modifiedKit->toHtml(tmp);
}
-void KitManagerConfigWidget::addAspectToWorkingCopy(KitAspect *aspect)
+void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect)
{
QTC_ASSERT(aspect, return);
KitAspectWidget *widget = aspect->createConfigWidget(workingCopy());
QTC_ASSERT(widget, return);
QTC_ASSERT(!m_widgets.contains(widget), return);
- widget->addToLayoutWithLabel(this);
+ widget->addToLayoutWithLabel(parent, this);
m_widgets.append(widget);
connect(widget->mutableAction(), &QAction::toggled,
@@ -213,11 +221,6 @@ void KitManagerConfigWidget::updateVisibility()
}
}
-void KitManagerConfigWidget::setHasUniqueName(bool unique)
-{
- m_hasUniqueName = unique;
-}
-
void KitManagerConfigWidget::makeStickySubWidgetsReadOnly()
{
for (KitAspectWidget *w : std::as_const(m_widgets)) {
@@ -231,31 +234,11 @@ Kit *KitManagerConfigWidget::workingCopy() const
return m_modifiedKit.get();
}
-bool KitManagerConfigWidget::configures(Kit *k) const
-{
- return m_kit == k;
-}
-
-void KitManagerConfigWidget::setIsDefaultKit(bool d)
-{
- if (m_isDefaultKit == d)
- return;
- m_isDefaultKit = d;
- emit dirty();
-}
-
bool KitManagerConfigWidget::isDefaultKit() const
{
return m_isDefaultKit;
}
-void KitManagerConfigWidget::removeKit()
-{
- if (!m_kit)
- return;
- KitManager::deregisterKit(m_kit);
-}
-
void KitManagerConfigWidget::setIcon()
{
const Utils::Id deviceType = DeviceTypeKitAspect::deviceTypeId(m_modifiedKit.get());
diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.h b/src/plugins/projectexplorer/kitmanagerconfigwidget.h
index 142eb8fa36..94218c5de8 100644
--- a/src/plugins/projectexplorer/kitmanagerconfigwidget.h
+++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.h
@@ -25,7 +25,7 @@ class KitManagerConfigWidget : public QWidget
Q_OBJECT
public:
- explicit KitManagerConfigWidget(Kit *k);
+ explicit KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName);
~KitManagerConfigWidget() override;
QString displayName() const;
@@ -35,19 +35,14 @@ public:
void discard();
bool isDirty() const;
QString validityMessage() const;
- void addAspectToWorkingCopy(KitAspect *aspect);
+ void addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect);
void makeStickySubWidgetsReadOnly();
Kit *workingCopy() const;
- bool configures(Kit *k) const;
bool isRegistering() const { return m_isRegistering; }
- void setIsDefaultKit(bool d);
bool isDefaultKit() const;
- void removeKit();
void updateVisibility();
- void setHasUniqueName(bool unique);
-
signals:
void dirty();
void isAutoDetectedChanged();
@@ -74,9 +69,9 @@ private:
QList<KitAspectWidget *> m_widgets;
Kit *m_kit;
std::unique_ptr<Kit> m_modifiedKit;
- bool m_isDefaultKit = false;
+ bool &m_isDefaultKit;
bool m_fixingKit = false;
- bool m_hasUniqueName = true;
+ bool &m_hasUniqueName;
bool m_isRegistering = false;
mutable QString m_cachedDisplayName;
};
diff --git a/src/plugins/projectexplorer/kitmodel.cpp b/src/plugins/projectexplorer/kitmodel.cpp
index 4b250f23c9..12f23fb281 100644
--- a/src/plugins/projectexplorer/kitmodel.cpp
+++ b/src/plugins/projectexplorer/kitmodel.cpp
@@ -24,57 +24,122 @@ namespace Internal {
class KitNode : public TreeItem
{
public:
- KitNode(Kit *k, KitModel *m)
+ KitNode(Kit *k, KitModel *m, QBoxLayout *parentLayout)
+ : m_kit(k), m_model(m), m_parentLayout(parentLayout)
+ {}
+
+ ~KitNode() override { delete m_widget; }
+
+ Kit *kit() const { return m_kit; }
+
+ QVariant data(int, int role) const override
{
- widget = new KitManagerConfigWidget(k);
+ if (role == Qt::FontRole) {
+ QFont f = QApplication::font();
+ if (isDirty())
+ f.setBold(!f.bold());
+ if (isDefaultKit())
+ f.setItalic(f.style() != QFont::StyleItalic);
+ return f;
+ }
+ if (role == Qt::DisplayRole) {
+ QString baseName = displayName();
+ if (isDefaultKit())
+ //: Mark up a kit as the default one.
+ baseName = Tr::tr("%1 (default)").arg(baseName);
+ return baseName;
+ }
- QObject::connect(widget, &KitManagerConfigWidget::dirty, m, [this] { update(); });
+ if (role == Qt::DecorationRole)
+ return displayIcon();
- QObject::connect(widget, &KitManagerConfigWidget::isAutoDetectedChanged, m, [this, m] {
- TreeItem *oldParent = parent();
- TreeItem *newParent =
- m->rootItem()->childAt(widget->workingCopy()->isAutoDetected() ? 0 : 1);
- if (oldParent && oldParent != newParent) {
- m->takeItem(this);
- newParent->appendChild(this);
- }
- });
+ if (role == Qt::ToolTipRole)
+ return widget()->validityMessage();
+
+ return {};
}
- ~KitNode() override
+ bool isDirty() const
{
- delete widget;
+ if (m_widget)
+ return m_widget->isDirty();
+ return false;
}
- QVariant data(int, int role) const override
+ QIcon displayIcon() const
{
- if (widget) {
- if (role == Qt::FontRole) {
- QFont f = QApplication::font();
- if (widget->isDirty())
- f.setBold(!f.bold());
- if (widget->isDefaultKit())
- f.setItalic(f.style() != QFont::StyleItalic);
- return f;
- }
- if (role == Qt::DisplayRole) {
- QString baseName = widget->displayName();
- if (widget->isDefaultKit())
- //: Mark up a kit as the default one.
- baseName = Tr::tr("%1 (default)").arg(baseName);
- return baseName;
- }
- if (role == Qt::DecorationRole) {
- return widget->displayIcon();
- }
- if (role == Qt::ToolTipRole) {
- return widget->validityMessage();
+ if (m_widget)
+ return m_widget->displayIcon();
+ return m_kit->displayIcon();
+ }
+
+ QString displayName() const
+ {
+ if (m_widget)
+ return m_widget->displayName();
+ return m_kit->displayName();
+ }
+
+ bool isDefaultKit() const
+ {
+ return m_isDefaultKit;
+ }
+
+ bool isRegistering() const
+ {
+ if (m_widget)
+ return m_widget->isRegistering();
+ return false;
+ }
+
+ void setIsDefaultKit(bool on)
+ {
+ if (m_isDefaultKit == on)
+ return;
+ m_isDefaultKit = on;
+ if (m_widget)
+ emit m_widget->dirty();
+ }
+
+ KitManagerConfigWidget *widget() const
+ {
+ const_cast<KitNode *>(this)->ensureWidget();
+ return m_widget;
+ }
+
+ void setHasUniqueName(bool on)
+ {
+ m_hasUniqueName = on;
+ }
+
+private:
+ void ensureWidget()
+ {
+ if (m_widget)
+ return;
+
+ m_widget = new KitManagerConfigWidget(m_kit, m_isDefaultKit, m_hasUniqueName);
+
+ QObject::connect(m_widget, &KitManagerConfigWidget::dirty, m_model, [this] { update(); });
+
+ QObject::connect(m_widget, &KitManagerConfigWidget::isAutoDetectedChanged, m_model, [this] {
+ TreeItem *oldParent = parent();
+ TreeItem *newParent =
+ m_model->rootItem()->childAt(m_widget->workingCopy()->isAutoDetected() ? 0 : 1);
+ if (oldParent && oldParent != newParent) {
+ m_model->takeItem(this);
+ newParent->appendChild(this);
}
- }
- return QVariant();
+ });
+ m_parentLayout->addWidget(m_widget);
}
- KitManagerConfigWidget *widget;
+ Kit *m_kit = m_kit;
+ KitModel *m_model = nullptr;
+ KitManagerConfigWidget *m_widget = nullptr;
+ QBoxLayout *m_parentLayout = nullptr;
+ bool m_isDefaultKit = false;
+ bool m_hasUniqueName = true;
};
// --------------------------------------------------------------------------
@@ -113,7 +178,7 @@ KitModel::KitModel(QBoxLayout *parentLayout, QObject *parent)
Kit *KitModel::kit(const QModelIndex &index)
{
KitNode *n = kitNode(index);
- return n ? n->widget->workingCopy() : nullptr;
+ return n ? n->widget()->workingCopy() : nullptr;
}
KitNode *KitModel::kitNode(const QModelIndex &index)
@@ -136,20 +201,20 @@ void KitModel::setDefaultKit(const QModelIndex &index)
bool KitModel::isDefaultKit(Kit *k) const
{
- return m_defaultNode && m_defaultNode->widget->workingCopy() == k;
+ return m_defaultNode && m_defaultNode->widget()->workingCopy() == k;
}
KitManagerConfigWidget *KitModel::widget(const QModelIndex &index)
{
KitNode *n = kitNode(index);
- return n ? n->widget : nullptr;
+ return n ? n->widget() : nullptr;
}
void KitModel::validateKitNames()
{
QHash<QString, int> nameHash;
forItemsAtLevel<2>([&nameHash](KitNode *n) {
- const QString displayName = n->widget->displayName();
+ const QString displayName = n->displayName();
if (nameHash.contains(displayName))
++nameHash[displayName];
else
@@ -157,8 +222,8 @@ void KitModel::validateKitNames()
});
forItemsAtLevel<2>([&nameHash](KitNode *n) {
- const QString displayName = n->widget->displayName();
- n->widget->setHasUniqueName(nameHash.value(displayName) == 1);
+ const QString displayName = n->displayName();
+ n->setHasUniqueName(nameHash.value(displayName) == 1);
});
}
@@ -166,8 +231,8 @@ void KitModel::apply()
{
// Add/update dirty nodes before removing kits. This ensures the right kit ends up as default.
forItemsAtLevel<2>([](KitNode *n) {
- if (n->widget->isDirty()) {
- n->widget->apply();
+ if (n->isDirty()) {
+ n->widget()->apply();
n->update();
}
});
@@ -175,7 +240,7 @@ void KitModel::apply()
// Remove unused kits:
const QList<KitNode *> removeList = m_toRemoveList;
for (KitNode *n : removeList)
- n->widget->removeKit();
+ KitManager::deregisterKit(n->kit());
emit layoutChanged(); // Force update.
}
@@ -197,7 +262,7 @@ void KitModel::markForRemoval(Kit *k)
setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; }));
takeItem(node);
- if (node->widget->configures(nullptr))
+ if (node->kit() == nullptr)
delete node;
else
m_toRemoveList.append(node);
@@ -209,7 +274,7 @@ Kit *KitModel::markForAddition(Kit *baseKit)
const QString newName = newKitName(baseKit ? baseKit->unexpandedDisplayName() : QString());
KitNode *node = createNode(nullptr);
m_manualRoot->appendChild(node);
- Kit *k = node->widget->workingCopy();
+ Kit *k = node->widget()->workingCopy();
KitGuard g(k);
if (baseKit) {
k->copyFrom(baseKit);
@@ -229,7 +294,7 @@ Kit *KitModel::markForAddition(Kit *baseKit)
void KitModel::updateVisibility()
{
forItemsAtLevel<2>([](const TreeItem *ti) {
- static_cast<const KitNode *>(ti)->widget->updateVisibility();
+ static_cast<const KitNode *>(ti)->widget()->updateVisibility();
});
}
@@ -237,32 +302,31 @@ QString KitModel::newKitName(const QString &sourceName) const
{
QList<Kit *> allKits;
forItemsAtLevel<2>([&allKits](const TreeItem *ti) {
- allKits << static_cast<const KitNode *>(ti)->widget->workingCopy();
+ allKits << static_cast<const KitNode *>(ti)->widget()->workingCopy();
});
return Kit::newKitName(sourceName, allKits);
}
KitNode *KitModel::findWorkingCopy(Kit *k) const
{
- return findItemAtLevel<2>([k](KitNode *n) { return n->widget->workingCopy() == k; });
+ return findItemAtLevel<2>([k](KitNode *n) { return n->widget()->workingCopy() == k; });
}
KitNode *KitModel::createNode(Kit *k)
{
- auto node = new KitNode(k, this);
- m_parentLayout->addWidget(node->widget);
+ auto node = new KitNode(k, this, m_parentLayout);
return node;
}
void KitModel::setDefaultNode(KitNode *node)
{
if (m_defaultNode) {
- m_defaultNode->widget->setIsDefaultKit(false);
+ m_defaultNode->setIsDefaultKit(false);
m_defaultNode->update();
}
m_defaultNode = node;
if (m_defaultNode) {
- m_defaultNode->widget->setIsDefaultKit(true);
+ m_defaultNode->setIsDefaultKit(true);
m_defaultNode->update();
}
}
@@ -271,7 +335,7 @@ void KitModel::addKit(Kit *k)
{
for (TreeItem *n : *m_manualRoot) {
// Was added by us
- if (static_cast<KitNode *>(n)->widget->isRegistering())
+ if (static_cast<KitNode *>(n)->isRegistering())
return;
}
@@ -292,7 +356,7 @@ void KitModel::removeKit(Kit *k)
{
QList<KitNode *> nodes = m_toRemoveList;
for (KitNode *n : std::as_const(nodes)) {
- if (n->widget->configures(k)) {
+ if (n->kit() == k) {
m_toRemoveList.removeOne(n);
if (m_defaultNode == n)
m_defaultNode = nullptr;
@@ -303,7 +367,7 @@ void KitModel::removeKit(Kit *k)
}
KitNode *node = findItemAtLevel<2>([k](KitNode *n) {
- return n->widget->configures(k);
+ return n->kit() == k;
});
if (node == m_defaultNode)
@@ -319,7 +383,7 @@ void KitModel::changeDefaultKit()
{
Kit *defaultKit = KitManager::defaultKit();
KitNode *node = findItemAtLevel<2>([defaultKit](KitNode *n) {
- return n->widget->configures(defaultKit);
+ return n->kit() == defaultKit;
});
setDefaultNode(node);
}
diff --git a/src/plugins/projectexplorer/kitoptionspage.cpp b/src/plugins/projectexplorer/kitoptionspage.cpp
index d5c709194a..db578a403e 100644
--- a/src/plugins/projectexplorer/kitoptionspage.cpp
+++ b/src/plugins/projectexplorer/kitoptionspage.cpp
@@ -23,11 +23,13 @@
namespace ProjectExplorer {
namespace Internal {
-// --------------------------------------------------------------------------
-// KitOptionsPageWidget:
-// --------------------------------------------------------------------------
+// KitOptionsPageWidget
+
+class KitOptionsPageWidget;
-class KitOptionsPageWidget : public QWidget
+static QPointer<KitOptionsPageWidget> kitOptionsPageWidget;
+
+class KitOptionsPageWidget : public Core::IOptionsPageWidget
{
public:
KitOptionsPageWidget();
@@ -42,6 +44,8 @@ public:
void makeDefaultKit();
void updateState();
+ void apply() final { m_model->apply(); }
+
public:
QTreeView *m_kitsView = nullptr;
QPushButton *m_addButton = nullptr;
@@ -58,6 +62,7 @@ public:
KitOptionsPageWidget::KitOptionsPageWidget()
{
+ kitOptionsPageWidget = this;
m_kitsView = new QTreeView(this);
m_kitsView->setUniformRowHeights(true);
m_kitsView->header()->setStretchLastSection(true);
@@ -239,38 +244,14 @@ QModelIndex KitOptionsPageWidget::currentIndex() const
// KitOptionsPage:
// --------------------------------------------------------------------------
-static KitOptionsPage *theKitOptionsPage = nullptr;
-
KitOptionsPage::KitOptionsPage()
{
- theKitOptionsPage = this;
setId(Constants::KITS_SETTINGS_PAGE_ID);
setDisplayName(Tr::tr("Kits"));
setCategory(Constants::KITS_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Kits"));
setCategoryIconPath(":/projectexplorer/images/settingscategory_kits.png");
-}
-
-QWidget *KitOptionsPage::widget()
-{
- if (!m_widget)
- m_widget = new Internal::KitOptionsPageWidget;
-
- return m_widget;
-}
-
-void KitOptionsPage::apply()
-{
- if (m_widget)
- m_widget->m_model->apply();
-}
-
-void KitOptionsPage::finish()
-{
- if (m_widget) {
- delete m_widget;
- m_widget = nullptr;
- }
+ setWidgetCreator([] { return new Internal::KitOptionsPageWidget; });
}
void KitOptionsPage::showKit(Kit *k)
@@ -278,18 +259,16 @@ void KitOptionsPage::showKit(Kit *k)
if (!k)
return;
- (void) widget();
- QModelIndex index = m_widget->m_model->indexOf(k);
- m_widget->m_selectionModel->select(index,
+ Internal::KitOptionsPageWidget *widget = Internal::kitOptionsPageWidget;
+ if (!widget)
+ return;
+
+ QModelIndex index = widget->m_model->indexOf(k);
+ widget->m_selectionModel->select(index,
QItemSelectionModel::Clear
| QItemSelectionModel::SelectCurrent
| QItemSelectionModel::Rows);
- m_widget->m_kitsView->scrollTo(index);
-}
-
-KitOptionsPage *KitOptionsPage::instance()
-{
- return theKitOptionsPage;
+ widget->m_kitsView->scrollTo(index);
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/kitoptionspage.h b/src/plugins/projectexplorer/kitoptionspage.h
index 5ce589cd6b..a7a4ba29db 100644
--- a/src/plugins/projectexplorer/kitoptionspage.h
+++ b/src/plugins/projectexplorer/kitoptionspage.h
@@ -7,12 +7,8 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
namespace ProjectExplorer {
-namespace Internal { class KitOptionsPageWidget; }
-
class Kit;
class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage
@@ -20,15 +16,7 @@ class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage
public:
KitOptionsPage();
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
- void showKit(Kit *k);
- static KitOptionsPage *instance();
-
-private:
- QPointer<Internal::KitOptionsPageWidget> m_widget;
+ static void showKit(Kit *k);
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp
index 57ebc2490b..b3b9c93486 100644
--- a/src/plugins/projectexplorer/makestep.cpp
+++ b/src/plugins/projectexplorer/makestep.cpp
@@ -18,7 +18,7 @@
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
@@ -318,14 +318,15 @@ CommandLine MakeStep::effectiveMakeCommand(MakeCommandType type) const
QWidget *MakeStep::createConfigWidget()
{
Layouting::Form builder;
- builder.addRow(m_makeCommandAspect);
- builder.addRow(m_userArgumentsAspect);
+ builder.addRow({m_makeCommandAspect});
+ builder.addRow({m_userArgumentsAspect});
builder.addRow({m_userJobCountAspect, m_overrideMakeflagsAspect, m_nonOverrideWarning});
if (m_disablingForSubDirsSupported)
- builder.addRow(m_disabledForSubdirsAspect);
- builder.addRow(m_buildTargetsAspect);
+ builder.addRow({m_disabledForSubdirsAspect});
+ builder.addRow({m_buildTargetsAspect});
+ builder.addItem(Layouting::noMargin);
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ auto widget = builder.emerge();
VariableChooser::addSupportForChildWidgets(widget, macroExpander());
@@ -383,22 +384,6 @@ QWidget *MakeStep::createConfigWidget()
return widget;
}
-bool MakeStep::buildsTarget(const QString &target) const
-{
- return m_buildTargetsAspect->value().contains(target);
-}
-
-void MakeStep::setBuildTarget(const QString &target, bool on)
-{
- QStringList old = m_buildTargetsAspect->value();
- if (on && !old.contains(target))
- old << target;
- else if (!on && old.contains(target))
- old.removeOne(target);
-
- m_buildTargetsAspect->setValue(old);
-}
-
QStringList MakeStep::availableTargets() const
{
return m_buildTargetsAspect->allValues();
diff --git a/src/plugins/projectexplorer/makestep.h b/src/plugins/projectexplorer/makestep.h
index b08462db2f..73c4e9b7e7 100644
--- a/src/plugins/projectexplorer/makestep.h
+++ b/src/plugins/projectexplorer/makestep.h
@@ -55,11 +55,6 @@ public:
Utils::Environment makeEnvironment() const;
- // FIXME: All unused, remove in 4.15.
- void setBuildTarget(const QString &buildTarget) { setSelectedBuildTarget(buildTarget); }
- bool buildsTarget(const QString &target) const;
- void setBuildTarget(const QString &target, bool on);
-
protected:
void supportDisablingForSubdirs() { m_disablingForSubDirsSupported = true; }
virtual QStringList displayArguments() const;
diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
index b169697cd8..3da81e6154 100644
--- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp
+++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
@@ -13,8 +13,8 @@
#include "projectexplorerconstants.h"
#include "projectexplorericons.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runconfiguration.h"
-#include "session.h"
#include "target.h"
#include <utils/algorithm.h>
@@ -253,9 +253,9 @@ public:
explicit ProjectListView(QWidget *parent = nullptr) : SelectorView(parent)
{
const auto model = new GenericModel(this);
- model->rebuild(transform<QList<QObject *>>(SessionManager::projects(),
+ model->rebuild(transform<QList<QObject *>>(ProjectManager::projects(),
[](Project *p) { return p; }));
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this, model](Project *project) {
const GenericItem *projectItem = model->addItemForObject(project);
QFontMetrics fn(font());
@@ -264,7 +264,7 @@ public:
setOptimalWidth(width);
restoreCurrentIndex();
});
- connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
+ connect(ProjectManager::instance(), &ProjectManager::aboutToRemoveProject,
this, [this, model](const Project *project) {
GenericItem * const item = model->itemForObject(project);
if (!item)
@@ -272,7 +272,7 @@ public:
model->destroyItem(item);
resetOptimalWidth();
});
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, [this, model](const Project *project) {
const GenericItem * const item = model->itemForObject(project);
if (item)
@@ -288,7 +288,7 @@ public:
this, [model](const QModelIndex &index) {
const GenericItem * const item = model->itemForIndex(index);
if (item && item->object())
- SessionManager::setStartupProject(qobject_cast<Project *>(item->object()));
+ ProjectManager::setStartupProject(qobject_cast<Project *>(item->object()));
});
}
@@ -296,7 +296,7 @@ private:
void restoreCurrentIndex()
{
const GenericItem * const itemForStartupProject
- = theModel()->itemForObject(SessionManager::startupProject());
+ = theModel()->itemForObject(ProjectManager::startupProject());
if (itemForStartupProject)
setCurrentIndex(theModel()->indexForItem(itemForStartupProject));
}
@@ -551,6 +551,11 @@ int SelectorView::padding()
/////////
// KitAreaWidget
/////////
+void doLayout(KitAspectWidget *widget, Layouting::LayoutItem &builder)
+{
+ widget->addToLayout(builder);
+}
+
class KitAreaWidget : public QWidget
{
Q_OBJECT
@@ -573,18 +578,15 @@ public:
delete layout();
- Layouting::LayoutBuilder builder(Layouting::LayoutBuilder::GridLayout);
+ Layouting::Grid grid;
for (KitAspect *aspect : KitManager::kitAspects()) {
if (k && k->isMutable(aspect->id())) {
KitAspectWidget *widget = aspect->createConfigWidget(k);
m_widgets << widget;
- QLabel *label = new QLabel(aspect->displayName());
- builder.addItem(label);
- widget->addToLayout(builder);
- builder.finishRow();
+ grid.addItems({aspect->displayName(), widget, Layouting::br});
}
}
- builder.attachTo(this);
+ grid.attachTo(this);
layout()->setContentsMargins(3, 3, 3, 3);
m_kit = k;
@@ -653,7 +655,7 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
QWidget(parent),
m_projectAction(targetSelectorAction)
{
- setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(this);
setContentsMargins(QMargins(0, 1, 1, 8));
setWindowFlags(Qt::Popup);
@@ -696,22 +698,22 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
m_listWidgets[RUN]->viewport()->setAttribute(Qt::WA_Hover);
// Validate state: At this point the session is still empty!
- Project *startup = SessionManager::startupProject();
+ Project *startup = ProjectManager::startupProject();
QTC_CHECK(!startup);
- QTC_CHECK(SessionManager::projects().isEmpty());
+ QTC_CHECK(ProjectManager::projects().isEmpty());
connect(m_summaryLabel, &QLabel::linkActivated,
this, &MiniProjectTargetSelector::switchToProjectsMode);
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::startupProjectChanged,
+ ProjectManager *sessionManager = ProjectManager::instance();
+ connect(sessionManager, &ProjectManager::startupProjectChanged,
this, &MiniProjectTargetSelector::changeStartupProject);
- connect(sessionManager, &SessionManager::projectAdded,
+ connect(sessionManager, &ProjectManager::projectAdded,
this, &MiniProjectTargetSelector::projectAdded);
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
this, &MiniProjectTargetSelector::projectRemoved);
- connect(sessionManager, &SessionManager::projectDisplayNameChanged,
+ connect(sessionManager, &ProjectManager::projectDisplayNameChanged,
this, &MiniProjectTargetSelector::updateActionAndSummary);
// for icon changes:
@@ -720,17 +722,17 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
connect(m_listWidgets[TARGET], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
- SessionManager::setActiveTarget(m_project, static_cast<Target *>(pc), SetActive::Cascade);
+ m_project->setActiveTarget(static_cast<Target *>(pc), SetActive::Cascade);
});
connect(m_listWidgets[BUILD], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
- SessionManager::setActiveBuildConfiguration(m_project->activeTarget(),
- static_cast<BuildConfiguration *>(pc), SetActive::Cascade);
+ m_project->activeTarget()->setActiveBuildConfiguration(
+ static_cast<BuildConfiguration *>(pc), SetActive::Cascade);
});
connect(m_listWidgets[DEPLOY], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
- SessionManager::setActiveDeployConfiguration(m_project->activeTarget(),
- static_cast<DeployConfiguration *>(pc), SetActive::Cascade);
+ m_project->activeTarget()->setActiveDeployConfiguration(
+ static_cast<DeployConfiguration *>(pc), SetActive::Cascade);
});
connect(m_listWidgets[RUN], &GenericListWidget::changeActiveProjectConfiguration,
this, [this](QObject *pc) {
@@ -881,7 +883,7 @@ void MiniProjectTargetSelector::doLayout(bool keepSize)
onlySummary = true;
} else {
if (visibleLineCount < 3) {
- if (Utils::anyOf(SessionManager::projects(), &Project::needsConfiguration))
+ if (Utils::anyOf(ProjectManager::projects(), &Project::needsConfiguration))
visibleLineCount = 3;
}
if (visibleLineCount)
@@ -1126,7 +1128,7 @@ void MiniProjectTargetSelector::removedRunConfiguration(RunConfiguration *rc, bo
void MiniProjectTargetSelector::updateProjectListVisible()
{
- int count = SessionManager::projects().size();
+ int count = ProjectManager::projects().size();
bool visible = count > 1;
m_projectListWidget->setVisible(visible);
@@ -1139,7 +1141,7 @@ void MiniProjectTargetSelector::updateProjectListVisible()
void MiniProjectTargetSelector::updateTargetListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects())
+ for (Project *p : ProjectManager::projects())
maxCount = qMax(p->targets().size(), maxCount);
bool visible = maxCount > 1;
@@ -1152,7 +1154,7 @@ void MiniProjectTargetSelector::updateTargetListVisible()
void MiniProjectTargetSelector::updateBuildListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
const QList<Target *> targets = p->targets();
for (Target *t : targets)
maxCount = qMax(t->buildConfigurations().size(), maxCount);
@@ -1168,7 +1170,7 @@ void MiniProjectTargetSelector::updateBuildListVisible()
void MiniProjectTargetSelector::updateDeployListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
const QList<Target *> targets = p->targets();
for (Target *t : targets)
maxCount = qMax(t->deployConfigurations().size(), maxCount);
@@ -1184,7 +1186,7 @@ void MiniProjectTargetSelector::updateDeployListVisible()
void MiniProjectTargetSelector::updateRunListVisible()
{
int maxCount = 0;
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
const QList<Target *> targets = p->targets();
for (Target *t : targets)
maxCount = qMax(t->runConfigurations().size(), maxCount);
@@ -1460,10 +1462,10 @@ void MiniProjectTargetSelector::updateActionAndSummary()
? Icons::DESKTOP_DEVICE.icon()
: style()->standardIcon(QStyle::SP_ComputerIcon);
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (project) {
projectName = project->displayName();
- for (Project *p : SessionManager::projects()) {
+ for (Project *p : ProjectManager::projects()) {
if (p != project && p->displayName() == projectName) {
fileName = project->projectFilePath().toUserOutput();
break;
@@ -1515,7 +1517,7 @@ void MiniProjectTargetSelector::updateActionAndSummary()
void MiniProjectTargetSelector::updateSummary()
{
QString summary;
- if (Project *startupProject = SessionManager::startupProject()) {
+ if (Project *startupProject = ProjectManager::startupProject()) {
if (!m_projectListWidget->isVisibleTo(this))
summary.append(Tr::tr("Project: <b>%1</b><br/>").arg(startupProject->displayName()));
if (Target *activeTarget = startupProject->activeTarget()) {
diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp
index d0856e29d4..d4d518de44 100644
--- a/src/plugins/projectexplorer/msvctoolchain.cpp
+++ b/src/plugins/projectexplorer/msvctoolchain.cpp
@@ -3,6 +3,7 @@
#include "msvctoolchain.h"
+#include "devicesupport/idevice.h"
#include "gcctoolchain.h"
#include "msvcparser.h"
#include "projectexplorer.h"
@@ -14,12 +15,12 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <utils/winutils.h>
@@ -254,7 +255,7 @@ static std::optional<VisualStudioInstallation> detectCppBuildTools2017()
static QVector<VisualStudioInstallation> detectVisualStudioFromVsWhere(const QString &vswhere)
{
QVector<VisualStudioInstallation> installations;
- QtcProcess vsWhereProcess;
+ Process vsWhereProcess;
vsWhereProcess.setCodec(QTextCodec::codecForName("UTF-8"));
const int timeoutS = 5;
vsWhereProcess.setTimeoutS(timeoutS);
@@ -648,7 +649,7 @@ Macros MsvcToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
return predefinedMacros;
}
- Utils::QtcProcess cpp;
+ Utils::Process cpp;
cpp.setEnvironment(env);
cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath());
QStringList arguments;
@@ -747,10 +748,8 @@ static QString winExpandDelayedEnvReferences(QString in, const Utils::Environmen
return in;
}
-void MsvcToolChain::environmentModifications(
- QFutureInterface<MsvcToolChain::GenerateEnvResult> &future,
- QString vcvarsBat,
- QString varsBatArg)
+void MsvcToolChain::environmentModifications(QPromise<MsvcToolChain::GenerateEnvResult> &promise,
+ QString vcvarsBat, QString varsBatArg)
{
const Utils::Environment inEnv = Utils::Environment::systemEnvironment();
Utils::Environment outEnv;
@@ -776,7 +775,7 @@ void MsvcToolChain::environmentModifications(
}
}
- future.reportResult({error, diff});
+ promise.addResult(MsvcToolChain::GenerateEnvResult{error, diff});
}
void MsvcToolChain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future)
@@ -1004,10 +1003,8 @@ bool MsvcToolChain::fromMap(const QVariantMap &data)
data.value(QLatin1String(environModsKeyC)).toList());
rescanForCompiler();
- initEnvModWatcher(Utils::runAsync(envModThreadPool(),
- &MsvcToolChain::environmentModifications,
- m_vcvarsBat,
- m_varsBatArg));
+ initEnvModWatcher(Utils::asyncRun(envModThreadPool(), &MsvcToolChain::environmentModifications,
+ m_vcvarsBat, m_varsBatArg));
const bool valid = !m_vcvarsBat.isEmpty() && targetAbi().isValid();
if (!valid)
@@ -1128,8 +1125,8 @@ WarningFlags MsvcToolChain::warningFlags(const QStringList &cflags) const
return flags;
}
-QStringList MsvcToolChain::includedFiles(const QStringList &flags,
- const QString &directoryPath) const
+FilePaths MsvcToolChain::includedFiles(const QStringList &flags,
+ const FilePath &directoryPath) const
{
return ToolChain::includedFiles("/FI", flags, directoryPath, PossiblyConcatenatedFlag::Yes);
}
@@ -1236,10 +1233,8 @@ void MsvcToolChain::setupVarsBat(const Abi &abi, const QString &varsBat, const Q
m_varsBatArg = varsBatArg;
if (!varsBat.isEmpty()) {
- initEnvModWatcher(Utils::runAsync(envModThreadPool(),
- &MsvcToolChain::environmentModifications,
- varsBat,
- varsBatArg));
+ initEnvModWatcher(Utils::asyncRun(envModThreadPool(),
+ &MsvcToolChain::environmentModifications, varsBat, varsBatArg));
}
}
@@ -1560,7 +1555,7 @@ static QVersionNumber clangClVersion(const FilePath &clangClPath)
if (!dllversion.isEmpty())
return QVersionNumber::fromString(dllversion);
- QtcProcess clangClProcess;
+ Process clangClProcess;
clangClProcess.setCommand({clangClPath, {"--version"}});
clangClProcess.runBlocking();
if (clangClProcess.result() != ProcessResult::FinishedWithSuccess)
@@ -1777,7 +1772,7 @@ Macros ClangClToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
if (!cxxflags.contains("--driver-mode=g++"))
return MsvcToolChain::msvcPredefinedMacros(cxxflags, env);
- QtcProcess cpp;
+ Process cpp;
cpp.setEnvironment(env);
cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryFilePath());
@@ -1915,7 +1910,7 @@ static void detectCppBuildTools2015(Toolchains *list)
Toolchains MsvcToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
- if (!detector.device.isNull()) {
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
// FIXME currently no support for msvc toolchains on a device
return {};
}
@@ -2030,7 +2025,7 @@ bool ClangClToolChainFactory::canCreate() const
Toolchains ClangClToolChainFactory::autoDetect(const ToolchainDetector &detector) const
{
- if (!detector.device.isNull()) {
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
// FIXME currently no support for msvc toolchains on a device
return {};
}
@@ -2127,7 +2122,7 @@ std::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::E
return QString();
}
- Utils::QtcProcess run;
+ Utils::Process run;
// As of WinSDK 7.1, there is logic preventing the path from being set
// correctly if "ORIGINALPATH" is already set. That can cause problems
diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h
index 042bbfe38f..68f8b9dcf1 100644
--- a/src/plugins/projectexplorer/msvctoolchain.h
+++ b/src/plugins/projectexplorer/msvctoolchain.h
@@ -55,8 +55,8 @@ public:
MacroInspectionRunner createMacroInspectionRunner() const override;
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
Utils::WarningFlags warningFlags(const QStringList &cflags) const override;
- QStringList includedFiles(const QStringList &flags,
- const QString &directoryPath) const override;
+ Utils::FilePaths includedFiles(const QStringList &flags,
+ const Utils::FilePath &directoryPath) const override;
BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(
const Utils::Environment &env) const override;
void addToEnvironment(Utils::Environment &env) const override;
@@ -81,6 +81,8 @@ public:
const QString &batchFile,
const QString &batchArgs,
QMap<QString, QString> &envPairs);
+ bool environmentInitialized() const { return !m_environmentModifications.isEmpty(); }
+
protected:
class WarningFlagAdder
{
@@ -111,9 +113,8 @@ protected:
std::optional<QString> error;
Utils::EnvironmentItems environmentItems;
};
- static void environmentModifications(QFutureInterface<GenerateEnvResult> &future,
- QString vcvarsBat,
- QString varsBatArg);
+ static void environmentModifications(QPromise<GenerateEnvResult> &future,
+ QString vcvarsBat, QString varsBatArg);
void initEnvModWatcher(const QFuture<GenerateEnvResult> &future);
protected:
diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp
index 49b50dccb3..0b3d6d83c7 100644
--- a/src/plugins/projectexplorer/processparameters.cpp
+++ b/src/plugins/projectexplorer/processparameters.cpp
@@ -5,7 +5,7 @@
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/theme/theme.h>
#include <utils/utilstr.h>
diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp
index fbd4f9e28f..3b32e3c265 100644
--- a/src/plugins/projectexplorer/project.cpp
+++ b/src/plugins/projectexplorer/project.cpp
@@ -11,15 +11,17 @@
#include "environmentaspect.h"
#include "kit.h"
#include "kitinformation.h"
+#include "msvctoolchain.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "runconfiguration.h"
#include "runconfigurationaspects.h"
-#include "session.h"
#include "target.h"
#include "taskhub.h"
+#include "toolchainmanager.h"
#include "userfileaccessor.h"
#include <coreplugin/idocument.h>
@@ -273,7 +275,7 @@ void Project::addTarget(std::unique_ptr<Target> &&t)
// check activeTarget:
if (!activeTarget())
- SessionManager::setActiveTarget(this, pointer, SetActive::Cascade);
+ setActiveTarget(pointer, SetActive::Cascade);
}
Target *Project::addTargetForDefaultKit()
@@ -309,7 +311,7 @@ bool Project::removeTarget(Target *target)
auto keep = take(d->m_targets, target);
if (target == d->m_activeTarget) {
Target *newActiveTarget = (d->m_targets.size() == 0 ? nullptr : d->m_targets.at(0).get());
- SessionManager::setActiveTarget(this, newActiveTarget, SetActive::Cascade);
+ setActiveTarget(newActiveTarget, SetActive::Cascade);
}
emit removedTarget(target);
@@ -326,7 +328,7 @@ Target *Project::activeTarget() const
return d->m_activeTarget;
}
-void Project::setActiveTarget(Target *target)
+void Project::setActiveTargetHelper(Target *target)
{
if (d->m_activeTarget == target)
return;
@@ -414,6 +416,29 @@ Target *Project::target(Kit *k) const
return findOrDefault(d->m_targets, equal(&Target::kit, k));
}
+void Project::setActiveTarget(Target *target, SetActive cascade)
+{
+ if (isShuttingDown())
+ return;
+
+ setActiveTargetHelper(target);
+
+ if (!target) // never cascade setting no target
+ return;
+
+ if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading())
+ return;
+
+ Utils::Id kitId = target->kit()->id();
+ for (Project *otherProject : ProjectManager::projects()) {
+ if (otherProject == this)
+ continue;
+ if (Target *otherTarget = Utils::findOrDefault(otherProject->targets(),
+ [kitId](Target *t) { return t->kit()->id() == kitId; }))
+ otherProject->setActiveTargetHelper(otherTarget);
+ }
+}
+
Tasks Project::projectIssues(const Kit *k) const
{
Tasks result;
@@ -445,12 +470,12 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget)
sourceBc->buildSystem()->name()));
newTarget->addBuildConfiguration(newBc);
if (sourceTarget->activeBuildConfiguration() == sourceBc)
- SessionManager::setActiveBuildConfiguration(newTarget, newBc, SetActive::NoCascade);
+ newTarget->setActiveBuildConfiguration(newBc, SetActive::NoCascade);
}
if (!newTarget->activeBuildConfiguration()) {
QList<BuildConfiguration *> bcs = newTarget->buildConfigurations();
if (!bcs.isEmpty())
- SessionManager::setActiveBuildConfiguration(newTarget, bcs.first(), SetActive::NoCascade);
+ newTarget->setActiveBuildConfiguration(bcs.first(), SetActive::NoCascade);
}
for (DeployConfiguration *sourceDc : sourceTarget->deployConfigurations()) {
@@ -462,12 +487,12 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget)
newDc->setDisplayName(sourceDc->displayName());
newTarget->addDeployConfiguration(newDc);
if (sourceTarget->activeDeployConfiguration() == sourceDc)
- SessionManager::setActiveDeployConfiguration(newTarget, newDc, SetActive::NoCascade);
+ newTarget->setActiveDeployConfiguration(newDc, SetActive::NoCascade);
}
if (!newTarget->activeBuildConfiguration()) {
QList<DeployConfiguration *> dcs = newTarget->deployConfigurations();
if (!dcs.isEmpty())
- SessionManager::setActiveDeployConfiguration(newTarget, dcs.first(), SetActive::NoCascade);
+ newTarget->setActiveDeployConfiguration(dcs.first(), SetActive::NoCascade);
}
for (RunConfiguration *sourceRc : sourceTarget->runConfigurations()) {
@@ -846,6 +871,34 @@ const Node *Project::nodeForFilePath(const FilePath &filePath,
return nullptr;
}
+FilePaths Project::binariesForSourceFile(const FilePath &sourceFile) const
+{
+ if (!rootProjectNode())
+ return {};
+ const QList<Node *> fileNodes = rootProjectNode()->findNodes([&sourceFile](Node *n) {
+ return n->filePath() == sourceFile;
+ });
+ FilePaths binaries;
+ for (const Node * const fileNode : fileNodes) {
+ for (ProjectNode *projectNode = fileNode->parentProjectNode(); projectNode;
+ projectNode = projectNode->parentProjectNode()) {
+ if (!projectNode->isProduct())
+ continue;
+ if (projectNode->productType() == ProductType::App
+ || projectNode->productType() == ProductType::Lib) {
+ const QList<Node *> binaryNodes = projectNode->findNodes([](Node *n) {
+ return n->asFileNode() && (n->asFileNode()->fileType() == FileType::App
+ || n->asFileNode()->fileType() == FileType::Lib);
+
+ });
+ binaries << Utils::transform(binaryNodes, &Node::filePath);
+ }
+ break;
+ }
+ }
+ return binaries;
+}
+
void Project::setProjectLanguages(Context language)
{
if (d->m_projectLanguages == language)
@@ -1435,8 +1488,7 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
Target * const target = theProject.project()->activeTarget();
QVERIFY(target);
QCOMPARE(target->buildConfigurations().size(), 6);
- SessionManager::setActiveBuildConfiguration(target, target->buildConfigurations().at(1),
- SetActive::Cascade);
+ target->setActiveBuildConfiguration(target->buildConfigurations().at(1), SetActive::Cascade);
BuildSystem * const bs = theProject.project()->activeTarget()->buildSystem();
QVERIFY(bs);
QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem());
@@ -1452,12 +1504,94 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
}
QVERIFY(!bs->isWaitingForParse() && !bs->isParsing());
- QCOMPARE(SessionManager::startupProject(), theProject.project());
+ QCOMPARE(ProjectManager::startupProject(), theProject.project());
QCOMPARE(ProjectTree::currentProject(), theProject.project());
QVERIFY(EditorManager::openEditor(projectDir.pathAppended("main.cpp")));
QVERIFY(ProjectTree::currentNode());
ProjectTree::instance()->expandAll();
- SessionManager::closeAllProjects(); // QTCREATORBUG-25655
+ ProjectManager::closeAllProjects(); // QTCREATORBUG-25655
+}
+
+void ProjectExplorerPlugin::testSourceToBinaryMapping()
+{
+ // Find suitable kit.
+ Kit * const kit = findOr(KitManager::kits(), nullptr, [](const Kit *k) {
+ return k->isValid() && ToolChainKitAspect::cxxToolChain(k);
+ });
+ if (!kit)
+ QSKIP("The test requires at least one kit with a toolchain.");
+
+ const auto toolchain = ToolChainKitAspect::cxxToolChain(kit);
+ QVERIFY(toolchain);
+ if (const auto msvcToolchain = dynamic_cast<Internal::MsvcToolChain *>(toolchain)) {
+ while (!msvcToolchain->environmentInitialized()) {
+ QSignalSpy parsingFinishedSpy(ToolChainManager::instance(),
+ &ToolChainManager::toolChainUpdated);
+ QVERIFY(parsingFinishedSpy.wait(10000));
+ }
+ }
+
+ // Copy project from qrc.
+ QTemporaryDir * const tempDir = TemporaryDirectory::masterTemporaryDirectory();
+ QVERIFY(tempDir->isValid());
+ const FilePath projectDir = FilePath::fromString(tempDir->path() + "/multi-target-project");
+ if (!projectDir.exists()) {
+ const auto result = FilePath(":/projectexplorer/testdata/multi-target-project")
+ .copyRecursively(projectDir);
+ QVERIFY2(result, qPrintable(result.error()));
+ const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files);
+ for (const QFileInfo &f : files)
+ QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser);
+ }
+
+ // Load Project.
+ QFETCH(QString, projectFileName);
+ const auto theProject = openProject(projectDir.pathAppended(projectFileName));
+ if (theProject.errorMessage().contains("text/")) {
+ QSKIP("This test requires the presence of the qmake/cmake/qbs project managers "
+ "to be fully functional");
+ }
+
+ QVERIFY2(theProject, qPrintable(theProject.errorMessage()));
+ theProject.project()->configureAsExampleProject(kit);
+ QCOMPARE(theProject.project()->targets().size(), 1);
+ Target * const target = theProject.project()->activeTarget();
+ QVERIFY(target);
+ BuildSystem * const bs = target->buildSystem();
+ QVERIFY(bs);
+ QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem());
+ if (bs->isWaitingForParse() || bs->isParsing()) {
+ QSignalSpy parsingFinishedSpy(bs, &BuildSystem::parsingFinished);
+ QVERIFY(parsingFinishedSpy.wait(10000));
+ }
+ QVERIFY(!bs->isWaitingForParse() && !bs->isParsing());
+
+ if (QLatin1String(QTest::currentDataTag()) == QLatin1String("qbs")) {
+ BuildManager::buildProjectWithoutDependencies(theProject.project());
+ if (BuildManager::isBuilding()) {
+ QSignalSpy buildingFinishedSpy(BuildManager::instance(), &BuildManager::buildQueueFinished);
+ QVERIFY(buildingFinishedSpy.wait(10000));
+ }
+ QVERIFY(!BuildManager::isBuilding());
+ QSignalSpy projectUpdateSpy(theProject.project(), &Project::fileListChanged);
+ QVERIFY(projectUpdateSpy.wait(5000));
+ }
+
+ // Check mapping
+ const auto binariesForSource = [&](const QString &fileName) {
+ return theProject.project()->binariesForSourceFile(projectDir.pathAppended(fileName));
+ };
+ QCOMPARE(binariesForSource("multi-target-project-main.cpp").size(), 1);
+ QCOMPARE(binariesForSource("multi-target-project-lib.cpp").size(), 1);
+ QCOMPARE(binariesForSource("multi-target-project-shared.h").size(), 2);
+}
+
+void ProjectExplorerPlugin::testSourceToBinaryMapping_data()
+{
+ QTest::addColumn<QString>("projectFileName");
+ QTest::addRow("cmake") << "CMakeLists.txt";
+ QTest::addRow("qbs") << "multi-target-project.qbs";
+ QTest::addRow("qmake") << "multi-target-project.pro";
}
#endif // WITH_TESTS
diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h
index ed336b5f7f..d753048aff 100644
--- a/src/plugins/projectexplorer/project.h
+++ b/src/plugins/projectexplorer/project.h
@@ -36,6 +36,7 @@ class ProjectImporter;
class ProjectNode;
class ProjectPrivate;
class Target;
+enum class SetActive : int;
// Documentation inside.
class PROJECTEXPLORER_EXPORT Project : public QObject
@@ -89,6 +90,8 @@ public:
Target *activeTarget() const;
Target *target(Utils::Id id) const;
Target *target(Kit *k) const;
+ void setActiveTarget(Target *target, SetActive cascade);
+
virtual Tasks projectIssues(const Kit *k) const;
static bool copySteps(Target *sourceTarget, Target *newTarget);
@@ -107,6 +110,7 @@ public:
bool isKnownFile(const Utils::FilePath &filename) const;
const Node *nodeForFilePath(const Utils::FilePath &filePath,
const NodeMatcher &extraMatcher = {}) const;
+ Utils::FilePaths binariesForSourceFile(const Utils::FilePath &sourceFile) const;
virtual QVariantMap toMap() const;
@@ -226,7 +230,7 @@ private:
void removeProjectLanguage(Utils::Id id);
void handleSubTreeChanged(FolderNode *node);
- void setActiveTarget(Target *target);
+ void setActiveTargetHelper(Target *target);
friend class ContainerNode;
ProjectPrivate *d;
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index f76f10175b..1244dbd1ba 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -8,6 +8,7 @@
#include "buildsystem.h"
#include "compileoutputwindow.h"
#include "configtaskhandler.h"
+#include "copystep.h"
#include "customexecutablerunconfiguration.h"
#include "customparserssettingspage.h"
#include "customwizard/customwizard.h"
@@ -34,11 +35,13 @@
#include "dependenciespanel.h"
#include "devicesupport/desktopdevice.h"
#include "devicesupport/desktopdevicefactory.h"
+#include "devicesupport/devicecheckbuildstep.h"
#include "devicesupport/devicemanager.h"
#include "devicesupport/devicesettingspage.h"
#include "devicesupport/sshsettings.h"
#include "devicesupport/sshsettingspage.h"
#include "editorsettingspropertiespage.h"
+#include "environmentaspect.h"
#include "filesinallprojectsfind.h"
#include "jsonwizard/jsonwizardfactory.h"
#include "jsonwizard/jsonwizardgeneratorfactory.h"
@@ -63,10 +66,10 @@
#include "projecttreewidget.h"
#include "projectwindow.h"
#include "removetaskhandler.h"
-#include "runconfigurationaspects.h"
#include "sanitizerparser.h"
#include "selectablefilesmodel.h"
#include "session.h"
+#include "session_p.h"
#include "sessiondialog.h"
#include "showineditortaskhandler.h"
#include "simpleprojectwizard.h"
@@ -127,6 +130,7 @@
#include <utils/qtcassert.h>
#include <utils/removefiledialog.h>
#include <utils/stringutils.h>
+#include <utils/terminalhooks.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
@@ -254,7 +258,6 @@ const char BUILD_BEFORE_DEPLOY_SETTINGS_KEY[] = "ProjectExplorer/Settings/BuildB
const char DEPLOY_BEFORE_RUN_SETTINGS_KEY[] = "ProjectExplorer/Settings/DeployBeforeRun";
const char SAVE_BEFORE_BUILD_SETTINGS_KEY[] = "ProjectExplorer/Settings/SaveBeforeBuild";
const char USE_JOM_SETTINGS_KEY[] = "ProjectExplorer/Settings/UseJom";
-const char AUTO_RESTORE_SESSION_SETTINGS_KEY[] = "ProjectExplorer/Settings/AutoRestoreLastSession";
const char ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY[] =
"ProjectExplorer/Settings/AddLibraryPathsToRunEnv";
const char PROMPT_TO_STOP_RUN_CONTROL_SETTINGS_KEY[] =
@@ -303,12 +306,12 @@ static const RunConfiguration *runConfigForNode(const Target *target, const Proj
static bool hideBuildMenu()
{
- return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_BUILD, false).toBool();
+ return ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_BUILD, false).toBool();
}
static bool hideDebugMenu()
{
- return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_DEBUG, false).toBool();
+ return ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_DEBUG, false).toBool();
}
static bool canOpenTerminalWithRunEnv(const Project *project, const ProjectNode *node)
@@ -337,7 +340,7 @@ static BuildConfiguration *currentBuildConfiguration()
static Target *activeTarget()
{
- const Project * const project = SessionManager::startupProject();
+ const Project * const project = ProjectManager::startupProject();
return project ? project->activeTarget() : nullptr;
}
@@ -405,40 +408,22 @@ protected:
void restoreState(const QJsonObject &object) override;
};
-class RunConfigurationLocatorFilter : public Core::ILocatorFilter
+class RunConfigurationStartFilter final : public ILocatorFilter
{
public:
- RunConfigurationLocatorFilter();
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
+ RunConfigurationStartFilter();
private:
- void targetListUpdated();
- QList<Core::LocatorFilterEntry> m_result;
-};
-
-class RunRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter
-{
-public:
- RunRunConfigurationLocatorFilter();
-
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+ Core::LocatorMatcherTasks matchers() final;
};
-class SwitchToRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter
+class RunConfigurationSwitchFilter final : public ILocatorFilter
{
public:
- SwitchToRunConfigurationLocatorFilter();
+ RunConfigurationSwitchFilter();
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText,
- int *selectionStart,
- int *selectionLength) const final;
+private:
+ Core::LocatorMatcherTasks matchers() final;
};
class ProjectExplorerPluginPrivate : public QObject
@@ -472,8 +457,6 @@ public:
void updateSessionMenu();
void setSession(QAction *action);
- void determineSessionToRestoreAtStartup();
- void restoreSession();
void runProjectContextMenu(RunConfiguration *rc);
void savePersistentSettings();
@@ -492,7 +475,7 @@ public:
void deleteFile();
void handleRenameFile();
void handleSetStartupProject();
- void setStartupProject(ProjectExplorer::Project *project);
+ void setStartupProject(Project *project);
bool closeAllFilesInProject(const Project *project);
void updateRecentProjectMenu();
@@ -504,11 +487,11 @@ public:
void openTerminalHere(const EnvironmentGetter &env);
void openTerminalHereWithRunEnv();
- void invalidateProject(ProjectExplorer::Project *project);
+ void invalidateProject(Project *project);
- void projectAdded(ProjectExplorer::Project *pro);
- void projectRemoved(ProjectExplorer::Project *pro);
- void projectDisplayNameChanged(ProjectExplorer::Project *pro);
+ void projectAdded(Project *pro);
+ void projectRemoved(Project *pro);
+ void projectDisplayNameChanged(Project *pro);
void doUpdateRunActions();
@@ -599,7 +582,6 @@ public:
QAction *m_runSubProject;
ProjectWindow *m_proWindow = nullptr;
- QString m_sessionToRestoreAtStartup;
QStringList m_profileMimeTypes;
int m_activeRunControlCount = 0;
@@ -622,7 +604,6 @@ public:
Id m_runMode = Constants::NO_RUN_MODE;
ToolChainManager *m_toolChainManager = nullptr;
- QStringList m_arguments;
#ifdef WITH_JOURNALD
JournaldWatcher m_journalWatcher;
@@ -667,7 +648,8 @@ public:
RemoveTaskHandler m_removeTaskHandler;
ConfigTaskHandler m_configTaskHandler{Task::compilerMissingTask(), Constants::KITS_SETTINGS_PAGE_ID};
- SessionManager m_sessionManager;
+ SessionManager m_sessionBase;
+ ProjectManager m_sessionManager;
AppOutputPane m_outputPane;
ProjectTree m_projectTree;
@@ -675,9 +657,11 @@ public:
AllProjectsFilter m_allProjectsFilter;
CurrentProjectFilter m_currentProjectFilter;
AllProjectFilesFilter m_allProjectDirectoriesFilter;
- RunRunConfigurationLocatorFilter m_runConfigurationLocatorFilter;
- SwitchToRunConfigurationLocatorFilter m_switchRunConfigurationLocatorFilter;
+ RunConfigurationStartFilter m_runConfigurationStartFilter;
+ RunConfigurationSwitchFilter m_runConfigurationSwitchFilter;
+ CopyFileStepFactory m_copyFileStepFactory;
+ CopyDirectoryStepFactory m_copyDirectoryFactory;
ProcessStepFactory m_processStepFactory;
AllProjectsFind m_allProjectsFind;
@@ -691,7 +675,6 @@ public:
// Settings pages
ProjectExplorerSettingsPage m_projectExplorerSettingsPage;
- BuildPropertiesSettingsPage m_buildPropertiesSettingsPage{&m_buildPropertiesSettings};
AppOutputSettingsPage m_appOutputSettingsPage;
CompileOutputSettingsPage m_compileOutputSettingsPage;
DeviceSettingsPage m_deviceSettingsPage;
@@ -721,6 +704,7 @@ public:
cmakeRunConfigFactory.runConfigurationId()
}};
+ DeviceCheckBuildStepFactory deviceCheckBuildStepFactory;
SanitizerOutputFormatterFactory sanitizerFormatterFactory;
};
@@ -743,7 +727,7 @@ static void openProjectsInDirectory(const FilePath &filePath)
{
const FilePaths projectFiles = projectsInDirectory(filePath);
if (!projectFiles.isEmpty())
- Core::ICore::openFiles(projectFiles);
+ ICore::openFiles(projectFiles);
}
static QStringList projectNames(const QVector<FolderNode *> &folders)
@@ -766,7 +750,7 @@ static QVector<FolderNode *> renamableFolderNodes(const FilePath &before, const
return folderNodes;
}
-static QVector<FolderNode *> removableFolderNodes(const Utils::FilePath &filePath)
+static QVector<FolderNode *> removableFolderNodes(const FilePath &filePath)
{
QVector<FolderNode *> folderNodes;
ProjectTree::forEachNode([&](Node *node) {
@@ -834,38 +818,28 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(&dd->m_welcomePage, &ProjectWelcomePage::manageSessions,
dd, &ProjectExplorerPluginPrivate::showSessionManager);
- SessionManager *sessionManager = &dd->m_sessionManager;
- connect(sessionManager, &SessionManager::projectAdded,
+ ProjectManager *sessionManager = &dd->m_sessionManager;
+ connect(sessionManager, &ProjectManager::projectAdded,
this, &ProjectExplorerPlugin::fileListChanged);
- connect(sessionManager, &SessionManager::aboutToRemoveProject,
+ connect(sessionManager, &ProjectManager::aboutToRemoveProject,
dd, &ProjectExplorerPluginPrivate::invalidateProject);
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
this, &ProjectExplorerPlugin::fileListChanged);
- connect(sessionManager, &SessionManager::projectAdded,
+ connect(sessionManager, &ProjectManager::projectAdded,
dd, &ProjectExplorerPluginPrivate::projectAdded);
- connect(sessionManager, &SessionManager::projectRemoved,
+ connect(sessionManager, &ProjectManager::projectRemoved,
dd, &ProjectExplorerPluginPrivate::projectRemoved);
- connect(sessionManager, &SessionManager::projectDisplayNameChanged,
+ connect(sessionManager, &ProjectManager::projectDisplayNameChanged,
dd, &ProjectExplorerPluginPrivate::projectDisplayNameChanged);
- connect(sessionManager, &SessionManager::dependencyChanged,
+ connect(sessionManager, &ProjectManager::dependencyChanged,
dd, &ProjectExplorerPluginPrivate::updateActions);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
dd, &ProjectExplorerPluginPrivate::updateActions);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
dd, &ProjectExplorerPluginPrivate::updateWelcomePage);
- connect(sessionManager, &SessionManager::sessionLoaded,
+ connect(SessionManager::instance(), &SessionManager::sessionLoaded,
dd, &ProjectExplorerPluginPrivate::loadSesssionTasks);
- connect(sessionManager, &SessionManager::projectAdded, dd, [](ProjectExplorer::Project *project) {
- dd->m_allProjectDirectoriesFilter.addDirectory(project->projectDirectory());
- });
- connect(sessionManager,
- &SessionManager::projectRemoved,
- dd,
- [](ProjectExplorer::Project *project) {
- dd->m_allProjectDirectoriesFilter.removeDirectory(project->projectDirectory());
- });
-
ProjectTree *tree = &dd->m_projectTree;
connect(tree, &ProjectTree::currentProjectChanged, dd, [] {
dd->updateContextMenuActions(ProjectTree::currentNode());
@@ -903,7 +877,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ICore::addPreCloseListener([]() -> bool { return coreAboutToClose(); });
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
&dd->m_outputPane, &AppOutputPane::projectRemoved);
// ProjectPanelFactories
@@ -1345,7 +1319,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
// without a project loaded.
connect(generatorContainer->menu(), &QMenu::aboutToShow, [menu = generatorContainer->menu()] {
menu->clear();
- if (Project * const project = SessionManager::startupProject()) {
+ if (Project * const project = ProjectManager::startupProject()) {
for (const auto &generator : project->allGenerators()) {
menu->addAction(generator.second, [project, id = generator.first] {
project->runGenerator(id);
@@ -1620,7 +1594,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
Command * const expandCmd = ActionManager::registerAction(
dd->m_projectTreeExpandAllAction, Constants::PROJECTTREE_EXPAND_ALL,
projectTreeContext);
- for (Core::ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu,
+ for (ActionContainer * const ac : {mfileContextMenu, msubProjectContextMenu,
mfolderContextMenu, mprojectContextMenu, msessionContextMenu}) {
ac->addSeparator(treeGroup);
ac->addAction(expandNodeCmd, treeGroup);
@@ -1659,7 +1633,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
dd, &ProjectExplorerPluginPrivate::savePersistentSettings);
connect(EditorManager::instance(), &EditorManager::autoSaved, this, [] {
if (!dd->m_shuttingDown && !SessionManager::loadingSession())
- SessionManager::save();
+ ProjectManager::save();
});
connect(qApp, &QApplication::applicationStateChanged, this, [](Qt::ApplicationState state) {
if (!dd->m_shuttingDown && state == Qt::ApplicationActive)
@@ -1697,10 +1671,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
.toBool();
dd->m_projectExplorerSettings.useJom
= s->value(Constants::USE_JOM_SETTINGS_KEY, defaultSettings.useJom).toBool();
- dd->m_projectExplorerSettings.autorestoreLastSession
- = s->value(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY,
- defaultSettings.autorestoreLastSession)
- .toBool();
dd->m_projectExplorerSettings.addLibraryPathsToRunEnv
= s->value(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY,
defaultSettings.addLibraryPathsToRunEnv)
@@ -1723,7 +1693,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
if (tmp < 0 || tmp > int(StopBeforeBuild::SameApp))
tmp = int(defaultSettings.stopBeforeBuild);
dd->m_projectExplorerSettings.stopBeforeBuild = StopBeforeBuild(tmp);
- dd->m_projectExplorerSettings.terminalMode = static_cast<ProjectExplorer::TerminalMode>(
+ dd->m_projectExplorerSettings.terminalMode = static_cast<TerminalMode>(
s->value(Constants::TERMINAL_MODE_SETTINGS_KEY, int(defaultSettings.terminalMode)).toInt());
dd->m_projectExplorerSettings.closeSourceFilesWithProject
= s->value(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY,
@@ -1742,6 +1712,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
.toBool();
dd->m_buildPropertiesSettings.readSettings(s);
+ sb_d->restoreSettings();
const int customParserCount = s->value(Constants::CUSTOM_PARSER_COUNT_KEY).toInt();
for (int i = 0; i < customParserCount; ++i) {
@@ -1764,20 +1735,20 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(dd->m_loadAction, &QAction::triggered,
dd, &ProjectExplorerPluginPrivate::loadAction);
connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjectWithoutDependencies(SessionManager::startupProject());
+ BuildManager::buildProjectWithoutDependencies(ProjectManager::startupProject());
});
connect(dd->m_buildAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjectWithDependencies(SessionManager::startupProject());
+ BuildManager::buildProjectWithDependencies(ProjectManager::startupProject());
});
connect(dd->m_buildProjectForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::buildProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::All);
});
connect(dd->m_buildActionContextMenu, &QAction::triggered, dd, [] {
BuildManager::buildProjectWithoutDependencies(ProjectTree::currentProject());
});
connect(dd->m_buildForRunConfigAction, &QAction::triggered, dd, [] {
- const Project * const project = SessionManager::startupProject();
+ const Project * const project = ProjectManager::startupProject();
QTC_ASSERT(project, return);
const Target * const target = project->activeTarget();
QTC_ASSERT(target, return);
@@ -1792,20 +1763,20 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
BuildManager::buildProjectWithDependencies(ProjectTree::currentProject());
});
connect(dd->m_buildSessionAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::Active);
+ BuildManager::buildProjects(ProjectManager::projectOrder(), ConfigSelection::Active);
});
connect(dd->m_buildSessionForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::buildProjects(SessionManager::projectOrder(), ConfigSelection::All);
+ BuildManager::buildProjects(ProjectManager::projectOrder(), ConfigSelection::All);
});
connect(dd->m_rebuildProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjectWithoutDependencies(SessionManager::startupProject());
+ BuildManager::rebuildProjectWithoutDependencies(ProjectManager::startupProject());
});
connect(dd->m_rebuildAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::rebuildProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::Active);
});
connect(dd->m_rebuildProjectForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::rebuildProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::All);
});
connect(dd->m_rebuildActionContextMenu, &QAction::triggered, dd, [] {
@@ -1816,32 +1787,32 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ConfigSelection::Active);
});
connect(dd->m_rebuildSessionAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::Active);
+ BuildManager::rebuildProjects(ProjectManager::projectOrder(), ConfigSelection::Active);
});
connect(dd->m_rebuildSessionForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::rebuildProjects(SessionManager::projectOrder(), ConfigSelection::All);
+ BuildManager::rebuildProjects(ProjectManager::projectOrder(), ConfigSelection::All);
});
connect(dd->m_deployProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::deployProjects({SessionManager::startupProject()});
+ BuildManager::deployProjects({ProjectManager::startupProject()});
});
connect(dd->m_deployAction, &QAction::triggered, dd, [] {
- BuildManager::deployProjects(SessionManager::projectOrder(SessionManager::startupProject()));
+ BuildManager::deployProjects(ProjectManager::projectOrder(ProjectManager::startupProject()));
});
connect(dd->m_deployActionContextMenu, &QAction::triggered, dd, [] {
BuildManager::deployProjects({ProjectTree::currentProject()});
});
connect(dd->m_deploySessionAction, &QAction::triggered, dd, [] {
- BuildManager::deployProjects(SessionManager::projectOrder());
+ BuildManager::deployProjects(ProjectManager::projectOrder());
});
connect(dd->m_cleanProjectOnlyAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjectWithoutDependencies(SessionManager::startupProject());
+ BuildManager::cleanProjectWithoutDependencies(ProjectManager::startupProject());
});
connect(dd->m_cleanAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::cleanProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::Active);
});
connect(dd->m_cleanProjectForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjectWithDependencies(SessionManager::startupProject(),
+ BuildManager::cleanProjectWithDependencies(ProjectManager::startupProject(),
ConfigSelection::All);
});
connect(dd->m_cleanActionContextMenu, &QAction::triggered, dd, [] {
@@ -1852,10 +1823,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ConfigSelection::Active);
});
connect(dd->m_cleanSessionAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::Active);
+ BuildManager::cleanProjects(ProjectManager::projectOrder(), ConfigSelection::Active);
});
connect(dd->m_cleanSessionForAllConfigsAction, &QAction::triggered, dd, [] {
- BuildManager::cleanProjects(SessionManager::projectOrder(), ConfigSelection::All);
+ BuildManager::cleanProjects(ProjectManager::projectOrder(), ConfigSelection::All);
});
connect(dd->m_runAction, &QAction::triggered,
dd, [] { runStartupProject(Constants::NORMAL_RUN_MODE); });
@@ -1920,7 +1891,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(dd->m_setStartupProjectAction, &QAction::triggered,
dd, &ProjectExplorerPluginPrivate::handleSetStartupProject);
connect(dd->m_closeProjectFilesActionFileMenu, &QAction::triggered,
- dd, [] { dd->closeAllFilesInProject(SessionManager::projects().first()); });
+ dd, [] { dd->closeAllFilesInProject(ProjectManager::projects().first()); });
connect(dd->m_closeProjectFilesActionContextMenu, &QAction::triggered,
dd, [] { dd->closeAllFilesInProject(ProjectTree::currentProject()); });
connect(dd->m_projectTreeCollapseAllAction, &QAction::triggered,
@@ -1935,6 +1906,18 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
dd->updateContextMenuActions(ProjectTree::currentNode());
});
+ connect(ModeManager::instance(),
+ &ModeManager::currentModeChanged,
+ dd,
+ &ProjectExplorerPluginPrivate::currentModeChanged);
+ connect(&dd->m_welcomePage,
+ &ProjectWelcomePage::requestProject,
+ m_instance,
+ &ProjectExplorerPlugin::openProjectWelcomePage);
+ connect(SessionManager::instance(),
+ &SessionManager::startupSessionRestored,
+ m_instance,
+ &ProjectExplorerPlugin::finishedInitialization);
dd->updateWelcomePage();
MacroExpander *expander = Utils::globalMacroExpander();
@@ -1965,7 +1948,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
Project::addVariablesToMacroExpander("ActiveProject:",
"Active project",
expander,
- &SessionManager::startupProject);
+ &ProjectManager::startupProject);
EnvironmentProvider::addProvider(
{"ActiveProject:BuildConfig:Env", Tr::tr("Active build environment of the active project."), [] {
if (const BuildConfiguration * const bc = activeBuildConfiguration())
@@ -2032,7 +2015,7 @@ void ProjectExplorerPluginPrivate::unloadProjectContextMenu()
void ProjectExplorerPluginPrivate::unloadOtherProjectsContextMenu()
{
if (Project *currentProject = ProjectTree::currentProject()) {
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
QTC_ASSERT(!projects.isEmpty(), return);
for (Project *p : projects) {
@@ -2045,7 +2028,7 @@ void ProjectExplorerPluginPrivate::unloadOtherProjectsContextMenu()
void ProjectExplorerPluginPrivate::handleUnloadProject()
{
- QList<Project *> projects = SessionManager::projects();
+ QList<Project *> projects = ProjectManager::projects();
QTC_ASSERT(!projects.isEmpty(), return);
ProjectExplorerPlugin::unloadProject(projects.first());
@@ -2072,7 +2055,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project)
dd->addToRecentProjects(project->projectFilePath(), project->displayName());
- SessionManager::removeProject(project);
+ ProjectManager::removeProject(project);
dd->updateActions();
}
@@ -2081,7 +2064,7 @@ void ProjectExplorerPluginPrivate::closeAllProjects()
if (!EditorManager::closeAllDocuments())
return; // Action has been cancelled
- SessionManager::closeAllProjects();
+ ProjectManager::closeAllProjects();
updateActions();
ModeManager::activateMode(Core::Constants::MODE_WELCOME);
@@ -2137,13 +2120,13 @@ void ProjectExplorerPlugin::extensionsInitialized()
Tr::tr("Sanitizer", "Category for sanitizer issues listed under 'Issues'"));
TaskHub::addCategory(Constants::TASK_CATEGORY_TASKLIST_ID, Tr::tr("My Tasks"));
- SshSettings::loadSettings(Core::ICore::settings());
+ SshSettings::loadSettings(ICore::settings());
const auto searchPathRetriever = [] {
- FilePaths searchPaths = {Core::ICore::libexecPath()};
+ FilePaths searchPaths = {ICore::libexecPath()};
if (HostOsInfo::isWindowsHost()) {
- const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git")
+ const QString gitBinary = ICore::settings()->value("Git/BinaryPath", "git")
.toString();
- const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path")
+ const QStringList rawGitSearchPaths = ICore::settings()->value("Git/Path")
.toString().split(':', Qt::SkipEmptyParts);
const FilePaths gitSearchPaths = Utils::transform(rawGitSearchPaths,
[](const QString &rawPath) { return FilePath::fromString(rawPath); });
@@ -2177,11 +2160,12 @@ void ProjectExplorerPlugin::extensionsInitialized()
void ProjectExplorerPlugin::restoreKits()
{
- dd->determineSessionToRestoreAtStartup();
ExtraAbi::load(); // Load this before Toolchains!
ToolChainManager::restoreToolChains();
KitManager::restoreKits();
- QTimer::singleShot(0, dd, &ProjectExplorerPluginPrivate::restoreSession); // delay a bit...
+ // restoring startup session is supposed to be done as a result of ICore::coreOpened,
+ // and that is supposed to happen after restoring kits:
+ QTC_CHECK(!sb_d->isStartupSessionRestored());
}
void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu()
@@ -2195,7 +2179,7 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
dd, &ProjectExplorerPluginPrivate::currentModeChanged);
ProjectTree::aboutToShutDown();
ToolChainManager::aboutToShutdown();
- SessionManager::closeAllProjects();
+ ProjectManager::closeAllProjects();
dd->m_shuttingDown = true;
@@ -2228,11 +2212,11 @@ void ProjectExplorerPlugin::openNewProjectDialog()
void ProjectExplorerPluginPrivate::showSessionManager()
{
- SessionManager::save();
+ ProjectManager::save();
SessionDialog sessionDialog(ICore::dialogParent());
- sessionDialog.setAutoLoadSession(dd->m_projectExplorerSettings.autorestoreLastSession);
+ sessionDialog.setAutoLoadSession(sb_d->isAutoRestoreLastSession());
sessionDialog.exec();
- dd->m_projectExplorerSettings.autorestoreLastSession = sessionDialog.autoLoadSession();
+ sb_d->setAutoRestoreLastSession(sessionDialog.autoLoadSession());
updateActions();
@@ -2244,7 +2228,7 @@ void ProjectExplorerPluginPrivate::setStartupProject(Project *project)
{
if (!project)
return;
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
updateActions();
}
@@ -2255,7 +2239,7 @@ bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project
Utils::erase(openFiles, [project](const DocumentModel::Entry *entry) {
return entry->pinned || !project->isKnownFile(entry->filePath());
});
- for (const Project * const otherProject : SessionManager::projects()) {
+ for (const Project * const otherProject : ProjectManager::projects()) {
if (otherProject == project)
continue;
Utils::erase(openFiles, [otherProject](const DocumentModel::Entry *entry) {
@@ -2271,19 +2255,13 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
return;
if (!SessionManager::loadingSession()) {
- for (Project *pro : SessionManager::projects())
+ for (Project *pro : ProjectManager::projects())
pro->saveSettings();
- SessionManager::save();
+ ProjectManager::save();
}
QtcSettings *s = ICore::settings();
- if (SessionManager::isDefaultVirgin()) {
- s->remove(Constants::STARTUPSESSION_KEY);
- } else {
- s->setValue(Constants::STARTUPSESSION_KEY, SessionManager::activeSession());
- s->setValue(Constants::LASTSESSION_KEY, SessionManager::activeSession());
- }
s->remove(QLatin1String("ProjectExplorer/RecentProjects/Files"));
QStringList fileNames;
@@ -2312,9 +2290,6 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
s->setValueWithDefault(Constants::USE_JOM_SETTINGS_KEY,
dd->m_projectExplorerSettings.useJom,
defaultSettings.useJom);
- s->setValueWithDefault(Constants::AUTO_RESTORE_SESSION_SETTINGS_KEY,
- dd->m_projectExplorerSettings.autorestoreLastSession,
- defaultSettings.autorestoreLastSession);
s->setValueWithDefault(Constants::ADD_LIBRARY_PATHS_TO_RUN_ENV_SETTINGS_KEY,
dd->m_projectExplorerSettings.addLibraryPathsToRunEnv,
defaultSettings.addLibraryPathsToRunEnv);
@@ -2346,6 +2321,7 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
int(defaultSettings.stopBeforeBuild));
dd->m_buildPropertiesSettings.writeSettings(s);
+ sb_d->saveSettings();
s->setValueWithDefault(Constants::CUSTOM_PARSER_COUNT_KEY, int(dd->m_customParsers.count()), 0);
for (int i = 0; i < dd->m_customParsers.count(); ++i) {
@@ -2368,7 +2344,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProject(cons
if (!project)
return result;
dd->addToRecentProjects(filePath, project->displayName());
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
return result;
}
@@ -2420,11 +2396,11 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
QTC_ASSERT(!fileName.isEmpty(), continue);
const FilePath filePath = fileName.absoluteFilePath();
- Project *found = Utils::findOrDefault(SessionManager::projects(),
+ Project *found = Utils::findOrDefault(ProjectManager::projects(),
Utils::equal(&Project::projectFilePath, filePath));
if (found) {
alreadyOpen.append(found);
- SessionManager::reportProjectLoadingProgress();
+ SessionManager::sessionLoadingProgress();
continue;
}
@@ -2439,7 +2415,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
if (restoreResult == Project::RestoreResult::Ok) {
connect(pro, &Project::fileListChanged,
m_instance, &ProjectExplorerPlugin::fileListChanged);
- SessionManager::addProject(pro);
+ ProjectManager::addProject(pro);
openedPro += pro;
} else {
if (restoreResult == Project::RestoreResult::Error)
@@ -2453,7 +2429,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con
.arg(mt.name()));
}
if (filePaths.size() > 1)
- SessionManager::reportProjectLoadingProgress();
+ SessionManager::sessionLoadingProgress();
}
dd->updateActions();
@@ -2496,33 +2472,6 @@ void ProjectExplorerPluginPrivate::currentModeChanged(Id mode, Id oldMode)
updateWelcomePage();
}
-void ProjectExplorerPluginPrivate::determineSessionToRestoreAtStartup()
-{
- // Process command line arguments first:
- const bool lastSessionArg =
- ExtensionSystem::PluginManager::specForPlugin(m_instance)->arguments().contains("-lastsession");
- m_sessionToRestoreAtStartup = lastSessionArg ? SessionManager::startupSession() : QString();
- const QStringList arguments = ExtensionSystem::PluginManager::arguments();
- if (!lastSessionArg) {
- QStringList sessions = SessionManager::sessions();
- // We have command line arguments, try to find a session in them
- // Default to no session loading
- for (const QString &arg : arguments) {
- if (sessions.contains(arg)) {
- // Session argument
- m_sessionToRestoreAtStartup = arg;
- break;
- }
- }
- }
- // Handle settings only after command line arguments:
- if (m_sessionToRestoreAtStartup.isEmpty() && m_projectExplorerSettings.autorestoreLastSession)
- m_sessionToRestoreAtStartup = SessionManager::startupSession();
-
- if (!m_sessionToRestoreAtStartup.isEmpty())
- ModeManager::activateMode(Core::Constants::MODE_EDIT);
-}
-
// Return a list of glob patterns for project files ("*.pro", etc), use first, main pattern only.
QStringList ProjectExplorerPlugin::projectFileGlobs()
{
@@ -2548,70 +2497,6 @@ MiniProjectTargetSelector *ProjectExplorerPlugin::targetSelector()
return dd->m_targetSelector;
}
-/*!
- This function is connected to the ICore::coreOpened signal. If
- there was no session explicitly loaded, it creates an empty new
- default session and puts the list of recent projects and sessions
- onto the welcome page.
-*/
-void ProjectExplorerPluginPrivate::restoreSession()
-{
- // We have command line arguments, try to find a session in them
- QStringList arguments = ExtensionSystem::PluginManager::arguments();
- if (!dd->m_sessionToRestoreAtStartup.isEmpty() && !arguments.isEmpty())
- arguments.removeOne(dd->m_sessionToRestoreAtStartup);
-
- // Massage the argument list.
- // Be smart about directories: If there is a session of that name, load it.
- // Other than that, look for project files in it. The idea is to achieve
- // 'Do what I mean' functionality when starting Creator in a directory with
- // the single command line argument '.' and avoid editor warnings about not
- // being able to open directories.
- // In addition, convert "filename" "+45" or "filename" ":23" into
- // "filename+45" and "filename:23".
- if (!arguments.isEmpty()) {
- const QStringList sessions = SessionManager::sessions();
- for (int a = 0; a < arguments.size(); ) {
- const QString &arg = arguments.at(a);
- const QFileInfo fi(arg);
- if (fi.isDir()) {
- const QDir dir(fi.absoluteFilePath());
- // Does the directory name match a session?
- if (dd->m_sessionToRestoreAtStartup.isEmpty()
- && sessions.contains(dir.dirName())) {
- dd->m_sessionToRestoreAtStartup = dir.dirName();
- arguments.removeAt(a);
- continue;
- }
- } // Done directories.
- // Converts "filename" "+45" or "filename" ":23" into "filename+45" and "filename:23"
- if (a && (arg.startsWith(QLatin1Char('+')) || arg.startsWith(QLatin1Char(':')))) {
- arguments[a - 1].append(arguments.takeAt(a));
- continue;
- }
- ++a;
- } // for arguments
- } // !arguments.isEmpty()
- // Restore latest session or what was passed on the command line
-
- SessionManager::loadSession(!dd->m_sessionToRestoreAtStartup.isEmpty()
- ? dd->m_sessionToRestoreAtStartup : QString(), true);
-
- // update welcome page
- connect(ModeManager::instance(), &ModeManager::currentModeChanged,
- dd, &ProjectExplorerPluginPrivate::currentModeChanged);
- connect(&dd->m_welcomePage, &ProjectWelcomePage::requestProject,
- m_instance, &ProjectExplorerPlugin::openProjectWelcomePage);
- dd->m_arguments = arguments;
- // delay opening projects from the command line even more
- QTimer::singleShot(0, m_instance, [] {
- ICore::openFiles(Utils::transform(dd->m_arguments, &FilePath::fromUserInput),
- ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers | ICore::SwitchMode));
- emit m_instance->finishedInitialization();
- });
- updateActions();
-}
-
void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *runConfiguration, Id runMode)
{
const Tasks runConfigIssues = runConfiguration->checkForIssues();
@@ -2732,7 +2617,7 @@ RecentProjectsEntries ProjectExplorerPluginPrivate::recentProjects() const
void ProjectExplorerPluginPrivate::updateActions()
{
- const Project *const project = SessionManager::startupProject();
+ const Project *const project = ProjectManager::startupProject();
const Project *const currentProject = ProjectTree::currentProject(); // for context menu actions
const QPair<bool, QString> buildActionState = buildSettingsEnabled(project);
@@ -2743,10 +2628,10 @@ void ProjectExplorerPluginPrivate::updateActions()
const QString projectName = project ? project->displayName() : QString();
const QString projectNameContextMenu = currentProject ? currentProject->displayName() : QString();
- m_unloadAction->setParameter(SessionManager::projects().size() == 1 ? projectName : QString());
+ m_unloadAction->setParameter(ProjectManager::projects().size() == 1 ? projectName : QString());
m_unloadActionContextMenu->setParameter(projectNameContextMenu);
m_unloadOthersActionContextMenu->setParameter(projectNameContextMenu);
- m_closeProjectFilesActionFileMenu->setParameter(SessionManager::projects().size() == 1
+ m_closeProjectFilesActionFileMenu->setParameter(ProjectManager::projects().size() == 1
? projectName : QString());
m_closeProjectFilesActionContextMenu->setParameter(projectNameContextMenu);
@@ -2791,7 +2676,7 @@ void ProjectExplorerPluginPrivate::updateActions()
m_setStartupProjectAction->setParameter(projectNameContextMenu);
m_setStartupProjectAction->setVisible(currentProject != project);
- const bool hasDependencies = SessionManager::projectOrder(currentProject).size() > 1;
+ const bool hasDependencies = ProjectManager::projectOrder(currentProject).size() > 1;
m_buildActionContextMenu->setVisible(hasDependencies);
m_rebuildActionContextMenu->setVisible(hasDependencies);
m_cleanActionContextMenu->setVisible(hasDependencies);
@@ -2818,17 +2703,17 @@ void ProjectExplorerPluginPrivate::updateActions()
m_cleanProjectOnlyAction->setToolTip(buildActionState.second);
// Session actions
- m_closeAllProjects->setEnabled(SessionManager::hasProjects());
- m_unloadAction->setEnabled(SessionManager::projects().size() <= 1);
- m_unloadAction->setEnabled(SessionManager::projects().size() == 1);
- m_unloadActionContextMenu->setEnabled(SessionManager::hasProjects());
- m_unloadOthersActionContextMenu->setEnabled(SessionManager::projects().size() >= 2);
- m_closeProjectFilesActionFileMenu->setEnabled(SessionManager::projects().size() == 1);
- m_closeProjectFilesActionContextMenu->setEnabled(SessionManager::hasProjects());
+ m_closeAllProjects->setEnabled(ProjectManager::hasProjects());
+ m_unloadAction->setEnabled(ProjectManager::projects().size() <= 1);
+ m_unloadAction->setEnabled(ProjectManager::projects().size() == 1);
+ m_unloadActionContextMenu->setEnabled(ProjectManager::hasProjects());
+ m_unloadOthersActionContextMenu->setEnabled(ProjectManager::projects().size() >= 2);
+ m_closeProjectFilesActionFileMenu->setEnabled(ProjectManager::projects().size() == 1);
+ m_closeProjectFilesActionContextMenu->setEnabled(ProjectManager::hasProjects());
ActionContainer *aci =
ActionManager::actionContainer(Constants::M_UNLOADPROJECTS);
- aci->menu()->menuAction()->setEnabled(SessionManager::hasProjects());
+ aci->menu()->menuAction()->setEnabled(ProjectManager::hasProjects());
m_buildSessionAction->setEnabled(buildSessionState.first);
m_buildSessionForAllConfigsAction->setEnabled(buildSessionState.first);
@@ -2846,7 +2731,7 @@ void ProjectExplorerPluginPrivate::updateActions()
m_cancelBuildAction->setEnabled(BuildManager::isBuilding());
- const bool hasProjects = SessionManager::hasProjects();
+ const bool hasProjects = ProjectManager::hasProjects();
m_projectSelectorAction->setEnabled(hasProjects);
m_projectSelectorActionMenu->setEnabled(hasProjects);
m_projectSelectorActionQuick->setEnabled(hasProjects);
@@ -2923,10 +2808,9 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory()
"The file \"%1\" was renamed to \"%2\", "
"but the following projects could not be automatically changed: %3")
.arg(before.toUserOutput(), after.toUserOutput(), projects);
- QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] {
- QMessageBox::warning(Core::ICore::dialogParent(),
- Tr::tr("Project Editing Failed"),
- errorMessage);
+ QTimer::singleShot(0, ICore::instance(), [errorMessage] {
+ QMessageBox::warning(ICore::dialogParent(),
+ Tr::tr("Project Editing Failed"), errorMessage);
});
}
});
@@ -2944,8 +2828,8 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory()
const QString errorMessage
= Tr::tr("The following projects failed to automatically remove the file: %1")
.arg(projects);
- QTimer::singleShot(0, Core::ICore::instance(), [errorMessage] {
- QMessageBox::warning(Core::ICore::dialogParent(),
+ QTimer::singleShot(0, ICore::instance(), [errorMessage] {
+ QMessageBox::warning(ICore::dialogParent(),
Tr::tr("Project Editing Failed"),
errorMessage);
});
@@ -2967,7 +2851,7 @@ void ProjectExplorerPluginPrivate::runProjectContextMenu(RunConfiguration *rc)
static bool hasBuildSettings(const Project *pro)
{
- return Utils::anyOf(SessionManager::projectOrder(pro), [](const Project *project) {
+ return Utils::anyOf(ProjectManager::projectOrder(pro), [](const Project *project) {
return project
&& project->activeTarget()
&& project->activeTarget()->activeBuildConfiguration();
@@ -2979,7 +2863,7 @@ static QPair<bool, QString> subprojectEnabledState(const Project *pro)
QPair<bool, QString> result;
result.first = true;
- const QList<Project *> &projects = SessionManager::projectOrder(pro);
+ const QList<Project *> &projects = ProjectManager::projectOrder(pro);
for (const Project *project : projects) {
if (project && project->activeTarget()
&& project->activeTarget()->activeBuildConfiguration()
@@ -3021,7 +2905,7 @@ QPair<bool, QString> ProjectExplorerPluginPrivate::buildSettingsEnabledForSessio
{
QPair<bool, QString> result;
result.first = true;
- if (!SessionManager::hasProjects()) {
+ if (!ProjectManager::hasProjects()) {
result.first = false;
result.second = Tr::tr("No project loaded.");
} else if (BuildManager::isBuilding()) {
@@ -3078,7 +2962,7 @@ void ProjectExplorerPlugin::handleCommandLineArguments(const QStringList &argume
static bool hasDeploySettings(Project *pro)
{
- return Utils::anyOf(SessionManager::projectOrder(pro), [](Project *project) {
+ return Utils::anyOf(ProjectManager::projectOrder(pro), [](Project *project) {
return project->activeTarget()
&& project->activeTarget()->activeDeployConfiguration();
});
@@ -3096,7 +2980,7 @@ void ProjectExplorerPlugin::runProject(Project *pro, Id mode, const bool forceSk
void ProjectExplorerPlugin::runStartupProject(Id runMode, bool forceSkipDeploy)
{
- runProject(SessionManager::startupProject(), runMode, forceSkipDeploy);
+ runProject(ProjectManager::startupProject(), runMode, forceSkipDeploy);
}
void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc,
@@ -3157,7 +3041,7 @@ void ProjectExplorerPluginPrivate::projectAdded(Project *pro)
void ProjectExplorerPluginPrivate::projectRemoved(Project *pro)
{
Q_UNUSED(pro)
- m_projectsMode.setEnabled(SessionManager::hasProjects());
+ m_projectsMode.setEnabled(ProjectManager::hasProjects());
}
void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro)
@@ -3168,7 +3052,7 @@ void ProjectExplorerPluginPrivate::projectDisplayNameChanged(Project *pro)
void ProjectExplorerPluginPrivate::updateDeployActions()
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
bool enableDeployActions = project
&& !BuildManager::isBuilding(project)
@@ -3187,7 +3071,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions()
enableDeployActionsContextMenu = false;
}
- bool hasProjects = SessionManager::hasProjects();
+ bool hasProjects = ProjectManager::hasProjects();
m_deployAction->setEnabled(enableDeployActions);
@@ -3203,7 +3087,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions()
&& !project->activeTarget()->activeBuildConfiguration()->isEnabled();
};
- if (Utils::anyOf(SessionManager::projectOrder(nullptr), hasDisabledBuildConfiguration))
+ if (Utils::anyOf(ProjectManager::projectOrder(nullptr), hasDisabledBuildConfiguration))
enableDeploySessionAction = false;
}
if (!hasProjects || !hasDeploySettings(nullptr) || BuildManager::isBuilding())
@@ -3215,7 +3099,7 @@ void ProjectExplorerPluginPrivate::updateDeployActions()
bool ProjectExplorerPlugin::canRunStartupProject(Id runMode, QString *whyNot)
{
- Project *project = SessionManager::startupProject();
+ Project *project = ProjectManager::startupProject();
if (!project) {
if (whyNot)
*whyNot = Tr::tr("No active project.");
@@ -3320,7 +3204,7 @@ void ProjectExplorerPluginPrivate::updateUnloadProjectMenu()
ActionContainer *aci = ActionManager::actionContainer(Constants::M_UNLOADPROJECTS);
QMenu *menu = aci->menu();
menu->clear();
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
QAction *action = menu->addAction(Tr::tr("Close Project \"%1\"").arg(project->displayName()));
connect(action, &QAction::triggered,
[project] { ProjectExplorerPlugin::unloadProject(project); } );
@@ -3419,7 +3303,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
m_removeFileAction->setVisible(true);
m_duplicateFileAction->setVisible(false);
m_deleteFileAction->setVisible(true);
- m_runActionContextMenu->setVisible(false);
+ m_runActionContextMenu->setEnabled(false);
m_defaultRunConfiguration.clear();
m_diffFileAction->setVisible(DiffService::instance());
@@ -3448,7 +3332,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
if (pn && project) {
if (pn == project->rootProjectNode()) {
- m_runActionContextMenu->setVisible(true);
+ m_runActionContextMenu->setEnabled(true);
} else {
QList<RunConfiguration *> runConfigs;
if (Target *t = project->activeTarget()) {
@@ -3459,7 +3343,7 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions(Node *currentNode)
}
}
if (runConfigs.count() == 1) {
- m_runActionContextMenu->setVisible(true);
+ m_runActionContextMenu->setEnabled(true);
m_defaultRunConfiguration = runConfigs.first();
} else if (runConfigs.count() > 1) {
runMenu->menu()->menuAction()->setVisible(true);
@@ -3592,9 +3476,7 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus()
: Tr::tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput());
auto *action = new QAction(displayName, nullptr);
connect(action, &QAction::triggered, this, [line, path] {
- Core::EditorManager::openEditorAt(Link(path, line),
- {},
- Core::EditorManager::AllowExternalEditor);
+ EditorManager::openEditorAt(Link(path, line), {}, EditorManager::AllowExternalEditor);
});
projectMenu->addAction(action);
@@ -3795,6 +3677,13 @@ void ProjectExplorerPluginPrivate::showInFileSystemPane()
Core::FileUtils::showInFileSystemView(currentNode->filePath());
}
+static BuildConfiguration *activeBuildConfiguration(Project *project)
+{
+ if (!project || !project->activeTarget() || !project->activeTarget()->activeBuildConfiguration())
+ return {};
+ return project->activeTarget()->activeBuildConfiguration();
+}
+
void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env)
{
const Node *currentNode = ProjectTree::currentNode();
@@ -3804,7 +3693,29 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env
if (!environment)
return;
- Core::FileUtils::openTerminal(currentNode->directory(), environment.value());
+ BuildConfiguration *bc = activeBuildConfiguration(ProjectTree::projectForNode(currentNode));
+ if (!bc) {
+ Terminal::Hooks::instance().openTerminal({{}, currentNode->directory(), environment});
+ return;
+ }
+
+ IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(bc->target()->kit());
+
+ if (!buildDevice)
+ return;
+
+ FilePath workingDir = currentNode->directory();
+ if (!buildDevice->filePath(workingDir.path()).exists()
+ && !buildDevice->ensureReachable(workingDir))
+ workingDir.clear();
+
+ const FilePath shell = Terminal::defaultShellForDevice(buildDevice->rootPath());
+
+ if (!shell.isEmpty() && buildDevice->rootPath().needsDevice()) {
+ Terminal::Hooks::instance().openTerminal({CommandLine{shell, {}}, workingDir, environment});
+ } else {
+ Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, environment});
+ }
}
void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
@@ -3825,9 +3736,21 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
if (!device)
device = DeviceKitAspect::device(target->kit());
QTC_ASSERT(device && device->canOpenTerminal(), return);
- const FilePath workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE
- ? currentNode->directory() : runnable.workingDirectory;
- device->openTerminal(runnable.environment, workingDir);
+
+ FilePath workingDir = device->type() == Constants::DESKTOP_DEVICE_TYPE
+ ? currentNode->directory()
+ : runnable.workingDirectory;
+
+ if (!device->filePath(workingDir.path()).exists() && !device->ensureReachable(workingDir))
+ workingDir.clear();
+
+ const FilePath shell = Terminal::defaultShellForDevice(device->rootPath());
+ if (!shell.isEmpty() && device->rootPath().needsDevice()) {
+ Terminal::Hooks::instance().openTerminal(
+ {CommandLine{shell, {}}, workingDir, runnable.environment});
+ } else {
+ Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, runnable.environment});
+ }
}
void ProjectExplorerPluginPrivate::removeFile()
@@ -3852,7 +3775,7 @@ void ProjectExplorerPluginPrivate::removeFile()
if (!siblings.isEmpty()) {
const QMessageBox::StandardButton reply = QMessageBox::question(
- Core::ICore::dialogParent(), Tr::tr("Remove More Files?"),
+ ICore::dialogParent(), Tr::tr("Remove More Files?"),
Tr::tr("Remove these files as well?\n %1")
.arg(Utils::transform<QStringList>(siblings, [](const NodeAndPath &np) {
return np.second.fileName();
@@ -4213,7 +4136,7 @@ void ProjectExplorerPlugin::updateActions()
void ProjectExplorerPlugin::activateProjectPanel(Id panelId)
{
- Core::ModeManager::activateMode(Constants::MODE_SESSION);
+ ModeManager::activateMode(Constants::MODE_SESSION);
dd->m_proWindow->activateProjectPanel(panelId);
}
@@ -4242,9 +4165,8 @@ RecentProjectsEntries ProjectExplorerPlugin::recentProjects()
return dd->recentProjects();
}
-void ProjectExplorerPlugin::renameFilesForSymbol(
- const QString &oldSymbolName, const QString &newSymbolName, const Utils::FilePaths &files,
- bool preferLowerCaseFileNames)
+void ProjectExplorerPlugin::renameFilesForSymbol(const QString &oldSymbolName,
+ const QString &newSymbolName, const FilePaths &files, bool preferLowerCaseFileNames)
{
static const auto isAllLowerCase = [](const QString &text) { return text.toLower() == text; };
@@ -4319,10 +4241,18 @@ AllProjectFilesFilter::AllProjectFilesFilter()
setDefaultIncludedByDefault(false); // but not included in default
setFilters({});
setIsCustomFilter(false);
- setDescription(Tr::tr(
- "Matches all files from all project directories. Append \"+<number>\" or "
- "\":<number>\" to jump to the given line number. Append another "
- "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+ setDescription(Tr::tr("Locates files from all project directories. Append \"+<number>\" or "
+ "\":<number>\" to jump to the given line number. Append another "
+ "\"+<number>\" or \":<number>\" to jump to the column number as well."));
+
+ ProjectManager *projectManager = ProjectManager::instance();
+ QTC_ASSERT(projectManager, return);
+ connect(projectManager, &ProjectManager::projectAdded, this, [this](Project *project) {
+ addDirectory(project->projectDirectory());
+ });
+ connect(projectManager, &ProjectManager::projectRemoved, this, [this](Project *project) {
+ removeDirectory(project->projectDirectory());
+ });
}
const char kDirectoriesKey[] = "directories";
@@ -4346,102 +4276,104 @@ void AllProjectFilesFilter::restoreState(const QJsonObject &object)
DirectoryFilter::restoreState(withoutDirectories);
}
-RunConfigurationLocatorFilter::RunConfigurationLocatorFilter()
+static void setupFilter(ILocatorFilter *filter)
{
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
- this, &RunConfigurationLocatorFilter::targetListUpdated);
-
- targetListUpdated();
+ QObject::connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
+ filter, [filter] { filter->setEnabled(ProjectManager::startupProject()); });
+ filter->setEnabled(ProjectManager::startupProject());
}
-void RunConfigurationLocatorFilter::prepareSearch(const QString &entry)
+using RunAcceptor = std::function<void(RunConfiguration *)>;
+
+static RunConfiguration *runConfigurationForDisplayName(const QString &displayName)
{
- m_result.clear();
- const Target *target = SessionManager::startupTarget();
+ const Target *target = ProjectManager::startupTarget();
if (!target)
- return;
- for (auto rc : target->runConfigurations()) {
- if (rc->displayName().contains(entry, Qt::CaseInsensitive))
- m_result.append(LocatorFilterEntry(this, rc->displayName()));
- }
+ return nullptr;
+ const QList<RunConfiguration *> runconfigs = target->runConfigurations();
+ return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) {
+ return rc->displayName() == displayName;
+ });
}
-QList<Core::LocatorFilterEntry> RunConfigurationLocatorFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+static LocatorMatcherTasks runConfigurationMatchers(const RunAcceptor &acceptor)
{
- Q_UNUSED(future)
- Q_UNUSED(entry)
- return m_result;
-}
+ using namespace Tasking;
-void RunConfigurationLocatorFilter::targetListUpdated()
-{
- setEnabled(SessionManager::startupProject()); // at least one project opened
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, acceptor] {
+ const QString input = storage->input();
+ const Target *target = ProjectManager::startupTarget();
+ if (!target)
+ return;
+
+ LocatorFilterEntries entries;
+ for (auto rc : target->runConfigurations()) {
+ if (rc->displayName().contains(input, Qt::CaseInsensitive)) {
+ LocatorFilterEntry entry;
+ entry.displayName = rc->displayName();
+ entry.acceptor = [name = entry.displayName, acceptor = acceptor] {
+ RunConfiguration *config = runConfigurationForDisplayName(name);
+ if (!config)
+ return AcceptResult();
+ acceptor(config);
+ return AcceptResult();
+ };
+ entries.append(entry);
+ }
+ }
+ storage->reportOutput(entries);
+ };
+ return {{Sync(onSetup), storage}};
}
-static RunConfiguration *runConfigurationForDisplayName(const QString &displayName)
+static void runAcceptor(RunConfiguration *config)
{
- const Project *project = SessionManager::instance()->startupProject();
- if (!project)
- return nullptr;
- const QList<RunConfiguration *> runconfigs = project->activeTarget()->runConfigurations();
- return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) {
- return rc->displayName() == displayName;
- });
+ if (!BuildManager::isBuilding(config->project()))
+ ProjectExplorerPlugin::runRunConfiguration(config, Constants::NORMAL_RUN_MODE, true);
}
-RunRunConfigurationLocatorFilter::RunRunConfigurationLocatorFilter()
+RunConfigurationStartFilter::RunConfigurationStartFilter()
{
setId("Run run configuration");
- setDisplayName(Tr::tr("Run run configuration"));
- setDescription(Tr::tr("Run a run configuration of the current active project"));
+ setDisplayName(Tr::tr("Run Run Configuration"));
+ setDescription(Tr::tr("Runs a run configuration of the active project."));
setDefaultShortcutString("rr");
setPriority(Medium);
+ setupFilter(this);
}
-void RunRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection, QString *newText,
- int *selectionStart, int *selectionLength) const
+LocatorMatcherTasks RunConfigurationStartFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
+ return runConfigurationMatchers(&runAcceptor);
+}
- RunConfiguration *toStart = runConfigurationForDisplayName(selection.displayName);
- if (!toStart)
- return;
- if (!BuildManager::isBuilding(toStart->project()))
- ProjectExplorerPlugin::runRunConfiguration(toStart, Constants::NORMAL_RUN_MODE, true);
+static void switchAcceptor(RunConfiguration *config)
+{
+ ProjectManager::startupTarget()->setActiveRunConfiguration(config);
+ QTimer::singleShot(200, ICore::mainWindow(), [name = config->displayName()] {
+ if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) {
+ ToolTip::show(ks->mapToGlobal(QPoint{25, 25}),
+ Tr::tr("Switched run configuration to\n%1").arg(name),
+ ICore::dialogParent());
+ }
+ });
}
-SwitchToRunConfigurationLocatorFilter::SwitchToRunConfigurationLocatorFilter()
+RunConfigurationSwitchFilter::RunConfigurationSwitchFilter()
{
setId("Switch run configuration");
- setDisplayName(Tr::tr("Switch run configuration"));
- setDescription(Tr::tr("Switch active run configuration"));
+ setDisplayName(Tr::tr("Switch Run Configuration"));
+ setDescription(Tr::tr("Switches the active run configuration of the active project."));
setDefaultShortcutString("sr");
setPriority(Medium);
+ setupFilter(this);
}
-void SwitchToRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart,
- int *selectionLength) const
+LocatorMatcherTasks RunConfigurationSwitchFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
-
- RunConfiguration *toSwitchTo = runConfigurationForDisplayName(selection.displayName);
- if (!toSwitchTo)
- return;
-
- SessionManager::startupTarget()->setActiveRunConfiguration(toSwitchTo);
- QTimer::singleShot(200, this, [displayName = selection.displayName] {
- if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) {
- Utils::ToolTip::show(ks->mapToGlobal(QPoint{25, 25}),
- Tr::tr("Switched run configuration to\n%1").arg(displayName),
- ICore::dialogParent());
- }
- });
+ return runConfigurationMatchers(&switchAcceptor);
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index baa8bb83a6..e5f91bd334 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -261,6 +261,9 @@ private slots:
void testProject_projectTree();
void testProject_multipleBuildConfigs();
+ void testSourceToBinaryMapping();
+ void testSourceToBinaryMapping_data();
+
void testSessionSwitch();
#endif // WITH_TESTS
};
diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs
index e65cc1bdc3..2387033485 100644
--- a/src/plugins/projectexplorer/projectexplorer.qbs
+++ b/src/plugins/projectexplorer/projectexplorer.qbs
@@ -45,6 +45,7 @@ Project {
"codestylesettingspropertiespage.cpp", "codestylesettingspropertiespage.h",
"compileoutputwindow.cpp", "compileoutputwindow.h",
"configtaskhandler.cpp", "configtaskhandler.h",
+ "copystep.cpp", "copystep.h",
"copytaskhandler.cpp", "copytaskhandler.h",
"currentprojectfilter.cpp", "currentprojectfilter.h",
"currentprojectfind.cpp", "currentprojectfind.h",
@@ -116,7 +117,7 @@ Project {
"projectfilewizardextension.cpp", "projectfilewizardextension.h",
"projectimporter.cpp", "projectimporter.h",
"projectmacro.cpp", "projectmacro.h",
- "projectmanager.h",
+ "projectmanager.cpp", "projectmanager.h",
"projectmodels.cpp", "projectmodels.h",
"projectnodes.cpp", "projectnodes.h",
"projectpanelfactory.cpp", "projectpanelfactory.h",
@@ -134,7 +135,7 @@ Project {
"runsettingspropertiespage.cpp", "runsettingspropertiespage.h",
"sanitizerparser.cpp", "sanitizerparser.h",
"selectablefilesmodel.cpp", "selectablefilesmodel.h",
- "session.cpp", "session.h",
+ "session.cpp", "session.h", "session_p.h",
"sessionmodel.cpp", "sessionmodel.h",
"sessionview.cpp", "sessionview.h",
"sessiondialog.cpp", "sessiondialog.h",
@@ -226,8 +227,7 @@ Project {
"idevicefactory.cpp", "idevicefactory.h",
"idevicefwd.h",
"idevicewidget.h",
- "localprocesslist.cpp", "localprocesslist.h",
- "sshdeviceprocesslist.cpp", "sshdeviceprocesslist.h",
+ "processlist.cpp", "processlist.h",
"sshparameters.cpp", "sshparameters.h",
"sshsettings.cpp", "sshsettings.h",
"sshsettingspage.cpp", "sshsettingspage.h",
@@ -250,9 +250,7 @@ Project {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: ["outputparser_test.h", "outputparser_test.cpp"]
}
diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc
index ececb0854b..0cc88e3331 100644
--- a/src/plugins/projectexplorer/projectexplorer.qrc
+++ b/src/plugins/projectexplorer/projectexplorer.qrc
@@ -86,5 +86,13 @@
<file>images/settingscategory_cpp@2x.png</file>
<file>images/importasproject.png</file>
<file>images/importasproject@2x.png</file>
+ <file>testdata/multi-target-project/CMakeLists.txt</file>
+ <file>testdata/multi-target-project/multi-target-project-app.pro</file>
+ <file>testdata/multi-target-project/multi-target-project-lib.cpp</file>
+ <file>testdata/multi-target-project/multi-target-project-lib.pro</file>
+ <file>testdata/multi-target-project/multi-target-project-main.cpp</file>
+ <file>testdata/multi-target-project/multi-target-project-shared.h</file>
+ <file>testdata/multi-target-project/multi-target-project.pro</file>
+ <file>testdata/multi-target-project/multi-target-project.qbs</file>
</qresource>
</RCC>
diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h
index 9c86d6d9cf..75054120a8 100644
--- a/src/plugins/projectexplorer/projectexplorerconstants.h
+++ b/src/plugins/projectexplorer/projectexplorerconstants.h
@@ -132,6 +132,10 @@ const char BUILDSTEPS_CLEAN[] = "ProjectExplorer.BuildSteps.Clean";
const char BUILDSTEPS_BUILD[] = "ProjectExplorer.BuildSteps.Build";
const char BUILDSTEPS_DEPLOY[] = "ProjectExplorer.BuildSteps.Deploy";
+const char COPY_FILE_STEP[] = "ProjectExplorer.CopyFileStep";
+const char COPY_DIRECTORY_STEP[] = "ProjectExplorer.CopyDirectoryStep";
+const char DEVICE_CHECK_STEP[] = "ProjectExplorer.DeviceCheckBuildStep";
+
// Language
// Keep these short: These constants are exposed to the MacroExplorer!
@@ -202,8 +206,6 @@ const char FILEOVERLAY_UNKNOWN[]=":/projectexplorer/images/fileoverlay_unknown.p
// Settings
const char ADD_FILES_DIALOG_FILTER_HISTORY_KEY[] = "ProjectExplorer.AddFilesFilterKey";
const char PROJECT_ROOT_PATH_KEY[] = "ProjectExplorer.Project.RootPath";
-const char STARTUPSESSION_KEY[] = "ProjectExplorer/SessionToRestore";
-const char LASTSESSION_KEY[] = "ProjectExplorer/StartupSession";
const char SETTINGS_MENU_HIDE_BUILD[] = "Menu/HideBuild";
const char SETTINGS_MENU_HIDE_DEBUG[] = "Menu/HideDebug";
const char SETTINGS_MENU_HIDE_ANALYZE[] = "Menu/HideAnalyze";
diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h
index b4b80124cd..bda218aef2 100644
--- a/src/plugins/projectexplorer/projectexplorersettings.h
+++ b/src/plugins/projectexplorer/projectexplorersettings.h
@@ -23,7 +23,6 @@ public:
&& p1.deployBeforeRun == p2.deployBeforeRun
&& p1.saveBeforeBuild == p2.saveBeforeBuild
&& p1.useJom == p2.useJom
- && p1.autorestoreLastSession == p2.autorestoreLastSession
&& p1.prompToStopRunControl == p2.prompToStopRunControl
&& p1.automaticallyCreateRunConfigurations == p2.automaticallyCreateRunConfigurations
&& p1.addLibraryPathsToRunEnv == p2.addLibraryPathsToRunEnv
@@ -40,7 +39,6 @@ public:
bool deployBeforeRun = true;
bool saveBeforeBuild = false;
bool useJom = true;
- bool autorestoreLastSession = false; // This option is set in the Session Manager!
bool prompToStopRunControl = false;
bool automaticallyCreateRunConfigurations = true;
bool addLibraryPathsToRunEnv = true;
diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp
index 30eb850cd0..c8bc51b967 100644
--- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp
+++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp
@@ -25,15 +25,14 @@
using namespace Core;
using namespace Utils;
-namespace ProjectExplorer {
-namespace Internal {
+namespace ProjectExplorer::Internal {
enum { UseCurrentDirectory, UseProjectDirectory };
-class ProjectExplorerSettingsWidget : public QWidget
+class ProjectExplorerSettingsWidget : public IOptionsPageWidget
{
public:
- explicit ProjectExplorerSettingsWidget(QWidget *parent = nullptr);
+ ProjectExplorerSettingsWidget();
ProjectExplorerSettings settings() const;
void setSettings(const ProjectExplorerSettings &s);
@@ -44,6 +43,13 @@ public:
bool useProjectsDirectory();
void setUseProjectsDirectory(bool v);
+ void apply() final
+ {
+ ProjectExplorerPlugin::setProjectExplorerSettings(settings());
+ DocumentManager::setProjectsDirectory(projectsDirectory());
+ DocumentManager::setUseProjectsDirectory(useProjectsDirectory());
+ }
+
private:
void slotDirectoryButtonGroupChanged();
@@ -68,8 +74,7 @@ private:
QButtonGroup *m_directoryButtonGroup;
};
-ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) :
- QWidget(parent)
+ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget()
{
m_currentDirectoryRadioButton = new QRadioButton(Tr::tr("Current directory"));
m_directoryRadioButton = new QRadioButton(Tr::tr("Directory"));
@@ -117,7 +122,7 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) :
"Disable it if you experience problems with your builds.");
jomLabel->setWordWrap(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Projects Directory")),
@@ -165,6 +170,10 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget(QWidget *parent) :
connect(m_directoryButtonGroup, &QButtonGroup::buttonClicked,
this, &ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged);
+
+ setSettings(ProjectExplorerPlugin::projectExplorerSettings());
+ setProjectsDirectory(DocumentManager::projectsDirectory());
+ setUseProjectsDirectory(DocumentManager::useProjectsDirectory());
}
ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const
@@ -236,7 +245,8 @@ void ProjectExplorerSettingsWidget::slotDirectoryButtonGroupChanged()
m_projectsDirectoryPathChooser->setEnabled(enable);
}
-// ------------------ ProjectExplorerSettingsPage
+// ProjectExplorerSettingsPage
+
ProjectExplorerSettingsPage::ProjectExplorerSettingsPage()
{
setId(Constants::BUILD_AND_RUN_SETTINGS_PAGE_ID);
@@ -244,32 +254,7 @@ ProjectExplorerSettingsPage::ProjectExplorerSettingsPage()
setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Build & Run"));
setCategoryIconPath(":/projectexplorer/images/settingscategory_buildrun.png");
+ setWidgetCreator([] { return new ProjectExplorerSettingsWidget; });
}
-QWidget *ProjectExplorerSettingsPage::widget()
-{
- if (!m_widget) {
- m_widget = new ProjectExplorerSettingsWidget;
- m_widget->setSettings(ProjectExplorerPlugin::projectExplorerSettings());
- m_widget->setProjectsDirectory(DocumentManager::projectsDirectory());
- m_widget->setUseProjectsDirectory(DocumentManager::useProjectsDirectory());
- }
- return m_widget;
-}
-
-void ProjectExplorerSettingsPage::apply()
-{
- if (m_widget) {
- ProjectExplorerPlugin::setProjectExplorerSettings(m_widget->settings());
- DocumentManager::setProjectsDirectory(m_widget->projectsDirectory());
- DocumentManager::setUseProjectsDirectory(m_widget->useProjectsDirectory());
- }
-}
-
-void ProjectExplorerSettingsPage::finish()
-{
- delete m_widget;
-}
-
-} // namespace Internal
-} // namespace ProjectExplorer
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.h b/src/plugins/projectexplorer/projectexplorersettingspage.h
index 82764c3850..a924681dae 100644
--- a/src/plugins/projectexplorer/projectexplorersettingspage.h
+++ b/src/plugins/projectexplorer/projectexplorersettingspage.h
@@ -5,25 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <QPointer>
-
-namespace ProjectExplorer {
-namespace Internal {
-
-class ProjectExplorerSettingsWidget;
+namespace ProjectExplorer::Internal {
class ProjectExplorerSettingsPage : public Core::IOptionsPage
{
public:
ProjectExplorerSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<ProjectExplorerSettingsWidget> m_widget;
};
-} // namespace Internal
-} // namespace ProjectExplorer
+} // ProjectExplorer::Internal
diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp
index 53d3eb1578..134db6ea48 100644
--- a/src/plugins/projectexplorer/projectfilewizardextension.cpp
+++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp
@@ -7,10 +7,10 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "projecttree.h"
#include "projectwizardpage.h"
-#include "session.h"
#include <coreplugin/icore.h>
@@ -27,7 +27,6 @@
#include <utils/stringutils.h>
#include <QDebug>
-#include <QFileInfo>
#include <QMessageBox>
#include <QPointer>
#include <QTextCursor>
@@ -134,7 +133,7 @@ Node *ProjectFileWizardExtension::findWizardContextNode(Node *contextNode, Proje
const FilePath &path)
{
if (contextNode && !ProjectTree::hasNode(contextNode)) {
- if (SessionManager::projects().contains(project) && project->rootProjectNode()) {
+ if (ProjectManager::projects().contains(project) && project->rootProjectNode()) {
contextNode = project->rootProjectNode()->findNode([path](const Node *n) {
return path == n->filePath();
});
diff --git a/src/plugins/projectexplorer/projectmanager.cpp b/src/plugins/projectexplorer/projectmanager.cpp
new file mode 100644
index 0000000000..ccfa95a289
--- /dev/null
+++ b/src/plugins/projectexplorer/projectmanager.cpp
@@ -0,0 +1,816 @@
+// 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 "projectmanager.h"
+
+#include "session_p.h"
+#include "session.h"
+
+#include "buildconfiguration.h"
+#include "editorconfiguration.h"
+#include "project.h"
+#include "projectexplorer.h"
+#include "projectexplorerconstants.h"
+#include "projectexplorertr.h"
+#include "projectmanager.h"
+#include "projectnodes.h"
+#include "target.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/foldernavigationwidget.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/idocument.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+
+#include <texteditor/texteditor.h>
+
+#include <utils/algorithm.h>
+#include <utils/filepath.h>
+#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
+#include <utils/qtcassert.h>
+
+#include <QDebug>
+#include <QMessageBox>
+#include <QPushButton>
+
+#ifdef WITH_TESTS
+#include <QTemporaryFile>
+#include <QTest>
+#include <vector>
+#endif
+
+using namespace Core;
+using namespace Utils;
+using namespace ProjectExplorer::Internal;
+
+namespace ProjectExplorer {
+
+class ProjectManagerPrivate
+{
+public:
+ void loadSession();
+ void restoreDependencies();
+ void restoreStartupProject();
+ void restoreProjects(const FilePaths &fileList);
+ void askUserAboutFailedProjects();
+
+ bool recursiveDependencyCheck(const FilePath &newDep, const FilePath &checkDep) const;
+ FilePaths dependencies(const FilePath &proName) const;
+ FilePaths dependenciesOrder() const;
+ void dependencies(const FilePath &proName, FilePaths &result) const;
+
+ static QString windowTitleAddition(const FilePath &filePath);
+ static QString sessionTitle(const FilePath &filePath);
+
+ bool hasProjects() const { return !m_projects.isEmpty(); }
+
+ bool m_casadeSetActive = false;
+
+ Project *m_startupProject = nullptr;
+ QList<Project *> m_projects;
+ FilePaths m_failedProjects;
+ QMap<FilePath, FilePaths> m_depMap;
+
+private:
+ static QString locationInProject(const FilePath &filePath);
+};
+
+static ProjectManager *m_instance = nullptr;
+static ProjectManagerPrivate *d = nullptr;
+
+static QString projectFolderId(Project *pro)
+{
+ return pro->projectFilePath().toString();
+}
+
+const int PROJECT_SORT_VALUE = 100;
+
+ProjectManager::ProjectManager()
+{
+ m_instance = this;
+ d = new ProjectManagerPrivate;
+
+ connect(EditorManager::instance(), &EditorManager::editorCreated,
+ this, &ProjectManager::configureEditor);
+ connect(this, &ProjectManager::projectAdded,
+ EditorManager::instance(), &EditorManager::updateWindowTitles);
+ connect(this, &ProjectManager::projectRemoved,
+ EditorManager::instance(), &EditorManager::updateWindowTitles);
+ connect(this, &ProjectManager::projectDisplayNameChanged,
+ EditorManager::instance(), &EditorManager::updateWindowTitles);
+
+ EditorManager::setWindowTitleAdditionHandler(&ProjectManagerPrivate::windowTitleAddition);
+ EditorManager::setSessionTitleHandler(&ProjectManagerPrivate::sessionTitle);
+
+ connect(SessionManager::instance(), &SessionManager::aboutToLoadSession, this, [] {
+ d->loadSession();
+ });
+}
+
+ProjectManager::~ProjectManager()
+{
+ EditorManager::setWindowTitleAdditionHandler({});
+ EditorManager::setSessionTitleHandler({});
+ delete d;
+ d = nullptr;
+}
+
+ProjectManager *ProjectManager::instance()
+{
+ return m_instance;
+}
+
+bool ProjectManagerPrivate::recursiveDependencyCheck(const FilePath &newDep,
+ const FilePath &checkDep) const
+{
+ if (newDep == checkDep)
+ return false;
+
+ const FilePaths depList = m_depMap.value(checkDep);
+ for (const FilePath &dependency : depList) {
+ if (!recursiveDependencyCheck(newDep, dependency))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * The dependency management exposes an interface based on projects, but
+ * is internally purely string based. This is suboptimal. Probably it would be
+ * nicer to map the filenames to projects on load and only map it back to
+ * filenames when saving.
+ */
+
+QList<Project *> ProjectManager::dependencies(const Project *project)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePaths proDeps = d->m_depMap.value(proName);
+
+ QList<Project *> projects;
+ for (const FilePath &dep : proDeps) {
+ Project *pro = Utils::findOrDefault(d->m_projects, [&dep](Project *p) {
+ return p->projectFilePath() == dep;
+ });
+ if (pro)
+ projects += pro;
+ }
+
+ return projects;
+}
+
+bool ProjectManager::hasDependency(const Project *project, const Project *depProject)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePath depName = depProject->projectFilePath();
+
+ const FilePaths proDeps = d->m_depMap.value(proName);
+ return proDeps.contains(depName);
+}
+
+bool ProjectManager::canAddDependency(const Project *project, const Project *depProject)
+{
+ const FilePath newDep = project->projectFilePath();
+ const FilePath checkDep = depProject->projectFilePath();
+
+ return d->recursiveDependencyCheck(newDep, checkDep);
+}
+
+bool ProjectManager::addDependency(Project *project, Project *depProject)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePath depName = depProject->projectFilePath();
+
+ // check if this dependency is valid
+ if (!d->recursiveDependencyCheck(proName, depName))
+ return false;
+
+ FilePaths proDeps = d->m_depMap.value(proName);
+ if (!proDeps.contains(depName)) {
+ proDeps.append(depName);
+ d->m_depMap[proName] = proDeps;
+ }
+ emit m_instance->dependencyChanged(project, depProject);
+
+ return true;
+}
+
+void ProjectManager::removeDependency(Project *project, Project *depProject)
+{
+ const FilePath proName = project->projectFilePath();
+ const FilePath depName = depProject->projectFilePath();
+
+ FilePaths proDeps = d->m_depMap.value(proName);
+ proDeps.removeAll(depName);
+ if (proDeps.isEmpty())
+ d->m_depMap.remove(proName);
+ else
+ d->m_depMap[proName] = proDeps;
+ emit m_instance->dependencyChanged(project, depProject);
+}
+
+bool ProjectManager::isProjectConfigurationCascading()
+{
+ return d->m_casadeSetActive;
+}
+
+void ProjectManager::setProjectConfigurationCascading(bool b)
+{
+ d->m_casadeSetActive = b;
+ SessionManager::markSessionFileDirty();
+}
+
+void ProjectManager::setStartupProject(Project *startupProject)
+{
+ QTC_ASSERT((!startupProject && d->m_projects.isEmpty())
+ || (startupProject && d->m_projects.contains(startupProject)), return);
+
+ if (d->m_startupProject == startupProject)
+ return;
+
+ d->m_startupProject = startupProject;
+ if (d->m_startupProject && d->m_startupProject->needsConfiguration()) {
+ ModeManager::activateMode(Constants::MODE_SESSION);
+ ModeManager::setFocusToCurrentMode();
+ }
+ FolderNavigationWidgetFactory::setFallbackSyncFilePath(
+ startupProject ? startupProject->projectFilePath().parentDir() : FilePath());
+ emit m_instance->startupProjectChanged(startupProject);
+}
+
+Project *ProjectManager::startupProject()
+{
+ return d->m_startupProject;
+}
+
+Target *ProjectManager::startupTarget()
+{
+ return d->m_startupProject ? d->m_startupProject->activeTarget() : nullptr;
+}
+
+BuildSystem *ProjectManager::startupBuildSystem()
+{
+ Target *t = startupTarget();
+ return t ? t->buildSystem() : nullptr;
+}
+
+/*!
+ * Returns the RunConfiguration of the currently active target
+ * of the startup project, if such exists, or \c nullptr otherwise.
+ */
+
+
+RunConfiguration *ProjectManager::startupRunConfiguration()
+{
+ Target *t = startupTarget();
+ return t ? t->activeRunConfiguration() : nullptr;
+}
+
+void ProjectManager::addProject(Project *pro)
+{
+ QTC_ASSERT(pro, return);
+ QTC_CHECK(!pro->displayName().isEmpty());
+ QTC_CHECK(pro->id().isValid());
+
+ sb_d->m_virginSession = false;
+ QTC_ASSERT(!d->m_projects.contains(pro), return);
+
+ d->m_projects.append(pro);
+
+ connect(pro, &Project::displayNameChanged,
+ m_instance, [pro]() { emit m_instance->projectDisplayNameChanged(pro); });
+
+ emit m_instance->projectAdded(pro);
+ const auto updateFolderNavigation = [pro] {
+ // destructing projects might trigger changes, so check if the project is actually there
+ if (QTC_GUARD(d->m_projects.contains(pro))) {
+ const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon();
+ FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro),
+ PROJECT_SORT_VALUE,
+ pro->displayName(),
+ pro->projectFilePath().parentDir(),
+ icon});
+ }
+ };
+ updateFolderNavigation();
+ configureEditors(pro);
+ connect(pro, &Project::fileListChanged, m_instance, [pro, updateFolderNavigation]() {
+ configureEditors(pro);
+ updateFolderNavigation(); // update icon
+ });
+ connect(pro, &Project::displayNameChanged, m_instance, updateFolderNavigation);
+
+ if (!startupProject())
+ setStartupProject(pro);
+}
+
+void ProjectManager::removeProject(Project *project)
+{
+ sb_d->m_virginSession = false;
+ QTC_ASSERT(project, return);
+ removeProjects({project});
+}
+
+bool ProjectManager::save()
+{
+ emit SessionManager::instance()->aboutToSaveSession();
+
+ const FilePath filePath = SessionManager::sessionNameToFileName(sb_d->m_sessionName);
+ QVariantMap data;
+
+ // See the explanation at loadSession() for how we handle the implicit default session.
+ if (SessionManager::isDefaultVirgin()) {
+ if (filePath.exists()) {
+ PersistentSettingsReader reader;
+ if (!reader.load(filePath)) {
+ QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
+ Tr::tr("Could not save session %1").arg(filePath.toUserOutput()));
+ return false;
+ }
+ data = reader.restoreValues();
+ }
+ } else {
+ // save the startup project
+ if (d->m_startupProject)
+ data.insert("StartupProject", d->m_startupProject->projectFilePath().toSettings());
+
+ const QColor c = StyleHelper::requestedBaseColor();
+ if (c.isValid()) {
+ QString tmp = QString::fromLatin1("#%1%2%3")
+ .arg(c.red(), 2, 16, QLatin1Char('0'))
+ .arg(c.green(), 2, 16, QLatin1Char('0'))
+ .arg(c.blue(), 2, 16, QLatin1Char('0'));
+ data.insert(QLatin1String("Color"), tmp);
+ }
+
+ FilePaths projectFiles = Utils::transform(projects(), &Project::projectFilePath);
+ // Restore information on projects that failed to load:
+ // don't read projects to the list, which the user loaded
+ for (const FilePath &failed : std::as_const(d->m_failedProjects)) {
+ if (!projectFiles.contains(failed))
+ projectFiles << failed;
+ }
+
+ data.insert("ProjectList", Utils::transform<QStringList>(projectFiles,
+ &FilePath::toString));
+ data.insert("CascadeSetActive", d->m_casadeSetActive);
+
+ QVariantMap depMap;
+ auto i = d->m_depMap.constBegin();
+ while (i != d->m_depMap.constEnd()) {
+ QString key = i.key().toString();
+ QStringList values;
+ const FilePaths valueList = i.value();
+ for (const FilePath &value : valueList)
+ values << value.toString();
+ depMap.insert(key, values);
+ ++i;
+ }
+ data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap));
+ data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64());
+ }
+
+ const auto end = sb_d->m_values.constEnd();
+ QStringList keys;
+ for (auto it = sb_d->m_values.constBegin(); it != end; ++it) {
+ data.insert(QLatin1String("value-") + it.key(), it.value());
+ keys << it.key();
+ }
+ data.insert(QLatin1String("valueKeys"), keys);
+
+ if (!sb_d->m_writer || sb_d->m_writer->fileName() != filePath) {
+ delete sb_d->m_writer;
+ sb_d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession");
+ }
+ const bool result = sb_d->m_writer->save(data, ICore::dialogParent());
+ if (result) {
+ if (!SessionManager::isDefaultVirgin())
+ sb_d->m_sessionDateTimes.insert(SessionManager::activeSession(), QDateTime::currentDateTime());
+ } else {
+ QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
+ Tr::tr("Could not save session to file %1").arg(sb_d->m_writer->fileName().toUserOutput()));
+ }
+
+ return result;
+}
+
+/*!
+ Closes all projects
+ */
+void ProjectManager::closeAllProjects()
+{
+ removeProjects(projects());
+}
+
+const QList<Project *> ProjectManager::projects()
+{
+ return d->m_projects;
+}
+
+bool ProjectManager::hasProjects()
+{
+ return d->hasProjects();
+}
+
+bool ProjectManager::hasProject(Project *p)
+{
+ return d->m_projects.contains(p);
+}
+
+FilePaths ProjectManagerPrivate::dependencies(const FilePath &proName) const
+{
+ FilePaths result;
+ dependencies(proName, result);
+ return result;
+}
+
+void ProjectManagerPrivate::dependencies(const FilePath &proName, FilePaths &result) const
+{
+ const FilePaths depends = m_depMap.value(proName);
+
+ for (const FilePath &dep : depends)
+ dependencies(dep, result);
+
+ if (!result.contains(proName))
+ result.append(proName);
+}
+
+QString ProjectManagerPrivate::sessionTitle(const FilePath &filePath)
+{
+ if (SessionManager::isDefaultSession(sb_d->m_sessionName)) {
+ if (filePath.isEmpty()) {
+ // use single project's name if there is only one loaded.
+ const QList<Project *> projects = ProjectManager::projects();
+ if (projects.size() == 1)
+ return projects.first()->displayName();
+ }
+ } else {
+ QString sessionName = sb_d->m_sessionName;
+ if (sessionName.isEmpty())
+ sessionName = Tr::tr("Untitled");
+ return sessionName;
+ }
+ return QString();
+}
+
+QString ProjectManagerPrivate::locationInProject(const FilePath &filePath)
+{
+ const Project *project = ProjectManager::projectForFile(filePath);
+ if (!project)
+ return QString();
+
+ const FilePath parentDir = filePath.parentDir();
+ if (parentDir == project->projectDirectory())
+ return "@ " + project->displayName();
+
+ if (filePath.isChildOf(project->projectDirectory())) {
+ const FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory());
+ return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")";
+ }
+
+ // For a file that is "outside" the project it belongs to, we display its
+ // dir's full path because it is easier to read than a series of "../../.".
+ // Example: /home/hugo/GenericProject/App.files lists /home/hugo/lib/Bar.cpp
+ return "(" + parentDir.toUserOutput() + " @ " + project->displayName() + ")";
+}
+
+QString ProjectManagerPrivate::windowTitleAddition(const FilePath &filePath)
+{
+ return filePath.isEmpty() ? QString() : locationInProject(filePath);
+}
+
+FilePaths ProjectManagerPrivate::dependenciesOrder() const
+{
+ QList<QPair<FilePath, FilePaths>> unordered;
+ FilePaths ordered;
+
+ // copy the map to a temporary list
+ for (const Project *pro : m_projects) {
+ const FilePath proName = pro->projectFilePath();
+ const FilePaths depList = filtered(m_depMap.value(proName),
+ [this](const FilePath &proPath) {
+ return contains(m_projects, [proPath](const Project *p) {
+ return p->projectFilePath() == proPath;
+ });
+ });
+ unordered.push_back({proName, depList});
+ }
+
+ while (!unordered.isEmpty()) {
+ for (int i = (unordered.count() - 1); i >= 0; --i) {
+ if (unordered.at(i).second.isEmpty()) {
+ ordered << unordered.at(i).first;
+ unordered.removeAt(i);
+ }
+ }
+
+ // remove the handled projects from the dependency lists
+ // of the remaining unordered projects
+ for (int i = 0; i < unordered.count(); ++i) {
+ for (const FilePath &pro : std::as_const(ordered)) {
+ FilePaths depList = unordered.at(i).second;
+ depList.removeAll(pro);
+ unordered[i].second = depList;
+ }
+ }
+ }
+
+ return ordered;
+}
+
+QList<Project *> ProjectManager::projectOrder(const Project *project)
+{
+ QList<Project *> result;
+
+ FilePaths pros;
+ if (project)
+ pros = d->dependencies(project->projectFilePath());
+ else
+ pros = d->dependenciesOrder();
+
+ for (const FilePath &proFile : std::as_const(pros)) {
+ for (Project *pro : projects()) {
+ if (pro->projectFilePath() == proFile) {
+ result << pro;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+Project *ProjectManager::projectForFile(const FilePath &fileName)
+{
+ if (Project * const project = Utils::findOrDefault(ProjectManager::projects(),
+ [&fileName](const Project *p) { return p->isKnownFile(fileName); })) {
+ return project;
+ }
+ return Utils::findOrDefault(ProjectManager::projects(),
+ [&fileName](const Project *p) {
+ for (const Target * const target : p->targets()) {
+ for (const BuildConfiguration * const bc : target->buildConfigurations()) {
+ if (fileName.isChildOf(bc->buildDirectory()))
+ return false;
+ }
+ }
+ return fileName.isChildOf(p->projectDirectory());
+ });
+}
+
+Project *ProjectManager::projectWithProjectFilePath(const FilePath &filePath)
+{
+ return Utils::findOrDefault(ProjectManager::projects(),
+ [&filePath](const Project *p) { return p->projectFilePath() == filePath; });
+}
+
+void ProjectManager::configureEditor(IEditor *editor, const QString &fileName)
+{
+ if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
+ Project *project = projectForFile(Utils::FilePath::fromString(fileName));
+ // Global settings are the default.
+ if (project)
+ project->editorConfiguration()->configureEditor(textEditor);
+ }
+}
+
+void ProjectManager::configureEditors(Project *project)
+{
+ const QList<IDocument *> documents = DocumentModel::openedDocuments();
+ for (IDocument *document : documents) {
+ if (project->isKnownFile(document->filePath())) {
+ const QList<IEditor *> editors = DocumentModel::editorsForDocument(document);
+ for (IEditor *editor : editors) {
+ if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
+ project->editorConfiguration()->configureEditor(textEditor);
+ }
+ }
+ }
+ }
+}
+
+void ProjectManager::removeProjects(const QList<Project *> &remove)
+{
+ for (Project *pro : remove)
+ emit m_instance->aboutToRemoveProject(pro);
+
+ bool changeStartupProject = false;
+
+ // Delete projects
+ for (Project *pro : remove) {
+ pro->saveSettings();
+ pro->markAsShuttingDown();
+
+ // Remove the project node:
+ d->m_projects.removeOne(pro);
+
+ if (pro == d->m_startupProject)
+ changeStartupProject = true;
+
+ FolderNavigationWidgetFactory::removeRootDirectory(projectFolderId(pro));
+ disconnect(pro, nullptr, m_instance, nullptr);
+ emit m_instance->projectRemoved(pro);
+ }
+
+ if (changeStartupProject)
+ setStartupProject(hasProjects() ? projects().first() : nullptr);
+
+ qDeleteAll(remove);
+}
+
+void ProjectManagerPrivate::restoreDependencies()
+{
+ QMap<QString, QVariant> depMap = SessionManager::sessionValue("ProjectDependencies").toMap();
+ auto i = depMap.constBegin();
+ while (i != depMap.constEnd()) {
+ const QString &key = i.key();
+ FilePaths values;
+ const QStringList valueList = i.value().toStringList();
+ for (const QString &value : valueList)
+ values << FilePath::fromString(value);
+ m_depMap.insert(FilePath::fromString(key), values);
+ ++i;
+ }
+}
+
+void ProjectManagerPrivate::askUserAboutFailedProjects()
+{
+ FilePaths failedProjects = m_failedProjects;
+ if (!failedProjects.isEmpty()) {
+ QString fileList = FilePath::formatFilePaths(failedProjects, "<br>");
+ QMessageBox box(QMessageBox::Warning,
+ Tr::tr("Failed to restore project files"),
+ Tr::tr("Could not restore the following project files:<br><b>%1</b>").
+ arg(fileList));
+ auto keepButton = new QPushButton(Tr::tr("Keep projects in Session"), &box);
+ auto removeButton = new QPushButton(Tr::tr("Remove projects from Session"), &box);
+ box.addButton(keepButton, QMessageBox::AcceptRole);
+ box.addButton(removeButton, QMessageBox::DestructiveRole);
+
+ box.exec();
+
+ if (box.clickedButton() == removeButton)
+ m_failedProjects.clear();
+ }
+}
+
+void ProjectManagerPrivate::restoreStartupProject()
+{
+ const FilePath startupProject = FilePath::fromSettings(
+ SessionManager::sessionValue("StartupProject"));
+ if (!startupProject.isEmpty()) {
+ for (Project *pro : std::as_const(m_projects)) {
+ if (pro->projectFilePath() == startupProject) {
+ m_instance->setStartupProject(pro);
+ break;
+ }
+ }
+ }
+ if (!m_startupProject) {
+ if (!startupProject.isEmpty())
+ qWarning() << "Could not find startup project" << startupProject;
+ if (hasProjects())
+ m_instance->setStartupProject(m_projects.first());
+ }
+}
+
+/*!
+ Loads a session, takes a session name (not filename).
+*/
+void ProjectManagerPrivate::restoreProjects(const FilePaths &fileList)
+{
+ // indirectly adds projects to session
+ // Keep projects that failed to load in the session!
+ m_failedProjects = fileList;
+ if (!fileList.isEmpty()) {
+ ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList);
+ if (!result)
+ ProjectExplorerPlugin::showOpenProjectError(result);
+ const QList<Project *> projects = result.projects();
+ for (const Project *p : projects)
+ m_failedProjects.removeAll(p->projectFilePath());
+ }
+}
+
+void ProjectManagerPrivate::loadSession()
+{
+ d->m_failedProjects.clear();
+ d->m_depMap.clear();
+ d->m_casadeSetActive = false;
+
+ // not ideal that this is in ProjectManager
+ Id modeId = Id::fromSetting(SessionManager::value(QLatin1String("ActiveMode")));
+ if (!modeId.isValid())
+ modeId = Id(Core::Constants::MODE_EDIT);
+
+ // find a list of projects to close later
+ const FilePaths fileList = FileUtils::toFilePathList(
+ SessionManager::sessionValue("ProjectList").toStringList());
+ const QList<Project *> projectsToRemove
+ = Utils::filtered(ProjectManager::projects(), [&fileList](Project *p) {
+ return !fileList.contains(p->projectFilePath());
+ });
+ const QList<Project *> openProjects = ProjectManager::projects();
+ const FilePaths projectPathsToLoad
+ = Utils::filtered(fileList, [&openProjects](const FilePath &path) {
+ return !Utils::contains(openProjects,
+ [&path](Project *p) { return p->projectFilePath() == path; });
+ });
+
+ SessionManager::addSessionLoadingSteps(projectPathsToLoad.count());
+
+ d->restoreProjects(projectPathsToLoad);
+ d->restoreDependencies();
+ d->restoreStartupProject();
+
+ // only remove old projects now that the startup project is set!
+ ProjectManager::removeProjects(projectsToRemove);
+
+ // Fall back to Project mode if the startup project is unconfigured and
+ // use the mode saved in the session otherwise
+ if (d->m_startupProject && d->m_startupProject->needsConfiguration())
+ modeId = Id(Constants::MODE_SESSION);
+
+ ModeManager::activateMode(modeId);
+ ModeManager::setFocusToCurrentMode();
+
+ d->m_casadeSetActive = SessionManager::sessionValue("CascadeSetActive", false).toBool();
+
+ // Starts a event loop, better do that at the very end
+ QMetaObject::invokeMethod(m_instance, [this] { askUserAboutFailedProjects(); });
+}
+
+FilePaths ProjectManager::projectsForSessionName(const QString &session)
+{
+ const FilePath fileName = SessionManager::sessionNameToFileName(session);
+ PersistentSettingsReader reader;
+ if (fileName.exists()) {
+ if (!reader.load(fileName)) {
+ qWarning() << "Could not restore session" << fileName.toUserOutput();
+ return {};
+ }
+ }
+ return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(),
+ &FilePath::fromUserInput);
+}
+
+#ifdef WITH_TESTS
+
+void ProjectExplorerPlugin::testSessionSwitch()
+{
+ QVERIFY(SessionManager::createSession("session1"));
+ QVERIFY(SessionManager::createSession("session2"));
+ QTemporaryFile cppFile("main.cpp");
+ QVERIFY(cppFile.open());
+ cppFile.close();
+ QTemporaryFile projectFile1("XXXXXX.pro");
+ QTemporaryFile projectFile2("XXXXXX.pro");
+ struct SessionSpec {
+ SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {}
+ const QString name;
+ QTemporaryFile &projectFile;
+ };
+ std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1),
+ SessionSpec("session2", projectFile2)};
+ for (const SessionSpec &sessionSpec : sessionSpecs) {
+ static const QByteArray proFileContents
+ = "TEMPLATE = app\n"
+ "CONFIG -= qt\n"
+ "SOURCES = " + cppFile.fileName().toLocal8Bit();
+ QVERIFY(sessionSpec.projectFile.open());
+ sessionSpec.projectFile.write(proFileContents);
+ sessionSpec.projectFile.close();
+ QVERIFY(SessionManager::loadSession(sessionSpec.name));
+ const OpenProjectResult openResult
+ = ProjectExplorerPlugin::openProject(
+ FilePath::fromString(sessionSpec.projectFile.fileName()));
+ if (openResult.errorMessage().contains("text/plain"))
+ QSKIP("This test requires the presence of QmakeProjectManager to be fully functional");
+ QVERIFY(openResult);
+ QCOMPARE(openResult.projects().count(), 1);
+ QVERIFY(openResult.project());
+ QCOMPARE(ProjectManager::projects().count(), 1);
+ }
+ for (int i = 0; i < 30; ++i) {
+ QVERIFY(SessionManager::loadSession("session1"));
+ QCOMPARE(SessionManager::activeSession(), "session1");
+ QCOMPARE(ProjectManager::projects().count(), 1);
+ QVERIFY(SessionManager::loadSession("session2"));
+ QCOMPARE(SessionManager::activeSession(), "session2");
+ QCOMPARE(ProjectManager::projects().count(), 1);
+ }
+ QVERIFY(SessionManager::loadSession("session1"));
+ ProjectManager::closeAllProjects();
+ QVERIFY(SessionManager::loadSession("session2"));
+ ProjectManager::closeAllProjects();
+ QVERIFY(SessionManager::deleteSession("session1"));
+ QVERIFY(SessionManager::deleteSession("session2"));
+}
+
+#endif // WITH_TESTS
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/projectmanager.h b/src/plugins/projectexplorer/projectmanager.h
index 9d54eba572..7f51586901 100644
--- a/src/plugins/projectexplorer/projectmanager.h
+++ b/src/plugins/projectexplorer/projectmanager.h
@@ -6,20 +6,35 @@
#include "projectexplorer_export.h"
#include <QString>
+#include <QObject>
+
+namespace Core { class IEditor; }
#include <functional>
namespace Utils {
class FilePath;
+using FilePaths = QList<FilePath>;
class MimeType;
} // Utils
namespace ProjectExplorer {
+class BuildSystem;
class Project;
+class RunConfiguration;
+class Target;
-class PROJECTEXPLORER_EXPORT ProjectManager
+class PROJECTEXPLORER_EXPORT ProjectManager : public QObject
{
+ Q_OBJECT
+
+public:
+ ProjectManager();
+ ~ProjectManager() override;
+
+ static ProjectManager *instance();
+
public:
static bool canOpenProjectForMimeType(const Utils::MimeType &mt);
static Project *openProject(const Utils::MimeType &mt, const Utils::FilePath &fileName);
@@ -32,7 +47,60 @@ public:
});
}
+ static bool save();
+ static void closeAllProjects();
+
+ static void addProject(Project *project);
+ static void removeProject(Project *project);
+ static void removeProjects(const QList<Project *> &remove);
+
+ static void setStartupProject(Project *startupProject);
+
+ static QList<Project *> dependencies(const Project *project);
+ static bool hasDependency(const Project *project, const Project *depProject);
+ static bool canAddDependency(const Project *project, const Project *depProject);
+ static bool addDependency(Project *project, Project *depProject);
+ static void removeDependency(Project *project, Project *depProject);
+
+ static bool isProjectConfigurationCascading();
+ static void setProjectConfigurationCascading(bool b);
+
+ static Project *startupProject();
+ static Target *startupTarget();
+ static BuildSystem *startupBuildSystem();
+ static RunConfiguration *startupRunConfiguration();
+
+ static const QList<Project *> projects();
+ static bool hasProjects();
+ static bool hasProject(Project *p);
+
+ // NBS rewrite projectOrder (dependency management)
+ static QList<Project *> projectOrder(const Project *project = nullptr);
+
+ static Project *projectForFile(const Utils::FilePath &fileName);
+ static Project *projectWithProjectFilePath(const Utils::FilePath &filePath);
+
+ static Utils::FilePaths projectsForSessionName(const QString &session);
+
+signals:
+ void targetAdded(ProjectExplorer::Target *target);
+ void targetRemoved(ProjectExplorer::Target *target);
+ void projectAdded(ProjectExplorer::Project *project);
+ void aboutToRemoveProject(ProjectExplorer::Project *project);
+ void projectDisplayNameChanged(ProjectExplorer::Project *project);
+ void projectRemoved(ProjectExplorer::Project *project);
+
+ void startupProjectChanged(ProjectExplorer::Project *project);
+
+ void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b);
+
+ // for tests only
+ void projectFinishedParsing(ProjectExplorer::Project *project);
+
private:
+ static void configureEditor(Core::IEditor *editor, const QString &fileName);
+ static void configureEditors(Project *project);
+
static void registerProjectCreator(const QString &mimeType,
const std::function<Project *(const Utils::FilePath &)> &);
};
diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp
index 0a761f91e6..bc5a0ef4d6 100644
--- a/src/plugins/projectexplorer/projectmodels.cpp
+++ b/src/plugins/projectexplorer/projectmodels.cpp
@@ -8,6 +8,7 @@
#include "projectnodes.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projecttree.h"
#include "session.h"
#include "target.h"
@@ -24,8 +25,8 @@
#include <utils/dropsupport.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/theme/theme.h>
@@ -174,14 +175,15 @@ FlatModel::FlatModel(QObject *parent)
ProjectTree *tree = ProjectTree::instance();
connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::updateSubtree);
- SessionManager *sm = SessionManager::instance();
- connect(sm, &SessionManager::projectRemoved, this, &FlatModel::handleProjectRemoved);
- connect(sm, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData);
- connect(sm, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData);
- connect(sm, &SessionManager::projectAdded, this, &FlatModel::handleProjectAdded);
- connect(sm, &SessionManager::startupProjectChanged, this, [this] { emit layoutChanged(); });
+ ProjectManager *sm = ProjectManager::instance();
+ SessionManager *sb = SessionManager::instance();
+ connect(sm, &ProjectManager::projectRemoved, this, &FlatModel::handleProjectRemoved);
+ connect(sb, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData);
+ connect(sb, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData);
+ connect(sm, &ProjectManager::projectAdded, this, &FlatModel::handleProjectAdded);
+ connect(sm, &ProjectManager::startupProjectChanged, this, [this] { emit layoutChanged(); });
- for (Project *project : SessionManager::projects())
+ for (Project *project : ProjectManager::projects())
handleProjectAdded(project);
}
@@ -234,7 +236,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const
}
case Qt::FontRole: {
QFont font;
- if (project == SessionManager::startupProject())
+ if (project == ProjectManager::startupProject())
font.setBold(true);
return font;
}
@@ -407,7 +409,7 @@ void FlatModel::updateSubtree(FolderNode *node)
void FlatModel::rebuildModel()
{
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
for (Project *project : projects)
addOrRebuildProjectModel(project);
}
diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp
index 10699b7d90..e5c8a9a3c6 100644
--- a/src/plugins/projectexplorer/projectnodes.cpp
+++ b/src/plugins/projectexplorer/projectnodes.cpp
@@ -326,14 +326,13 @@ FilePath Node::pathOrDirectory(bool dir) const
FilePath location;
// Virtual Folder case
// If there are files directly below or no subfolders, take the folder path
- if (!folder->fileNodes().isEmpty() || folder->folderNodes().isEmpty()) {
+ auto Any = [](auto) { return true; };
+ if (folder->findChildFileNode(Any) || !folder->findChildFolderNode(Any)) {
location = m_filePath;
} else {
// Otherwise we figure out a commonPath from the subfolders
FilePaths list;
- const QList<FolderNode *> folders = folder->folderNodes();
- for (FolderNode *f : folders)
- list << f->filePath();
+ folder->forEachFolderNode([&](FolderNode *f) { list << f->filePath(); });
location = FileUtils::commonPath(list);
}
@@ -547,6 +546,22 @@ void FolderNode::forEachProjectNode(const std::function<void(const ProjectNode *
}
}
+void FolderNode::forEachFileNode(const std::function<void (FileNode *)> &fileTask) const
+{
+ for (const std::unique_ptr<Node> &n : m_nodes) {
+ if (FileNode *fn = n->asFileNode())
+ fileTask(fn);
+ }
+}
+
+void FolderNode::forEachFolderNode(const std::function<void (FolderNode *)> &folderTask) const
+{
+ for (const std::unique_ptr<Node> &n : m_nodes) {
+ if (FolderNode *fn = n->asFolderNode())
+ folderTask(fn);
+ }
+}
+
ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectNode *)> &predicate)
{
if (ProjectNode *projectNode = asProjectNode()) {
@@ -563,19 +578,29 @@ ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectN
return nullptr;
}
-const QList<Node *> FolderNode::nodes() const
+FolderNode *FolderNode::findChildFolderNode(const std::function<bool(FolderNode *)> &predicate) const
{
- return Utils::toRawPointer<QList>(m_nodes);
+ for (const std::unique_ptr<Node> &n : m_nodes) {
+ if (FolderNode *fn = n->asFolderNode())
+ if (predicate(fn))
+ return fn;
+ }
+ return nullptr;
}
-QList<FileNode *> FolderNode::fileNodes() const
+FileNode *FolderNode::findChildFileNode(const std::function<bool(FileNode *)> &predicate) const
{
- QList<FileNode *> result;
for (const std::unique_ptr<Node> &n : m_nodes) {
if (FileNode *fn = n->asFileNode())
- result.append(fn);
+ if (predicate(fn))
+ return fn;
}
- return result;
+ return nullptr;
+}
+
+const QList<Node *> FolderNode::nodes() const
+{
+ return Utils::toRawPointer<QList>(m_nodes);
}
FileNode *FolderNode::fileNode(const Utils::FilePath &file) const
@@ -587,16 +612,6 @@ FileNode *FolderNode::fileNode(const Utils::FilePath &file) const
}));
}
-QList<FolderNode *> FolderNode::folderNodes() const
-{
- QList<FolderNode *> result;
- for (const std::unique_ptr<Node> &n : m_nodes) {
- if (FolderNode *fn = n->asFolderNode())
- result.append(fn);
- }
- return result;
-}
-
FolderNode *FolderNode::folderNode(const Utils::FilePath &directory) const
{
Node *node = Utils::findOrDefault(m_nodes, [directory](const std::unique_ptr<Node> &n) {
@@ -669,8 +684,7 @@ void FolderNode::compress()
compress();
} else {
- for (FolderNode *fn : folderNodes())
- fn->compress();
+ forEachFolderNode([&](FolderNode *fn) { fn->compress(); });
}
}
diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h
index 35927c02c8..0e0068ba7b 100644
--- a/src/plugins/projectexplorer/projectnodes.h
+++ b/src/plugins/projectexplorer/projectnodes.h
@@ -32,6 +32,8 @@ enum class FileType : quint16 {
Resource,
QML,
Project,
+ App,
+ Lib,
FileTypeSize
};
@@ -228,11 +230,13 @@ public:
const std::function<bool(const FolderNode *)> &folderFilterTask = {}) const;
void forEachGenericNode(const std::function<void(Node *)> &genericTask) const;
void forEachProjectNode(const std::function<void(const ProjectNode *)> &genericTask) const;
- ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate);
+ void forEachFileNode(const std::function<void(FileNode *)> &fileTask) const;
+ void forEachFolderNode(const std::function<void(FolderNode *)> &folderTask) const;
+ ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate); // recursive
+ FolderNode *findChildFolderNode(const std::function<bool (FolderNode *)> &predicate) const; // non-recursive
+ FileNode *findChildFileNode(const std::function<bool (FileNode *)> &predicate) const; // non-recursive
const QList<Node *> nodes() const;
- QList<FileNode *> fileNodes() const;
FileNode *fileNode(const Utils::FilePath &file) const;
- QList<FolderNode *> folderNodes() const;
FolderNode *folderNode(const Utils::FilePath &directory) const;
using FolderNodeFactory = std::function<std::unique_ptr<FolderNode>(const Utils::FilePath &)>;
diff --git a/src/plugins/projectexplorer/projectnodeshelper.h b/src/plugins/projectexplorer/projectnodeshelper.h
index 727497c6bf..bcb5454fd8 100644
--- a/src/plugins/projectexplorer/projectnodeshelper.h
+++ b/src/plugins/projectexplorer/projectnodeshelper.h
@@ -11,18 +11,19 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
+#include <QPromise>
+
namespace ProjectExplorer {
template<typename Result>
-QList<FileNode *> scanForFiles(QFutureInterface<Result> &future,
- const Utils::FilePath &directory,
+QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory,
const std::function<FileNode *(const Utils::FilePath &)> factory);
namespace Internal {
template<typename Result>
QList<FileNode *> scanForFilesRecursively(
- QFutureInterface<Result> &future,
+ QPromise<Result> &promise,
double progressStart,
double progressRange,
const Utils::FilePath &directory,
@@ -46,7 +47,7 @@ QList<FileNode *> scanForFilesRecursively(
const double progressIncrement = progressRange / static_cast<double>(entries.count());
int lastIntProgress = 0;
for (const QFileInfo &entry : entries) {
- if (future.isCanceled())
+ if (promise.isCanceled())
return result;
const Utils::FilePath entryName = Utils::FilePath::fromString(entry.absoluteFilePath());
@@ -54,7 +55,7 @@ QList<FileNode *> scanForFilesRecursively(
return vc->isVcsFileOrDirectory(entryName);
})) {
if (entry.isDir())
- result.append(scanForFilesRecursively(future,
+ result.append(scanForFilesRecursively(promise,
progress,
progressIncrement,
entryName,
@@ -66,26 +67,25 @@ QList<FileNode *> scanForFilesRecursively(
}
progress += progressIncrement;
const int intProgress = std::min(static_cast<int>(progressStart + progress),
- future.progressMaximum());
+ promise.future().progressMaximum());
if (lastIntProgress < intProgress) {
- future.setProgressValue(intProgress);
+ promise.setProgressValue(intProgress);
lastIntProgress = intProgress;
}
}
- future.setProgressValue(
- std::min(static_cast<int>(progressStart + progressRange), future.progressMaximum()));
+ promise.setProgressValue(std::min(static_cast<int>(progressStart + progressRange),
+ promise.future().progressMaximum()));
return result;
}
} // namespace Internal
template<typename Result>
-QList<FileNode *> scanForFiles(QFutureInterface<Result> &future,
- const Utils::FilePath &directory,
+QList<FileNode *> scanForFiles(QPromise<Result> &promise, const Utils::FilePath &directory,
const std::function<FileNode *(const Utils::FilePath &)> factory)
{
QSet<QString> visited;
- future.setProgressRange(0, 1000000);
- return Internal::scanForFilesRecursively(future,
+ promise.setProgressRange(0, 1000000);
+ return Internal::scanForFilesRecursively(promise,
0.0,
1000000.0,
directory,
diff --git a/src/plugins/projectexplorer/projecttree.cpp b/src/plugins/projectexplorer/projecttree.cpp
index 18b03d3ae0..5f81f473fe 100644
--- a/src/plugins/projectexplorer/projecttree.cpp
+++ b/src/plugins/projectexplorer/projecttree.cpp
@@ -6,9 +6,9 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "projecttreewidget.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/actionmanager/actioncontainer.h>
@@ -53,11 +53,11 @@ ProjectTree::ProjectTree(QObject *parent) : QObject(parent)
connect(qApp, &QApplication::focusChanged,
this, &ProjectTree::update);
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, &ProjectTree::sessionAndTreeChanged);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, &ProjectTree::sessionAndTreeChanged);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &ProjectTree::sessionChanged);
connect(this, &ProjectTree::subtreeChanged, this, &ProjectTree::treeChanged);
}
@@ -170,7 +170,7 @@ void ProjectTree::updateFromNode(Node *node)
if (node)
project = projectForNode(node);
else
- project = SessionManager::startupProject();
+ project = ProjectManager::startupProject();
setCurrent(node, project);
for (ProjectTreeWidget *widget : std::as_const(m_projectTreeWidgets))
@@ -224,7 +224,7 @@ void ProjectTree::sessionChanged()
{
if (m_currentProject) {
Core::DocumentManager::setDefaultLocationForNewFiles(m_currentProject->projectDirectory());
- } else if (Project *project = SessionManager::startupProject()) {
+ } else if (Project *project = ProjectManager::startupProject()) {
Core::DocumentManager::setDefaultLocationForNewFiles(project->projectDirectory());
updateFromNode(nullptr); // Make startup project current if there is no other current
} else {
@@ -300,7 +300,7 @@ void ProjectTree::updateFileWarning(Core::IDocument *document, const QString &te
if (!infoBar->canInfoBeAdded(infoId))
return;
const FilePath filePath = document->filePath();
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
if (projects.isEmpty())
return;
for (Project *project : projects) {
@@ -394,7 +394,7 @@ void ProjectTree::applyTreeManager(FolderNode *folder, ConstructionPhase phase)
bool ProjectTree::hasNode(const Node *node)
{
- return Utils::contains(SessionManager::projects(), [node](const Project *p) {
+ return Utils::contains(ProjectManager::projects(), [node](const Project *p) {
if (!p)
return false;
if (p->containerNode() == node)
@@ -409,7 +409,7 @@ bool ProjectTree::hasNode(const Node *node)
void ProjectTree::forEachNode(const std::function<void(Node *)> &task)
{
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
for (Project *project : projects) {
if (ProjectNode *projectNode = project->rootProjectNode()) {
task(projectNode);
@@ -430,7 +430,7 @@ Project *ProjectTree::projectForNode(const Node *node)
while (folder && folder->parentFolderNode())
folder = folder->parentFolderNode();
- return Utils::findOrDefault(SessionManager::projects(), [folder](const Project *pro) {
+ return Utils::findOrDefault(ProjectManager::projects(), [folder](const Project *pro) {
return pro->containerNode() == folder;
});
}
@@ -438,7 +438,7 @@ Project *ProjectTree::projectForNode(const Node *node)
Node *ProjectTree::nodeForFile(const FilePath &fileName)
{
Node *node = nullptr;
- for (const Project *project : SessionManager::projects()) {
+ for (const Project *project : ProjectManager::projects()) {
project->nodeForFilePath(fileName, [&](const Node *n) {
if (!node || (!node->asFileNode() && n->asFileNode()))
node = const_cast<Node *>(n);
diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp
index 65f38626fe..a3d608ebd8 100644
--- a/src/plugins/projectexplorer/projecttreewidget.cpp
+++ b/src/plugins/projectexplorer/projecttreewidget.cpp
@@ -6,10 +6,10 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectmodels.h"
#include "projectnodes.h"
#include "projecttree.h"
-#include "session.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -24,6 +24,7 @@
#include <utils/navigationtreeview.h>
#include <utils/progressindicator.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
@@ -343,7 +344,7 @@ Node *ProjectTreeWidget::nodeForFile(const FilePath &fileName)
int bestNodeExpandCount = INT_MAX;
// FIXME: Looks like this could be done with less cycles.
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (ProjectNode *projectNode = project->rootProjectNode()) {
projectNode->forEachGenericNode([&](Node *node) {
if (node->filePath() == fileName) {
@@ -420,7 +421,7 @@ QList<QToolButton *> ProjectTreeWidget::createToolButtons()
filter->setIcon(Icons::FILTER.icon());
filter->setToolTip(Tr::tr("Filter Tree"));
filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
+ filter->setProperty(StyleHelper::C_NO_ARROW, true);
auto filterMenu = new QMenu(filter);
filterMenu->addAction(m_filterProjectsAction);
diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp
index 156212a504..5b392ea7a1 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepage.cpp
@@ -7,6 +7,7 @@
#include "sessionmodel.h"
#include "projectexplorer.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
@@ -324,7 +325,7 @@ public:
if (expanded) {
painter->setPen(textColor);
painter->setFont(sizedFont(12, option.widget));
- const FilePaths projects = SessionManager::projectsForSessionName(sessionName);
+ const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
int yy = firstBase + SESSION_LINE_HEIGHT - 3;
QFontMetrics fm(option.widget->font());
for (const FilePath &projectPath : projects) {
@@ -378,7 +379,7 @@ public:
int h = SESSION_LINE_HEIGHT;
QString sessionName = idx.data(Qt::DisplayRole).toString();
if (m_expandedSessions.contains(sessionName)) {
- const FilePaths projects = SessionManager::projectsForSessionName(sessionName);
+ const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
h += projects.size() * 40 + LINK_HEIGHT - 6;
}
return QSize(380, h + ItemGap);
diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp
index afe2b45e7e..6d51ada410 100644
--- a/src/plugins/projectexplorer/projectwindow.cpp
+++ b/src/plugins/projectexplorer/projectwindow.cpp
@@ -12,9 +12,9 @@
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "projectimporter.h"
+#include "projectmanager.h"
#include "projectpanelfactory.h"
#include "projectsettingswidget.h"
-#include "session.h"
#include "target.h"
#include "targetsettingspanel.h"
@@ -35,6 +35,7 @@
#include <utils/hostosinfo.h>
#include <utils/navigationtreeview.h>
#include <utils/qtcassert.h>
+#include <utils/qtcsettings.h>
#include <utils/styledbar.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
@@ -351,7 +352,7 @@ public:
case Qt::FontRole: {
QFont font;
- font.setBold(m_project == SessionManager::startupProject());
+ font.setBold(m_project == ProjectManager::startupProject());
return font;
}
@@ -391,7 +392,7 @@ public:
if (role == ItemActivatedDirectlyRole) {
// Someone selected the project using the combobox or similar.
- SessionManager::setStartupProject(m_project);
+ ProjectManager::setStartupProject(m_project);
m_currentChildIndex = 0; // Use some Target page by defaults
m_targetsItem->setData(column, dat, ItemActivatedFromAboveRole); // And propagate downwards.
announceChange();
@@ -545,18 +546,18 @@ public:
m_projectSelection->showPopup();
});
- SessionManager *sessionManager = SessionManager::instance();
- connect(sessionManager, &SessionManager::projectAdded,
+ ProjectManager *sessionManager = ProjectManager::instance();
+ connect(sessionManager, &ProjectManager::projectAdded,
this, &ProjectWindowPrivate::registerProject);
- connect(sessionManager, &SessionManager::aboutToRemoveProject,
+ connect(sessionManager, &ProjectManager::aboutToRemoveProject,
this, &ProjectWindowPrivate::deregisterProject);
- connect(sessionManager, &SessionManager::startupProjectChanged,
+ connect(sessionManager, &ProjectManager::startupProjectChanged,
this, &ProjectWindowPrivate::startupProjectChanged);
m_importBuild = new QPushButton(Tr::tr("Import Existing Build..."));
connect(m_importBuild, &QPushButton::clicked,
this, &ProjectWindowPrivate::handleImportBuild);
- connect(sessionManager, &SessionManager::startupProjectChanged, this, [this](Project *project) {
+ connect(sessionManager, &ProjectManager::startupProjectChanged, this, [this](Project *project) {
m_importBuild->setEnabled(project && project->projectImporter());
});
@@ -571,9 +572,6 @@ public:
selectorView->setObjectName("ProjectSelector"); // Needed for dock widget state saving
selectorView->setWindowTitle(Tr::tr("Project Selector"));
selectorView->setAutoFillBackground(true);
- selectorView->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(selectorView, &QWidget::customContextMenuRequested,
- this, &ProjectWindowPrivate::openContextMenu);
auto activeLabel = new QLabel(Tr::tr("Active Project"));
QFont font = activeLabel->font();
@@ -585,8 +583,13 @@ public:
innerLayout->setSpacing(10);
innerLayout->setContentsMargins(PanelsWidget::PanelVMargin, innerLayout->spacing(),
PanelsWidget::PanelVMargin, 0);
- innerLayout->addWidget(m_manageKits);
- innerLayout->addSpacerItem(new QSpacerItem(10, 30, QSizePolicy::Maximum, QSizePolicy::Maximum));
+
+ QStringList list = Core::ICore::settings()->value("HideOptionCategories").toStringList();
+ if (!list.contains("Kit")) {
+ innerLayout->addWidget(m_manageKits);
+ innerLayout->addSpacerItem(new QSpacerItem(10, 30, QSizePolicy::Maximum, QSizePolicy::Maximum));
+ }
+
innerLayout->addWidget(activeLabel);
innerLayout->addWidget(m_projectSelection);
innerLayout->addWidget(m_importBuild);
@@ -671,7 +674,7 @@ public:
void projectSelected(int index)
{
Project *project = m_comboBoxModel.rootItem()->childAt(index)->m_projectItem->project();
- SessionManager::setStartupProject(project);
+ ProjectManager::setStartupProject(project);
}
ComboBoxItem *itemForProject(Project *project) const
@@ -740,8 +743,7 @@ public:
void handleManageKits()
{
if (ProjectItem *projectItem = m_projectsModel.rootItem()->childAt(0)) {
- if (auto kitPage = KitOptionsPage::instance())
- kitPage->showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole))));
+ KitOptionsPage::showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole))));
}
ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID);
}
@@ -774,8 +776,8 @@ public:
}
}
if (lastTarget && lastBc) {
- SessionManager::setActiveBuildConfiguration(lastTarget, lastBc, SetActive::Cascade);
- SessionManager::setActiveTarget(project, lastTarget, SetActive::Cascade);
+ lastTarget->setActiveBuildConfiguration(lastBc, SetActive::Cascade);
+ project->setActiveTarget(lastTarget, SetActive::Cascade);
}
}
diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp
index 34dc0b9aaa..83fd4d2cba 100644
--- a/src/plugins/projectexplorer/projectwizardpage.cpp
+++ b/src/plugins/projectexplorer/projectwizardpage.cpp
@@ -5,8 +5,8 @@
#include "project.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectmodels.h"
-#include "session.h"
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
@@ -245,12 +245,10 @@ static AddNewTree *buildAddFilesTree(FolderNode *root, const FilePaths &files,
Node *contextNode, BestNodeSelector *selector)
{
QList<AddNewTree *> children;
- const QList<FolderNode *> folderNodes = root->folderNodes();
- for (FolderNode *fn : folderNodes) {
- AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector);
- if (child)
+ root->forEachFolderNode([&](FolderNode *fn) {
+ if (AddNewTree *child = buildAddFilesTree(fn, files, contextNode, selector))
children.append(child);
- }
+ });
if (root->supportsAction(AddNewFile, root) && !root->supportsAction(InheritedFromParent, root)) {
FolderNode::AddNewInformation info = root->addNewInformation(files, contextNode);
@@ -290,7 +288,7 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent)
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(m_filesLabel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
m_projectLabel, m_projectComboBox, br,
@@ -463,7 +461,7 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const FilePaths &pa
TreeItem *root = m_model.rootItem();
root->removeChildren();
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (ProjectNode *pn = project->rootProjectNode()) {
if (kind == IWizardFactory::ProjectWizard) {
if (AddNewTree *child = buildAddProjectTree(pn, paths.first(), context, &selector))
diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp
index 6abbedde93..31147c3803 100644
--- a/src/plugins/projectexplorer/rawprojectpart.cpp
+++ b/src/plugins/projectexplorer/rawprojectpart.cpp
@@ -11,13 +11,14 @@
#include "target.h"
#include <ios/iosconstants.h>
+
#include <utils/algorithm.h>
namespace ProjectExplorer {
RawProjectPartFlags::RawProjectPartFlags(const ToolChain *toolChain,
const QStringList &commandLineFlags,
- const QString &includeFileBaseDir)
+ const Utils::FilePath &includeFileBaseDir)
{
// Keep the following cheap/non-blocking for the ui thread. Expensive
// operations are encapsulated in ToolChainInfo as "runners".
@@ -25,7 +26,8 @@ RawProjectPartFlags::RawProjectPartFlags(const ToolChain *toolChain,
if (toolChain) {
warningFlags = toolChain->warningFlags(commandLineFlags);
languageExtensions = toolChain->languageExtensions(commandLineFlags);
- includedFiles = toolChain->includedFiles(commandLineFlags, includeFileBaseDir);
+ includedFiles = Utils::transform(toolChain->includedFiles(commandLineFlags, includeFileBaseDir),
+ &Utils::FilePath::toFSPathString);
}
}
diff --git a/src/plugins/projectexplorer/rawprojectpart.h b/src/plugins/projectexplorer/rawprojectpart.h
index 580c83d439..ca210ed43e 100644
--- a/src/plugins/projectexplorer/rawprojectpart.h
+++ b/src/plugins/projectexplorer/rawprojectpart.h
@@ -37,7 +37,7 @@ class PROJECTEXPLORER_EXPORT RawProjectPartFlags
public:
RawProjectPartFlags() = default;
RawProjectPartFlags(const ToolChain *toolChain, const QStringList &commandLineFlags,
- const QString &includeFileBaseDir);
+ const Utils::FilePath &includeFileBaseDir);
public:
QStringList commandLineFlags;
diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp
index b5f3f8e703..d05c23eaf5 100644
--- a/src/plugins/projectexplorer/runconfiguration.cpp
+++ b/src/plugins/projectexplorer/runconfiguration.cpp
@@ -12,10 +12,10 @@
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectnodes.h"
#include "runconfigurationaspects.h"
#include "runcontrol.h"
-#include "session.h"
#include "target.h"
#include <coreplugin/icontext.h>
@@ -33,7 +33,6 @@
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
-#include <QDir>
#include <QHash>
#include <QPushButton>
#include <QTimer>
@@ -217,13 +216,15 @@ bool RunConfiguration::isEnabled() const
QWidget *RunConfiguration::createConfigurationWidget()
{
- Layouting::Form builder;
+ Layouting::Form form;
for (BaseAspect *aspect : std::as_const(m_aspects)) {
- if (aspect->isVisible())
- aspect->addToLayout(builder.finishRow());
+ if (aspect->isVisible()) {
+ form.addItem(aspect);
+ form.addItem(Layouting::br);
+ }
}
-
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ form.addItem(Layouting::noMargin);
+ auto widget = form.emerge();
VariableChooser::addSupportForChildWidgets(widget, &m_expander);
@@ -299,6 +300,13 @@ CommandLine RunConfiguration::commandLine() const
return m_commandLineGetter();
}
+bool RunConfiguration::isPrintEnvironmentEnabled() const
+{
+ if (const auto envAspect = aspect<EnvironmentAspect>())
+ return envAspect->isPrintOnRunEnabled();
+ return false;
+}
+
void RunConfiguration::setRunnableModifier(const RunnableModifier &runnableModifier)
{
m_runnableModifier = runnableModifier;
@@ -313,7 +321,7 @@ void RunConfiguration::update()
const bool isActive = target()->isActive() && target()->activeRunConfiguration() == this;
- if (isActive && project() == SessionManager::startupProject())
+ if (isActive && project() == ProjectManager::startupProject())
ProjectExplorerPlugin::updateRunActions();
}
@@ -392,7 +400,7 @@ Runnable RunConfiguration::runnable() const
Runnable r;
r.command = commandLine();
if (auto workingDirectoryAspect = aspect<WorkingDirectoryAspect>())
- r.workingDirectory = workingDirectoryAspect->workingDirectory().onDevice(r.command.executable());
+ r.workingDirectory = r.command.executable().withNewPath(workingDirectoryAspect->workingDirectory().path());
if (auto environmentAspect = aspect<EnvironmentAspect>())
r.environment = environmentAspect->environment();
if (m_runnableModifier)
diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h
index 32f61b5a30..a0d83ebb48 100644
--- a/src/plugins/projectexplorer/runconfiguration.h
+++ b/src/plugins/projectexplorer/runconfiguration.h
@@ -116,6 +116,7 @@ public:
using CommandLineGetter = std::function<Utils::CommandLine()>;
void setCommandLineGetter(const CommandLineGetter &cmdGetter);
Utils::CommandLine commandLine() const;
+ bool isPrintEnvironmentEnabled() const;
using RunnableModifier = std::function<void(Runnable &)>;
void setRunnableModifier(const RunnableModifier &extraModifier);
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp
index 9ba24c0467..8098606afb 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.cpp
+++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp
@@ -19,8 +19,8 @@
#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <QCheckBox>
@@ -33,7 +33,7 @@
#include <QPushButton>
using namespace Utils;
-using namespace Utils::Layouting;
+using namespace Layouting;
namespace ProjectExplorer {
@@ -62,12 +62,13 @@ TerminalAspect::TerminalAspect()
/*!
\reimp
*/
-void TerminalAspect::addToLayout(LayoutBuilder &builder)
+void TerminalAspect::addToLayout(LayoutItem &parent)
{
QTC_CHECK(!m_checkBox);
- m_checkBox = new QCheckBox(Tr::tr("Run in terminal"));
+ m_checkBox = createSubWidget<QCheckBox>(Tr::tr("Run in terminal"));
m_checkBox->setChecked(m_useTerminal);
- builder.addItems({{}, m_checkBox.data()});
+ m_checkBox->setEnabled(isEnabled());
+ parent.addItems({{}, m_checkBox.data()});
connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
m_userSet = true;
m_useTerminal = m_checkBox->isChecked();
@@ -123,7 +124,7 @@ void TerminalAspect::calculateUseTerminal()
*/
bool TerminalAspect::useTerminal() const
{
- return m_useTerminal;
+ return m_useTerminal && isEnabled();
}
/*!
@@ -163,7 +164,7 @@ WorkingDirectoryAspect::WorkingDirectoryAspect(const MacroExpander *expander,
/*!
\reimp
*/
-void WorkingDirectoryAspect::addToLayout(LayoutBuilder &builder)
+void WorkingDirectoryAspect::addToLayout(LayoutItem &builder)
{
QTC_CHECK(!m_chooser);
m_chooser = new PathChooser;
@@ -435,7 +436,7 @@ QWidget *ArgumentsAspect::setupChooser()
/*!
\reimp
*/
-void ArgumentsAspect::addToLayout(LayoutBuilder &builder)
+void ArgumentsAspect::addToLayout(LayoutItem &builder)
{
QTC_CHECK(!m_chooser && !m_multiLineChooser && !m_multiLineButton);
@@ -570,19 +571,12 @@ void ExecutableAspect::setExpectedKind(const PathChooser::Kind expectedKind)
Sets the environment in which paths will be searched when the expected kind
of paths is chosen as PathChooser::Command or PathChooser::ExistingCommand
to \a env.
-
- \sa Utils::StringAspect::setEnvironmentChange()
*/
-void ExecutableAspect::setEnvironmentChange(const EnvironmentChange &change)
-{
- m_executable.setEnvironmentChange(change);
- if (m_alternativeExecutable)
- m_alternativeExecutable->setEnvironmentChange(change);
-}
-
void ExecutableAspect::setEnvironment(const Environment &env)
{
- setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary()));
+ m_executable.setEnvironment(env);
+ if (m_alternativeExecutable)
+ m_alternativeExecutable->setEnvironment(env);
}
/*!
@@ -631,7 +625,7 @@ FilePath ExecutableAspect::executable() const
: m_executable.filePath();
if (const IDevice::ConstPtr dev = executionDevice(m_target, m_selector))
- exe = exe.onDevice(dev->rootPath());
+ exe = dev->rootPath().withNewMappedPath(exe);
return exe;
}
@@ -639,11 +633,11 @@ FilePath ExecutableAspect::executable() const
/*!
\reimp
*/
-void ExecutableAspect::addToLayout(LayoutBuilder &builder)
+void ExecutableAspect::addToLayout(LayoutItem &builder)
{
- m_executable.addToLayout(builder);
+ builder.addItem(m_executable);
if (m_alternativeExecutable)
- m_alternativeExecutable->addToLayout(builder.finishRow());
+ builder.addItems({br, m_alternativeExecutable});
}
/*!
@@ -837,7 +831,7 @@ void InterpreterAspect::toMap(QVariantMap &map) const
saveToMap(map, m_currentId, QString(), settingsKey());
}
-void InterpreterAspect::addToLayout(LayoutBuilder &builder)
+void InterpreterAspect::addToLayout(LayoutItem &builder)
{
if (QTC_GUARD(m_comboBox.isNull()))
m_comboBox = new QComboBox;
diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h
index 9e948bc6ec..9fc7364772 100644
--- a/src/plugins/projectexplorer/runconfigurationaspects.h
+++ b/src/plugins/projectexplorer/runconfigurationaspects.h
@@ -29,7 +29,7 @@ class PROJECTEXPLORER_EXPORT TerminalAspect : public Utils::BaseAspect
public:
TerminalAspect();
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
bool useTerminal() const;
void setUseTerminalHint(bool useTerminal);
@@ -62,7 +62,7 @@ public:
explicit WorkingDirectoryAspect(const Utils::MacroExpander *expander,
EnvironmentAspect *envAspect);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
Utils::FilePath workingDirectory() const;
Utils::FilePath defaultWorkingDirectory() const;
@@ -91,7 +91,7 @@ class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect
public:
explicit ArgumentsAspect(const Utils::MacroExpander *macroExpander);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QString arguments() const;
QString unexpandedArguments() const;
@@ -163,12 +163,11 @@ public:
void setSettingsKey(const QString &key);
void makeOverridable(const QString &overridingKey, const QString &useOverridableKey);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
void setLabelText(const QString &labelText);
void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const QString &historyCompleterKey);
void setExpectedKind(const Utils::PathChooser::Kind expectedKind);
- void setEnvironmentChange(const Utils::EnvironmentChange &change);
void setEnvironment(const Utils::Environment &env);
void setDisplayStyle(Utils::StringAspect::DisplayStyle style);
@@ -236,7 +235,7 @@ public:
void fromMap(const QVariantMap &) override;
void toMap(QVariantMap &) const override;
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
struct Data : Utils::BaseAspect::Data { Interpreter interpreter; };
diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp
index 72b2e76e50..82aeeba4ff 100644
--- a/src/plugins/projectexplorer/runcontrol.cpp
+++ b/src/plugins/projectexplorer/runcontrol.cpp
@@ -25,9 +25,9 @@
#include <utils/checkablemessagebox.h>
#include <utils/fileinprojectfinder.h>
#include <utils/outputformatter.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/utilsicons.h>
#include <coreplugin/icontext.h>
@@ -255,9 +255,9 @@ public:
// A handle to the actual application process.
ProcessHandle applicationProcessHandle;
- RunControlState state = RunControlState::Initialized;
-
QList<QPointer<RunWorker>> m_workers;
+ RunControlState state = RunControlState::Initialized;
+ bool printEnvironment = false;
};
class RunControlPrivate : public QObject, public RunControlPrivateData
@@ -334,6 +334,7 @@ void RunControl::copyDataFromRunConfiguration(RunConfiguration *runConfig)
d->buildKey = runConfig->buildKey();
d->settingsData = runConfig->settingsData();
d->aspectData = runConfig->aspectData();
+ d->printEnvironment = runConfig->isPrintEnvironmentEnabled();
setTarget(runConfig->target());
@@ -817,6 +818,11 @@ Utils::Id RunControl::runMode() const
return d->runMode;
}
+bool RunControl::isPrintEnvironmentEnabled() const
+{
+ return d->printEnvironment;
+}
+
const Runnable &RunControl::runnable() const
{
return d->runnable;
@@ -1045,35 +1051,30 @@ bool RunControl::showPromptToStopDialog(const QString &title,
{
// Show a question message box where user can uncheck this
// question for this class.
- Utils::CheckableMessageBox messageBox(Core::ICore::dialogParent());
- messageBox.setWindowTitle(title);
- messageBox.setText(text);
- messageBox.setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::Cancel);
+ QMap<QMessageBox::StandardButton, QString> buttonTexts;
if (!stopButtonText.isEmpty())
- messageBox.button(QDialogButtonBox::Yes)->setText(stopButtonText);
+ buttonTexts[QMessageBox::Yes] = stopButtonText;
if (!cancelButtonText.isEmpty())
- messageBox.button(QDialogButtonBox::Cancel)->setText(cancelButtonText);
- messageBox.setDefaultButton(QDialogButtonBox::Yes);
- if (prompt) {
- messageBox.setCheckBoxText(Utils::CheckableMessageBox::msgDoNotAskAgain());
- messageBox.setChecked(false);
- } else {
- messageBox.setCheckBoxVisible(false);
- }
- messageBox.exec();
- const bool close = messageBox.clickedStandardButton() == QDialogButtonBox::Yes;
- if (close && prompt && messageBox.isChecked())
- *prompt = false;
- return close;
+ buttonTexts[QMessageBox::Cancel] = cancelButtonText;
+
+ std::optional<CheckableMessageBox::Decider> decider
+ = prompt ? std::nullopt : make_optional(CheckableMessageBox::make_decider(*prompt));
+
+ auto selected = CheckableMessageBox::question(Core::ICore::dialogParent(),
+ title,
+ text,
+ decider,
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Yes);
+
+ return selected == QMessageBox::Yes;
}
void RunControl::provideAskPassEntry(Environment &env)
{
- if (env.value("SUDO_ASKPASS").isEmpty()) {
- const FilePath askpass = SshSettings::askpassFilePath();
- if (askpass.exists())
- env.set("SUDO_ASKPASS", askpass.toUserOutput());
- }
+ const FilePath askpass = SshSettings::askpassFilePath();
+ if (askpass.exists())
+ env.setFallback("SUDO_ASKPASS", askpass.toUserOutput());
}
bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlState to)
@@ -1175,7 +1176,7 @@ public:
bool m_runAsRoot = false;
- QtcProcess m_process;
+ Process m_process;
QTextCodec *m_outputCodec = nullptr;
QTextCodec::ConverterState m_outputCodecState;
@@ -1212,11 +1213,11 @@ SimpleTargetRunnerPrivate::SimpleTargetRunnerPrivate(SimpleTargetRunner *parent)
: q(parent)
{
m_process.setProcessChannelMode(defaultProcessChannelMode());
- connect(&m_process, &QtcProcess::started, this, &SimpleTargetRunnerPrivate::forwardStarted);
- connect(&m_process, &QtcProcess::done, this, &SimpleTargetRunnerPrivate::handleDone);
- connect(&m_process, &QtcProcess::readyReadStandardError,
+ connect(&m_process, &Process::started, this, &SimpleTargetRunnerPrivate::forwardStarted);
+ connect(&m_process, &Process::done, this, &SimpleTargetRunnerPrivate::handleDone);
+ connect(&m_process, &Process::readyReadStandardError,
this, &SimpleTargetRunnerPrivate::handleStandardError);
- connect(&m_process, &QtcProcess::readyReadStandardOutput,
+ connect(&m_process, &Process::readyReadStandardOutput,
this, &SimpleTargetRunnerPrivate::handleStandardOutput);
if (WinDebugInterface::instance()) {
@@ -1371,7 +1372,7 @@ void SimpleTargetRunnerPrivate::start()
Encapsulates processes running in a console or as GUI processes,
captures debug output of GUI processes on Windows (outputDebugString()).
- \sa Utils::QtcProcess
+ \sa Utils::Process
*/
SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl)
@@ -1433,11 +1434,20 @@ void SimpleTargetRunner::start()
d->m_stopForced = false;
d->m_stopReported = false;
d->disconnect(this);
- d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off);
+ d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::Run : Utils::TerminalMode::Off);
d->m_runAsRoot = runAsRoot;
const QString msg = Tr::tr("Starting %1...").arg(d->m_command.displayName());
appendMessage(msg, NormalMessageFormat);
+ if (runControl()->isPrintEnvironmentEnabled()) {
+ appendMessage(Tr::tr("Environment:"), NormalMessageFormat);
+ runControl()->runnable().environment
+ .forEachEntry([this](const QString &key, const QString &value, bool enabled) {
+ if (enabled)
+ appendMessage(key + '=' + value, StdOutFormat);
+ });
+ appendMessage({}, StdOutFormat);
+ }
const bool isDesktop = !d->m_command.executable().needsDevice();
if (isDesktop && d->m_command.isEmpty()) {
@@ -1653,9 +1663,9 @@ void RunWorker::reportFailure(const QString &msg)
* Appends a message in the specified \a format to
* the owning RunControl's \uicontrol{Application Output} pane.
*/
-void RunWorker::appendMessage(const QString &msg, OutputFormat format)
+void RunWorker::appendMessage(const QString &msg, OutputFormat format, bool appendNewLine)
{
- if (msg.endsWith('\n'))
+ if (!appendNewLine || msg.endsWith('\n'))
emit d->runControl->appendMessage(msg, format);
else
emit d->runControl->appendMessage(msg + '\n', format);
diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h
index d779b693a8..84a5b5c166 100644
--- a/src/plugins/projectexplorer/runcontrol.h
+++ b/src/plugins/projectexplorer/runcontrol.h
@@ -67,7 +67,7 @@ public:
QVariant recordedData(const QString &channel) const;
// Part of read-only interface of RunControl for convenience.
- void appendMessage(const QString &msg, Utils::OutputFormat format);
+ void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true);
void appendMessageChunk(const QString &msg, Utils::OutputFormat format);
IDeviceConstPtr device() const;
@@ -205,6 +205,7 @@ public:
void setupFormatter(Utils::OutputFormatter *formatter) const;
Utils::Id runMode() const;
+ bool isPrintEnvironmentEnabled() const;
const Runnable &runnable() const;
diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp
index bd8b30f2b7..c1a3a75e3c 100644
--- a/src/plugins/projectexplorer/runsettingspropertiespage.cpp
+++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp
@@ -291,11 +291,10 @@ void RunSettingsWidget::currentDeployConfigurationChanged(int index)
if (m_ignoreChanges.isLocked())
return;
if (index == -1)
- SessionManager::setActiveDeployConfiguration(m_target, nullptr, SetActive::Cascade);
+ m_target->setActiveDeployConfiguration(nullptr, SetActive::Cascade);
else
- SessionManager::setActiveDeployConfiguration(m_target,
- qobject_cast<DeployConfiguration *>(m_target->deployConfigurationModel()->projectConfigurationAt(index)),
- SetActive::Cascade);
+ m_target->setActiveDeployConfiguration(qobject_cast<DeployConfiguration *>(m_target->deployConfigurationModel()->projectConfigurationAt(index)),
+ SetActive::Cascade);
}
void RunSettingsWidget::aboutToShowDeployMenu()
@@ -309,7 +308,7 @@ void RunSettingsWidget::aboutToShowDeployMenu()
if (!newDc)
return;
m_target->addDeployConfiguration(newDc);
- SessionManager::setActiveDeployConfiguration(m_target, newDc, SetActive::Cascade);
+ m_target->setActiveDeployConfiguration(newDc, SetActive::Cascade);
m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().size() > 1);
});
}
diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp
index 365c319992..de5b0b79b4 100644
--- a/src/plugins/projectexplorer/selectablefilesmodel.cpp
+++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp
@@ -9,10 +9,10 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/fancylineedit.h>
#include <utils/fsengine/fileiconprovider.h>
#include <utils/pathchooser.h>
-#include <utils/runextensions.h>
#include <utils/stringutils.h>
#include <QDialogButtonBox>
@@ -51,13 +51,13 @@ void SelectableFilesFromDirModel::startParsing(const Utils::FilePath &baseDir)
m_rootForFuture->fullPath = baseDir;
m_rootForFuture->isDir = true;
- m_watcher.setFuture(Utils::runAsync(&SelectableFilesFromDirModel::run, this));
+ m_watcher.setFuture(Utils::asyncRun(&SelectableFilesFromDirModel::run, this));
}
-void SelectableFilesFromDirModel::run(QFutureInterface<void> &fi)
+void SelectableFilesFromDirModel::run(QPromise<void> &promise)
{
m_futureCount = 0;
- buildTree(m_baseDir, m_rootForFuture, fi, 5);
+ buildTree(m_baseDir, m_rootForFuture, promise, 5);
}
void SelectableFilesFromDirModel::buildTreeFinished()
@@ -97,7 +97,7 @@ SelectableFilesModel::FilterState SelectableFilesModel::filter(Tree *t)
}
void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree *tree,
- QFutureInterface<void> &fi, int symlinkDepth)
+ QPromise<void> &promise, int symlinkDepth)
{
if (symlinkDepth == 0)
return;
@@ -111,7 +111,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree
Utils::FilePath fn = Utils::FilePath::fromFileInfo(fileInfo);
if (m_futureCount % 100) {
emit parsingProgress(fn);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return;
}
++m_futureCount;
@@ -121,7 +121,7 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree
t->name = fileInfo.fileName();
t->fullPath = fn;
t->isDir = true;
- buildTree(fn, t, fi, symlinkDepth - fileInfo.isSymLink());
+ buildTree(fn, t, promise, symlinkDepth - fileInfo.isSymLink());
allChecked &= t->checked == Qt::Checked;
allUnchecked &= t->checked == Qt::Unchecked;
tree->childDirectories.append(t);
diff --git a/src/plugins/projectexplorer/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h
index 6340e2d1a2..8c47fbd990 100644
--- a/src/plugins/projectexplorer/selectablefilesmodel.h
+++ b/src/plugins/projectexplorer/selectablefilesmodel.h
@@ -9,7 +9,6 @@
#include <QAbstractItemModel>
#include <QDialog>
-#include <QFutureInterface>
#include <QFutureWatcher>
#include <QLabel>
#include <QRegularExpression>
@@ -147,11 +146,9 @@ signals:
void parsingProgress(const Utils::FilePath &fileName);
private:
- void buildTree(const Utils::FilePath &baseDir,
- Tree *tree,
- QFutureInterface<void> &fi,
+ void buildTree(const Utils::FilePath &baseDir, Tree *tree, QPromise<void> &promise,
int symlinkDepth);
- void run(QFutureInterface<void> &fi);
+ void run(QPromise<void> &promise);
void buildTreeFinished();
// Used in the future thread need to all not used after calling startParsing
diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp
index 6dcfd9a461..5edd7b4f9d 100644
--- a/src/plugins/projectexplorer/session.cpp
+++ b/src/plugins/projectexplorer/session.cpp
@@ -3,16 +3,14 @@
#include "session.h"
-#include "buildconfiguration.h"
-#include "deployconfiguration.h"
-#include "editorconfiguration.h"
-#include "kit.h"
-#include "project.h"
+#include "session_p.h"
+
#include "projectexplorer.h"
-#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
-#include "projectnodes.h"
-#include "target.h"
+#include "projectmanager.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -34,21 +32,19 @@
#include <QDebug>
#include <QMessageBox>
#include <QPushButton>
-
-#ifdef WITH_TESTS
-#include <QTemporaryFile>
-#include <QTest>
-#include <vector>
-#endif
+#include <QTimer>
using namespace Core;
using namespace Utils;
-using namespace ProjectExplorer::Internal;
namespace ProjectExplorer {
const char DEFAULT_SESSION[] = "default";
const char LAST_ACTIVE_TIMES_KEY[] = "LastActiveTimes";
+const char STARTUPSESSION_KEY[] = "ProjectExplorer/SessionToRestore";
+const char LASTSESSION_KEY[] = "ProjectExplorer/StartupSession";
+const char AUTO_RESTORE_SESSION_SETTINGS_KEY[] = "ProjectExplorer/Settings/AutoRestoreLastSession";
+static bool kIsAutoRestoreLastSessionDefault = false;
/*!
\class ProjectExplorer::SessionManager
@@ -61,98 +57,45 @@ const char LAST_ACTIVE_TIMES_KEY[] = "LastActiveTimes";
This could be improved.
*/
-class SessionManagerPrivate
-{
-public:
- void restoreValues(const PersistentSettingsReader &reader);
- void restoreDependencies(const PersistentSettingsReader &reader);
- void restoreStartupProject(const PersistentSettingsReader &reader);
- void restoreEditors(const PersistentSettingsReader &reader);
- void restoreProjects(const FilePaths &fileList);
- void askUserAboutFailedProjects();
- void sessionLoadingProgress();
-
- bool recursiveDependencyCheck(const FilePath &newDep, const FilePath &checkDep) const;
- FilePaths dependencies(const FilePath &proName) const;
- FilePaths dependenciesOrder() const;
- void dependencies(const FilePath &proName, FilePaths &result) const;
-
- static QString windowTitleAddition(const FilePath &filePath);
- static QString sessionTitle(const FilePath &filePath);
-
- bool hasProjects() const { return !m_projects.isEmpty(); }
-
- QString m_sessionName = QLatin1String(DEFAULT_SESSION);
- bool m_virginSession = true;
- bool m_loadingSession = false;
- bool m_casadeSetActive = false;
-
- mutable QStringList m_sessions;
- mutable QHash<QString, QDateTime> m_sessionDateTimes;
- QHash<QString, QDateTime> m_lastActiveTimes;
-
- Project *m_startupProject = nullptr;
- QList<Project *> m_projects;
- FilePaths m_failedProjects;
- QMap<FilePath, FilePaths> m_depMap;
- QMap<QString, QVariant> m_values;
- QFutureInterface<void> m_future;
- PersistentSettingsWriter *m_writer = nullptr;
-
-private:
- static QString locationInProject(const FilePath &filePath);
-};
-
static SessionManager *m_instance = nullptr;
-static SessionManagerPrivate *d = nullptr;
+SessionManagerPrivate *sb_d = nullptr;
-static QString projectFolderId(Project *pro)
-{
- return pro->projectFilePath().toString();
-}
-
-const int PROJECT_SORT_VALUE = 100;
-
-SessionManager::SessionManager(QObject *parent) : QObject(parent)
+SessionManager::SessionManager()
{
m_instance = this;
- d = new SessionManagerPrivate;
+ sb_d = new SessionManagerPrivate;
+
+ connect(ICore::instance(), &ICore::coreOpened, this, [] { sb_d->restoreStartupSession(); });
connect(ModeManager::instance(), &ModeManager::currentModeChanged,
this, &SessionManager::saveActiveMode);
connect(ICore::instance(), &ICore::saveSettingsRequested, this, [] {
+ QtcSettings *s = ICore::settings();
QVariantMap times;
- for (auto it = d->m_lastActiveTimes.cbegin(); it != d->m_lastActiveTimes.cend(); ++it)
+ for (auto it = sb_d->m_lastActiveTimes.cbegin(); it != sb_d->m_lastActiveTimes.cend(); ++it)
times.insert(it.key(), it.value());
- ICore::settings()->setValue(LAST_ACTIVE_TIMES_KEY, times);
+ s->setValue(LAST_ACTIVE_TIMES_KEY, times);
+ if (SessionManager::isDefaultVirgin()) {
+ s->remove(STARTUPSESSION_KEY);
+ } else {
+ s->setValue(STARTUPSESSION_KEY, SessionManager::activeSession());
+ s->setValue(LASTSESSION_KEY, SessionManager::activeSession());
+ }
});
- connect(EditorManager::instance(), &EditorManager::editorCreated,
- this, &SessionManager::configureEditor);
- connect(this, &SessionManager::projectAdded,
- EditorManager::instance(), &EditorManager::updateWindowTitles);
- connect(this, &SessionManager::projectRemoved,
- EditorManager::instance(), &EditorManager::updateWindowTitles);
- connect(this, &SessionManager::projectDisplayNameChanged,
- EditorManager::instance(), &EditorManager::updateWindowTitles);
connect(EditorManager::instance(), &EditorManager::editorOpened,
this, &SessionManager::markSessionFileDirty);
connect(EditorManager::instance(), &EditorManager::editorsClosed,
this, &SessionManager::markSessionFileDirty);
-
- EditorManager::setWindowTitleAdditionHandler(&SessionManagerPrivate::windowTitleAddition);
- EditorManager::setSessionTitleHandler(&SessionManagerPrivate::sessionTitle);
}
SessionManager::~SessionManager()
{
- EditorManager::setWindowTitleAdditionHandler({});
- EditorManager::setSessionTitleHandler({});
- emit m_instance->aboutToUnloadSession(d->m_sessionName);
- delete d->m_writer;
- delete d;
- d = nullptr;
+ emit m_instance->aboutToUnloadSession(sb_d->m_sessionName);
+ delete sb_d->m_writer;
+ delete sb_d;
+ sb_d = nullptr;
}
SessionManager *SessionManager::instance()
@@ -162,7 +105,7 @@ SessionManager *SessionManager::instance()
bool SessionManager::isDefaultVirgin()
{
- return isDefaultSession(d->m_sessionName) && d->m_virginSession;
+ return isDefaultSession(sb_d->m_sessionName) && sb_d->m_virginSession;
}
bool SessionManager::isDefaultSession(const QString &session)
@@ -176,651 +119,74 @@ void SessionManager::saveActiveMode(Id mode)
setValue(QLatin1String("ActiveMode"), mode.toString());
}
-bool SessionManagerPrivate::recursiveDependencyCheck(const FilePath &newDep,
- const FilePath &checkDep) const
-{
- if (newDep == checkDep)
- return false;
-
- const FilePaths depList = m_depMap.value(checkDep);
- for (const FilePath &dependency : depList) {
- if (!recursiveDependencyCheck(newDep, dependency))
- return false;
- }
-
- return true;
-}
-
-/*
- * The dependency management exposes an interface based on projects, but
- * is internally purely string based. This is suboptimal. Probably it would be
- * nicer to map the filenames to projects on load and only map it back to
- * filenames when saving.
- */
-
-QList<Project *> SessionManager::dependencies(const Project *project)
-{
- const FilePath proName = project->projectFilePath();
- const FilePaths proDeps = d->m_depMap.value(proName);
-
- QList<Project *> projects;
- for (const FilePath &dep : proDeps) {
- Project *pro = Utils::findOrDefault(d->m_projects, [&dep](Project *p) {
- return p->projectFilePath() == dep;
- });
- if (pro)
- projects += pro;
- }
-
- return projects;
-}
-
-bool SessionManager::hasDependency(const Project *project, const Project *depProject)
-{
- const FilePath proName = project->projectFilePath();
- const FilePath depName = depProject->projectFilePath();
-
- const FilePaths proDeps = d->m_depMap.value(proName);
- return proDeps.contains(depName);
-}
-
-bool SessionManager::canAddDependency(const Project *project, const Project *depProject)
-{
- const FilePath newDep = project->projectFilePath();
- const FilePath checkDep = depProject->projectFilePath();
-
- return d->recursiveDependencyCheck(newDep, checkDep);
-}
-
-bool SessionManager::addDependency(Project *project, Project *depProject)
-{
- const FilePath proName = project->projectFilePath();
- const FilePath depName = depProject->projectFilePath();
-
- // check if this dependency is valid
- if (!d->recursiveDependencyCheck(proName, depName))
- return false;
-
- FilePaths proDeps = d->m_depMap.value(proName);
- if (!proDeps.contains(depName)) {
- proDeps.append(depName);
- d->m_depMap[proName] = proDeps;
- }
- emit m_instance->dependencyChanged(project, depProject);
-
- return true;
-}
-
-void SessionManager::removeDependency(Project *project, Project *depProject)
-{
- const FilePath proName = project->projectFilePath();
- const FilePath depName = depProject->projectFilePath();
-
- FilePaths proDeps = d->m_depMap.value(proName);
- proDeps.removeAll(depName);
- if (proDeps.isEmpty())
- d->m_depMap.remove(proName);
- else
- d->m_depMap[proName] = proDeps;
- emit m_instance->dependencyChanged(project, depProject);
-}
-
-bool SessionManager::isProjectConfigurationCascading()
-{
- return d->m_casadeSetActive;
-}
-
-void SessionManager::setProjectConfigurationCascading(bool b)
-{
- d->m_casadeSetActive = b;
- markSessionFileDirty();
-}
-
-void SessionManager::setActiveTarget(Project *project, Target *target, SetActive cascade)
-{
- QTC_ASSERT(project, return);
-
- if (project->isShuttingDown())
- return;
-
- project->setActiveTarget(target);
-
- if (!target) // never cascade setting no target
- return;
-
- if (cascade != SetActive::Cascade || !d->m_casadeSetActive)
- return;
-
- Utils::Id kitId = target->kit()->id();
- for (Project *otherProject : SessionManager::projects()) {
- if (otherProject == project)
- continue;
- if (Target *otherTarget = Utils::findOrDefault(otherProject->targets(),
- [kitId](Target *t) { return t->kit()->id() == kitId; }))
- otherProject->setActiveTarget(otherTarget);
- }
-}
-
-void SessionManager::setActiveBuildConfiguration(Target *target, BuildConfiguration *bc, SetActive cascade)
-{
- QTC_ASSERT(target, return);
- QTC_ASSERT(target->project(), return);
-
- if (target->project()->isShuttingDown() || target->isShuttingDown())
- return;
-
- target->setActiveBuildConfiguration(bc);
-
- if (!bc)
- return;
- if (cascade != SetActive::Cascade || !d->m_casadeSetActive)
- return;
-
- Utils::Id kitId = target->kit()->id();
- QString name = bc->displayName(); // We match on displayname
- for (Project *otherProject : SessionManager::projects()) {
- if (otherProject == target->project())
- continue;
- Target *otherTarget = otherProject->activeTarget();
- if (!otherTarget || otherTarget->kit()->id() != kitId)
- continue;
-
- for (BuildConfiguration *otherBc : otherTarget->buildConfigurations()) {
- if (otherBc->displayName() == name) {
- otherTarget->setActiveBuildConfiguration(otherBc);
- break;
- }
- }
- }
-}
-
-void SessionManager::setActiveDeployConfiguration(Target *target, DeployConfiguration *dc, SetActive cascade)
-{
- QTC_ASSERT(target, return);
- QTC_ASSERT(target->project(), return);
-
- if (target->project()->isShuttingDown() || target->isShuttingDown())
- return;
-
- target->setActiveDeployConfiguration(dc);
-
- if (!dc)
- return;
- if (cascade != SetActive::Cascade || !d->m_casadeSetActive)
- return;
-
- Utils::Id kitId = target->kit()->id();
- QString name = dc->displayName(); // We match on displayname
- for (Project *otherProject : SessionManager::projects()) {
- if (otherProject == target->project())
- continue;
- Target *otherTarget = otherProject->activeTarget();
- if (!otherTarget || otherTarget->kit()->id() != kitId)
- continue;
-
- for (DeployConfiguration *otherDc : otherTarget->deployConfigurations()) {
- if (otherDc->displayName() == name) {
- otherTarget->setActiveDeployConfiguration(otherDc);
- break;
- }
- }
- }
-}
-
-void SessionManager::setStartupProject(Project *startupProject)
-{
- QTC_ASSERT((!startupProject && d->m_projects.isEmpty())
- || (startupProject && d->m_projects.contains(startupProject)), return);
-
- if (d->m_startupProject == startupProject)
- return;
-
- d->m_startupProject = startupProject;
- if (d->m_startupProject && d->m_startupProject->needsConfiguration()) {
- ModeManager::activateMode(Constants::MODE_SESSION);
- ModeManager::setFocusToCurrentMode();
- }
- FolderNavigationWidgetFactory::setFallbackSyncFilePath(
- startupProject ? startupProject->projectFilePath().parentDir() : FilePath());
- emit m_instance->startupProjectChanged(startupProject);
-}
-
-Project *SessionManager::startupProject()
-{
- return d->m_startupProject;
-}
-
-Target *SessionManager::startupTarget()
-{
- return d->m_startupProject ? d->m_startupProject->activeTarget() : nullptr;
-}
-
-BuildSystem *SessionManager::startupBuildSystem()
-{
- Target *t = startupTarget();
- return t ? t->buildSystem() : nullptr;
-}
-
-/*!
- * Returns the RunConfiguration of the currently active target
- * of the startup project, if such exists, or \c nullptr otherwise.
- */
-
-
-RunConfiguration *SessionManager::startupRunConfiguration()
-{
- Target *t = startupTarget();
- return t ? t->activeRunConfiguration() : nullptr;
-}
-
-void SessionManager::addProject(Project *pro)
-{
- QTC_ASSERT(pro, return);
- QTC_CHECK(!pro->displayName().isEmpty());
- QTC_CHECK(pro->id().isValid());
-
- d->m_virginSession = false;
- QTC_ASSERT(!d->m_projects.contains(pro), return);
-
- d->m_projects.append(pro);
-
- connect(pro, &Project::displayNameChanged,
- m_instance, [pro]() { emit m_instance->projectDisplayNameChanged(pro); });
-
- emit m_instance->projectAdded(pro);
- const auto updateFolderNavigation = [pro] {
- // destructing projects might trigger changes, so check if the project is actually there
- if (QTC_GUARD(d->m_projects.contains(pro))) {
- const QIcon icon = pro->rootProjectNode() ? pro->rootProjectNode()->icon() : QIcon();
- FolderNavigationWidgetFactory::insertRootDirectory({projectFolderId(pro),
- PROJECT_SORT_VALUE,
- pro->displayName(),
- pro->projectFilePath().parentDir(),
- icon});
- }
- };
- updateFolderNavigation();
- configureEditors(pro);
- connect(pro, &Project::fileListChanged, m_instance, [pro, updateFolderNavigation]() {
- configureEditors(pro);
- updateFolderNavigation(); // update icon
- });
- connect(pro, &Project::displayNameChanged, m_instance, updateFolderNavigation);
-
- if (!startupProject())
- setStartupProject(pro);
-}
-
-void SessionManager::removeProject(Project *project)
-{
- d->m_virginSession = false;
- QTC_ASSERT(project, return);
- removeProjects({project});
-}
-
bool SessionManager::loadingSession()
{
- return d->m_loadingSession;
-}
-
-bool SessionManager::save()
-{
- emit m_instance->aboutToSaveSession();
-
- const FilePath filePath = sessionNameToFileName(d->m_sessionName);
- QVariantMap data;
-
- // See the explanation at loadSession() for how we handle the implicit default session.
- if (isDefaultVirgin()) {
- if (filePath.exists()) {
- PersistentSettingsReader reader;
- if (!reader.load(filePath)) {
- QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
- Tr::tr("Could not save session %1").arg(filePath.toUserOutput()));
- return false;
- }
- data = reader.restoreValues();
- }
- } else {
- // save the startup project
- if (d->m_startupProject)
- data.insert("StartupProject", d->m_startupProject->projectFilePath().toSettings());
-
- const QColor c = StyleHelper::requestedBaseColor();
- if (c.isValid()) {
- QString tmp = QString::fromLatin1("#%1%2%3")
- .arg(c.red(), 2, 16, QLatin1Char('0'))
- .arg(c.green(), 2, 16, QLatin1Char('0'))
- .arg(c.blue(), 2, 16, QLatin1Char('0'));
- data.insert(QLatin1String("Color"), tmp);
- }
-
- FilePaths projectFiles = Utils::transform(projects(), &Project::projectFilePath);
- // Restore information on projects that failed to load:
- // don't read projects to the list, which the user loaded
- for (const FilePath &failed : std::as_const(d->m_failedProjects)) {
- if (!projectFiles.contains(failed))
- projectFiles << failed;
- }
-
- data.insert("ProjectList", Utils::transform<QStringList>(projectFiles,
- &FilePath::toString));
- data.insert("CascadeSetActive", d->m_casadeSetActive);
-
- QVariantMap depMap;
- auto i = d->m_depMap.constBegin();
- while (i != d->m_depMap.constEnd()) {
- QString key = i.key().toString();
- QStringList values;
- const FilePaths valueList = i.value();
- for (const FilePath &value : valueList)
- values << value.toString();
- depMap.insert(key, values);
- ++i;
- }
- data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap));
- data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64());
- }
-
- const auto end = d->m_values.constEnd();
- QStringList keys;
- for (auto it = d->m_values.constBegin(); it != end; ++it) {
- data.insert(QLatin1String("value-") + it.key(), it.value());
- keys << it.key();
- }
- data.insert(QLatin1String("valueKeys"), keys);
-
- if (!d->m_writer || d->m_writer->fileName() != filePath) {
- delete d->m_writer;
- d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession");
- }
- const bool result = d->m_writer->save(data, ICore::dialogParent());
- if (result) {
- if (!isDefaultVirgin())
- d->m_sessionDateTimes.insert(activeSession(), QDateTime::currentDateTime());
- } else {
- QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
- Tr::tr("Could not save session to file %1").arg(d->m_writer->fileName().toUserOutput()));
- }
-
- return result;
+ return sb_d->m_loadingSession;
}
/*!
- Closes all projects
- */
-void SessionManager::closeAllProjects()
-{
- removeProjects(projects());
-}
-
-const QList<Project *> SessionManager::projects()
-{
- return d->m_projects;
-}
-
-bool SessionManager::hasProjects()
-{
- return d->hasProjects();
-}
-
-bool SessionManager::hasProject(Project *p)
-{
- return d->m_projects.contains(p);
-}
-
-FilePaths SessionManagerPrivate::dependencies(const FilePath &proName) const
-{
- FilePaths result;
- dependencies(proName, result);
- return result;
-}
-
-void SessionManagerPrivate::dependencies(const FilePath &proName, FilePaths &result) const
-{
- const FilePaths depends = m_depMap.value(proName);
-
- for (const FilePath &dep : depends)
- dependencies(dep, result);
-
- if (!result.contains(proName))
- result.append(proName);
-}
-
-QString SessionManagerPrivate::sessionTitle(const FilePath &filePath)
-{
- if (SessionManager::isDefaultSession(d->m_sessionName)) {
- if (filePath.isEmpty()) {
- // use single project's name if there is only one loaded.
- const QList<Project *> projects = SessionManager::projects();
- if (projects.size() == 1)
- return projects.first()->displayName();
- }
- } else {
- QString sessionName = d->m_sessionName;
- if (sessionName.isEmpty())
- sessionName = Tr::tr("Untitled");
- return sessionName;
- }
- return QString();
-}
-
-QString SessionManagerPrivate::locationInProject(const FilePath &filePath) {
- const Project *project = SessionManager::projectForFile(filePath);
- if (!project)
- return QString();
-
- const FilePath parentDir = filePath.parentDir();
- if (parentDir == project->projectDirectory())
- return "@ " + project->displayName();
-
- if (filePath.isChildOf(project->projectDirectory())) {
- const FilePath dirInProject = parentDir.relativeChildPath(project->projectDirectory());
- return "(" + dirInProject.toUserOutput() + " @ " + project->displayName() + ")";
- }
-
- // For a file that is "outside" the project it belongs to, we display its
- // dir's full path because it is easier to read than a series of "../../.".
- // Example: /home/hugo/GenericProject/App.files lists /home/hugo/lib/Bar.cpp
- return "(" + parentDir.toUserOutput() + " @ " + project->displayName() + ")";
-}
-
-QString SessionManagerPrivate::windowTitleAddition(const FilePath &filePath)
-{
- return filePath.isEmpty() ? QString() : locationInProject(filePath);
-}
-
-FilePaths SessionManagerPrivate::dependenciesOrder() const
-{
- QList<QPair<FilePath, FilePaths>> unordered;
- FilePaths ordered;
-
- // copy the map to a temporary list
- for (const Project *pro : m_projects) {
- const FilePath proName = pro->projectFilePath();
- const FilePaths depList = filtered(m_depMap.value(proName),
- [this](const FilePath &proPath) {
- return contains(m_projects, [proPath](const Project *p) {
- return p->projectFilePath() == proPath;
- });
- });
- unordered.push_back({proName, depList});
- }
-
- while (!unordered.isEmpty()) {
- for (int i = (unordered.count() - 1); i >= 0; --i) {
- if (unordered.at(i).second.isEmpty()) {
- ordered << unordered.at(i).first;
- unordered.removeAt(i);
- }
- }
-
- // remove the handled projects from the dependency lists
- // of the remaining unordered projects
- for (int i = 0; i < unordered.count(); ++i) {
- for (const FilePath &pro : std::as_const(ordered)) {
- FilePaths depList = unordered.at(i).second;
- depList.removeAll(pro);
- unordered[i].second = depList;
- }
- }
- }
-
- return ordered;
-}
-
-QList<Project *> SessionManager::projectOrder(const Project *project)
-{
- QList<Project *> result;
-
- FilePaths pros;
- if (project)
- pros = d->dependencies(project->projectFilePath());
- else
- pros = d->dependenciesOrder();
-
- for (const FilePath &proFile : std::as_const(pros)) {
- for (Project *pro : projects()) {
- if (pro->projectFilePath() == proFile) {
- result << pro;
- break;
- }
- }
- }
-
- return result;
-}
-
-Project *SessionManager::projectForFile(const FilePath &fileName)
-{
- if (Project * const project = Utils::findOrDefault(SessionManager::projects(),
- [&fileName](const Project *p) { return p->isKnownFile(fileName); })) {
- return project;
- }
- return Utils::findOrDefault(SessionManager::projects(),
- [&fileName](const Project *p) {
- for (const Target * const target : p->targets()) {
- for (const BuildConfiguration * const bc : target->buildConfigurations()) {
- if (fileName.isChildOf(bc->buildDirectory()))
- return false;
- }
- }
- return fileName.isChildOf(p->projectDirectory());
- });
-}
-
-Project *SessionManager::projectWithProjectFilePath(const FilePath &filePath)
-{
- return Utils::findOrDefault(SessionManager::projects(),
- [&filePath](const Project *p) { return p->projectFilePath() == filePath; });
-}
-
-void SessionManager::configureEditor(IEditor *editor, const QString &fileName)
-{
- if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
- Project *project = projectForFile(Utils::FilePath::fromString(fileName));
- // Global settings are the default.
- if (project)
- project->editorConfiguration()->configureEditor(textEditor);
- }
-}
+ Lets other plugins store persistent values within the session file.
+*/
-void SessionManager::configureEditors(Project *project)
+void SessionManager::setValue(const QString &name, const QVariant &value)
{
- const QList<IDocument *> documents = DocumentModel::openedDocuments();
- for (IDocument *document : documents) {
- if (project->isKnownFile(document->filePath())) {
- const QList<IEditor *> editors = DocumentModel::editorsForDocument(document);
- for (IEditor *editor : editors) {
- if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) {
- project->editorConfiguration()->configureEditor(textEditor);
- }
- }
- }
- }
+ if (sb_d->m_values.value(name) == value)
+ return;
+ sb_d->m_values.insert(name, value);
}
-void SessionManager::removeProjects(const QList<Project *> &remove)
+QVariant SessionManager::value(const QString &name)
{
- for (Project *pro : remove)
- emit m_instance->aboutToRemoveProject(pro);
-
- bool changeStartupProject = false;
-
- // Delete projects
- for (Project *pro : remove) {
- pro->saveSettings();
- pro->markAsShuttingDown();
-
- // Remove the project node:
- d->m_projects.removeOne(pro);
-
- if (pro == d->m_startupProject)
- changeStartupProject = true;
-
- FolderNavigationWidgetFactory::removeRootDirectory(projectFolderId(pro));
- disconnect(pro, nullptr, m_instance, nullptr);
- emit m_instance->projectRemoved(pro);
- }
-
- if (changeStartupProject)
- setStartupProject(hasProjects() ? projects().first() : nullptr);
-
- qDeleteAll(remove);
+ auto it = sb_d->m_values.constFind(name);
+ return (it == sb_d->m_values.constEnd()) ? QVariant() : *it;
}
-/*!
- Lets other plugins store persistent values within the session file.
-*/
-
-void SessionManager::setValue(const QString &name, const QVariant &value)
+void SessionManager::setSessionValue(const QString &name, const QVariant &value)
{
- if (d->m_values.value(name) == value)
- return;
- d->m_values.insert(name, value);
+ sb_d->m_sessionValues.insert(name, value);
}
-QVariant SessionManager::value(const QString &name)
+QVariant SessionManager::sessionValue(const QString &name, const QVariant &defaultValue)
{
- auto it = d->m_values.constFind(name);
- return (it == d->m_values.constEnd()) ? QVariant() : *it;
+ auto it = sb_d->m_sessionValues.constFind(name);
+ return (it == sb_d->m_sessionValues.constEnd()) ? defaultValue : *it;
}
QString SessionManager::activeSession()
{
- return d->m_sessionName;
+ return sb_d->m_sessionName;
}
QStringList SessionManager::sessions()
{
- if (d->m_sessions.isEmpty()) {
+ if (sb_d->m_sessions.isEmpty()) {
// We are not initialized yet, so do that now
const FilePaths sessionFiles =
ICore::userResourcePath().dirEntries({{"*qws"}}, QDir::Time | QDir::Reversed);
const QVariantMap lastActiveTimes = ICore::settings()->value(LAST_ACTIVE_TIMES_KEY).toMap();
for (const FilePath &file : sessionFiles) {
const QString &name = file.completeBaseName();
- d->m_sessionDateTimes.insert(name, file.lastModified());
+ sb_d->m_sessionDateTimes.insert(name, file.lastModified());
const auto lastActiveTime = lastActiveTimes.find(name);
- d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end()
+ sb_d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end()
? lastActiveTime->toDateTime()
: file.lastModified());
if (name != QLatin1String(DEFAULT_SESSION))
- d->m_sessions << name;
+ sb_d->m_sessions << name;
}
- d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION));
+ sb_d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION));
}
- return d->m_sessions;
+ return sb_d->m_sessions;
}
QDateTime SessionManager::sessionDateTime(const QString &session)
{
- return d->m_sessionDateTimes.value(session);
+ return sb_d->m_sessionDateTimes.value(session);
}
QDateTime SessionManager::lastActiveTime(const QString &session)
{
- return d->m_lastActiveTimes.value(session);
+ return sb_d->m_lastActiveTimes.value(session);
}
FilePath SessionManager::sessionNameToFileName(const QString &session)
@@ -836,9 +202,9 @@ bool SessionManager::createSession(const QString &session)
{
if (sessions().contains(session))
return false;
- Q_ASSERT(d->m_sessions.size() > 0);
- d->m_sessions.insert(1, session);
- d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
+ Q_ASSERT(sb_d->m_sessions.size() > 0);
+ sb_d->m_sessions.insert(1, session);
+ sb_d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
return true;
}
@@ -873,10 +239,10 @@ bool SessionManager::confirmSessionDelete(const QStringList &sessions)
*/
bool SessionManager::deleteSession(const QString &session)
{
- if (!d->m_sessions.contains(session))
+ if (!sb_d->m_sessions.contains(session))
return false;
- d->m_sessions.removeOne(session);
- d->m_lastActiveTimes.remove(session);
+ sb_d->m_sessions.removeOne(session);
+ sb_d->m_lastActiveTimes.remove(session);
emit instance()->sessionRemoved(session);
FilePath sessionFile = sessionNameToFileName(session);
if (sessionFile.exists())
@@ -892,19 +258,132 @@ void SessionManager::deleteSessions(const QStringList &sessions)
bool SessionManager::cloneSession(const QString &original, const QString &clone)
{
- if (!d->m_sessions.contains(original))
+ if (!sb_d->m_sessions.contains(original))
return false;
FilePath sessionFile = sessionNameToFileName(original);
// If the file does not exist, we can still clone
if (!sessionFile.exists() || sessionFile.copyFile(sessionNameToFileName(clone))) {
- d->m_sessions.insert(1, clone);
- d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified());
+ sb_d->m_sessions.insert(1, clone);
+ sb_d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified());
return true;
}
return false;
}
+static QString determineSessionToRestoreAtStartup()
+{
+ // TODO (session) move argument to core
+ // Process command line arguments first:
+ const bool lastSessionArg = ExtensionSystem::PluginManager::specForPlugin(
+ ProjectExplorerPlugin::instance())
+ ->arguments()
+ .contains("-lastsession");
+ if (lastSessionArg && !SessionManager::startupSession().isEmpty())
+ return SessionManager::startupSession();
+ const QStringList arguments = ExtensionSystem::PluginManager::arguments();
+ QStringList sessions = SessionManager::sessions();
+ // We have command line arguments, try to find a session in them
+ // Default to no session loading
+ for (const QString &arg : arguments) {
+ if (sessions.contains(arg)) {
+ // Session argument
+ return arg;
+ }
+ }
+ // Handle settings only after command line arguments:
+ if (sb_d->m_isAutoRestoreLastSession)
+ return SessionManager::startupSession();
+ return {};
+}
+
+void SessionManagerPrivate::restoreStartupSession()
+{
+ m_isStartupSessionRestored = true;
+ QString sessionToRestoreAtStartup = determineSessionToRestoreAtStartup();
+ if (!sessionToRestoreAtStartup.isEmpty())
+ ModeManager::activateMode(Core::Constants::MODE_EDIT);
+
+ // We have command line arguments, try to find a session in them
+ QStringList arguments = ExtensionSystem::PluginManager::arguments();
+ if (!sessionToRestoreAtStartup.isEmpty() && !arguments.isEmpty())
+ arguments.removeOne(sessionToRestoreAtStartup);
+
+ // Massage the argument list.
+ // Be smart about directories: If there is a session of that name, load it.
+ // Other than that, look for project files in it. The idea is to achieve
+ // 'Do what I mean' functionality when starting Creator in a directory with
+ // the single command line argument '.' and avoid editor warnings about not
+ // being able to open directories.
+ // In addition, convert "filename" "+45" or "filename" ":23" into
+ // "filename+45" and "filename:23".
+ if (!arguments.isEmpty()) {
+ const QStringList sessions = SessionManager::sessions();
+ for (int a = 0; a < arguments.size();) {
+ const QString &arg = arguments.at(a);
+ const QFileInfo fi(arg);
+ if (fi.isDir()) {
+ const QDir dir(fi.absoluteFilePath());
+ // Does the directory name match a session?
+ if (sessionToRestoreAtStartup.isEmpty() && sessions.contains(dir.dirName())) {
+ sessionToRestoreAtStartup = dir.dirName();
+ arguments.removeAt(a);
+ continue;
+ }
+ } // Done directories.
+ // Converts "filename" "+45" or "filename" ":23" into "filename+45" and "filename:23"
+ if (a && (arg.startsWith(QLatin1Char('+')) || arg.startsWith(QLatin1Char(':')))) {
+ arguments[a - 1].append(arguments.takeAt(a));
+ continue;
+ }
+ ++a;
+ } // for arguments
+ } // !arguments.isEmpty()
+
+ // Restore latest session or what was passed on the command line
+ SessionManager::loadSession(!sessionToRestoreAtStartup.isEmpty() ? sessionToRestoreAtStartup
+ : QString(),
+ true);
+
+ // delay opening projects from the command line even more
+ QTimer::singleShot(0, m_instance, [arguments] {
+ ICore::openFiles(Utils::transform(arguments, &FilePath::fromUserInput),
+ ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers
+ | ICore::SwitchMode));
+ emit m_instance->startupSessionRestored();
+ });
+}
+
+bool SessionManagerPrivate::isStartupSessionRestored()
+{
+ return sb_d->m_isStartupSessionRestored;
+}
+
+void SessionManagerPrivate::saveSettings()
+{
+ ICore::settings()->setValueWithDefault(AUTO_RESTORE_SESSION_SETTINGS_KEY,
+ sb_d->m_isAutoRestoreLastSession,
+ kIsAutoRestoreLastSessionDefault);
+}
+
+void SessionManagerPrivate::restoreSettings()
+{
+ sb_d->m_isAutoRestoreLastSession = ICore::settings()
+ ->value(AUTO_RESTORE_SESSION_SETTINGS_KEY,
+ kIsAutoRestoreLastSessionDefault)
+ .toBool();
+}
+
+bool SessionManagerPrivate::isAutoRestoreLastSession()
+{
+ return sb_d->m_isAutoRestoreLastSession;
+}
+
+void SessionManagerPrivate::setAutoRestoreLastSession(bool restore)
+{
+ sb_d->m_isAutoRestoreLastSession = restore;
+}
+
void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader)
{
const QStringList keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList();
@@ -914,86 +393,57 @@ void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader
}
}
-void SessionManagerPrivate::restoreDependencies(const PersistentSettingsReader &reader)
+void SessionManagerPrivate::restoreSessionValues(const PersistentSettingsReader &reader)
{
- QMap<QString, QVariant> depMap = reader.restoreValue(QLatin1String("ProjectDependencies")).toMap();
- auto i = depMap.constBegin();
- while (i != depMap.constEnd()) {
- const QString &key = i.key();
- FilePaths values;
- const QStringList valueList = i.value().toStringList();
- for (const QString &value : valueList)
- values << FilePath::fromString(value);
- m_depMap.insert(FilePath::fromString(key), values);
- ++i;
+ const QVariantMap values = reader.restoreValues();
+ // restore toplevel items that are not restored by restoreValues
+ const auto end = values.constEnd();
+ for (auto it = values.constBegin(); it != end; ++it) {
+ if (it.key() == "valueKeys" || it.key().startsWith("value-"))
+ continue;
+ m_sessionValues.insert(it.key(), it.value());
}
}
-void SessionManagerPrivate::askUserAboutFailedProjects()
+void SessionManagerPrivate::restoreEditors()
{
- FilePaths failedProjects = m_failedProjects;
- if (!failedProjects.isEmpty()) {
- QString fileList = FilePath::formatFilePaths(failedProjects, "<br>");
- QMessageBox box(QMessageBox::Warning,
- Tr::tr("Failed to restore project files"),
- Tr::tr("Could not restore the following project files:<br><b>%1</b>").
- arg(fileList));
- auto keepButton = new QPushButton(Tr::tr("Keep projects in Session"), &box);
- auto removeButton = new QPushButton(Tr::tr("Remove projects from Session"), &box);
- box.addButton(keepButton, QMessageBox::AcceptRole);
- box.addButton(removeButton, QMessageBox::DestructiveRole);
-
- box.exec();
-
- if (box.clickedButton() == removeButton)
- m_failedProjects.clear();
+ const QVariant editorsettings = m_sessionValues.value("EditorSettings");
+ if (editorsettings.isValid()) {
+ EditorManager::restoreState(QByteArray::fromBase64(editorsettings.toByteArray()));
+ SessionManager::sessionLoadingProgress();
}
}
-void SessionManagerPrivate::restoreStartupProject(const PersistentSettingsReader &reader)
+/*!
+ Returns the last session that was opened by the user.
+*/
+QString SessionManager::lastSession()
{
- const FilePath startupProject = FilePath::fromSettings(reader.restoreValue("StartupProject"));
- if (!startupProject.isEmpty()) {
- for (Project *pro : std::as_const(m_projects)) {
- if (pro->projectFilePath() == startupProject) {
- m_instance->setStartupProject(pro);
- break;
- }
- }
- }
- if (!m_startupProject) {
- if (!startupProject.isEmpty())
- qWarning() << "Could not find startup project" << startupProject;
- if (hasProjects())
- m_instance->setStartupProject(m_projects.first());
- }
+ return ICore::settings()->value(LASTSESSION_KEY).toString();
}
-void SessionManagerPrivate::restoreEditors(const PersistentSettingsReader &reader)
+/*!
+ Returns the session that was active when Qt Creator was last closed, if any.
+*/
+QString SessionManager::startupSession()
{
- const QVariant editorsettings = reader.restoreValue(QLatin1String("EditorSettings"));
- if (editorsettings.isValid()) {
- EditorManager::restoreState(QByteArray::fromBase64(editorsettings.toByteArray()));
- sessionLoadingProgress();
- }
+ return ICore::settings()->value(STARTUPSESSION_KEY).toString();
}
-/*!
- Loads a session, takes a session name (not filename).
-*/
-void SessionManagerPrivate::restoreProjects(const FilePaths &fileList)
+void SessionManager::markSessionFileDirty()
{
- // indirectly adds projects to session
- // Keep projects that failed to load in the session!
- m_failedProjects = fileList;
- if (!fileList.isEmpty()) {
- ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProjects(fileList);
- if (!result)
- ProjectExplorerPlugin::showOpenProjectError(result);
- const QList<Project *> projects = result.projects();
- for (const Project *p : projects)
- m_failedProjects.removeAll(p->projectFilePath());
- }
+ sb_d->m_virginSession = false;
+}
+
+void SessionManager::sessionLoadingProgress()
+{
+ sb_d->m_future.setProgressValue(sb_d->m_future.progressValue() + 1);
+ QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+}
+
+void SessionManager::addSessionLoadingSteps(int steps)
+{
+ sb_d->m_future.setProgressRange(0, sb_d->m_future.progressMaximum() + steps);
}
/*
@@ -1025,238 +475,100 @@ bool SessionManager::loadSession(const QString &session, bool initial)
{
const bool loadImplicitDefault = session.isEmpty();
const bool switchFromImplicitToExplicitDefault = session == DEFAULT_SESSION
- && d->m_sessionName == DEFAULT_SESSION && !initial;
+ && sb_d->m_sessionName == DEFAULT_SESSION
+ && !initial;
// Do nothing if we have that session already loaded,
// exception if the session is the default virgin session
// we still want to be able to load the default session
- if (session == d->m_sessionName && !isDefaultVirgin())
+ if (session == sb_d->m_sessionName && !SessionManager::isDefaultVirgin())
return true;
- if (!loadImplicitDefault && !sessions().contains(session))
+ if (!loadImplicitDefault && !SessionManager::sessions().contains(session))
return false;
- FilePaths fileList;
// Try loading the file
- FilePath fileName = sessionNameToFileName(loadImplicitDefault ? DEFAULT_SESSION : session);
+ FilePath fileName = SessionManager::sessionNameToFileName(loadImplicitDefault ? DEFAULT_SESSION
+ : session);
PersistentSettingsReader reader;
if (fileName.exists()) {
if (!reader.load(fileName)) {
- QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while restoring session"),
+ QMessageBox::warning(ICore::dialogParent(),
+ Tr::tr("Error while restoring session"),
Tr::tr("Could not restore session %1").arg(fileName.toUserOutput()));
return false;
}
if (loadImplicitDefault) {
- d->restoreValues(reader);
- emit m_instance->sessionLoaded(DEFAULT_SESSION);
+ sb_d->restoreValues(reader);
+ emit SessionManager::instance()->sessionLoaded(DEFAULT_SESSION);
return true;
}
-
- fileList = FileUtils::toFilePathList(reader.restoreValue("ProjectList").toStringList());
} else if (loadImplicitDefault) {
return true;
}
- d->m_loadingSession = true;
+ sb_d->m_loadingSession = true;
// Allow everyone to set something in the session and before saving
- emit m_instance->aboutToUnloadSession(d->m_sessionName);
+ emit SessionManager::instance()->aboutToUnloadSession(sb_d->m_sessionName);
- if (!save()) {
- d->m_loadingSession = false;
+ if (!ProjectManager::save()) {
+ sb_d->m_loadingSession = false;
return false;
}
// Clean up
if (!EditorManager::closeAllEditors()) {
- d->m_loadingSession = false;
+ sb_d->m_loadingSession = false;
return false;
}
- // find a list of projects to close later
- const QList<Project *> projectsToRemove = Utils::filtered(projects(), [&fileList](Project *p) {
- return !fileList.contains(p->projectFilePath());
- });
- const QList<Project *> openProjects = projects();
- const FilePaths projectPathsToLoad = Utils::filtered(fileList, [&openProjects](const FilePath &path) {
- return !Utils::contains(openProjects, [&path](Project *p) {
- return p->projectFilePath() == path;
- });
- });
- d->m_failedProjects.clear();
- d->m_depMap.clear();
if (!switchFromImplicitToExplicitDefault)
- d->m_values.clear();
- d->m_casadeSetActive = false;
+ sb_d->m_values.clear();
+ sb_d->m_sessionValues.clear();
- d->m_sessionName = session;
- delete d->m_writer;
- d->m_writer = nullptr;
+ sb_d->m_sessionName = session;
+ delete sb_d->m_writer;
+ sb_d->m_writer = nullptr;
EditorManager::updateWindowTitles();
- if (fileName.exists()) {
- d->m_virginSession = false;
+ sb_d->m_virginSession = false;
- ProgressManager::addTask(d->m_future.future(), Tr::tr("Loading Session"),
- "ProjectExplorer.SessionFile.Load");
+ ProgressManager::addTask(sb_d->m_future.future(),
+ Tr::tr("Loading Session"),
+ "ProjectExplorer.SessionFile.Load");
- d->m_future.setProgressRange(0, 1);
- d->m_future.setProgressValue(0);
+ sb_d->m_future.setProgressRange(0, 1 /*initialization*/ + 1 /*editors*/);
+ sb_d->m_future.setProgressValue(0);
+ if (fileName.exists()) {
if (!switchFromImplicitToExplicitDefault)
- d->restoreValues(reader);
- emit m_instance->aboutToLoadSession(session);
-
- // retrieve all values before the following code could change them again
- Id modeId = Id::fromSetting(value(QLatin1String("ActiveMode")));
- if (!modeId.isValid())
- modeId = Id(Core::Constants::MODE_EDIT);
-
- QColor c = QColor(reader.restoreValue(QLatin1String("Color")).toString());
- if (c.isValid())
- StyleHelper::setBaseColor(c);
-
- d->m_future.setProgressRange(0, projectPathsToLoad.count() + 1/*initialization above*/ + 1/*editors*/);
- d->m_future.setProgressValue(1);
- QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
-
- d->restoreProjects(projectPathsToLoad);
- d->sessionLoadingProgress();
- d->restoreDependencies(reader);
- d->restoreStartupProject(reader);
-
- removeProjects(projectsToRemove); // only remove old projects now that the startup project is set!
-
- d->restoreEditors(reader);
-
- d->m_future.reportFinished();
- d->m_future = QFutureInterface<void>();
-
- // Fall back to Project mode if the startup project is unconfigured and
- // use the mode saved in the session otherwise
- if (d->m_startupProject && d->m_startupProject->needsConfiguration())
- modeId = Id(Constants::MODE_SESSION);
-
- ModeManager::activateMode(modeId);
- ModeManager::setFocusToCurrentMode();
- } else {
- removeProjects(projects());
- ModeManager::activateMode(Id(Core::Constants::MODE_EDIT));
- ModeManager::setFocusToCurrentMode();
+ sb_d->restoreValues(reader);
+ sb_d->restoreSessionValues(reader);
}
- d->m_casadeSetActive = reader.restoreValue(QLatin1String("CascadeSetActive"), false).toBool();
- d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
-
- emit m_instance->sessionLoaded(session);
+ QColor c = QColor(SessionManager::sessionValue("Color").toString());
+ if (c.isValid())
+ StyleHelper::setBaseColor(c);
- // Starts a event loop, better do that at the very end
- d->askUserAboutFailedProjects();
- d->m_loadingSession = false;
- return true;
-}
-
-/*!
- Returns the last session that was opened by the user.
-*/
-QString SessionManager::lastSession()
-{
- return ICore::settings()->value(Constants::LASTSESSION_KEY).toString();
-}
-
-/*!
- Returns the session that was active when Qt Creator was last closed, if any.
-*/
-QString SessionManager::startupSession()
-{
- return ICore::settings()->value(Constants::STARTUPSESSION_KEY).toString();
-}
+ SessionManager::sessionLoadingProgress();
-void SessionManager::reportProjectLoadingProgress()
-{
- d->sessionLoadingProgress();
-}
+ sb_d->restoreEditors();
-void SessionManager::markSessionFileDirty()
-{
- d->m_virginSession = false;
-}
+ // let other code restore the session
+ emit SessionManager::instance()->aboutToLoadSession(session);
-void SessionManagerPrivate::sessionLoadingProgress()
-{
- m_future.setProgressValue(m_future.progressValue() + 1);
- QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
-}
+ sb_d->m_future.reportFinished();
+ sb_d->m_future = QFutureInterface<void>();
-FilePaths SessionManager::projectsForSessionName(const QString &session)
-{
- const FilePath fileName = sessionNameToFileName(session);
- PersistentSettingsReader reader;
- if (fileName.exists()) {
- if (!reader.load(fileName)) {
- qWarning() << "Could not restore session" << fileName.toUserOutput();
- return {};
- }
- }
- return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(),
- &FilePath::fromUserInput);
-}
+ sb_d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime());
-#ifdef WITH_TESTS
+ emit SessionManager::instance()->sessionLoaded(session);
-void ProjectExplorerPlugin::testSessionSwitch()
-{
- QVERIFY(SessionManager::createSession("session1"));
- QVERIFY(SessionManager::createSession("session2"));
- QTemporaryFile cppFile("main.cpp");
- QVERIFY(cppFile.open());
- cppFile.close();
- QTemporaryFile projectFile1("XXXXXX.pro");
- QTemporaryFile projectFile2("XXXXXX.pro");
- struct SessionSpec {
- SessionSpec(const QString &n, QTemporaryFile &f) : name(n), projectFile(f) {}
- const QString name;
- QTemporaryFile &projectFile;
- };
- std::vector<SessionSpec> sessionSpecs{SessionSpec("session1", projectFile1),
- SessionSpec("session2", projectFile2)};
- for (const SessionSpec &sessionSpec : sessionSpecs) {
- static const QByteArray proFileContents
- = "TEMPLATE = app\n"
- "CONFIG -= qt\n"
- "SOURCES = " + cppFile.fileName().toLocal8Bit();
- QVERIFY(sessionSpec.projectFile.open());
- sessionSpec.projectFile.write(proFileContents);
- sessionSpec.projectFile.close();
- QVERIFY(SessionManager::loadSession(sessionSpec.name));
- const OpenProjectResult openResult
- = ProjectExplorerPlugin::openProject(
- FilePath::fromString(sessionSpec.projectFile.fileName()));
- if (openResult.errorMessage().contains("text/plain"))
- QSKIP("This test requires the presence of QmakeProjectManager to be fully functional");
- QVERIFY(openResult);
- QCOMPARE(openResult.projects().count(), 1);
- QVERIFY(openResult.project());
- QCOMPARE(SessionManager::projects().count(), 1);
- }
- for (int i = 0; i < 30; ++i) {
- QVERIFY(SessionManager::loadSession("session1"));
- QCOMPARE(SessionManager::activeSession(), "session1");
- QCOMPARE(SessionManager::projects().count(), 1);
- QVERIFY(SessionManager::loadSession("session2"));
- QCOMPARE(SessionManager::activeSession(), "session2");
- QCOMPARE(SessionManager::projects().count(), 1);
- }
- QVERIFY(SessionManager::loadSession("session1"));
- SessionManager::closeAllProjects();
- QVERIFY(SessionManager::loadSession("session2"));
- SessionManager::closeAllProjects();
- QVERIFY(SessionManager::deleteSession("session1"));
- QVERIFY(SessionManager::deleteSession("session2"));
+ sb_d->m_loadingSession = false;
+ return true;
}
-#endif // WITH_TESTS
-
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h
index a29e478046..b40e687d64 100644
--- a/src/plugins/projectexplorer/session.h
+++ b/src/plugins/projectexplorer/session.h
@@ -12,25 +12,14 @@
#include <QString>
#include <QStringList>
-namespace Core { class IEditor; }
-
namespace ProjectExplorer {
-class Project;
-class Target;
-class BuildConfiguration;
-class BuildSystem;
-class DeployConfiguration;
-class RunConfiguration;
-
-enum class SetActive { Cascade, NoCascade };
-
class PROJECTEXPLORER_EXPORT SessionManager : public QObject
{
Q_OBJECT
public:
- explicit SessionManager(QObject *parent = nullptr);
+ SessionManager();
~SessionManager() override;
static SessionManager *instance();
@@ -52,85 +41,44 @@ public:
static bool cloneSession(const QString &original, const QString &clone);
static bool renameSession(const QString &original, const QString &newName);
- static bool loadSession(const QString &session, bool initial = false);
-
- static bool save();
- static void closeAllProjects();
-
- static void addProject(Project *project);
- static void removeProject(Project *project);
- static void removeProjects(const QList<Project *> &remove);
-
- static void setStartupProject(Project *startupProject);
-
- static QList<Project *> dependencies(const Project *project);
- static bool hasDependency(const Project *project, const Project *depProject);
- static bool canAddDependency(const Project *project, const Project *depProject);
- static bool addDependency(Project *project, Project *depProject);
- static void removeDependency(Project *project, Project *depProject);
-
- static bool isProjectConfigurationCascading();
- static void setProjectConfigurationCascading(bool b);
-
- static void setActiveTarget(Project *p, Target *t, SetActive cascade);
- static void setActiveBuildConfiguration(Target *t, BuildConfiguration *bc, SetActive cascade);
- static void setActiveDeployConfiguration(Target *t, DeployConfiguration *dc, SetActive cascade);
-
static Utils::FilePath sessionNameToFileName(const QString &session);
- static Project *startupProject();
- static Target *startupTarget();
- static BuildSystem *startupBuildSystem();
- static RunConfiguration *startupRunConfiguration();
-
- static const QList<Project *> projects();
- static bool hasProjects();
- static bool hasProject(Project *p);
static bool isDefaultVirgin();
static bool isDefaultSession(const QString &session);
// Let other plugins store persistent values within the session file
+ // These are settings that are also saved and loaded at startup, and are taken over
+ // to the default session when switching from implicit to explicit default session
static void setValue(const QString &name, const QVariant &value);
static QVariant value(const QString &name);
- // NBS rewrite projectOrder (dependency management)
- static QList<Project *> projectOrder(const Project *project = nullptr);
-
- static Project *projectForFile(const Utils::FilePath &fileName);
- static Project *projectWithProjectFilePath(const Utils::FilePath &filePath);
+ // These are settings that are specific to a session and are not loaded
+ // at startup and also not taken over to the default session when switching from implicit
+ static void setSessionValue(const QString &name, const QVariant &value);
+ static QVariant sessionValue(const QString &name, const QVariant &defaultValue = {});
- static Utils::FilePaths projectsForSessionName(const QString &session);
-
- static void reportProjectLoadingProgress();
static bool loadingSession();
+ static void markSessionFileDirty();
-signals:
- void targetAdded(ProjectExplorer::Target *target);
- void targetRemoved(ProjectExplorer::Target *target);
- void projectAdded(ProjectExplorer::Project *project);
- void aboutToRemoveProject(ProjectExplorer::Project *project);
- void projectDisplayNameChanged(ProjectExplorer::Project *project);
- void projectRemoved(ProjectExplorer::Project *project);
+ static void sessionLoadingProgress();
+ static void addSessionLoadingSteps(int steps);
- void startupProjectChanged(ProjectExplorer::Project *project);
+ static bool loadSession(const QString &session, bool initial = false);
+signals:
+ void startupSessionRestored();
void aboutToUnloadSession(QString sessionName);
+ // Sent during session loading, after the values of the session are available via value() and
+ // sessionValue. Use to restore values from the new session
void aboutToLoadSession(QString sessionName);
void sessionLoaded(QString sessionName);
void aboutToSaveSession();
- void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b);
void sessionRenamed(const QString &oldName, const QString &newName);
void sessionRemoved(const QString &name);
- // for tests only
- void projectFinishedParsing(ProjectExplorer::Project *project);
-
private:
static void saveActiveMode(Utils::Id mode);
- static void configureEditor(Core::IEditor *editor, const QString &fileName);
- static void markSessionFileDirty();
- static void configureEditors(Project *project);
};
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/session_p.h b/src/plugins/projectexplorer/session_p.h
new file mode 100644
index 0000000000..40f691ab2d
--- /dev/null
+++ b/src/plugins/projectexplorer/session_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <utils/persistentsettings.h>
+
+#include <QFutureInterface>
+
+using namespace Utils;
+
+namespace ProjectExplorer {
+
+class SessionManagerPrivate
+{
+public:
+ void restoreStartupSession();
+
+ void restoreValues(const PersistentSettingsReader &reader);
+ void restoreSessionValues(const PersistentSettingsReader &reader);
+ void restoreEditors();
+
+ bool isStartupSessionRestored();
+ void saveSettings();
+ void restoreSettings();
+ bool isAutoRestoreLastSession();
+ void setAutoRestoreLastSession(bool restore);
+
+ static QString windowTitleAddition(const FilePath &filePath);
+ static QString sessionTitle(const FilePath &filePath);
+
+ QString m_sessionName = "default";
+ bool m_isStartupSessionRestored = false;
+ bool m_isAutoRestoreLastSession = false;
+ bool m_virginSession = true;
+ bool m_loadingSession = false;
+
+ mutable QStringList m_sessions;
+ mutable QHash<QString, QDateTime> m_sessionDateTimes;
+ QHash<QString, QDateTime> m_lastActiveTimes;
+
+ QMap<QString, QVariant> m_values;
+ QMap<QString, QVariant> m_sessionValues;
+ QFutureInterface<void> m_future;
+ PersistentSettingsWriter *m_writer = nullptr;
+};
+
+extern SessionManagerPrivate *sb_d;
+
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/sessiondialog.cpp b/src/plugins/projectexplorer/sessiondialog.cpp
index 745a876230..fe9758dc1b 100644
--- a/src/plugins/projectexplorer/sessiondialog.cpp
+++ b/src/plugins/projectexplorer/sessiondialog.cpp
@@ -77,7 +77,7 @@ SessionNameInputDialog::SessionNameInputDialog(QWidget *parent)
m_usedSwitchTo = true;
});
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Enter the name of the session:"),
m_newSessionLineEdit,
@@ -144,13 +144,11 @@ SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent)
m_openButton->setDefault(true);
- // FIXME: Simplify translator's work.
- auto whatsASessionLabel = new QLabel(
- Tr::tr("<a href=\"qthelp://org.qt-project.qtcreator/doc/creator-project-managing-sessions.html\">"
- "What is a Session?</a>"));
+ auto whatsASessionLabel = new QLabel(QString("<a href=\"qthelp://org.qt-project.qtcreator/doc/"
+ "creator-project-managing-sessions.html\">%1</a>").arg(Tr::tr("What is a Session?")));
whatsASessionLabel->setOpenExternalLinks(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
diff --git a/src/plugins/projectexplorer/sessionmodel.cpp b/src/plugins/projectexplorer/sessionmodel.cpp
index d8c9aa935e..960a9dbdee 100644
--- a/src/plugins/projectexplorer/sessionmodel.cpp
+++ b/src/plugins/projectexplorer/sessionmodel.cpp
@@ -4,6 +4,7 @@
#include "sessionmodel.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "session.h"
#include "sessiondialog.h"
@@ -122,10 +123,10 @@ QVariant SessionModel::data(const QModelIndex &index, int role) const
result = SessionManager::activeSession() == sessionName;
break;
case ProjectsPathRole:
- result = pathsWithTildeHomePath(SessionManager::projectsForSessionName(sessionName));
+ result = pathsWithTildeHomePath(ProjectManager::projectsForSessionName(sessionName));
break;
case ProjectsDisplayRole:
- result = pathsToBaseNames(SessionManager::projectsForSessionName(sessionName));
+ result = pathsToBaseNames(ProjectManager::projectsForSessionName(sessionName));
break;
case ShortcutRole: {
const Id sessionBase = SESSION_BASE_ID;
diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp
index 8f227512ee..d2e59aab06 100644
--- a/src/plugins/projectexplorer/target.cpp
+++ b/src/plugins/projectexplorer/target.cpp
@@ -21,8 +21,8 @@
#include "projectexplorericons.h"
#include "projectexplorersettings.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "runconfiguration.h"
-#include "session.h"
#include <coreplugin/coreconstants.h>
@@ -120,10 +120,10 @@ Target::Target(Project *project, Kit *k, _constructor_tag) :
});
connect(this, &Target::parsingFinished, this, [this, project](bool success) {
- if (success && this == SessionManager::startupTarget())
+ if (success && this == ProjectManager::startupTarget())
updateDefaultRunConfigurations();
// For testing.
- emit SessionManager::instance()->projectFinishedParsing(project);
+ emit ProjectManager::instance()->projectFinishedParsing(project);
emit project->anyParsingFinished(this, success);
}, Qt::QueuedConnection); // Must wait for run configs to change their enabled state.
@@ -242,6 +242,70 @@ QString Target::activeBuildKey() const
return d->m_activeRunConfiguration->buildKey();
}
+void Target::setActiveBuildConfiguration(BuildConfiguration *bc, SetActive cascade)
+{
+ QTC_ASSERT(project(), return);
+
+ if (project()->isShuttingDown() || isShuttingDown())
+ return;
+
+ setActiveBuildConfiguration(bc);
+
+ if (!bc)
+ return;
+ if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading())
+ return;
+
+ Id kitId = kit()->id();
+ QString name = bc->displayName(); // We match on displayname
+ for (Project *otherProject : ProjectManager::projects()) {
+ if (otherProject == project())
+ continue;
+ Target *otherTarget = otherProject->activeTarget();
+ if (!otherTarget || otherTarget->kit()->id() != kitId)
+ continue;
+
+ for (BuildConfiguration *otherBc : otherTarget->buildConfigurations()) {
+ if (otherBc->displayName() == name) {
+ otherTarget->setActiveBuildConfiguration(otherBc);
+ break;
+ }
+ }
+ }
+}
+
+void Target::setActiveDeployConfiguration(DeployConfiguration *dc, SetActive cascade)
+{
+ QTC_ASSERT(project(), return);
+
+ if (project()->isShuttingDown() || isShuttingDown())
+ return;
+
+ setActiveDeployConfiguration(dc);
+
+ if (!dc)
+ return;
+ if (cascade != SetActive::Cascade || !ProjectManager::isProjectConfigurationCascading())
+ return;
+
+ Id kitId = kit()->id();
+ QString name = dc->displayName(); // We match on displayname
+ for (Project *otherProject : ProjectManager::projects()) {
+ if (otherProject == project())
+ continue;
+ Target *otherTarget = otherProject->activeTarget();
+ if (!otherTarget || otherTarget->kit()->id() != kitId)
+ continue;
+
+ for (DeployConfiguration *otherDc : otherTarget->deployConfigurations()) {
+ if (otherDc->displayName() == name) {
+ otherTarget->setActiveDeployConfiguration(otherDc);
+ break;
+ }
+ }
+ }
+}
+
Utils::Id Target::id() const
{
return d->m_kit->id();
@@ -307,9 +371,9 @@ bool Target::removeBuildConfiguration(BuildConfiguration *bc)
if (activeBuildConfiguration() == bc) {
if (d->m_buildConfigurations.isEmpty())
- SessionManager::setActiveBuildConfiguration(this, nullptr, SetActive::Cascade);
+ setActiveBuildConfiguration(nullptr, SetActive::Cascade);
else
- SessionManager::setActiveBuildConfiguration(this, d->m_buildConfigurations.at(0), SetActive::Cascade);
+ setActiveBuildConfiguration(d->m_buildConfigurations.at(0), SetActive::Cascade);
}
emit removedBuildConfiguration(bc);
@@ -377,10 +441,9 @@ bool Target::removeDeployConfiguration(DeployConfiguration *dc)
if (activeDeployConfiguration() == dc) {
if (d->m_deployConfigurations.isEmpty())
- SessionManager::setActiveDeployConfiguration(this, nullptr, SetActive::Cascade);
+ setActiveDeployConfiguration(nullptr, SetActive::Cascade);
else
- SessionManager::setActiveDeployConfiguration(this, d->m_deployConfigurations.at(0),
- SetActive::Cascade);
+ setActiveDeployConfiguration(d->m_deployConfigurations.at(0), SetActive::Cascade);
}
ProjectExplorerPlugin::targetSelector()->removedDeployConfiguration(dc);
diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h
index 78f0b5f3b5..aeca2fd9e0 100644
--- a/src/plugins/projectexplorer/target.h
+++ b/src/plugins/projectexplorer/target.h
@@ -26,11 +26,13 @@ class Project;
class ProjectConfigurationModel;
class RunConfiguration;
+enum class SetActive : int { Cascade, NoCascade };
+
class TargetPrivate;
class PROJECTEXPLORER_EXPORT Target : public QObject
{
- friend class SessionManager; // for setActiveBuild and setActiveDeployConfiguration
+ friend class ProjectManager; // for setActiveBuild and setActiveDeployConfiguration
Q_OBJECT
public:
@@ -109,6 +111,9 @@ public:
QString activeBuildKey() const; // Build key of active run configuaration
+ void setActiveBuildConfiguration(BuildConfiguration *bc, SetActive cascade);
+ void setActiveDeployConfiguration(DeployConfiguration *dc, SetActive cascade);
+
signals:
void targetEnabled(bool);
void iconChanged();
diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp
index 6d8c81e634..d2fdc3b43a 100644
--- a/src/plugins/projectexplorer/targetsettingspanel.cpp
+++ b/src/plugins/projectexplorer/targetsettingspanel.cpp
@@ -12,9 +12,9 @@
#include "project.h"
#include "projectexplorericons.h"
#include "projectexplorertr.h"
+#include "projectmanager.h"
#include "projectwindow.h"
#include "runsettingspropertiespage.h"
-#include "session.h"
#include "target.h"
#include "targetsetuppage.h"
#include "task.h"
@@ -281,7 +281,7 @@ public:
QFont font = parent()->data(column, role).value<QFont>();
if (TargetItem *targetItem = parent()->currentTargetItem()) {
Target *t = targetItem->target();
- if (t && t->id() == m_kitId && m_project == SessionManager::startupProject())
+ if (t && t->id() == m_kitId && m_project == ProjectManager::startupProject())
font.setBold(true);
}
return font;
@@ -334,7 +334,7 @@ public:
// Go to Run page, when on Run previously etc.
TargetItem *previousItem = parent()->currentTargetItem();
m_currentChild = previousItem ? previousItem->m_currentChild : DefaultPage;
- SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade);
+ m_project->setActiveTarget(target(), SetActive::Cascade);
parent()->setData(column, QVariant::fromValue(static_cast<TreeItem *>(this)),
ItemActivatedFromBelowRole);
}
@@ -346,7 +346,7 @@ public:
int child = indexOf(data.value<TreeItem *>());
QTC_ASSERT(child != -1, return false);
m_currentChild = child; // Triggered from sub-item.
- SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade);
+ m_project->setActiveTarget(target(), SetActive::Cascade);
// Propagate Build/Run selection up.
parent()->setData(column, QVariant::fromValue(static_cast<TreeItem *>(this)),
ItemActivatedFromBelowRole);
@@ -355,7 +355,7 @@ public:
if (role == ItemActivatedFromAboveRole) {
// Usually programmatic activation, e.g. after opening the Project mode.
- SessionManager::setActiveTarget(m_project, target(), SetActive::Cascade);
+ m_project->setActiveTarget(target(), SetActive::Cascade);
return true;
}
return false;
@@ -377,7 +377,7 @@ public:
= menu->addAction(Tr::tr("Enable Kit for All Projects"));
enableForAllAction->setEnabled(isSelectable);
QObject::connect(enableForAllAction, &QAction::triggered, [kit] {
- for (Project * const p : SessionManager::projects()) {
+ for (Project * const p : ProjectManager::projects()) {
if (!p->target(kit))
p->addTargetForKit(kit);
}
@@ -411,7 +411,7 @@ public:
QAction *disableForAllAction = menu->addAction(Tr::tr("Disable Kit for All Projects"));
disableForAllAction->setEnabled(isSelectable);
QObject::connect(disableForAllAction, &QAction::triggered, [kit] {
- for (Project * const p : SessionManager::projects()) {
+ for (Project * const p : ProjectManager::projects()) {
Target * const t = p->target(kit);
if (!t)
continue;
diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp
index a17aa58c94..fd1e8a78c6 100644
--- a/src/plugins/projectexplorer/targetsetuppage.cpp
+++ b/src/plugins/projectexplorer/targetsetuppage.cpp
@@ -11,18 +11,17 @@
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
-#include "session.h"
#include "target.h"
#include "targetsetupwidget.h"
#include "task.h"
#include <coreplugin/icore.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/wizard.h>
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
+#include <utils/process.h>
+#include <utils/qtcassert.h>
+#include <utils/wizard.h>
#include <QApplication>
#include <QCheckBox>
@@ -658,7 +657,7 @@ bool TargetSetupPage::setupProject(Project *project)
if (m_importer)
activeTarget = m_importer->preferredTarget(project->targets());
if (activeTarget)
- SessionManager::setActiveTarget(project, activeTarget, SetActive::NoCascade);
+ project->setActiveTarget(activeTarget, SetActive::NoCascade);
return true;
}
diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp
index cae28fc3e1..63b61401d5 100644
--- a/src/plugins/projectexplorer/targetsetupwidget.cpp
+++ b/src/plugins/projectexplorer/targetsetupwidget.cpp
@@ -180,10 +180,8 @@ void TargetSetupWidget::manageKit()
if (!m_kit)
return;
- if (auto kitPage = KitOptionsPage::instance()) {
- kitPage->showKit(m_kit);
- Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget());
- }
+ KitOptionsPage::showKit(m_kit);
+ Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget());
}
void TargetSetupWidget::setProjectPath(const FilePath &projectPath)
diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp
index dab4c7ed17..03c2a282be 100644
--- a/src/plugins/projectexplorer/task.cpp
+++ b/src/plugins/projectexplorer/task.cpp
@@ -8,8 +8,8 @@
#include "projectexplorertr.h"
#include <app/app_version.h>
+#include <texteditor/fontsettings.h>
#include <texteditor/textmark.h>
-
#include <utils/algorithm.h>
#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
@@ -105,11 +105,16 @@ void Task::setFile(const Utils::FilePath &file_)
}
}
-QString Task::description() const
+QString Task::description(DescriptionTags tags) const
{
- QString desc = summary;
- if (!details.isEmpty())
- desc.append('\n').append(details.join('\n'));
+ QString desc;
+ if (tags & WithSummary)
+ desc = summary;
+ if (!details.isEmpty()) {
+ if (!desc.isEmpty())
+ desc.append('\n');
+ desc.append(details.join('\n'));
+ }
return desc;
}
@@ -120,6 +125,39 @@ QIcon Task::icon() const
return m_icon;
}
+QString Task::formattedDescription(DescriptionTags tags, const QString &extraHeading) const
+{
+ if (isNull())
+ return {};
+
+ QString text = description(tags);
+ const int offset = (tags & WithSummary) ? 0 : summary.size() + 1;
+ static const QString linkTagStartPlaceholder("__QTC_LINK_TAG_START__");
+ static const QString linkTagEndPlaceholder("__QTC_LINK_TAG_END__");
+ static const QString linkEndPlaceholder("__QTC_LINK_END__");
+ if (tags & WithLinks) {
+ for (auto formatRange = formats.crbegin(); formatRange != formats.crend(); ++formatRange) {
+ if (!formatRange->format.isAnchor())
+ continue;
+ text.insert(formatRange->start - offset + formatRange->length, linkEndPlaceholder);
+ text.insert(formatRange->start - offset, QString::fromLatin1("%1%2%3").arg(
+ linkTagStartPlaceholder, formatRange->format.anchorHref(), linkTagEndPlaceholder));
+ }
+ }
+ text = text.toHtmlEscaped();
+ if (tags & WithLinks) {
+ text.replace(linkEndPlaceholder, "</a>");
+ text.replace(linkTagStartPlaceholder, "<a href=\"");
+ text.replace(linkTagEndPlaceholder, "\">");
+ }
+ const QString htmlExtraHeading = extraHeading.isEmpty()
+ ? QString()
+ : QString::fromUtf8("<b>%1</b><br/>").arg(extraHeading);
+ return QString::fromUtf8("<html><body>%1<code style=\"white-space:pre;font-family:%2\">"
+ "%3</code></body></html>")
+ .arg(htmlExtraHeading, TextEditor::FontSettings::defaultFixedFontFamily(), text);
+}
+
//
// functions
//
diff --git a/src/plugins/projectexplorer/task.h b/src/plugins/projectexplorer/task.h
index f38302f90f..830cea7939 100644
--- a/src/plugins/projectexplorer/task.h
+++ b/src/plugins/projectexplorer/task.h
@@ -38,6 +38,9 @@ public:
};
using Options = char;
+ enum DescriptionTag { WithSummary = 1, WithLinks = 2 };
+ using DescriptionTags = QFlags<DescriptionTag>;
+
Task() = default;
Task(TaskType type, const QString &description,
const Utils::FilePath &file, int line, Utils::Id category,
@@ -49,8 +52,9 @@ public:
bool isNull() const;
void clear();
void setFile(const Utils::FilePath &file);
- QString description() const;
+ QString description(DescriptionTags tags = WithSummary) const;
QIcon icon() const;
+ QString formattedDescription(DescriptionTags tags, const QString &extraHeading = {}) const;
friend PROJECTEXPLORER_EXPORT bool operator==(const Task &t1, const Task &t2);
friend PROJECTEXPLORER_EXPORT bool operator<(const Task &a, const Task &b);
diff --git a/src/plugins/projectexplorer/taskfile.cpp b/src/plugins/projectexplorer/taskfile.cpp
index 6375d12ee7..f137b0f812 100644
--- a/src/plugins/projectexplorer/taskfile.cpp
+++ b/src/plugins/projectexplorer/taskfile.cpp
@@ -14,8 +14,8 @@
#include <utils/algorithm.h>
#include <utils/filepath.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QAction>
#include <QMessageBox>
diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp
index da839e89f1..58e797de80 100644
--- a/src/plugins/projectexplorer/taskhub.cpp
+++ b/src/plugins/projectexplorer/taskhub.cpp
@@ -51,13 +51,9 @@ public:
: Tr::tr("Warning"));
setPriority(task.type == Task::Error ? TextEditor::TextMark::NormalPriority
: TextEditor::TextMark::LowPriority);
- if (task.category == Constants::TASK_CATEGORY_COMPILE) {
- setToolTip("<html><body><b>" + Tr::tr("Build Issue")
- + "</b><br/><code style=\"white-space:pre;font-family:monospace\">"
- + task.description().toHtmlEscaped() + "</code></body></html>");
- } else {
- setToolTip(task.description());
- }
+ setToolTip(task.formattedDescription({Task::WithSummary | Task::WithLinks},
+ task.category == Constants::TASK_CATEGORY_COMPILE
+ ? Tr::tr("Build Issue") : QString()));
setIcon(task.icon());
setVisible(!task.icon().isNull());
}
diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp
index b8f7d72306..c05614a445 100644
--- a/src/plugins/projectexplorer/taskmodel.cpp
+++ b/src/plugins/projectexplorer/taskmodel.cpp
@@ -210,58 +210,80 @@ void TaskModel::clearTasks(Utils::Id categoryId)
QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid())
- return QModelIndex();
+ return createIndex(row, column, quintptr(parent.row() + 1));
return createIndex(row, column);
}
QModelIndex TaskModel::parent(const QModelIndex &child) const
{
- Q_UNUSED(child)
- return QModelIndex();
+ if (child.internalId())
+ return index(child.internalId() - 1, 0);
+ return {};
}
int TaskModel::rowCount(const QModelIndex &parent) const
{
- return parent.isValid() ? 0 : m_tasks.count();
+ if (!parent.isValid())
+ return m_tasks.count();
+ if (parent.column() != 0)
+ return 0;
+ return task(parent).details.isEmpty() ? 0 : 1;
}
int TaskModel::columnCount(const QModelIndex &parent) const
{
- return parent.isValid() ? 0 : 1;
+ return parent.isValid() ? 1 : 2;
}
QVariant TaskModel::data(const QModelIndex &index, int role) const
{
- int row = index.row();
- if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.column() != 0)
- return QVariant();
-
- if (role == TaskModel::File)
- return m_tasks.at(index.row()).file.toString();
- else if (role == TaskModel::Line)
- return m_tasks.at(index.row()).line;
- else if (role == TaskModel::MovedLine)
- return m_tasks.at(index.row()).movedLine;
- else if (role == TaskModel::Description)
- return m_tasks.at(index.row()).description();
- else if (role == TaskModel::FileNotFound)
- return m_fileNotFound.value(m_tasks.at(index.row()).file.toString());
- else if (role == TaskModel::Type)
- return (int)m_tasks.at(index.row()).type;
- else if (role == TaskModel::Category)
- return m_tasks.at(index.row()).category.uniqueIdentifier();
- else if (role == TaskModel::Icon)
- return m_tasks.at(index.row()).icon();
- else if (role == TaskModel::Task_t)
- return QVariant::fromValue(task(index));
- return QVariant();
+ if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())
+ || index.column() >= columnCount(index.parent())) {
+ return {};
+ }
+
+ if (index.internalId()) {
+ const Task &task = m_tasks.at(index.internalId() - 1);
+ if (role != Qt::DisplayRole)
+ return {};
+ return task.formattedDescription(Task::WithLinks);
+ }
+
+ static const auto lineString = [](const Task &task) {
+ QString file = task.file.fileName();
+ const int line = task.movedLine > 0 ? task.movedLine : task.line;
+ if (line > 0)
+ file.append(':').append(QString::number(line));
+ return file;
+ };
+
+ const Task &task = m_tasks.at(index.row());
+ if (index.column() == 1) {
+ if (role == Qt::DisplayRole)
+ return lineString(task);
+ if (role == Qt::ToolTipRole)
+ return task.file.toUserOutput();
+ return {};
+ }
+
+ switch (role) {
+ case Qt::DecorationRole:
+ return task.icon();
+ case Qt::DisplayRole:
+ return task.summary;
+ case TaskModel::Description:
+ return task.description();
+ }
+ return {};
}
Task TaskModel::task(const QModelIndex &index) const
{
int row = index.row();
- if (!index.isValid() || row < 0 || row >= m_tasks.count())
+ if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.internalId()
+ || index.column() > 0) {
return Task();
+ }
return m_tasks.at(row);
}
diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h
index e10833f613..e60d8a30a5 100644
--- a/src/plugins/projectexplorer/taskmodel.h
+++ b/src/plugins/projectexplorer/taskmodel.h
@@ -44,7 +44,7 @@ public:
int sizeOfLineNumber(const QFont &font);
void setFileNotFound(const QModelIndex &index, bool b);
- enum Roles { File = Qt::UserRole, Line, MovedLine, Description, FileNotFound, Type, Category, Icon, Task_t };
+ enum Roles { Description = Qt::UserRole, };
int taskCount(Utils::Id categoryId);
int errorTaskCount(Utils::Id categoryId);
diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp
index 52e3899d65..e1e346887f 100644
--- a/src/plugins/projectexplorer/taskwindow.cpp
+++ b/src/plugins/projectexplorer/taskwindow.cpp
@@ -22,27 +22,31 @@
#include <utils/algorithm.h>
#include <utils/fileinprojectfinder.h>
+#include <utils/hostosinfo.h>
#include <utils/itemviews.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
+#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
+#include <QAbstractTextDocumentLayout>
+#include <QApplication>
#include <QDir>
+#include <QLabel>
+#include <QMenu>
#include <QPainter>
+#include <QScrollBar>
#include <QStyledItemDelegate>
-#include <QMenu>
+#include <QTextDocument>
#include <QToolButton>
-#include <QScrollBar>
+#include <QVBoxLayout>
using namespace Utils;
-namespace {
-const int ELLIPSIS_GRADIENT_WIDTH = 16;
const char SESSION_FILTER_CATEGORIES[] = "TaskWindow.Categories";
const char SESSION_FILTER_WARNINGS[] = "TaskWindow.IncludeWarnings";
-}
namespace ProjectExplorer {
@@ -84,195 +88,42 @@ bool ITaskHandler::canHandle(const Tasks &tasks) const
namespace Internal {
-class TaskView : public ListView
+class TaskView : public TreeView
{
public:
- TaskView(QWidget *parent = nullptr);
- ~TaskView() override;
+ TaskView() { setMouseTracking(true); }
+ void resizeColumns();
private:
void resizeEvent(QResizeEvent *e) override;
+ void keyReleaseEvent(QKeyEvent *e) override;
+ bool event(QEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
- void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
- Link locationForPos(const QPoint &pos);
+ QString anchorAt(const QPoint &pos);
+ void showToolTip(const Task &task, const QPoint &pos);
- bool m_linksActive = true;
- Qt::MouseButton m_mouseButtonPressed = Qt::NoButton;
+ QString m_hoverAnchor;
+ QString m_clickAnchor;
};
class TaskDelegate : public QStyledItemDelegate
{
- Q_OBJECT
-
- friend class TaskView; // for using Positions::minimumSize()
-
public:
- TaskDelegate(QObject * parent = nullptr);
- ~TaskDelegate() override;
- void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-
- // TaskView uses this method if the size of the taskview changes
- void emitSizeHintChanged(const QModelIndex &index);
-
- void currentChanged(const QModelIndex &current, const QModelIndex &previous);
-
- QString hrefForPos(const QPointF &pos);
+ using QStyledItemDelegate::QStyledItemDelegate;
+ QTextDocument &doc() { return m_doc; }
private:
- void generateGradientPixmap(int width, int height, QColor color, bool selected) const;
-
- mutable int m_cachedHeight = 0;
- mutable QFont m_cachedFont;
- mutable QList<QPair<QRectF, QString>> m_hrefs;
-
- /*
- Collapsed:
- +----------------------------------------------------------------------------------------------------+
- | TASKICONAREA TEXTAREA FILEAREA LINEAREA |
- +----------------------------------------------------------------------------------------------------+
-
- Expanded:
- +----------------------------------------------------------------------------------------------------+
- | TASKICONICON TEXTAREA FILEAREA LINEAREA |
- | more text -------------------------------------------------------------------------> |
- +----------------------------------------------------------------------------------------------------+
- */
- class Positions
- {
- public:
- Positions(const QStyleOptionViewItem &options, TaskModel *model) :
- m_totalWidth(options.rect.width()),
- m_maxFileLength(model->sizeOfFile(options.font)),
- m_maxLineLength(model->sizeOfLineNumber(options.font)),
- m_realFileLength(m_maxFileLength),
- m_top(options.rect.top()),
- m_bottom(options.rect.bottom())
- {
- int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
- if (m_maxFileLength > flexibleArea / 2)
- m_realFileLength = flexibleArea / 2;
- m_fontHeight = QFontMetrics(options.font).height();
- }
-
- int top() const { return m_top + ITEM_MARGIN; }
- int left() const { return ITEM_MARGIN; }
- int right() const { return m_totalWidth - ITEM_MARGIN; }
- int bottom() const { return m_bottom; }
- int firstLineHeight() const { return m_fontHeight + 1; }
- static int minimumHeight() { return taskIconHeight() + 2 * ITEM_MARGIN; }
-
- int taskIconLeft() const { return left(); }
- static int taskIconWidth() { return TASK_ICON_SIZE; }
- static int taskIconHeight() { return TASK_ICON_SIZE; }
- int taskIconRight() const { return taskIconLeft() + taskIconWidth(); }
- QRect taskIcon() const { return QRect(taskIconLeft(), top(), taskIconWidth(), taskIconHeight()); }
-
- int textAreaLeft() const { return taskIconRight() + ITEM_SPACING; }
- int textAreaWidth() const { return textAreaRight() - textAreaLeft(); }
- int textAreaRight() const { return fileAreaLeft() - ITEM_SPACING; }
- QRect textArea() const { return QRect(textAreaLeft(), top(), textAreaWidth(), firstLineHeight()); }
-
- int fileAreaLeft() const { return fileAreaRight() - fileAreaWidth(); }
- int fileAreaWidth() const { return m_realFileLength; }
- int fileAreaRight() const { return lineAreaLeft() - ITEM_SPACING; }
- QRect fileArea() const { return QRect(fileAreaLeft(), top(), fileAreaWidth(), firstLineHeight()); }
-
- int lineAreaLeft() const { return lineAreaRight() - lineAreaWidth(); }
- int lineAreaWidth() const { return m_maxLineLength; }
- int lineAreaRight() const { return right(); }
- QRect lineArea() const { return QRect(lineAreaLeft(), top(), lineAreaWidth(), firstLineHeight()); }
-
- private:
- int m_totalWidth;
- int m_maxFileLength;
- int m_maxLineLength;
- int m_realFileLength;
- int m_top;
- int m_bottom;
- int m_fontHeight;
-
- static const int TASK_ICON_SIZE = 16;
- static const int ITEM_MARGIN = 2;
- static const int ITEM_SPACING = 2 * ITEM_MARGIN;
- };
-};
-
-TaskView::TaskView(QWidget *parent)
- : ListView(parent)
-{
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- setMouseTracking(true);
- setAutoScroll(false); // QTCREATORBUG-25101
-
- QFontMetrics fm(font());
- int vStepSize = fm.height() + 3;
- if (vStepSize < TaskDelegate::Positions::minimumHeight())
- vStepSize = TaskDelegate::Positions::minimumHeight();
-
- verticalScrollBar()->setSingleStep(vStepSize);
-}
-
-TaskView::~TaskView() = default;
-
-void TaskView::resizeEvent(QResizeEvent *e)
-{
- Q_UNUSED(e)
- static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex());
-}
-
-void TaskView::mousePressEvent(QMouseEvent *e)
-{
- m_mouseButtonPressed = e->button();
- ListView::mousePressEvent(e);
-}
-
-void TaskView::mouseReleaseEvent(QMouseEvent *e)
-{
- if (m_linksActive && m_mouseButtonPressed == Qt::LeftButton) {
- const Link loc = locationForPos(e->pos());
- if (!loc.targetFilePath.isEmpty()) {
- Core::EditorManager::openEditorAt(loc, {},
- Core::EditorManager::SwitchSplitIfAlreadyVisible);
- }
- }
-
- // Mouse was released, activate links again
- m_linksActive = true;
- m_mouseButtonPressed = Qt::NoButton;
- ListView::mouseReleaseEvent(e);
-}
-
-void TaskView::mouseMoveEvent(QMouseEvent *e)
-{
- // Cursor was dragged, deactivate links
- if (m_mouseButtonPressed != Qt::NoButton)
- m_linksActive = false;
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- viewport()->setCursor(m_linksActive && !locationForPos(e->pos()).targetFilePath.isEmpty()
- ? Qt::PointingHandCursor : Qt::ArrowCursor);
- ListView::mouseMoveEvent(e);
-}
+ bool needsSpecialHandling(const QModelIndex &index) const;
-Link TaskView::locationForPos(const QPoint &pos)
-{
- const auto delegate = qobject_cast<TaskDelegate *>(itemDelegate(indexAt(pos)));
- if (!delegate)
- return {};
- OutputFormatter formatter;
- Link loc;
- connect(&formatter, &OutputFormatter::openInEditorRequested, this, [&loc](const Link &link) {
- loc = link;
- });
-
- const QString href = delegate->hrefForPos(pos);
- if (!href.isEmpty())
- formatter.handleLink(href);
- return loc;
-}
+ mutable QTextDocument m_doc;
+};
/////
// TaskWindow
@@ -289,7 +140,7 @@ public:
Internal::TaskModel *m_model;
Internal::TaskFilterModel *m_filter;
- Internal::TaskView *m_listview;
+ TaskView m_treeView;
Core::IContext *m_taskWindowContext;
QMenu *m_contextMenu;
QMap<const QAction *, ITaskHandler *> m_actionToHandlerMap;
@@ -318,45 +169,46 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>())
{
d->m_model = new Internal::TaskModel(this);
d->m_filter = new Internal::TaskFilterModel(d->m_model);
- d->m_listview = new Internal::TaskView;
auto agg = new Aggregation::Aggregate;
- agg->add(d->m_listview);
- agg->add(new Core::ItemViewFind(d->m_listview, TaskModel::Description));
-
- d->m_listview->setModel(d->m_filter);
- d->m_listview->setFrameStyle(QFrame::NoFrame);
- d->m_listview->setWindowTitle(displayName());
- d->m_listview->setSelectionMode(QAbstractItemView::ExtendedSelection);
- auto *tld = new Internal::TaskDelegate(this);
- d->m_listview->setItemDelegate(tld);
- d->m_listview->setWindowIcon(Icons::WINDOW.icon());
- d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu);
- d->m_listview->setAttribute(Qt::WA_MacShowFocusRect, false);
-
- d->m_taskWindowContext = new Core::IContext(d->m_listview);
- d->m_taskWindowContext->setWidget(d->m_listview);
+ agg->add(&d->m_treeView);
+ agg->add(new Core::ItemViewFind(&d->m_treeView, TaskModel::Description));
+
+ d->m_treeView.setHeaderHidden(true);
+ d->m_treeView.setExpandsOnDoubleClick(false);
+ d->m_treeView.setAlternatingRowColors(true);
+ d->m_treeView.setTextElideMode(Qt::ElideMiddle);
+ d->m_treeView.setItemDelegate(new TaskDelegate(this));
+ d->m_treeView.setModel(d->m_filter);
+ d->m_treeView.setFrameStyle(QFrame::NoFrame);
+ d->m_treeView.setWindowTitle(displayName());
+ d->m_treeView.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ d->m_treeView.setWindowIcon(Icons::WINDOW.icon());
+ d->m_treeView.setContextMenuPolicy(Qt::ActionsContextMenu);
+ d->m_treeView.setAttribute(Qt::WA_MacShowFocusRect, false);
+ d->m_treeView.resizeColumns();
+
+ d->m_taskWindowContext = new Core::IContext(&d->m_treeView);
+ d->m_taskWindowContext->setWidget(&d->m_treeView);
d->m_taskWindowContext->setContext(Core::Context(Core::Constants::C_PROBLEM_PANE));
Core::ICore::addContextObject(d->m_taskWindowContext);
- connect(d->m_listview->selectionModel(), &QItemSelectionModel::currentChanged,
- tld, &TaskDelegate::currentChanged);
- connect(d->m_listview->selectionModel(), &QItemSelectionModel::currentChanged,
- this, [this](const QModelIndex &index) { d->m_listview->scrollTo(index); });
- connect(d->m_listview, &QAbstractItemView::activated,
+ connect(d->m_treeView.selectionModel(), &QItemSelectionModel::currentChanged,
+ this, [this](const QModelIndex &index) { d->m_treeView.scrollTo(index); });
+ connect(&d->m_treeView, &QAbstractItemView::activated,
this, &TaskWindow::triggerDefaultHandler);
- connect(d->m_listview->selectionModel(), &QItemSelectionModel::selectionChanged,
+ connect(d->m_treeView.selectionModel(), &QItemSelectionModel::selectionChanged,
this, [this] {
- const Tasks tasks = d->m_filter->tasks(d->m_listview->selectionModel()->selectedIndexes());
+ const Tasks tasks = d->m_filter->tasks(d->m_treeView.selectionModel()->selectedIndexes());
for (QAction * const action : std::as_const(d->m_actions)) {
ITaskHandler * const h = d->handler(action);
action->setEnabled(h && h->canHandle(tasks));
}
});
- d->m_contextMenu = new QMenu(d->m_listview);
+ d->m_contextMenu = new QMenu(&d->m_treeView);
- d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu);
+ d->m_treeView.setContextMenuPolicy(Qt::ActionsContextMenu);
d->m_filterWarningsButton = createFilterButton(
Utils::Icons::WARNING_TOOLBAR.icon(),
@@ -365,7 +217,7 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>())
d->m_categoriesButton = new QToolButton;
d->m_categoriesButton->setIcon(Utils::Icons::FILTER.icon());
d->m_categoriesButton->setToolTip(Tr::tr("Filter by categories"));
- d->m_categoriesButton->setProperty("noArrow", true);
+ d->m_categoriesButton->setProperty(StyleHelper::C_NO_ARROW, true);
d->m_categoriesButton->setPopupMode(QToolButton::InstantPopup);
d->m_categoriesMenu = new QMenu(d->m_categoriesButton);
@@ -411,7 +263,6 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>())
TaskWindow::~TaskWindow()
{
delete d->m_filterWarningsButton;
- delete d->m_listview;
delete d->m_filter;
delete d->m_model;
}
@@ -435,7 +286,7 @@ void TaskWindow::delayedInitialization()
connect(action, &QAction::triggered, this, [this, action] {
ITaskHandler *h = d->handler(action);
if (h)
- h->handle(d->m_filter->tasks(d->m_listview->selectionModel()->selectedIndexes()));
+ h->handle(d->m_filter->tasks(d->m_treeView.selectionModel()->selectedIndexes()));
});
d->m_actions << action;
@@ -445,7 +296,7 @@ void TaskWindow::delayedInitialization()
Core::ActionManager::registerAction(action, id, d->m_taskWindowContext->context(), true);
action = cmd->action();
}
- d->m_listview->addAction(action);
+ d->m_treeView.addAction(action);
}
}
@@ -461,7 +312,7 @@ QString TaskWindow::displayName() const
QWidget *TaskWindow::outputWidget(QWidget *)
{
- return d->m_listview;
+ return &d->m_treeView;
}
void TaskWindow::clearTasks(Id categoryId)
@@ -565,7 +416,7 @@ void TaskWindow::showTask(const Task &task)
int sourceRow = d->m_model->rowForTask(task);
QModelIndex sourceIdx = d->m_model->index(sourceRow, 0);
QModelIndex filterIdx = d->m_filter->mapFromSource(sourceIdx);
- d->m_listview->setCurrentIndex(filterIdx);
+ d->m_treeView.setCurrentIndex(filterIdx);
popup(Core::IOutputPane::ModeSwitch);
}
@@ -582,7 +433,10 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
if (!index.isValid() || !d->m_defaultHandler)
return;
- Task task(d->m_filter->task(index));
+ QModelIndex taskIndex = index;
+ if (index.parent().isValid())
+ taskIndex = index.parent();
+ Task task(d->m_filter->task(taskIndex));
if (task.isNull())
return;
@@ -599,7 +453,7 @@ void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
d->m_defaultHandler->handle(task);
} else {
if (!task.file.exists())
- d->m_model->setFileNotFound(index, true);
+ d->m_model->setFileNotFound(taskIndex, true);
}
}
@@ -665,7 +519,7 @@ void TaskWindow::clearContents()
bool TaskWindow::hasFocus() const
{
- return d->m_listview->window()->focusWidget() == d->m_listview;
+ return d->m_treeView.window()->focusWidget() == &d->m_treeView;
}
bool TaskWindow::canFocus() const
@@ -676,9 +530,13 @@ bool TaskWindow::canFocus() const
void TaskWindow::setFocus()
{
if (d->m_filter->rowCount()) {
- d->m_listview->setFocus();
- if (d->m_listview->currentIndex() == QModelIndex())
- d->m_listview->setCurrentIndex(d->m_filter->index(0,0, QModelIndex()));
+ d->m_treeView.setFocus();
+ if (!d->m_treeView.currentIndex().isValid())
+ d->m_treeView.setCurrentIndex(d->m_filter->index(0,0, QModelIndex()));
+ if (d->m_treeView.selectionModel()->selection().isEmpty()) {
+ d->m_treeView.selectionModel()->setCurrentIndex(d->m_treeView.currentIndex(),
+ QItemSelectionModel::Select);
+ }
}
}
@@ -696,7 +554,7 @@ void TaskWindow::goToNext()
{
if (!canNext())
return;
- QModelIndex startIndex = d->m_listview->currentIndex();
+ QModelIndex startIndex = d->m_treeView.currentIndex();
QModelIndex currentIndex = startIndex;
if (startIndex.isValid()) {
@@ -711,7 +569,7 @@ void TaskWindow::goToNext()
} else {
currentIndex = d->m_filter->index(0, 0);
}
- d->m_listview->setCurrentIndex(currentIndex);
+ d->m_treeView.setCurrentIndex(currentIndex);
triggerDefaultHandler(currentIndex);
}
@@ -719,7 +577,7 @@ void TaskWindow::goToPrev()
{
if (!canPrevious())
return;
- QModelIndex startIndex = d->m_listview->currentIndex();
+ QModelIndex startIndex = d->m_treeView.currentIndex();
QModelIndex currentIndex = startIndex;
if (startIndex.isValid()) {
@@ -734,7 +592,7 @@ void TaskWindow::goToPrev()
} else {
currentIndex = d->m_filter->index(0, 0);
}
- d->m_listview->setCurrentIndex(currentIndex);
+ d->m_treeView.setCurrentIndex(currentIndex);
triggerDefaultHandler(currentIndex);
}
@@ -749,268 +607,161 @@ bool TaskWindow::canNavigate() const
return true;
}
-/////
-// Delegate
-/////
-
-TaskDelegate::TaskDelegate(QObject *parent) :
- QStyledItemDelegate(parent)
-{ }
-
-TaskDelegate::~TaskDelegate() = default;
-
-QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
{
- QStyleOptionViewItem opt = option;
- initStyleOption(&opt, index);
-
- auto view = qobject_cast<const QAbstractItemView *>(opt.widget);
- const bool current = view->selectionModel()->currentIndex() == index;
- QSize s;
- s.setWidth(option.rect.width());
-
- if (!current && option.font == m_cachedFont && m_cachedHeight > 0) {
- s.setHeight(m_cachedHeight);
- return s;
+ if (!needsSpecialHandling(index)) {
+ QStyledItemDelegate::paint(painter, option, index);
+ return;
}
- QFontMetrics fm(option.font);
- int fontHeight = fm.height();
- int fontLeading = fm.leading();
-
- auto model = static_cast<TaskFilterModel *>(view->model())->taskModel();
- Positions positions(option, model);
-
- if (current) {
- QString description = index.data(TaskModel::Description).toString();
- // Layout the description
- int leading = fontLeading;
- int height = 0;
- description.replace(QLatin1Char('\n'), QChar::LineSeparator);
- QTextLayout tl(description);
- tl.setFormats(index.data(TaskModel::Task_t).value<Task>().formats);
- tl.beginLayout();
- while (true) {
- QTextLine line = tl.createLine();
- if (!line.isValid())
- break;
- line.setLineWidth(positions.textAreaWidth());
- height += leading;
- line.setPosition(QPoint(0, height));
- height += static_cast<int>(line.height());
- }
- tl.endLayout();
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
- s.setHeight(height + leading + fontHeight + 3);
- } else {
- s.setHeight(fontHeight + 3);
+ painter->save();
+ m_doc.setHtml(options.text);
+ options.text = "";
+ options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);
+ painter->translate(options.rect.left(), options.rect.top());
+ QRect clip(0, 0, options.rect.width(), options.rect.height());
+ QAbstractTextDocumentLayout::PaintContext paintContext;
+ paintContext.palette = options.palette;
+ painter->setClipRect(clip);
+ paintContext.clip = clip;
+ if (qobject_cast<const QAbstractItemView *>(options.widget)
+ ->selectionModel()->isSelected(index)) {
+ QAbstractTextDocumentLayout::Selection selection;
+ selection.cursor = QTextCursor(&m_doc);
+ selection.cursor.select(QTextCursor::Document);
+ selection.format.setBackground(options.palette.brush(QPalette::Highlight));
+ selection.format.setForeground(options.palette.brush(QPalette::HighlightedText));
+ paintContext.selections << selection;
}
- if (s.height() < Positions::minimumHeight())
- s.setHeight(Positions::minimumHeight());
+ m_doc.documentLayout()->draw(painter, paintContext);
+ painter->restore();
+}
- if (!current) {
- m_cachedHeight = s.height();
- m_cachedFont = option.font;
- }
+QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (!needsSpecialHandling(index))
+ return QStyledItemDelegate::sizeHint(option, index);
- return s;
+ QStyleOptionViewItem options = option;
+ initStyleOption(&options, index);
+ m_doc.setHtml(options.text);
+ m_doc.setTextWidth(options.rect.width());
+ return QSize(m_doc.idealWidth(), m_doc.size().height());
}
-void TaskDelegate::emitSizeHintChanged(const QModelIndex &index)
+bool TaskDelegate::needsSpecialHandling(const QModelIndex &index) const
{
- emit sizeHintChanged(index);
+ QModelIndex sourceIndex = index;
+ if (const auto proxyModel = qobject_cast<const QAbstractProxyModel *>(index.model()))
+ sourceIndex = proxyModel->mapToSource(index);
+ return sourceIndex.internalId();
}
-void TaskDelegate::currentChanged(const QModelIndex &current, const QModelIndex &previous)
+void TaskView::resizeColumns()
{
- m_hrefs.clear();
- emit sizeHintChanged(current);
- emit sizeHintChanged(previous);
+ setColumnWidth(0, width() * 0.85);
+ setColumnWidth(1, width() * 0.15);
}
-QString TaskDelegate::hrefForPos(const QPointF &pos)
+void TaskView::resizeEvent(QResizeEvent *e)
{
- for (const auto &link : std::as_const(m_hrefs)) {
- if (link.first.contains(pos))
- return link.second;
- }
- return {};
+ TreeView::resizeEvent(e);
+ resizeColumns();
}
-void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+void TaskView::mousePressEvent(QMouseEvent *e)
{
- QStyleOptionViewItem opt = option;
- initStyleOption(&opt, index);
- painter->save();
-
- QFontMetrics fm(opt.font);
- QColor backgroundColor;
- QColor textColor;
+ m_clickAnchor = anchorAt(e->pos());
+ if (m_clickAnchor.isEmpty())
+ TreeView::mousePressEvent(e);
+}
- auto view = qobject_cast<const QAbstractItemView *>(opt.widget);
- const bool selected = view->selectionModel()->isSelected(index);
- const bool current = view->selectionModel()->currentIndex() == index;
+void TaskView::mouseMoveEvent(QMouseEvent *e)
+{
+ const QString anchor = anchorAt(e->pos());
+ if (m_clickAnchor != anchor)
+ m_clickAnchor.clear();
+ if (m_hoverAnchor != anchor) {
+ m_hoverAnchor = anchor;
+ if (!m_hoverAnchor.isEmpty())
+ setCursor(Qt::PointingHandCursor);
+ else
+ unsetCursor();
+ }
+}
- if (selected) {
- painter->setBrush(opt.palette.highlight().color());
- backgroundColor = opt.palette.highlight().color();
- } else {
- painter->setBrush(opt.palette.window().color());
- backgroundColor = opt.palette.window().color();
+void TaskView::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (m_clickAnchor.isEmpty()) {
+ TreeView::mouseReleaseEvent(e);
+ return;
}
- painter->setPen(Qt::NoPen);
- painter->drawRect(opt.rect);
- // Set Text Color
- if (selected)
- textColor = opt.palette.highlightedText().color();
- else
- textColor = opt.palette.text().color();
-
- painter->setPen(textColor);
-
- auto model = static_cast<TaskFilterModel *>(view->model())->taskModel();
- Positions positions(opt, model);
-
- // Paint TaskIconArea:
- QIcon icon = index.data(TaskModel::Icon).value<QIcon>();
- painter->drawPixmap(positions.left(), positions.top(),
- icon.pixmap(Positions::taskIconWidth(), Positions::taskIconHeight()));
-
- // Paint TextArea:
- if (!current) {
- // in small mode we lay out differently
- QString bottom = index.data(TaskModel::Description).toString().split(QLatin1Char('\n')).first();
- painter->setClipRect(positions.textArea());
- painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), bottom);
- if (fm.horizontalAdvance(bottom) > positions.textAreaWidth()) {
- // draw a gradient to mask the text
- int gradientStart = positions.textAreaRight() - ELLIPSIS_GRADIENT_WIDTH + 1;
- QLinearGradient lg(gradientStart, 0, gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0);
- lg.setColorAt(0, Qt::transparent);
- lg.setColorAt(1, backgroundColor);
- painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
- }
- } else {
- // Description
- QString description = index.data(TaskModel::Description).toString();
- // Layout the description
- int leading = fm.leading();
- int height = 0;
- description.replace(QLatin1Char('\n'), QChar::LineSeparator);
- QTextLayout tl(description);
- QVector<QTextLayout::FormatRange> formats = index.data(TaskModel::Task_t).value<Task>().formats;
- for (QTextLayout::FormatRange &format : formats)
- format.format.setForeground(textColor);
- tl.setFormats(formats);
- tl.beginLayout();
- while (true) {
- QTextLine line = tl.createLine();
- if (!line.isValid())
- break;
- line.setLineWidth(positions.textAreaWidth());
- height += leading;
- line.setPosition(QPoint(0, height));
- height += static_cast<int>(line.height());
- }
- tl.endLayout();
- const QPoint indexPos = view->visualRect(index).topLeft();
- tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top()));
- m_hrefs.clear();
- for (const auto &range : tl.formats()) {
- if (!range.format.isAnchor())
- continue;
- const QTextLine &firstLinkLine = tl.lineForTextPosition(range.start);
- const QTextLine &lastLinkLine = tl.lineForTextPosition(range.start + range.length - 1);
- for (int i = firstLinkLine.lineNumber(); i <= lastLinkLine.lineNumber(); ++i) {
- const QTextLine &linkLine = tl.lineAt(i);
- if (!linkLine.isValid())
- break;
- const QPointF linePos = linkLine.position();
- const int linkStartPos = i == firstLinkLine.lineNumber()
- ? range.start : linkLine.textStart();
- const qreal startOffset = linkLine.cursorToX(linkStartPos);
- const int linkEndPos = i == lastLinkLine.lineNumber()
- ? range.start + range.length
- : linkLine.textStart() + linkLine.textLength();
- const qreal endOffset = linkLine.cursorToX(linkEndPos);
- const QPointF linkPos(indexPos.x() + positions.textAreaLeft() + linePos.x()
- + startOffset, positions.top() + linePos.y());
- const QSize linkSize(endOffset - startOffset, linkLine.height());
- const QRectF linkRect(linkPos, linkSize);
- m_hrefs.push_back({linkRect, range.format.anchorHref()});
- }
- }
+ const QString anchor = anchorAt(e->pos());
+ if (anchor == m_clickAnchor) {
+ Core::EditorManager::openEditorAt(OutputLineParser::parseLinkTarget(m_clickAnchor), {},
+ Core::EditorManager::SwitchSplitIfAlreadyVisible);
+ }
+ m_clickAnchor.clear();
+}
- const QColor mix = StyleHelper::mergedColors(textColor, backgroundColor, 70);
- const QString directory = QDir::toNativeSeparators(index.data(TaskModel::File).toString());
- int secondBaseLine = positions.top() + fm.ascent() + height + leading;
- if (index.data(TaskModel::FileNotFound).toBool() && !directory.isEmpty()) {
- const QString fileNotFound = Tr::tr("File not found: %1").arg(directory);
- const QColor errorColor = selected ? mix : creatorTheme()->color(Theme::TextColorError);
- painter->setPen(errorColor);
- painter->drawText(positions.textAreaLeft(), secondBaseLine, fileNotFound);
- } else {
- painter->setPen(mix);
- painter->drawText(positions.textAreaLeft(), secondBaseLine, directory);
+void TaskView::keyReleaseEvent(QKeyEvent *e)
+{
+ TreeView::keyReleaseEvent(e);
+ if (e->key() == Qt::Key_Space) {
+ const Task task = static_cast<TaskFilterModel *>(model())->task(currentIndex());
+ if (!task.isNull()) {
+ const QPoint toolTipPos = mapToGlobal(visualRect(currentIndex()).topLeft());
+ QMetaObject::invokeMethod(this, [this, task, toolTipPos] {
+ showToolTip(task, toolTipPos); }, Qt::QueuedConnection);
}
}
- painter->setPen(textColor);
-
- // Paint FileArea
- QString file = index.data(TaskModel::File).toString();
- const int pos = file.lastIndexOf(QLatin1Char('/'));
- if (pos != -1)
- file = file.mid(pos +1);
- const int realFileWidth = fm.horizontalAdvance(file);
- painter->setClipRect(positions.fileArea());
- painter->drawText(qMin(positions.fileAreaLeft(), positions.fileAreaRight() - realFileWidth),
- positions.top() + fm.ascent(), file);
- if (realFileWidth > positions.fileAreaWidth()) {
- // draw a gradient to mask the text
- int gradientStart = positions.fileAreaLeft() - 1;
- QLinearGradient lg(gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0, gradientStart, 0);
- lg.setColorAt(0, Qt::transparent);
- lg.setColorAt(1, backgroundColor);
- painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
- }
+}
- // Paint LineArea
- int line = index.data(TaskModel::Line).toInt();
- int movedLine = index.data(TaskModel::MovedLine).toInt();
- QString lineText;
-
- if (line == -1) {
- // No line information at all
- } else if (movedLine == -1) {
- // removed the line, but we had line information, show the line in ()
- QFont f = painter->font();
- f.setItalic(true);
- painter->setFont(f);
- lineText = QLatin1Char('(') + QString::number(line) + QLatin1Char(')');
- } else if (movedLine != line) {
- // The line was moved
- QFont f = painter->font();
- f.setItalic(true);
- painter->setFont(f);
- lineText = QString::number(movedLine);
- } else {
- lineText = QString::number(line);
+bool TaskView::event(QEvent *e)
+{
+ if (e->type() != QEvent::ToolTip)
+ return TreeView::event(e);
+
+ const auto helpEvent = static_cast<QHelpEvent*>(e);
+ const Task task = static_cast<TaskFilterModel *>(model())->task(indexAt(helpEvent->pos()));
+ if (task.isNull())
+ return TreeView::event(e);
+ showToolTip(task, helpEvent->globalPos());
+ e->accept();
+ return true;
+}
+
+void TaskView::showToolTip(const Task &task, const QPoint &pos)
+{
+ if (task.details.isEmpty()) {
+ ToolTip::hideImmediately();
+ return;
}
- painter->setClipRect(positions.lineArea());
- const int realLineWidth = fm.horizontalAdvance(lineText);
- painter->drawText(positions.lineAreaRight() - realLineWidth, positions.top() + fm.ascent(), lineText);
- painter->setClipRect(opt.rect);
+ const auto layout = new QVBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(new QLabel(task.formattedDescription({})));
+ ToolTip::show(pos, layout);
+}
- // Separator lines
- painter->setPen(QColor::fromRgb(150,150,150));
- const QRectF borderRect = QRectF(opt.rect).adjusted(0.5, 0.5, -0.5, -0.5);
- painter->drawLine(borderRect.bottomLeft(), borderRect.bottomRight());
- painter->restore();
+QString TaskView::anchorAt(const QPoint &pos)
+{
+ const QModelIndex index = indexAt(pos);
+ if (!index.isValid() || !index.internalId())
+ return {};
+
+ const QRect itemRect = visualRect(index);
+ QTextDocument &doc = static_cast<TaskDelegate *>(itemDelegate())->doc();
+ doc.setHtml(model()->data(index, Qt::DisplayRole).toString());
+ const QAbstractTextDocumentLayout * const textLayout = doc.documentLayout();
+ QTC_ASSERT(textLayout, return {});
+ return textLayout->anchorAt(pos - itemRect.topLeft());
}
} // namespace Internal
} // namespace ProjectExplorer
-
-#include "taskwindow.moc"
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt b/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt
new file mode 100644
index 0000000000..5313539cd7
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(multi-target-project)
+add_executable(multi-target-project-app multi-target-project-main.cpp multi-target-project-shared.h)
+add_library(multi-target-project-lib STATIC multi-target-project-lib.cpp multi-target-project-shared.h)
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro
new file mode 100644
index 0000000000..96870c05c1
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-app.pro
@@ -0,0 +1,4 @@
+TARGET = app
+CONFIG -= qt
+SOURCES = multi-target-project-main.cpp
+HEADERS = multi-target-project-shared.h
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp
new file mode 100644
index 0000000000..9b7a34861c
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.cpp
@@ -0,0 +1,6 @@
+#include "multi-target-project-shared.h"
+
+int increaseNumber()
+{
+ return getNumber() + 1;
+}
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro
new file mode 100644
index 0000000000..4257154051
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-lib.pro
@@ -0,0 +1,4 @@
+TEMPLATE = lib
+CONFIG += static
+SOURCES = multi-target-project-lib.cpp
+HEADERS = multi-target-project-shared.h
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp
new file mode 100644
index 0000000000..306400b350
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-main.cpp
@@ -0,0 +1,6 @@
+#include "multi-target-project-shared.h"
+
+int main()
+{
+ return getNumber();
+}
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h
new file mode 100644
index 0000000000..9f839e82d0
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project-shared.h
@@ -0,0 +1,3 @@
+#pragma once
+
+inline int getNumber() { return 5; }
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro
new file mode 100644
index 0000000000..5e6289d7b2
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+app.file = multi-target-project-app.pro
+lib.file = multi-target-project-lib.pro
+SUBDIRS = app lib
diff --git a/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs
new file mode 100644
index 0000000000..079ac82c95
--- /dev/null
+++ b/src/plugins/projectexplorer/testdata/multi-target-project/multi-target-project.qbs
@@ -0,0 +1,10 @@
+Project {
+ CppApplication {
+ name: "app"
+ files: ["multi-target-project-main.cpp", "multi-target-project-shared.h"]
+ }
+ StaticLibrary {
+ Depends { name: "cpp" }
+ files: ["multi-target-project-lib.cpp", "multi-target-project-shared.h"]
+ }
+}
diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp
index 1a3d3d43f5..8b8cfce2c8 100644
--- a/src/plugins/projectexplorer/toolchain.cpp
+++ b/src/plugins/projectexplorer/toolchain.cpp
@@ -192,7 +192,7 @@ bool ToolChain::isValid() const
return d->m_isValid.value_or(false);
}
-QStringList ToolChain::includedFiles(const QStringList &flags, const QString &directory) const
+FilePaths ToolChain::includedFiles(const QStringList &flags, const FilePath &directory) const
{
Q_UNUSED(flags)
Q_UNUSED(directory)
@@ -466,12 +466,12 @@ Utils::LanguageVersion ToolChain::languageVersion(const Utils::Id &language, con
}
}
-QStringList ToolChain::includedFiles(const QString &option,
- const QStringList &flags,
- const QString &directoryPath,
- PossiblyConcatenatedFlag possiblyConcatenated)
+FilePaths ToolChain::includedFiles(const QString &option,
+ const QStringList &flags,
+ const FilePath &directoryPath,
+ PossiblyConcatenatedFlag possiblyConcatenated)
{
- QStringList result;
+ FilePaths result;
for (int i = 0; i < flags.size(); ++i) {
QString includeFile;
@@ -484,11 +484,8 @@ QStringList ToolChain::includedFiles(const QString &option,
if (includeFile.isEmpty() && flag == option && i + 1 < flags.size())
includeFile = flags[++i];
- if (!includeFile.isEmpty()) {
- if (!QFileInfo(includeFile).isAbsolute())
- includeFile = directoryPath + "/" + includeFile;
- result.append(QDir::cleanPath(includeFile));
- }
+ if (!includeFile.isEmpty())
+ result.append(directoryPath.resolvePath(includeFile));
}
return result;
@@ -677,7 +674,9 @@ ToolchainDetector::ToolchainDetector(const Toolchains &alreadyKnown,
const IDevice::ConstPtr &device,
const FilePaths &searchPaths)
: alreadyKnown(alreadyKnown), device(device), searchPaths(searchPaths)
-{}
+{
+ QTC_CHECK(device);
+}
BadToolchain::BadToolchain(const Utils::FilePath &filePath)
: BadToolchain(filePath, filePath.symLinkTarget(), filePath.lastModified())
diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h
index 15bee2fdaa..35cad5333e 100644
--- a/src/plugins/projectexplorer/toolchain.h
+++ b/src/plugins/projectexplorer/toolchain.h
@@ -102,7 +102,7 @@ public:
virtual Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const = 0;
virtual Utils::WarningFlags warningFlags(const QStringList &cflags) const = 0;
- virtual QStringList includedFiles(const QStringList &flags, const QString &directory) const;
+ virtual Utils::FilePaths includedFiles(const QStringList &flags, const Utils::FilePath &directory) const;
virtual QString sysRoot() const;
QString explicitCodeModelTargetTriple() const;
@@ -184,10 +184,10 @@ protected:
virtual bool fromMap(const QVariantMap &data);
enum class PossiblyConcatenatedFlag { No, Yes };
- static QStringList includedFiles(const QString &option,
- const QStringList &flags,
- const QString &directoryPath,
- PossiblyConcatenatedFlag possiblyConcatenated);
+ static Utils::FilePaths includedFiles(const QString &option,
+ const QStringList &flags,
+ const Utils::FilePath &directoryPath,
+ PossiblyConcatenatedFlag possiblyConcatenated);
private:
ToolChain(const ToolChain &) = delete;
diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.cpp b/src/plugins/projectexplorer/toolchainconfigwidget.cpp
index ef97ad72cf..84b80b4e04 100644
--- a/src/plugins/projectexplorer/toolchainconfigwidget.cpp
+++ b/src/plugins/projectexplorer/toolchainconfigwidget.cpp
@@ -7,8 +7,8 @@
#include "projectexplorertr.h"
#include <utils/detailswidget.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QFormLayout>
#include <QLineEdit>
diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp
index b8df603850..e0717ab780 100644
--- a/src/plugins/projectexplorer/toolchainoptionspage.cpp
+++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp
@@ -48,20 +48,8 @@ class ToolChainTreeItem : public TreeItem
{
public:
ToolChainTreeItem(QStackedWidget *parentWidget, ToolChain *tc, bool c) :
- toolChain(tc), changed(c)
- {
- widget = tc->createConfigurationWidget().release();
- if (widget) {
- parentWidget->addWidget(widget);
- if (tc->isAutoDetected())
- widget->makeReadOnly();
- QObject::connect(widget, &ToolChainConfigWidget::dirty,
- [this] {
- changed = true;
- update();
- });
- }
- }
+ toolChain(tc), changed(c), m_parentWidget(parentWidget)
+ {}
QVariant data(int column, int role) const override
{
@@ -93,9 +81,30 @@ public:
return QVariant();
}
+ ToolChainConfigWidget *widget()
+ {
+ if (!m_widget) {
+ m_widget = toolChain->createConfigurationWidget().release();
+ if (m_widget) {
+ m_parentWidget->addWidget(m_widget);
+ if (toolChain->isAutoDetected())
+ m_widget->makeReadOnly();
+ QObject::connect(m_widget, &ToolChainConfigWidget::dirty,
+ [this] {
+ changed = true;
+ update();
+ });
+ }
+ }
+ return m_widget;
+ }
+
ToolChain *toolChain;
- ToolChainConfigWidget *widget;
bool changed;
+
+private:
+ ToolChainConfigWidget *m_widget = nullptr;
+ QStackedWidget *m_parentWidget = nullptr;
};
class DetectionSettingsDialog : public QDialog
@@ -423,7 +432,7 @@ void ToolChainOptionsWidget::toolChainSelectionChanged()
{
ToolChainTreeItem *item = currentTreeItem();
- QWidget *currentTcWidget = item ? item->widget : nullptr;
+ QWidget *currentTcWidget = item ? item->widget() : nullptr;
if (currentTcWidget)
m_widgetStack->setCurrentWidget(currentTcWidget);
m_container->setVisible(currentTcWidget);
@@ -447,8 +456,8 @@ void ToolChainOptionsWidget::apply()
for (TreeItem *item : *parent) {
auto tcItem = static_cast<ToolChainTreeItem *>(item);
Q_ASSERT(tcItem->toolChain);
- if (!tcItem->toolChain->isAutoDetected() && tcItem->widget && tcItem->changed)
- tcItem->widget->apply();
+ if (!tcItem->toolChain->isAutoDetected() && tcItem->widget() && tcItem->changed)
+ tcItem->widget()->apply();
tcItem->changed = false;
tcItem->update();
}
diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp
index 4152e9882e..35224e4d02 100644
--- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp
+++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp
@@ -3,6 +3,7 @@
#include "toolchainsettingsaccessor.h"
+#include "devicesupport/devicemanager.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "toolchain.h"
@@ -168,11 +169,10 @@ static ToolChainOperations mergeToolChainLists(const Toolchains &systemFileTcs,
// ToolChainSettingsAccessor:
// --------------------------------------------------------------------
-ToolChainSettingsAccessor::ToolChainSettingsAccessor() :
- UpgradingSettingsAccessor("QtCreatorToolChains",
- Tr::tr("Tool Chains"),
- Core::Constants::IDE_DISPLAY_NAME)
+ToolChainSettingsAccessor::ToolChainSettingsAccessor()
{
+ setDocType("QtCreatorToolChains");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
setBaseFilePath(Core::ICore::userResourcePath(TOOLCHAIN_FILENAME));
addVersionUpgrader(std::make_unique<ToolChainSettingsUpgraderV0>());
@@ -192,9 +192,11 @@ Toolchains ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) const
// Autodetect: Pass autodetected toolchains from user file so the information can be reused:
const Toolchains autodetectedUserFileTcs
= Utils::filtered(userFileTcs, &ToolChain::isAutoDetected);
- // FIXME: Use real device?
- const Toolchains autodetectedTcs =
- autoDetectToolChains(ToolchainDetector(autodetectedUserFileTcs, {}, {}));
+
+ // Autodect from system paths on the desktop device.
+ // The restriction is intentional to keep startup and automatic validation a limited effort
+ ToolchainDetector detector(autodetectedUserFileTcs, DeviceManager::defaultDesktopDevice(), {});
+ const Toolchains autodetectedTcs = autoDetectToolChains(detector);
// merge tool chains and register those that we need to keep:
const ToolChainOperations ops = mergeToolChainLists(systemFileTcs, userFileTcs, autodetectedTcs);
diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp
index abcc66c3cc..294ef4988e 100644
--- a/src/plugins/projectexplorer/treescanner.cpp
+++ b/src/plugins/projectexplorer/treescanner.cpp
@@ -9,9 +9,9 @@
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
#include <memory>
@@ -42,9 +42,9 @@ bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory)
if (!m_futureWatcher.isFinished())
return false;
- m_scanFuture = Utils::runAsync(
- [directory, filter = m_filter, factory = m_factory] (FutureInterface &fi) {
- TreeScanner::scanForFiles(fi, directory, filter, factory);
+ m_scanFuture = Utils::asyncRun(
+ [directory, filter = m_filter, factory = m_factory] (Promise &promise) {
+ TreeScanner::scanForFiles(promise, directory, filter, factory);
});
m_futureWatcher.setFuture(m_scanFuture);
@@ -139,10 +139,10 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc
return fileSystemNode;
}
-void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& directory,
+void TreeScanner::scanForFiles(Promise &promise, const Utils::FilePath& directory,
const FileFilter &filter, const FileTypeFactory &factory)
{
- QList<FileNode *> nodes = ProjectExplorer::scanForFiles(fi, directory,
+ QList<FileNode *> nodes = ProjectExplorer::scanForFiles(promise, directory,
[&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn);
@@ -160,10 +160,10 @@ void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& direc
Utils::sort(nodes, ProjectExplorer::Node::sortByPath);
- fi.setProgressValue(fi.progressMaximum());
+ promise.setProgressValue(promise.future().progressMaximum());
Result result{createFolderNode(directory, nodes), nodes};
- fi.reportResult(result);
+ promise.addResult(result);
}
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/treescanner.h b/src/plugins/projectexplorer/treescanner.h
index e324303c17..f8d019121b 100644
--- a/src/plugins/projectexplorer/treescanner.h
+++ b/src/plugins/projectexplorer/treescanner.h
@@ -31,7 +31,7 @@ public:
};
using Future = QFuture<Result>;
using FutureWatcher = QFutureWatcher<Result>;
- using FutureInterface = QFutureInterface<Result>;
+ using Promise = QPromise<Result>;
using FileFilter = std::function<bool(const Utils::MimeType &, const Utils::FilePath &)>;
using FileTypeFactory = std::function<ProjectExplorer::FileType(const Utils::MimeType &, const Utils::FilePath &)>;
@@ -69,7 +69,7 @@ signals:
void finished();
private:
- static void scanForFiles(FutureInterface &fi, const Utils::FilePath &directory,
+ static void scanForFiles(Promise &fi, const Utils::FilePath &directory,
const FileFilter &filter, const FileTypeFactory &factory);
private:
diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp
index cf347e032f..ae900ea818 100644
--- a/src/plugins/projectexplorer/userfileaccessor.cpp
+++ b/src/plugins/projectexplorer/userfileaccessor.cpp
@@ -18,8 +18,8 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QRegularExpression>
@@ -283,19 +283,21 @@ FilePaths UserFileBackUpStrategy::readFileCandidates(const FilePath &baseFileNam
// UserFileAccessor:
// --------------------------------------------------------------------
-UserFileAccessor::UserFileAccessor(Project *project) :
- MergingSettingsAccessor(std::make_unique<VersionedBackUpStrategy>(this),
- "QtCreatorProject", project->displayName(),
- Core::Constants::IDE_DISPLAY_NAME),
- m_project(project)
+UserFileAccessor::UserFileAccessor(Project *project)
+ : m_project(project)
{
+ setStrategy(std::make_unique<VersionedBackUpStrategy>(this));
+ setDocType("QtCreatorProject");
+ setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
+
// Setup:
const FilePath externalUser = externalUserFile();
const FilePath projectUser = projectUserFile();
setBaseFilePath(externalUser.isEmpty() ? projectUser : externalUser);
- auto secondary
- = std::make_unique<SettingsAccessor>(docType, displayName, applicationDisplayName);
+ auto secondary = std::make_unique<SettingsAccessor>();
+ secondary->setDocType(m_docType);
+ secondary->setApplicationDisplayName(m_applicationDisplayName);
secondary->setBaseFilePath(sharedFile());
secondary->setReadOnly();
setSecondaryAccessor(std::move(secondary));
diff --git a/src/plugins/python/CMakeLists.txt b/src/plugins/python/CMakeLists.txt
index e98190a569..0e4ca94419 100644
--- a/src/plugins/python/CMakeLists.txt
+++ b/src/plugins/python/CMakeLists.txt
@@ -20,4 +20,5 @@ add_qtc_plugin(Python
pythonsettings.cpp pythonsettings.h
pythontr.h
pythonutils.cpp pythonutils.h
+ pythonwizardpage.cpp pythonwizardpage.h
)
diff --git a/src/plugins/python/Python.json.in b/src/plugins/python/Python.json.in
index 3e62d14631..98fd52ec8d 100644
--- a/src/plugins/python/Python.json.in
+++ b/src/plugins/python/Python.json.in
@@ -30,12 +30,16 @@
\" <comment>Python module interface file</comment>\",
\" <glob pattern=\'*.pyi\'/>\",
\" </mime-type>\",
- \" <mime-type type=\'text/x-python-project\'>\",
+ \" <mime-type type=\'text/x-pyqt-project\'>\",
\" <sub-class-of type=\'text/x-python\'/>\",
\" <comment>Qt Creator Python project file</comment>\",
- \" <glob pattern=\'*.pyproject\'/>\",
\" <glob pattern=\'*.pyqtc\'/>\",
\" </mime-type>\",
+ \" <mime-type type=\'text/x-python-project\'>\",
+ \" <sub-class-of type=\'application/json\'/>\",
+ \" <comment>Qt Creator Python project file</comment>\",
+ \" <glob pattern=\'*.pyproject\'/>\",
+ \" </mime-type>\",
\"</mime-info>\"
]
}
diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp
index a282702d9b..8e3f55dd11 100644
--- a/src/plugins/python/pipsupport.cpp
+++ b/src/plugins/python/pipsupport.cpp
@@ -10,13 +10,13 @@
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
using namespace Utils;
@@ -27,31 +27,39 @@ const char pipInstallTaskId[] = "Python::pipInstallTask";
PipInstallTask::PipInstallTask(const FilePath &python)
: m_python(python)
{
- connect(&m_process, &QtcProcess::done, this, &PipInstallTask::handleDone);
- connect(&m_process, &QtcProcess::readyReadStandardError, this, &PipInstallTask::handleError);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, &PipInstallTask::handleOutput);
+ connect(&m_process, &Process::done, this, &PipInstallTask::handleDone);
+ connect(&m_process, &Process::readyReadStandardError, this, &PipInstallTask::handleError);
+ connect(&m_process, &Process::readyReadStandardOutput, this, &PipInstallTask::handleOutput);
connect(&m_killTimer, &QTimer::timeout, this, &PipInstallTask::cancel);
connect(&m_watcher, &QFutureWatcher<void>::canceled, this, &PipInstallTask::cancel);
m_watcher.setFuture(m_future.future());
}
-void PipInstallTask::setPackage(const PipPackage &package)
+void PipInstallTask::addPackage(const PipPackage &package)
{
- m_package = package;
+ m_packages << package;
+}
+
+void PipInstallTask::setPackages(const QList<PipPackage> &packages)
+{
+ m_packages = packages;
}
void PipInstallTask::run()
{
- if (m_package.packageName.isEmpty()) {
+ if (m_packages.isEmpty()) {
emit finished(false);
return;
}
- const QString taskTitle = Tr::tr("Install %1").arg(m_package.displayName);
+ const QString taskTitle = Tr::tr("Install Python Packages");
Core::ProgressManager::addTask(m_future.future(), taskTitle, pipInstallTaskId);
- QString package = m_package.packageName;
- if (!m_package.version.isEmpty())
- package += "==" + m_package.version;
- QStringList arguments = {"-m", "pip", "install", package};
+ QStringList arguments = {"-m", "pip", "install"};
+ for (const PipPackage &package : m_packages) {
+ QString pipPackage = package.packageName;
+ if (!package.version.isEmpty())
+ pipPackage += "==" + package.version;
+ arguments << pipPackage;
+ }
// add --user to global pythons, but skip it for venv pythons
if (!QDir(m_python.parentDir().toString()).exists("activate"))
@@ -62,7 +70,7 @@ void PipInstallTask::run()
Core::MessageManager::writeDisrupting(
Tr::tr("Running \"%1\" to install %2.")
- .arg(m_process.commandLine().toUserOutput(), m_package.displayName));
+ .arg(m_process.commandLine().toUserOutput(), packagesDisplayName()));
m_killTimer.setSingleShot(true);
m_killTimer.start(5 /*minutes*/ * 60 * 1000);
@@ -74,7 +82,7 @@ void PipInstallTask::cancel()
m_process.waitForFinished();
Core::MessageManager::writeFlashing(
Tr::tr("The %1 installation was canceled by %2.")
- .arg(m_package.displayName, m_killTimer.isActive() ? Tr::tr("user") : Tr::tr("time out")));
+ .arg(packagesDisplayName(), m_killTimer.isActive() ? Tr::tr("user") : Tr::tr("time out")));
}
void PipInstallTask::handleDone()
@@ -82,8 +90,8 @@ void PipInstallTask::handleDone()
m_future.reportFinished();
const bool success = m_process.result() == ProcessResult::FinishedWithSuccess;
if (!success) {
- Core::MessageManager::writeFlashing(Tr::tr("Installing the %1 failed with exit code %2")
- .arg(m_package.displayName).arg(m_process.exitCode()));
+ Core::MessageManager::writeFlashing(Tr::tr("Installing %1 failed with exit code %2")
+ .arg(packagesDisplayName()).arg(m_process.exitCode()));
}
emit finished(success);
}
@@ -102,6 +110,11 @@ void PipInstallTask::handleError()
Core::MessageManager::writeSilently(stdErr);
}
+QString PipInstallTask::packagesDisplayName() const
+{
+ return Utils::transform(m_packages, &PipPackage::displayName).join(", ");
+}
+
void PipPackageInfo::parseField(const QString &field, const QStringList &data)
{
if (field.isEmpty())
@@ -147,7 +160,7 @@ static PipPackageInfo infoImpl(const PipPackage &package, const FilePath &python
{
PipPackageInfo result;
- QtcProcess pip;
+ Process pip;
pip.setCommand(CommandLine(python, {"-m", "pip", "show", "-f", package.packageName}));
pip.runBlocking();
QString fieldName;
@@ -175,7 +188,7 @@ static PipPackageInfo infoImpl(const PipPackage &package, const FilePath &python
QFuture<PipPackageInfo> Pip::info(const PipPackage &package)
{
- return Utils::runAsync(infoImpl, package, m_python);
+ return Utils::asyncRun(infoImpl, package, m_python);
}
Pip::Pip(const Utils::FilePath &python)
diff --git a/src/plugins/python/pipsupport.h b/src/plugins/python/pipsupport.h
index 4ef6fcca73..4d08a3b1ea 100644
--- a/src/plugins/python/pipsupport.h
+++ b/src/plugins/python/pipsupport.h
@@ -4,7 +4,7 @@
#pragma once
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QFutureWatcher>
#include <QTimer>
@@ -63,7 +63,8 @@ class PipInstallTask : public QObject
Q_OBJECT
public:
explicit PipInstallTask(const Utils::FilePath &python);
- void setPackage(const PipPackage &package);
+ void addPackage(const PipPackage &package);
+ void setPackages(const QList<PipPackage> &packages);
void run();
signals:
@@ -75,9 +76,11 @@ private:
void handleOutput();
void handleError();
+ QString packagesDisplayName() const;
+
const Utils::FilePath m_python;
- PipPackage m_package;
- Utils::QtcProcess m_process;
+ QList<PipPackage> m_packages;
+ Utils::Process m_process;
QFutureInterface<void> m_future;
QFutureWatcher<void> m_watcher;
QTimer m_killTimer;
diff --git a/src/plugins/python/pyside.cpp b/src/plugins/python/pyside.cpp
index 592addd308..4b9df185a3 100644
--- a/src/plugins/python/pyside.cpp
+++ b/src/plugins/python/pyside.cpp
@@ -5,7 +5,6 @@
#include "pipsupport.h"
#include "pythonplugin.h"
-#include "pythonsettings.h"
#include "pythontr.h"
#include "pythonutils.h"
@@ -17,10 +16,10 @@
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/infobar.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <QRegularExpression>
#include <QTextCursor>
@@ -55,7 +54,7 @@ bool PySideInstaller::missingPySideInstallation(const FilePath &pythonPath,
if (pythonWithPyside[pythonPath].contains(pySide))
return false;
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setCommand({pythonPath, {"-c", "import " + pySide}});
pythonProcess.runBlocking();
const bool missing = pythonProcess.result() != ProcessResult::FinishedWithSuccess;
@@ -88,19 +87,10 @@ void PySideInstaller::installPyside(const FilePath &python,
if (success)
emit pySideInstalled(python, pySide);
});
- install->setPackage(PipPackage(pySide));
+ install->setPackages({PipPackage(pySide)});
install->run();
}
-void PySideInstaller::changeInterpreter(const QString &interpreterId,
- RunConfiguration *runConfig)
-{
- if (runConfig) {
- if (auto aspect = runConfig->aspect<InterpreterAspect>())
- aspect->setCurrentInterpreter(PythonSettings::interpreter(interpreterId));
- }
-}
-
void PySideInstaller::handlePySideMissing(const FilePath &python,
const QString &pySide,
TextEditor::TextDocument *document)
@@ -140,8 +130,7 @@ void PySideInstaller::runPySideChecker(const FilePath &python,
handlePySideMissing(python, pySide, document);
watcher->deleteLater();
});
- watcher->setFuture(
- Utils::runAsync(&missingPySideInstallation, python, pySide));
+ watcher->setFuture(Utils::asyncRun(&missingPySideInstallation, python, pySide));
}
} // Python::Internal
diff --git a/src/plugins/python/pyside.h b/src/plugins/python/pyside.h
index 299a095412..3b4f99974a 100644
--- a/src/plugins/python/pyside.h
+++ b/src/plugins/python/pyside.h
@@ -30,7 +30,6 @@ private:
void installPyside(const Utils::FilePath &python,
const QString &pySide, TextEditor::TextDocument *document);
- void changeInterpreter(const QString &interpreterId, ProjectExplorer::RunConfiguration *runConfig);
void handlePySideMissing(const Utils::FilePath &python,
const QString &pySide,
TextEditor::TextDocument *document);
diff --git a/src/plugins/python/pysidebuildconfiguration.cpp b/src/plugins/python/pysidebuildconfiguration.cpp
index 3480be3e87..eae9b4eeea 100644
--- a/src/plugins/python/pysidebuildconfiguration.cpp
+++ b/src/plugins/python/pysidebuildconfiguration.cpp
@@ -42,7 +42,7 @@ PySideBuildStepFactory::PySideBuildStepFactory()
registerStep<PySideBuildStep>(pySideBuildStep);
setSupportedProjectType(PythonProjectId);
setDisplayName(Tr::tr("Run PySide6 project tool"));
- setFlags(BuildStepInfo::UniqueStep);
+ setFlags(BuildStep::UniqueStep);
}
PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id)
@@ -63,7 +63,7 @@ PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id)
setCommandLineProvider([this] { return CommandLine(m_pysideProject->filePath(), {"build"}); });
setWorkingDirectoryProvider([this] {
- return target()->project()->projectDirectory().onDevice(m_pysideProject->filePath());
+ return m_pysideProject->filePath().withNewMappedPath(target()->project()->projectDirectory()); // FIXME: new path needed?
});
setEnvironmentModifier([this](Environment &env) {
env.prependOrSetPath(m_pysideProject->filePath().parentDir());
diff --git a/src/plugins/python/pysideuicextracompiler.cpp b/src/plugins/python/pysideuicextracompiler.cpp
index b1e7e31bb7..45fb68066f 100644
--- a/src/plugins/python/pysideuicextracompiler.cpp
+++ b/src/plugins/python/pysideuicextracompiler.cpp
@@ -3,7 +3,7 @@
#include "pysideuicextracompiler.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -30,7 +30,7 @@ FilePath PySideUicExtraCompiler::command() const
return m_pySideUic;
}
-FileNameToContentsHash PySideUicExtraCompiler::handleProcessFinished(QtcProcess *process)
+FileNameToContentsHash PySideUicExtraCompiler::handleProcessFinished(Process *process)
{
FileNameToContentsHash result;
if (process->exitStatus() != QProcess::NormalExit && process->exitCode() != 0)
diff --git a/src/plugins/python/pysideuicextracompiler.h b/src/plugins/python/pysideuicextracompiler.h
index f2a09bed78..058717621e 100644
--- a/src/plugins/python/pysideuicextracompiler.h
+++ b/src/plugins/python/pysideuicextracompiler.h
@@ -21,7 +21,7 @@ public:
private:
Utils::FilePath command() const override;
ProjectExplorer::FileNameToContentsHash handleProcessFinished(
- Utils::QtcProcess *process) override;
+ Utils::Process *process) override;
Utils::FilePath m_pySideUic;
};
diff --git a/src/plugins/python/python.qbs b/src/plugins/python/python.qbs
index 9f8288cb11..5186dafcdc 100644
--- a/src/plugins/python/python.qbs
+++ b/src/plugins/python/python.qbs
@@ -49,6 +49,8 @@ QtcPlugin {
"pythontr.h",
"pythonutils.cpp",
"pythonutils.h",
+ "pythonwizardpage.cpp",
+ "pythonwizardpage.h",
]
}
}
diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp
index 6551facef5..d23685bc3b 100644
--- a/src/plugins/python/pythoneditor.cpp
+++ b/src/plugins/python/pythoneditor.cpp
@@ -19,12 +19,14 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h>
+#include <utils/stylehelper.h>
+
#include <QAction>
#include <QActionGroup>
#include <QComboBox>
@@ -120,7 +122,7 @@ private:
PythonEditorWidget::PythonEditorWidget(QWidget *parent) : TextEditorWidget(parent)
{
auto replButton = new QToolButton(this);
- replButton->setProperty("noArrow", true);
+ replButton->setProperty(StyleHelper::C_NO_ARROW, true);
replButton->setText(Tr::tr("REPL"));
replButton->setPopupMode(QToolButton::InstantPopup);
replButton->setToolTip(Tr::tr("Open interactive Python. Either importing nothing, "
@@ -152,7 +154,7 @@ void PythonEditorWidget::setUserDefinedPython(const Interpreter &interpreter)
QTC_ASSERT(pythonDocument, return);
FilePath documentPath = pythonDocument->filePath();
QTC_ASSERT(!documentPath.isEmpty(), return);
- if (Project *project = SessionManager::projectForFile(documentPath)) {
+ if (Project *project = ProjectManager::projectForFile(documentPath)) {
if (Target *target = project->activeTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration()) {
if (auto interpretersAspect= rc->aspect<InterpreterAspect>()) {
@@ -163,6 +165,7 @@ void PythonEditorWidget::setUserDefinedPython(const Interpreter &interpreter)
}
}
definePythonForDocument(textDocument()->filePath(), interpreter.command);
+ updateInterpretersSelector();
pythonDocument->checkForPyls();
}
@@ -174,7 +177,7 @@ void PythonEditorWidget::updateInterpretersSelector()
m_interpreters->setMenu(new QMenu(m_interpreters));
m_interpreters->setPopupMode(QToolButton::InstantPopup);
m_interpreters->setToolButtonStyle(Qt::ToolButtonTextOnly);
- m_interpreters->setProperty("noArrow", true);
+ m_interpreters->setProperty(StyleHelper::C_NO_ARROW, true);
}
QMenu *menu = m_interpreters->menu();
@@ -184,7 +187,7 @@ void PythonEditorWidget::updateInterpretersSelector()
disconnect(connection);
m_projectConnections.clear();
const FilePath documentPath = textDocument()->filePath();
- if (Project *project = SessionManager::projectForFile(documentPath)) {
+ if (Project *project = ProjectManager::projectForFile(documentPath)) {
m_projectConnections << connect(project,
&Project::activeTargetChanged,
this,
@@ -212,33 +215,51 @@ void PythonEditorWidget::updateInterpretersSelector()
m_interpreters->setText(text);
};
- const FilePath currentInterpreter = detectPython(textDocument()->filePath());
+ const FilePath currentInterpreterPath = detectPython(textDocument()->filePath());
const QList<Interpreter> configuredInterpreters = PythonSettings::interpreters();
- bool foundCurrentInterpreter = false;
auto interpretersGroup = new QActionGroup(menu);
interpretersGroup->setExclusive(true);
+ std::optional<Interpreter> currentInterpreter;
for (const Interpreter &interpreter : configuredInterpreters) {
QAction *action = interpretersGroup->addAction(interpreter.name);
connect(action, &QAction::triggered, this, [this, interpreter]() {
setUserDefinedPython(interpreter);
});
action->setCheckable(true);
- if (!foundCurrentInterpreter && interpreter.command == currentInterpreter) {
- foundCurrentInterpreter = true;
+ if (!currentInterpreter && interpreter.command == currentInterpreterPath) {
+ currentInterpreter = interpreter;
action->setChecked(true);
setButtonText(interpreter.name);
m_interpreters->setToolTip(interpreter.command.toUserOutput());
}
}
menu->addActions(interpretersGroup->actions());
- if (!foundCurrentInterpreter) {
- if (currentInterpreter.exists())
- setButtonText(currentInterpreter.toUserOutput());
+ if (!currentInterpreter) {
+ if (currentInterpreterPath.exists())
+ setButtonText(currentInterpreterPath.toUserOutput());
else
setButtonText(Tr::tr("No Python Selected"));
}
- if (!interpretersGroup->actions().isEmpty())
- menu->addSeparator();
+ if (!interpretersGroup->actions().isEmpty()) {
+ menu->addSeparator();
+ auto venvAction = menu->addAction(Tr::tr("Create Virtual Environment"));
+ connect(venvAction,
+ &QAction::triggered,
+ this,
+ [self = QPointer<PythonEditorWidget>(this), currentInterpreter]() {
+ if (!currentInterpreter)
+ return;
+ auto callback = [self](const std::optional<Interpreter> &venvInterpreter) {
+ if (self && venvInterpreter)
+ self->setUserDefinedPython(*venvInterpreter);
+ };
+ PythonSettings::createVirtualEnvironmentInteractive(self->textDocument()
+ ->filePath()
+ .parentDir(),
+ *currentInterpreter,
+ callback);
+ });
+ }
auto settingsAction = menu->addAction(Tr::tr("Manage Python Interpreters"));
connect(settingsAction, &QAction::triggered, this, []() {
Core::ICore::showOptionsDialog(Constants::C_PYTHONOPTIONS_PAGE_ID);
diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp
index dde324dbd9..9f347d8e48 100644
--- a/src/plugins/python/pythonlanguageclient.cpp
+++ b/src/plugins/python/pythonlanguageclient.cpp
@@ -23,15 +23,15 @@
#include <languageserverprotocol/workspace.h>
#include <projectexplorer/extracompiler.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
+#include <utils/async.h>
#include <utils/infobar.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
@@ -80,7 +80,7 @@ FilePath getPylsModulePath(CommandLine pylsCommand)
pylsCommand.addArg("-h");
- QtcProcess pythonProcess;
+ Process pythonProcess;
Environment env = pythonProcess.environment();
env.set("PYTHONVERBOSE", "x");
pythonProcess.setEnvironment(env);
@@ -114,7 +114,7 @@ static PythonLanguageServerState checkPythonLanguageServer(const FilePath &pytho
const CommandLine pythonLShelpCommand(python, {"-m", "pylsp", "-h"});
const FilePath &modulePath = getPylsModulePath(pythonLShelpCommand);
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setCommand(pythonLShelpCommand);
pythonProcess.runBlocking();
if (pythonProcess.allOutput().contains("Python Language Server"))
@@ -305,7 +305,7 @@ void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python,
install->deleteLater();
});
- install->setPackage(PipPackage{"python-lsp-server[all]", "Python Language Server"});
+ install->setPackages({PipPackage{"python-lsp-server[all]", "Python Language Server"}});
install->run();
}
@@ -341,7 +341,7 @@ void PyLSConfigureAssistant::openDocumentWithPython(const FilePath &python,
instance()->handlePyLSState(python, watcher->result(), document);
watcher->deleteLater();
});
- watcher->setFuture(Utils::runAsync(&checkPythonLanguageServer, python));
+ watcher->setFuture(Utils::asyncRun(&checkPythonLanguageServer, python));
}
void PyLSConfigureAssistant::handlePyLSState(const FilePath &python,
diff --git a/src/plugins/python/pythonplugin.cpp b/src/plugins/python/pythonplugin.cpp
index 3bc19a8725..67857aec23 100644
--- a/src/plugins/python/pythonplugin.cpp
+++ b/src/plugins/python/pythonplugin.cpp
@@ -6,17 +6,18 @@
#include "pysidebuildconfiguration.h"
#include "pythoneditor.h"
#include "pythonproject.h"
-#include "pythonsettings.h"
#include "pythonrunconfiguration.h"
+#include "pythonsettings.h"
+#include "pythonwizardpage.h"
#include <projectexplorer/buildtargetinfo.h>
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <projectexplorer/localenvironmentaspect.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/taskhub.h>
#include <utils/fsengine/fileiconprovider.h>
-#include <utils/futuresynchronizer.h>
#include <utils/theme/theme.h>
using namespace ProjectExplorer;
@@ -36,7 +37,6 @@ public:
PySideBuildConfigurationFactory buildConfigFactory;
SimpleTargetRunnerFactory runWorkerFactory{{runConfigFactory.runConfigurationId()}};
PythonSettings settings;
- FutureSynchronizer m_futureSynchronizer;
};
PythonPlugin::PythonPlugin()
@@ -55,17 +55,13 @@ PythonPlugin *PythonPlugin::instance()
return m_instance;
}
-FutureSynchronizer *PythonPlugin::futureSynchronizer()
-{
- QTC_ASSERT(m_instance, return nullptr);
- return &m_instance->d->m_futureSynchronizer;
-}
-
void PythonPlugin::initialize()
{
d = new PythonPluginPrivate;
ProjectManager::registerProjectType<PythonProject>(PythonMimeType);
+ ProjectManager::registerProjectType<PythonProject>(PythonMimeTypeLegacy);
+ JsonWizardFactory::registerPageFactory(new PythonWizardPageFactory);
}
void PythonPlugin::extensionsInitialized()
diff --git a/src/plugins/python/pythonplugin.h b/src/plugins/python/pythonplugin.h
index 7c8ca121f3..ef0860bbca 100644
--- a/src/plugins/python/pythonplugin.h
+++ b/src/plugins/python/pythonplugin.h
@@ -5,8 +5,6 @@
#include <extensionsystem/iplugin.h>
-namespace Utils { class FutureSynchronizer; }
-
namespace Python::Internal {
class PythonPlugin final : public ExtensionSystem::IPlugin
@@ -19,7 +17,6 @@ public:
~PythonPlugin() final;
static PythonPlugin *instance();
- static Utils::FutureSynchronizer *futureSynchronizer();
private:
void initialize() final;
diff --git a/src/plugins/python/pythonproject.h b/src/plugins/python/pythonproject.h
index 0217a77b61..676e010a01 100644
--- a/src/plugins/python/pythonproject.h
+++ b/src/plugins/python/pythonproject.h
@@ -7,7 +7,8 @@
namespace Python::Internal {
-const char PythonMimeType[] = "text/x-python-project"; // ### FIXME
+const char PythonMimeType[] = "text/x-python-project";
+const char PythonMimeTypeLegacy[] = "text/x-pyqt-project";
const char PythonProjectId[] = "PythonProject";
const char PythonErrorTaskCategory[] = "Task.Category.Python";
diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp
index d21e7f8bfc..537da98125 100644
--- a/src/plugins/python/pythonrunconfiguration.cpp
+++ b/src/plugins/python/pythonrunconfiguration.cpp
@@ -9,7 +9,6 @@
#include "pysideuicextracompiler.h"
#include "pythonconstants.h"
#include "pythonlanguageclient.h"
-#include "pythonplugin.h"
#include "pythonproject.h"
#include "pythonsettings.h"
#include "pythontr.h"
@@ -17,6 +16,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <languageclient/languageclientmanager.h>
#include <projectexplorer/buildsteplist.h>
@@ -218,7 +219,7 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id)
currentInterpreterChanged();
setRunnableModifier([](Runnable &r) {
- r.workingDirectory = r.workingDirectory.onDevice(r.command.executable());
+ r.workingDirectory = r.command.executable().withNewMappedPath(r.workingDirectory); // FIXME: Needed?
});
connect(PySideInstaller::instance(), &PySideInstaller::pySideInstalled, this,
@@ -248,7 +249,7 @@ void PythonRunConfigurationPrivate::checkForPySide(const FilePath &python,
});
const auto future = Pip::instance(python)->info(package);
m_watcher.setFuture(future);
- PythonPlugin::futureSynchronizer()->addFuture(future);
+ ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
}
void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
@@ -279,12 +280,12 @@ void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo
= OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-uic");
for (const FilePath &file : files) {
if (file.fileName() == pySide6ProjectName) {
- result.pySideProjectPath = location.resolvePath(file).onDevice(python);
+ result.pySideProjectPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideProjectPath = result.pySideProjectPath.cleanPath();
if (!result.pySideUicPath.isEmpty())
return result;
} else if (file.fileName() == pySide6UicName) {
- result.pySideUicPath = location.resolvePath(file).onDevice(python);
+ result.pySideUicPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideUicPath = result.pySideUicPath.cleanPath();
if (!result.pySideProjectPath.isEmpty())
return result;
diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp
index 9306fe3b6f..e5251384d2 100644
--- a/src/plugins/python/pythonsettings.cpp
+++ b/src/plugins/python/pythonsettings.cpp
@@ -6,6 +6,7 @@
#include "pythonconstants.h"
#include "pythonplugin.h"
#include "pythontr.h"
+#include "pythonutils.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
@@ -26,23 +27,26 @@
#include <utils/listmodel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
#include <QDir>
+#include <QFormLayout>
+#include <QGroupBox>
+#include <QJsonDocument>
+#include <QJsonObject>
#include <QLabel>
-#include <QPushButton>
#include <QPointer>
+#include <QPushButton>
#include <QSettings>
#include <QStackedWidget>
#include <QTreeView>
-#include <QWidget>
#include <QVBoxLayout>
-#include <QGroupBox>
-#include <QCheckBox>
-#include <QJsonDocument>
-#include <QJsonObject>
+#include <QWidget>
using namespace ProjectExplorer;
using namespace Utils;
@@ -58,7 +62,7 @@ static Interpreter createInterpreter(const FilePath &python,
result.id = QUuid::createUuid().toString();
result.command = python;
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setProcessChannelMode(QProcess::MergedChannels);
pythonProcess.setTimeoutS(1);
pythonProcess.setCommand({python, {"--version"}});
@@ -69,7 +73,7 @@ static Interpreter createInterpreter(const FilePath &python,
result.name = defaultName;
QDir pythonDir(python.parentDir().toString());
if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp())
- result.name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName());
+ result.name += QString(" (%1)").arg(pythonDir.dirName());
if (!suffix.isEmpty())
result.name += ' ' + suffix;
@@ -92,8 +96,9 @@ public:
Form {
Tr::tr("Name:"), m_name, br,
- Tr::tr("Executable"), m_executable
- }.attachTo(this, WithoutMargins);
+ Tr::tr("Executable"), m_executable,
+ noMargin
+ }.attachTo(this);
}
void updateInterpreter(const Interpreter &interpreter)
@@ -314,34 +319,37 @@ public:
setCategory(Constants::C_PYTHON_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Python"));
setCategoryIconPath(":/python/images/settingscategory_python.png");
- setWidgetCreator([]() { return new InterpreterOptionsWidget(); });
+ setWidgetCreator([this] { m_widget = new InterpreterOptionsWidget; return m_widget; });
}
QList<Interpreter> interpreters()
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- return w->interpreters();
+ if (m_widget)
+ return m_widget->interpreters();
return {};
}
void addInterpreter(const Interpreter &interpreter)
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- w->addInterpreter(interpreter);
+ if (m_widget)
+ m_widget->addInterpreter(interpreter);
}
void removeInterpreterFrom(const QString &detectionSource)
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- w->removeInterpreterFrom(detectionSource);
+ if (m_widget)
+ m_widget->removeInterpreterFrom(detectionSource);
}
QList<Interpreter> interpreterFrom(const QString &detectionSource)
{
- if (auto w = static_cast<InterpreterOptionsWidget *>(widget()))
- return w->interpreterFrom(detectionSource);
+ if (m_widget)
+ return m_widget->interpreterFrom(detectionSource);
return {};
}
+
+private:
+ InterpreterOptionsWidget *m_widget = nullptr;
};
static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable)
@@ -769,12 +777,86 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau
saveSettings();
}
+Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath,
+ bool isDefault,
+ const QString &nameSuffix)
+{
+ const Interpreter interpreter = createInterpreter(interpreterPath, {}, nameSuffix);
+ addInterpreter(interpreter, isDefault);
+ return interpreter;
+}
+
PythonSettings *PythonSettings::instance()
{
QTC_CHECK(settingsInstance);
return settingsInstance;
}
+void PythonSettings::createVirtualEnvironmentInteractive(
+ const FilePath &startDirectory,
+ const Interpreter &defaultInterpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback)
+{
+ QDialog dialog;
+ dialog.setModal(true);
+ auto layout = new QFormLayout(&dialog);
+ auto interpreters = new QComboBox;
+ const QString preselectedId = defaultInterpreter.id.isEmpty()
+ ? PythonSettings::defaultInterpreter().id
+ : defaultInterpreter.id;
+ for (const Interpreter &interpreter : PythonSettings::interpreters()) {
+ interpreters->addItem(interpreter.name, interpreter.id);
+ if (!preselectedId.isEmpty() && interpreter.id == preselectedId)
+ interpreters->setCurrentIndex(interpreters->count() - 1);
+ }
+ layout->addRow(Tr::tr("Python Interpreter"), interpreters);
+ auto pathChooser = new PathChooser();
+ pathChooser->setInitialBrowsePathBackup(startDirectory);
+ pathChooser->setExpectedKind(PathChooser::Directory);
+ pathChooser->setPromptDialogTitle(Tr::tr("New Python Virtual Environment Directory"));
+ layout->addRow(Tr::tr("Virtual Environment Directory"), pathChooser);
+ auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel);
+ auto createButton = buttons->addButton(Tr::tr("Create"), QDialogButtonBox::AcceptRole);
+ createButton->setEnabled(false);
+ connect(pathChooser,
+ &PathChooser::validChanged,
+ createButton,
+ [createButton](bool valid) { createButton->setEnabled(valid); });
+ connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
+ connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+ layout->addRow(buttons);
+ dialog.setLayout(layout);
+ if (dialog.exec() == QDialog::Rejected) {
+ callback({});
+ return;
+ }
+
+ const Interpreter interpreter = PythonSettings::interpreter(
+ interpreters->currentData().toString());
+
+ auto venvDir = pathChooser->filePath();
+ createVirtualEnvironment(venvDir, interpreter, callback);
+}
+
+void PythonSettings::createVirtualEnvironment(
+ const FilePath &directory,
+ const Interpreter &interpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback,
+ const QString &nameSuffix)
+{
+ createVenv(interpreter.command, directory, [directory, callback, nameSuffix](bool success) {
+ std::optional<Interpreter> result;
+ if (success) {
+ FilePath venvPython = directory.osType() == Utils::OsTypeWindows ? directory / "Scripts"
+ : directory / "bin";
+ venvPython = venvPython.pathAppended("python").withExecutableSuffix();
+ if (venvPython.exists())
+ result = PythonSettings::addInterpreter(venvPython, false, nameSuffix);
+ }
+ callback(result);
+ });
+}
+
QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path)
{
QList<Interpreter> result;
diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h
index 693c732208..35939c1ecd 100644
--- a/src/plugins/python/pythonsettings.h
+++ b/src/plugins/python/pythonsettings.h
@@ -24,12 +24,23 @@ public:
static Interpreter interpreter(const QString &interpreterId);
static void setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId);
static void addInterpreter(const Interpreter &interpreter, bool isDefault = false);
+ static Interpreter addInterpreter(const Utils::FilePath &interpreterPath,
+ bool isDefault = false,
+ const QString &nameSuffix = {});
static void setPyLSConfiguration(const QString &configuration);
static bool pylsEnabled();
static void setPylsEnabled(const bool &enabled);
static QString pylsConfiguration();
static PythonSettings *instance();
-
+ static void createVirtualEnvironmentInteractive(
+ const Utils::FilePath &startDirectory,
+ const Interpreter &defaultInterpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback);
+ static void createVirtualEnvironment(
+ const Utils::FilePath &directory,
+ const Interpreter &interpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback,
+ const QString &nameSuffix = {});
static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path);
signals:
diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp
index 346c42d183..bf3f683dd1 100644
--- a/src/plugins/python/pythonutils.cpp
+++ b/src/plugins/python/pythonutils.cpp
@@ -8,14 +8,15 @@
#include "pythontr.h"
#include <coreplugin/messagemanager.h>
+#include <coreplugin/progressmanager/processprogress.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -31,9 +32,9 @@ static QHash<FilePath, FilePath> &userDefinedPythonsForDocument()
FilePath detectPython(const FilePath &documentPath)
{
Project *project = documentPath.isEmpty() ? nullptr
- : SessionManager::projectForFile(documentPath);
+ : ProjectManager::projectForFile(documentPath);
if (!project)
- project = SessionManager::startupProject();
+ project = ProjectManager::startupProject();
Environment env = Environment::systemEnvironment();
@@ -105,9 +106,11 @@ static QStringList replImportArgs(const FilePath &pythonFile, ReplType type)
void openPythonRepl(QObject *parent, const FilePath &file, ReplType type)
{
+ Q_UNUSED(parent)
+
static const auto workingDir = [](const FilePath &file) {
if (file.isEmpty()) {
- if (Project *project = SessionManager::startupProject())
+ if (Project *project = ProjectManager::startupProject())
return project->projectDirectory();
return FilePath::currentWorkingPath();
}
@@ -115,23 +118,21 @@ void openPythonRepl(QObject *parent, const FilePath &file, ReplType type)
};
const auto args = QStringList{"-i"} + replImportArgs(file, type);
- auto process = new QtcProcess(parent);
- process->setTerminalMode(TerminalMode::On);
const FilePath pythonCommand = detectPython(file);
- process->setCommand({pythonCommand, args});
- process->setWorkingDirectory(workingDir(file));
- const QString commandLine = process->commandLine().toUserOutput();
- QObject::connect(process, &QtcProcess::done, process, [process, commandLine] {
- if (process->error() != QProcess::UnknownError) {
- Core::MessageManager::writeDisrupting(Tr::tr(
- (process->error() == QProcess::FailedToStart)
- ? "Failed to run Python (%1): \"%2\"."
- : "Error while running Python (%1): \"%2\".")
- .arg(commandLine, process->errorString()));
- }
- process->deleteLater();
- });
- process->start();
+
+ Process process;
+ process.setCommand({pythonCommand, args});
+ process.setWorkingDirectory(workingDir(file));
+ process.setTerminalMode(TerminalMode::Detached);
+ process.start();
+
+ if (process.error() != QProcess::UnknownError) {
+ Core::MessageManager::writeDisrupting(
+ Tr::tr((process.error() == QProcess::FailedToStart)
+ ? "Failed to run Python (%1): \"%2\"."
+ : "Error while running Python (%1): \"%2\".")
+ .arg(process.commandLine().toUserOutput(), process.errorString()));
+ }
}
QString pythonName(const FilePath &pythonPath)
@@ -141,7 +142,7 @@ QString pythonName(const FilePath &pythonPath)
return {};
QString name = nameForPython.value(pythonPath);
if (name.isEmpty()) {
- QtcProcess pythonProcess;
+ Process pythonProcess;
pythonProcess.setTimeoutS(2);
pythonProcess.setCommand({pythonPath, {"--version"}});
pythonProcess.runBlocking();
@@ -155,7 +156,7 @@ QString pythonName(const FilePath &pythonPath)
PythonProject *pythonProjectForFile(const FilePath &pythonFile)
{
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
if (auto pythonProject = qobject_cast<PythonProject *>(project)) {
if (pythonProject->isKnownFile(pythonFile))
return pythonProject;
@@ -164,4 +165,24 @@ PythonProject *pythonProjectForFile(const FilePath &pythonFile)
return nullptr;
}
+void createVenv(const Utils::FilePath &python,
+ const Utils::FilePath &venvPath,
+ const std::function<void(bool)> &callback)
+{
+ QTC_ASSERT(python.isExecutableFile(), callback(false); return);
+ QTC_ASSERT(!venvPath.exists() || venvPath.isDir(), callback(false); return);
+
+ const CommandLine command(python, QStringList{"-m", "venv", venvPath.toUserOutput()});
+
+ auto process = new Process;
+ auto progress = new Core::ProcessProgress(process);
+ progress->setDisplayName(Tr::tr("Create Python venv"));
+ QObject::connect(process, &Process::done, [process, callback](){
+ callback(process->result() == ProcessResult::FinishedWithSuccess);
+ process->deleteLater();
+ });
+ process->setCommand(command);
+ process->start();
+}
+
} // Python::Internal
diff --git a/src/plugins/python/pythonutils.h b/src/plugins/python/pythonutils.h
index 8d5b06974e..f3e685b4ae 100644
--- a/src/plugins/python/pythonutils.h
+++ b/src/plugins/python/pythonutils.h
@@ -16,4 +16,8 @@ QString pythonName(const Utils::FilePath &pythonPath);
class PythonProject;
PythonProject *pythonProjectForFile(const Utils::FilePath &pythonFile);
+void createVenv(const Utils::FilePath &python,
+ const Utils::FilePath &venvPath,
+ const std::function<void(bool)> &callback);
+
} // Python::Internal
diff --git a/src/plugins/python/pythonwizardpage.cpp b/src/plugins/python/pythonwizardpage.cpp
new file mode 100644
index 0000000000..d132f3550c
--- /dev/null
+++ b/src/plugins/python/pythonwizardpage.cpp
@@ -0,0 +1,220 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "pythonwizardpage.h"
+
+#include "pythonconstants.h"
+#include "pythonsettings.h"
+#include "pythontr.h"
+
+#include <coreplugin/generatedfile.h>
+
+#include <utils/algorithm.h>
+#include <utils/layoutbuilder.h>
+#include <utils/mimeutils.h>
+#include <utils/qtcassert.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/target.h>
+
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace Python::Internal {
+
+PythonWizardPageFactory::PythonWizardPageFactory()
+{
+ setTypeIdsSuffix("PythonConfiguration");
+}
+
+WizardPage *PythonWizardPageFactory::create(JsonWizard *wizard, Id typeId, const QVariant &data)
+{
+ Q_UNUSED(wizard)
+
+ QTC_ASSERT(canCreate(typeId), return nullptr);
+
+ QList<QPair<QString, QVariant>> pySideAndData;
+ for (const QVariant &item : data.toMap().value("items").toList()) {
+ const QMap<QString, QVariant> map = item.toMap();
+ const QVariant name = map.value("trKey");
+ if (name.isValid())
+ pySideAndData.emplaceBack(QPair<QString, QVariant>{name.toString(), map.value("value")});
+ }
+ bool validIndex = false;
+ int defaultPySide = data.toMap().value("index").toInt(&validIndex);
+ if (!validIndex)
+ defaultPySide = -1;
+ return new PythonWizardPage(pySideAndData, defaultPySide);
+}
+
+static bool validItem(const QVariant &item)
+{
+ QMap<QString, QVariant> map = item.toMap();
+ if (!map.value("trKey").canConvert<QString>())
+ return false;
+ map = map.value("value").toMap();
+ return map.value("PySideVersion").canConvert<QString>();
+}
+
+bool PythonWizardPageFactory::validateData(Id typeId, const QVariant &data, QString *errorMessage)
+{
+ QTC_ASSERT(canCreate(typeId), return false);
+ const QList<QVariant> items = data.toMap().value("items").toList();
+
+ if (items.isEmpty()) {
+ if (errorMessage) {
+ *errorMessage = Tr::tr("'data' of a Python wizard page expects a map with 'items' "
+ "containing a list of objects");
+ }
+ return false;
+ }
+
+ if (!Utils::allOf(items, &validItem)) {
+ if (errorMessage) {
+ *errorMessage = Tr::tr(
+ "An item of Python wizard page data expects a 'trKey' field containing the ui "
+ "visible string for that python version and an field 'value' containing an object "
+ "with a 'PySideVersion' field used for import statements in the python files.");
+ }
+ return false;
+ }
+ return true;
+}
+
+PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData,
+ const int defaultPyside)
+{
+ using namespace Layouting;
+ m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
+ connect(PythonSettings::instance(),
+ &PythonSettings::interpretersChanged,
+ this,
+ &PythonWizardPage::updateInterpreters);
+
+ m_pySideVersion.setLabelText(Tr::tr("PySide version"));
+ m_pySideVersion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
+ for (auto [name, data] : pySideAndData)
+ m_pySideVersion.addOption(SelectionAspect::Option(name, {}, data));
+ if (defaultPyside >= 0)
+ m_pySideVersion.setDefaultValue(defaultPyside);
+
+ m_createVenv.setLabelText(Tr::tr("Create new Virtual Environment"));
+
+ m_venvPath.setLabelText(Tr::tr("Path to virtual environment"));
+ m_venvPath.setDisplayStyle(StringAspect::PathChooserDisplay);
+ m_venvPath.setEnabler(&m_createVenv);
+ m_venvPath.setExpectedKind(PathChooser::Directory);
+
+ m_stateLabel = new InfoLabel();
+ m_stateLabel->setWordWrap(true);
+ m_stateLabel->setFilled(true);
+ m_stateLabel->setType(InfoLabel::Error);
+ connect(&m_venvPath, &StringAspect::valueChanged, this, &PythonWizardPage::updateStateLabel);
+ connect(&m_createVenv, &BoolAspect::valueChanged, this, &PythonWizardPage::updateStateLabel);
+
+ Grid {
+ m_pySideVersion, br,
+ m_interpreter, br,
+ m_createVenv, br,
+ m_venvPath, br,
+ m_stateLabel, br,
+ noMargin
+ }.attachTo(this);
+}
+
+void PythonWizardPage::initializePage()
+{
+ auto wiz = qobject_cast<JsonWizard *>(wizard());
+ QTC_ASSERT(wiz, return);
+ connect(wiz, &JsonWizard::filesPolished,
+ this, &PythonWizardPage::setupProject,
+ Qt::UniqueConnection);
+
+ const FilePath projectDir = FilePath::fromString(wiz->property("ProjectDirectory").toString());
+ m_createVenv.setValue(!projectDir.isEmpty());
+ if (m_venvPath.filePath().isEmpty())
+ m_venvPath.setFilePath(projectDir.isEmpty() ? FilePath{} : projectDir / "venv");
+
+ updateInterpreters();
+ updateStateLabel();
+}
+
+bool PythonWizardPage::validatePage()
+{
+ if (m_createVenv.value() && !m_venvPath.pathChooser()->isValid())
+ return false;
+ auto wiz = qobject_cast<JsonWizard *>(wizard());
+ const QMap<QString, QVariant> data = m_pySideVersion.itemValue().toMap();
+ for (auto it = data.begin(), end = data.end(); it != end; ++it)
+ wiz->setValue(it.key(), it.value());
+ return true;
+}
+
+void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files)
+{
+ for (const JsonWizard::GeneratorFile &f : files) {
+ if (f.file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
+ Interpreter interpreter = m_interpreter.currentInterpreter();
+ Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()),
+ f.file.filePath().absoluteFilePath());
+ if (m_createVenv.value()) {
+ auto openProjectWithInterpreter = [f](const std::optional<Interpreter> &interpreter) {
+ if (!interpreter)
+ return;
+ Project *project = ProjectManager::projectWithProjectFilePath(f.file.filePath());
+ if (!project)
+ return;
+ if (Target *target = project->activeTarget()) {
+ if (RunConfiguration *rc = target->activeRunConfiguration()) {
+ if (auto interpreters = rc->aspect<InterpreterAspect>())
+ interpreters->setCurrentInterpreter(*interpreter);
+ }
+ }
+ };
+ PythonSettings::createVirtualEnvironment(m_venvPath.filePath(),
+ interpreter,
+ openProjectWithInterpreter,
+ project ? project->displayName()
+ : QString{});
+ }
+
+ if (project) {
+ project->addTargetForDefaultKit();
+ if (Target *target = project->activeTarget()) {
+ if (RunConfiguration *rc = target->activeRunConfiguration()) {
+ if (auto interpreters = rc->aspect<InterpreterAspect>()) {
+ interpreters->setCurrentInterpreter(interpreter);
+ project->saveSettings();
+ }
+ }
+ }
+ delete project;
+ }
+ }
+ }
+}
+
+void PythonWizardPage::updateInterpreters()
+{
+ m_interpreter.setDefaultInterpreter(PythonSettings::defaultInterpreter());
+ m_interpreter.updateInterpreters(PythonSettings::interpreters());
+}
+
+void PythonWizardPage::updateStateLabel()
+{
+ QTC_ASSERT(m_stateLabel, return);
+ if (m_createVenv.value()) {
+ if (PathChooser *pathChooser = m_venvPath.pathChooser()) {
+ if (!pathChooser->isValid()) {
+ m_stateLabel->show();
+ m_stateLabel->setText(pathChooser->errorMessage());
+ return;
+ }
+ }
+ }
+ m_stateLabel->hide();
+}
+
+} // namespace Python::Internal
+
diff --git a/src/plugins/python/pythonwizardpage.h b/src/plugins/python/pythonwizardpage.h
new file mode 100644
index 0000000000..1d605b2417
--- /dev/null
+++ b/src/plugins/python/pythonwizardpage.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <projectexplorer/jsonwizard/jsonwizard.h>
+#include <projectexplorer/jsonwizard/jsonwizardpagefactory.h>
+#include <projectexplorer/runconfigurationaspects.h>
+
+#include <utils/aspects.h>
+#include <utils/wizardpage.h>
+
+namespace Python::Internal {
+
+class PythonWizardPageFactory : public ProjectExplorer::JsonWizardPageFactory
+{
+public:
+ PythonWizardPageFactory();
+
+ Utils::WizardPage *create(ProjectExplorer::JsonWizard *wizard,
+ Utils::Id typeId,
+ const QVariant &data) override;
+ bool validateData(Utils::Id typeId, const QVariant &data, QString *errorMessage) override;
+};
+
+class PythonWizardPage : public Utils::WizardPage
+{
+public:
+ PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData, const int defaultPyside);
+ void initializePage() override;
+ bool validatePage() override;
+
+private:
+ void setupProject(const ProjectExplorer::JsonWizard::GeneratorFiles &files);
+ void updateInterpreters();
+ void updateStateLabel();
+
+ ProjectExplorer::InterpreterAspect m_interpreter;
+ Utils::SelectionAspect m_pySideVersion;
+ Utils::BoolAspect m_createVenv;
+ Utils::StringAspect m_venvPath;
+ Utils::InfoLabel *m_stateLabel = nullptr;
+};
+
+} // namespace Python::Internal
diff --git a/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp b/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
index 4f3d88907a..78e0634bc5 100644
--- a/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
+++ b/src/plugins/qbsprojectmanager/customqbspropertiesdialog.cpp
@@ -43,7 +43,7 @@ CustomQbsPropertiesDialog::CustomQbsPropertiesDialog(const QVariantMap &properti
m_removeButton = new QPushButton(Tr::tr("&Remove"));
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
m_propertiesTable,
diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
index 46433eca5a..e2d1ae9ee7 100644
--- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
+++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
@@ -107,7 +107,7 @@ static QStringList toolchainList(const ProjectExplorer::ToolChain *tc)
const Utils::Id type = tc->typeId();
if (type == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID
|| (type == Android::Constants::ANDROID_TOOLCHAIN_TYPEID
- && tc->compilerCommand().toString().contains("clang"))) {
+ && tc->compilerCommand().fileName().contains("clang"))) {
return {"clang", "llvm", "gcc"};
}
if (type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID
@@ -156,7 +156,7 @@ static QString architecture(const ProjectExplorer::Abi &targetAbi)
switch (targetAbi.architecture()) {
case ProjectExplorer::Abi::X86Architecture:
architecture.append(QLatin1Char('_'));
- // fall through
+ [[fallthrough]];
case ProjectExplorer::Abi::ArmArchitecture:
// ARM sub-architectures are currently not handled, which is kind of problematic
case ProjectExplorer::Abi::MipsArchitecture:
diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
index c5d4b62a0d..41ae75d8b9 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
+++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp
@@ -27,8 +27,8 @@
#include <qtsupport/qtkitinformation.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QCryptographicHash>
diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
index f8fad01ff7..794e134d5a 100644
--- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
+++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp
@@ -28,8 +28,8 @@
#include <utils/macroexpander.h>
#include <utils/outputformatter.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
@@ -63,7 +63,7 @@ public:
ArchitecturesAspect();
void setKit(const ProjectExplorer::Kit *kit) { m_kit = kit; }
- void addToLayout(Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
QStringList selectedArchitectures() const;
void setSelectedArchitectures(const QStringList& architectures);
bool isManagedByTarget() const { return m_isManagedByTarget; }
@@ -86,9 +86,9 @@ ArchitecturesAspect::ArchitecturesAspect()
setAllValues(m_abisToArchMap.keys());
}
-void ArchitecturesAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void ArchitecturesAspect::addToLayout(Layouting::LayoutItem &parent)
{
- MultiSelectionAspect::addToLayout(builder);
+ MultiSelectionAspect::addToLayout(parent);
const auto changeHandler = [this] {
const QtVersion *qtVersion = QtKitAspect::qtVersion(m_kit);
if (!qtVersion) {
@@ -658,25 +658,27 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step)
installDirChooser = new PathChooser(this);
installDirChooser->setExpectedKind(PathChooser::Directory);
- Layouting::Form builder;
- builder.addRow(m_qbsStep->m_buildVariant);
- builder.addRow(m_qbsStep->m_selectedAbis);
- builder.addRow(m_qbsStep->m_maxJobCount);
- builder.addRow({QbsProjectManager::Tr::tr("Properties:"), propertyEdit});
-
- builder.addRow(QbsProjectManager::Tr::tr("Flags:"));
- m_qbsStep->m_keepGoing->addToLayout(builder);
- m_qbsStep->m_showCommandLines->addToLayout(builder);
- m_qbsStep->m_forceProbes->addToLayout(builder);
-
- builder.addRow(QbsProjectManager::Tr::tr("Installation flags:"));
- m_qbsStep->m_install->addToLayout(builder);
- m_qbsStep->m_cleanInstallDir->addToLayout(builder);
- builder.addItem(defaultInstallDirCheckBox);
-
- builder.addRow({QbsProjectManager::Tr::tr("Installation directory:"), installDirChooser});
- builder.addRow(m_qbsStep->m_commandLine);
- builder.attachTo(this, Layouting::WithoutMargins);
+ using namespace Layouting;
+ Form {
+ m_qbsStep->m_buildVariant, br,
+ m_qbsStep->m_selectedAbis, br,
+ m_qbsStep->m_maxJobCount, br,
+ QbsProjectManager::Tr::tr("Properties:"), propertyEdit, br,
+
+ QbsProjectManager::Tr::tr("Flags:"),
+ m_qbsStep->m_keepGoing,
+ m_qbsStep->m_showCommandLines,
+ m_qbsStep->m_forceProbes, br,
+
+ QbsProjectManager::Tr::tr("Installation flags:"),
+ m_qbsStep->m_install,
+ m_qbsStep->m_cleanInstallDir,
+ defaultInstallDirCheckBox, br,
+
+ QbsProjectManager::Tr::tr("Installation directory:"), installDirChooser, br,
+ m_qbsStep->m_commandLine, br,
+ noMargin,
+ }.attachTo(this);
propertyEdit->setToolTip(QbsProjectManager::Tr::tr("Properties to pass to the project."));
defaultInstallDirCheckBox->setText(QbsProjectManager::Tr::tr("Use default location"));
diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp
index 27c875e868..939a03d9d9 100644
--- a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp
+++ b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp
@@ -81,7 +81,7 @@ void QbsInstallStep::doRun()
QJsonObject request;
request.insert("type", "install-project");
- request.insert("install-root", installRoot());
+ request.insert("install-root", installRoot().path());
request.insert("clean-install-root", m_cleanInstallRoot->value());
request.insert("keep-going", m_keepGoing->value());
request.insert("dry-run", m_dryRun->value());
@@ -102,10 +102,10 @@ void QbsInstallStep::doCancel()
m_session->cancelCurrentJob();
}
-QString QbsInstallStep::installRoot() const
+FilePath QbsInstallStep::installRoot() const
{
const QbsBuildStep * const bs = buildConfig()->qbsStep();
- return bs ? bs->installRoot().toString() : QString();
+ return bs ? bs->installRoot() : FilePath();
}
const QbsBuildConfiguration *QbsInstallStep::buildConfig() const
@@ -162,7 +162,7 @@ QWidget *QbsInstallStep::createConfigWidget()
{
auto widget = new QWidget;
- auto installRootValueLabel = new QLabel(installRoot());
+ auto installRootValueLabel = new QLabel(installRoot().toUserOutput());
auto commandLineKeyLabel = new QLabel(Tr::tr("Equivalent command line:"));
commandLineKeyLabel->setAlignment(Qt::AlignTop);
@@ -172,18 +172,15 @@ QWidget *QbsInstallStep::createConfigWidget()
commandLineTextEdit->setTextInteractionFlags(Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse);
commandLineTextEdit->setMinimumHeight(QFontMetrics(widget->font()).height() * 8);
- Layouting::Form builder;
- builder.addRow({Tr::tr("Install root:"), installRootValueLabel});
- builder.addRow(Tr::tr("Flags:"));
- m_dryRun->addToLayout(builder);
- m_keepGoing->addToLayout(builder);
- m_cleanInstallRoot->addToLayout(builder);
-
- builder.addRow({commandLineKeyLabel, commandLineTextEdit});
- builder.attachTo(widget);
+ using namespace Layouting;
+ Form {
+ Tr::tr("Install root:"), installRootValueLabel, br,
+ Tr::tr("Flags:"), m_dryRun, m_keepGoing, m_cleanInstallRoot, br,
+ commandLineKeyLabel, commandLineTextEdit
+ }.attachTo(widget);
const auto updateState = [this, commandLineTextEdit, installRootValueLabel] {
- installRootValueLabel->setText(installRoot());
+ installRootValueLabel->setText(installRoot().toUserOutput());
commandLineTextEdit->setPlainText(buildConfig()->equivalentCommandLine(stepData()));
};
diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.h b/src/plugins/qbsprojectmanager/qbsinstallstep.h
index 7eef7aa9e1..e0063da818 100644
--- a/src/plugins/qbsprojectmanager/qbsinstallstep.h
+++ b/src/plugins/qbsprojectmanager/qbsinstallstep.h
@@ -25,7 +25,7 @@ public:
QbsInstallStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
~QbsInstallStep() override;
- QString installRoot() const;
+ Utils::FilePath installRoot() const;
QbsBuildStepData stepData() const;
private:
diff --git a/src/plugins/qbsprojectmanager/qbskitinformation.cpp b/src/plugins/qbsprojectmanager/qbskitinformation.cpp
index 8bdd801261..b9cb79231d 100644
--- a/src/plugins/qbsprojectmanager/qbskitinformation.cpp
+++ b/src/plugins/qbsprojectmanager/qbskitinformation.cpp
@@ -35,11 +35,11 @@ private:
void makeReadOnly() override { m_changeButton->setEnabled(false); }
void refresh() override { m_contentLabel->setText(QbsKitAspect::representation(kit())); }
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_contentLabel);
- builder.addItem(m_contentLabel);
- builder.addItem(m_changeButton);
+ parent.addItem(m_contentLabel);
+ parent.addItem(m_changeButton);
}
void changeProperties()
diff --git a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
index 55ae7d8ac0..82e896e77a 100644
--- a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
+++ b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp
@@ -42,6 +42,10 @@ static FileType fileType(const QJsonObject &artifact)
return FileType::StateChart;
if (fileTags.contains("qt.qml.qml"))
return FileType::QML;
+ if (fileTags.contains("application"))
+ return FileType::App;
+ if (fileTags.contains("staticlibrary") || fileTags.contains("dynamiclibrary"))
+ return FileType::Lib;
return FileType::Unknown;
}
diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
index c5e658ba54..2d952513ab 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp
@@ -18,8 +18,8 @@
#include <qmljstools/qmljstoolsconstants.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCryptographicHash>
#include <QJSEngine>
@@ -223,7 +223,7 @@ QString QbsProfileManager::runQbsConfig(QbsConfigOp op, const QString &key, cons
const Utils::FilePath qbsConfigExe = QbsSettings::qbsConfigFilePath();
if (qbsConfigExe.isEmpty() || !qbsConfigExe.exists())
return {};
- Utils::QtcProcess qbsConfig;
+ Utils::Process qbsConfig;
qbsConfig.setCommand({qbsConfigExe, args});
qbsConfig.start();
if (!qbsConfig.waitForFinished(5000)) {
diff --git a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
index 64c73ed5f0..fb048f9475 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.cpp
@@ -26,8 +26,7 @@
using namespace ProjectExplorer;
-namespace QbsProjectManager {
-namespace Internal {
+namespace QbsProjectManager::Internal {
class ProfileTreeItem : public Utils::TypedTreeItem<ProfileTreeItem, ProfileTreeItem>
{
@@ -53,7 +52,6 @@ private:
class ProfileModel : public Utils::TreeModel<ProfileTreeItem>
{
- Q_OBJECT
public:
ProfileModel() : TreeModel(static_cast<QObject *>(nullptr))
{
@@ -91,13 +89,14 @@ public:
}
};
-class QbsProfilesSettingsWidget : public QWidget
+class QbsProfilesSettingsWidget : public Core::IOptionsPageWidget
{
- Q_OBJECT
public:
QbsProfilesSettingsWidget();
private:
+ void apply() final {}
+
void refreshKitsList();
void displayCurrentProfile();
@@ -112,19 +111,7 @@ QbsProfilesSettingsPage::QbsProfilesSettingsPage()
setId("Y.QbsProfiles");
setDisplayName(Tr::tr("Profiles"));
setCategory(Constants::QBS_SETTINGS_CATEGORY);
-}
-
-QWidget *QbsProfilesSettingsPage::widget()
-{
- if (!m_widget)
- m_widget = new QbsProfilesSettingsWidget;
- return m_widget;
-}
-
-void QbsProfilesSettingsPage::finish()
-{
- delete m_widget;
- m_widget = nullptr;
+ setWidgetCreator([] { return new QbsProfilesSettingsWidget; });
}
QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
@@ -133,7 +120,7 @@ QbsProfilesSettingsWidget::QbsProfilesSettingsWidget()
m_profileValueLabel = new QLabel;
m_propertiesView = new QTreeView;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
Tr::tr("Kit:"), m_kitsComboBox, br,
@@ -211,7 +198,4 @@ void QbsProfilesSettingsWidget::displayCurrentProfile()
}
}
-} // namespace Internal
-} // namespace QbsProjectManager
-
-#include "qbsprofilessettingspage.moc"
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h
index 4ffab9bce9..db63e32354 100644
--- a/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h
+++ b/src/plugins/qbsprojectmanager/qbsprofilessettingspage.h
@@ -5,22 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace QbsProjectManager {
-namespace Internal {
-class QbsProfilesSettingsWidget;
+namespace QbsProjectManager::Internal {
class QbsProfilesSettingsPage : public Core::IOptionsPage
{
public:
QbsProfilesSettingsPage();
-
-private:
- QWidget *widget() override;
- void apply() override { }
- void finish() override;
-
- QbsProfilesSettingsWidget *m_widget = nullptr;
};
-} // namespace Internal
-} // namespace QbsProjectManager
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp
index dc72660e7a..3b59618310 100644
--- a/src/plugins/qbsprojectmanager/qbsproject.cpp
+++ b/src/plugins/qbsprojectmanager/qbsproject.cpp
@@ -41,10 +41,10 @@
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <qtsupport/qtcppkitinfo.h>
@@ -483,7 +483,7 @@ void QbsBuildSystem::updateProjectNodes(const std::function<void ()> &continuati
if (continuation)
continuation();
});
- m_treeCreationWatcher->setFuture(runAsync(ProjectExplorerPlugin::sharedThreadPool(),
+ m_treeCreationWatcher->setFuture(Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
QThread::LowPriority, &QbsNodeTreeBuilder::buildTree,
project()->displayName(), project()->projectFilePath(), project()->projectDirectory(),
projectData()));
@@ -498,7 +498,7 @@ FilePath QbsBuildSystem::installRoot()
if (!step->enabled())
continue;
if (const auto qbsInstallStep = qobject_cast<const QbsInstallStep *>(step))
- return FilePath::fromUserInput(qbsInstallStep->installRoot());
+ return qbsInstallStep->installRoot();
}
}
const QbsBuildStep * const buildStep = m_buildConfiguration->qbsStep();
@@ -744,6 +744,7 @@ static void getExpandedCompilerFlags(QStringList &cFlags, QStringList &cxxFlags,
};
const QJsonValue &enableExceptions = getCppProp("enableExceptions");
const QJsonValue &enableRtti = getCppProp("enableRtti");
+ const QString warningLevel = getCppProp("warningLevel").toString();
QStringList commonFlags = arrayToStringList(getCppProp("platformCommonCompilerFlags"));
commonFlags << arrayToStringList(getCppProp("commonCompilerFlags"))
<< arrayToStringList(getCppProp("platformDriverFlags"))
@@ -773,6 +774,10 @@ static void getExpandedCompilerFlags(QStringList &cFlags, QStringList &cxxFlags,
if (!machineType.isEmpty())
commonFlags << ("-march=" + machineType);
}
+ if (warningLevel == "all")
+ commonFlags << "-Wall" << "-Wextra";
+ else if (warningLevel == "none")
+ commonFlags << "-w";
const QStringList targetOS = arrayToStringList(properties.value("qbs.targetOS"));
if (targetOS.contains("unix")) {
const QVariant positionIndependentCode = getCppProp("positionIndependentCode");
@@ -837,6 +842,10 @@ static void getExpandedCompilerFlags(QStringList &cFlags, QStringList &cxxFlags,
else if (exceptionModel == "externc")
commonFlags << "/EHs";
}
+ if (warningLevel == "all")
+ commonFlags << "/Wall";
+ else if (warningLevel == "none")
+ commonFlags << "/w";
cFlags = cxxFlags = commonFlags;
cFlags << "/TC";
cxxFlags << "/TP";
diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
index 01a58753a2..3245b609be 100644
--- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp
@@ -33,7 +33,7 @@
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtsupportconstants.h>
@@ -60,7 +60,7 @@ static Node *currentEditorNode()
static QbsProject *currentEditorProject()
{
Core::IDocument *doc = Core::EditorManager::currentDocument();
- return doc ? qobject_cast<QbsProject *>(SessionManager::projectForFile(doc->filePath())) : nullptr;
+ return doc ? qobject_cast<QbsProject *>(ProjectManager::projectForFile(doc->filePath())) : nullptr;
}
class QbsProjectManagerPluginPrivate
@@ -226,13 +226,13 @@ void QbsProjectManagerPlugin::initialize()
connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
this, &QbsProjectManagerPlugin::updateBuildActions);
- connect(SessionManager::instance(), &SessionManager::targetAdded,
+ connect(ProjectManager::instance(), &ProjectManager::targetAdded,
this, &QbsProjectManagerPlugin::targetWasAdded);
- connect(SessionManager::instance(), &SessionManager::targetRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::targetRemoved,
this, &QbsProjectManagerPlugin::updateBuildActions);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &QbsProjectManagerPlugin::updateReparseQbsAction);
- connect(SessionManager::instance(), &SessionManager::projectAdded,
+ connect(ProjectManager::instance(), &ProjectManager::projectAdded,
this, [this](Project *project) {
auto qbsProject = qobject_cast<QbsProject *>(project);
connect(project, &Project::anyParsingStarted,
@@ -283,7 +283,7 @@ void QbsProjectManagerPlugin::updateContextActions(Node *node)
void QbsProjectManagerPlugin::updateReparseQbsAction()
{
- auto project = qobject_cast<QbsProject *>(SessionManager::startupProject());
+ auto project = qobject_cast<QbsProject *>(ProjectManager::startupProject());
m_reparseQbs->setEnabled(project
&& !BuildManager::isBuilding(project)
&& project && project->activeTarget()
@@ -342,7 +342,7 @@ void QbsProjectManagerPlugin::projectChanged(QbsProject *project)
{
auto qbsProject = qobject_cast<QbsProject *>(project);
- if (!qbsProject || qbsProject == SessionManager::startupProject())
+ if (!qbsProject || qbsProject == ProjectManager::startupProject())
updateReparseQbsAction();
if (!qbsProject || qbsProject == ProjectTree::currentProject())
@@ -537,7 +537,7 @@ void QbsProjectManagerPlugin::reparseSelectedProject()
void QbsProjectManagerPlugin::reparseCurrentProject()
{
- reparseProject(dynamic_cast<QbsProject *>(SessionManager::startupProject()));
+ reparseProject(dynamic_cast<QbsProject *>(ProjectManager::startupProject()));
}
void QbsProjectManagerPlugin::reparseProject(QbsProject *project)
diff --git a/src/plugins/qbsprojectmanager/qbsprojectparser.cpp b/src/plugins/qbsprojectmanager/qbsprojectparser.cpp
index 1620b747ac..ee1aafdb6b 100644
--- a/src/plugins/qbsprojectmanager/qbsprojectparser.cpp
+++ b/src/plugins/qbsprojectmanager/qbsprojectparser.cpp
@@ -67,10 +67,10 @@ void QbsProjectParser::parse(const QVariantMap &config, const Environment &env,
request.insert("override-build-graph-data", true);
static const auto envToJson = [](const Environment &env) {
QJsonObject envObj;
- for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
- if (env.isEnabled(it))
- envObj.insert(env.key(it), env.value(it));
- }
+ env.forEachEntry([&](const QString &key, const QString &value, bool enabled) {
+ if (enabled)
+ envObj.insert(key, value);
+ });
return envObj;
};
request.insert("environment", envToJson(env));
diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp
index 06c605e842..13121cfb66 100644
--- a/src/plugins/qbsprojectmanager/qbssession.cpp
+++ b/src/plugins/qbsprojectmanager/qbssession.cpp
@@ -14,8 +14,8 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QEventLoop>
@@ -129,7 +129,7 @@ private:
class QbsSession::Private
{
public:
- QtcProcess *qbsProcess = nullptr;
+ Process *qbsProcess = nullptr;
PacketReader *packetReader = nullptr;
QJsonObject currentRequest;
QJsonObject projectData;
@@ -152,16 +152,16 @@ void QbsSession::initialize()
d->packetReader = new PacketReader(this);
- d->qbsProcess = new QtcProcess(this);
+ d->qbsProcess = new Process(this);
d->qbsProcess->setProcessMode(ProcessMode::Writer);
d->qbsProcess->setEnvironment(env);
- connect(d->qbsProcess, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(d->qbsProcess, &Process::readyReadStandardOutput, this, [this] {
d->packetReader->handleData(d->qbsProcess->readAllRawStandardOutput());
});
- connect(d->qbsProcess, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(d->qbsProcess, &Process::readyReadStandardError, this, [this] {
qCDebug(qbsPmLog) << "[qbs stderr]: " << d->qbsProcess->readAllRawStandardError();
});
- connect(d->qbsProcess, &QtcProcess::done, this, [this] {
+ connect(d->qbsProcess, &Process::done, this, [this] {
if (d->qbsProcess->result() == ProcessResult::StartFailed) {
d->eventLoop.exit(1);
setError(Error::QbsFailedToStart);
@@ -370,6 +370,7 @@ void QbsSession::insertRequestedModuleProperties(QJsonObject &request)
"cpp.useCxxPrecompiledHeader",
"cpp.useObjcPrecompiledHeader",
"cpp.useObjcxxPrecompiledHeader",
+ "cpp.warningLevel",
"qbs.architecture",
"qbs.architectures",
"qbs.sysroot",
diff --git a/src/plugins/qbsprojectmanager/qbssettings.cpp b/src/plugins/qbsprojectmanager/qbssettings.cpp
index 78eac2b308..91e4763bf2 100644
--- a/src/plugins/qbsprojectmanager/qbssettings.cpp
+++ b/src/plugins/qbsprojectmanager/qbssettings.cpp
@@ -12,7 +12,7 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCoreApplication>
#include <QCheckBox>
@@ -21,8 +21,7 @@
using namespace Utils;
-namespace QbsProjectManager {
-namespace Internal {
+namespace QbsProjectManager::Internal {
const char QBS_EXE_KEY[] = "QbsProjectManager/QbsExecutable";
const char QBS_DEFAULT_INSTALL_DIR_KEY[] = "QbsProjectManager/DefaultInstallDir";
@@ -32,7 +31,7 @@ static QString getQbsVersion(const FilePath &qbsExe)
{
if (qbsExe.isEmpty() || !qbsExe.exists())
return {};
- QtcProcess qbsProc;
+ Process qbsProc;
qbsProc.setCommand({qbsExe, {"--version"}});
qbsProc.start();
if (!qbsProc.waitForFinished(5000) || qbsProc.exitCode() != 0)
@@ -142,10 +141,10 @@ void QbsSettings::storeSettings() const
s->setValue(USE_CREATOR_SETTINGS_KEY, m_settings.useCreatorSettings);
}
-class QbsSettingsPage::SettingsWidget : public QWidget
+class QbsSettingsPageWidget : public Core::IOptionsPageWidget
{
public:
- SettingsWidget()
+ QbsSettingsPageWidget()
{
m_qbsExePathChooser.setExpectedKind(PathChooser::ExistingCommand);
m_qbsExePathChooser.setFilePath(QbsSettings::qbsExecutableFilePath());
@@ -166,7 +165,7 @@ public:
});
}
- void apply()
+ void apply() final
{
QbsSettingsData settings = QbsSettings::rawSettingsData();
if (m_qbsExePathChooser.filePath() != QbsSettings::qbsExecutableFilePath())
@@ -197,26 +196,7 @@ QbsSettingsPage::QbsSettingsPage()
setCategory(Constants::QBS_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr(Constants::QBS_SETTINGS_TR_CATEGORY));
setCategoryIconPath(":/qbsprojectmanager/images/settingscategory_qbsprojectmanager.png");
+ setWidgetCreator([] { return new QbsSettingsPageWidget; });
}
-QWidget *QbsSettingsPage::widget()
-{
- if (!m_widget)
- m_widget = new SettingsWidget;
- return m_widget;
-
-}
-
-void QbsSettingsPage::apply()
-{
- if (m_widget)
- m_widget->apply();
-}
-
-void QbsSettingsPage::finish()
-{
- delete m_widget;
-}
-
-} // namespace Internal
-} // namespace QbsProjectManager
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qbsprojectmanager/qbssettings.h b/src/plugins/qbsprojectmanager/qbssettings.h
index 8b30e4b247..a91a905965 100644
--- a/src/plugins/qbsprojectmanager/qbssettings.h
+++ b/src/plugins/qbsprojectmanager/qbssettings.h
@@ -4,16 +4,15 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/fileutils.h>
-#include <QObject>
-#include <QPointer>
+#include <utils/filepath.h>
+
#include <QVersionNumber>
-namespace QbsProjectManager {
-namespace Internal {
+namespace QbsProjectManager::Internal {
-class QbsSettingsData {
+class QbsSettingsData
+{
public:
Utils::FilePath qbsExecutableFilePath;
QString defaultInstallDirTemplate;
@@ -51,18 +50,8 @@ private:
class QbsSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
public:
QbsSettingsPage();
-
-private:
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
- class SettingsWidget;
- QPointer<SettingsWidget> m_widget;
};
-} // namespace Internal
-} // namespace QbsProjectManager
+} // QbsProjectManager::Internal
diff --git a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp
index ebf266d701..ff74701cf7 100644
--- a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp
+++ b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp
@@ -178,7 +178,6 @@ DetailsPage::DetailsPage(AddLibraryWizard *parent)
: QWizardPage(parent), m_libraryWizard(parent)
{
m_libraryDetailsWidget = new LibraryDetailsWidget(this);
- resize(456, 438);
PathChooser * const libPathChooser = m_libraryDetailsWidget->libraryPathChooser;
libPathChooser->setHistoryCompleter("Qmake.LibDir.History");
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp
index 05c7c25d78..22958e0819 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp
@@ -21,7 +21,7 @@ ClassDefinition::ClassDefinition(QWidget *parent) :
QTabWidget(parent),
m_domXmlChanged(false)
{
- using namespace Utils::Layouting;
+ using namespace Layouting;
// "Sources" tab
auto sourceTab = new QWidget;
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp
index 851f07ce71..d0885dfc83 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetpluginwizardpage.cpp
@@ -34,7 +34,7 @@ CustomWidgetPluginWizardPage::CustomWidgetPluginWizardPage(QWidget *parent) :
m_pluginNameEdit = new QLineEdit;
m_resourceFileEdit = new QLineEdit(Tr::tr("icons.qrc"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Specify the properties of the plugin library and the collection class."),
Space(10),
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp
index 8265f0a8b4..20fe10ac13 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwidgetswizardpage.cpp
@@ -39,7 +39,7 @@ CustomWidgetWidgetsWizardPage::CustomWidgetWidgetsWizardPage(QWidget *parent) :
dummy->setEnabled(false);
m_tabStackLayout->addWidget(dummy);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Tr::tr("Specify the list of custom widgets and their properties."),
Space(10),
diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp
index ab3f59d851..28e67ec3ec 100644
--- a/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp
+++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp
@@ -49,17 +49,10 @@ static Core::GeneratedFile generateIconFile(const FilePath &source,
return rc;
}
-static QString qt4PluginExport(const QString &pluginName, const QString &pluginClassName)
-{
- return QLatin1String("#if QT_VERSION < 0x050000\nQ_EXPORT_PLUGIN2(")
- + pluginName + QLatin1String(", ") + pluginClassName
- + QLatin1String(")\n#endif // QT_VERSION < 0x050000");
-}
-
static QString qt5PluginMetaData(const QString &interfaceName)
{
- return QLatin1String("#if QT_VERSION >= 0x050000\n Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.")
- + interfaceName + QLatin1String("\")\n#endif // QT_VERSION >= 0x050000");
+ return QLatin1String(" Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.")
+ + interfaceName + QLatin1String("\")");
}
QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationParameters& p, const PluginOptions &options,
@@ -121,10 +114,8 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara
sm.insert(QLatin1String("WIDGET_TOOLTIP"), cStringQuote(wo.toolTip));
sm.insert(QLatin1String("WIDGET_WHATSTHIS"), cStringQuote(wo.whatsThis));
sm.insert(QLatin1String("WIDGET_ISCONTAINER"), wo.isContainer ? QLatin1String("true") : QLatin1String("false"));
- sm.insert(QLatin1String("WIDGET_DOMXML"), cStringQuote(wo.domXml));
- sm.insert(QLatin1String("SINGLE_PLUGIN_EXPORT"),
- options.widgetOptions.count() == 1 ?
- qt4PluginExport(options.pluginName, wo.pluginClassName) : QString());
+ sm.insert(QLatin1String("WIDGET_DOMXML"), QLatin1String("R\"(")
+ + wo.domXml.trimmed() + QLatin1String(")\""));
const QString pluginSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.cpp"), sm, errorMessage);
if (pluginSourceContents.isEmpty())
@@ -239,7 +230,6 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara
options.collectionHeaderFile +
QLatin1String("\""));
sm.insert(QLatin1String("PLUGIN_ADDITIONS"), pluginAdditions);
- sm.insert(QLatin1String("COLLECTION_PLUGIN_EXPORT"), qt4PluginExport(options.pluginName, options.collectionClassName));
const QString collectionSourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.cpp"), sm, errorMessage);
if (collectionSourceFileContents.isEmpty())
return QList<Core::GeneratedFile>();
diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
index b1427c84ba..800d7350c1 100644
--- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
+++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
@@ -3,24 +3,25 @@
#include "librarydetailscontroller.h"
-#include "qmakebuildconfiguration.h"
#include "qmakeparsernodes.h"
#include "qmakeproject.h"
#include "qmakeprojectmanagertr.h"
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QCheckBox>
#include <QComboBox>
#include <QDir>
#include <QFileInfo>
#include <QGroupBox>
+#include <QLabel>
#include <QRadioButton>
#include <QTextStream>
@@ -868,7 +869,7 @@ QString PackageLibraryDetailsController::snippet() const
bool PackageLibraryDetailsController::isLinkPackageGenerated() const
{
- const Project *project = SessionManager::projectForFile(proFile());
+ const Project *project = ProjectManager::projectForFile(proFile());
if (!project)
return false;
@@ -1016,7 +1017,7 @@ void InternalLibraryDetailsController::updateProFile()
libraryDetailsWidget()->libraryComboBox->clear();
const QmakeProject *project
- = dynamic_cast<QmakeProject *>(SessionManager::projectForFile(proFile()));
+ = dynamic_cast<QmakeProject *>(ProjectManager::projectForFile(proFile()));
if (!project)
return;
@@ -1104,7 +1105,7 @@ QString InternalLibraryDetailsController::snippet() const
// the build directory of the active build configuration
QDir rootBuildDir = rootDir; // If the project is unconfigured use the project dir
- if (const Project *project = SessionManager::projectForFile(proFile())) {
+ if (const Project *project = ProjectManager::projectForFile(proFile())) {
if (ProjectExplorer::Target *t = project->activeTarget())
if (ProjectExplorer::BuildConfiguration *bc = t->activeBuildConfiguration())
rootBuildDir.setPath(bc->buildDirectory().toString());
diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp
index e9d8c2878e..990fd0ff41 100644
--- a/src/plugins/qmakeprojectmanager/makefileparse.cpp
+++ b/src/plugins/qmakeprojectmanager/makefileparse.cpp
@@ -5,7 +5,7 @@
#include <qtsupport/qtversionmanager.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QDebug>
#include <QDir>
@@ -359,11 +359,12 @@ void MakeFileParse::parseCommandLine(const QString &command, const QString &proj
// Unit tests:
#ifdef WITH_TESTS
-# include <QTest>
-# include "qmakeprojectmanagerplugin.h"
+#include "qmakeprojectmanagerplugin.h"
-# include "projectexplorer/outputparser_test.h"
+#include <projectexplorer/outputparser_test.h>
+
+#include <QTest>
using namespace QmakeProjectManager::Internal;
using namespace ProjectExplorer;
@@ -479,8 +480,8 @@ void QmakeProjectManagerPlugin::testMakefileParser()
MakeFileParse parser("/tmp/something", MakeFileParse::Mode::FilterKnownConfigValues);
parser.parseCommandLine(command, project);
- QCOMPARE(ProcessArgs::splitArgs(parser.unparsedArguments()),
- ProcessArgs::splitArgs(unparsedArguments));
+ QCOMPARE(ProcessArgs::splitArgs(parser.unparsedArguments(), HostOsInfo::hostOs()),
+ ProcessArgs::splitArgs(unparsedArguments, HostOsInfo::hostOs()));
QCOMPARE(parser.effectiveBuildConfig({}), effectiveBuildConfig);
const QMakeStepConfig qmsc = parser.config();
diff --git a/src/plugins/qmakeprojectmanager/profileeditor.cpp b/src/plugins/qmakeprojectmanager/profileeditor.cpp
index 810d5d4f2e..b492112db3 100644
--- a/src/plugins/qmakeprojectmanager/profileeditor.cpp
+++ b/src/plugins/qmakeprojectmanager/profileeditor.cpp
@@ -7,18 +7,22 @@
#include "profilehighlighter.h"
#include "profilehoverhandler.h"
#include "qmakenodes.h"
-#include "qmakeproject.h"
#include "qmakeprojectmanagerconstants.h"
#include <coreplugin/coreplugintr.h>
+
#include <extensionsystem/pluginmanager.h>
+
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+
#include <qtsupport/qtsupportconstants.h>
+
#include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h>
+
#include <utils/fsengine/fileiconprovider.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
@@ -65,7 +69,7 @@ QString ProFileEditorWidget::checkForPrfFile(const QString &baseName) const
const QmakePriFileNode *projectNode = nullptr;
// FIXME: Remove this check once project nodes are fully "static".
- for (const Project * const project : SessionManager::projects()) {
+ for (const Project * const project : ProjectManager::projects()) {
static const auto isParsing = [](const Project *project) {
for (const Target * const t : project->targets()) {
for (const BuildConfiguration * const bc : t->buildConfigurations()) {
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index 779d175b80..173443958d 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -37,8 +37,8 @@
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QInputDialog>
@@ -160,7 +160,7 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Id id)
this, &QmakeBuildConfiguration::updateProblemLabel);
connect(this, &QmakeBuildConfiguration::qmakeBuildConfigurationChanged,
this, &QmakeBuildConfiguration::updateProblemLabel);
- connect(&QmakeSettings::instance(), &QmakeSettings::settingsChanged,
+ connect(&settings(), &AspectContainer::changed,
this, &QmakeBuildConfiguration::updateProblemLabel);
connect(target, &Target::parsingFinished, this, &QmakeBuildConfiguration::updateProblemLabel);
connect(target, &Target::kitChanged, this, &QmakeBuildConfiguration::updateProblemLabel);
@@ -267,7 +267,7 @@ void QmakeBuildConfiguration::updateProblemLabel()
}
}
- const bool unalignedBuildDir = QmakeSettings::warnAgainstUnalignedBuildDir()
+ const bool unalignedBuildDir = settings().warnAgainstUnalignedBuildDir()
&& !isBuildDirAtSafeLocation();
if (unalignedBuildDir)
allGood = false;
@@ -426,7 +426,7 @@ bool QmakeBuildConfiguration::runSystemFunction() const
return true;
if (runSystem == TriState::Disabled)
return false;
- return QmakeSettings::runSystemFunction();
+ return settings().runSystemFunction();
}
QStringList QmakeBuildConfiguration::configCommandLineArguments() const
@@ -753,7 +753,7 @@ QmakeBuildConfigurationFactory::QmakeBuildConfigurationFactory()
Tasks issues;
if (version)
issues << version->reportIssues(projectPath, buildDir);
- if (QmakeSettings::warnAgainstUnalignedBuildDir()
+ if (settings().warnAgainstUnalignedBuildDir()
&& !QmakeBuildConfiguration::isBuildDirAtSafeLocation(
projectPath.absolutePath(), buildDir.absoluteFilePath())) {
issues.append(BuildSystemTask(Task::Warning,
diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp
index 06f2011244..c413758048 100644
--- a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp
@@ -40,10 +40,10 @@ public:
~QmakeKitAspectWidget() override { delete m_lineEdit; }
private:
- void addToLayout(Layouting::LayoutBuilder &builder) override
+ void addToLayout(Layouting::LayoutItem &parent) override
{
addMutableAction(m_lineEdit);
- builder.addItem(m_lineEdit);
+ parent.addItem(m_lineEdit);
}
void makeReadOnly() override { m_lineEdit->setEnabled(false); }
diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
index 0fdaecc3db..e72d346758 100644
--- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp
@@ -22,7 +22,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/xcodebuildparser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/variablechooser.h>
#include <QDir>
@@ -222,7 +222,7 @@ void QmakeMakeStep::doRun()
void QmakeMakeStep::finish(ProcessResult result)
{
if (!isSuccess(result) && !isCanceled() && m_unalignedBuildDir
- && QmakeSettings::warnAgainstUnalignedBuildDir()) {
+ && settings().warnAgainstUnalignedBuildDir()) {
const QString msg = Tr::tr("The build directory is not at the same level as the source "
"directory, which could be the reason for the build failure.");
emit addTask(BuildSystemTask(Task::Warning, msg));
diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
index 4f59d223ce..144a9cc0c9 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
@@ -115,9 +115,7 @@ bool QmakeBuildSystem::supportsAction(Node *context, ProjectAction action, const
const FolderNode *folder = node->asFolderNode();
if (folder) {
FilePaths list;
- const auto folderNodes = folder->folderNodes();
- for (FolderNode *f : folderNodes)
- list << f->filePath();
+ folder->forEachFolderNode([&](FolderNode *f) { list << f->filePath(); });
if (n->deploysFolder(FileUtils::commonPath(list).toString()))
addExistingFiles = false;
}
diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.h b/src/plugins/qmakeprojectmanager/qmakenodes.h
index 778f84b64f..7dc1133fd0 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakenodes.h
@@ -12,7 +12,6 @@
namespace QmakeProjectManager {
class QmakeProFileNode;
-class QmakeProject;
// Implements ProjectNode for qmake .pri files
class QMAKEPROJECTMANAGER_EXPORT QmakePriFileNode : public ProjectExplorer::ProjectNode
diff --git a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp
index f261594e59..528b18c95e 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp
@@ -195,14 +195,29 @@ static void createTree(QmakeBuildSystem *buildSystem,
fileNode->setEnabled(fn.second == FileOrigin::ExactParse);
vfolder->addNestedNode(std::move(fileNode));
}
- for (FolderNode *fn : vfolder->folderNodes())
- fn->compress();
+ vfolder->forEachFolderNode([](FolderNode *fn) { fn->compress(); });
}
node->addNode(std::move(vfolder));
}
}
- if (!generatedFiles.empty()) {
+ FileType targetFileType = FileType::Unknown;
+ FilePath targetBinary;
+ if (proFile && proFile->targetInformation().valid) {
+ if (proFile->projectType() == ProjectType::ApplicationTemplate) {
+ targetFileType = FileType::App;
+ targetBinary = buildSystem->executableFor(proFile);
+ } else if (proFile->projectType() == ProjectType::SharedLibraryTemplate
+ || proFile->projectType() == ProjectType::StaticLibraryTemplate) {
+ targetFileType = FileType::Lib;
+ const FilePaths libs = Utils::sorted(buildSystem->allLibraryTargetFiles(proFile),
+ [](const FilePath &fp1, const FilePath &fp2) {
+ return fp1.fileName().length() < fp2.fileName().length(); });
+ if (!libs.isEmpty())
+ targetBinary = libs.last(); // Longest file name is the one that's not a symlink.
+ }
+ }
+ if (!generatedFiles.empty() || !targetBinary.isEmpty()) {
QTC_CHECK(proFile);
const FilePath baseDir = generatedFiles.size() == 1 ? generatedFiles.first().parentDir()
: buildSystem->buildDir(proFile->filePath());
@@ -214,6 +229,11 @@ static void createTree(QmakeBuildSystem *buildSystem,
fileNode->setIsGenerated(true);
genFolder->addNestedNode(std::move(fileNode));
}
+ if (!targetBinary.isEmpty()) {
+ auto targetFileNode = std::make_unique<FileNode>(targetBinary, targetFileType);
+ targetFileNode->setIsGenerated(true);
+ genFolder->addNestedNode(std::move(targetFileNode));
+ }
node->addNode(std::move(genFolder));
}
diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.cpp b/src/plugins/qmakeprojectmanager/qmakeparser.cpp
index e95b403bff..2cb9d941c1 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparser.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparser.cpp
@@ -71,11 +71,12 @@ OutputLineParser::Result QMakeParser::handleLine(const QString &line, OutputForm
// Unit tests:
#ifdef WITH_TESTS
-# include <QTest>
-# include "qmakeprojectmanagerplugin.h"
+#include "qmakeprojectmanagerplugin.h"
-# include "projectexplorer/outputparser_test.h"
+#include <projectexplorer/outputparser_test.h>
+
+#include <QTest>
using namespace QmakeProjectManager::Internal;
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index e15fadb46d..31a55faab0 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -30,10 +30,11 @@
#include <utils/QtConcurrentTools>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesystemwatcher.h>
#include <utils/mimeutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -1284,9 +1285,9 @@ void QmakeProFile::asyncUpdate()
if (!includedInExactParse())
m_readerExact->setExact(false);
QmakeEvalInput input = evalInput();
- QFuture<QmakeEvalResultPtr> future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
- QThread::LowestPriority,
- &QmakeProFile::asyncEvaluate, this, input);
+ QFuture<QmakeEvalResultPtr> future = Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
+ QThread::LowestPriority,
+ &QmakeProFile::asyncEvaluate, this, input);
m_parseFutureWatcher->setFuture(future);
}
@@ -1630,9 +1631,9 @@ QmakeEvalResultPtr QmakeProFile::evaluate(const QmakeEvalInput &input)
return result;
}
-void QmakeProFile::asyncEvaluate(QFutureInterface<QmakeEvalResultPtr> &fi, QmakeEvalInput input)
+void QmakeProFile::asyncEvaluate(QPromise<QmakeEvalResultPtr> &promise, QmakeEvalInput input)
{
- fi.reportResult(evaluate(input));
+ promise.addResult(evaluate(input));
}
bool sortByParserNodes(Node *a, Node *b)
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
index 34c4958564..9296addfa8 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
@@ -336,7 +336,7 @@ private:
static Internal::QmakeEvalResultPtr evaluate(const Internal::QmakeEvalInput &input);
void applyEvaluate(const Internal::QmakeEvalResultPtr &parseResult);
- void asyncEvaluate(QFutureInterface<Internal::QmakeEvalResultPtr> &fi,
+ void asyncEvaluate(QPromise<Internal::QmakeEvalResultPtr> &promise,
Internal::QmakeEvalInput input);
void cleanupProFileReaders();
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 6d84018725..2b73c154f6 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -40,15 +40,16 @@
#include <proparser/qmakevfs.h>
#include <proparser/qmakeglobals.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
+
#include <qtsupport/profilereader.h>
#include <qtsupport/qtcppkitinfo.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <utils/async.h>
+#include <utils/process.h>
#include <QDebug>
#include <QDir>
@@ -335,7 +336,7 @@ void QmakeBuildSystem::updateCppCodeModel()
rpp.setBuildTargetType(BuildTargetType::Unknown);
break;
}
- const QString includeFileBaseDir = pro->sourceDir().toString();
+ const FilePath includeFileBaseDir = pro->sourceDir();
QStringList cxxArgs = pro->variableValue(Variable::CppFlags);
QStringList cArgs = pro->variableValue(Variable::CFlags);
@@ -776,32 +777,25 @@ Tasks QmakeProject::projectIssues(const Kit *k) const
}
// Find the folder that contains a file with a certain name (recurse down)
-static FolderNode *folderOf(FolderNode *in, const FilePath &fileName)
+static FolderNode *folderOf(FolderNode *in, const FilePath &filePath)
{
- const QList<FileNode*> fileNodeList = in->fileNodes();
- for (FileNode *fn : fileNodeList) {
- if (fn->filePath() == fileName)
- return in;
- }
- const QList<FolderNode *> folderNodeList = in->folderNodes();
- for (FolderNode *folder : folderNodeList) {
- if (FolderNode *pn = folderOf(folder, fileName))
- return pn;
- }
- return {};
+ if (in->findChildFileNode([&filePath](FileNode *fn) { return fn->filePath() == filePath; }))
+ return in;
+
+ return in->findChildFolderNode([&filePath](FolderNode *folder) {
+ return folderOf(folder, filePath);
+ });
}
// Find the QmakeProFileNode that contains a certain file.
// First recurse down to folder, then find the pro-file.
-static FileNode *fileNodeOf(FolderNode *in, const FilePath &fileName)
-{
- for (FolderNode *folder = folderOf(in, fileName); folder; folder = folder->parentFolderNode()) {
- if (auto *proFile = dynamic_cast<QmakeProFileNode *>(folder)) {
- const QList<FileNode*> fileNodeList = proFile->fileNodes();
- for (FileNode *fileNode : fileNodeList) {
- if (fileNode->filePath() == fileName)
- return fileNode;
- }
+static FileNode *fileNodeOf(FolderNode *in, const FilePath &filePath)
+{
+ for (FolderNode *folder = folderOf(in, filePath); folder; folder = folder->parentFolderNode()) {
+ if (auto proFile = dynamic_cast<QmakeProFileNode *>(folder)) {
+ return proFile->findChildFileNode([&filePath](FileNode *fn) {
+ return fn->filePath() == filePath;
+ });
}
}
return nullptr;
@@ -872,9 +866,9 @@ QtSupport::ProFileReader *QmakeBuildSystem::createProFileReader(const QmakeProFi
rootProFileName,
deviceRoot());
- Environment::const_iterator eit = env.constBegin(), eend = env.constEnd();
- for (; eit != eend; ++eit)
- m_qmakeGlobals->environment.insert(env.key(eit), env.expandedValueForKey(env.key(eit)));
+ env.forEachEntry([&](const QString &key, const QString &value, bool) {
+ m_qmakeGlobals->environment.insert(key, env.expandVariables(value));
+ });
m_qmakeGlobals->setCommandLineArguments(rootProFileName, qmakeArgs);
m_qmakeGlobals->runSystemFunction = bc->runSystemFunction();
@@ -923,9 +917,9 @@ const FilePath &QmakeBuildSystem::qmakeSysroot() const
void QmakeBuildSystem::destroyProFileReader(QtSupport::ProFileReader *reader)
{
// The ProFileReader destructor is super expensive (but thread-safe).
- const auto deleteFuture = runAsync(ProjectExplorerPlugin::sharedThreadPool(), QThread::LowestPriority,
- [reader] { delete reader; });
- onFinished(deleteFuture, this, [this](const QFuture<void> &) {
+ const auto deleteFuture = Utils::asyncRun(ProjectExplorerPlugin::sharedThreadPool(),
+ [reader] { delete reader; });
+ Utils::onFinished(deleteFuture, this, [this](const QFuture<void> &) {
if (!--m_qmakeGlobalsRefCnt) {
deregisterFromCacheManager();
m_qmakeGlobals.reset();
@@ -1309,15 +1303,13 @@ static FilePath destDirFor(const TargetInformation &ti)
return ti.destDir;
}
-void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentData &deploymentData)
+FilePaths QmakeBuildSystem::allLibraryTargetFiles(const QmakeProFile *file) const
{
- const QString targetPath = file->installsList().targetPath;
- if (targetPath.isEmpty())
- return;
const ToolChain *const toolchain = ToolChainKitAspect::cxxToolChain(kit());
if (!toolchain)
- return;
+ return {};
+ FilePaths libs;
TargetInformation ti = file->targetInformation();
QString targetFileName = ti.target;
const QStringList config = file->variableValue(Variable::Config);
@@ -1337,7 +1329,7 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
}
targetFileName += targetVersionExt + QLatin1Char('.');
targetFileName += QLatin1String(isStatic ? "lib" : "dll");
- deploymentData.addFile(destDirFor(ti) / targetFileName, targetPath);
+ libs << FilePath::fromString(targetFileName);
break;
}
case Abi::DarwinOS: {
@@ -1357,10 +1349,10 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
targetFileName += majorVersion;
}
targetFileName += QLatin1Char('.');
- targetFileName += file->singleVariableValue(isStatic
- ? Variable::StaticLibExtension : Variable::ShLibExtension);
+ targetFileName += file->singleVariableValue(isStatic ? Variable::StaticLibExtension
+ : Variable::ShLibExtension);
}
- deploymentData.addFile(destDir / targetFileName, targetPath);
+ libs << destDir / targetFileName;
break;
}
case Abi::LinuxOS:
@@ -1372,10 +1364,10 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
targetFileName += QLatin1Char('.');
if (isStatic) {
- targetFileName += QLatin1Char('a');
+ libs << destDirFor(ti) / (targetFileName + QLatin1Char('a'));
} else {
targetFileName += QLatin1String("so");
- deploymentData.addFile(destDirFor(ti) / targetFileName, targetPath);
+ libs << destDirFor(ti) / targetFileName;
if (nameIsVersioned) {
QString version = file->singleVariableValue(Variable::Version);
if (version.isEmpty())
@@ -1386,9 +1378,7 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
targetFileName += QLatin1Char('.');
while (!versionComponents.isEmpty()) {
const QString versionString = versionComponents.join(QLatin1Char('.'));
- deploymentData.addFile(destDirFor(ti).pathAppended(targetFileName
- + versionString),
- targetPath);
+ libs << destDirFor(ti).pathAppended(targetFileName + versionString);
versionComponents.removeLast();
}
}
@@ -1397,6 +1387,18 @@ void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentDa
default:
break;
}
+
+ return libs;
+}
+
+void QmakeBuildSystem::collectLibraryData(const QmakeProFile *file, DeploymentData &deploymentData)
+{
+ const QString targetPath = file->installsList().targetPath;
+ if (!targetPath.isEmpty()) {
+ const FilePaths libs = allLibraryTargetFiles(file);
+ for (const FilePath &lib : libs)
+ deploymentData.addFile(lib, targetPath);
+ }
}
static FilePath getFullPathOf(const QmakeProFile *pro, Variable variable,
@@ -1591,12 +1593,12 @@ void QmakeBuildSystem::runGenerator(Utils::Id id)
showError(Tr::tr("Cannot create output directory \"%1\"").arg(outDir.toUserOutput()));
return;
}
- const auto proc = new QtcProcess(this);
- connect(proc, &QtcProcess::done, proc, &QtcProcess::deleteLater);
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [proc] {
+ const auto proc = new Process(this);
+ connect(proc, &Process::done, proc, &Process::deleteLater);
+ connect(proc, &Process::readyReadStandardOutput, this, [proc] {
Core::MessageManager::writeFlashing(QString::fromLocal8Bit(proc->readAllRawStandardOutput()));
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [proc] {
+ connect(proc, &Process::readyReadStandardError, this, [proc] {
Core::MessageManager::writeDisrupting(QString::fromLocal8Bit(proc->readAllRawStandardError()));
});
proc->setWorkingDirectory(outDir);
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h
index 6615baaaca..3097ae7b57 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.h
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.h
@@ -106,6 +106,7 @@ public:
void collectData(const QmakeProFile *file, ProjectExplorer::DeploymentData &deploymentData);
void collectApplicationData(const QmakeProFile *file,
ProjectExplorer::DeploymentData &deploymentData);
+ Utils::FilePaths allLibraryTargetFiles(const QmakeProFile *file) const;
void collectLibraryData(const QmakeProFile *file,
ProjectExplorer::DeploymentData &deploymentData);
void startAsyncTimer(QmakeProFile::AsyncUpdateDelay delay);
diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
index 8bcbe2027c..1a929bf9a4 100644
--- a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp
@@ -23,8 +23,8 @@
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp
index e557615507..d89393909b 100644
--- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp
@@ -29,7 +29,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -80,7 +80,7 @@ public:
ProFileEditorFactory profileEditorFactory;
- QmakeSettingsPage settingsPage;
+ QmakeSettings settings;
QmakeProject *m_previousStartupProject = nullptr;
Target *m_previousTarget = nullptr;
@@ -244,7 +244,7 @@ void QmakeProjectManagerPlugin::initialize()
connect(BuildManager::instance(), &BuildManager::buildStateChanged,
d, &QmakeProjectManagerPluginPrivate::buildStateChanged);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
d, &QmakeProjectManagerPluginPrivate::projectChanged);
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
d, &QmakeProjectManagerPluginPrivate::projectChanged);
@@ -294,7 +294,7 @@ void QmakeProjectManagerPluginPrivate::projectChanged()
if (ProjectTree::currentProject())
m_previousStartupProject = qobject_cast<QmakeProject *>(ProjectTree::currentProject());
else
- m_previousStartupProject = qobject_cast<QmakeProject *>(SessionManager::startupProject());
+ m_previousStartupProject = qobject_cast<QmakeProject *>(ProjectManager::startupProject());
if (m_previousStartupProject) {
connect(m_previousStartupProject, &Project::activeTargetChanged,
@@ -366,7 +366,7 @@ void QmakeProjectManagerPluginPrivate::addLibraryImpl(const FilePath &filePath,
void QmakeProjectManagerPluginPrivate::runQMake()
{
- runQMakeImpl(SessionManager::startupProject(), nullptr);
+ runQMakeImpl(ProjectManager::startupProject(), nullptr);
}
void QmakeProjectManagerPluginPrivate::runQMakeContextMenu()
@@ -411,7 +411,7 @@ void QmakeProjectManagerPluginPrivate::buildFile()
FileNode *node = n ? n->asFileNode() : nullptr;
if (!node)
return;
- Project *project = SessionManager::projectForFile(file);
+ Project *project = ProjectManager::projectForFile(file);
if (!project)
return;
Target *target = project->activeTarget();
@@ -563,7 +563,7 @@ void QmakeProjectManagerPluginPrivate::enableBuildFileMenus(const FilePath &file
bool enabled = false;
if (Node *node = ProjectTree::nodeForFile(file)) {
- if (Project *project = SessionManager::projectForFile(file)) {
+ if (Project *project = ProjectManager::projectForFile(file)) {
if (const FileNode *fileNode = node->asFileNode()) {
const FileType type = fileNode->fileType();
visible = qobject_cast<QmakeProject *>(project)
diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.cpp b/src/plugins/qmakeprojectmanager/qmakesettings.cpp
index a9e1de07c3..292851e444 100644
--- a/src/plugins/qmakeprojectmanager/qmakesettings.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakesettings.cpp
@@ -4,105 +4,64 @@
#include "qmakesettings.h"
#include "qmakeprojectmanagertr.h"
-#include <coreplugin/icore.h>
-
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
-#include <QXmlStreamWriter>
-
using namespace Utils;
-namespace QmakeProjectManager {
-namespace Internal {
+namespace QmakeProjectManager::Internal {
-QmakeSettings::QmakeSettings()
-{
- setAutoApply(false);
+static QmakeSettings *theSettings;
- registerAspect(&m_warnAgainstUnalignedBuildDir);
- m_warnAgainstUnalignedBuildDir.setSettingsKey("QmakeProjectManager/WarnAgainstUnalignedBuildDir");
- m_warnAgainstUnalignedBuildDir.setDefaultValue(HostOsInfo::isWindowsHost());
- m_warnAgainstUnalignedBuildDir.setLabelText(Tr::tr("Warn if a project's source and "
- "build directories are not at the same level"));
- m_warnAgainstUnalignedBuildDir.setToolTip(Tr::tr("Qmake has subtle bugs that "
- "can be triggered if source and build directory are not at the same level."));
+QmakeSettings &settings() { return *theSettings; }
- registerAspect(&m_alwaysRunQmake);
- m_alwaysRunQmake.setSettingsKey("QmakeProjectManager/AlwaysRunQmake");
- m_alwaysRunQmake.setLabelText(Tr::tr("Run qmake on every build"));
- m_alwaysRunQmake.setToolTip(Tr::tr("This option can help to prevent failures on "
- "incremental builds, but might slow them down unnecessarily in the general case."));
+QmakeSettings::QmakeSettings()
+{
+ theSettings = this;
- registerAspect(&m_ignoreSystemFunction);
- m_ignoreSystemFunction.setSettingsKey("QmakeProjectManager/RunSystemFunction");
- m_ignoreSystemFunction.setLabelText(Tr::tr("Ignore qmake's system() function when parsing a project"));
- m_ignoreSystemFunction.setToolTip(Tr::tr("Checking this option avoids unwanted side effects, "
- "but may result in inexact parsing results."));
+ setId("K.QmakeProjectManager.QmakeSettings");
+ setDisplayName(Tr::tr("Qmake"));
+ setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
+ setSettingsGroup("QmakeProjectManager");
+
+ registerAspect(&warnAgainstUnalignedBuildDir);
+ warnAgainstUnalignedBuildDir.setSettingsKey("WarnAgainstUnalignedBuildDir");
+ warnAgainstUnalignedBuildDir.setDefaultValue(HostOsInfo::isWindowsHost());
+ warnAgainstUnalignedBuildDir.setLabelText(Tr::tr("Warn if a project's source and "
+ "build directories are not at the same level"));
+ warnAgainstUnalignedBuildDir.setToolTip(Tr::tr("Qmake has subtle bugs that "
+ "can be triggered if source and build directory are not at the same level."));
+
+ registerAspect(&alwaysRunQmake);
+ alwaysRunQmake.setSettingsKey("AlwaysRunQmake");
+ alwaysRunQmake.setLabelText(Tr::tr("Run qmake on every build"));
+ alwaysRunQmake.setToolTip(Tr::tr("This option can help to prevent failures on "
+ "incremental builds, but might slow them down unnecessarily in the general case."));
+
+ registerAspect(&ignoreSystemFunction);
+ ignoreSystemFunction.setSettingsKey("RunSystemFunction");
+ ignoreSystemFunction.setLabelText(Tr::tr("Ignore qmake's system() function when parsing a project"));
+ ignoreSystemFunction.setToolTip(Tr::tr("Checking this option avoids unwanted side effects, "
+ "but may result in inexact parsing results."));
// The settings value has been stored with the opposite meaning for a while.
// Avoid changing the stored value, but flip it on read/write:
const auto invertBoolVariant = [](const QVariant &v) { return QVariant(!v.toBool()); };
- m_ignoreSystemFunction.setFromSettingsTransformation(invertBoolVariant);
- m_ignoreSystemFunction.setToSettingsTransformation(invertBoolVariant);
-
- readSettings(Core::ICore::settings());
-}
+ ignoreSystemFunction.setFromSettingsTransformation(invertBoolVariant);
+ ignoreSystemFunction.setToSettingsTransformation(invertBoolVariant);
-bool QmakeSettings::warnAgainstUnalignedBuildDir()
-{
- return instance().m_warnAgainstUnalignedBuildDir.value();
-}
-
-bool QmakeSettings::alwaysRunQmake()
-{
- return instance().m_alwaysRunQmake.value();
-}
-
-bool QmakeSettings::runSystemFunction()
-{
- return !instance().m_ignoreSystemFunction.value(); // Note: negated.
-}
-
-QmakeSettings &QmakeSettings::instance()
-{
- static QmakeSettings theSettings;
- return theSettings;
-}
-
-class SettingsWidget final : public Core::IOptionsPageWidget
-{
-public:
- SettingsWidget()
- {
- auto &s = QmakeSettings::instance();
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
Column {
- s.m_warnAgainstUnalignedBuildDir,
- s.m_alwaysRunQmake,
- s.m_ignoreSystemFunction,
+ warnAgainstUnalignedBuildDir,
+ alwaysRunQmake,
+ ignoreSystemFunction,
st
- }.attachTo(this);
- }
+ }.attachTo(widget);
+ });
- void apply() final
- {
- auto &s = QmakeSettings::instance();
- if (s.isDirty()) {
- s.apply();
- s.writeSettings(Core::ICore::settings());
- }
- }
-};
-
-QmakeSettingsPage::QmakeSettingsPage()
-{
- setId("K.QmakeProjectManager.QmakeSettings");
- setDisplayName(Tr::tr("Qmake"));
- setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
- setWidgetCreator([] { return new SettingsWidget; });
+ readSettings();
}
-} // namespace Internal
-} // namespace QmakeProjectManager
+} // QmakeProjectManager::Internal
diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.h b/src/plugins/qmakeprojectmanager/qmakesettings.h
index 242f6601e2..de9a9ec0d3 100644
--- a/src/plugins/qmakeprojectmanager/qmakesettings.h
+++ b/src/plugins/qmakeprojectmanager/qmakesettings.h
@@ -5,38 +5,20 @@
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/aspects.h>
+namespace QmakeProjectManager::Internal {
-namespace QmakeProjectManager {
-namespace Internal {
-
-class QmakeSettings : public Utils::AspectContainer
+class QmakeSettings : public Core::PagedSettings
{
- Q_OBJECT
-
public:
- static QmakeSettings &instance();
- static bool warnAgainstUnalignedBuildDir();
- static bool alwaysRunQmake();
- static bool runSystemFunction();
-
-signals:
- void settingsChanged();
-
-private:
QmakeSettings();
- friend class SettingsWidget;
- Utils::BoolAspect m_warnAgainstUnalignedBuildDir;
- Utils::BoolAspect m_alwaysRunQmake;
- Utils::BoolAspect m_ignoreSystemFunction;
-};
+ bool runSystemFunction() { return !ignoreSystemFunction(); }
-class QmakeSettingsPage final : public Core::IOptionsPage
-{
-public:
- QmakeSettingsPage();
+ Utils::BoolAspect warnAgainstUnalignedBuildDir;
+ Utils::BoolAspect alwaysRunQmake;
+ Utils::BoolAspect ignoreSystemFunction;
};
-} // namespace Internal
-} // namespace QmakeProjectManager
+QmakeSettings &settings();
+
+} // QmakeProjectManager::Internal
diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp
index c726156e94..010d160823 100644
--- a/src/plugins/qmakeprojectmanager/qmakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp
@@ -36,7 +36,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
@@ -225,7 +225,7 @@ bool QMakeStep::init()
}
// Check whether we need to run qmake
- if (m_forced || QmakeSettings::alwaysRunQmake()
+ if (m_forced || settings().alwaysRunQmake()
|| qmakeBc->compareToImportFrom(makeFile) != QmakeBuildConfiguration::MakefileMatches) {
m_needToRunQMake = true;
}
@@ -284,14 +284,14 @@ void QMakeStep::doRun()
using namespace Tasking;
- const auto setupQMake = [this](QtcProcess &process) {
+ const auto setupQMake = [this](Process &process) {
m_outputFormatter->setLineParsers({new QMakeParser});
ProcessParameters *pp = processParameters();
pp->setCommandLine(m_qmakeCommand);
setupProcess(&process);
};
- const auto setupMakeQMake = [this](QtcProcess &process) {
+ const auto setupMakeQMake = [this](Process &process) {
auto *parser = new GnuMakeParser;
parser->addSearchDir(processParameters()->workingDirectory());
m_outputFormatter->setLineParsers({parser});
@@ -300,13 +300,13 @@ void QMakeStep::doRun()
setupProcess(&process);
};
- const auto onDone = [this](const QtcProcess &) {
+ const auto onDone = [this](const Process &) {
const QString command = displayedParameters()->effectiveCommand().toUserOutput();
emit addOutput(Tr::tr("The process \"%1\" exited normally.").arg(command),
OutputFormat::NormalMessage);
};
- const auto onError = [this](const QtcProcess &process) {
+ const auto onError = [this](const Process &process) {
const QString command = displayedParameters()->effectiveCommand().toUserOutput();
if (process.result() == ProcessResult::FinishedWithError) {
emit addOutput(Tr::tr("The process \"%1\" exited with code %2.")
@@ -330,9 +330,9 @@ void QMakeStep::doRun()
emit buildConfiguration()->buildDirectoryInitialized();
};
- QList<TaskItem> processList = {Process(setupQMake, onDone, onError)};
+ QList<TaskItem> processList = {ProcessTask(setupQMake, onDone, onError)};
if (m_runMakeQmake)
- processList << Process(setupMakeQMake, onDone, onError);
+ processList << ProcessTask(setupMakeQMake, onDone, onError);
processList << OnGroupDone(onGroupDone);
runTaskTree(Group(processList));
@@ -481,11 +481,12 @@ QWidget *QMakeStep::createConfigWidget()
abisListWidget = new QListWidget;
Layouting::Form builder;
- builder.addRow(m_buildType);
- builder.addRow(m_userArgs);
- builder.addRow(m_effectiveCall);
+ builder.addRow({m_buildType});
+ builder.addRow({m_userArgs});
+ builder.addRow({m_effectiveCall});
builder.addRow({abisLabel, abisListWidget});
- auto widget = builder.emerge(Layouting::WithoutMargins);
+ builder.addItem(Layouting::noMargin);
+ auto widget = builder.emerge();
qmakeBuildConfigChanged();
@@ -744,7 +745,7 @@ QMakeStepFactory::QMakeStepFactory()
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
//: QMakeStep default display name
setDisplayName(::QmakeProjectManager::Tr::tr("qmake")); // Fully qualifying for lupdate
- setFlags(BuildStepInfo::UniqueStep);
+ setFlags(BuildStep::UniqueStep);
}
QMakeStepConfig::TargetArchConfig QMakeStepConfig::targetArchFor(const Abi &, const QtVersion *)
diff --git a/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp b/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp
index e2db56e824..1612e36753 100644
--- a/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp
+++ b/src/plugins/qmakeprojectmanager/wizards/qtprojectparameters.cpp
@@ -65,8 +65,10 @@ void QtProjectParameters::writeProFile(QTextStream &str) const
switch (type) {
case ConsoleApp:
// Mac: Command line apps should not be bundles
- str << "CONFIG += console\nCONFIG -= app_bundle\n\n";
- // fallthrough
+ str << "CONFIG += console\n"
+ "CONFIG -= app_bundle\n\n"
+ "TEMPLATE = app\n";
+ break;
case GuiApp:
str << "TEMPLATE = app\n";
break;
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 03db5b27ca..74fcd86330 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -1,18 +1,19 @@
+#only if the plugin is requested by qtc_plugin_enabled continue if not stop as early as possible
add_qtc_plugin(QmlDesigner
- CONDITION TARGET Qt::QuickWidgets AND TARGET Qt::Svg
- DEPENDS
- QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
- Qt::QuickWidgets Qt::CorePrivate Sqlite Qt::Xml Qt::Svg
- PLUGIN_DEPENDS
- Core ProjectExplorer QmlJSEditor QmlProjectManager
- QtSupport QmakeProjectManager
PLUGIN_RECOMMENDS QmlPreview
+ CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.2.0 AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg
+ PROPERTIES COMPILE_WARNING_AS_ERROR ON
+ PLUGIN_DEPENDS
+ Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager
+ QtSupport
)
qtc_plugin_enabled(_qmlDesignerEnabled QmlDesigner)
if (NOT _qmlDesignerEnabled)
return()
endif()
+find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate)
+
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/qmldesigner")
if (APPLE)
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner")
@@ -23,21 +24,22 @@ env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF)
option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE})
add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "")
-env_with_default("QDS_WITH_QMLDOM" ENV_QDS_WITH_QMLDOM OFF)
-option(WITH_QMLDOM "Build with QmlDom" ${ENV_QDS_WITH_QMLDOM})
-add_feature_info("Build with QmlDom" ${WITH_QMLDOM} "")
-
add_qtc_library(QmlDesignerUtils STATIC
+ PROPERTIES COMPILE_WARNING_AS_ERROR ON
DEPENDS
- Qt::Gui
+ Qt::Gui Utils Qt::QmlPrivate
DEFINES QMLDESIGNERUTILS_LIBRARY
PUBLIC_DEFINES $<$<BOOL:${QTC_STATIC_BUILD}>:QMLDESIGNER_STATIC_LIBRARY>
+
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils
SOURCES
asset.cpp asset.h
- designersettings.cpp designersettings.h
+ filedownloader.cpp filedownloader.h
+ multifiledownloader.cpp multifiledownloader.h
+ fileextractor.cpp fileextractor.h
hdrimage.cpp hdrimage.h
+ ktximage.cpp ktximage.h
imageutils.cpp imageutils.h
qmldesignerutils_global.h
)
@@ -60,6 +62,7 @@ add_qtc_library(QmlDesignerCore STATIC
QmlProjectManager
QtSupport
PUBLIC_DEPENDS
+ QmlDesignerBase
QmlPuppetCommunication
QmlDesignerUtils
TextEditor
@@ -77,15 +80,16 @@ add_qtc_library(QmlDesignerCore STATIC
)
extend_qtc_library(QmlDesignerCore
- CONDITION UNIX AND NOT APPLE
- PUBLIC_DEPENDS rt
+ CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0
+
+ DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate
+ DEFINES QDS_HAS_QMLDOM
)
-set(UI_FILES
- ${CMAKE_CURRENT_LIST_DIR}/designercore/instances/puppetbuildprogressdialog.ui
- ${CMAKE_CURRENT_LIST_DIR}/designercore/instances/puppetdialog.ui
+extend_qtc_library(QmlDesignerCore
+ CONDITION UNIX AND NOT APPLE
+ PUBLIC_DEPENDS rt
)
-qt_wrap_ui(UI_SOURCES ${UI_FILES})
extend_qtc_library(QmlDesignerCore
INCLUDES ${CMAKE_CURRENT_BINARY_DIR}
@@ -174,6 +178,8 @@ extend_qtc_library(QmlDesignerCore
meshimagecachecollector.cpp
meshimagecachecollector.h
synchronousimagecache.cpp
+ textureimagecachecollector.cpp
+ textureimagecachecollector.h
timestampprovider.cpp
timestampprovider.h
timestampproviderinterface.h
@@ -279,13 +285,9 @@ extend_qtc_library(QmlDesignerCore
nodeinstanceserverproxy.cpp
nodeinstanceserverproxy.h
nodeinstanceview.cpp
- puppetbuildprogressdialog.cpp
- puppetbuildprogressdialog.h
puppetstartdata.h
puppetstarter.cpp
puppetstarter.h
- puppetdialog.cpp
- puppetdialog.h
qprocessuniqueptr.h
)
@@ -380,6 +382,7 @@ extend_qtc_library(QmlDesignerCore
filestatus.h
filestatuscache.cpp filestatuscache.h
nonlockingmutex.h
+ projectstorageexceptions.cpp projectstorageexceptions.h
projectstorageinterface.h
projectstoragefwd.h
projectstorageinfotypes.h
@@ -410,6 +413,14 @@ file(GLOB PROJECTSTORAGE_EXCLUDED_SOURCES designercore/projectstorage/*.cpp)
set_property(SOURCE ${PROJECTSTORAGE_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON)
extend_qtc_plugin(QmlDesigner
+ PUBLIC_DEPENDS
+ QmlDesignerUtils
+ QmlDesignerBase
+ QmlPuppetCommunication
+ DEPENDS
+ QmlDesignerCore
+ QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem Sqlite
+ Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg
DEFINES
IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"
SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner"
@@ -432,13 +443,8 @@ extend_qtc_plugin(QmlDesigner
${CMAKE_CURRENT_LIST_DIR}/components/texteditor
PUBLIC_INCLUDES
${CMAKE_CURRENT_LIST_DIR}
- ${CMAKE_CURRENT_LIST_DIR}/designercore
- ${CMAKE_CURRENT_LIST_DIR}/designercore/include
- PUBLIC_DEPENDS
- QmlDesignerUtils
- QmlPuppetCommunication
- DEPENDS
- QmlDesignerCore
+ ${CMAKE_CURRENT_LIST_DIR}/designercore #can not be a public dependency -> EXCLUDE_FROM_INSTALL in QmlDesignerCore
+ ${CMAKE_CURRENT_LIST_DIR}/designercore/include #iwidgetplugin.h is used by other plugins
SOURCES
designmodecontext.cpp designmodecontext.h
designmodewidget.cpp designmodewidget.h
@@ -503,33 +509,15 @@ function(get_and_add_as_subdirectory name repository git_tag build_dir source_di
add_subdirectory(${source_dir}/${name}/${source_subdir} ${name})
endfunction()
-if (WITH_QMLDOM)
- if (Qt6_VERSION VERSION_LESS 6.3.2)
- message(FATAL_ERROR "You need Qt 6.3.2 or newer to enable WITH_QMLDOM feature.")
- endif()
-
-
- get_and_add_as_subdirectory("qmldom_standalone"
- "https://codereview.qt-project.org/qt/qtdeclarative"
- "origin/dev"
- "${CMAKE_BINARY_DIR}"
- "${CMAKE_CURRENT_SOURCE_DIR}/../.."
- "src/qmldom/standalone"
- )
-
- set_target_properties(qmldomlib PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY "$<TARGET_PROPERTY:QmlJS,RUNTIME_OUTPUT_DIRECTORY>"
- LIBRARY_OUTPUT_DIRECTORY "$<TARGET_PROPERTY:QmlJS,LIBRARY_OUTPUT_DIRECTORY>")
-
- extend_qtc_target(QmlDesigner
- CONDITION TARGET qmldomlib
- DEFINES QDS_HAS_QMLDOM
- DEPENDS qmldomlib
- )
-endif()
-
-
if (QTC_STATIC_BUILD AND TARGET QmlDesigner)
+ get_target_property(_designerType Qt5::Designer TYPE)
+ if (${_designerType} STREQUAL "STATIC_LIBRARY")
+ extend_qtc_target(QmlDesigner PUBLIC_DEFINES QT_DESIGNER_STATIC)
+ endif()
+ get_target_property(_designerType Qt::Designer TYPE)
+ if (${_designerType} STREQUAL "STATIC_LIBRARY")
+ extend_qtc_target(QmlDesigner PUBLIC_DEFINES QT_DESIGNER_STATIC)
+ endif()
extend_qtc_target(QmlDesigner PUBLIC_DEPENDS TextEditor)
endif()
@@ -621,6 +609,7 @@ extend_qtc_plugin(QmlDesigner
designeractionmanagerview.cpp designeractionmanagerview.h
designericons.cpp designericons.h
findimplementation.cpp findimplementation.h
+ groupitemaction.cpp groupitemaction.h
layoutingridlayout.cpp layoutingridlayout.h
modelnodecontextmenu.cpp modelnodecontextmenu.h
modelnodecontextmenu_helper.cpp modelnodecontextmenu_helper.h
@@ -628,6 +617,7 @@ extend_qtc_plugin(QmlDesigner
formatoperation.cpp formatoperation.h
navigation2d.cpp navigation2d.h
qmldesignericonprovider.cpp qmldesignericonprovider.h
+ qmleditormenu.cpp qmleditormenu.h
selectioncontext.cpp selectioncontext.h
theme.cpp theme.h
zoomaction.cpp zoomaction.h
@@ -779,6 +769,7 @@ extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/propertyeditor
SOURCES
aligndistribute.cpp aligndistribute.h
+ assetimageprovider.cpp assetimageprovider.h
colorpalettebackend.cpp colorpalettebackend.h
designerpropertymap.cpp designerpropertymap.h
fileresourcesmodel.cpp fileresourcesmodel.h
@@ -790,7 +781,6 @@ extend_qtc_plugin(QmlDesigner
gradientpresetitem.cpp gradientpresetitem.h
gradientpresetlistmodel.cpp gradientpresetlistmodel.h
propertyeditorcontextobject.cpp propertyeditorcontextobject.h
- propertyeditorimageprovider.cpp propertyeditorimageprovider.h
propertyeditorqmlbackend.cpp propertyeditorqmlbackend.h
propertyeditortransaction.cpp propertyeditortransaction.h
propertyeditorvalue.cpp propertyeditorvalue.h
@@ -845,6 +835,7 @@ extend_qtc_plugin(QmlDesigner
materialbrowserwidget.cpp materialbrowserwidget.h
materialbrowsermodel.cpp materialbrowsermodel.h
materialbrowsertexturesmodel.cpp materialbrowsertexturesmodel.h
+ materialutils.cpp materialutils.h
)
extend_qtc_plugin(QmlDesigner
@@ -917,6 +908,10 @@ extend_qtc_plugin(QmlDesigner
SOURCES
explicitimagecacheimageprovider.cpp
explicitimagecacheimageprovider.h
+ imagecacheimageresponse.cpp
+ imagecacheimageresponse.h
+ midsizeimagecacheprovider.cpp
+ midsizeimagecacheprovider.h
smallimagecacheprovider.cpp
smallimagecacheprovider.h
)
@@ -1075,6 +1070,7 @@ extend_qtc_plugin(QmlDesigner
curveeditorview.cpp curveeditorview.h
animationcurve.cpp animationcurve.h
curveeditor.cpp curveeditor.h
+ curveeditorconstants.h
curveeditortoolbar.cpp curveeditortoolbar.h
curveeditormodel.cpp curveeditormodel.h
curveeditorstyle.h
@@ -1121,6 +1117,15 @@ extend_qtc_plugin(QmlDesigner
)
extend_qtc_plugin(QmlDesigner
+ SOURCES_PREFIX components/toolbar
+ SOURCES
+ toolbar.cpp
+ toolbar.h
+ toolbarbackend.cpp
+ toolbarbackend.h
+)
+
+extend_qtc_plugin(QmlDesigner
CONDITION TARGET Nanotrace
DEPENDS Nanotrace
)
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
index f3460cbca8..e9b5ab7082 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
@@ -11,7 +11,7 @@
#include <projectexplorer/task.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/fileutils.h>
#include <utils/outputformatter.h>
@@ -63,7 +63,7 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath,
m_ui->exportPath->setExpectedKind(Utils::PathChooser::Kind::SaveFile);
m_ui->exportPath->setFilePath(
exportPath.pathAppended(
- ProjectExplorer::SessionManager::startupProject()->displayName() + ".metadata"
+ ProjectExplorer::ProjectManager::startupProject()->displayName() + ".metadata"
));
m_ui->exportPath->setPromptDialogTitle(tr("Choose Export File"));
m_ui->exportPath->setPromptDialogFilter(tr("Metadata file (*.metadata)"));
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
index d9167049a5..7b0604c19a 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
@@ -10,11 +10,12 @@
#include "rewriterview.h"
#include "qmlitemnode.h"
#include "qmlobjectnode.h"
-#include "coreplugin/editormanager/editormanager.h"
-#include "utils/qtcassert.h"
-#include "utils/runextensions.h"
-#include "projectexplorer/session.h"
-#include "projectexplorer/project.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <utils/async.h>
+#include <utils/qtcassert.h>
#include <auxiliarydataproperties.h>
@@ -69,7 +70,7 @@ public:
private:
void addAsset(const QPixmap &p, const Utils::FilePath &path);
- void doDumping(QFutureInterface<void> &fi);
+ void doDumping(QPromise<void> &promise);
void savePixmap(const QPixmap &p, Utils::FilePath &path) const;
QFuture<void> m_dumpFuture;
@@ -406,7 +407,7 @@ void AssetExporter::writeMetadata() const
m_currentState.change(ParsingState::WritingJson);
- auto const startupProject = ProjectExplorer::SessionManager::startupProject();
+ auto const startupProject = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(startupProject, return);
const QString projectName = startupProject->displayName();
@@ -452,7 +453,7 @@ QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s)
AssetDumper::AssetDumper():
m_quitDumper(false)
{
- m_dumpFuture = Utils::runAsync(&AssetDumper::doDumping, this);
+ m_dumpFuture = Utils::asyncRun(&AssetDumper::doDumping, this);
}
AssetDumper::~AssetDumper()
@@ -489,7 +490,7 @@ void AssetDumper::addAsset(const QPixmap &p, const Utils::FilePath &path)
m_assets.push({p, path});
}
-void AssetDumper::doDumping(QFutureInterface<void> &fi)
+void AssetDumper::doDumping(QPromise<void> &promise)
{
auto haveAsset = [this] (std::pair<QPixmap, Utils::FilePath> *asset) {
QMutexLocker locker(&m_queueMutex);
@@ -503,7 +504,7 @@ void AssetDumper::doDumping(QFutureInterface<void> &fi)
forever {
std::pair<QPixmap, Utils::FilePath> asset;
if (haveAsset(&asset)) {
- if (fi.isCanceled())
+ if (promise.isCanceled())
break;
savePixmap(asset.first, asset.second);
} else {
@@ -513,10 +514,9 @@ void AssetDumper::doDumping(QFutureInterface<void> &fi)
m_queueCondition.wait(&m_queueMutex);
}
- if (fi.isCanceled())
+ if (promise.isCanceled())
break;
}
- fi.reportFinished();
}
void AssetDumper::savePixmap(const QPixmap &p, Utils::FilePath &path) const
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
index 2d6f6c7d80..3ab1d1a686 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
@@ -19,9 +19,9 @@
#include "coreplugin/documentmanager.h"
#include "qmldesigner/qmldesignerplugin.h"
#include "projectexplorer/projectexplorerconstants.h"
-#include "projectexplorer/session.h"
+#include "projectexplorer/projectmanager.h"
#include "projectexplorer/project.h"
-#include "projectexplorer/session.h"
+#include "projectexplorer/projectmanager.h"
#include "projectexplorer/taskhub.h"
#include "extensionsystem/pluginmanager.h"
@@ -54,8 +54,8 @@ AssetExporterPlugin::AssetExporterPlugin()
// Instantiate actions created by the plugin.
addActions();
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this, &AssetExporterPlugin::updateActions);
updateActions();
@@ -68,7 +68,7 @@ QString AssetExporterPlugin::pluginName() const
void AssetExporterPlugin::onExport()
{
- auto startupProject = ProjectExplorer::SessionManager::startupProject();
+ auto startupProject = ProjectExplorer::ProjectManager::startupProject();
if (!startupProject)
return;
@@ -97,7 +97,7 @@ void AssetExporterPlugin::addActions()
void AssetExporterPlugin::updateActions()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
QAction* const exportAction = Core::ActionManager::command(Constants::EXPORT_QML)->action();
exportAction->setEnabled(project && !project->needsConfiguration());
}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
index b50bc3cffb..5020b095a3 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
@@ -4,9 +4,10 @@
#include "exportnotification.h"
-#include "projectexplorer/project.h"
-#include "projectexplorer/projectnodes.h"
-#include "utils/runextensions.h"
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectnodes.h>
+
+#include <utils/async.h>
#include <QLoggingCategory>
#include <QTimer>
@@ -17,19 +18,19 @@ namespace {
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.filePathModel", QtCriticalMsg)
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", QtInfoMsg)
-void findQmlFiles(QFutureInterface<Utils::FilePath> &f, const Project *project)
+void findQmlFiles(QPromise<Utils::FilePath> &promise, const Project *project)
{
- if (!project || f.isCanceled())
+ if (!project || promise.isCanceled())
return;
int index = 0;
- project->files([&f, &index](const Node* node) ->bool {
- if (f.isCanceled())
+ project->files([&promise, &index](const Node* node) ->bool {
+ if (promise.isCanceled())
return false;
Utils::FilePath path = node->filePath();
bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper();
if (isComponent && node->filePath().endsWith(".ui.qml"))
- f.reportResult(path, index++);
+ promise.addResult(path, index++);
return true;
});
}
@@ -132,7 +133,7 @@ void FilePathModel::processProject()
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished,
this, &FilePathModel::endResetModel);
- QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
+ QFuture<Utils::FilePath> f = Utils::asyncRun(&findQmlFiles, m_project);
m_preprocessWatcher->setFuture(f);
}
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc b/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc
index 26b4250d0a..fb4255cdf6 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrary.qrc
@@ -19,5 +19,8 @@
<file>images/assets_default_128.png</file>
<file>images/asset_effectClass_128.png</file>
<file>images/asset_effectExported_128.png</file>
+ <file>images/asset_ktx.png</file>
+ <file>images/asset_ktx@2x.png</file>
+ <file>images/asset_ktx_128.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
index 6fc9bd4a26..838af4d63d 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
@@ -7,6 +7,7 @@
#include <theme.h>
#include <utils/hdrimage.h>
+#include <utils/ktximage.h>
#include <utils/stylehelper.h>
namespace QmlDesigner {
@@ -43,13 +44,16 @@ Thumbnail AssetsLibraryIconProvider::createThumbnail(const QString &id, const QS
{
auto [pixmap, fileSize] = fetchPixmap(id, requestedSize);
QSize originalSize = pixmap.size();
- Asset::Type assetType = Asset(id).type();
+ Asset asset(id);
+ Asset::Type assetType = asset.type();
if (pixmap.isNull()) {
pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/assets_default.png");
if (assetType == Asset::Image)
assetType = Asset::MissingImage;
+ else if (asset.isKtxFile())
+ originalSize = KtxImage(id).dimensions();
}
if (requestedSize.isValid())
@@ -86,6 +90,10 @@ QPair<QPixmap, qint64> AssetsLibraryIconProvider::fetchPixmap(const QString &id,
qint64 size = QFileInfo(id).size();
QPixmap pixmap = HdrImage{id}.toPixmap();
return {pixmap, size};
+ } else if (asset.isKtxFile()) {
+ qint64 size = QFileInfo(id).size();
+ QString filePath = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/asset_ktx.png");
+ return {QPixmap{filePath}, size};
} else {
QString type;
if (asset.isShader())
@@ -129,12 +137,5 @@ qint64 AssetsLibraryIconProvider::fileSize(const QString &id)
return m_thumbnails.contains(id) ? m_thumbnails[id].fileSize : 0;
}
-bool AssetsLibraryIconProvider::assetIsImage(const QString &id)
-{
- return m_thumbnails.contains(id)
- ? (m_thumbnails[id].assetType == Asset::Type::Image || Asset(id).isHdrFile())
- : false;
-}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
index b1a611d641..fb38605ea6 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
@@ -29,7 +29,6 @@ public:
void invalidateThumbnail(const QString &id);
QSize imageSize(const QString &id);
qint64 fileSize(const QString &id);
- bool assetIsImage(const QString &id);
private:
QPixmap generateFontIcons(const QString &filePath, const QSize &requestedSize) const;
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
index 8a4a25c18d..faf4f1eb54 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
@@ -150,6 +150,12 @@ bool AssetsLibraryModel::addNewFolder(const QString &folderPath)
return dir.mkpath(iterPath);
}
+bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const
+{
+ QModelIndex index = indexForPath(url.toLocalFile());
+ return index.isValid();
+}
+
bool AssetsLibraryModel::deleteFolderRecursively(const QModelIndex &folderIndex)
{
auto idx = mapToSource(folderIndex);
@@ -160,52 +166,13 @@ bool AssetsLibraryModel::deleteFolderRecursively(const QModelIndex &folderIndex)
return ok;
}
-bool AssetsLibraryModel::allFilePathsAreImages(const QStringList &filePaths) const
+bool AssetsLibraryModel::allFilePathsAreTextures(const QStringList &filePaths) const
{
return Utils::allOf(filePaths, [](const QString &path) {
- return Asset(path).isImage();
+ return Asset(path).isValidTextureSource();
});
}
-QString AssetsLibraryModel::getUniqueEffectPath(const QString &parentFolder, const QString &effectName)
-{
- auto genEffectPath = [=](const QString &name) {
- return QString(parentFolder + "/" + name + ".qep");
- };
-
- QString uniqueName = effectName;
- QString path = genEffectPath(uniqueName);
- QFileInfo file{path};
-
- while (file.exists()) {
- uniqueName = getUniqueName(uniqueName);
-
- path = genEffectPath(uniqueName);
- file.setFile(path);
- }
-
- return path;
-}
-
-bool AssetsLibraryModel::createNewEffect(const QString &effectPath, bool openEffectMaker)
-{
- bool created = QFile(effectPath).open(QIODevice::WriteOnly);
-
- if (created && openEffectMaker)
- ModelNodeOperations::openEffectMaker(effectPath);
-
- return created;
-}
-
-bool AssetsLibraryModel::canCreateEffects() const
-{
-#ifdef LICENSECHECKER
- return checkLicense() == FoundLicense::enterprise;
-#else
- return true;
-#endif
-}
-
bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QString path = m_sourceFsModel->filePath(sourceParent);
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
index ff9832ee3c..f003ef2c54 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
@@ -39,6 +39,7 @@ public:
Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const;
Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const;
+ Q_INVOKABLE bool urlPathExistsInModel(const QUrl &url) const;
Q_INVOKABLE QString currentProjectDirPath() const;
Q_INVOKABLE QString contentDirPath() const;
Q_INVOKABLE bool requestDeleteFiles(const QStringList &filePaths);
@@ -46,12 +47,7 @@ public:
Q_INVOKABLE bool renameFolder(const QString &folderPath, const QString &newName);
Q_INVOKABLE bool addNewFolder(const QString &folderPath);
Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex);
- Q_INVOKABLE bool allFilePathsAreImages(const QStringList &filePaths) const;
-
- Q_INVOKABLE QString getUniqueEffectPath(const QString &parentFolder, const QString &effectName);
- Q_INVOKABLE bool createNewEffect(const QString &effectPath, bool openEffectMaker = true);
-
- Q_INVOKABLE bool canCreateEffects() const;
+ Q_INVOKABLE bool allFilePathsAreTextures(const QStringList &filePaths) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
@@ -61,6 +57,8 @@ public:
bool haveFiles() const { return m_haveFiles; }
+ QString getUniqueName(const QString &oldName);
+
signals:
void directoryLoaded(const QString &path);
void rootPathChanged();
@@ -75,7 +73,6 @@ private:
void destroyBackendModel();
bool checkHaveFiles(const QModelIndex &parentIdx) const;
bool checkHaveFiles() const;
- QString getUniqueName(const QString &oldName);
QString m_searchText;
QString m_rootPath;
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp
index 9792a6615a..6d6efbb5d2 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp
@@ -4,7 +4,7 @@
#include "assetslibraryview.h"
#include "assetslibrarywidget.h"
-#include "createtexture.h"
+#include "designmodecontext.h"
#include "qmldesignerplugin.h"
#include <asynchronousimagecache.h>
@@ -19,7 +19,7 @@
#include <nodelistproperty.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <rewriterview.h>
#include <sqlitedatabase.h>
@@ -46,7 +46,6 @@ public:
AssetsLibraryView::AssetsLibraryView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}
- , m_createTextures{this, false}
{}
AssetsLibraryView::~AssetsLibraryView()
@@ -61,36 +60,37 @@ WidgetInfo AssetsLibraryView::widgetInfo()
{
if (m_widget.isNull()) {
m_widget = new AssetsLibraryWidget{imageCacheData()->asynchronousFontImageCache,
- imageCacheData()->synchronousFontImageCache};
-
- connect(m_widget, &AssetsLibraryWidget::addTexturesRequested, this,
- [&] (const QStringList &filePaths, AddTextureMode mode) {
- executeInTransaction("AssetsLibraryView::widgetInfo", [&]() {
- m_createTextures.execute(filePaths, mode, m_sceneId);
- });
- });
+ imageCacheData()->synchronousFontImageCache,
+ this};
+
+ auto context = new Internal::AssetsLibraryContext(m_widget.data());
+ Core::ICore::addContextObject(context);
}
return createWidgetInfo(m_widget.data(), "Assets", WidgetInfo::LeftPane, 0, tr("Assets"));
}
+void AssetsLibraryView::customNotification(const AbstractView * /*view*/,
+ const QString &identifier,
+ const QList<ModelNode> & /*nodeList*/,
+ const QList<QVariant> & /*data*/)
+{
+ if (identifier == "delete_selected_assets")
+ m_widget->deleteSelectedAssets();
+}
+
void AssetsLibraryView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
m_widget->clearSearchFilter();
- m_widget->setModel(model);
setResourcePath(DocumentManager::currentResourcePath().toFileInfo().absoluteFilePath());
-
- m_sceneId = model->active3DSceneId();
}
void AssetsLibraryView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
-
- m_widget->setModel(nullptr);
}
void AssetsLibraryView::setResourcePath(const QString &resourcePath)
@@ -103,7 +103,8 @@ void AssetsLibraryView::setResourcePath(const QString &resourcePath)
if (m_widget.isNull()) {
m_widget = new AssetsLibraryWidget{imageCacheData()->asynchronousFontImageCache,
- imageCacheData()->synchronousFontImageCache};
+ imageCacheData()->synchronousFontImageCache,
+ this};
}
m_widget->setResourcePath(resourcePath);
@@ -116,9 +117,4 @@ AssetsLibraryView::ImageCacheData *AssetsLibraryView::imageCacheData()
return m_imageCacheData.get();
}
-void AssetsLibraryView::active3DSceneChanged(qint32 sceneId)
-{
- m_sceneId = sceneId;
-}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h
index d872439011..f1a408c303 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.h
@@ -4,7 +4,6 @@
#pragma once
#include "abstractview.h"
-#include "createtexture.h"
#include <QPointer>
@@ -31,18 +30,18 @@ public:
void modelAboutToBeDetached(Model *model) override;
void setResourcePath(const QString &resourcePath);
- void active3DSceneChanged(qint32 sceneId) override;
private:
class ImageCacheData;
ImageCacheData *imageCacheData();
+ void customNotification(const AbstractView *view, const QString &identifier,
+ const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
+
std::once_flag imageCacheFlag;
std::unique_ptr<ImageCacheData> m_imageCacheData;
QPointer<AssetsLibraryWidget> m_widget;
QString m_lastResourcePath;
- CreateTextures m_createTextures;
- qint32 m_sceneId = -1;
};
}
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
index d2c208929c..14110cc699 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
@@ -6,26 +6,16 @@
#include "asset.h"
#include "assetslibraryiconprovider.h"
#include "assetslibrarymodel.h"
-
-#include <theme.h>
-
-#include <designeractionmanager.h>
+#include "assetslibraryview.h"
+#include "designeractionmanager.h"
#include "modelnodeoperations.h"
-#include <model.h>
-#include <navigatorwidget.h>
-#include <qmldesignerconstants.h>
-#include <qmldesignerplugin.h>
+#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
+#include "theme.h"
-#include <utils/algorithm.h>
-#include <utils/environment.h>
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-#include <utils/stylehelper.h>
-#include <utils/utilsicons.h>
-#include "utils/environment.h"
-#include "utils/filepath.h"
+#include <studioquickwidget.h>
-#include <coreplugin/coreconstants.h>
+#include <coreplugin/fileutils.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
@@ -33,21 +23,22 @@
#include <projectexplorer/target.h>
#include <projectexplorer/project.h>
-#include <QApplication>
-#include <QDrag>
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/filepath.h>
+#include <utils/qtcassert.h>
+
#include <QFileDialog>
#include <QFileInfo>
-#include <QFileSystemModel>
-#include <QVBoxLayout>
#include <QImageReader>
-#include <QMenu>
#include <QMimeData>
#include <QMouseEvent>
-#include <QShortcut>
-#include <QTimer>
-#include <QToolButton>
+#include <QPointF>
#include <QQmlContext>
#include <QQuickItem>
+#include <QShortcut>
+#include <QToolButton>
+#include <QVBoxLayout>
namespace QmlDesigner {
@@ -63,38 +54,50 @@ static QString propertyEditorResourcesPath()
bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::FocusOut) {
- if (obj == m_assetsWidget.data())
+ if (obj == m_assetsWidget->quickWidget())
QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "handleViewFocusOut");
} else if (event->type() == QMouseEvent::MouseMove) {
- if (!m_assetsToDrag.isEmpty() && !m_model.isNull()) {
+ if (!m_assetsToDrag.isEmpty() && m_assetsView->model()) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) {
QMimeData *mimeData = new QMimeData;
mimeData->setData(Constants::MIME_TYPE_ASSETS, m_assetsToDrag.join(',').toUtf8());
- m_model->startDrag(mimeData,
- m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128}));
+
+ QList<QUrl> urlsToDrag = Utils::transform(m_assetsToDrag, [](const QString &path) {
+ return QUrl::fromLocalFile(path);
+ });
+
+ mimeData->setUrls(urlsToDrag);
+
+ m_assetsView->model()->startDrag(mimeData, m_assetsIconProvider->requestPixmap(
+ m_assetsToDrag[0], nullptr, {128, 128}));
+
m_assetsToDrag.clear();
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_assetsToDrag.clear();
+ setIsDragging(false);
}
return QObject::eventFilter(obj, event);
}
AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFontImageCache,
- SynchronousImageCache &synchronousFontImageCache)
+ SynchronousImageCache &synchronousFontImageCache,
+ AssetsLibraryView *view)
: m_itemIconSize{24, 24}
, m_fontImageCache{synchronousFontImageCache}
, m_assetsIconProvider{new AssetsLibraryIconProvider(synchronousFontImageCache)}
, m_assetsModel{new AssetsLibraryModel(this)}
- , m_assetsWidget{new QQuickWidget(this)}
+ , m_assetsView{view}
+ , m_createTextures{view}
+ , m_assetsWidget{new StudioQuickWidget(this)}
{
setWindowTitle(tr("Assets Library", "Title of assets library widget"));
setMinimumWidth(250);
- m_assetsWidget->installEventFilter(this);
+ m_assetsWidget->quickWidget()->installEventFilter(this);
m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache);
// We want font images to have custom size, so don't scale them in the tooltip
@@ -109,16 +112,12 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
"over the lazy dog\n"
"1234567890")});
// create assets widget
+ m_assetsWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_ASSET_LIBRARY);
m_assetsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
Theme::setupTheme(m_assetsWidget->engine());
m_assetsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_assetsWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
m_assetsWidget->engine()->addImageProvider("qmldesigner_assets", m_assetsIconProvider);
- m_assetsWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{
- {{"assetsModel"}, QVariant::fromValue(m_assetsModel)},
- {{"rootView"}, QVariant::fromValue(this)},
- {{"tooltipBackend"}, QVariant::fromValue(m_fontPreviewTooltipBackend.get())}
- });
connect(m_assetsModel, &AssetsLibraryModel::fileChanged, [](const QString &changeFilePath) {
QmlDesignerPlugin::instance()->emitAssetChanged(changeFilePath);
@@ -136,28 +135,135 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F6), this);
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &AssetsLibraryWidget::reloadQmlSource);
- connect(this, &AssetsLibraryWidget::extFilesDrop, this, &AssetsLibraryWidget::handleExtFilesDrop, Qt::QueuedConnection);
+ connect(this,
+ &AssetsLibraryWidget::extFilesDrop,
+ this,
+ &AssetsLibraryWidget::handleExtFilesDrop,
+ Qt::QueuedConnection);
+
+ QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_ASSETSLIBRARY_TIME);
+
+ auto map = m_assetsWidget->registerPropertyMap("AssetsLibraryBackend");
- QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_ASSETSLIBRARY_TIME);
+ map->setProperties({{"assetsModel", QVariant::fromValue(m_assetsModel)},
+ {"rootView", QVariant::fromValue(this)},
+ {"tooltipBackend", QVariant::fromValue(m_fontPreviewTooltipBackend.get())}});
// init the first load of the QML UI elements
reloadQmlSource();
}
-bool AssetsLibraryWidget::qtVersionIsAtLeast6_4() const
+void AssetsLibraryWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
+{
+ if (m_assetsView)
+ QmlDesignerPlugin::contextHelp(callback, m_assetsView->contextHelpId());
+ else
+ callback({});
+}
+
+void AssetsLibraryWidget::deleteSelectedAssets()
{
- return (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0));
+ emit deleteSelectedAssetsRequested();
}
+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 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;
+}
+
+bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openEffectMaker)
+{
+ bool created = QFile(effectPath).open(QIODevice::WriteOnly);
+
+ if (created && openEffectMaker) {
+ ModelNodeOperations::openEffectMaker(effectPath);
+ emit directoryCreated(QFileInfo(effectPath).absolutePath());
+ }
+
+ return created;
+}
+
+bool AssetsLibraryWidget::canCreateEffects() const
+{
+#ifdef LICENSECHECKER
+ return checkLicense() == FoundLicense::enterprise;
+#else
+ return true;
+#endif
+}
+
+void AssetsLibraryWidget::showInGraphicalShell(const QString &path)
+{
+ Core::FileUtils::showInGraphicalShell(Core::ICore::dialogParent(), Utils::FilePath::fromString(path));
+}
+
+QString AssetsLibraryWidget::showInGraphicalShellMsg() const
+{
+ return Core::FileUtils::msgGraphicalShellAction();
+}
+
+int AssetsLibraryWidget::qtVersion() const
+{
+ return QT_VERSION;
+}
void AssetsLibraryWidget::addTextures(const QStringList &filePaths)
{
- emit addTexturesRequested(filePaths, AddTextureMode::Texture);
+ m_assetsView->executeInTransaction(__FUNCTION__, [&] {
+ m_createTextures.execute(filePaths, AddTextureMode::Texture,
+ m_assetsView->model()->active3DSceneId());
+ });
}
void AssetsLibraryWidget::addLightProbe(const QString &filePath)
{
- emit addTexturesRequested({filePath}, AddTextureMode::LightProbe);
+ m_assetsView->executeInTransaction(__FUNCTION__, [&] {
+ m_createTextures.execute({filePath}, AddTextureMode::LightProbe,
+ m_assetsView->model()->active3DSceneId());
+ });
+}
+
+void AssetsLibraryWidget::updateContextMenuActionsEnableState()
+{
+ setHasMaterialLibrary(m_assetsView->materialLibraryNode().isValid()
+ && m_assetsView->model()->hasImport("QtQuick3D"));
+
+ ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv(m_assetsView->model()->active3DSceneId());
+ setHasSceneEnv(activeSceneEnv.isValid());
+}
+
+void AssetsLibraryWidget::setHasMaterialLibrary(bool enable)
+{
+ if (m_hasMaterialLibrary == enable)
+ return;
+
+ m_hasMaterialLibrary = enable;
+ emit hasMaterialLibraryChanged();
+}
+
+void AssetsLibraryWidget::setHasSceneEnv(bool b)
+{
+ if (b == m_hasSceneEnv)
+ return;
+
+ m_hasSceneEnv = b;
+ emit hasSceneEnvChanged();
}
void AssetsLibraryWidget::invalidateThumbnail(const QString &id)
@@ -176,9 +282,9 @@ QString AssetsLibraryWidget::assetFileSize(const QString &id)
return QLocale::system().formattedDataSize(fileSize, 2, QLocale::DataSizeTraditionalFormat);
}
-bool AssetsLibraryWidget::assetIsImage(const QString &id)
+bool AssetsLibraryWidget::assetIsImageOrTexture(const QString &id)
{
- return m_assetsIconProvider->assetIsImage(id);
+ return Asset(id).isValidTextureSource();
}
QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets()
@@ -218,15 +324,16 @@ void AssetsLibraryWidget::handleExtFilesDrop(const QList<QUrl> &simpleFilePaths,
auto toLocalFile = [](const QUrl &url) { return url.toLocalFile(); };
QStringList simpleFilePathStrings = Utils::transform<QStringList>(simpleFilePaths, toLocalFile);
- QStringList complexFilePathStrings = Utils::transform<QStringList>(complexFilePaths,
- toLocalFile);
+ QStringList complexFilePathStrings = Utils::transform<QStringList>(complexFilePaths, toLocalFile);
if (!simpleFilePathStrings.isEmpty()) {
if (targetDirPath.isEmpty()) {
addResources(simpleFilePathStrings);
} else {
+ bool isDropOnRoot = m_assetsModel->rootPath() == targetDirPath;
AddFilesResult result = ModelNodeOperations::addFilesToProject(simpleFilePathStrings,
- targetDirPath);
+ targetDirPath,
+ isDropOnRoot);
if (result.status() == AddFilesResult::Failed) {
Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project.")
@@ -237,6 +344,8 @@ void AssetsLibraryWidget::handleExtFilesDrop(const QList<QUrl> &simpleFilePaths,
if (!complexFilePathStrings.empty())
addResources(complexFilePathStrings);
+
+ m_assetsView->model()->endDrag();
}
QSet<QString> AssetsLibraryWidget::supportedAssetSuffixes(bool complex)
@@ -246,7 +355,7 @@ QSet<QString> AssetsLibraryWidget::supportedAssetSuffixes(bool complex)
QSet<QString> suffixes;
for (const AddResourceHandler &handler : handlers) {
- if (Asset(handler.filter).isSupported() != complex)
+ if (Asset::isSupported(handler.filter) != complex)
suffixes.insert(handler.filter);
}
@@ -258,18 +367,13 @@ void AssetsLibraryWidget::openEffectMaker(const QString &filePath)
ModelNodeOperations::openEffectMaker(filePath);
}
-void AssetsLibraryWidget::setModel(Model *model)
-{
- m_model = model;
-}
-
QString AssetsLibraryWidget::qmlSourcesPath()
{
#ifdef SHARE_QML_PATH
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
- return QLatin1String(SHARE_QML_PATH) + "/itemLibraryQmlSources";
+ return QLatin1String(SHARE_QML_PATH) + "/assetsLibraryQmlSources";
#endif
- return Core::ICore::resourcePath("qmldesigner/itemLibraryQmlSources").toString();
+ return Core::ICore::resourcePath("qmldesigner/assetsLibraryQmlSources").toString();
}
void AssetsLibraryWidget::clearSearchFilter()
@@ -281,7 +385,6 @@ void AssetsLibraryWidget::reloadQmlSource()
{
const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml";
QTC_ASSERT(QFileInfo::exists(assetsQmlPath), return);
- m_assetsWidget->engine()->clearComponentCache();
m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath));
}
@@ -290,6 +393,14 @@ void AssetsLibraryWidget::updateSearch()
m_assetsModel->setSearchText(m_filterText);
}
+void AssetsLibraryWidget::setIsDragging(bool val)
+{
+ if (m_isDragging != val) {
+ m_isDragging = val;
+ emit isDraggingChanged();
+ }
+}
+
void AssetsLibraryWidget::setResourcePath(const QString &resourcePath)
{
m_assetsModel->setRootPath(resourcePath);
@@ -303,6 +414,7 @@ void AssetsLibraryWidget::startDragAsset(const QStringList &assetPaths, const QP
// active (and blocks mouse release) if mouse is released at the same spot of the drag start.
m_assetsToDrag = assetPaths;
m_dragStartPoint = mousePos.toPoint();
+ setIsDragging(true);
}
QPair<QString, QByteArray> AssetsLibraryWidget::getAssetTypeAndData(const QString &assetPath)
@@ -351,7 +463,7 @@ static QHash<QByteArray, QStringList> allImageFormats()
return imageFormats;
}
-void AssetsLibraryWidget::addResources(const QStringList &files)
+void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog)
{
clearSearchFilter();
@@ -425,7 +537,7 @@ void AssetsLibraryWidget::addResources(const QStringList &files)
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_RESOURCE_IMPORTED + category);
if (operation) {
AddFilesResult result = operation(fileNames,
- document->fileName().parentDir().toString(), true);
+ document->fileName().parentDir().toString(), showDialog);
if (result.status() == AddFilesResult::Failed) {
Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project.")
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
index 980cae69dc..33ad100c0b 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
@@ -3,27 +3,27 @@
#pragma once
-#include <previewtooltip/previewtooltipbackend.h>
-
-#include "assetslibrarymodel.h"
#include "createtexture.h"
+#include "previewtooltipbackend.h"
+
+#include <coreplugin/icontext.h>
-#include <QFileIconProvider>
#include <QFrame>
-#include <QPointF>
#include <QQmlPropertyMap>
#include <QQuickWidget>
-#include <QTimer>
-#include <QToolButton>
#include <memory>
QT_BEGIN_NAMESPACE
+class QPointF;
class QShortcut;
+class QToolButton;
QT_END_NAMESPACE
+class StudioQuickWidget;
+
namespace Utils {
- class QtcProcess;
+ class Process;
}
namespace QmlDesigner {
@@ -33,6 +33,7 @@ class Model;
class AssetsLibraryIconProvider;
class AssetsLibraryModel;
+class AssetsLibraryView;
class SynchronousImageCache;
class AsynchronousImageCache;
class ImageCacheCollector;
@@ -41,12 +42,19 @@ class AssetsLibraryWidget : public QFrame
{
Q_OBJECT
+ Q_PROPERTY(bool hasMaterialLibrary MEMBER m_hasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
+ Q_PROPERTY(bool hasSceneEnv MEMBER m_hasSceneEnv NOTIFY hasSceneEnvChanged)
+
+ // 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)
+
public:
AssetsLibraryWidget(AsynchronousImageCache &asynchronousFontImageCache,
- SynchronousImageCache &synchronousFontImageCache);
+ SynchronousImageCache &synchronousFontImageCache, AssetsLibraryView *view);
~AssetsLibraryWidget() = default;
QList<QToolButton *> createToolBarWidgets();
+ void contextHelp(const Core::IContext::HelpCallback &callback) const;
static QString qmlSourcesPath();
void clearSearchFilter();
@@ -55,9 +63,10 @@ public:
void updateModel();
void setResourcePath(const QString &resourcePath);
- void setModel(Model *model);
static QPair<QString, QByteArray> getAssetTypeAndData(const QString &assetPath);
+ void deleteSelectedAssets();
+
Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos);
Q_INVOKABLE void handleAddAsset();
Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
@@ -72,14 +81,23 @@ public:
Q_INVOKABLE QSet<QString> supportedAssetSuffixes(bool complex);
Q_INVOKABLE void openEffectMaker(const QString &filePath);
- Q_INVOKABLE bool qtVersionIsAtLeast6_4() const;
+ Q_INVOKABLE int qtVersion() const;
Q_INVOKABLE void invalidateThumbnail(const QString &id);
Q_INVOKABLE QSize imageSize(const QString &id);
Q_INVOKABLE QString assetFileSize(const QString &id);
- Q_INVOKABLE bool assetIsImage(const QString &id);
+ Q_INVOKABLE bool assetIsImageOrTexture(const QString &id);
Q_INVOKABLE void addTextures(const QStringList &filePaths);
Q_INVOKABLE void addLightProbe(const QString &filePaths);
+ Q_INVOKABLE void updateContextMenuActionsEnableState();
+
+ Q_INVOKABLE QString getUniqueEffectPath(const QString &parentFolder, const QString &effectName);
+ Q_INVOKABLE bool createNewEffect(const QString &effectPath, bool openEffectMaker = true);
+
+ Q_INVOKABLE bool canCreateEffects() const;
+
+ Q_INVOKABLE void showInGraphicalShell(const QString &path);
+ Q_INVOKABLE QString showInGraphicalShellMsg() const;
signals:
void itemActivated(const QString &itemName);
@@ -87,7 +105,11 @@ signals:
const QList<QUrl> &complexFilePaths,
const QString &targetDirPath);
void directoryCreated(const QString &path);
- void addTexturesRequested(const QStringList &filePaths, QmlDesigner::AddTextureMode mode);
+ void hasMaterialLibraryChanged();
+ void hasSceneEnvChanged();
+ void isDraggingChanged();
+ void endDrag();
+ void deleteSelectedAssetsRequested();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -95,8 +117,12 @@ protected:
private:
void reloadQmlSource();
- void addResources(const QStringList &files);
+ void addResources(const QStringList &files, bool showDialog = true);
void updateSearch();
+ void setIsDragging(bool val);
+
+ void setHasMaterialLibrary(bool enable);
+ void setHasSceneEnv(bool b);
QSize m_itemIconSize;
@@ -104,16 +130,20 @@ private:
AssetsLibraryIconProvider *m_assetsIconProvider = nullptr;
AssetsLibraryModel *m_assetsModel = nullptr;
+ AssetsLibraryView *m_assetsView = nullptr;
+ CreateTextures m_createTextures = nullptr;
- QScopedPointer<QQuickWidget> m_assetsWidget;
+ QScopedPointer<StudioQuickWidget> m_assetsWidget;
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
- QPointer<Model> m_model;
QStringList m_assetsToDrag;
bool m_updateRetry = false;
QString m_filterText;
QPoint m_dragStartPoint;
+ bool m_hasMaterialLibrary = false;
+ bool m_hasSceneEnv = false;
+ bool m_isDragging = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx.png
new file mode 100644
index 0000000000..077b2f3d8d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx@2x.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx@2x.png
new file mode 100644
index 0000000000..3dca205a4f
--- /dev/null
+++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx_128.png b/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx_128.png
new file mode 100644
index 0000000000..ed897ae414
--- /dev/null
+++ b/src/plugins/qmldesigner/components/assetslibrary/images/asset_ktx_128.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
index ff3a27e9f0..559e8ea69c 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_defaultAction(new DefaultAction(description))
+ : m_pureAction(new DefaultAction(description))
{
const Utils::Icon defaultIcon({
{":/utils/images/select.png", Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle);
@@ -16,14 +16,14 @@ AbstractAction::AbstractAction(const QString &description)
action()->setIcon(defaultIcon.icon());
}
-AbstractAction::AbstractAction(DefaultAction *action)
- : m_defaultAction(action)
+AbstractAction::AbstractAction(PureActionInterface *action)
+ : m_pureAction(action)
{
}
QAction *AbstractAction::action() const
{
- return m_defaultAction.data();
+ return m_pureAction->action();
}
void AbstractAction::currentContextChanged(const SelectionContext &selectionContext)
@@ -34,12 +34,13 @@ void AbstractAction::currentContextChanged(const SelectionContext &selectionCont
void AbstractAction::updateContext()
{
- m_defaultAction->setSelectionContext(m_selectionContext);
+ m_pureAction->setSelectionContext(m_selectionContext);
if (m_selectionContext.isValid()) {
- m_defaultAction->setEnabled(isEnabled(m_selectionContext));
- m_defaultAction->setVisible(isVisible(m_selectionContext));
- if (m_defaultAction->isCheckable())
- m_defaultAction->setChecked(isChecked(m_selectionContext));
+ QAction *action = m_pureAction->action();
+ action->setEnabled(isEnabled(m_selectionContext));
+ action->setVisible(isVisible(m_selectionContext));
+ if (action->isCheckable())
+ action->setChecked(isChecked(m_selectionContext));
}
}
@@ -50,12 +51,12 @@ bool AbstractAction::isChecked(const SelectionContext &) const
void AbstractAction::setCheckable(bool checkable)
{
- m_defaultAction->setCheckable(checkable);
+ action()->setCheckable(checkable);
}
-DefaultAction *AbstractAction::defaultAction() const
+PureActionInterface *AbstractAction::pureAction() const
{
- return m_defaultAction.data();
+ return m_pureAction.data();
}
SelectionContext AbstractAction::selectionContext() const
@@ -65,6 +66,7 @@ SelectionContext AbstractAction::selectionContext() const
DefaultAction::DefaultAction(const QString &description)
: QAction(description, nullptr)
+ , PureActionInterface(this)
{
connect(this, &QAction::triggered, this, &DefaultAction::actionTriggered);
}
@@ -74,4 +76,15 @@ void DefaultAction::setSelectionContext(const SelectionContext &selectionContext
m_selectionContext = selectionContext;
}
+PureActionInterface::PureActionInterface(QAction *action)
+ : m_action(action)
+{
+
+}
+
+QAction *PureActionInterface::action()
+{
+ return m_action;
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.h b/src/plugins/qmldesigner/components/componentcore/abstractaction.h
index 320879e7ed..ca4cc582ce 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractaction.h
+++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.h
@@ -10,7 +10,19 @@
namespace QmlDesigner {
-class QMLDESIGNERCOMPONENTS_EXPORT DefaultAction : public QAction
+class QMLDESIGNERCOMPONENTS_EXPORT PureActionInterface
+{
+public:
+ explicit PureActionInterface(QAction *action);
+ virtual ~PureActionInterface() = default;
+ virtual void setSelectionContext(const SelectionContext &selectionContext) = 0;
+ QAction *action();
+
+private:
+ QAction *m_action = nullptr;
+};
+
+class QMLDESIGNERCOMPONENTS_EXPORT DefaultAction : public QAction, public PureActionInterface
{
Q_OBJECT
@@ -19,7 +31,7 @@ public:
// virtual function instead of slot
virtual void actionTriggered([[maybe_unused]] bool enable) {}
- void setSelectionContext(const SelectionContext &selectionContext);
+ virtual void setSelectionContext(const SelectionContext &selectionContext) override;
protected:
SelectionContext m_selectionContext;
@@ -29,10 +41,10 @@ class QMLDESIGNERCOMPONENTS_EXPORT AbstractAction : public ActionInterface
{
public:
AbstractAction(const QString &description = QString());
- AbstractAction(DefaultAction *action);
+ AbstractAction(PureActionInterface *action);
QAction *action() const override final;
- DefaultAction *defaultAction() const;
+ PureActionInterface *pureAction() const;
void currentContextChanged(const SelectionContext &selectionContext) override;
@@ -46,7 +58,7 @@ protected:
SelectionContext selectionContext() const;
private:
- QScopedPointer<DefaultAction> m_defaultAction;
+ QScopedPointer<PureActionInterface> m_pureAction;
SelectionContext m_selectionContext;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
index 46067a6efe..288b8e409d 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "abstractactiongroup.h"
+#include "qmleditormenu.h"
#include <QMenu>
@@ -9,10 +10,14 @@ namespace QmlDesigner {
AbstractActionGroup::AbstractActionGroup(const QString &displayName) :
m_displayName(displayName),
- m_menu(new QMenu)
+ m_menu(new QmlEditorMenu)
{
m_menu->setTitle(displayName);
m_action = m_menu->menuAction();
+
+ QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.data());
+ if (qmlEditorMenu)
+ qmlEditorMenu->setIconsVisible(false);
}
ActionInterface::Type AbstractActionGroup::type() const
diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp
index 56ab654912..6ef8bbb6c9 100644
--- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp
@@ -5,7 +5,7 @@
#include "designermcumanager.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <QComboBox>
#include <QSettings>
@@ -14,7 +14,7 @@ namespace QmlDesigner {
static QString styleConfigFileName(const QString &qmlFileName)
{
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(Utils::FilePath::fromString(qmlFileName));
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(Utils::FilePath::fromString(qmlFileName));
if (currentProject) {
const QList<Utils::FilePath> fileNames = currentProject->files(
@@ -31,22 +31,8 @@ ChangeStyleWidgetAction::ChangeStyleWidgetAction(QObject *parent) : QWidgetActio
{
// The Default style was renamed to Basic in Qt 6. In Qt 6, "Default"
// will result in a platform-specific style being chosen.
- items = {
- {"Basic", "Basic", {}},
- {"Default", "Default", {}},
- {"Fusion", "Fusion", {}},
- {"Imagine", "Imagine", {}},
- {"Material Light", "Material", "Light"},
- {"Material Dark", "Material", "Dark"},
- {"Universal Light", "Universal", "Light"},
- {"Universal Dark", "Universal", "Dark"},
- {"Universal System", "Universal", "System"}
- };
- if (Utils::HostOsInfo::isMacHost())
- items.append({"macOS", "macOS", {}});
- if (Utils::HostOsInfo::isWindowsHost())
- items.append({"Windows", "Windows", {}});
+ items = getAllStyleItems();
}
void ChangeStyleWidgetAction::handleModelUpdate(const QString &style)
@@ -59,12 +45,35 @@ const QList<StyleWidgetEntry> ChangeStyleWidgetAction::styleItems() const
return items;
}
-void ChangeStyleWidgetAction::changeStyle(const QString &style)
+QList<StyleWidgetEntry> ChangeStyleWidgetAction::getAllStyleItems()
+{
+ QList<StyleWidgetEntry> items = {{"Basic", "Basic", {}},
+ {"Default", "Default", {}},
+ {"Fusion", "Fusion", {}},
+ {"Imagine", "Imagine", {}},
+ {"Material Light", "Material", "Light"},
+ {"Material Dark", "Material", "Dark"},
+ {"Universal Light", "Universal", "Light"},
+ {"Universal Dark", "Universal", "Dark"},
+ {"Universal System", "Universal", "System"}};
+
+ if (Utils::HostOsInfo::isMacHost())
+ items.append({"macOS", "macOS", {}});
+ if (Utils::HostOsInfo::isWindowsHost())
+ items.append({"Windows", "Windows", {}});
+
+ return items;
+}
+
+void ChangeStyleWidgetAction::changeCurrentStyle(const QString &style, const QString &qmlFileName)
{
if (style.isEmpty())
return;
- const Utils::FilePath configFileName = Utils::FilePath::fromString(styleConfigFileName(qmlFileName));
+ auto items = getAllStyleItems();
+
+ const Utils::FilePath configFileName = Utils::FilePath::fromString(
+ styleConfigFileName(qmlFileName));
if (configFileName.exists()) {
QSettings infiFile(configFileName.toString(), QSettings::IniFormat);
@@ -86,14 +95,39 @@ void ChangeStyleWidgetAction::changeStyle(const QString &style)
if (!styleTheme.isEmpty())
infiFile.setValue((styleName + "/Theme"), styleTheme);
- }
- else {
+ } else {
infiFile.setValue("Controls/Style", style);
}
+ }
+}
- if (view)
- view->resetPuppet();
+int ChangeStyleWidgetAction::getCurrentStyle(const QString &fileName)
+{
+ const QString confFileName = styleConfigFileName(fileName);
+
+ if (Utils::FilePath::fromString(confFileName).exists()) {
+ QSettings infiFile(confFileName, QSettings::IniFormat);
+ const QString styleName = infiFile.value("Controls/Style", "Basic").toString();
+ const QString styleTheme = infiFile.value(styleName + "/Theme", "").toString();
+ const auto items = getAllStyleItems();
+
+ int i = 0;
+ for (const auto &item : items) {
+ if (item.styleName == styleName && item.styleTheme == styleTheme)
+ return i;
+ ++i;
+ }
}
+
+ return 0;
+}
+
+void ChangeStyleWidgetAction::handleStyleChanged(const QString &style)
+{
+ changeCurrentStyle(style, qmlFileName);
+
+ if (view)
+ view->resetPuppet();
}
const char enabledTooltip[] = QT_TRANSLATE_NOOP("ChangeStyleWidgetAction",
@@ -134,8 +168,7 @@ QWidget *ChangeStyleWidgetAction::createWidget(QWidget *parent)
}
});
- connect(comboBox, &QComboBox::textActivated,
- this, &ChangeStyleWidgetAction::changeStyle);
+ connect(comboBox, &QComboBox::textActivated, this, &ChangeStyleWidgetAction::handleStyleChanged);
return comboBox;
}
diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.h b/src/plugins/qmldesigner/components/componentcore/changestyleaction.h
index 3d8d1c7eaa..673c591c20 100644
--- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.h
+++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.h
@@ -44,8 +44,14 @@ public:
const QList<StyleWidgetEntry> styleItems() const;
+ static QList<StyleWidgetEntry> getAllStyleItems();
+
+ static void changeCurrentStyle(const QString &style, const QString &qmlFileName);
+
+ static int getCurrentStyle(const QString &fileName);
+
public slots:
- void changeStyle(const QString &style);
+ void handleStyleChanged(const QString &style);
protected:
QWidget *createWidget(QWidget *parent) override;
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index e7df0ab9b4..5644648336 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -66,7 +66,6 @@ const char layoutFillHeightCommandId[] = "LayoutFillHeight";
const char goIntoComponentCommandId[] = "GoIntoComponent";
const char mergeTemplateCommandId[] = "MergeTemplate";
const char goToImplementationCommandId[] = "GoToImplementation";
-const char addSignalHandlerCommandId[] = "AddSignalHandler";
const char makeComponentCommandId[] = "MakeComponent";
const char editMaterialCommandId[] = "EditMaterial";
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
@@ -76,7 +75,6 @@ const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedC
const char flowAssignEffectCommandId[] = "AssignFlowEffect";
const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect";
const char addToGroupItemCommandId[] = "AddToGroupItem";
-const char removeGroupItemCommandId[] = "RemoveToGroupItem";
const char fitRootToScreenCommandId[] = "FitRootToScreen";
const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen";
const char editAnnotationsCommandId[] = "EditAnnotation";
@@ -121,11 +119,10 @@ const char resetPositionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen
const char copyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Copy Formatting");
const char applyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Apply Formatting");
-const char enterComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Enter Component");
+const char enterComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Component");
const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Merge with Template");
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
-const char addSignalHandlerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add New Signal Handler");
-const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Make Component");
+const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component");
const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material");
const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations");
const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area");
diff --git a/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp b/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp
index b752729555..f4b6697c86 100644
--- a/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp
@@ -6,6 +6,7 @@
#include "qmldesignerplugin.h"
#include <nodeabstractproperty.h>
+#include <toolbar.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/idocument.h>
@@ -60,6 +61,7 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
{
if (!m_isInternalCalled) {
crumblePath()->clear();
+ m_pathes.clear();
} else {
// If the path already exists in crumblePath, pop up to first instance of that to avoid
// cyclical crumblePath
@@ -71,8 +73,10 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
}
if (match != -1) {
- for (int i = crumblePath()->length() - 1 - match; i > 0; --i)
+ for (int i = crumblePath()->length() - 1 - match; i > 0; --i) {
crumblePath()->popElement();
+ m_pathes.removeLast();
+ }
}
}
@@ -81,10 +85,13 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
CrumbleBarInfo crumbleBarInfo;
crumbleBarInfo.fileName = fileName;
crumblePath()->pushElement(fileName.fileName(), QVariant::fromValue(crumbleBarInfo));
+ m_pathes.append({fileName, fileName.fileName(), {}});
}
m_isInternalCalled = false;
updateVisibility();
+
+ emit pathChanged();
}
void CrumbleBar::pushInFileComponent(const ModelNode &modelNode)
@@ -97,10 +104,13 @@ void CrumbleBar::pushInFileComponent(const ModelNode &modelNode)
crumblePath()->popElement();
crumblePath()->pushElement(crumbleBarInfo.displayName, QVariant::fromValue(crumbleBarInfo));
+ m_pathes.append({{}, crumbleBarInfo.displayName, modelNode});
m_isInternalCalled = false;
updateVisibility();
+
+ emit pathChanged();
}
void CrumbleBar::nextFileIsCalledInternally()
@@ -120,6 +130,21 @@ Utils::CrumblePath *CrumbleBar::crumblePath()
return m_crumblePath;
}
+QStringList CrumbleBar::path() const
+{
+ QStringList list;
+ for (auto &path : m_pathes) {
+ list.append(path.displayName);
+ }
+
+ return list;
+}
+
+QList<CrumbleBarInfo> CrumbleBar::infos() const
+{
+ return m_pathes;
+}
+
bool CrumbleBar::showSaveDialog()
{
bool canceled = false;
@@ -151,11 +176,15 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data)
if (!inlineComp && !showSaveDialog())
return;
- while (clickedCrumbleBarInfo != crumblePath()->dataForLastIndex().value<CrumbleBarInfo>())
+ while (clickedCrumbleBarInfo != crumblePath()->dataForLastIndex().value<CrumbleBarInfo>()) {
crumblePath()->popElement();
+ m_pathes.removeLast();
+ }
- if (crumblePath()->dataForLastIndex().value<CrumbleBarInfo>().modelNode.isValid())
+ if (crumblePath()->dataForLastIndex().value<CrumbleBarInfo>().modelNode.isValid()) {
crumblePath()->popElement();
+ m_pathes.removeLast();
+ }
m_isInternalCalled = true;
if (inlineComp) {
@@ -164,6 +193,7 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data)
QmlDesignerPlugin::instance()->viewManager().setComponentViewToMaster();
} else {
crumblePath()->popElement();
+ m_pathes.removeLast();
nextFileIsCalledInternally();
Core::EditorManager::openEditor(clickedCrumbleBarInfo.fileName,
Utils::Id(),
@@ -175,12 +205,14 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data)
QmlDesignerPlugin::instance()->viewManager().setComponentViewToMaster();
}
}
+ emit pathChanged();
updateVisibility();
}
void CrumbleBar::updateVisibility()
{
- crumblePath()->setVisible(crumblePath()->length() > 1);
+ if (!ToolBar::isVisible())
+ crumblePath()->setVisible(crumblePath()->length() > 1);
}
bool operator ==(const CrumbleBarInfo &first, const CrumbleBarInfo &second)
diff --git a/src/plugins/qmldesigner/components/componentcore/crumblebar.h b/src/plugins/qmldesigner/components/componentcore/crumblebar.h
index 232d8237d7..b9da4488f6 100644
--- a/src/plugins/qmldesigner/components/componentcore/crumblebar.h
+++ b/src/plugins/qmldesigner/components/componentcore/crumblebar.h
@@ -10,6 +10,24 @@
namespace QmlDesigner {
+class CrumbleBarInfo {
+public:
+
+ CrumbleBarInfo() = default;
+
+ CrumbleBarInfo(Utils::FilePath f,
+ QString d,
+ ModelNode m) :
+ fileName(f),
+ displayName(d),
+ modelNode(m)
+ {}
+
+ Utils::FilePath fileName;
+ QString displayName;
+ ModelNode modelNode;
+};
+
class CrumbleBar : public QObject
{
Q_OBJECT
@@ -24,21 +42,23 @@ public:
Utils::CrumblePath *crumblePath();
-private:
+ QStringList path() const;
+
+ QList<CrumbleBarInfo> infos() const;
+
void onCrumblePathElementClicked(const QVariant &data);
+
+signals:
+ void pathChanged();
+
+private:
void updateVisibility();
bool showSaveDialog();
private:
bool m_isInternalCalled = false;
Utils::CrumblePath *m_crumblePath = nullptr;
-};
-
-class CrumbleBarInfo {
-public:
- Utils::FilePath fileName;
- QString displayName;
- ModelNode modelNode;
+ QList<CrumbleBarInfo> m_pathes;
};
bool operator ==(const CrumbleBarInfo &first, const CrumbleBarInfo &second);
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index 535703ac5f..77976993e4 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -9,8 +9,10 @@
#include "designericons.h"
#include "designermcumanager.h"
#include "formatoperation.h"
+#include "groupitemaction.h"
#include "modelnodecontextmenu_helper.h"
#include "qmldesignerconstants.h"
+#include "qmleditormenu.h"
#include "rewritingexception.h"
#include <bindingproperty.h>
#include <nodehints.h>
@@ -112,12 +114,14 @@ void DesignerActionManager::polishActions() const
Core::Context qmlDesignerEditor3DContext(Constants::C_QMLEDITOR3D);
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
+ Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
Core::Context qmlDesignerUIContext;
qmlDesignerUIContext.add(qmlDesignerFormEditorContext);
qmlDesignerUIContext.add(qmlDesignerEditor3DContext);
qmlDesignerUIContext.add(qmlDesignerNavigatorContext);
qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext);
+ qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext);
for (auto *action : actions) {
if (!action->menuId().isEmpty()) {
@@ -233,7 +237,7 @@ ModelNodePreviewImageOperation DesignerActionManager::modelNodePreviewOperation(
bool DesignerActionManager::externalDragHasSupportedAssets(const QMimeData *mimeData) const
{
- if (!mimeData->hasUrls())
+ if (!mimeData->hasUrls() || mimeData->hasFormat(Constants::MIME_TYPE_ASSETS))
return false;
QSet<QString> filtersSet;
@@ -292,6 +296,11 @@ QIcon DesignerActionManager::contextIcon(int contextId) const
return m_designerIcons->icon(DesignerIcons::IconId(contextId), DesignerIcons::ContextMenuArea);
}
+void DesignerActionManager::addAddActionCallback(ActionAddedInterface callback)
+{
+ m_callBacks.append(callback);
+}
+
class VisiblityModelNodeAction : public ModelNodeContextMenuAction
{
public:
@@ -304,17 +313,17 @@ public:
void updateContext() override
{
- defaultAction()->setSelectionContext(selectionContext());
+ pureAction()->setSelectionContext(selectionContext());
if (selectionContext().isValid()) {
- defaultAction()->setEnabled(isEnabled(selectionContext()));
- defaultAction()->setVisible(isVisible(selectionContext()));
+ action()->setEnabled(isEnabled(selectionContext()));
+ action()->setVisible(isVisible(selectionContext()));
- defaultAction()->setCheckable(true);
+ action()->setCheckable(true);
QmlItemNode itemNode = QmlItemNode(selectionContext().currentSingleSelectedNode());
if (itemNode.isValid())
- defaultAction()->setChecked(itemNode.instanceValue("visible").toBool());
+ action()->setChecked(itemNode.instanceValue("visible").toBool());
else
- defaultAction()->setEnabled(false);
+ action()->setEnabled(false);
}
}
};
@@ -330,12 +339,12 @@ public:
{}
void updateContext() override
{
- defaultAction()->setSelectionContext(selectionContext());
+ pureAction()->setSelectionContext(selectionContext());
if (selectionContext().isValid()) {
- defaultAction()->setEnabled(isEnabled(selectionContext()));
- defaultAction()->setVisible(isVisible(selectionContext()));
+ action()->setEnabled(isEnabled(selectionContext()));
+ action()->setVisible(isVisible(selectionContext()));
- defaultAction()->setCheckable(true);
+ action()->setCheckable(true);
QmlItemNode itemNode = QmlItemNode(selectionContext().currentSingleSelectedNode());
if (itemNode.isValid()) {
bool flag = false;
@@ -343,9 +352,9 @@ public:
|| itemNode.propertyAffectedByCurrentState(m_propertyName)) {
flag = itemNode.modelValue(m_propertyName).toBool();
}
- defaultAction()->setChecked(flag);
+ action()->setChecked(flag);
} else {
- defaultAction()->setEnabled(false);
+ action()->setEnabled(false);
}
}
}
@@ -578,10 +587,15 @@ QList<SlotList> getSlotsLists(const ModelNode &node)
ModelNode createNewConnection(ModelNode targetNode)
{
NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->qtQuickConnectionsMetaInfo();
- ModelNode newConnectionNode = targetNode.view()->createModelNode(
- "QtQuick.Connections", connectionsMetaInfo.majorVersion(), connectionsMetaInfo.minorVersion());
- if (QmlItemNode::isValidQmlItemNode(targetNode))
+ ModelNode newConnectionNode = targetNode.view()->createModelNode(connectionsMetaInfo.typeName(),
+ connectionsMetaInfo.majorVersion(),
+ connectionsMetaInfo.minorVersion());
+ if (QmlItemNode::isValidQmlItemNode(targetNode)) {
targetNode.nodeAbstractProperty("data").reparentHere(newConnectionNode);
+ } else {
+ targetNode.view()->rootModelNode().defaultNodeAbstractProperty().reparentHere(
+ newConnectionNode);
+ }
newConnectionNode.bindingProperty("target").setExpression(targetNode.id());
@@ -648,11 +662,11 @@ public:
const QString propertyName = QString::fromUtf8(signalHandler.name());
- QMenu *activeSignalHandlerGroup = new QMenu(propertyName, menu());
+ QMenu *activeSignalHandlerGroup = new QmlEditorMenu(propertyName, menu());
- QMenu *editSignalGroup = new QMenu(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
- "Change Signal"),
- menu());
+ QMenu *editSignalGroup = new QmlEditorMenu(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
+ "Change Signal"),
+ menu());
for (const auto &signalStr : signalsList) {
if (prependSignal(signalStr).toUtf8() == signalHandler.name())
@@ -679,9 +693,9 @@ public:
activeSignalHandlerGroup->addMenu(editSignalGroup);
if (!slotsLists.isEmpty()) {
- QMenu *editSlotGroup = new QMenu(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
- "Change Slot"),
- menu());
+ QMenu *editSlotGroup = new QmlEditorMenu(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
+ "Change Slot"),
+ menu());
if (slotsLists.size() == 1) {
for (const auto &slot : slotsLists.at(0).slotEntries) {
@@ -701,7 +715,7 @@ public:
}
} else {
for (const auto &slotCategory : slotsLists) {
- QMenu *slotCategoryMenu = new QMenu(slotCategory.categoryName, menu());
+ QMenu *slotCategoryMenu = new QmlEditorMenu(slotCategory.categoryName, menu());
for (const auto &slot : slotCategory.slotEntries) {
ActionTemplate *newSlotAction = new ActionTemplate(
(slot.name + "Id").toLatin1(),
@@ -754,12 +768,12 @@ public:
}
//singular add connection:
- QMenu *addConnection = new QMenu(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
- "Add Signal Handler")),
- menu());
+ QMenu *addConnection = new QmlEditorMenu(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
+ "Add Signal Handler")),
+ menu());
for (const auto &signalStr : signalsList) {
- QMenu *newSignal = new QMenu(signalStr, addConnection);
+ QMenu *newSignal = new QmlEditorMenu(signalStr, addConnection);
if (!slotsLists.isEmpty()) {
if (slotsLists.size() == 1) {
@@ -782,7 +796,7 @@ public:
}
} else {
for (const auto &slotCategory : slotsLists) {
- QMenu *slotCategoryMenu = new QMenu(slotCategory.categoryName, menu());
+ QMenu *slotCategoryMenu = new QmlEditorMenu(slotCategory.categoryName, menu());
for (const auto &slot : slotCategory.slotEntries) {
ActionTemplate *newSlot = new ActionTemplate(
QString(signalStr + slot.name + "Id").toLatin1(),
@@ -1328,6 +1342,26 @@ bool anchorsMenuEnabled(const SelectionContext &context)
|| singleSelectionItemIsAnchored(context);
}
+static QIcon createResetIcon(const QStringList &basicIconAddresses)
+{
+ using namespace Utils;
+ static const IconMaskAndColor resetMask({":/utils/images/iconoverlay_reset.png",
+ Theme::IconsStopToolBarColor});
+ QList<IconMaskAndColor> iconMaskList = transform(basicIconAddresses, [=] (const QString &refAddr) {
+ return IconMaskAndColor(
+ FilePath::fromString(refAddr),
+ Theme::IconsBaseColor);
+ });
+
+ QIcon finalIcon = Icon(iconMaskList).icon();
+ iconMaskList.append(resetMask);
+ QIcon finalOn = Icon(iconMaskList).icon();
+ for (const QSize &iSize : finalIcon.availableSizes()) {
+ for (const QIcon::Mode &mode : {QIcon::Normal, QIcon::Disabled, QIcon::Active, QIcon::Selected})
+ finalIcon.addPixmap(finalOn.pixmap(iSize, mode, QIcon::On), mode, QIcon::On);
+ }
+ return finalIcon;
+}
void DesignerActionManager::createDefaultDesignerActions()
{
@@ -1364,7 +1398,7 @@ void DesignerActionManager::createDefaultDesignerActions()
Priorities::ArrangeCategory,
&selectionNotEmpty));
- addDesignerAction(new SeperatorDesignerAction(arrangeCategory, 10));
+ addDesignerAction(new SeparatorDesignerAction(arrangeCategory, 10));
addDesignerAction(new ModelNodeContextMenuAction(
toFrontCommandId,
@@ -1379,7 +1413,7 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ModelNodeContextMenuAction(
raiseCommandId,
raiseDisplayName,
- Utils::Icon({{":/qmldesigner/icon/designeractions/images/raise.png", Utils::Theme::IconsBaseColor}}).icon(),
+ {},
arrangeCategory,
QKeySequence(),
11,
@@ -1389,7 +1423,7 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ModelNodeContextMenuAction(
lowerCommandId,
lowerDisplayName,
- Utils::Icon({{":/qmldesigner/icon/designeractions/images/lower.png", Utils::Theme::IconsBaseColor}}).icon(),
+ {},
arrangeCategory,
QKeySequence(),
12,
@@ -1406,7 +1440,7 @@ void DesignerActionManager::createDefaultDesignerActions()
&toBack,
&lowerAvailable));
- addDesignerAction(new SeperatorDesignerAction(arrangeCategory, 20));
+ addDesignerAction(new SeparatorDesignerAction(arrangeCategory, 20));
addDesignerAction(new ModelNodeContextMenuAction(
reverseCommandId,
@@ -1424,75 +1458,45 @@ void DesignerActionManager::createDefaultDesignerActions()
Priorities::EditCategory,
&selectionNotEmpty));
- addDesignerAction(new SeperatorDesignerAction(editCategory, 30));
-
- addDesignerAction(
- new ModelNodeAction(resetPositionCommandId,
- resetPositionDisplayName,
- Utils::Icon({{":/utils/images/pan.png", Utils::Theme::IconsBaseColor},
- {":/utils/images/iconoverlay_reset.png",
- Utils::Theme::IconsStopToolBarColor}})
- .icon(),
- resetPositionTooltip,
- editCategory,
- QKeySequence("Ctrl+d"),
- 32,
- &resetPosition,
- &selectionNotEmptyAndHasXorYProperty));
-
- const QString fontName = "qtds_propertyIconFont.ttf";
- const QColor iconColorDefault(Theme::getColor(Theme::IconsBaseColor));
- const QColor iconColorDisabled(Theme::getColor(Theme::IconsDisabledColor));
- const QString copyUnicode = Theme::getIconUnicode(Theme::Icon::copyStyle);
- const QString pasteUnicode = Theme::getIconUnicode(Theme::Icon::pasteStyle);
-
- const auto copyDefault = Utils::StyleHelper::IconFontHelper(copyUnicode,
- iconColorDefault,
- QSize(28, 28),
- QIcon::Normal);
- const auto copyDisabled = Utils::StyleHelper::IconFontHelper(copyUnicode,
- iconColorDisabled,
- QSize(28, 28),
- QIcon::Disabled);
- const QIcon copyIcon = Utils::StyleHelper::getIconFromIconFont(fontName,
- {copyDefault, copyDisabled});
-
- const auto pasteDefault = Utils::StyleHelper::IconFontHelper(pasteUnicode,
- iconColorDefault,
- QSize(28, 28),
- QIcon::Normal);
- const auto pasteDisabled = Utils::StyleHelper::IconFontHelper(pasteUnicode,
- iconColorDisabled,
- QSize(28, 28),
- QIcon::Disabled);
- const QIcon pasteIcon = Utils::StyleHelper::getIconFromIconFont(fontName,
- {pasteDefault, pasteDisabled});
-
- addDesignerAction(new ModelNodeAction(copyFormatCommandId,
- copyFormatDisplayName,
- copyIcon,
- copyFormatTooltip,
- editCategory,
- QKeySequence(),
- 41,
- &copyFormat,
- &propertiesCopyable));
-
- addDesignerAction(new ModelNodeAction(applyFormatCommandId,
- applyFormatDisplayName,
- pasteIcon,
- applyFormatTooltip,
- editCategory,
- QKeySequence(),
- 42,
- &applyFormat,
- &propertiesApplyable));
+ addDesignerAction(new SeparatorDesignerAction(editCategory, 30));
+
+ addDesignerAction(new ModelNodeAction(
+ resetPositionCommandId,
+ resetPositionDisplayName,
+ createResetIcon({":/utils/images/pan.png"}),
+ resetPositionTooltip,
+ editCategory,
+ QKeySequence("Ctrl+d"),
+ 32,
+ &resetPosition,
+ &selectionNotEmptyAndHasXorYProperty));
+
+ addDesignerAction(new ModelNodeAction(
+ copyFormatCommandId,
+ copyFormatDisplayName,
+ contextIcon(DesignerIcons::CopyIcon),
+ copyFormatTooltip,
+ editCategory,
+ QKeySequence(),
+ 41,
+ &copyFormat,
+ &propertiesCopyable));
+
+ addDesignerAction(new ModelNodeAction(
+ applyFormatCommandId,
+ applyFormatDisplayName,
+ contextIcon(DesignerIcons::PasteIcon),
+ applyFormatTooltip,
+ editCategory,
+ QKeySequence(),
+ 42,
+ &applyFormat,
+ &propertiesApplyable));
addDesignerAction(new ModelNodeAction(
resetSizeCommandId,
resetSizeDisplayName,
- Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor},
- {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(),
+ createResetIcon({":/utils/images/fittoview.png"}),
resetSizeToolTip,
editCategory,
QKeySequence("shift+s"),
@@ -1500,7 +1504,7 @@ void DesignerActionManager::createDefaultDesignerActions()
&resetSize,
&selectionNotEmptyAndHasWidthOrHeightProperty));
- addDesignerAction(new SeperatorDesignerAction(editCategory, 40));
+ addDesignerAction(new SeparatorDesignerAction(editCategory, 40));
addDesignerAction(new VisiblityModelNodeAction(
visiblityCommandId,
@@ -1541,12 +1545,13 @@ void DesignerActionManager::createDefaultDesignerActions()
&anchorsReset,
&singleSelectionItemIsAnchored));
- addDesignerAction(new SeperatorDesignerAction(anchorsCategory, 10));
+ addDesignerAction(new SeparatorDesignerAction(anchorsCategory, 10));
addDesignerAction(new ParentAnchorAction(
anchorParentTopAndBottomCommandId,
anchorParentTopAndBottomDisplayName,
- {},
+ createResetIcon({":/qmldesigner/images/anchor_top.png",
+ ":/qmldesigner/images/anchor_bottom.png"}),
{},
anchorsCategory,
QKeySequence(),
@@ -1556,20 +1561,20 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ParentAnchorAction(
anchorParentLeftAndRightCommandId,
anchorParentLeftAndRightDisplayName,
- {},
+ createResetIcon({":/qmldesigner/images/anchor_left.png",
+ ":/qmldesigner/images/anchor_right.png"}),
{},
anchorsCategory,
QKeySequence(),
12,
AnchorLineType(AnchorLineLeft | AnchorLineRight)));
- addDesignerAction(new SeperatorDesignerAction(anchorsCategory, 20));
+ addDesignerAction(new SeparatorDesignerAction(anchorsCategory, 20));
addDesignerAction(new ParentAnchorAction(
anchorParentTopCommandId,
anchorParentTopDisplayName,
- Utils::Icon({{":/qmldesigner/images/anchor_top.png", Utils::Theme::IconsBaseColor},
- {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(),
+ createResetIcon({":/qmldesigner/images/anchor_top.png"}),
{},
anchorsCategory,
QKeySequence(),
@@ -1579,8 +1584,7 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ParentAnchorAction(
anchorParentBottomCommandId,
anchorParentBottomDisplayName,
- Utils::Icon({{":/qmldesigner/images/anchor_bottom.png", Utils::Theme::IconsBaseColor},
- {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(),
+ createResetIcon({":/qmldesigner/images/anchor_bottom.png"}),
{},
anchorsCategory,
QKeySequence(),
@@ -1590,8 +1594,7 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ParentAnchorAction(
anchorParentLeftCommandId,
anchorParentLeftDisplayName,
- Utils::Icon({{":/qmldesigner/images/anchor_left.png", Utils::Theme::IconsBaseColor},
- {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(),
+ createResetIcon({":/qmldesigner/images/anchor_left.png"}),
{},
anchorsCategory,
QKeySequence(),
@@ -1601,8 +1604,7 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ParentAnchorAction(
anchorParentRightCommandId,
anchorParentRightDisplayName,
- Utils::Icon({{":/qmldesigner/images/anchor_right.png", Utils::Theme::IconsBaseColor},
- {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(),
+ createResetIcon({":/qmldesigner/images/anchor_right.png"}),
{},
anchorsCategory,
QKeySequence(),
@@ -1631,12 +1633,10 @@ void DesignerActionManager::createDefaultDesignerActions()
&selectionEnabled,
&selectionEnabled));
- addDesignerAction(new ActionGroup(
- groupCategoryDisplayName,
- groupCategory,
+ addDesignerAction(new GroupItemAction(
contextIcon(DesignerIcons::GroupSelectionIcon),
- Priorities::Group,
- &studioComponentsAvailableAndSelectionCanBeLayouted));
+ {},
+ Priorities::Group));
addDesignerAction(new ActionGroup(
flowCategoryDisplayName,
@@ -1659,16 +1659,16 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(effectMenu);
addDesignerAction(new ModelNodeFormEditorAction(
- createFlowActionAreaCommandId,
- createFlowActionAreaDisplayName,
- addIcon.icon(),
- addFlowActionToolTip,
- flowCategory,
- {},
- 1,
- &createFlowActionArea,
- &isFlowItem,
- &flowOptionVisible));
+ createFlowActionAreaCommandId,
+ createFlowActionAreaDisplayName,
+ addIcon.icon(),
+ addFlowActionToolTip,
+ flowCategory,
+ {},
+ 1,
+ &createFlowActionArea,
+ &isFlowItem,
+ &flowOptionVisible));
addDesignerAction(new ModelNodeContextMenuAction(
setFlowStartCommandId,
@@ -1699,20 +1699,21 @@ void DesignerActionManager::createDefaultDesignerActions()
addCustomTransitionEffectAction();
addDesignerAction(new ModelNodeContextMenuAction(
- selectFlowEffectCommandId,
- selectEffectDisplayName,
- {},
- flowCategory,
- {},
- 2,
- &selectFlowEffect,
- &isFlowTransitionItemWithEffect));
+ selectFlowEffectCommandId,
+ selectEffectDisplayName,
+ {},
+ flowCategory,
+ {},
+ 2,
+ &selectFlowEffect,
+ &isFlowTransitionItemWithEffect));
addDesignerAction(new ActionGroup(
stackedContainerCategoryDisplayName,
stackedContainerCategory,
- {},
+ addIcon.icon(),
Priorities::StackedContainerCategory,
+ &isStackedContainer,
&isStackedContainer));
addDesignerAction(new ModelNodeContextMenuAction(
@@ -1770,7 +1771,7 @@ void DesignerActionManager::createDefaultDesignerActions()
&selectionCanBeLayouted,
&selectionCanBeLayouted));
- addDesignerAction(new SeperatorDesignerAction(layoutCategory, 0));
+ addDesignerAction(new SeparatorDesignerAction(layoutCategory, 0));
addDesignerAction(new ModelNodeContextMenuAction(
removeLayoutCommandId,
@@ -1783,34 +1784,17 @@ void DesignerActionManager::createDefaultDesignerActions()
&isLayout,
&isLayout));
- addDesignerAction(new ModelNodeContextMenuAction(addToGroupItemCommandId,
- addToGroupItemDisplayName,
- {},
- groupCategory,
- QKeySequence("Ctrl+Shift+g"),
- 1,
- &addToGroupItem,
- &selectionCanBeLayouted));
-
- addDesignerAction(new ModelNodeContextMenuAction(removeGroupItemCommandId,
- removeGroupItemDisplayName,
- {},
- groupCategory,
- QKeySequence(),
- 2,
- &removeGroup,
- &isGroup));
-
- addDesignerAction(new ModelNodeFormEditorAction(addItemToStackedContainerCommandId,
- addItemToStackedContainerDisplayName,
- addIcon.icon(),
- addItemToStackedContainerToolTip,
- stackedContainerCategory,
- QKeySequence("Ctrl+Shift+a"),
- 1,
- &addItemToStackedContainer,
- &isStackedContainer,
- &isStackedContainer));
+ addDesignerAction(new ModelNodeFormEditorAction(
+ addItemToStackedContainerCommandId,
+ addItemToStackedContainerDisplayName,
+ addIcon.icon(),
+ addItemToStackedContainerToolTip,
+ stackedContainerCategory,
+ QKeySequence("Ctrl+Shift+a"),
+ 1,
+ &addItemToStackedContainer,
+ &isStackedContainer,
+ &isStackedContainer));
addDesignerAction(new ModelNodeContextMenuAction(
addTabBarToStackedContainerCommandId,
@@ -1850,7 +1834,8 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ModelNodeAction(
layoutRowLayoutCommandId,
layoutRowLayoutDisplayName,
- Utils::Icon({{":/qmldesigner/icon/designeractions/images/row.png", Utils::Theme::IconsBaseColor}}).icon(),
+ Utils::Icon({{":/qmldesigner/icon/designeractions/images/row.png",
+ Utils::Theme::IconsBaseColor}}).icon(),
layoutRowLayoutToolTip,
layoutCategory,
QKeySequence("Ctrl+u"),
@@ -1861,7 +1846,8 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ModelNodeAction(
layoutColumnLayoutCommandId,
layoutColumnLayoutDisplayName,
- Utils::Icon({{":/qmldesigner/icon/designeractions/images/column.png", Utils::Theme::IconsBaseColor}}).icon(),
+ Utils::Icon({{":/qmldesigner/icon/designeractions/images/column.png",
+ Utils::Theme::IconsBaseColor}}).icon(),
layoutColumnLayoutToolTip,
layoutCategory,
QKeySequence("Ctrl+l"),
@@ -1872,7 +1858,8 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ModelNodeAction(
layoutGridLayoutCommandId,
layoutGridLayoutDisplayName,
- Utils::Icon({{":/qmldesigner/icon/designeractions/images/grid.png", Utils::Theme::IconsBaseColor}}).icon(),
+ Utils::Icon({{":/qmldesigner/icon/designeractions/images/grid.png",
+ Utils::Theme::IconsBaseColor}}).icon(),
layoutGridLayoutToolTip,
layoutCategory,
QKeySequence("shift+g"),
@@ -1880,7 +1867,7 @@ void DesignerActionManager::createDefaultDesignerActions()
&layoutGridLayout,
&selectionCanBeLayoutedAndQtQuickLayoutPossibleAndNotMCU));
- addDesignerAction(new SeperatorDesignerAction(layoutCategory, 10));
+ addDesignerAction(new SeparatorDesignerAction(layoutCategory, 10));
addDesignerAction(new FillWidthModelNodeAction(
layoutFillWidthCommandId,
@@ -1902,12 +1889,12 @@ void DesignerActionManager::createDefaultDesignerActions()
&singleSelectionAndInQtQuickLayout,
&singleSelectionAndInQtQuickLayout));
- addDesignerAction(new SeperatorDesignerAction(rootCategory, Priorities::ModifySection));
- addDesignerAction(new SeperatorDesignerAction(rootCategory, Priorities::PositionSection));
- addDesignerAction(new SeperatorDesignerAction(rootCategory, Priorities::EventSection));
- addDesignerAction(new SeperatorDesignerAction(rootCategory, Priorities::AdditionsSection));
- addDesignerAction(new SeperatorDesignerAction(rootCategory, Priorities::ViewOprionsSection));
- addDesignerAction(new SeperatorDesignerAction(rootCategory, Priorities::CustomActionsSection));
+ addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::ModifySection));
+ addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::PositionSection));
+ addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::EventSection));
+ addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::AdditionsSection));
+ addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::ViewOprionsSection));
+ addDesignerAction(new SeparatorDesignerAction(rootCategory, Priorities::CustomActionsSection));
addDesignerAction(new ModelNodeContextMenuAction(
goIntoComponentCommandId,
@@ -1956,15 +1943,6 @@ void DesignerActionManager::createDefaultDesignerActions()
}
addDesignerAction(new ModelNodeContextMenuAction(
- addSignalHandlerCommandId,
- addSignalHandlerDisplayName,
- {},
- rootCategory, QKeySequence(),
- 42, &addNewSignalHandler,
- &singleSelectedAndUiFile,
- &singleSelectedAndUiFile));
-
- addDesignerAction(new ModelNodeContextMenuAction(
makeComponentCommandId,
makeComponentDisplayName,
contextIcon(DesignerIcons::MakeComponentIcon),
@@ -1986,14 +1964,15 @@ void DesignerActionManager::createDefaultDesignerActions()
&modelHasMaterial,
&isModel));
- addDesignerAction(new ModelNodeContextMenuAction(mergeTemplateCommandId,
- mergeTemplateDisplayName,
- {},
- rootCategory,
- {},
- Priorities::MergeWithTemplate,
- [&] (const SelectionContext& context) { mergeWithTemplate(context, m_externalDependencies); },
- &SelectionContextFunctors::always));
+ addDesignerAction(new ModelNodeContextMenuAction(
+ mergeTemplateCommandId,
+ mergeTemplateDisplayName,
+ contextIcon(DesignerIcons::MergeWithTemplateIcon),
+ rootCategory,
+ {},
+ Priorities::MergeWithTemplate,
+ [&] (const SelectionContext& context) { mergeWithTemplate(context, m_externalDependencies); },
+ &SelectionContextFunctors::always));
addDesignerAction(new ActionGroup(
"",
@@ -2005,15 +1984,15 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new EditListModelAction);
- addDesignerAction(new ModelNodeContextMenuAction(
- openSignalDialogCommandId,
- openSignalDialogDisplayName,
- {},
- rootCategory,
- QKeySequence(),
- Priorities::SignalsDialog,
- &openSignalDialog,
- &singleSelectionAndHasSlotTrigger));
+ addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId,
+ openSignalDialogDisplayName,
+ {},
+ rootCategory,
+ QKeySequence(),
+ Priorities::SignalsDialog,
+ &openSignalDialog,
+ &singleSelectionAndHasSlotTrigger,
+ &singleSelectionAndHasSlotTrigger));
addDesignerAction(new ModelNodeContextMenuAction(
update3DAssetCommandId,
@@ -2102,6 +2081,10 @@ void DesignerActionManager::createDefaultModelNodePreviewImageHandlers()
void DesignerActionManager::addDesignerAction(ActionInterface *newAction)
{
m_designerActions.append(QSharedPointer<ActionInterface>(newAction));
+
+ for (auto callback : m_callBacks) {
+ callback(newAction);
+ }
}
void DesignerActionManager::addCreatorCommand(Core::Command *command, const QByteArray &category, int priority,
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
index 569c495f88..e1544de114 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
@@ -14,6 +14,8 @@
#include <QToolBar>
#include <QImage>
+#include <functional>
+
QT_BEGIN_NAMESPACE
class QGraphicsItem;
class QGraphicsWidget;
@@ -27,6 +29,7 @@ class DesignerIcons;
using AddResourceOperation = std::function<AddFilesResult(const QStringList &, const QString &, bool)>;
using ModelNodePreviewImageOperation = std::function<QVariant(const ModelNode &)>;
+using ActionAddedInterface = std::function<void(ActionInterface*)>;
struct AddResourceHandler
{
@@ -121,6 +124,8 @@ public:
QHash<QString, QStringList> handleExternalAssetsDrop(const QMimeData *data) const;
QIcon contextIcon(int contextId) const;
+ void addAddActionCallback(ActionAddedInterface callback);
+
private:
void addTransitionEffectAction(const TypeName &typeName);
void addCustomTransitionEffectAction();
@@ -133,6 +138,7 @@ private:
QList<ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers;
ExternalDependenciesInterface &m_externalDependencies;
QScopedPointer<DesignerIcons> m_designerIcons;
+ QList<ActionAddedInterface> m_callBacks;
};
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.cpp b/src/plugins/qmldesigner/components/componentcore/designericons.cpp
index 2af0709c3a..053a188328 100644
--- a/src/plugins/qmldesigner/components/componentcore/designericons.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designericons.cpp
@@ -222,8 +222,17 @@ struct JsonMap<QMap<Key, Value>>
static QJsonObject json(const QMap<Key, Value> &map)
{
QJsonObject output;
- for (auto it = map.cbegin(), end = map.cend(); it != end; ++it)
- output[DesignerIconEnums<Key>::toString(it.key())] = JsonMap<Value>::json(it.value());
+
+ #if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
+ for (const auto &[key, val] : map.asKeyValueRange())
+ output[DesignerIconEnums<Key>::toString(key)] = JsonMap<Value>::json(val);
+ #else
+ const auto mapKeys = map.keys();
+ for (const Key &key : mapKeys) {
+ const Value &val = map.value(key);
+ output[DesignerIconEnums<Key>::toString(key)] = JsonMap<Value>::json(val);
+ }
+ #endif
return output;
}
@@ -390,6 +399,33 @@ void DesignerIcons::addIcon(IconId iconId, Area area, Theme::Icon themeIcon, The
addIcon(iconId, area, {themeIcon, color, size});
}
+QIcon DesignerIcons::rotateIcon(const QIcon &icon, const double &degrees)
+{
+ QIcon result;
+ const QMetaEnum &modeMetaEnum = DesignerIconEnums<QIcon::Mode>().metaEnum;
+ const QMetaEnum &stateMetaEnum = DesignerIconEnums<QIcon::State>().metaEnum;
+
+ const int maxModeIdx = modeMetaEnum.keyCount();
+ const int maxStateIdx = stateMetaEnum.keyCount();
+ for (int modeI = 0; modeI < maxModeIdx; ++modeI) {
+ const QIcon::Mode mode = static_cast<QIcon::Mode>(modeMetaEnum.value(modeI));
+ for (int stateI = 0; stateI < maxStateIdx; ++stateI) {
+ const QIcon::State state = static_cast<QIcon::State>(stateMetaEnum.value(stateI));
+ const QList<QSize> iconSizes = icon.availableSizes();
+ for (const QSize &size : iconSizes) {
+ QTransform tr;
+ tr.translate(size.width()/2, size.height()/2);
+ tr.rotate(degrees);
+
+ QPixmap pix = icon.pixmap(size, mode, state).transformed(tr);
+ result.addPixmap(pix, mode, state);
+ }
+ }
+ }
+
+ return result;
+}
+
QList<Utils::StyleHelper::IconFontHelper> DesignerIcons::helperList(const IconId &iconId,
const Area &area) const
{
diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h
index 342016b77c..f460707b91 100644
--- a/src/plugins/qmldesigner/components/componentcore/designericons.h
+++ b/src/plugins/qmldesigner/components/componentcore/designericons.h
@@ -24,14 +24,14 @@ public:
QIcon::Mode mode = QIcon::Normal,
QIcon::State state = QIcon::Off);
+ IconFontHelper();
+
static IconFontHelper fromJson(const QJsonObject &jsonObject);
QJsonObject toJson() const;
Theme::Icon themeIcon() const;
Theme::Color themeColor() const;
private:
- IconFontHelper();
-
Theme::Icon mThemeIcon;
Theme::Color mThemeColor;
};
@@ -45,25 +45,54 @@ public:
enum IconId {
AddMouseAreaIcon,
AlignBottomIcon,
+ AlignCameraToViewIcon,
AlignLeftIcon,
AlignRightIcon,
AlignTopIcon,
+ AlignViewToCameraIcon,
AnchorsIcon,
AnnotationIcon,
ArrangeIcon,
+ CameraIcon,
+ CameraOrthographicIcon,
+ CameraPerspectiveIcon,
ConnectionsIcon,
+ CopyIcon,
+ CreateIcon,
+ DeleteIcon,
+ DuplicateIcon,
+ EditComponentIcon,
EditIcon,
EnterComponentIcon,
EventListIcon,
+ FitSelectedIcon,
GroupSelectionIcon,
+ ImportedModelsIcon,
LayoutsIcon,
+ LightIcon,
+ LightDirectionalIcon,
+ LightPointIcon,
+ LightSpotIcon,
MakeComponentIcon,
+ MaterialIcon,
MergeWithTemplateIcon,
+ MinimalDownArrowIcon,
+ ModelConeIcon,
+ ModelCubeIcon,
+ ModelCylinderIcon,
+ ModelPlaneIcon,
+ ModelSphereIcon,
+ ParentIcon,
+ PasteIcon,
PositionsersIcon,
+ PrimitivesIcon,
+ ResetViewIcon,
SelecionIcon,
+ ShowBoundsIcon,
+ SimpleCheckIcon,
SnappingIcon,
TimelineIcon,
- ShowBoundsIcon,
+ ToggleGroupIcon,
VisibilityIcon
};
Q_ENUM(IconId)
@@ -80,7 +109,6 @@ public:
Hovered = QIcon::Active,
Selected = QIcon::Selected
};
-
Q_ENUM(Mode)
enum State {
@@ -123,6 +151,8 @@ public:
Theme::Color color,
const QSize &size);
+ static QIcon rotateIcon(const QIcon &icon, const double &degrees);
+
private:
QList<Utils::StyleHelper::IconFontHelper> helperList(const IconId &iconId,
const Area &area) const;
diff --git a/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp b/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp
new file mode 100644
index 0000000000..f60e0ee5e3
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/groupitemaction.cpp
@@ -0,0 +1,155 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "groupitemaction.h"
+
+#include "nodeabstractproperty.h"
+#include "nodelistproperty.h"
+
+#include <utils/algorithm.h>
+
+using namespace QmlDesigner;
+
+namespace {
+
+bool selectionsAreSiblings(const QList<ModelNode> &selectedItems)
+{
+ const QList<ModelNode> prunedSelectedItems = ModelNode::pruneChildren(selectedItems);
+ if (prunedSelectedItems.size() < 2)
+ return false;
+
+ ModelNode modelItemNode(prunedSelectedItems.first());
+ if (!modelItemNode.isValid())
+ return false;
+
+ ModelNode parentNode = modelItemNode.parentProperty().parentModelNode();
+ if (!parentNode.isValid())
+ return false;
+
+ for (const ModelNode &node : Utils::span(prunedSelectedItems).subspan(1)) {
+ if (!node.isValid())
+ return false;
+
+ if (node.parentProperty().parentModelNode() != parentNode)
+ return false;
+ }
+
+ return true;
+}
+
+ModelNode availableGroupNode(const SelectionContext &selection)
+{
+ if (!selection.isValid())
+ return {};
+
+ if (selection.singleNodeIsSelected()) {
+ const ModelNode node = selection.currentSingleSelectedNode();
+ if (node.metaInfo().isQtQuickStudioComponentsGroupItem())
+ return node;
+ }
+
+ const ModelNode parentNode = selection
+ .firstSelectedModelNode()
+ .parentProperty().parentModelNode();
+
+ if (!parentNode.isValid())
+ return {};
+
+ QList<ModelNode> allSiblingNodes = parentNode.directSubModelNodes();
+
+ QList<ModelNode> selectedNodes = ModelNode::pruneChildren(selection.selectedModelNodes());
+ if (selectedNodes.size() != allSiblingNodes.size())
+ return {};
+
+ Utils::sort(allSiblingNodes);
+ Utils::sort(selectedNodes);
+
+ if (allSiblingNodes != selectedNodes)
+ return {};
+
+ if (parentNode.metaInfo().isQtQuickStudioComponentsGroupItem())
+ return parentNode;
+
+ return {};
+}
+
+inline bool itemsAreGrouped(const SelectionContext &selection)
+{
+ return availableGroupNode(selection).isValid();
+}
+
+bool groupingEnabled(const SelectionContext &selection)
+{
+ if (selection.singleNodeIsSelected())
+ return itemsAreGrouped(selection);
+ else
+ return selectionsAreSiblings(selection.selectedModelNodes());
+}
+
+void removeGroup(const ModelNode &group)
+{
+ QmlItemNode groupItem(group);
+ QmlItemNode parent = groupItem.instanceParentItem();
+
+ if (!groupItem || !parent)
+ return;
+
+ group.view()->executeInTransaction(
+ "removeGroup", [group, &groupItem, parent]() {
+ for (const ModelNode &modelNode : group.directSubModelNodes()) {
+ if (QmlVisualNode qmlItem = modelNode) {
+ QPointF pos = qmlItem.position().toPointF();
+ pos = groupItem.instanceTransform().map(pos);
+ qmlItem.setPosition(pos);
+
+ parent.modelNode().defaultNodeListProperty().reparentHere(modelNode);
+ }
+ }
+ groupItem.destroy();
+ });
+}
+
+void toggleGrouping(const SelectionContext &selection)
+{
+ if (!selection.isValid())
+ return;
+
+ ModelNode groupNode = availableGroupNode(selection);
+
+ if (groupNode.isValid())
+ removeGroup(groupNode);
+ else
+ ModelNodeOperations::addToGroupItem(selection);
+}
+
+} // blank namespace
+
+GroupItemAction::GroupItemAction(const QIcon &icon,
+ const QKeySequence &key,
+ int priority)
+ : ModelNodeAction(ComponentCoreConstants::addToGroupItemCommandId,
+ {},
+ icon,
+ {},
+ {},
+ key,
+ priority,
+ &toggleGrouping,
+ &groupingEnabled)
+{
+ setCheckable(true);
+}
+
+void GroupItemAction::updateContext()
+{
+ using namespace ComponentCoreConstants;
+ ModelNodeAction::updateContext();
+ action()->setText(QString::fromLatin1(action()->isChecked()
+ ? removeGroupItemDisplayName
+ : addToGroupItemDisplayName));
+}
+
+bool GroupItemAction::isChecked(const SelectionContext &selectionContext) const
+{
+ return itemsAreGrouped(selectionContext);
+}
diff --git a/src/plugins/qmldesigner/components/componentcore/groupitemaction.h b/src/plugins/qmldesigner/components/componentcore/groupitemaction.h
new file mode 100644
index 0000000000..8d217a8719
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/groupitemaction.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <modelnodecontextmenu_helper.h>
+
+namespace QmlDesigner {
+
+class GroupItemAction: public ModelNodeAction
+{
+public:
+ GroupItemAction(const QIcon &icon,
+ const QKeySequence &key,
+ int priority);
+
+
+protected:
+ virtual void updateContext() override;
+ virtual bool isChecked(const SelectionContext &selectionContext) const override;
+};
+
+}
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp
index eac545b2ef..0102b4126c 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu.cpp
@@ -5,6 +5,7 @@
#include "modelnodecontextmenu_helper.h"
#include "designeractionmanager.h"
#include <qmldesignerplugin.h>
+#include "qmleditormenu.h"
#include <modelnode.h>
@@ -68,7 +69,7 @@ void populateMenu(QSet<ActionInterface* > &actionInterfaces,
void ModelNodeContextMenu::execute(const QPoint &position, bool selectionMenuBool)
{
- auto mainMenu = new QMenu();
+ auto mainMenu = new QmlEditorMenu();
m_selectionContext.setShowSelectionTools(selectionMenuBool);
m_selectionContext.setScenePosition(m_scenePos);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
index 50f5e83bd8..6bc9c703bc 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
@@ -173,14 +173,18 @@ private:
QByteArray m_category;
};
-class SeperatorDesignerAction : public AbstractAction
+class SeparatorDesignerAction : public AbstractAction
{
public:
- SeperatorDesignerAction(const QByteArray &category, int priority) :
- m_category(category),
- m_priority(priority),
- m_visibility(&SelectionContextFunctors::always)
- { defaultAction()->setSeparator(true); }
+ SeparatorDesignerAction(const QByteArray &category, int priority)
+ : AbstractAction()
+ , m_category(category)
+ , m_priority(priority)
+ , m_visibility(&SelectionContextFunctors::always)
+ {
+ action()->setSeparator(true);
+ action()->setIcon({});
+ }
bool isVisible(const SelectionContext &m_selectionState) const override { return m_visibility(m_selectionState); }
bool isEnabled(const SelectionContext &) const override { return true; }
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index 95ea8575c2..10386425fe 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -32,6 +32,7 @@
#include <designermcumanager.h>
#include <qmldesignerplugin.h>
+#include <qmldesignerconstants.h>
#include <coreplugin/messagebox.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -54,8 +55,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include "utils/qtcprocess.h"
#include <utils/smallstring.h>
#include <QComboBox>
@@ -834,8 +835,8 @@ void editMaterial(const SelectionContext &selectionContext)
if (material.isValid()) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor");
- // to MaterialEditor and MaterialBrowser...
- view->emitCustomNotification("selected_material_changed", {material});
+ // to MaterialBrowser...
+ view->emitCustomNotification("select_material", {material});
}
}
@@ -1209,7 +1210,7 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ
if (container.hasProperty("effect"))
container.removeProperty("effect");
- if (effectMetaInfo.isValid()) {
+ if (effectMetaInfo.isQtObject()) {
ModelNode effectNode =
view->createModelNode(effectMetaInfo.typeName(),
effectMetaInfo.majorVersion(),
@@ -1384,15 +1385,15 @@ void addCustomFlowEffect(const SelectionContext &selectionContext)
if (typeName.isEmpty())
return;
- qDebug() << Q_FUNC_INFO << typeName << importString;
-
- const Import import = Import::createFileImport("FlowEffects");
+ AbstractView *view = selectionContext.view();
- if (!importString.isEmpty() && !selectionContext.view()->model()->hasImport(import, true, true)) {
- selectionContext.view()-> model()->changeImports({import}, {});
- }
+ view->executeInTransaction("DesignerActionManager:addFlowEffect", [view, importString]() {
+ const Import import = Import::createFileImport("FlowEffects");
- AbstractView *view = selectionContext.view();
+ if (!importString.isEmpty() && !view->model()->hasImport(import, true, true)) {
+ view->model()->changeImports({import}, {});
+ }
+ });
QTC_ASSERT(view && selectionContext.hasSingleSelectedModelNode(), return);
ModelNode container = selectionContext.currentSingleSelectedNode();
@@ -1642,10 +1643,10 @@ void openEffectMaker(const QString &filePath)
Utils::FilePath projectPath = target->project()->projectDirectory();
QString effectName = QFileInfo(filePath).baseName();
- QString effectResDir = "asset_imports/Effects/" + effectName;
- Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir);
+ QString effectResDir = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER) + "/Effects/" + effectName;
+ Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir);
if (!effectResPath.exists())
- QDir(projectPath.toString()).mkpath(effectResDir);
+ QDir().mkpath(effectResPath.toString());
const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (baseQtVersion) {
@@ -1666,20 +1667,19 @@ void openEffectMaker(const QString &filePath)
if (env.osType() == Utils::OsTypeMac)
env.appendOrSet("QSG_RHI_BACKEND", "metal");
- Utils::QtcProcess *qqemProcess = new Utils::QtcProcess();
+ Utils::Process *qqemProcess = new Utils::Process();
qqemProcess->setEnvironment(env);
qqemProcess->setCommand({ effectMakerPath, arguments });
- qqemProcess->start();
-
- QObject::connect(qqemProcess, &Utils::QtcProcess::done, [qqemProcess]() {
+ QObject::connect(qqemProcess, &Utils::Process::done, [qqemProcess]() {
qqemProcess->deleteLater();
});
+ qqemProcess->start();
}
}
Utils::FilePath getEffectsImportDirectory()
{
- QString defaultDir = "asset_imports/Effects";
+ QString defaultDir = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER) + "/Effects";
Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir);
@@ -1698,13 +1698,7 @@ QString getEffectsDefaultDirectory(const QString &defaultDir)
QString getEffectIcon(const QString &effectPath)
{
- const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget();
- if (!target) {
- qWarning() << __FUNCTION__ << "No project open";
- return QString();
- }
-
- Utils::FilePath projectPath = target->project()->projectDirectory();
+ Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
QString effectName = QFileInfo(effectPath).baseName();
QString effectResDir = "asset_imports/Effects/" + effectName;
Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir + "/" + effectName + ".qml");
@@ -1727,8 +1721,9 @@ bool validateEffect(const QString &effectPath)
Utils::FilePath qmlPath = effectsResDir.resolvePath(effectName + "/" + effectName + ".qml");
if (!qmlPath.exists()) {
QMessageBox msgBox;
- msgBox.setText(QObject::tr("Effect %1 not complete").arg(effectName));
- msgBox.setInformativeText(QObject::tr("Do you want to edit %1?").arg(effectName));
+ msgBox.setText(QObject::tr("Effect %1 is not complete.").arg(effectName));
+ msgBox.setInformativeText(QObject::tr("Ensure that you have saved it in Qt Quick Effect Maker."
+ "\nDo you want to edit this effect?"));
msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setIcon(QMessageBox::Question);
diff --git a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp
new file mode 100644
index 0000000000..173838f170
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0#include "qmleditormenu.h"
+
+#include "qmleditormenu.h"
+
+#include "designeractionmanager.h"
+#include "designericons.h"
+
+#include <utils/hostosinfo.h>
+
+#include <QApplication>
+#include <QStyleOption>
+
+using namespace QmlDesigner;
+
+class QmlDesigner::QmlEditorMenuPrivate
+{
+private:
+ friend class QmlDesigner::QmlEditorMenu;
+ friend class QmlDesigner::QmlEditorStyleObject;
+
+ bool iconVisibility = true;
+ int maxIconWidth = 0;
+
+ static QIcon cascadeLeft;
+ static QIcon cascadeRight;
+ static QIcon tick;
+ static QIcon backspaceIcon;
+};
+
+QIcon QmlEditorMenuPrivate::cascadeLeft;
+QIcon QmlEditorMenuPrivate::cascadeRight;
+QIcon QmlEditorMenuPrivate::tick;
+QIcon QmlEditorMenuPrivate::backspaceIcon;
+
+QmlEditorMenu::QmlEditorMenu(QWidget *parent)
+ : QMenu(parent)
+ , d(new QmlEditorMenuPrivate)
+{
+}
+
+QmlEditorMenu::QmlEditorMenu(const QString &title, QWidget *parent)
+ : QMenu(title, parent)
+ , d(new QmlEditorMenuPrivate)
+{
+}
+
+QmlEditorMenu::~QmlEditorMenu()
+{
+ delete d;
+}
+
+bool QmlEditorMenu::isValid(const QMenu *menu)
+{
+ return qobject_cast<const QmlEditorMenu *>(menu);
+}
+
+bool QmlEditorMenu::iconsVisible() const
+{
+ return d->iconVisibility;
+}
+
+void QmlEditorMenu::setIconsVisible(bool visible)
+{
+ if (d->iconVisibility == visible)
+ return;
+
+ d->iconVisibility = visible;
+ emit iconVisibilityChanged(visible);
+
+ if (isVisible()) {
+ style()->unpolish(this);
+ style()->polish(this);
+ }
+}
+
+void QmlEditorMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
+{
+ if (option->maxIconWidth == 0)
+ d->maxIconWidth = 0;
+
+ QMenu::initStyleOption(option, action);
+
+#ifndef QT_NO_SHORTCUT
+ if (!action->isShortcutVisibleInContextMenu() && !action->shortcut().isEmpty()) {
+ int tabIndex = option->text.indexOf("\t");
+ if (tabIndex < 0)
+ option->text += QLatin1String("\t") + action->shortcut().toString(QKeySequence::NativeText);
+ }
+#endif
+ if (d->iconVisibility) {
+ if (Utils::HostOsInfo::isMacHost()) {
+ if (qApp->testAttribute(Qt::AA_DontShowIconsInMenus))
+ option->icon = action->icon();
+ } else {
+ option->icon = action->isIconVisibleInMenu() ? action->icon() : QIcon();
+ }
+ } else {
+ option->icon = {};
+ }
+
+ if (!option->icon.isNull() && (d->maxIconWidth == 0))
+ d->maxIconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, option, this);
+
+ option->maxIconWidth = d->maxIconWidth;
+ option->styleObject = QmlEditorStyleObject::instance();
+}
+
+bool QmlEditorMenu::qmlEditorMenu() const
+{
+ return true;
+}
+
+QmlEditorStyleObject *QmlEditorStyleObject::instance()
+{
+ static QmlEditorStyleObject *s_instance = nullptr;
+ if (!s_instance)
+ s_instance = new QmlEditorStyleObject;
+ return s_instance;
+}
+
+QIcon QmlEditorStyleObject::cascadeIconLeft() const
+{
+ return QmlEditorMenuPrivate::cascadeLeft;
+}
+
+QIcon QmlEditorStyleObject::cascadeIconRight() const
+{
+ return QmlEditorMenuPrivate::cascadeRight;
+}
+
+QIcon QmlEditorStyleObject::tickIcon() const
+{
+ return QmlEditorMenuPrivate::tick;
+}
+
+QIcon QmlEditorStyleObject::backspaceIcon() const
+{
+ return QmlEditorMenuPrivate::backspaceIcon;
+}
+
+QmlEditorStyleObject::QmlEditorStyleObject()
+ : QObject(qApp)
+{
+ QIcon downIcon = DesignerActionManager::instance()
+ .contextIcon(DesignerIcons::MinimalDownArrowIcon);
+
+ QmlEditorMenuPrivate::cascadeLeft = DesignerIcons::rotateIcon(downIcon, 90);
+ QmlEditorMenuPrivate::cascadeRight = DesignerIcons::rotateIcon(downIcon, -90);
+ QmlEditorMenuPrivate::tick = DesignerActionManager::instance()
+ .contextIcon(DesignerIcons::SimpleCheckIcon);
+}
diff --git a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.h b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.h
new file mode 100644
index 0000000000..4163fccbea
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QMenu>
+
+class QStyleOptionMenuItem;
+
+namespace QmlDesigner {
+
+class QmlEditorMenuPrivate;
+class DesignerIcons;
+
+class QmlEditorMenu : public QMenu
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool qmlEditorMenu READ qmlEditorMenu CONSTANT)
+ Q_PROPERTY(bool iconsVisible READ iconsVisible WRITE setIconsVisible NOTIFY iconVisibilityChanged)
+
+public:
+ explicit QmlEditorMenu(QWidget *parent = nullptr);
+ explicit QmlEditorMenu(const QString &title, QWidget *parent = nullptr);
+ virtual ~QmlEditorMenu();
+
+ static bool isValid(const QMenu *menu);
+
+ bool iconsVisible() const;
+ void setIconsVisible(bool visible);
+
+signals:
+ void iconVisibilityChanged(bool);
+
+protected:
+ virtual void initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const override;
+
+private:
+ bool qmlEditorMenu() const;
+
+ QmlEditorMenuPrivate *d = nullptr;
+};
+
+class QmlEditorStyleObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QIcon cascadeIconLeft READ cascadeIconLeft CONSTANT)
+ Q_PROPERTY(QIcon cascadeIconRight READ cascadeIconRight CONSTANT)
+ Q_PROPERTY(QIcon backspaceIcon READ backspaceIcon CONSTANT)
+ Q_PROPERTY(QIcon tickIcon READ tickIcon CONSTANT)
+
+public:
+ static QmlEditorStyleObject *instance();
+
+ QIcon cascadeIconLeft() const;
+ QIcon cascadeIconRight() const;
+ QIcon tickIcon() const;
+ QIcon backspaceIcon() const;
+
+private:
+ QmlEditorStyleObject();
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp
index 2771aff7d6..af495fd3b5 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp
@@ -11,12 +11,13 @@
#include <utils/stylehelper.h>
#include <QApplication>
-#include <QRegularExpression>
-#include <QScreen>
+#include <QMainWindow>
#include <QPointer>
-#include <QQmlEngine>
#include <QQmlComponent>
+#include <QQmlEngine>
#include <QQmlProperty>
+#include <QRegularExpression>
+#include <QScreen>
#include <qqml.h>
static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg)
@@ -137,6 +138,11 @@ bool Theme::highPixelDensity() const
return qApp->primaryScreen()->logicalDotsPerInch() > 100;
}
+QWindow *Theme::mainWindowHandle() const
+{
+ return Core::ICore::mainWindow()->windowHandle();
+}
+
QPixmap Theme::getPixmap(const QString &id)
{
return QmlDesignerIconProvider::getPixmap(id);
@@ -166,6 +172,21 @@ QString Theme::getIconUnicode(const QString &name)
return instance()->m_constants->property(name.toStdString().data()).toString();
}
+QIcon Theme::iconFromName(Icon i, QColor c)
+{
+ QColor color = c;
+ if (!color.isValid())
+ color = getColor(Theme::Color::DSiconColor);
+
+ const QString fontName = "qtds_propertyIconFont.ttf";
+ return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(i), 32, 32, color);
+}
+
+int Theme::toolbarSize()
+{
+ return 41;
+}
+
QColor Theme::qmlDesignerBackgroundColorDarker() const
{
return getColor(QmlDesigner_BackgroundColorDarker);
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h
index f6bc8cc0bf..8e5f10d361 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.h
+++ b/src/plugins/qmldesigner/components/componentcore/theme.h
@@ -29,6 +29,8 @@ public:
addRowAfter,
addRowBefore,
addTable,
+ add_medium,
+ add_small,
adsClose,
adsDetach,
adsDropDown,
@@ -40,6 +42,10 @@ public:
alignLeft,
alignRight,
alignTo,
+ alignToCam_medium,
+ alignToCamera_small,
+ alignToObject_small,
+ alignToView_medium,
alignTop,
anchorBaseline,
anchorBottom,
@@ -47,33 +53,70 @@ public:
anchorLeft,
anchorRight,
anchorTop,
+ anchors_small,
animatedProperty,
annotationBubble,
annotationDecal,
+ annotations_large,
+ annotations_small,
applyMaterialToSelected,
+ apply_medium,
+ apply_small,
+ arrange_small,
+ arrow_small,
assign,
+ attach_medium,
+ back_medium,
+ backspace_small,
bevelAll,
bevelCorner,
+ bezier_medium,
+ binding_medium,
+ bounds_small,
+ branch_medium,
+ camera_small,
centerHorizontal,
centerVertical,
+ cleanLogs_medium,
closeCross,
+ closeFile_large,
closeLink,
+ close_small,
colorPopupClose,
+ colorSelection_medium,
columnsAndRows,
+ cone_medium,
+ cone_small,
+ connection_small,
+ connections_medium,
copyLink,
copyStyle,
+ copy_small,
cornerA,
cornerB,
cornersAll,
+ createComponent_large,
+ createComponent_small,
+ create_medium,
+ create_small,
+ cube_medium,
+ cube_small,
curveDesigner,
+ curveDesigner_medium,
curveEditor,
customMaterialEditor,
+ cylinder_medium,
+ cylinder_small,
decisionNode,
deleteColumn,
deleteMaterial,
deleteRow,
deleteTable,
+ delete_medium,
+ delete_small,
+ designMode_large,
detach,
+ directionalLight_small,
distributeBottom,
distributeCenterHorizontal,
distributeCenterVertical,
@@ -90,48 +133,115 @@ public:
downloadUnavailable,
downloadUpdate,
downloaded,
+ duplicate_small,
edit,
+ editComponent_large,
+ editComponent_small,
+ editLightOff_medium,
+ editLightOn_medium,
+ edit_medium,
+ edit_small,
+ events_small,
+ export_medium,
eyeDropper,
favorite,
+ fitAll_medium,
+ fitSelected_small,
+ fitSelection_medium,
+ fitToView_medium,
flowAction,
flowTransition,
fontStyleBold,
fontStyleItalic,
fontStyleStrikethrough,
fontStyleUnderline,
+ forward_medium,
+ globalOrient_medium,
gradient,
gridView,
+ grid_medium,
+ group_small,
+ home_large,
idAliasOff,
idAliasOn,
+ import_medium,
imported,
+ importedModels_small,
infinity,
+ invisible_medium,
keyframe,
+ languageList_medium,
+ layouts_small,
+ lights_small,
+ linear_medium,
linkTriangle,
linked,
listView,
+ list_medium,
+ localOrient_medium,
lockOff,
lockOn,
+ loopPlayback_medium,
+ materialBrowser_medium,
materialPreviewEnvironment,
materialPreviewModel,
+ material_medium,
mergeCells,
+ merge_small,
minus,
mirror,
+ more_medium,
+ mouseArea_small,
+ moveDown_medium,
+ moveInwards_medium,
+ moveUp_medium,
+ moveUpwards_medium,
+ move_medium,
newMaterial,
+ nextFile_large,
openLink,
openMaterialBrowser,
orientation,
+ orthCam_medium,
+ orthCam_small,
paddingEdge,
paddingFrame,
+ particleAnimation_medium,
pasteStyle,
+ paste_small,
pause,
+ perspectiveCam_medium,
+ perspectiveCam_small,
pin,
+ plane_medium,
+ plane_small,
play,
+ playFill_medium,
+ playOutline_medium,
plus,
+ pointLight_small,
+ positioners_small,
+ previewEnv_medium,
+ previousFile_large,
promote,
+ properties_medium,
readOnly,
+ recordFill_medium,
+ recordOutline_medium,
redo,
+ reload_medium,
+ remove_medium,
+ remove_small,
+ rename_small,
+ replace_small,
+ resetView_small,
+ restartParticles_medium,
+ reverseOrder_medium,
+ roatate_medium,
rotationFill,
rotationOutline,
+ runProjFill_large,
+ runProjOutline_large,
s_anchors,
s_annotations,
s_arrange,
@@ -150,11 +260,27 @@ public:
s_snapping,
s_timeline,
s_visibility,
+ saveLogs_medium,
+ scale_medium,
search,
+ search_small,
sectionToggle,
+ selectFill_medium,
+ selectOutline_medium,
+ selectParent_small,
+ selection_small,
+ settings_medium,
+ signal_small,
+ snapping_small,
+ sphere_medium,
+ sphere_small,
splitColumns,
splitRows,
+ spotLight_small,
+ stackedContainer_small,
startNode,
+ step_medium,
+ stop_medium,
testIcon,
textAlignBottom,
textAlignCenter,
@@ -166,7 +292,14 @@ public:
textBulletList,
textFullJustification,
textNumberedList,
+ textures_medium,
tickIcon,
+ tickMark_small,
+ timeline_small,
+ toEndFrame_medium,
+ toNextFrame_medium,
+ toPrevFrame_medium,
+ toStartFrame_medium,
topToolbar_annotations,
topToolbar_closeFile,
topToolbar_designMode,
@@ -189,11 +322,14 @@ public:
triangleCornerB,
unLinked,
undo,
+ unify_medium,
unpin,
upDownIcon,
upDownSquare2,
visibilityOff,
visibilityOn,
+ visible_medium,
+ visible_small,
wildcard,
wizardsAutomotive,
wizardsDesktop,
@@ -204,7 +340,9 @@ public:
wizardsUnknown,
zoomAll,
zoomIn,
+ zoomIn_medium,
zoomOut,
+ zoomOut_medium,
zoomSelection
};
Q_ENUM(Icon)
@@ -217,6 +355,10 @@ public:
static QString getIconUnicode(Theme::Icon i);
static QString getIconUnicode(const QString &name);
+ static QIcon iconFromName(Theme::Icon i, QColor c = {});
+
+ static int toolbarSize();
+
Q_INVOKABLE QColor qmlDesignerBackgroundColorDarker() const;
Q_INVOKABLE QColor qmlDesignerBackgroundColorDarkAlternate() const;
Q_INVOKABLE QColor qmlDesignerTabLight() const;
@@ -228,6 +370,8 @@ public:
Q_INVOKABLE int captionFontPixelSize() const;
Q_INVOKABLE bool highPixelDensity() const;
+ Q_INVOKABLE QWindow *mainWindowHandle() const;
+
private:
Theme(Utils::Theme *originTheme, QObject *parent);
QColor evaluateColorAtThemeInstance(const QString &themeColorName);
diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
index 2eecd3d1cc..ee7ae3f678 100644
--- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
@@ -261,7 +261,7 @@ void ViewManager::registerNanotraceActions()
22,
handleShutdownNanotraceAction);
- QObject::connect(startNanotraceAction->defaultAction(), &QAction::triggered, [&]() {
+ QObject::connect(startNanotraceAction->action(), &QAction::triggered, [&]() {
d->nodeInstanceView.startNanotrace();
});
@@ -276,7 +276,7 @@ void ViewManager::registerNanotraceActions()
23,
handleShutdownNanotraceAction);
- QObject::connect(shutDownNanotraceAction->defaultAction(), &QAction::triggered, [&]() {
+ QObject::connect(shutDownNanotraceAction->action(), &QAction::triggered, [&]() {
d->nodeInstanceView.endNanotrace();
});
diff --git a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp
index d6289a0fc2..e967be04ec 100644
--- a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp
@@ -7,6 +7,8 @@
#include <iterator>
#include <utility>
+#include <utils/stylehelper.h>
+
#include <QAbstractItemView>
#include <QComboBox>
#include <QToolBar>
@@ -32,7 +34,9 @@ bool isValidIndex(int index)
ZoomAction::ZoomAction(QObject *parent)
: QWidgetAction(parent)
, m_combo(nullptr)
-{}
+{
+ m_index = indexOf(1.0);
+}
std::array<double, 27> ZoomAction::zoomLevels()
{
@@ -51,14 +55,20 @@ int ZoomAction::indexOf(double zoom)
void ZoomAction::setZoomFactor(double zoom)
{
if (int index = indexOf(zoom); index >= 0) {
- m_combo->setCurrentIndex(index);
- m_combo->setToolTip(m_combo->currentText());
+ emitZoomLevelChanged(index);
+ if (m_combo) {
+ m_combo->setCurrentIndex(index);
+ m_combo->setToolTip(m_combo->currentText());
+ }
+ m_index = index;
return;
}
- int rounded = static_cast<int>(std::round(zoom * 100));
- m_combo->setEditable(true);
- m_combo->setEditText(QString::number(rounded) + " %");
- m_combo->setToolTip(m_combo->currentText());
+ if (m_combo) {
+ int rounded = static_cast<int>(std::round(zoom * 100));
+ m_combo->setEditable(true);
+ m_combo->setEditText(QString::number(rounded) + " %");
+ m_combo->setToolTip(m_combo->currentText());
+ }
}
double ZoomAction::setNextZoomFactor(double zoom)
@@ -91,6 +101,11 @@ double ZoomAction::setPreviousZoomFactor(double zoom)
return zoom;
}
+int ZoomAction::currentIndex() const
+{
+ return m_index;
+}
+
bool parentIsToolBar(QWidget *parent)
{
return qobject_cast<QToolBar *>(parent) != nullptr;
@@ -110,8 +125,8 @@ QWidget *ZoomAction::createWidget(QWidget *parent)
{
if (!m_combo && parentIsToolBar(parent)) {
m_combo = createZoomComboBox(parent);
- m_combo->setProperty("hideborder", true);
- m_combo->setCurrentIndex(indexOf(1.0));
+ m_combo->setProperty(Utils::StyleHelper::C_HIDE_BORDER, true);
+ m_combo->setCurrentIndex(m_index);
m_combo->setToolTip(m_combo->currentText());
connect(m_combo, &QComboBox::currentIndexChanged, this, &ZoomAction::emitZoomLevelChanged);
diff --git a/src/plugins/qmldesigner/components/componentcore/zoomaction.h b/src/plugins/qmldesigner/components/componentcore/zoomaction.h
index 7be82e92b7..d211a19e7c 100644
--- a/src/plugins/qmldesigner/components/componentcore/zoomaction.h
+++ b/src/plugins/qmldesigner/components/componentcore/zoomaction.h
@@ -32,6 +32,8 @@ public:
double setNextZoomFactor(double zoom);
double setPreviousZoomFactor(double zoom);
+ int currentIndex() const;
+
protected:
QWidget *createWidget(QWidget *parent) override;
@@ -40,6 +42,7 @@ private:
static std::array<double, 27> m_zooms;
QPointer<QComboBox> m_combo;
+ int m_index = -1;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
index 6f5a9021be..6c970f2646 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp
@@ -11,7 +11,6 @@
#include "nodemetainfo.h"
#include "nodeproperty.h"
#include "rewriterview.h"
-#include "rewritertransaction.h"
#include "addnewbackenddialog.h"
@@ -20,8 +19,6 @@
namespace QmlDesigner {
-namespace Internal {
-
BackendModel::BackendModel(ConnectionView *parent) :
QStandardItemModel(parent)
,m_connectionView(parent)
@@ -29,7 +26,7 @@ BackendModel::BackendModel(ConnectionView *parent) :
connect(this, &QStandardItemModel::dataChanged, this, &BackendModel::handleDataChanged);
}
-ConnectionView *QmlDesigner::Internal::BackendModel::connectionView() const
+ConnectionView *BackendModel::connectionView() const
{
return m_connectionView;
}
@@ -311,6 +308,4 @@ void BackendModel::handleDataChanged(const QModelIndex &topLeft, const QModelInd
m_lock = false;
}
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
index fc9542ca18..c4f148aa2e 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h
@@ -8,8 +8,6 @@
namespace QmlDesigner {
-namespace Internal {
-
class ConnectionView;
class BackendModel : public QStandardItemModel
@@ -47,6 +45,4 @@ private:
bool m_lock = false;
};
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
index 2b9e20219b..e0a8f03e99 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
@@ -18,8 +18,6 @@
namespace QmlDesigner {
-namespace Internal {
-
BindingModel::BindingModel(ConnectionView *parent)
: QStandardItemModel(parent)
, m_connectionView(parent)
@@ -440,6 +438,4 @@ void BindingModel::handleException()
resetModel();
}
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
index 400209918d..12685679e9 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
@@ -11,8 +11,6 @@
namespace QmlDesigner {
-namespace Internal {
-
class ConnectionView;
class BindingModel : public QStandardItemModel
@@ -65,6 +63,4 @@ private:
};
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
index 52a6f5aabd..b3eb7542f8 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
@@ -48,8 +48,6 @@ bool isConnection(const QmlDesigner::ModelNode &modelNode)
namespace QmlDesigner {
-namespace Internal {
-
ConnectionModel::ConnectionModel(ConnectionView *parent)
: QStandardItemModel(parent)
, m_connectionView(parent)
@@ -351,7 +349,7 @@ void ConnectionModel::deleteConnectionByRow(int currentRow)
{
SignalHandlerProperty targetSignal = signalHandlerPropertyForRow(currentRow);
QTC_ASSERT(targetSignal.isValid(), return );
- QmlDesigner::ModelNode node = targetSignal.parentModelNode();
+ ModelNode node = targetSignal.parentModelNode();
QTC_ASSERT(node.isValid(), return );
QList<SignalHandlerProperty> allSignals = node.signalProperties();
@@ -527,6 +525,4 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co
return stringList;
}
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
index 56971a3732..42cbe33fc7 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
@@ -13,8 +13,6 @@ class BindingProperty;
class SignalHandlerProperty;
class VariantProperty;
-namespace Internal {
-
class ConnectionView;
class ConnectionModel : public QStandardItemModel
@@ -73,6 +71,4 @@ private:
QString m_exceptionError;
};
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
index 0c34eefd45..39e05a898c 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
@@ -22,8 +22,6 @@
namespace QmlDesigner {
-namespace Internal {
-
ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}
, m_connectionViewWidget(new ConnectionViewWidget())
@@ -278,6 +276,4 @@ ConnectionView *ConnectionView::instance()
return s_instance;
}
-} // namesapce Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
index ef70922a12..dcf61ad79d 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
@@ -15,8 +15,6 @@ QT_END_NAMESPACE
namespace QmlDesigner {
-namespace Internal {
-
class ConnectionViewWidget;
class BindingModel;
class ConnectionModel;
@@ -82,6 +80,4 @@ private: //variables
BackendModel *m_backendModel;
};
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
index 4b658aa9c6..f1b0c3f4cb 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp
@@ -34,8 +34,6 @@
namespace QmlDesigner {
-namespace Internal {
-
ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
QFrame(parent),
ui(new Ui::ConnectionViewWidget)
@@ -72,6 +70,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
const QList<QToolButton*> buttons = createToolBarWidgets();
+ ui->toolBar->setFixedHeight(41);
for (auto toolButton : buttons)
ui->toolBar->addWidget(toolButton);
@@ -611,6 +610,4 @@ void ConnectionViewWidget::backendTableViewSelectionChanged(const QModelIndex &c
}
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h
index eff9937511..f71641bd8b 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h
@@ -20,8 +20,6 @@ namespace Ui { class ConnectionViewWidget; }
class ActionEditor;
class BindingEditor;
-namespace Internal {
-
class BindingModel;
class ConnectionModel;
class DynamicPropertiesModel;
@@ -93,6 +91,4 @@ private:
QModelIndex m_dynamicIndex;
};
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp
index 3c482997cd..e2c30b0a03 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp
@@ -20,8 +20,6 @@
namespace QmlDesigner {
-namespace Internal {
-
QStringList prependOnForSignalHandler(const QStringList &signalNames)
{
QStringList signalHandlerNames;
@@ -408,6 +406,4 @@ QWidget *BackendDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt
return widget;
}
-} // namesapce Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.h b/src/plugins/qmldesigner/components/connectioneditor/delegates.h
index f66cbb0ba0..55f886b98a 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/delegates.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/delegates.h
@@ -9,8 +9,6 @@
namespace QmlDesigner {
-namespace Internal {
-
class PropertiesComboBox : public QComboBox
{
Q_OBJECT
@@ -77,6 +75,4 @@ public:
const QModelIndex &index) const override;
};
-} // namespace Internal
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
index 1eca539f5b..89e08c5441 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
@@ -3,32 +3,32 @@
#include "dynamicpropertiesmodel.h"
-#include "connectionview.h"
-
-#include <bindingproperty.h>
-#include <nodemetainfo.h>
-#include <nodeproperty.h>
-#include <rewritertransaction.h>
-#include <rewritingexception.h>
-#include <variantproperty.h>
-#include <qmldesignerconstants.h>
-#include <qmldesignerplugin.h>
+#include "bindingproperty.h"
+#include "nodeabstractproperty.h"
+#include "nodemetainfo.h"
+#include "qmlchangeset.h"
+#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
+#include "qmlobjectnode.h"
+#include "qmltimeline.h"
+#include "rewritertransaction.h"
+#include "rewritingexception.h"
+#include "variantproperty.h"
#include <utils/algorithm.h>
-#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QMessageBox>
#include <QTimer>
-#include <QUrl>
namespace {
-bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProperty01, const QmlDesigner::VariantProperty &variantProperty02)
+bool compareVariantProperties(const QmlDesigner::VariantProperty &variantProp1,
+ const QmlDesigner::VariantProperty &variantProp2)
{
- if (variantProperty01.parentModelNode() != variantProperty02.parentModelNode())
+ if (variantProp1.parentModelNode() != variantProp2.parentModelNode())
return false;
- if (variantProperty01.name() != variantProperty02.name())
+ if (variantProp1.name() != variantProp2.name())
return false;
return true;
}
@@ -87,22 +87,19 @@ QVariant convertVariantForTypeName(const QVariant &variant, const QmlDesigner::T
return returnValue;
}
-} //internal namespace
+} // namespace
namespace QmlDesigner {
-namespace Internal {
-
-QmlDesigner::PropertyName DynamicPropertiesModel::unusedProperty(const QmlDesigner::ModelNode &modelNode)
+PropertyName DynamicPropertiesModel::unusedProperty(const ModelNode &modelNode)
{
- QmlDesigner::PropertyName propertyName = "property";
+ PropertyName propertyName = "property";
int i = 0;
if (modelNode.isValid() && modelNode.metaInfo().isValid()) {
while (true) {
- const QmlDesigner::PropertyName currentPropertyName = propertyName + QString::number(i).toLatin1();
+ const PropertyName currentPropertyName = propertyName + QString::number(i++).toLatin1();
if (!modelNode.hasProperty(currentPropertyName) && !modelNode.metaInfo().hasProperty(currentPropertyName))
return currentPropertyName;
- i++;
}
}
@@ -167,8 +164,7 @@ void DynamicPropertiesModel::resetModel()
{
beginResetModel();
clear();
- setHorizontalHeaderLabels(
- QStringList({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}));
+ setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")});
if (m_view->isAttached()) {
const auto nodes = selectedNodes();
@@ -180,8 +176,8 @@ void DynamicPropertiesModel::resetModel()
}
-//Method creates dynamic BindingProperty with the same name and type as old VariantProperty
-//Value copying is optional
+// Method creates dynamic BindingProperty with the same name and type as old VariantProperty
+// Value copying is optional
BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const PropertyName &name, bool copyValue)
{
if (selectedNodes().count() == 1) {
@@ -211,12 +207,11 @@ BindingProperty DynamicPropertiesModel::replaceVariantWithBinding(const Property
qWarning() << "DynamicPropertiesModel::replaceVariantWithBinding: no selected nodes";
}
- return BindingProperty();
+ return {};
}
-
-//Finds selected property, and changes it to empty value (QVariant())
-//If it's a BindingProperty, then replaces it with empty VariantProperty
+// Finds selected property, and changes it to empty value (QVariant())
+// If it's a BindingProperty, then replaces it with empty VariantProperty
void DynamicPropertiesModel::resetProperty(const PropertyName &name)
{
if (selectedNodes().count() == 1) {
@@ -235,7 +230,7 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
BindingProperty property = abProp.toBindingProperty();
TypeName oldType = property.dynamicTypeName();
- //removing old property, to create the new one with the same name:
+ // removing old property, to create the new one with the same name
modelNode.removeProperty(name);
VariantProperty newProperty = modelNode.variantProperty(name);
@@ -249,8 +244,7 @@ void DynamicPropertiesModel::resetProperty(const PropertyName &name)
}
}
}
- }
- else {
+ } else {
qWarning() << "DynamicPropertiesModel::resetProperty: no selected nodes";
}
}
@@ -279,14 +273,14 @@ void DynamicPropertiesModel::bindingPropertyChanged(const BindingProperty &bindi
const QList<ModelNode> nodes = selectedNodes();
if (!nodes.contains(bindingProperty.parentModelNode()))
return;
+
if (!m_lock) {
int rowNumber = findRowForBindingProperty(bindingProperty);
- if (rowNumber == -1) {
+ if (rowNumber == -1)
addBindingProperty(bindingProperty);
- } else {
+ else
updateBindingProperty(rowNumber);
- }
}
m_handleDataChanged = true;
@@ -302,6 +296,7 @@ void DynamicPropertiesModel::abstractPropertyChanged(const AbstractProperty &pro
const QList<ModelNode> nodes = selectedNodes();
if (!nodes.contains(property.parentModelNode()))
return;
+
int rowNumber = findRowForProperty(property);
if (rowNumber > -1) {
if (property.isVariantProperty())
@@ -323,6 +318,7 @@ void DynamicPropertiesModel::variantPropertyChanged(const VariantProperty &varia
const QList<ModelNode> nodes = selectedNodes();
if (!nodes.contains(variantProperty.parentModelNode()))
return;
+
if (!m_lock) {
int rowNumber = findRowForVariantProperty(variantProperty);
@@ -342,6 +338,7 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
const QList<ModelNode> nodes = selectedNodes();
if (!nodes.contains(bindingProperty.parentModelNode()))
return;
+
if (!m_lock) {
int rowNumber = findRowForBindingProperty(bindingProperty);
removeRow(rowNumber);
@@ -357,6 +354,7 @@ void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProper
const QList<ModelNode> nodes = selectedNodes();
if (!nodes.contains(variantProperty.parentModelNode()))
return;
+
if (!m_lock) {
int rowNumber = findRowForVariantProperty(variantProperty);
removeRow(rowNumber);
@@ -375,7 +373,9 @@ void DynamicPropertiesModel::reset()
void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
{
QTC_ASSERT(m_explicitSelection, return);
- QTC_ASSERT(node.isValid(), return);
+
+ if (!node.isValid())
+ return;
m_selectedNodes.clear();
m_selectedNodes.append(node);
@@ -396,7 +396,7 @@ AbstractProperty DynamicPropertiesModel::abstractPropertyForRow(int rowNumber) c
if (modelNode.isValid())
return modelNode.property(targetPropertyName.toUtf8());
- return AbstractProperty();
+ return {};
}
BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) const
@@ -409,7 +409,7 @@ BindingProperty DynamicPropertiesModel::bindingPropertyForRow(int rowNumber) con
if (modelNode.isValid())
return modelNode.bindingProperty(targetPropertyName.toUtf8());
- return BindingProperty();
+ return {};
}
VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) const
@@ -422,7 +422,7 @@ VariantProperty DynamicPropertiesModel::variantPropertyForRow(int rowNumber) con
if (modelNode.isValid())
return modelNode.variantProperty(targetPropertyName.toUtf8());
- return VariantProperty();
+ return {};
}
QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProperty &bindingProperty) const
@@ -438,7 +438,8 @@ QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProper
if (metaInfo.isValid()) {
QStringList possibleProperties;
- for (const auto &property : metaInfo.properties()) {
+ const PropertyMetaInfos props = metaInfo.properties();
+ for (const auto &property : props) {
if (property.isWritable())
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
@@ -446,7 +447,7 @@ QStringList DynamicPropertiesModel::possibleTargetProperties(const BindingProper
return possibleProperties;
}
- return QStringList();
+ return {};
}
void DynamicPropertiesModel::addDynamicPropertyForCurrentNode()
@@ -475,11 +476,10 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper
NodeMetaInfo type;
- if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid()) {
+ if (auto metaInfo = bindingProperty.parentModelNode().metaInfo(); metaInfo.isValid())
type = metaInfo.property(bindingProperty.name()).propertyType();
- } else {
+ else
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for target node";
- }
const QString &id = stringlist.constFirst();
@@ -494,8 +494,9 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper
if (metaInfo.isValid()) {
QStringList possibleProperties;
- for (const auto &property : metaInfo.properties()) {
- if (property.propertyType() == type) //### todo proper check
+ const PropertyMetaInfos props = metaInfo.properties();
+ for (const auto &property : props) {
+ if (property.propertyType() == type) // TODO: proper check
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
return possibleProperties;
@@ -503,40 +504,39 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper
qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node";
}
- return QStringList();
+ return {};
}
void DynamicPropertiesModel::deleteDynamicPropertyByRow(int rowNumber)
{
- m_view->executeInTransaction(
- "DynamicPropertiesModel::deleteDynamicPropertyByRow", [this, rowNumber]() {
- const AbstractProperty property = abstractPropertyForRow(rowNumber);
- const PropertyName propertyName = property.name();
- BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
- if (bindingProperty.isValid()) {
- bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
- } else {
- VariantProperty variantProperty = variantPropertyForRow(rowNumber);
- if (variantProperty.isValid())
- variantProperty.parentModelNode().removeProperty(variantProperty.name());
- }
+ m_view->executeInTransaction(__FUNCTION__, [this, rowNumber]() {
+ const AbstractProperty property = abstractPropertyForRow(rowNumber);
+ const PropertyName propertyName = property.name();
+ BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
+ if (bindingProperty.isValid()) {
+ bindingProperty.parentModelNode().removeProperty(bindingProperty.name());
+ } else {
+ VariantProperty variantProperty = variantPropertyForRow(rowNumber);
+ if (variantProperty.isValid())
+ variantProperty.parentModelNode().removeProperty(variantProperty.name());
+ }
- if (property.isValid()) {
- QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode());
- const auto stateOperations = objectNode.allAffectingStatesOperations();
- for (const QmlModelStateOperation &stateOperation : stateOperations) {
- if (stateOperation.modelNode().hasProperty(propertyName))
- stateOperation.modelNode().removeProperty(propertyName);
- }
+ if (property.isValid()) {
+ QmlObjectNode objectNode = QmlObjectNode(property.parentModelNode());
+ const auto stateOperations = objectNode.allAffectingStatesOperations();
+ for (const QmlModelStateOperation &stateOperation : stateOperations) {
+ if (stateOperation.modelNode().hasProperty(propertyName))
+ stateOperation.modelNode().removeProperty(propertyName);
+ }
- const auto timelineNodes = objectNode.allTimelines();
- for (auto &timelineNode : timelineNodes) {
- QmlTimeline timeline(timelineNode);
- timeline.removeKeyframesForTargetAndProperty(objectNode.modelNode(),
- propertyName);
- }
+ const QList<ModelNode> timelineNodes = objectNode.allTimelines();
+ for (auto &timelineNode : timelineNodes) {
+ QmlTimeline timeline(timelineNode);
+ timeline.removeKeyframesForTargetAndProperty(objectNode.modelNode(),
+ propertyName);
}
- });
+ }
+ });
resetModel();
}
@@ -555,7 +555,8 @@ void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
idItem = new QStandardItem(idOrTypeNameForNode(abstractProperty.parentModelNode()));
updateCustomData(idItem, abstractProperty);
- propertyNameItem = new QStandardItem(QString::fromUtf8(abstractProperty.name()));
+ const QString propName = QString::fromUtf8(abstractProperty.name());
+ propertyNameItem = new QStandardItem(propName);
items.append(idItem);
items.append(propertyNameItem);
@@ -567,6 +568,13 @@ void DynamicPropertiesModel::addProperty(const QVariant &propertyValue,
propertyValueItem->setData(propertyValue, Qt::DisplayRole);
items.append(propertyValueItem);
+ for (int i = 0; i < rowCount(); ++i) {
+ if (data(index(i, PropertyNameRow)).toString() > propName) {
+ insertRow(i, items);
+ return;
+ }
+ }
+
appendRow(items);
}
@@ -618,7 +626,6 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
if (objectNode.isValid() && !objectNode.view()->currentState().isBaseState())
value = objectNode.modelValue(variantProperty.name());
-
updateDisplayRoleFromVariant(rowNumber, PropertyValueRow, value);
}
}
@@ -628,9 +635,8 @@ void DynamicPropertiesModel::addModelNode(const ModelNode &modelNode)
if (!modelNode.isValid())
return;
- auto properties = modelNode.properties();
-
- auto dynamicProperties = Utils::filtered(properties, [](const AbstractProperty &p) {
+ const QList<AbstractProperty> properties = modelNode.properties();
+ QList<AbstractProperty> dynamicProperties = Utils::filtered(properties, [](const AbstractProperty &p) {
return p.isDynamic();
});
@@ -653,10 +659,10 @@ void DynamicPropertiesModel::updateValue(int row)
if (bindingProperty.isBindingProperty()) {
const QString expression = data(index(row, PropertyValueRow)).toString();
- RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
+ RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__);
try {
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(), expression);
- transaction.commit(); //committing in the try block
+ transaction.commit(); // committing in the try block
} catch (Exception &e) {
m_exceptionError = e.description();
QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
@@ -669,10 +675,10 @@ void DynamicPropertiesModel::updateValue(int row)
if (variantProperty.isVariantProperty()) {
const QVariant value = data(index(row, PropertyValueRow));
- RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("DynamicPropertiesModel::updateValue"));
+ RewriterTransaction transaction = m_view->beginRewriterTransaction(__FUNCTION__);
try {
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(), value);
- transaction.commit(); //committing in the try block
+ transaction.commit(); // committing in the try block
} catch (Exception &e) {
m_exceptionError = e.description();
QTimer::singleShot(200, this, &DynamicPropertiesModel::handleException);
@@ -683,17 +689,14 @@ void DynamicPropertiesModel::updateValue(int row)
void DynamicPropertiesModel::updatePropertyName(int rowNumber)
{
const PropertyName newName = data(index(rowNumber, PropertyNameRow)).toString().toUtf8();
- if (newName.isEmpty()) {
- qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property name";
- return;
- }
+ QTC_ASSERT(!newName.isEmpty(), return);
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
ModelNode targetNode = bindingProperty.parentModelNode();
if (bindingProperty.isBindingProperty()) {
- m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [bindingProperty, newName, &targetNode](){
+ m_view->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() {
const QString expression = bindingProperty.expression();
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
@@ -712,7 +715,7 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
ModelNode targetNode = variantProperty.parentModelNode();
- m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyName", [=](){
+ m_view->executeInTransaction(__FUNCTION__, [=]() {
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType, value);
targetNode.removeProperty(variantProperty.name());
});
@@ -723,13 +726,8 @@ void DynamicPropertiesModel::updatePropertyName(int rowNumber)
void DynamicPropertiesModel::updatePropertyType(int rowNumber)
{
-
const TypeName newType = data(index(rowNumber, PropertyTypeRow)).toString().toLatin1();
-
- if (newType.isEmpty()) {
- qWarning() << "DynamicPropertiesModel::updatePropertyName invalid property type";
- return;
- }
+ QTC_ASSERT(!newType.isEmpty(), return);
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
@@ -738,7 +736,7 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
const PropertyName propertyName = bindingProperty.name();
ModelNode targetNode = bindingProperty.parentModelNode();
- m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
+ m_view->executeInTransaction(__FUNCTION__, [=]() {
targetNode.removeProperty(bindingProperty.name());
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(newType, expression);
});
@@ -754,7 +752,7 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
ModelNode targetNode = variantProperty.parentModelNode();
const PropertyName propertyName = variantProperty.name();
- m_view->executeInTransaction("DynamicPropertiesModel::updatePropertyType", [=](){
+ m_view->executeInTransaction(__FUNCTION__, [=]() {
targetNode.removeProperty(variantProperty.name());
if (!isValueType(newType)) {
targetNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression(
@@ -767,26 +765,22 @@ void DynamicPropertiesModel::updatePropertyType(int rowNumber)
updateCustomData(rowNumber, targetNode.variantProperty(propertyName));
- if (variantProperty.isVariantProperty()) {
+ if (variantProperty.isVariantProperty())
updateVariantProperty(rowNumber);
- } else if (bindingProperty.isBindingProperty()) {
+ else if (bindingProperty.isBindingProperty())
updateBindingProperty(rowNumber);
- }
}
}
ModelNode DynamicPropertiesModel::getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const
{
- ModelNode modelNode;
+ if (id != QLatin1String("parent"))
+ return m_view->modelNodeForId(id);
- if (id != QLatin1String("parent")) {
- modelNode = m_view->modelNodeForId(id);
- } else {
- if (targetNode.hasParentProperty()) {
- modelNode = targetNode.parentProperty().parentModelNode();
- }
- }
- return modelNode;
+ if (targetNode.hasParentProperty())
+ return targetNode.parentProperty().parentModelNode();
+
+ return {};
}
void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const AbstractProperty &property)
@@ -803,54 +797,56 @@ void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &p
int DynamicPropertiesModel::findRowForBindingProperty(const BindingProperty &bindingProperty) const
{
- for (int i=0; i < rowCount(); i++) {
+ for (int i = 0; i < rowCount(); ++i) {
if (compareBindingProperties(bindingPropertyForRow(i), bindingProperty))
return i;
}
- //not found
- return -1;
+
+ return -1; // not found
}
int DynamicPropertiesModel::findRowForVariantProperty(const VariantProperty &variantProperty) const
{
- for (int i=0; i < rowCount(); i++) {
+ for (int i = 0; i < rowCount(); ++i) {
if (compareVariantProperties(variantPropertyForRow(i), variantProperty))
return i;
}
- //not found
- return -1;
+
+ return -1; // not found
}
int DynamicPropertiesModel::findRowForProperty(const AbstractProperty &abstractProperty) const
{
- for (int i = 0; i < rowCount(); i++) {
+ for (int i = 0; i < rowCount(); ++i) {
if ((abstractPropertyForRow(i).name() == abstractProperty.name()))
return i;
}
- //not found
- return -1;
+
+ return -1; // not found
}
-bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
+bool DynamicPropertiesModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode,
+ QString *sourceProperty)
{
- //### todo we assume no expressions yet
+ // TODO: we assume no expressions yet
const QString expression = bindingProperty.expression();
if (true) {
- const QStringList stringList = expression.split(QLatin1String("."));
+ const QStringList expressionParts = expression.split('.');
- *sourceNode = stringList.constFirst();
+ *sourceNode = expressionParts.constFirst();
QString propertyName;
- for (int i=1; i < stringList.count(); i++) {
- propertyName += stringList.at(i);
- if (i != stringList.count() - 1)
+ for (int i = 1; i < expressionParts.count(); ++i) {
+ propertyName += expressionParts.at(i);
+ if (i != expressionParts.count() - 1)
propertyName += QLatin1String(".");
}
*sourceProperty = propertyName;
}
+
return true;
}
@@ -875,7 +871,7 @@ void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const
return;
if (topLeft != bottomRight) {
- qWarning() << "BindingModel::handleDataChanged multi edit?";
+ qWarning() << __FUNCTION__ << ": multi edit?";
return;
}
@@ -886,7 +882,7 @@ void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const
switch (currentColumn) {
case TargetModelNodeRow: {
- //updating user data
+ // updating user data
} break;
case PropertyNameRow: {
updatePropertyName(currentRow);
@@ -898,7 +894,7 @@ void DynamicPropertiesModel::handleDataChanged(const QModelIndex &topLeft, const
updateValue(currentRow);
} break;
- default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
+ default: qWarning() << __FUNCTION__ << " column" << currentColumn;
}
m_lock = false;
@@ -916,18 +912,16 @@ const QList<ModelNode> DynamicPropertiesModel::selectedNodes() const
// Otherwise return actual selected nodes of the model.
if (m_explicitSelection)
return m_selectedNodes;
- else
- return m_view->selectedModelNodes();
+
+ return m_view->selectedModelNodes();
}
const ModelNode DynamicPropertiesModel::singleSelectedNode() const
{
if (m_explicitSelection)
return m_selectedNodes.first();
- else
- return m_view->singleSelectedModelNode();
-}
-} // namespace Internal
+ return m_view->singleSelectedModelNode();
+}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
index fb8b5fb855..f094516e63 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
@@ -3,17 +3,17 @@
#pragma once
-#include <modelnode.h>
-#include <bindingproperty.h>
-#include <variantproperty.h>
+#include <nodeinstanceglobal.h>
#include <QStandardItemModel>
namespace QmlDesigner {
+class AbstractProperty;
class AbstractView;
-
-namespace Internal {
+class BindingProperty;
+class ModelNode;
+class VariantProperty;
class DynamicPropertiesModel : public QStandardItemModel
{
@@ -22,11 +22,13 @@ class DynamicPropertiesModel : public QStandardItemModel
public:
enum ColumnRoles {
TargetModelNodeRow = 0,
- PropertyNameRow = 1,
- PropertyTypeRow = 2,
- PropertyValueRow = 3
+ PropertyNameRow = 1,
+ PropertyTypeRow = 2,
+ PropertyValueRow = 3
};
+
DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
+
void bindingPropertyChanged(const BindingProperty &bindingProperty);
void abstractPropertyChanged(const AbstractProperty &bindingProperty);
void variantPropertyChanged(const VariantProperty &variantProperty);
@@ -54,7 +56,7 @@ public:
void dispatchPropertyChanges(const AbstractProperty &abstractProperty);
- QmlDesigner::PropertyName unusedProperty(const QmlDesigner::ModelNode &modelNode);
+ PropertyName unusedProperty(const ModelNode &modelNode);
static bool isValueType(const TypeName &type);
static QVariant defaultValueForType(const TypeName &type);
@@ -84,7 +86,7 @@ protected:
void updateDisplayRole(int row, int columns, const QString &string);
private:
- void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
+ void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void handleException();
AbstractView *m_view = nullptr;
@@ -95,5 +97,4 @@ private:
bool m_explicitSelection = false;
};
-} // namespace Internal
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.cpp
index 7232e83413..80cd732a9b 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.cpp
@@ -28,7 +28,7 @@
#include <dynamicpropertiesmodel.h>
#include <connectionview.h>
-using namespace QmlDesigner::Internal;
+namespace QmlDesigner {
SelectionDynamicPropertiesProxyModel::SelectionDynamicPropertiesProxyModel(QObject *parent)
: DynamicPropertiesProxyModel(parent)
@@ -42,3 +42,5 @@ void SelectionDynamicPropertiesProxyModel::registerDeclarativeType()
DynamicPropertiesProxyModel::registerDeclarativeType();
qmlRegisterType<SelectionDynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "SelectionDynamicPropertiesModel");
}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.h b/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.h
index 124846dc86..8957abe5af 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/selectiondynamicpropertiesproxymodel.h
@@ -27,6 +27,8 @@
#include <dynamicpropertiesproxymodel.h>
+namespace QmlDesigner {
+
class SelectionDynamicPropertiesProxyModel : public DynamicPropertiesProxyModel
{
Q_OBJECT
@@ -35,3 +37,5 @@ public:
static void registerDeclarativeType();
};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
index d2811592cd..834bc8aa30 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
@@ -3,6 +3,8 @@
#include "contentlibrarymaterial.h"
+#include <QFileInfo>
+
namespace QmlDesigner {
ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
@@ -10,8 +12,15 @@ ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
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) {}
+ const QStringList &files,
+ const QString &downloadPath,
+ const QString &baseWebUrl)
+ : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
+ , m_downloadPath(downloadPath), m_baseWebUrl(baseWebUrl)
+{
+ m_allFiles = m_files;
+ m_allFiles.push_back(m_qml);
+}
bool ContentLibraryMaterial::filter(const QString &searchText)
{
@@ -64,4 +73,25 @@ bool ContentLibraryMaterial::imported() const
return m_imported;
}
+bool ContentLibraryMaterial::isDownloaded() const
+{
+ QString fullPath = qmlFilePath();
+ return QFileInfo(fullPath).isFile();
+}
+
+QString ContentLibraryMaterial::qmlFilePath() const
+{
+ return m_downloadPath + "/" + m_qml;
+}
+
+QString ContentLibraryMaterial::parentDirPath() const
+{
+ return m_downloadPath;
+}
+
+QStringList ContentLibraryMaterial::allFiles() const
+{
+ return m_allFiles;
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
index cbcf5077dc..f546ea98cd 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
@@ -19,6 +19,9 @@ class ContentLibraryMaterial : public QObject
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(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT)
+ Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT)
+ Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT)
public:
ContentLibraryMaterial(QObject *parent,
@@ -26,18 +29,25 @@ public:
const QString &qml,
const TypeName &type,
const QUrl &icon,
- const QStringList &files);
+ const QStringList &files,
+ const QString &downloadPath,
+ const QString &baseWebUrl);
bool filter(const QString &searchText);
+ Q_INVOKABLE bool isDownloaded() const;
+
QUrl icon() const;
QString qml() const;
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:
void materialVisibleChanged();
@@ -52,6 +62,10 @@ private:
bool m_visible = true;
bool m_imported = false;
+
+ QString m_downloadPath;
+ QString m_baseWebUrl;
+ QStringList m_allFiles;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
index 5baf4bd068..127ecb4225 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
@@ -7,14 +7,21 @@
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialscategory.h"
#include "contentlibrarywidget.h"
+#include "filedownloader.h"
+#include "fileextractor.h"
+#include "multifiledownloader.h"
#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
-#include "utils/algorithm.h"
-#include "utils/qtcassert.h"
+#include <utils/algorithm.h>
+#include <utils/hostosinfo.h>
+#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QJsonArray>
#include <QJsonDocument>
+#include <QQmlEngine>
+#include <QStandardPaths>
#include <QUrl>
namespace QmlDesigner {
@@ -23,7 +30,19 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget
: QAbstractListModel(parent)
, m_widget(parent)
{
- loadMaterialBundle();
+ m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)
+ + "/QtDesignStudio/bundles/Materials";
+
+ m_baseUrl = QmlDesignerPlugin::settings()
+ .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL)
+ .toString() + "/materials/v1";
+
+ qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
+ qmlRegisterType<QmlDesigner::MultiFileDownloader>("WebFetcher", 1, 0, "MultiFileDownloader");
+
+ QDir bundleDir{m_downloadPath};
+ if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir))
+ loadMaterialBundle(bundleDir);
}
int ContentLibraryMaterialsModel::rowCount(const QModelIndex &) const
@@ -90,24 +109,126 @@ QHash<int, QByteArray> ContentLibraryMaterialsModel::roleNames() const
return roles;
}
-void ContentLibraryMaterialsModel::loadMaterialBundle()
+bool ContentLibraryMaterialsModel::fetchBundleIcons(const QDir &bundleDir)
{
- if (m_matBundleExists || m_probeMatBundleDir)
- return;
+ QString iconsPath = bundleDir.filePath("icons");
- QDir matBundleDir(qEnvironmentVariable("MATERIAL_BUNDLE_PATH"));
+ QDir iconsDir(iconsPath);
+ if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0)
+ return true;
- // search for matBundleDir from exec dir and up
- if (matBundleDir.dirName() == ".") {
- m_probeMatBundleDir = true; // probe only once
+ QString zipFileUrl = m_baseUrl + "/icons.zip";
- matBundleDir.setPath(QCoreApplication::applicationDirPath());
- while (!matBundleDir.cd("material_bundle") && matBundleDir.cdUp())
- ; // do nothing
+ FileDownloader *downloader = new FileDownloader(this);
+ downloader->setUrl(zipFileUrl);
+ downloader->setProbeUrl(false);
+ downloader->setDownloadEnabled(true);
- if (matBundleDir.dirName() != "material_bundle") // bundlePathDir not found
- return;
- }
+ QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
+ FileExtractor *extractor = new FileExtractor(this);
+ extractor->setArchiveName(downloader->completeBaseName());
+ extractor->setSourceFile(downloader->outputFile());
+ extractor->setTargetPath(bundleDir.absolutePath());
+ extractor->setAlwaysCreateDir(false);
+ extractor->setClearTargetPathContents(false);
+
+ QObject::connect(extractor, &FileExtractor::finishedChanged, this, [=]() {
+ downloader->deleteLater();
+ extractor->deleteLater();
+
+ loadMaterialBundle(bundleDir);
+ });
+
+ extractor->extract();
+ });
+
+ downloader->start();
+ return false;
+}
+
+bool ContentLibraryMaterialsModel::fetchBundleMetadata(const QDir &bundleDir)
+{
+ QString matBundlePath = bundleDir.filePath("material_bundle.json");
+
+ QFileInfo fi(matBundlePath);
+ if (fi.exists() && fi.size() > 0)
+ return true;
+
+ QString metaFileUrl = m_baseUrl + "/material_bundle.json";
+ FileDownloader *downloader = new FileDownloader(this);
+ downloader->setUrl(metaFileUrl);
+ downloader->setProbeUrl(false);
+ downloader->setDownloadEnabled(true);
+ downloader->setTargetFilePath(matBundlePath);
+
+ QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
+ if (fetchBundleIcons(bundleDir))
+ loadMaterialBundle(bundleDir);
+
+ downloader->deleteLater();
+ });
+
+ downloader->start();
+ return false;
+}
+
+void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, const QStringList &)
+{
+ QString metaFileUrl = m_baseUrl + "/shared_files.zip";
+ FileDownloader *downloader = new FileDownloader(this);
+ downloader->setUrl(metaFileUrl);
+ downloader->setProbeUrl(false);
+ downloader->setDownloadEnabled(true);
+
+ QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
+ FileExtractor *extractor = new FileExtractor(this);
+ extractor->setArchiveName(downloader->completeBaseName());
+ extractor->setSourceFile(downloader->outputFile());
+ extractor->setTargetPath(targetDir.absolutePath());
+ extractor->setAlwaysCreateDir(false);
+ extractor->setClearTargetPathContents(false);
+
+ QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() {
+ downloader->deleteLater();
+ extractor->deleteLater();
+
+ createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles);
+ });
+
+ extractor->extract();
+ });
+
+ downloader->start();
+}
+
+void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId,
+ const QStringList &sharedFiles)
+{
+ m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
+ connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
+ m_importerRunning = false;
+ emit importerRunningChanged();
+ if (metaInfo.isValid())
+ emit bundleMaterialImported(metaInfo);
+ });
+
+ connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
+ Q_UNUSED(metaInfo)
+ m_importerRunning = false;
+ emit importerRunningChanged();
+ emit bundleMaterialUnimported(metaInfo);
+ });
+
+ resetModel();
+ updateIsEmpty();
+}
+
+void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
+{
+ if (m_matBundleExists)
+ return;
QString matBundlePath = matBundleDir.filePath("material_bundle.json");
@@ -128,8 +249,6 @@ void ContentLibraryMaterialsModel::loadMaterialBundle()
}
}
- m_matBundleExists = true;
-
QString bundleId = m_matBundleObj.value("id").toString();
const QJsonObject catsObj = m_matBundleObj.value("categories").toObject();
@@ -154,7 +273,8 @@ void ContentLibraryMaterialsModel::loadMaterialBundle()
bundleId,
qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
- auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files);
+ auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files,
+ m_downloadPath, m_baseUrl);
category->addBundleMaterial(bundleMat);
}
@@ -166,24 +286,25 @@ void ContentLibraryMaterialsModel::loadMaterialBundle()
for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
sharedFiles.append(file.toString());
- m_importer = new Internal::ContentLibraryBundleImporter(matBundleDir.path(), bundleId, sharedFiles);
- connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (metaInfo.isValid())
- emit bundleMaterialImported(metaInfo);
- });
+ QStringList missingSharedFiles;
+ for (const QString &s : std::as_const(sharedFiles)) {
+ const QString fullSharedFilePath = matBundleDir.filePath(s);
- connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- Q_UNUSED(metaInfo)
- m_importerRunning = false;
- emit importerRunningChanged();
- emit bundleMaterialUnimported(metaInfo);
- });
+ if (!QFile::exists(fullSharedFilePath))
+ missingSharedFiles.push_back(s);
+ }
- updateIsEmpty();
+ if (missingSharedFiles.length() > 0) {
+ m_importerBundlePath = matBundleDir.path();
+ m_importerBundleId = bundleId;
+ m_importerSharedFiles = sharedFiles;
+ downloadSharedFiles(matBundleDir, missingSharedFiles);
+ } else {
+ createImporter(matBundleDir.path(), bundleId, sharedFiles);
+ }
+
+ m_matBundleExists = true;
+ emit matBundleExistsChanged();
}
bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
index 2fcd152f54..836ebc6c1e 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
@@ -6,6 +6,7 @@
#include "nodemetainfo.h"
#include <QAbstractListModel>
+#include <QDir>
#include <QJsonObject>
namespace QmlDesigner {
@@ -70,8 +71,13 @@ signals:
void matBundleExistsChanged();
private:
- void loadMaterialBundle();
+ void loadMaterialBundle(const QDir &matBundleDir);
+ bool fetchBundleIcons(const QDir &bundleDir);
+ 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;
@@ -82,11 +88,17 @@ private:
bool m_isEmpty = true;
bool m_matBundleExists = false;
bool m_hasModelSelection = false;
- bool m_probeMatBundleDir = false;
bool m_importerRunning = 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 d14d56b7d5..fbc5199f98 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
@@ -4,20 +4,33 @@
#include "contentlibrarytexture.h"
#include "imageutils.h"
+#include <utils/algorithm.h>
+
+#include <QDir>
+#include <QFileInfo>
namespace QmlDesigner {
-ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QString &path, const QUrl &icon)
+ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo,
+ const QString &downloadPath, const QUrl &icon,
+ const QString &webUrl, const QString &fileExt,
+ const QSize &dimensions, const qint64 sizeInBytes)
: QObject(parent)
- , m_path(path)
+ , m_iconPath(iconFileInfo.filePath())
+ , m_downloadPath(downloadPath)
+ , m_webUrl(webUrl)
+ , m_baseName{iconFileInfo.baseName()}
+ , m_fileExt(fileExt)
, m_icon(icon)
+ , m_dimensions(dimensions)
+ , m_sizeInBytes(sizeInBytes)
{
- m_toolTip = QLatin1String("%1\n%2").arg(path.split('/').last(), ImageUtils::imageInfo(path));
+ doSetDownloaded();
}
bool ContentLibraryTexture::filter(const QString &searchText)
{
- if (m_visible != m_path.contains(searchText, Qt::CaseInsensitive)) {
+ if (m_visible != m_iconPath.contains(searchText, Qt::CaseInsensitive)) {
m_visible = !m_visible;
emit textureVisibleChanged();
}
@@ -30,9 +43,83 @@ QUrl ContentLibraryTexture::icon() const
return m_icon;
}
-QString ContentLibraryTexture::path() const
+QString ContentLibraryTexture::iconPath() const
+{
+ return m_iconPath;
+}
+
+QString ContentLibraryTexture::resolveFileExt()
+{
+ const QFileInfoList files = QDir(m_downloadPath).entryInfoList(QDir::Files);
+ const QFileInfoList textureFiles = Utils::filtered(files, [this](const QFileInfo &fi) {
+ return fi.baseName() == m_baseName;
+ });
+
+ if (textureFiles.isEmpty())
+ return {};
+
+ if (textureFiles.count() > 1) {
+ qWarning() << "Found multiple textures with the same name in the same directories: "
+ << Utils::transform(textureFiles, [](const QFileInfo &fi) {
+ return fi.fileName();
+ });
+ }
+
+ return '.' + textureFiles.at(0).completeSuffix();
+}
+
+QString ContentLibraryTexture::resolveToolTipText()
+{
+ if (m_fileExt.isEmpty()) {
+ // No supplied or resolved extension means we have just the icon and no other data
+ return m_baseName;
+ }
+
+ QString fileName = m_baseName + m_fileExt;
+ QString imageInfo;
+
+ if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) {
+ imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes);
+ } else {
+ QString fullDownloadPath = m_downloadPath + '/' + fileName;
+ imageInfo = ImageUtils::imageInfo(fullDownloadPath);
+ }
+
+ return QStringLiteral("%1\n%2").arg(fileName, imageInfo);
+}
+
+bool ContentLibraryTexture::isDownloaded() const
+{
+ return m_isDownloaded;
+}
+
+QString ContentLibraryTexture::downloadedTexturePath() const
+{
+ return m_downloadPath + '/' + m_baseName + m_fileExt;
+}
+
+void ContentLibraryTexture::setDownloaded()
+{
+ QString toolTip = m_toolTip;
+
+ doSetDownloaded();
+
+ if (toolTip != m_toolTip)
+ emit textureToolTipChanged();
+}
+
+void ContentLibraryTexture::doSetDownloaded()
+{
+ if (m_fileExt.isEmpty())
+ m_fileExt = resolveFileExt();
+
+ m_isDownloaded = QFileInfo::exists(downloadedTexturePath());
+ m_toolTip = resolveToolTipText();
+}
+
+QString ContentLibraryTexture::parentDirPath() const
{
- return m_path;
+ return m_downloadPath;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
index 399b7644ce..074d4abb77 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
@@ -3,7 +3,9 @@
#pragma once
+#include <QFileInfo>
#include <QObject>
+#include <QSize>
#include <QUrl>
namespace QmlDesigner {
@@ -12,26 +14,47 @@ class ContentLibraryTexture : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString texturePath MEMBER m_path CONSTANT)
- Q_PROPERTY(QString textureToolTip MEMBER m_toolTip CONSTANT)
+ Q_PROPERTY(QString textureIconPath MEMBER m_iconPath CONSTANT)
+ Q_PROPERTY(QString textureParentPath READ parentDirPath CONSTANT)
+ Q_PROPERTY(QString textureToolTip MEMBER m_toolTip NOTIFY textureToolTipChanged)
Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged)
+ Q_PROPERTY(QString textureWebUrl MEMBER m_webUrl CONSTANT)
public:
- ContentLibraryTexture(QObject *parent, const QString &path, const QUrl &icon);
+ ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo,
+ const QString &downloadPath, const QUrl &icon, const QString &webUrl,
+ const QString &fileExt, const QSize &dimensions, const qint64 sizeInBytes);
+
+ Q_INVOKABLE bool isDownloaded() const;
+ Q_INVOKABLE void setDownloaded();
bool filter(const QString &searchText);
QUrl icon() const;
- QString path() const;
+ QString iconPath() const;
+ QString downloadedTexturePath() const;
+ QString parentDirPath() const;
signals:
void textureVisibleChanged();
+ void textureToolTipChanged();
private:
- QString m_path;
+ QString resolveFileExt();
+ QString resolveToolTipText();
+ void doSetDownloaded();
+
+ QString m_iconPath;
+ QString m_downloadPath;
+ QString m_webUrl;
QString m_toolTip;
+ QString m_baseName;
+ QString m_fileExt;
QUrl m_icon;
+ QSize m_dimensions;
+ qint64 m_sizeInBytes = -1;
+ bool m_isDownloaded = false;
bool m_visible = true;
};
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
index d2de827f03..eecab42552 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
@@ -12,10 +12,14 @@ namespace QmlDesigner {
ContentLibraryTexturesCategory::ContentLibraryTexturesCategory(QObject *parent, const QString &name)
: QObject(parent), m_name(name) {}
-void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex)
+void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath,
+ const QString &webUrl, const QString &fileExt,
+ const QSize &dimensions, const qint64 sizeInBytes)
{
- QUrl icon = QUrl::fromLocalFile(tex.path() + "/icon/" + tex.baseName() + ".png");
- m_categoryTextures.append(new ContentLibraryTexture(this, tex.filePath(), icon));
+ QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath());
+
+ m_categoryTextures.append(new ContentLibraryTexture(this, tex, downloadPath, icon, webUrl,
+ fileExt, dimensions, sizeInBytes));
}
bool ContentLibraryTexturesCategory::filter(const QString &searchText)
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
index 69aeeb3297..91ecaaac96 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
@@ -7,6 +7,7 @@
QT_BEGIN_NAMESPACE
class QFileInfo;
+class QSize;
QT_END_NAMESPACE
namespace QmlDesigner {
@@ -26,7 +27,8 @@ class ContentLibraryTexturesCategory : public QObject
public:
ContentLibraryTexturesCategory(QObject *parent, const QString &name);
- void addTexture(const QFileInfo &tex);
+ void addTexture(const QFileInfo &tex, const QString &subPath, const QString &webUrl,
+ const QString &fileExt, const QSize &dimensions, const qint64 sizeInBytes);
bool filter(const QString &searchText);
QString name() const;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
index 0dfc8eb530..0d74be596f 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
@@ -4,20 +4,29 @@
#include "contentlibrarytexturesmodel.h"
#include "contentlibrarytexturescategory.h"
+#include "qmldesignerplugin.h"
-#include "utils/algorithm.h"
-#include "utils/qtcassert.h"
+#include <qmldesignerbase/qmldesignerbaseplugin.h>
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QDir>
+#include <QFile>
#include <QFileInfo>
+#include <QJsonDocument>
+#include <QQmlEngine>
+#include <QSize>
+#include <QStandardPaths>
#include <QUrl>
namespace QmlDesigner {
-ContentLibraryTexturesModel::ContentLibraryTexturesModel(QObject *parent)
+ContentLibraryTexturesModel::ContentLibraryTexturesModel(const QString &category, QObject *parent)
: QAbstractListModel(parent)
{
+ m_category = category; // textures main category (ex: Textures, Environments)
}
int ContentLibraryTexturesModel::rowCount(const QModelIndex &) const
@@ -83,26 +92,53 @@ QHash<int, QByteArray> ContentLibraryTexturesModel::roleNames() const
return roles;
}
-void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath)
+/**
+ * @brief Load the bundle categorized icons. Actual textures are downloaded on demand
+ *
+ * @param bundlePath local path to the bundle folder and icons
+ * @param metaData bundle textures metadata
+ */
+void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, const QString &bundleIconPath,
+ const QVariantMap &metaData)
{
- QDir bundleDir = QDir(bundlePath);
+ if (!m_bundleCategories.isEmpty())
+ return;
+
+ QDir bundleDir = QString("%1/%2").arg(bundleIconPath, m_category);
if (!bundleDir.exists()) {
- qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundlePath;
+ qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundleDir.absolutePath();
return;
}
- if (!m_bundleCategories.isEmpty())
- return;
+ const QVariantMap imageItems = metaData.value("image_items").toMap();
- const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &dir : dirs) {
auto category = new ContentLibraryTexturesCategory(this, dir.fileName());
const QFileInfoList texFiles = QDir(dir.filePath()).entryInfoList(QDir::Files);
- for (const QFileInfo &tex : texFiles)
- category->addTexture(tex);
+ for (const QFileInfo &tex : texFiles) {
+ QString fullRemoteUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(),
+ tex.baseName());
+ QString localDownloadPath = QString("%1/%2/%3").arg(QmlDesignerBasePlugin::bundlesPathSetting(),
+ m_category, dir.fileName());
+ QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), tex.baseName());
+ QString fileExt;
+ QSize dimensions;
+ qint64 sizeInBytes = -1;
+
+ if (imageItems.contains(key)) {
+ QVariantMap dataMap = imageItems[key].toMap();
+ fileExt = '.' + dataMap.value("format").toString();
+ dimensions = QSize(dataMap.value("width").toInt(), dataMap.value("height").toInt());
+ sizeInBytes = dataMap.value("file_size").toLongLong();
+ }
+
+ category->addTexture(tex, localDownloadPath, fullRemoteUrl, fileExt, dimensions, sizeInBytes);
+ }
m_bundleCategories.append(category);
}
+ resetModel();
updateIsEmpty();
}
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
index 077199d84f..3f7e4d4868 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
@@ -18,7 +18,7 @@ class ContentLibraryTexturesModel : public QAbstractListModel
Q_PROPERTY(bool hasSceneEnv READ hasSceneEnv NOTIFY hasSceneEnvChanged)
public:
- ContentLibraryTexturesModel(QObject *parent = nullptr);
+ ContentLibraryTexturesModel(const QString &category, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -33,7 +33,8 @@ public:
void setHasSceneEnv(bool b);
void resetModel();
- void loadTextureBundle(const QString &bundlePath);
+ void loadTextureBundle(const QString &remoteUrl, const QString &bundlePath,
+ const QVariantMap &metaData);
signals:
void isEmptyChanged();
@@ -45,6 +46,7 @@ private:
void updateIsEmpty();
QString m_searchText;
+ QString m_category;
QList<ContentLibraryTexturesCategory *> m_bundleCategories;
bool m_isEmpty = true;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
index f0969bfabb..9ce7a4e737 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
@@ -20,7 +20,7 @@
#ifndef QMLDESIGNER_TEST
#include <projectexplorer/kit.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
@@ -30,7 +30,7 @@ namespace QmlDesigner {
ContentLibraryView::ContentLibraryView(ExternalDependenciesInterface &externalDependencies)
: AbstractView(externalDependencies)
- , m_createTexture(this, true)
+ , m_createTexture(this)
{}
ContentLibraryView::~ContentLibraryView()
@@ -132,6 +132,8 @@ void ContentLibraryView::modelAttached(Model *model)
m_widget->setHasQuick3DImport(m_hasQuick3DImport);
m_sceneId = model->active3DSceneId();
+
+ m_widget->clearSearchFilter();
}
void ContentLibraryView::modelAboutToBeDetached(Model *model)
@@ -323,6 +325,8 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo)
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
objNameProp.setValue(newName);
+ emitCustomNotification("focus_material_section", {});
+
return newMatNode;
}
@@ -367,7 +371,7 @@ void ContentLibraryView::updateBundleMaterialsQuick3DVersion()
#ifndef QMLDESIGNER_TEST
if (hasImport && major == -1) {
// Import without specifying version, so we take the kit version
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
if (target) {
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (qtVersion) {
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
index 89256a1e5f..a1dfff5ef5 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
@@ -8,26 +8,36 @@
#include "contentlibrarytexture.h"
#include "contentlibrarytexturesmodel.h"
+#include "utils/filedownloader.h"
+#include "utils/fileextractor.h"
+
#include <coreplugin/icore.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
+#include <studioquickwidget.h>
#include <theme.h>
#include <utils/algorithm.h>
+#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
+#include <QDir>
+#include <QJsonDocument>
#include <QMimeData>
#include <QMouseEvent>
#include <QQmlContext>
-#include <QQuickWidget>
#include <QQmlEngine>
#include <QQuickItem>
+#include <QQuickWidget>
#include <QShortcut>
+#include <QStandardPaths>
#include <QVBoxLayout>
namespace QmlDesigner {
+constexpr int TextureBundleMetadataVersion = 1;
+
static QString propertyEditorResourcesPath()
{
#ifdef SHARE_QML_PATH
@@ -40,7 +50,7 @@ static QString propertyEditorResourcesPath()
bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::FocusOut) {
- if (obj == m_quickWidget.data())
+ if (obj == m_quickWidget->quickWidget())
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "closeContextMenu");
} else if (event->type() == QMouseEvent::MouseMove) {
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -50,7 +60,8 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
if (m_materialToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
- if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) {
+ if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20
+ && m_materialToDrag->isDownloaded()) {
QByteArray data;
QMimeData *mimeData = new QMimeData;
QDataStream stream(&data, QIODevice::WriteOnly);
@@ -64,13 +75,14 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
}
} else if (m_textureToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
- if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) {
- QByteArray data;
+ if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20
+ && m_textureToDrag->isDownloaded()) {
QMimeData *mimeData = new QMimeData;
- QDataStream stream(&data, QIODevice::WriteOnly);
- stream << m_textureToDrag->path();
- mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE, data);
- mimeData->removeFormat("text/plain");
+ mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE,
+ {m_textureToDrag->downloadedTexturePath().toUtf8()});
+
+ // Allows standard file drag-n-drop. As of now needed to drop on Assets view
+ mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->downloadedTexturePath())});
emit bundleTextureDragStarted(m_textureToDrag);
model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile());
@@ -80,37 +92,43 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_materialToDrag = nullptr;
m_textureToDrag = nullptr;
+ setIsDragging(false);
}
return QObject::eventFilter(obj, event);
}
ContentLibraryWidget::ContentLibraryWidget()
- : m_quickWidget(new QQuickWidget(this))
+ : m_quickWidget(new StudioQuickWidget(this))
, m_materialsModel(new ContentLibraryMaterialsModel(this))
- , m_texturesModel(new ContentLibraryTexturesModel(this))
- , m_environmentsModel(new ContentLibraryTexturesModel(this))
+ , m_texturesModel(new ContentLibraryTexturesModel("Textures", this))
+ , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this))
{
+ qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
+ qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor");
+
setWindowTitle(tr("Content Library", "Title of content library widget"));
setMinimumWidth(120);
+ m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_CONTENT_LIBRARY);
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
- QString textureBundlePath = findTextureBundlePath();
- m_texturesModel->loadTextureBundle(textureBundlePath + "/Textures");
- m_environmentsModel->loadTextureBundle(textureBundlePath + "/Environments");
+ m_baseUrl = QmlDesignerPlugin::settings()
+ .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString()
+ + "/textures/v1";
- m_quickWidget->rootContext()->setContextProperties({
- {"rootView", QVariant::fromValue(this)},
- {"materialsModel", QVariant::fromValue(m_materialsModel.data())},
- {"texturesModel", QVariant::fromValue(m_texturesModel.data())},
- {"environmentsModel", QVariant::fromValue(m_environmentsModel.data())},
- });
+ m_texturesUrl = m_baseUrl + "/Textures";
+ m_environmentsUrl = m_baseUrl + "/Environments";
+
+ m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)
+ + "/QtDesignStudio/bundles";
+
+ loadTextureBundle();
Theme::setupTheme(m_quickWidget->engine());
- m_quickWidget->installEventFilter(this);
+ m_quickWidget->quickWidget()->installEventFilter(this);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
@@ -125,11 +143,127 @@ ContentLibraryWidget::ContentLibraryWidget()
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F11), this);
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ContentLibraryWidget::reloadQmlSource);
-// QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MATERIALBROWSER_TIME); // TODO
+ QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_CONTENTLIBRARY_TIME);
+
+ auto map = m_quickWidget->registerPropertyMap("ContentLibraryBackend");
+
+ map->setProperties({{"rootView", QVariant::fromValue(this)},
+ {"materialsModel", QVariant::fromValue(m_materialsModel.data())},
+ {"texturesModel", QVariant::fromValue(m_texturesModel.data())},
+ {"environmentsModel", QVariant::fromValue(m_environmentsModel.data())}});
reloadQmlSource();
}
+QVariantMap ContentLibraryWidget::readBundleMetadata()
+{
+ QVariantMap metaData;
+ QFile jsonFile(m_downloadPath + "/texture_bundle.json");
+ if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap();
+
+ int version = metaData["version"].toInt();
+ if (version > TextureBundleMetadataVersion) {
+ qWarning() << "Unrecognized texture metadata file version: " << version;
+ return {};
+ }
+
+ return metaData;
+}
+
+void ContentLibraryWidget::loadTextureBundle()
+{
+ QDir bundleDir{m_downloadPath};
+
+ if (fetchTextureBundleMetadata(bundleDir) && fetchTextureBundleIcons(bundleDir)) {
+ QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
+ QVariantMap metaData = readBundleMetadata();
+ m_texturesModel->loadTextureBundle(m_texturesUrl, bundleIconPath, metaData);
+ m_environmentsModel->loadTextureBundle(m_environmentsUrl, bundleIconPath, metaData);
+ }
+}
+
+bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir)
+{
+ QString filePath = bundleDir.filePath("texture_bundle.json");
+
+ QFileInfo fi(filePath);
+ if (fi.exists() && fi.size() > 0)
+ return true;
+
+ QString metaFileUrl = m_baseUrl + "/texture_bundle.zip";
+ FileDownloader *downloader = new FileDownloader(this);
+ downloader->setUrl(metaFileUrl);
+ downloader->setProbeUrl(false);
+ downloader->setDownloadEnabled(true);
+
+ QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
+ FileExtractor *extractor = new FileExtractor(this);
+ extractor->setArchiveName(downloader->completeBaseName());
+ extractor->setSourceFile(downloader->outputFile());
+ extractor->setTargetPath(bundleDir.absolutePath());
+ extractor->setAlwaysCreateDir(false);
+ extractor->setClearTargetPathContents(false);
+
+ QObject::connect(extractor, &FileExtractor::finishedChanged, this, [=]() {
+ downloader->deleteLater();
+ extractor->deleteLater();
+
+ if (fetchTextureBundleIcons(bundleDir)) {
+ QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
+ QVariantMap metaData = readBundleMetadata();
+ m_texturesModel->loadTextureBundle(m_texturesUrl, bundleIconPath, metaData);
+ m_environmentsModel->loadTextureBundle(m_environmentsUrl, bundleIconPath, metaData);
+ }
+ });
+
+ extractor->extract();
+ });
+
+ downloader->start();
+ return false;
+}
+
+bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir)
+{
+ QString iconsPath = bundleDir.filePath("TextureBundleIcons");
+
+ QDir iconsDir(iconsPath);
+ if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0)
+ return true;
+
+ QString zipFileUrl = m_baseUrl + "/icons.zip";
+
+ FileDownloader *downloader = new FileDownloader(this);
+ downloader->setUrl(zipFileUrl);
+ downloader->setProbeUrl(false);
+ downloader->setDownloadEnabled(true);
+
+ QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
+ FileExtractor *extractor = new FileExtractor(this);
+ extractor->setArchiveName(downloader->completeBaseName());
+ extractor->setSourceFile(downloader->outputFile());
+ extractor->setTargetPath(bundleDir.absolutePath());
+ extractor->setAlwaysCreateDir(false);
+ extractor->setClearTargetPathContents(false);
+
+ QObject::connect(extractor, &FileExtractor::finishedChanged, this, [=]() {
+ downloader->deleteLater();
+ extractor->deleteLater();
+
+ QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
+ QVariantMap metaData = readBundleMetadata();
+ m_texturesModel->loadTextureBundle(m_texturesUrl, bundleIconPath, metaData);
+ m_environmentsModel->loadTextureBundle(m_environmentsUrl, bundleIconPath, metaData);
+ });
+
+ extractor->extract();
+ });
+
+ downloader->start();
+ return false;
+}
+
QList<QToolButton *> ContentLibraryWidget::createToolBarWidgets()
{
return {};
@@ -201,7 +335,6 @@ void ContentLibraryWidget::reloadQmlSource()
QTC_ASSERT(QFileInfo::exists(materialBrowserQmlPath), return);
- m_quickWidget->engine()->clearComponentCache();
m_quickWidget->setSource(QUrl::fromLocalFile(materialBrowserQmlPath));
}
@@ -213,9 +346,22 @@ void ContentLibraryWidget::updateSearch()
m_quickWidget->update();
}
+void ContentLibraryWidget::setIsDragging(bool val)
+{
+ if (m_isDragging != val) {
+ m_isDragging = val;
+ emit isDraggingChanged();
+ }
+}
+
QString ContentLibraryWidget::findTextureBundlePath()
{
- QDir texBundleDir(qEnvironmentVariable("TEXTURE_BUNDLE_PATH"));
+ 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() == ".") {
@@ -235,6 +381,7 @@ void ContentLibraryWidget::startDragMaterial(QmlDesigner::ContentLibraryMaterial
{
m_materialToDrag = mat;
m_dragStartPoint = mousePos.toPoint();
+ setIsDragging(true);
}
void ContentLibraryWidget::startDragTexture(QmlDesigner::ContentLibraryTexture *tex,
@@ -242,21 +389,31 @@ void ContentLibraryWidget::startDragTexture(QmlDesigner::ContentLibraryTexture *
{
m_textureToDrag = tex;
m_dragStartPoint = mousePos.toPoint();
+ setIsDragging(true);
}
void ContentLibraryWidget::addImage(ContentLibraryTexture *tex)
{
- emit addTextureRequested(tex->path(), AddTextureMode::Image);
+ if (!tex->isDownloaded())
+ return;
+
+ emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Image);
}
void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
{
- emit addTextureRequested(tex->path(), AddTextureMode::Texture);
+ if (!tex->isDownloaded())
+ return;
+
+ emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Texture);
}
void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
{
- emit addTextureRequested(tex->path(), AddTextureMode::LightProbe);
+ if (!tex->isDownloaded())
+ return;
+
+ emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::LightProbe);
}
void ContentLibraryWidget::updateSceneEnvState()
@@ -279,4 +436,18 @@ QPointer<ContentLibraryTexturesModel> ContentLibraryWidget::environmentsModel()
return m_environmentsModel;
}
+bool ContentLibraryWidget::markTextureDownloading()
+{
+ if (m_anyTextureBeingDownloaded)
+ return false;
+
+ m_anyTextureBeingDownloaded = true;
+ return true; // let the caller know it can begin download
+}
+
+void ContentLibraryWidget::markNoTextureDownloading()
+{
+ m_anyTextureBeingDownloaded = false; // allow other textures to be downloaded
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
index 6de5042ed6..519d6fcf78 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
@@ -9,11 +9,13 @@
#include <QPointer>
QT_BEGIN_NAMESPACE
+class QDir;
class QShortcut;
class QToolButton;
-class QQuickWidget;
QT_END_NAMESPACE
+class StudioQuickWidget;
+
namespace QmlDesigner {
class ContentLibraryTexture;
@@ -28,6 +30,9 @@ class ContentLibraryWidget : public QFrame
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport NOTIFY hasQuick3DImportChanged)
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
+ // 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)
+
public:
ContentLibraryWidget();
@@ -56,6 +61,8 @@ public:
Q_INVOKABLE void addTexture(QmlDesigner::ContentLibraryTexture *tex);
Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex);
Q_INVOKABLE void updateSceneEnvState();
+ Q_INVOKABLE bool markTextureDownloading();
+ Q_INVOKABLE void markNoTextureDownloading();
signals:
void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat);
@@ -64,6 +71,7 @@ signals:
void updateSceneEnvStateRequested();
void hasQuick3DImportChanged();
void hasMaterialLibraryChanged();
+ void isDraggingChanged();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -71,9 +79,14 @@ protected:
private:
void reloadQmlSource();
void updateSearch();
+ void setIsDragging(bool val);
QString findTextureBundlePath();
+ void loadTextureBundle();
+ QVariantMap readBundleMetadata();
+ bool fetchTextureBundleMetadata(const QDir &bundleDir);
+ bool fetchTextureBundleIcons(const QDir &bundleDir);
- QScopedPointer<QQuickWidget> m_quickWidget;
+ QScopedPointer<StudioQuickWidget> m_quickWidget;
QPointer<ContentLibraryMaterialsModel> m_materialsModel;
QPointer<ContentLibraryTexturesModel> m_texturesModel;
QPointer<ContentLibraryTexturesModel> m_environmentsModel;
@@ -88,6 +101,12 @@ private:
bool m_hasMaterialLibrary = false;
bool m_hasQuick3DImport = false;
+ bool m_isDragging = false;
+ bool m_anyTextureBeingDownloaded = false;
+ QString m_baseUrl;
+ QString m_texturesUrl;
+ QString m_environmentsUrl;
+ QString m_downloadPath;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp
index 1ef8190048..8ef538bd2f 100644
--- a/src/plugins/qmldesigner/components/createtexture.cpp
+++ b/src/plugins/qmldesigner/components/createtexture.cpp
@@ -4,7 +4,9 @@
#include "createtexture.h"
#include "abstractview.h"
+#include "asset.h"
#include "documentmanager.h"
+#include "modelnode.h"
#include "modelnodeoperations.h"
#include "nodelistproperty.h"
#include "nodemetainfo.h"
@@ -17,27 +19,39 @@
namespace QmlDesigner {
-CreateTexture::CreateTexture(AbstractView *view, bool importFile)
+CreateTexture::CreateTexture(AbstractView *view)
: m_view{view}
- , m_importFile{importFile}
{}
-void CreateTexture::execute(const QString &filePath, AddTextureMode mode, int sceneId)
+ModelNode CreateTexture::execute(const QString &filePath, AddTextureMode mode, int sceneId)
{
- if (m_importFile && !addFileToProject(filePath))
- return;
+ Asset asset(filePath);
+ if (!asset.isValidTextureSource())
+ return {};
+
+ Utils::FilePath assetPath = Utils::FilePath::fromString(filePath);
+ if (!assetPath.isChildOf(DocumentManager::currentResourcePath())) {
+ if (!addFileToProject(filePath))
+ return {};
+
+ // after importing change assetPath to path in project
+ QString assetName = assetPath.fileName();
+ assetPath = ModelNodeOperations::getImagesDefaultDirectory().pathAppended(assetName);
+ }
- ModelNode texture = createTextureFromImage(filePath, mode);
+ ModelNode texture = createTextureFromImage(assetPath, mode);
if (!texture.isValid())
- return;
+ return {};
if (mode == AddTextureMode::LightProbe && sceneId != -1)
assignTextureAsLightProbe(texture, sceneId);
QTimer::singleShot(0, m_view, [this, texture]() {
if (m_view->model())
- m_view->emitCustomNotification("selected_texture_changed", {texture});
+ m_view->emitCustomNotification("select_texture", {texture}, {true});
});
+
+ return texture;
}
bool CreateTexture::addFileToProject(const QString &filePath)
@@ -54,7 +68,7 @@ bool CreateTexture::addFileToProject(const QString &filePath)
return true;
}
-ModelNode CreateTexture::createTextureFromImage(const QString &assetPath, AddTextureMode mode)
+ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPath, AddTextureMode mode)
{
if (mode != AddTextureMode::Texture && mode != AddTextureMode::LightProbe)
return {};
@@ -65,25 +79,16 @@ ModelNode CreateTexture::createTextureFromImage(const QString &assetPath, AddTex
NodeMetaInfo metaInfo = m_view->model()->qtQuick3DTextureMetaInfo();
- Utils::FilePath currentDocumentPath = QmlDesigner::DocumentManager::currentFilePath();
- Utils::FilePath imageTargetPath;
- if (m_importFile) {
- QString assetName = Utils::FilePath::fromString(assetPath).fileName();
- // if the asset had to be imported from somewhere else, then assetPath is the source where
- // the asset was taken from, and we have to compute where it was placed in the project.
- imageTargetPath = ModelNodeOperations::getImagesDefaultDirectory().pathAppended(assetName);
- } else {
- imageTargetPath = Utils::FilePath::fromString(assetPath);
- }
-
- QString textureSource = imageTargetPath.relativePathFrom(currentDocumentPath).toString();
+ QString textureSource = assetPath.relativePathFrom(DocumentManager::currentFilePath()).toString();
ModelNode newTexNode = m_view->getTextureDefaultInstance(textureSource);
if (!newTexNode.isValid()) {
newTexNode = m_view->createModelNode("QtQuick3D.Texture",
metaInfo.majorVersion(),
metaInfo.minorVersion());
- newTexNode.validId();
+
+ newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName()));
+
VariantProperty sourceProp = newTexNode.variantProperty("source");
sourceProp.setValue(textureSource);
matLib.defaultNodeListProperty().reparentHere(newTexNode);
@@ -130,4 +135,10 @@ ModelNode CreateTexture::resolveSceneEnv(int sceneId)
return activeSceneEnv;
}
+void CreateTextures::execute(const QStringList &filePaths, AddTextureMode mode, int sceneId)
+{
+ for (const QString &path : filePaths)
+ CreateTexture::execute(path, mode, sceneId);
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/createtexture.h b/src/plugins/qmldesigner/components/createtexture.h
index d02eacc1ee..326696c67d 100644
--- a/src/plugins/qmldesigner/components/createtexture.h
+++ b/src/plugins/qmldesigner/components/createtexture.h
@@ -3,40 +3,42 @@
#pragma once
-#include <modelnode.h>
+#include <QObject>
+
+namespace Utils {
+class FilePath;
+}
namespace QmlDesigner {
class AbstractView;
+class ModelNode;
enum class AddTextureMode { Image, Texture, LightProbe };
-class CreateTexture
+class CreateTexture : public QObject
{
+ Q_OBJECT
+
public:
- CreateTexture(AbstractView *view, bool importFiles = false);
- void execute(const QString &filePath, AddTextureMode mode, int sceneId);
+ CreateTexture(AbstractView *view);
+
+ ModelNode execute(const QString &filePath, AddTextureMode mode = AddTextureMode::Texture, int sceneId = -1);
ModelNode resolveSceneEnv(int sceneId);
void assignTextureAsLightProbe(const ModelNode &texture, int sceneId);
private:
bool addFileToProject(const QString &filePath);
- ModelNode createTextureFromImage(const QString &assetPath, AddTextureMode mode);
+ ModelNode createTextureFromImage(const Utils::FilePath &assetPath, AddTextureMode mode);
-private:
AbstractView *m_view = nullptr;
- bool m_importFile = false;
};
class CreateTextures : public CreateTexture
{
public:
using CreateTexture::CreateTexture;
- void execute(const QStringList &filePaths, AddTextureMode mode, int sceneId)
- {
- for (const QString &path : filePaths)
- CreateTexture::execute(path, mode, sceneId);
- }
+ void execute(const QStringList &filePaths, AddTextureMode mode, int sceneId = -1);
};
-}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
index 54b7c14cc7..d0452fd6e2 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
@@ -8,6 +8,8 @@
#include "detail/graphicsview.h"
#include "detail/treeview.h"
+#include <utils/fileutils.h>
+
#include <QDoubleSpinBox>
#include <QLabel>
#include <QScrollArea>
@@ -29,6 +31,10 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
"To create an animation, add a timeline by clicking the + button in the \"Timeline\" view."
);
m_infoText = new QLabel(labelText);
+ setContentsMargins(0, 0, 0, 0);
+
+ m_toolbar->setStyleSheet(Theme::replaceCssColors(
+ QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
auto *splitter = new QSplitter;
splitter->addWidget(m_tree);
@@ -38,10 +44,12 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
QScrollArea* area = new QScrollArea;
area->setWidget(splitter);
area->setWidgetResizable(true);
+ area->setContentsMargins(0, 0, 0, 0);
m_statusLine = new QLabel();
auto *box = new QVBoxLayout;
+ box->setContentsMargins(0, 0, 0, 0);
box->addWidget(m_infoText);
box->addWidget(m_toolbar);
box->addWidget(area);
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorconstants.h b/src/plugins/qmldesigner/components/curveeditor/curveeditorconstants.h
new file mode 100644
index 0000000000..23cb29b8e1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorconstants.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace QmlDesigner {
+namespace CurveEditorConstants {
+
+constexpr char C_QMLCURVEEDITOR[] = "QmlDesigner::CurveEditor";
+
+constexpr char C_ZOOM_IN[] = "QmlDesigner.ZoomIn";
+constexpr char C_ZOOM_OUT[] = "QmlDesigner.ZoomOut";
+} // CurveEditorConstants
+} // QmlDesigner
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
index 5f3d49a879..27a14968c6 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
@@ -2,8 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "curveeditortoolbar.h"
+#include "curveeditorconstants.h"
#include "curveeditormodel.h"
+#include "coreplugin/actionmanager/actionmanager.h"
+#include "coreplugin/icontext.h"
+#include "theme.h"
+
+#include <utils/id.h>
+#include <utils/stylehelper.h>
+
#include <QAction>
#include <QHBoxLayout>
#include <QLabel>
@@ -14,7 +22,11 @@ namespace QmlDesigner {
ValidatableSpinBox::ValidatableSpinBox(std::function<bool(int)> validator, QWidget* parent)
: QSpinBox(parent)
, m_validator(validator)
-{ }
+{
+ setButtonSymbols(NoButtons);
+ setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ setFrame(false);
+}
QValidator::State ValidatableSpinBox::validate(QString &text, int &pos) const
{
@@ -28,6 +40,20 @@ QValidator::State ValidatableSpinBox::validate(QString &text, int &pos) const
return result;
}
+static QAction *createAction(const Utils::Id &id,
+ const QIcon &icon,
+ const QString &name,
+ const QKeySequence &shortcut)
+{
+ Core::Context context(CurveEditorConstants::C_QMLCURVEEDITOR);
+
+ auto *action = new QAction(icon, name);
+ auto *command = Core::ActionManager::registerAction(action, id, context);
+ command->setDefaultKeySequence(shortcut);
+ command->augmentActionWithShortcutToolTip(action);
+
+ return action;
+}
CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent)
: QToolBar(parent)
@@ -37,15 +63,16 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent)
{
setFloatable(false);
+ setFixedHeight(Theme::toolbarSize());
+ setContentsMargins(0, 0, 0, 0);
+
+ addSpace(5);
- QAction *tangentLinearAction = addAction(
- QIcon(":/curveeditor/images/tangetToolsLinearIcon.png"), "Linear");
- QAction *tangentStepAction = addAction(
- QIcon(":/curveeditor/images/tangetToolsStepIcon.png"), "Step");
- QAction *tangentSplineAction = addAction(
- QIcon(":/curveeditor/images/tangetToolsSplineIcon.png"), "Spline");
+ QAction *tangentLinearAction = addAction(Theme::iconFromName(Theme::linear_medium), "Linear");
+ QAction *tangentStepAction = addAction(Theme::iconFromName(Theme::step_medium), "Step");
+ QAction *tangentSplineAction = addAction(Theme::iconFromName(Theme::bezier_medium), "Spline");
- QAction *tangentUnifyAction = addAction(tr("Unify"));
+ QAction *tangentUnifyAction = addAction(Theme::iconFromName(Theme::unify_medium), tr("Unify"));
auto setLinearInterpolation = [this]() {
emit interpolationClicked(Keyframe::Interpolation::Linear);
@@ -96,33 +123,70 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent)
m_currentSpin->setMinimum(0);
m_currentSpin->setMaximum(std::numeric_limits<int>::max());
m_currentSpin->setFixedWidth(70);
+ m_currentSpin->setButtonSymbols(QSpinBox::NoButtons);
+ m_currentSpin->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ m_currentSpin->setFrame(false);
connect(m_currentSpin, &QSpinBox::valueChanged, this, &CurveEditorToolBar::currentFrameChanged);
+ addSpacer();
+
auto *durationBox = new QHBoxLayout;
+ durationBox->setContentsMargins(0, 0, 0, 0);
durationBox->addWidget(new QLabel(tr("Start Frame")));
durationBox->addWidget(m_startSpin);
+ addSpace(durationBox);
durationBox->addWidget(new QLabel(tr("End Frame")));
durationBox->addWidget(m_endSpin);
auto *durationWidget = new QWidget;
durationWidget->setLayout(durationBox);
addWidget(durationWidget);
+ addSpace();
auto *positionBox = new QHBoxLayout;
+ positionBox->setContentsMargins(0, 0, 0, 0);
positionBox->addWidget(new QLabel(tr("Current Frame")));
positionBox->addWidget(m_currentSpin);
auto *positionWidget = new QWidget;
positionWidget->setLayout(positionBox);
addWidget(positionWidget);
+ addSpace();
m_zoomSlider = new QSlider(Qt::Horizontal);
m_zoomSlider->setRange(0, 100);
+ Utils::StyleHelper::setPanelWidget(m_zoomSlider);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_zoomSlider);
+ m_zoomSlider->setFixedWidth(120);
+
connect(m_zoomSlider, &QSlider::valueChanged, [this](int value) {
emit zoomChanged(static_cast<double>(value)/100.0f);
});
+
+ auto *zoomOut = createAction(CurveEditorConstants::C_ZOOM_OUT,
+ Theme::iconFromName(Theme::Icon::zoomOut_medium),
+ tr("Zoom Out"),
+ QKeySequence(QKeySequence::ZoomOut));
+
+ connect(zoomOut, &QAction::triggered, [this]() {
+ m_zoomSlider->setValue(m_zoomSlider->value() - m_zoomSlider->pageStep());
+ });
+
+ auto *zoomIn = createAction(CurveEditorConstants::C_ZOOM_IN,
+ Theme::iconFromName(Theme::Icon::zoomIn_medium),
+ tr("Zoom In"),
+ QKeySequence(QKeySequence::ZoomIn));
+
+ connect(zoomIn, &QAction::triggered, [this]() {
+ m_zoomSlider->setValue(m_zoomSlider->value() + m_zoomSlider->pageStep());
+ });
+
+ addAction(zoomOut);
addWidget(m_zoomSlider);
+ addAction(zoomIn);
+
+ addSpace(5);
}
void CurveEditorToolBar::setZoom(double zoom)
@@ -150,4 +214,27 @@ void CurveEditorToolBar::updateBoundsSilent(int start, int end)
m_endSpin->setValue(end);
}
+void CurveEditorToolBar::addSpacer()
+{
+ QWidget *spacerWidget = new QWidget(this);
+ spacerWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+ addWidget(spacerWidget);
+}
+
+void CurveEditorToolBar::addSpace(int spaceSize)
+{
+ QWidget *spacerWidget = new QWidget(this);
+ spacerWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ spacerWidget->setFixedSize(spaceSize, 1);
+ addWidget(spacerWidget);
+}
+
+void CurveEditorToolBar::addSpace(QLayout *layout, int spaceSize)
+{
+ QWidget *spacerWidget = new QWidget(layout->widget());
+ spacerWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ spacerWidget->setFixedSize(spaceSize, 1);
+ layout->addWidget(spacerWidget);
+}
+
} // End namespace QmlDesigner.
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h
index bb368364e6..c7bb6bc818 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.h
@@ -18,10 +18,13 @@ class CurveEditorModel;
class ValidatableSpinBox : public QSpinBox
{
Q_OBJECT
+
public:
ValidatableSpinBox(std::function<bool(int)> validator, QWidget* parent=nullptr);
+
protected:
QValidator::State validate(QString &text, int &pos) const override;
+
private:
std::function<bool(int)> m_validator;
};
@@ -54,6 +57,10 @@ public:
void updateBoundsSilent(int start, int end);
private:
+ void addSpacer();
+ void addSpace(int spaceSize = 32);
+ void addSpace(QLayout *layout, int spaceSize = 32);
+
ValidatableSpinBox *m_startSpin;
ValidatableSpinBox *m_endSpin;
QSpinBox *m_currentSpin;
diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp
index df5b081814..2888575e44 100644
--- a/src/plugins/qmldesigner/components/debugview/debugview.cpp
+++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp
@@ -283,7 +283,8 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel
if (selectedNode.metaInfo().isValid()) {
for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().classHierarchy())
- message << metaInfo.typeName() << lineBreak;
+ message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "."
+ << metaInfo.minorVersion() << lineBreak;
message << lineBreak;
message << selectedNode.metaInfo().typeName();
@@ -425,7 +426,11 @@ void DebugView::rewriterEndTransaction()
WidgetInfo DebugView::widgetInfo()
{
- return createWidgetInfo(m_debugViewWidget.data(), QStringLiteral("DebugView"), WidgetInfo::LeftPane, 0, tr("Debug View"));
+ return createWidgetInfo(m_debugViewWidget.data(),
+ QStringLiteral("DebugView"),
+ WidgetInfo::LeftPane,
+ 0,
+ tr("Debug View"));
}
bool DebugView::hasWidget() const
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp
index 39f6442095..61731c578f 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp
@@ -6,8 +6,9 @@
#include <viewmanager.h>
#include <nodeinstanceview.h>
-#include <qmldesignerplugin.h>
#include <nodemetainfo.h>
+#include <qmldesignerplugin.h>
+#include "seekerslider.h"
#include <utils/algorithm.h>
@@ -36,22 +37,27 @@ void Edit3DActionTemplate::actionTriggered(bool b)
m_action(m_selectionContext);
}
+Edit3DWidgetActionTemplate::Edit3DWidgetActionTemplate(QWidgetAction *widget)
+ : PureActionInterface(widget)
+{
+
+}
+
Edit3DAction::Edit3DAction(const QByteArray &menuId,
View3DActionType type,
const QString &description,
const QKeySequence &key,
bool checkable,
bool checked,
- const QIcon &iconOff,
- const QIcon &iconOn,
+ const QIcon &icon,
Edit3DView *view,
SelectionContextOperation selectionAction,
const QString &toolTip)
- : AbstractAction(new Edit3DActionTemplate(description, selectionAction, view, type))
- , m_menuId(menuId)
- , m_actionTemplate(qobject_cast<Edit3DActionTemplate *>(defaultAction()))
+ : Edit3DAction(menuId, type, view, new Edit3DActionTemplate(description,
+ selectionAction,
+ view,
+ type))
{
- view->registerEdit3DAction(this);
action()->setShortcut(key);
action()->setShortcutContext(Qt::WidgetWithChildrenShortcut);
action()->setCheckable(checkable);
@@ -61,22 +67,18 @@ Edit3DAction::Edit3DAction(const QByteArray &menuId,
if (!toolTip.isEmpty())
action()->setToolTip(toolTip);
- if (checkable) {
- QIcon onOffIcon;
- const auto onAvail = iconOn.availableSizes(); // Assume both icons have same sizes available
- for (const auto &size : onAvail) {
- onOffIcon.addPixmap(iconOn.pixmap(size), QIcon::Normal, QIcon::On);
- onOffIcon.addPixmap(iconOff.pixmap(size), QIcon::Normal, QIcon::Off);
- }
- action()->setIcon(onOffIcon);
- } else {
- action()->setIcon(iconOff);
- }
+ action()->setIcon(icon);
}
-Edit3DAction::~Edit3DAction()
+Edit3DAction::Edit3DAction(const QByteArray &menuId,
+ View3DActionType type,
+ Edit3DView *view,
+ PureActionInterface *pureInt)
+ : AbstractAction(pureInt)
+ , m_menuId(menuId)
+ , m_actionType(type)
{
- m_actionTemplate->m_view->unregisterEdit3DAction(this);
+ view->registerEdit3DAction(this);
}
QByteArray Edit3DAction::category() const
@@ -86,7 +88,7 @@ QByteArray Edit3DAction::category() const
View3DActionType Edit3DAction::actionType() const
{
- return m_actionTemplate->m_type;
+ return m_actionType;
}
bool Edit3DAction::isVisible([[maybe_unused]] const SelectionContext &selectionContext) const
@@ -105,11 +107,10 @@ Edit3DCameraAction::Edit3DCameraAction(const QByteArray &menuId,
const QKeySequence &key,
bool checkable,
bool checked,
- const QIcon &iconOff,
- const QIcon &iconOn,
+ const QIcon &icon,
Edit3DView *view,
SelectionContextOperation selectionAction)
- : Edit3DAction(menuId, type, description, key, checkable, checked, iconOff, iconOn, view, selectionAction)
+ : Edit3DAction(menuId, type, description, key, checkable, checked, icon, view, selectionAction)
{
}
@@ -119,5 +120,31 @@ bool Edit3DCameraAction::isEnabled(const SelectionContext &selectionContext) con
[](const ModelNode &node) { return node.metaInfo().isQtQuick3DCamera(); });
}
+Edit3DParticleSeekerAction::Edit3DParticleSeekerAction(const QByteArray &menuId,
+ View3DActionType type,
+ Edit3DView *view)
+ : Edit3DAction(menuId,
+ type,
+ view,
+ new Edit3DWidgetActionTemplate(
+ new SeekerSliderAction(nullptr)))
+{
+ m_seeker = qobject_cast<SeekerSliderAction *>(action());
+}
+
+SeekerSliderAction *Edit3DParticleSeekerAction::seekerAction()
+{
+ return m_seeker;
+}
+
+bool Edit3DParticleSeekerAction::isVisible(const SelectionContext &) const
+{
+ return m_seeker->isVisible();
+}
+
+bool Edit3DParticleSeekerAction::isEnabled(const SelectionContext &) const
+{
+ return m_seeker->isEnabled();
}
+}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h
index 5b87563c30..bad427a25b 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h
@@ -5,12 +5,15 @@
#include <abstractaction.h>
#include <QAction>
+#include <QWidgetAction>
#include <QIcon>
+class QWidgetAction;
namespace QmlDesigner {
using SelectionContextOperation = std::function<void(const SelectionContext &)>;
class Edit3DView;
+class SeekerSliderAction;
class Edit3DActionTemplate : public DefaultAction
{
@@ -29,6 +32,15 @@ public:
View3DActionType m_type;
};
+class Edit3DWidgetActionTemplate : public PureActionInterface
+{
+ Q_DISABLE_COPY(Edit3DWidgetActionTemplate)
+
+public:
+ explicit Edit3DWidgetActionTemplate(QWidgetAction *widget);
+ virtual void setSelectionContext(const SelectionContext &) {}
+};
+
class Edit3DAction : public AbstractAction
{
public:
@@ -38,13 +50,15 @@ public:
const QKeySequence &key,
bool checkable,
bool checked,
- const QIcon &iconOff,
- const QIcon &iconOn,
+ const QIcon &icon,
Edit3DView *view,
SelectionContextOperation selectionAction = nullptr,
const QString &toolTip = {});
- virtual ~Edit3DAction();
+ Edit3DAction(const QByteArray &menuId,
+ View3DActionType type,
+ Edit3DView *view,
+ PureActionInterface *pureInt);
QByteArray category() const override;
@@ -71,7 +85,7 @@ protected:
private:
QByteArray m_menuId;
- Edit3DActionTemplate *m_actionTemplate = nullptr;
+ View3DActionType m_actionType;
};
class Edit3DCameraAction : public Edit3DAction
@@ -83,8 +97,7 @@ public:
const QKeySequence &key,
bool checkable,
bool checked,
- const QIcon &iconOff,
- const QIcon &iconOn,
+ const QIcon &icon,
Edit3DView *view,
SelectionContextOperation selectionAction = nullptr);
@@ -92,4 +105,21 @@ protected:
bool isEnabled(const SelectionContext &selectionContext) const override;
};
+class Edit3DParticleSeekerAction : public Edit3DAction
+{
+public:
+ Edit3DParticleSeekerAction(const QByteArray &menuId,
+ View3DActionType type,
+ Edit3DView *view);
+
+ SeekerSliderAction *seekerAction();
+
+protected:
+ bool isVisible(const SelectionContext &) const override;
+ bool isEnabled(const SelectionContext &) const override;
+
+private:
+ SeekerSliderAction *m_seeker = nullptr;
+};
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
index cb03b5906c..60cba02584 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
@@ -35,6 +35,8 @@ static QQuickWidget *createBusyIndicator(QWidget *p)
widget->setAttribute(Qt::WA_AlwaysStackOnTop);
widget->setClearColor(Qt::transparent);
widget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ widget->setObjectName(Constants::OBJECT_NAME_BUSY_INDICATOR);
+
return widget;
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index afd4558f5c..d92e76362b 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -4,17 +4,19 @@
#include "edit3dview.h"
#include "backgroundcolorselection.h"
+#include "designeractionmanager.h"
+#include "designericons.h"
#include "designersettings.h"
#include "designmodecontext.h"
#include "edit3dactions.h"
#include "edit3dcanvas.h"
#include "edit3dviewconfig.h"
#include "edit3dwidget.h"
+#include "materialutils.h"
#include "metainfo.h"
#include "nodehints.h"
#include "nodeinstanceview.h"
#include "qmldesignerconstants.h"
-#include "qmldesignericons.h"
#include "qmldesignerplugin.h"
#include "qmlvisualnode.h"
#include "seekerslider.h"
@@ -22,14 +24,39 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
+#include <theme.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QToolButton>
namespace QmlDesigner {
+static inline QIcon contextIcon (const DesignerIcons::IconId &iconId) {
+ return DesignerActionManager::instance().contextIcon(iconId);
+};
+
+static QIcon toolbarIcon (const Theme::Icon &iconOffId, const Theme::Icon &iconnOnId) {
+ QIcon iconOff = Theme::iconFromName(iconOffId);
+ QIcon iconOn= Theme::iconFromName(iconnOnId,
+ Theme::getColor(Theme::Color::DStextSelectedTextColor));
+ QIcon retIcon;
+
+ const auto onAvail = iconOff.availableSizes(); // Assume both icons have same sizes available
+ for (const auto &size : onAvail) {
+ retIcon.addPixmap(iconOff.pixmap(size), QIcon::Normal, QIcon::Off);
+ retIcon.addPixmap(iconOn.pixmap(size), QIcon::Normal, QIcon::On);
+ }
+
+ return retIcon;
+};
+
+static inline QIcon toolbarIcon (const Theme::Icon &iconOffId) {
+ return toolbarIcon(iconOffId, iconOffId);
+};
+
Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}
{
@@ -38,11 +65,6 @@ Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies)
connect(&m_compressionTimer, &QTimer::timeout, this, &Edit3DView::handleEntriesChanged);
}
-Edit3DView::~Edit3DView()
-{
- qDeleteAll(m_edit3DActions);
-}
-
void Edit3DView::createEdit3DWidget()
{
createEdit3DActions();
@@ -62,7 +84,13 @@ WidgetInfo Edit3DView::widgetInfo()
if (!m_edit3DWidget)
createEdit3DWidget();
- return createWidgetInfo(m_edit3DWidget.data(), "Editor3D", WidgetInfo::CentralPane, 0, tr("3D"), DesignerWidgetFlags::IgnoreErrors);
+ return createWidgetInfo(m_edit3DWidget.data(),
+ "Editor3D",
+ WidgetInfo::CentralPane,
+ 0,
+ tr("3D"),
+ tr("3D view"),
+ DesignerWidgetFlags::IgnoreErrors);
}
Edit3DWidget *Edit3DView::edit3DWidget() const
@@ -202,27 +230,8 @@ void Edit3DView::onEntriesChanged()
void Edit3DView::registerEdit3DAction(Edit3DAction *action)
{
- View3DActionType actionType = action->actionType();
- if (actionType == View3DActionType::Empty)
- return;
-
- if (m_edit3DActions.contains(actionType)) {
- Edit3DAction *formerAction = m_edit3DActions.value(actionType);
- if (formerAction == action)
- return;
-
- qWarning() << Q_FUNC_INFO << __LINE__ << "Reregistering action for" << int(actionType);
- delete formerAction;
- }
-
- m_edit3DActions.insert(actionType, action);
-}
-
-void Edit3DView::unregisterEdit3DAction(Edit3DAction *action)
-{
- View3DActionType actionType = action->actionType();
- if (m_edit3DActions.value(actionType, nullptr) == action)
- m_edit3DActions.remove(actionType);
+ if (action->actionType() != View3DActionType::Empty)
+ m_edit3DActions.insert(action->actionType(), QSharedPointer<Edit3DAction>(action));
}
void Edit3DView::handleEntriesChanged()
@@ -230,37 +239,42 @@ void Edit3DView::handleEntriesChanged()
if (!model())
return;
- const QString cameras = tr("Cameras");
- const QString lights = tr("Lights");
- const QString primitives = tr("Primitives");
- const QString importedModels = tr("Imported Models");
- const QStringList keys {cameras, lights, primitives, importedModels}; // used to maintain order
-
- QHash<QString, QList<ItemLibraryEntry>> entriesMap {
- {cameras, {}},
- {lights, {}},
- {primitives, {}},
- {importedModels, {}}
+ enum ItemLibraryEntryKeys : int { // used to maintain order
+ EK_cameras,
+ EK_lights,
+ EK_primitives,
+ EK_importedModels
+ };
+
+ QMap<ItemLibraryEntryKeys, ItemLibraryDetails> entriesMap {
+ {EK_cameras, {tr("Cameras"), contextIcon(DesignerIcons::CameraIcon)}},
+ {EK_lights, {tr("Lights"), contextIcon(DesignerIcons::LightIcon)}},
+ {EK_primitives, {tr("Primitives"), contextIcon(DesignerIcons::PrimitivesIcon)}},
+ {EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}
};
const QList<ItemLibraryEntry> itemLibEntries = model()->metaInfo().itemLibraryInfo()->entries();
for (const ItemLibraryEntry &entry : itemLibEntries) {
+ ItemLibraryEntryKeys entryKey;
if (entry.typeName() == "QtQuick3D.Model" && entry.name() != "Empty") {
- entriesMap[primitives].append(entry);
+ entryKey = EK_primitives;
} else if (entry.typeName() == "QtQuick3D.DirectionalLight"
|| entry.typeName() == "QtQuick3D.PointLight"
|| entry.typeName() == "QtQuick3D.SpotLight") {
- entriesMap[lights].append(entry);
+ entryKey = EK_lights;
} else if (entry.typeName() == "QtQuick3D.OrthographicCamera"
|| entry.typeName() == "QtQuick3D.PerspectiveCamera") {
- entriesMap[cameras].append(entry);
+ entryKey = EK_cameras;
} else if (entry.typeName().startsWith("Quick3DAssets.")
&& NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) {
- entriesMap[importedModels].append(entry);
+ entryKey = EK_importedModels;
+ } else {
+ continue;
}
+ entriesMap[entryKey].entryList.append(entry);
}
- m_edit3DWidget->updateCreateSubMenu(keys, entriesMap);
+ m_edit3DWidget->updateCreateSubMenu(entriesMap.values());
}
void Edit3DView::modelAboutToBeDetached(Model *model)
@@ -311,24 +325,29 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
createdNode = QmlVisualNode::createQml3DNode(
this, m_droppedEntry, edit3DWidget()->canvas()->activeScene(), pos3d).modelNode();
if (createdNode.metaInfo().isQtQuick3DModel())
- assignMaterialTo3dModel(createdNode);
+ MaterialUtils::assignMaterialTo3dModel(this, createdNode);
});
if (createdNode.isValid())
setSelectedModelNode(createdNode);
} else if (m_nodeAtPosReqType == NodeAtPosReqType::MaterialDrop) {
bool isModel = modelNode.metaInfo().isQtQuick3DModel();
- if (m_droppedModelNode.isValid() && modelNode.isValid() && isModel) {
+ if (m_droppedModelNode.isValid() && isModel) {
executeInTransaction(__FUNCTION__, [&] {
- assignMaterialTo3dModel(modelNode, m_droppedModelNode);
+ MaterialUtils::assignMaterialTo3dModel(this, modelNode, m_droppedModelNode);
});
}
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) {
emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView
} else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) {
emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode});
+ } else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) {
+ bool isModel = modelNode.metaInfo().isQtQuick3DModel();
+ if (!m_droppedFile.isEmpty() && isModel)
+ emitCustomNotification("apply_asset_to_model3D", {modelNode}, {m_droppedFile}); // To MaterialBrowserView
}
m_droppedModelNode = {};
+ m_droppedFile.clear();
m_nodeAtPosReqType = NodeAtPosReqType::None;
}
@@ -352,11 +371,6 @@ QSize Edit3DView::canvasSize() const
return {};
}
-void Edit3DView::setSeeker(SeekerSlider *slider)
-{
- m_seeker = slider;
-}
-
Edit3DAction *Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgroundColorAction)
{
QString description = QCoreApplication::translate("SelectBackgroundColorAction",
@@ -385,7 +399,6 @@ Edit3DAction *Edit3DView::createSelectBackgroundColorAction(QAction *syncBackgro
false,
false,
{},
- {},
this,
operation,
tooltip);
@@ -412,7 +425,6 @@ Edit3DAction *Edit3DView::createGridColorSelectionAction()
false,
false,
{},
- {},
this,
operation,
tooltip);
@@ -447,7 +459,6 @@ Edit3DAction *Edit3DView::createResetColorAction(QAction *syncBackgroundColorAct
false,
false,
{},
- {},
this,
operation,
tooltip);
@@ -468,24 +479,41 @@ Edit3DAction *Edit3DView::createSyncBackgroundColorAction()
true,
false,
{},
- {},
this,
{},
tooltip);
}
+Edit3DAction *Edit3DView::createSeekerSliderAction()
+{
+ Edit3DParticleSeekerAction *seekerAction = new Edit3DParticleSeekerAction(
+ QmlDesigner::Constants::EDIT3D_PARTICLES_SEEKER,
+ View3DActionType::ParticlesSeek,
+ this);
+
+ seekerAction->action()->setEnabled(false);
+ seekerAction->action()->setToolTip(QLatin1String("Seek particle system time when paused."));
+
+ connect(seekerAction->seekerAction(),
+ &SeekerSliderAction::valueChanged,
+ this, [=] (int value) {
+ this->emitView3DAction(View3DActionType::ParticlesSeek, value);
+ });
+
+ return seekerAction;
+}
+
void Edit3DView::createEdit3DActions()
{
m_selectionModeAction = new Edit3DAction(
- QmlDesigner::Constants::EDIT3D_SELECTION_MODE,
- View3DActionType::SelectionModeToggle,
- QCoreApplication::translate("SelectionModeToggleAction", "Toggle Group/Single Selection Mode"),
- QKeySequence(Qt::Key_Q),
- true,
- false,
- Icons::EDIT3D_SELECTION_MODE_OFF.icon(),
- Icons::EDIT3D_SELECTION_MODE_ON.icon(),
- this);
+ QmlDesigner::Constants::EDIT3D_SELECTION_MODE,
+ View3DActionType::SelectionModeToggle,
+ QCoreApplication::translate("SelectionModeToggleAction", "Toggle Group/Single Selection Mode"),
+ QKeySequence(Qt::Key_Q),
+ true,
+ false,
+ toolbarIcon(Theme::selectOutline_medium, Theme::selectFill_medium),
+ this);
m_moveToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_MOVE_TOOL,
View3DActionType::MoveTool,
@@ -494,8 +522,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_W),
true,
true,
- Icons::EDIT3D_MOVE_TOOL_OFF.icon(),
- Icons::EDIT3D_MOVE_TOOL_ON.icon(),
+ toolbarIcon(Theme::move_medium),
this);
m_rotateToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_ROTATE_TOOL,
@@ -505,8 +532,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_E),
true,
false,
- Icons::EDIT3D_ROTATE_TOOL_OFF.icon(),
- Icons::EDIT3D_ROTATE_TOOL_ON.icon(),
+ toolbarIcon(Theme::roatate_medium),
this);
m_scaleToolAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_SCALE_TOOL,
@@ -516,8 +542,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_R),
true,
false,
- Icons::EDIT3D_SCALE_TOOL_OFF.icon(),
- Icons::EDIT3D_SCALE_TOOL_ON.icon(),
+ toolbarIcon(Theme::scale_medium),
this);
m_fitAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_FIT_SELECTED,
@@ -527,8 +552,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_F),
false,
false,
- Icons::EDIT3D_FIT_SELECTED_OFF.icon(),
- {},
+ toolbarIcon(Theme::fitToView_medium),
this);
m_alignCamerasAction = new Edit3DCameraAction(
@@ -538,8 +562,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(),
false,
false,
- Icons::EDIT3D_ALIGN_CAMERA_ON.icon(),
- {},
+ toolbarIcon(Theme::alignToCam_medium),
this);
m_alignViewAction = new Edit3DCameraAction(
@@ -549,8 +572,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(),
false,
false,
- Icons::EDIT3D_ALIGN_VIEW_ON.icon(),
- {},
+ toolbarIcon(Theme::alignToView_medium),
this);
m_cameraModeAction = new Edit3DAction(
@@ -561,8 +583,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_T),
true,
false,
- Icons::EDIT3D_EDIT_CAMERA_OFF.icon(),
- Icons::EDIT3D_EDIT_CAMERA_ON.icon(),
+ toolbarIcon(Theme::orthCam_small, Theme::perspectiveCam_small),
this);
m_orientationModeAction = new Edit3DAction(
@@ -572,20 +593,19 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_Y),
true,
false,
- Icons::EDIT3D_ORIENTATION_OFF.icon(),
- Icons::EDIT3D_ORIENTATION_ON.icon(),
+ toolbarIcon(Theme::localOrient_medium),
this);
- m_editLightAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_EDIT_LIGHT,
- View3DActionType::EditLightToggle,
- QCoreApplication::translate("EditLightToggleAction",
- "Toggle Edit Light On/Off"),
- QKeySequence(Qt::Key_U),
- true,
- false,
- Icons::EDIT3D_LIGHT_OFF.icon(),
- Icons::EDIT3D_LIGHT_ON.icon(),
- this);
+ m_editLightAction = new Edit3DAction(
+ QmlDesigner::Constants::EDIT3D_EDIT_LIGHT,
+ View3DActionType::EditLightToggle,
+ QCoreApplication::translate("EditLightToggleAction",
+ "Toggle Edit Light On/Off"),
+ QKeySequence(Qt::Key_U),
+ true,
+ false,
+ toolbarIcon(Theme::editLightOff_medium, Theme::editLightOn_medium),
+ this);
m_showGridAction = new Edit3DAction(
QmlDesigner::Constants::EDIT3D_EDIT_SHOW_GRID,
@@ -595,7 +615,6 @@ void Edit3DView::createEdit3DActions()
true,
true,
{},
- {},
this,
nullptr,
QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid."));
@@ -608,7 +627,6 @@ void Edit3DView::createEdit3DActions()
true,
true,
{},
- {},
this,
nullptr,
QCoreApplication::translate("ShowSelectionBoxAction",
@@ -622,7 +640,6 @@ void Edit3DView::createEdit3DActions()
true,
true,
{},
- {},
this,
nullptr,
QCoreApplication::translate(
@@ -637,7 +654,6 @@ void Edit3DView::createEdit3DActions()
true,
false,
{},
- {},
this,
nullptr,
QCoreApplication::translate(
@@ -654,7 +670,6 @@ void Edit3DView::createEdit3DActions()
true,
false,
{},
- {},
this,
nullptr,
QCoreApplication::translate(
@@ -667,8 +682,8 @@ void Edit3DView::createEdit3DActions()
m_particlesRestartAction->action()->setEnabled(particlemode);
if (particlemode)
m_particlesPlayAction->action()->setChecked(true);
- if (m_seeker)
- m_seeker->setEnabled(false);
+ if (m_seekerAction)
+ m_seekerAction->action()->setEnabled(false);
resetPuppet();
};
@@ -676,62 +691,60 @@ void Edit3DView::createEdit3DActions()
particlemode = !particlemode;
m_particlesPlayAction->action()->setEnabled(particlemode);
m_particlesRestartAction->action()->setEnabled(particlemode);
- if (m_seeker)
- m_seeker->setEnabled(false);
+ if (m_seekerAction)
+ m_seekerAction->action()->setEnabled(false);
QmlDesignerPlugin::settings().insert("particleMode", particlemode);
resetPuppet();
};
SelectionContextOperation particlesPlayTrigger = [this](const SelectionContext &) {
- if (m_seeker)
- m_seeker->setEnabled(!m_particlesPlayAction->action()->isChecked());
+ if (m_seekerAction)
+ m_seekerAction->action()->setEnabled(!m_particlesPlayAction->action()->isChecked());
};
m_particleViewModeAction = new Edit3DAction(
- QmlDesigner::Constants::EDIT3D_PARTICLE_MODE,
- View3DActionType::Edit3DParticleModeToggle,
- QCoreApplication::translate("ParticleViewModeAction", "Toggle particle animation On/Off"),
- QKeySequence(Qt::Key_V),
- true,
- false,
- Icons::EDIT3D_PARTICLE_OFF.icon(),
- Icons::EDIT3D_PARTICLE_ON.icon(),
- this,
- particlesTrigger);
+ QmlDesigner::Constants::EDIT3D_PARTICLE_MODE,
+ View3DActionType::Edit3DParticleModeToggle,
+ QCoreApplication::translate("ParticleViewModeAction", "Toggle particle animation On/Off"),
+ QKeySequence(Qt::Key_V),
+ true,
+ false,
+ toolbarIcon(Theme::particleAnimation_medium),
+ this,
+ particlesTrigger);
particlemode = false;
- m_particlesPlayAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_PARTICLES_PLAY,
- View3DActionType::ParticlesPlay,
- QCoreApplication::translate("ParticlesPlayAction",
- "Play Particles"),
- QKeySequence(Qt::Key_Comma),
- true,
- true,
- Icons::EDIT3D_PARTICLE_PLAY.icon(),
- Icons::EDIT3D_PARTICLE_PAUSE.icon(),
- this,
- particlesPlayTrigger);
+ m_particlesPlayAction = new Edit3DAction(
+ QmlDesigner::Constants::EDIT3D_PARTICLES_PLAY,
+ View3DActionType::ParticlesPlay,
+ QCoreApplication::translate("ParticlesPlayAction", "Play Particles"),
+ QKeySequence(Qt::Key_Comma),
+ true,
+ true,
+ toolbarIcon(Theme::playOutline_medium, Theme::pause), // Icons::EDIT3D_PARTICLE_PLAY.icon(), Icons::EDIT3D_PARTICLE_PAUSE.icon(),
+ this,
+ particlesPlayTrigger);
m_particlesRestartAction = new Edit3DAction(
- QmlDesigner::Constants::EDIT3D_PARTICLES_RESTART,
- View3DActionType::ParticlesRestart,
- QCoreApplication::translate("ParticlesRestartAction", "Restart Particles"),
- QKeySequence(Qt::Key_Slash),
- false,
- false,
- Icons::EDIT3D_PARTICLE_RESTART.icon(),
- Icons::EDIT3D_PARTICLE_RESTART.icon(),
- this);
+ QmlDesigner::Constants::EDIT3D_PARTICLES_RESTART,
+ View3DActionType::ParticlesRestart,
+ QCoreApplication::translate("ParticlesRestartAction", "Restart Particles"),
+ QKeySequence(Qt::Key_Slash),
+ false,
+ false,
+ toolbarIcon(Theme::restartParticles_medium),
+ this);
m_particlesPlayAction->action()->setEnabled(particlemode);
m_particlesRestartAction->action()->setEnabled(particlemode);
- m_resetAction = new Edit3DAction(QmlDesigner::Constants::EDIT3D_RESET_VIEW,
- View3DActionType::Empty,
- QCoreApplication::translate("ResetView", "Reset View"),
- QKeySequence(Qt::Key_P),
- false,
- false,
- Utils::Icons::RESET_TOOLBAR.icon(),
- {},
- this,
- resetTrigger);
+
+ m_resetAction = new Edit3DAction(
+ QmlDesigner::Constants::EDIT3D_RESET_VIEW,
+ View3DActionType::Empty,
+ QCoreApplication::translate("ResetView", "Reset View"),
+ QKeySequence(Qt::Key_P),
+ false,
+ false,
+ toolbarIcon(Theme::reload_medium),
+ this,
+ resetTrigger);
SelectionContextOperation visibilityTogglesTrigger = [this](const SelectionContext &) {
if (!edit3DWidget()->visibilityTogglesMenu())
@@ -757,8 +770,7 @@ void Edit3DView::createEdit3DActions()
QKeySequence(),
false,
false,
- Utils::Icons::EYE_OPEN_TOOLBAR.icon(),
- {},
+ toolbarIcon(Theme::invisible_medium, Theme::visible_medium),
this,
visibilityTogglesTrigger);
@@ -786,11 +798,12 @@ void Edit3DView::createEdit3DActions()
QKeySequence(),
false,
false,
- Icons::COLOR_PALETTE.icon(),
- {},
+ toolbarIcon(Theme::colorSelection_medium),
this,
backgroundColorActionsTrigger);
+ m_seekerAction = createSeekerSliderAction();
+
m_leftActions << m_selectionModeAction;
m_leftActions << nullptr; // Null indicates separator
m_leftActions << nullptr; // Second null after separator indicates an exclusive group
@@ -815,6 +828,8 @@ void Edit3DView::createEdit3DActions()
m_rightActions << m_particlesPlayAction;
m_rightActions << m_particlesRestartAction;
m_rightActions << nullptr;
+ m_rightActions << m_seekerAction;
+ m_rightActions << nullptr;
m_rightActions << m_resetAction;
m_visibilityToggleActions << m_showGridAction;
@@ -852,7 +867,7 @@ QVector<Edit3DAction *> Edit3DView::backgroundColorActions() const
Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const
{
- return m_edit3DActions.value(type, nullptr);
+ return m_edit3DActions.value(type, nullptr).data();
}
void Edit3DView::addQuick3DImport()
@@ -910,6 +925,17 @@ void Edit3DView::dropComponent(const ItemLibraryEntry &entry, const QPointF &pos
{
m_nodeAtPosReqType = NodeAtPosReqType::ComponentDrop;
m_droppedEntry = entry;
+ NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName());
+ if (metaInfo.isQtQuick3DNode())
+ emitView3DAction(View3DActionType::GetNodeAtPos, pos);
+ else
+ nodeAtPosReady({}, {}); // No need to actually resolve position for non-node items
+}
+
+void Edit3DView::dropAsset(const QString &file, const QPointF &pos)
+{
+ m_nodeAtPosReqType = NodeAtPosReqType::AssetDrop;
+ m_droppedFile = file;
emitView3DAction(View3DActionType::GetNodeAtPos, pos);
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index 03dc13b4ac..2b746cf1af 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -25,7 +25,6 @@ namespace QmlDesigner {
class Edit3DWidget;
class Edit3DAction;
class Edit3DCameraAction;
-class SeekerSlider;
class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView
{
@@ -33,7 +32,6 @@ class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView
public:
Edit3DView(ExternalDependenciesInterface &externalDependencies);
- ~Edit3DView() override;
WidgetInfo widgetInfo() override;
@@ -59,7 +57,6 @@ public:
QVector<Edit3DAction *> visibilityToggleActions() const;
QVector<Edit3DAction *> backgroundColorActions() const;
Edit3DAction *edit3DAction(View3DActionType type) const;
- void setSeeker(SeekerSlider *slider);
void addQuick3DImport();
void startContextMenu(const QPoint &pos);
@@ -67,6 +64,7 @@ public:
void dropBundleMaterial(const QPointF &pos);
void dropTexture(const ModelNode &textureNode, const QPointF &pos);
void dropComponent(const ItemLibraryEntry &entry, const QPointF &pos);
+ void dropAsset(const QString &file, const QPointF &pos);
private slots:
void onEntriesChanged();
@@ -78,11 +76,11 @@ private:
MaterialDrop,
TextureDrop,
ContextMenu,
+ AssetDrop,
None
};
void registerEdit3DAction(Edit3DAction *action);
- void unregisterEdit3DAction(Edit3DAction *action);
void createEdit3DWidget();
void checkImports();
@@ -93,13 +91,15 @@ private:
Edit3DAction *createGridColorSelectionAction();
Edit3DAction *createResetColorAction(QAction *syncBackgroundColorAction);
Edit3DAction *createSyncBackgroundColorAction();
+ Edit3DAction *createSeekerSliderAction();
QPointer<Edit3DWidget> m_edit3DWidget;
QVector<Edit3DAction *> m_leftActions;
QVector<Edit3DAction *> m_rightActions;
QVector<Edit3DAction *> m_visibilityToggleActions;
QVector<Edit3DAction *> m_backgroundColorActions;
- QMap<View3DActionType, Edit3DAction *> m_edit3DActions;
+
+ QMap<View3DActionType, QSharedPointer<Edit3DAction>> m_edit3DActions;
Edit3DAction *m_selectionModeAction = nullptr;
Edit3DAction *m_moveToolAction = nullptr;
Edit3DAction *m_rotateToolAction = nullptr;
@@ -121,11 +121,12 @@ private:
Edit3DAction *m_particlesRestartAction = nullptr;
Edit3DAction *m_visibilityTogglesAction = nullptr;
Edit3DAction *m_backgrondColorMenuAction = nullptr;
- SeekerSlider *m_seeker = nullptr;
+ Edit3DAction *m_seekerAction = nullptr;
int particlemode;
ModelCache<QImage> m_canvasCache;
ModelNode m_droppedModelNode;
ItemLibraryEntry m_droppedEntry;
+ QString m_droppedFile;
NodeAtPosReqType m_nodeAtPosReqType;
QPoint m_contextMenuPos;
QTimer m_compressionTimer;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 701237dd71..408584e166 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -3,16 +3,19 @@
#include "edit3dwidget.h"
#include "designdocument.h"
+#include "designericons.h"
#include "edit3dactions.h"
#include "edit3dcanvas.h"
#include "edit3dview.h"
#include "edit3dvisibilitytogglesmenu.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"
@@ -26,6 +29,7 @@
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
#include <toolbox.h>
+#include <utils/asset.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -37,11 +41,41 @@
namespace QmlDesigner {
+static inline QIcon contextIcon(const DesignerIcons::IconId &iconId) {
+ return DesignerActionManager::instance().contextIcon(iconId);
+};
+
+static QIcon getEntryIcon(const ItemLibraryEntry &entry)
+{
+ static const QMap<QString, DesignerIcons::IconId> itemLibraryDesignerIconId = {
+ {"QtQuick3D.OrthographicCamera__Camera Orthographic", DesignerIcons::CameraOrthographicIcon},
+ {"QtQuick3D.PerspectiveCamera__Camera Perspective", DesignerIcons::CameraPerspectiveIcon},
+ {"QtQuick3D.DirectionalLight__Light Directional", DesignerIcons::LightDirectionalIcon},
+ {"QtQuick3D.PointLight__Light Point", DesignerIcons::LightPointIcon},
+ {"QtQuick3D.SpotLight__Light Spot", DesignerIcons::LightSpotIcon},
+ {"QtQuick3D.Model__Cone", DesignerIcons::ModelConeIcon},
+ {"QtQuick3D.Model__Cube", DesignerIcons::ModelCubeIcon},
+ {"QtQuick3D.Model__Cylinder", DesignerIcons::ModelCylinderIcon},
+ {"QtQuick3D.Model__Plane", DesignerIcons::ModelPlaneIcon},
+ {"QtQuick3D.Model__Sphere", DesignerIcons::ModelSphereIcon},
+ };
+
+ QString entryKey = entry.typeName() + "__" + entry.name();
+ if (itemLibraryDesignerIconId.contains(entryKey)) {
+ return contextIcon(itemLibraryDesignerIconId.value(entryKey));
+ }
+ return QIcon(entry.libraryEntryIconPath());
+}
+
Edit3DWidget::Edit3DWidget(Edit3DView *view)
: m_view(view)
{
setAcceptDrops(true);
+ QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css");
+ sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css");
+ setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet)));
+
Core::Context context(Constants::C_QMLEDITOR3D);
m_context = new Core::IContext(this);
m_context->setContext(context);
@@ -55,11 +89,8 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view)
fillLayout->setSpacing(0);
setLayout(fillLayout);
- SeekerSlider *seeker = new SeekerSlider(this);
- seeker->setEnabled(false);
-
// Initialize toolbar
- m_toolBox = new ToolBox(seeker, this);
+ m_toolBox = new ToolBox(this);
fillLayout->addWidget(m_toolBox.data());
// Iterate through view actions. A null action indicates a separator and a second null action
@@ -136,13 +167,6 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view)
createContextMenu();
- view->setSeeker(seeker);
- seeker->setToolTip(QLatin1String("Seek particle system time when paused."));
-
- QObject::connect(seeker, &SeekerSlider::positionChanged, [seeker, view]() {
- view->emitView3DAction(View3DActionType::ParticlesSeek, seeker->position());
- });
-
// Onboarding label contains instructions for new users how to get 3D content into the project
m_onboardingLabel = new QLabel(this);
QString labelText =
@@ -172,13 +196,17 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view)
void Edit3DWidget::createContextMenu()
{
- m_contextMenu = new QMenu(this);
+ m_contextMenu = new QmlEditorMenu(this);
- m_editComponentAction = m_contextMenu->addAction(tr("Edit Component"), [&] {
+ m_editComponentAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::EditComponentIcon),
+ tr("Edit Component"), [&] {
DocumentManager::goIntoComponent(m_view->singleSelectedModelNode());
});
- m_editMaterialAction = m_contextMenu->addAction(tr("Edit Material"), [&] {
+ m_editMaterialAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::MaterialIcon),
+ tr("Edit Material"), [&] {
SelectionContext selCtx(m_view);
selCtx.setTargetNode(m_contextMenuTarget);
ModelNodeOperations::editMaterial(selCtx);
@@ -186,42 +214,58 @@ void Edit3DWidget::createContextMenu()
m_contextMenu->addSeparator();
- m_duplicateAction = m_contextMenu->addAction(tr("Duplicate"), [&] {
- QmlDesignerPlugin::instance()->currentDesignDocument()->duplicateSelected();
- });
-
- m_copyAction = m_contextMenu->addAction(tr("Copy"), [&] {
+ m_copyAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::CopyIcon),
+ tr("Copy"), [&] {
QmlDesignerPlugin::instance()->currentDesignDocument()->copySelected();
});
- m_pasteAction = m_contextMenu->addAction(tr("Paste"), [&] {
+ m_pasteAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::PasteIcon),
+ tr("Paste"), [&] {
QmlDesignerPlugin::instance()->currentDesignDocument()->pasteToPosition(m_contextMenuPos3d);
});
- m_deleteAction = m_contextMenu->addAction(tr("Delete"), [&] {
+ m_deleteAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::DeleteIcon),
+ tr("Delete"), [&] {
view()->executeInTransaction("Edit3DWidget::createContextMenu", [&] {
for (ModelNode &node : m_view->selectedModelNodes())
node.destroy();
});
});
+ m_duplicateAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::DuplicateIcon),
+ tr("Duplicate"), [&] {
+ QmlDesignerPlugin::instance()->currentDesignDocument()->duplicateSelected();
+ });
+
m_contextMenu->addSeparator();
- m_fitSelectedAction = m_contextMenu->addAction(tr("Fit Selected Items to View"), [&] {
+ m_fitSelectedAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::FitSelectedIcon),
+ tr("Fit Selected Items to View"), [&] {
view()->emitView3DAction(View3DActionType::FitToView, true);
});
- m_alignCameraAction = m_contextMenu->addAction(tr("Align Camera to View"), [&] {
+ m_alignCameraAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::AlignCameraToViewIcon),
+ tr("Align Camera to View"), [&] {
view()->emitView3DAction(View3DActionType::AlignCamerasToView, true);
});
- m_alignViewAction = m_contextMenu->addAction(tr("Align View to Camera"), [&] {
+ m_alignViewAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::AlignViewToCameraIcon),
+ tr("Align View to Camera"), [&] {
view()->emitView3DAction(View3DActionType::AlignViewToCamera, true);
});
m_contextMenu->addSeparator();
- m_selectParentAction = m_contextMenu->addAction(tr("Select Parent"), [&] {
+ m_selectParentAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::ParentIcon),
+ tr("Select Parent"), [&] {
ModelNode parentNode = ModelNode::lowestCommonAncestor(view()->selectedModelNodes());
if (!parentNode.isValid())
return;
@@ -233,8 +277,9 @@ void Edit3DWidget::createContextMenu()
});
QAction *defaultToggleGroupAction = view()->edit3DAction(View3DActionType::SelectionModeToggle)->action();
- m_toggleGroupAction = m_contextMenu->addAction(tr("Group Selection Mode"), [&](const bool &mode) {
- Q_UNUSED(mode)
+ m_toggleGroupAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::ToggleGroupIcon),
+ tr("Group Selection Mode"), [&]() {
view()->edit3DAction(View3DActionType::SelectionModeToggle)->action()->trigger();
});
connect(defaultToggleGroupAction, &QAction::toggled, m_toggleGroupAction, &QAction::setChecked);
@@ -260,8 +305,7 @@ bool Edit3DWidget::isSceneLocked() const
}
// Called by the view to update the "create" sub-menu when the Quick3D entries are ready.
-void Edit3DWidget::updateCreateSubMenu(const QStringList &keys,
- const QHash<QString, QList<ItemLibraryEntry>> &entriesMap)
+void Edit3DWidget::updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesList)
{
if (!m_contextMenu)
return;
@@ -272,21 +316,45 @@ void Edit3DWidget::updateCreateSubMenu(const QStringList &keys,
}
m_nameToEntry.clear();
- m_createSubMenu = m_contextMenu->addMenu(tr("Create"));
- for (const QString &cat : keys) {
- QList<ItemLibraryEntry> entries = entriesMap.value(cat);
+ m_createSubMenu = new QmlEditorMenu(tr("Create"), m_contextMenu);
+ m_createSubMenu->setIcon(contextIcon(DesignerIcons::CreateIcon));
+ m_contextMenu->addMenu(m_createSubMenu);
+
+ const QString docPath = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().toString();
+
+ auto isEntryValid = [&](const ItemLibraryEntry &entry) -> bool {
+ // Don't allow entries that match current document
+ const QString path = entry.customComponentSource();
+ return path.isEmpty() || docPath != path;
+ };
+
+ for (const auto &details : entriesList) {
+ QList<ItemLibraryEntry> entries = details.entryList;
if (entries.isEmpty())
continue;
- QMenu *catMenu = m_createSubMenu->addMenu(cat);
+ QMenu *catMenu = nullptr;
std::sort(entries.begin(), entries.end(), [](const ItemLibraryEntry &a, const ItemLibraryEntry &b) {
return a.name() < b.name();
});
for (const ItemLibraryEntry &entry : std::as_const(entries)) {
- QAction *action = catMenu->addAction(entry.name(), this, &Edit3DWidget::onCreateAction);
+ if (!isEntryValid(entry))
+ continue;
+
+ if (!catMenu) {
+ catMenu = new QmlEditorMenu(details.name, m_createSubMenu);
+ catMenu->setIcon(details.icon);
+ m_createSubMenu->addMenu(catMenu);
+ }
+
+ QAction *action = catMenu->addAction(
+ getEntryIcon(entry),
+ entry.name(),
+ this,
+ &Edit3DWidget::onCreateAction);
action->setData(entry.name());
m_nameToEntry.insert(entry.name(), entry);
}
@@ -320,7 +388,7 @@ void Edit3DWidget::onCreateAction()
// if added node is a Model, assign it a material
if (modelNode.metaInfo().isQtQuick3DModel())
- m_view->assignMaterialTo3dModel(modelNode);
+ MaterialUtils::assignMaterialTo3dModel(m_view, modelNode);
});
}
@@ -431,10 +499,17 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
->viewManager().designerActionManager();
- 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_TEXTURE)) {
+ if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)
+ || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
+ const auto urls = dragEnterEvent->mimeData()->urls();
+ if (!urls.isEmpty()) {
+ if (Asset(urls.first().toLocalFile()).isValidTextureSource())
+ dragEnterEvent->acceptProposedAction();
+ }
+ } 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_TEXTURE)) {
dragEnterEvent->acceptProposedAction();
} else if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) {
QByteArray data = dragEnterEvent->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO);
@@ -449,6 +524,8 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
{
+ dropEvent->accept();
+ setFocus();
const QPointF pos = m_canvas->mapFrom(this, dropEvent->position());
// handle dropping materials and textures
@@ -464,12 +541,14 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
else
m_view->dropTexture(dropNode, pos);
}
+ m_view->model()->endDrag();
return;
}
// handle dropping bundle materials
if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) {
m_view->dropBundleMaterial(pos);
+ m_view->model()->endDrag();
return;
}
@@ -477,6 +556,15 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) {
if (!m_draggedEntry.name().isEmpty())
m_view->dropComponent(m_draggedEntry, pos);
+ m_view->model()->endDrag();
+ return;
+ }
+
+ // handle dropping image assets
+ if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)
+ || dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
+ m_view->dropAsset(dropEvent->mimeData()->urls().first().toLocalFile(), pos);
+ m_view->model()->endDrag();
return;
}
@@ -501,6 +589,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
}
}
});
+
+ m_view->model()->endDrag();
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
index 728d81646d..092abd313a 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
@@ -18,6 +18,19 @@ class Edit3DView;
class Edit3DCanvas;
class ToolBox;
+struct ItemLibraryDetails {
+ QString name;
+ QIcon icon;
+ QList<ItemLibraryEntry> entryList;
+
+ ItemLibraryDetails(
+ const QString &name = QString(),
+ const QIcon &icon = QIcon())
+ : name (name)
+ , icon(icon)
+ {}
+};
+
class Edit3DWidget : public QWidget
{
Q_OBJECT
@@ -37,8 +50,7 @@ public:
void showBackgroundColorMenu(bool show, const QPoint &pos);
void showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d);
- void updateCreateSubMenu(const QStringList &keys,
- const QHash<QString, QList<ItemLibraryEntry>> &entriesMap);
+ void updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesList);
private slots:
void onCreateAction();
diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
index 23e60dbda2..a0d7cc8981 100644
--- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
+++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp
@@ -8,7 +8,7 @@
#include "bindingproperty.h"
#include "metainfo.h"
#include "projectexplorer/project.h"
-#include "projectexplorer/session.h"
+#include "projectexplorer/projectmanager.h"
#include "qmldesignerplugin.h"
#include "signalhandlerproperty.h"
#include "utils/fileutils.h"
@@ -23,7 +23,7 @@ namespace QmlDesigner {
Utils::FilePath projectFilePath()
{
if (auto *doc = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()) {
- if (auto *proj = ProjectExplorer::SessionManager::projectForFile(doc->fileName()))
+ if (auto *proj = ProjectExplorer::ProjectManager::projectForFile(doc->fileName()))
return proj->projectDirectory();
}
return Utils::FilePath();
diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp
index 9d57112747..72205bd760 100644
--- a/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp
+++ b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp
@@ -51,7 +51,7 @@ void EventListPluginView::registerActions()
&SelectionContextFunctors::always,
&SelectionContextFunctors::always));
auto eventListAction = new EventListAction();
- connect(eventListAction->defaultAction(), &QAction::triggered, [this]() {
+ connect(eventListAction->action(), &QAction::triggered, [this]() {
if (!m_eventListDialog)
m_eventListDialog = new EventListDialog(Core::ICore::dialogParent());
@@ -62,7 +62,7 @@ void EventListPluginView::registerActions()
designerActionManager.addDesignerAction(eventListAction);
auto assignEventAction = new AssignEventEditorAction();
- connect(assignEventAction->defaultAction(), &QAction::triggered, [this]() {
+ connect(assignEventAction->action(), &QAction::triggered, [this]() {
if (!m_assigner)
m_assigner = new AssignEventDialog(Core::ICore::dialogParent());
if (!m_eventListDialog)
@@ -78,7 +78,7 @@ void EventListPluginView::registerActions()
auto *connectSignalAction = new ConnectSignalAction();
- connect(connectSignalAction->defaultAction(), &QAction::triggered, [this, connectSignalAction]() {
+ connect(connectSignalAction->action(), &QAction::triggered, [this, connectSignalAction]() {
if (!m_signalConnector)
m_signalConnector = new ConnectSignalDialog(Core::ICore::dialogParent());
diff --git a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp
index 7403c239b3..a9d72be8dd 100644
--- a/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/backgroundaction.cpp
@@ -3,6 +3,8 @@
#include "backgroundaction.h"
+#include <utils/stylehelper.h>
+
#include <QComboBox>
#include <QPainter>
@@ -52,7 +54,7 @@ QWidget *BackgroundAction::createWidget(QWidget *parent)
connect(comboBox, &QComboBox::currentIndexChanged,
this, &BackgroundAction::emitBackgroundChanged);
- comboBox->setProperty("hideborder", true);
+ comboBox->setProperty(Utils::StyleHelper::C_HIDE_BORDER, true);
comboBox->setToolTip(tr("Set the color of the canvas."));
m_comboBox = comboBox;
return comboBox;
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
index 0077755d90..8695186605 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
@@ -7,6 +7,7 @@
#include "formeditorview.h"
#include "assetslibrarywidget.h"
#include "assetslibrarymodel.h"
+#include "materialutils.h"
#include <metainfo.h>
#include <modelnodeoperations.h>
#include <nodehints.h>
@@ -284,6 +285,7 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneD
}
view()->changeToSelectionTool();
+ view()->model()->endDrag();
}
}
@@ -440,7 +442,7 @@ void DragTool::handleView3dDrop()
const QList<ModelNode> models = dragNode.modelNode().subModelNodesOfType(
model->qtQuick3DModelMetaInfo());
QTC_ASSERT(models.size() == 1, return);
- view()->assignMaterialTo3dModel(models.at(0));
+ MaterialUtils::assignMaterialTo3dModel(view(), models.at(0));
}
}
}
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
index 0e9873dd19..1f4d526285 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
@@ -271,7 +271,7 @@ void FormEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVer
for (FormEditorItem *item : items) {
item->setParentItem(nullptr);
m_scene->removeItemFromHash(item);
- delete item;
+ deleteWithoutChildren({item});
}
QmlItemNode newItemNode(rootModelNode());
@@ -341,7 +341,13 @@ WidgetInfo FormEditorView::widgetInfo()
if (!m_formEditorWidget)
createFormEditorWidget();
- return createWidgetInfo(m_formEditorWidget.data(), "FormEditor", WidgetInfo::CentralPane, 0, tr("2D"), DesignerWidgetFlags::IgnoreErrors);
+ return createWidgetInfo(m_formEditorWidget.data(),
+ "FormEditor",
+ WidgetInfo::CentralPane,
+ 0,
+ tr("2D"),
+ tr("2D view"),
+ DesignerWidgetFlags::IgnoreErrors);
}
FormEditorWidget *FormEditorView::formEditorWidget()
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
index 648a51b91b..bfc03d2b23 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
@@ -105,7 +105,8 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view)
m_showBoundingRectAction = new QAction(tr("Show Bounds"), this);
m_showBoundingRectAction->setCheckable(true);
m_showBoundingRectAction->setChecked(false);
- m_showBoundingRectAction->setIcon(DesignerActionManager::instance().contextIcon(DesignerIcons::ShowBoundsIcon));
+ m_showBoundingRectAction->setIcon(
+ DesignerActionManager::instance().contextIcon(DesignerIcons::ShowBoundsIcon));
registerActionAsCommand(m_showBoundingRectAction,
Constants::FORMEDITOR_NO_SHOW_BOUNDING_RECTANGLE,
QKeySequence(Qt::Key_A),
@@ -116,53 +117,49 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view)
m_rootWidthAction = new LineEditAction(tr("Override Width"), this);
m_rootWidthAction->setToolTip(tr("Override width of root component."));
- connect(m_rootWidthAction.data(), &LineEditAction::textChanged,
- this, &FormEditorWidget::changeRootItemWidth);
+ connect(m_rootWidthAction.data(),
+ &LineEditAction::textChanged,
+ this,
+ &FormEditorWidget::changeRootItemWidth);
addAction(m_rootWidthAction.data());
upperActions.append(m_rootWidthAction.data());
m_rootHeightAction = new LineEditAction(tr("Override Height"), this);
m_rootHeightAction->setToolTip(tr("Override height of root component."));
- connect(m_rootHeightAction.data(), &LineEditAction::textChanged,
- this, &FormEditorWidget::changeRootItemHeight);
+ connect(m_rootHeightAction.data(),
+ &LineEditAction::textChanged,
+ this,
+ &FormEditorWidget::changeRootItemHeight);
addAction(m_rootHeightAction.data());
upperActions.append(m_rootHeightAction.data());
- m_toolBox = new ToolBox(nullptr, this);
+ m_toolBox = new ToolBox(this);
fillLayout->addWidget(m_toolBox.data());
m_toolBox->setLeftSideActions(upperActions);
m_backgroundAction = new BackgroundAction(m_toolActionGroup.data());
- connect(m_backgroundAction.data(), &BackgroundAction::backgroundChanged, this, &FormEditorWidget::changeBackgound);
+ connect(m_backgroundAction.data(),
+ &BackgroundAction::backgroundChanged,
+ this,
+ &FormEditorWidget::changeBackgound);
addAction(m_backgroundAction.data());
upperActions.append(m_backgroundAction.data());
m_toolBox->addRightSideAction(m_backgroundAction.data());
// Zoom actions
- const QString fontName = "qtds_propertyIconFont.ttf";
- const QColor iconColorNormal(Theme::getColor(Theme::IconsBaseColor));
- const QColor iconColorDisabled(Theme::getColor(Theme::IconsDisabledColor));
- const QIcon zoomAllIcon = Utils::StyleHelper::getIconFromIconFont(
- fontName, Theme::getIconUnicode(Theme::Icon::zoomAll), 28, 28, iconColorNormal);
-
- const QString zoomSelectionUnicode = Theme::getIconUnicode(Theme::Icon::zoomSelection);
- const auto zoomSelectionNormal = Utils::StyleHelper::IconFontHelper(zoomSelectionUnicode,
- iconColorNormal,
- QSize(28, 28),
- QIcon::Normal);
- const auto zoomSelectionDisabeld = Utils::StyleHelper::IconFontHelper(zoomSelectionUnicode,
- iconColorDisabled,
- QSize(28, 28),
- QIcon::Disabled);
-
- const QIcon zoomSelectionIcon = Utils::StyleHelper::getIconFromIconFont(fontName,
- {zoomSelectionNormal,
- zoomSelectionDisabeld});
- const QIcon zoomInIcon = Utils::StyleHelper::getIconFromIconFont(
- fontName, Theme::getIconUnicode(Theme::Icon::zoomIn), 28, 28, iconColorNormal);
- const QIcon zoomOutIcon = Utils::StyleHelper::getIconFromIconFont(
- fontName, Theme::getIconUnicode(Theme::Icon::zoomOut), 28, 28, iconColorNormal);
+ const QIcon zoomAllIcon = Theme::iconFromName(Theme::Icon::fitAll_medium);
+ auto zoomSelectionNormal = Theme::iconFromName(Theme::Icon::fitSelection_medium);
+ auto zoomSelectionDisabeld = Theme::iconFromName(Theme::Icon::fitSelection_medium,
+ Theme::getColor(
+ Theme::Color::DStoolbarIcon_blocked));
+ QIcon zoomSelectionIcon;
+ zoomSelectionIcon.addPixmap(zoomSelectionNormal.pixmap({16, 16}), QIcon::Normal);
+ zoomSelectionIcon.addPixmap(zoomSelectionDisabeld.pixmap({16, 16}), QIcon::Disabled);
+
+ const QIcon zoomInIcon = Theme::iconFromName(Theme::Icon::zoomIn_medium);
+ const QIcon zoomOutIcon = Theme::iconFromName(Theme::Icon::zoomOut_medium);
+ const QIcon reloadIcon = Theme::iconFromName(Theme::Icon::reload_medium);
auto writeZoomLevel = [this]() {
double level = m_graphicsView->transform().m11();
@@ -270,7 +267,7 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view)
m_toolBox->addRightSideAction(m_zoomSelectionAction.data());
connect(m_zoomSelectionAction.data(), &QAction::triggered, frameSelection);
- m_resetAction = new QAction(Utils::Icons::RESET_TOOLBAR.icon(), tr("Reset View"), this);
+ m_resetAction = new QAction(reloadIcon, tr("Reload View"), this);
registerActionAsCommand(m_resetAction,
Constants::FORMEDITOR_REFRESH,
QKeySequence(Qt::Key_R),
@@ -611,7 +608,8 @@ void FormEditorWidget::showEvent(QShowEvent *event)
void FormEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
{
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
- ->viewManager().designerActionManager();
+ ->viewManager()
+ .designerActionManager();
if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData()))
dragEnterEvent->acceptProposedAction();
}
@@ -619,25 +617,34 @@ void FormEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
void FormEditorWidget::dropEvent(QDropEvent *dropEvent)
{
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
- ->viewManager().designerActionManager();
- QHash<QString, QStringList> addedAssets = actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+ ->viewManager()
+ .designerActionManager();
+ QHash<QString, QStringList> addedAssets = actionManager.handleExternalAssetsDrop(
+ dropEvent->mimeData());
m_formEditorView->executeInTransaction("FormEditorWidget::dropEvent", [&] {
// Create Image components for added image assets
- const QStringList addedImages = addedAssets.value(ComponentCoreConstants::addImagesDisplayString);
+ const QStringList addedImages = addedAssets.value(
+ ComponentCoreConstants::addImagesDisplayString);
for (const QString &imgPath : addedImages) {
- QmlItemNode::createQmlItemNodeFromImage(m_formEditorView, imgPath, {},
- m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode(),
- false);
+ QmlItemNode::createQmlItemNodeFromImage(
+ m_formEditorView,
+ imgPath,
+ {},
+ m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode(),
+ false);
}
// Create Text components for added font assets
const QStringList addedFonts = addedAssets.value(ComponentCoreConstants::addFontsDisplayString);
for (const QString &fontPath : addedFonts) {
QString fontFamily = QFileInfo(fontPath).baseName();
- QmlItemNode::createQmlItemNodeFromFont(m_formEditorView, fontFamily, rootItemRect().center(),
- m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode(),
- false);
+ QmlItemNode::createQmlItemNodeFromFont(
+ m_formEditorView,
+ fontFamily,
+ rootItemRect().center(),
+ m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode(),
+ false);
}
});
}
diff --git a/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp b/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp
index 284dff1e55..804f061657 100644
--- a/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/seekerslider.cpp
@@ -4,113 +4,104 @@
#include "seekerslider.h"
#include <utils/icon.h>
+#include <utils/stylehelper.h>
+#include <QMouseEvent>
#include <QStyleOption>
#include <QSlider>
-#include <QDebug>
#include <QPainter>
namespace QmlDesigner {
-SeekerSlider::SeekerSlider(QWidget *parentWidget)
- : QWidget(parentWidget),
- m_bgIcon(QLatin1String(":/icon/layout/scrubbg.png"))
+SeekerSlider::SeekerSlider(QWidget *parent)
+ : QSlider(parent)
{
- m_handleIcon.addFile(QLatin1String(":/icon/layout/scrubhandle-24.png"), QSize(24, 24));
- m_handleIcon.addFile(QLatin1String(":/icon/layout/scrubhandle-48.png"), QSize(48, 48));
- m_handleIcon.addFile(QLatin1String(":/icon/layout/scrubhandle-disabled-24.png"), QSize(24, 24), QIcon::Disabled);
- m_handleIcon.addFile(QLatin1String(":/icon/layout/scrubhandle-disabled-48.png"), QSize(48, 48), QIcon::Disabled);
- const Utils::Icon bg({{":/icon/layout/scrubbg.png", Utils::Theme::IconsBaseColor}});
- m_bgWidth = bg.pixmap().width();
- m_bgHeight = bg.pixmap().height();
- m_handleWidth = m_bgHeight;
- m_handleHeight = m_bgHeight;
- int width = m_bgWidth + m_handleWidth * 2;
- m_sliderHalfWidth = m_bgWidth / 2;
- setMinimumWidth(width);
- setMaximumWidth(width);
- setProperty("panelwidget", true);
- setProperty("panelwidget_singlerow", true);
+ Utils::StyleHelper::setPanelWidget(this);
+ Utils::StyleHelper::setPanelWidgetSingleRow(this);
+ setOrientation(Qt::Horizontal);
+ setFixedWidth(120);
+ setMaxValue(30);
}
-void SeekerSlider::paintEvent([[maybe_unused]] QPaintEvent *event)
+int SeekerSlider::maxValue() const
{
- QPainter painter(this);
- {
- QStyleOptionToolBar option;
- option.rect = rect();
- option.state = QStyle::State_Horizontal;
- style()->drawControl(QStyle::CE_ToolBar, &option, &painter, this);
- }
-
- int x = rect().width() / 2;
- int y = rect().height() / 2;
-
- const QPixmap bg = m_bgIcon.pixmap(QSize(m_bgWidth, m_bgHeight), isEnabled() ? QIcon::Normal : QIcon::Disabled, QIcon::On);
- painter.drawPixmap(x - m_bgWidth / 2, y - m_bgHeight / 2, bg);
-
- if (m_moving) {
- const QPixmap handle = m_handleIcon.pixmap(QSize(m_handleWidth, m_handleHeight), QIcon::Active, QIcon::On);
- painter.drawPixmap(x - m_handleWidth / 2 + m_sliderPos, y - m_handleHeight / 2, handle);
- } else {
- const QPixmap handle = m_handleIcon.pixmap(QSize(m_handleWidth, m_handleHeight), isEnabled() ? QIcon::Normal : QIcon::Disabled, QIcon::On);
- painter.drawPixmap(x - m_handleWidth / 2, y - m_handleHeight / 2, handle);
- }
+ return maximum();
+}
+
+void SeekerSlider::setMaxValue(int maxValue)
+{
+ maxValue = std::abs(maxValue);
+ setRange(-maxValue, +maxValue);
}
void SeekerSlider::mousePressEvent(QMouseEvent *event)
{
- if (event->button() != Qt::LeftButton) {
- QWidget::mousePressEvent(event);
+ if (event->button() != Qt::LeftButton)
return;
- }
-
- int x = rect().width() / 2;
- int y = rect().height() / 2;
- auto pos = event->localPos();
- if (pos.x() >= x - m_handleWidth / 2 && pos.x() <= x + m_handleWidth / 2
- && pos.y() >= y - m_handleHeight / 2 && pos.y() <= y + m_handleHeight / 2) {
- m_moving = true;
- m_startPos = pos.x();
- }
+
+ QStyleOptionSlider os;
+ initStyleOption(&os);
+ QRect handleRect = style()->subControlRect(QStyle::CC_Slider, &os, QStyle::SC_SliderHandle, this);
+ m_moving = handleRect.contains(event->localPos().toPoint());
+ if (m_moving)
+ QSlider::mousePressEvent(event);
+ else
+ event->setAccepted(false);
}
void SeekerSlider::mouseMoveEvent(QMouseEvent *event)
{
- if (!m_moving) {
- QWidget::mouseMoveEvent(event);
+ if (!m_moving)
return;
- }
-
- auto pos = event->localPos();
- int delta = pos.x() - m_startPos;
- m_sliderPos = qBound(-m_sliderHalfWidth, delta, m_sliderHalfWidth);
- delta = m_maxPosition * m_sliderPos / m_sliderHalfWidth;
- if (delta != m_position) {
- m_position = delta;
- Q_EMIT positionChanged();
- update();
- }
+
+ QSlider::mouseMoveEvent(event);
}
void SeekerSlider::mouseReleaseEvent(QMouseEvent *event)
{
- if (!m_moving) {
- QWidget::mouseReleaseEvent(event);
+ if (!m_moving)
return;
- }
+ setValue(0);
m_moving = false;
- m_position = 0;
- m_startPos = 0;
- m_sliderPos = 0;
- Q_EMIT positionChanged();
- update();
+ QSlider::mouseReleaseEvent(event);
+}
+
+SeekerSliderAction::SeekerSliderAction(QObject *parent)
+ : QWidgetAction(parent)
+ , m_defaultSlider(new SeekerSlider())
+{
+ setDefaultWidget(m_defaultSlider);
+ QObject::connect(m_defaultSlider, &QSlider::valueChanged, this, &SeekerSliderAction::valueChanged);
+}
+
+SeekerSliderAction::~SeekerSliderAction()
+{
+ m_defaultSlider->deleteLater();
+}
+
+SeekerSlider *SeekerSliderAction::defaultSlider() const
+{
+ return m_defaultSlider;
+}
+
+int SeekerSliderAction::value()
+{
+ return m_defaultSlider->value();
}
-int SeekerSlider::position() const
+QWidget *SeekerSliderAction::createWidget(QWidget *parent)
{
- return m_position;
+ SeekerSlider *slider = new SeekerSlider(parent);
+
+ QObject::connect(m_defaultSlider, &SeekerSlider::valueChanged, slider, &SeekerSlider::setValue);
+ QObject::connect(slider, &SeekerSlider::valueChanged, m_defaultSlider, &SeekerSlider::setValue);
+ QObject::connect(m_defaultSlider, &QSlider::rangeChanged, slider, &QSlider::setRange);
+
+ slider->setValue(m_defaultSlider->value());
+ slider->setMaxValue(m_defaultSlider->maxValue());
+
+ return slider;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/seekerslider.h b/src/plugins/qmldesigner/components/formeditor/seekerslider.h
index 0c9a6a65dc..d8bc8686ca 100644
--- a/src/plugins/qmldesigner/components/formeditor/seekerslider.h
+++ b/src/plugins/qmldesigner/components/formeditor/seekerslider.h
@@ -2,50 +2,54 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include <QWidget>
-#include <QMouseEvent>
-#include <QIcon>
+#include <QSlider>
+#include <QWidgetAction>
namespace QmlDesigner {
-
-class SeekerSlider : public QWidget
+class SeekerSlider : public QSlider
{
Q_OBJECT
-public:
- SeekerSlider(QWidget *parentWidget);
- int position() const;
- int maxPosition() const
- {
- return m_maxPosition;
- }
- void setMaxPosition(int pos)
- {
- m_maxPosition = qMax(0, pos);
- }
+public:
+ explicit SeekerSlider(QWidget *parent = nullptr);
-Q_SIGNALS:
- void positionChanged();
+ int maxValue() const;
+ void setMaxValue(int maxValue);
protected:
- void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
- int m_position = 0;
- int m_startPos = 0;
- int m_sliderPos = 0;
- int m_sliderHalfWidth = 0;
- int m_maxPosition = 30;
+ using QSlider::setMinimum;
+ using QSlider::setMaximum;
+ using QSlider::setRange;
+
bool m_moving = false;
- int m_bgWidth;
- int m_bgHeight;
- int m_handleWidth;
- int m_handleHeight;
- QIcon m_bgIcon;
- QIcon m_handleIcon;
+};
+
+class SeekerSlider;
+class SeekerSliderAction : public QWidgetAction
+{
+ Q_OBJECT
+
+public:
+ explicit SeekerSliderAction(QObject *parent);
+ virtual ~SeekerSliderAction();
+
+ SeekerSlider *defaultSlider() const;
+ int value();
+
+signals:
+ void valueChanged(int);
+
+protected:
+ virtual QWidget *createWidget(QWidget *parent) override;
+
+private:
+ using QWidgetAction::setDefaultWidget;
+ SeekerSlider *m_defaultSlider = nullptr;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp
index b81bdeba0f..acd0b7401b 100644
--- a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp
@@ -3,6 +3,10 @@
#include "toolbox.h"
+#include <theme.h>
+
+#include <utils/stylehelper.h>
+
#include <QToolBar>
#include <QHBoxLayout>
#include <QDebug>
@@ -10,12 +14,13 @@
namespace QmlDesigner {
-ToolBox::ToolBox(SeekerSlider *seeker, QWidget *parentWidget)
- : Utils::StyledBar(parentWidget),
- m_leftToolBar(new QToolBar(QLatin1String("LeftSidebar"), this)),
- m_rightToolBar(new QToolBar(QLatin1String("RightSidebar"), this)),
- m_seeker(seeker)
+ToolBox::ToolBox(QWidget *parentWidget)
+ : Utils::StyledBar(parentWidget)
+ , m_leftToolBar(new QToolBar(QLatin1String("LeftSidebar"), this))
+ , m_rightToolBar(new QToolBar(QLatin1String("RightSidebar"), this))
{
+ Utils::StyleHelper::setPanelWidget(this, false);
+
m_leftToolBar->setFloatable(true);
m_leftToolBar->setMovable(true);
m_leftToolBar->setOrientation(Qt::Horizontal);
@@ -24,24 +29,25 @@ ToolBox::ToolBox(SeekerSlider *seeker, QWidget *parentWidget)
horizontalLayout->setContentsMargins(0, 0, 0, 0);
horizontalLayout->setSpacing(0);
- auto stretchToolbar = new QToolBar(this);
-
- m_leftToolBar->setProperty("panelwidget", true);
- m_leftToolBar->setProperty("panelwidget_singlerow", false);
+ setFixedHeight(Theme::toolbarSize());
- m_rightToolBar->setProperty("panelwidget", true);
- m_rightToolBar->setProperty("panelwidget_singlerow", false);
+ Utils::StyleHelper::setPanelWidget(m_leftToolBar, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_leftToolBar, false);
+ m_leftToolBar->setFixedHeight(Theme::toolbarSize());
- stretchToolbar->setProperty("panelwidget", true);
- stretchToolbar->setProperty("panelwidget_singlerow", false);
+ Utils::StyleHelper::setPanelWidget(m_rightToolBar, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_rightToolBar, false);
+ m_rightToolBar->setFixedHeight(Theme::toolbarSize());
+ m_rightToolBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+ auto stretchToolbar = new QToolBar(this);
+ Utils::StyleHelper::setPanelWidget(stretchToolbar, false);
+ Utils::StyleHelper::setPanelWidgetSingleRow(stretchToolbar, false);
stretchToolbar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_rightToolBar->setOrientation(Qt::Horizontal);
horizontalLayout->addWidget(m_leftToolBar);
horizontalLayout->addWidget(stretchToolbar);
- if (seeker)
- horizontalLayout->addWidget(m_seeker);
horizontalLayout->addWidget(m_rightToolBar);
}
@@ -74,9 +80,4 @@ QList<QAction*> ToolBox::actions() const
return m_leftToolBar->actions() + m_rightToolBar->actions();
}
-SeekerSlider *ToolBox::seeker() const
-{
- return m_seeker;
-}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.h b/src/plugins/qmldesigner/components/formeditor/toolbox.h
index 793c328a7e..9d3b86c686 100644
--- a/src/plugins/qmldesigner/components/formeditor/toolbox.h
+++ b/src/plugins/qmldesigner/components/formeditor/toolbox.h
@@ -16,18 +16,16 @@ namespace QmlDesigner {
class ToolBox : public Utils::StyledBar
{
public:
- ToolBox(SeekerSlider *seeker, QWidget *parentWidget);
+ explicit ToolBox(QWidget *parentWidget);
void setLeftSideActions(const QList<QAction*> &actions);
void setRightSideActions(const QList<QAction*> &actions);
void addLeftSideAction(QAction *action);
void addRightSideAction(QAction *action);
QList<QAction*> actions() const;
- SeekerSlider *seeker() const;
private:
QToolBar *m_leftToolBar;
QToolBar *m_rightToolBar;
- SeekerSlider *m_seeker;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index efeea8aa36..fecaaefadf 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -20,7 +20,7 @@
#include <projectexplorer/projecttree.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/kit.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
@@ -403,7 +403,7 @@ bool DesignDocument::isQtForMCUsProject() const
Utils::FilePath DesignDocument::projectFolder() const
{
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName());
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName());
if (currentProject)
return currentProject->projectDirectory();
@@ -434,7 +434,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod
void DesignDocument::updateQrcFiles()
{
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName());
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName());
if (currentProject) {
const auto srcFiles = currentProject->files(ProjectExplorer::Project::SourceFiles);
@@ -700,23 +700,27 @@ ModelNode DesignDocument::rootModelNode() const
void DesignDocument::undo()
{
- if (rewriterView() && !rewriterView()->modificationGroupActive())
+ if (rewriterView() && !rewriterView()->modificationGroupActive()) {
plainTextEdit()->undo();
+ rewriterView()->forceAmend();
+ }
viewManager().resetPropertyEditorView();
}
void DesignDocument::redo()
{
- if (rewriterView() && !rewriterView()->modificationGroupActive())
+ if (rewriterView() && !rewriterView()->modificationGroupActive()) {
plainTextEdit()->redo();
+ rewriterView()->forceAmend();
+ }
viewManager().resetPropertyEditorView();
}
static Target *getActiveTarget(DesignDocument *designDocument)
{
- Project *currentProject = SessionManager::projectForFile(designDocument->fileName());
+ Project *currentProject = ProjectManager::projectForFile(designDocument->fileName());
if (!currentProject)
currentProject = ProjectTree::currentProject();
diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
index d7862f926f..a587580590 100644
--- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
@@ -148,6 +148,7 @@ void DesignDocumentView::fromText(const QString &text)
RewriterView rewriterView{externalDependencies()};
rewriterView.setCheckSemanticErrors(false);
+ rewriterView.setPossibleImportsEnabled(false);
rewriterView.setTextModifier(&modifier);
inputModel->setRewriterView(&rewriterView);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon.png b/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon.png
index ef59d89279..d211de4554 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon.png
+++ b/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon@2x.png b/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon@2x.png
index 6540cc859c..8de2aeea05 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon@2x.png
+++ b/src/plugins/qmldesigner/components/itemlibrary/images/item-default-icon@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 648ffdb56c..91f403f218 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
#include "itemlibraryassetimportdialog.h"
#include "ui_itemlibraryassetimportdialog.h"
@@ -13,7 +14,8 @@
#include "theme.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+
#include <coreplugin/icore.h>
#include <QFileInfo>
@@ -61,6 +63,9 @@ const int labelMinWidth = 130;
const int controlMinWidth = 65;
const int columnSpacing = 16;
+constexpr QStringView qdsWorkaroundsKey{u"designStudioWorkarounds"};
+constexpr QStringView expandValuesKey{u"expandValueComponents"};
+
} // namespace
ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
@@ -170,6 +175,11 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
while (optIt != supportedOpts.constEnd()) {
QJsonObject options = QJsonObject::fromVariantMap(qvariant_cast<QVariantMap>(optIt.value()));
m_importOptions << options.value("options").toObject();
+ if (m_importOptions.last().contains(qdsWorkaroundsKey)) {
+ QJsonObject optObj = m_importOptions.last()[qdsWorkaroundsKey].toObject();
+ optObj.insert("value", QJsonValue{true});
+ m_importOptions.last().insert(qdsWorkaroundsKey, optObj);
+ }
auto it = defaultOpts.constBegin();
while (it != defaultOpts.constEnd()) {
if (m_importOptions.last().contains(it.key())) {
@@ -322,7 +332,7 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
// Unable to find original scene source, launch file dialog to locate it
QString initialPath;
ProjectExplorer::Project *currentProject
- = ProjectExplorer::SessionManager::projectForFile(
+ = ProjectExplorer::ProjectManager::projectForFile(
Utils::FilePath::fromString(compFileName));
if (currentProject)
initialPath = currentProject->projectDirectory().toString();
@@ -475,7 +485,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonObject &options = m_importOptions[optionsIndex];
const auto optKeys = options.keys();
for (const auto &optKey : optKeys) {
- if (!advanced && !isSimpleOption(optKey))
+ if ((!advanced && !isSimpleOption(optKey)) || isHiddenOption(optKey))
continue;
QJsonObject optObj = options.value(optKey).toObject();
const QString optName = optObj.value("name").toString();
@@ -587,11 +597,34 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
m_labelToControlWidgetMaps[optionsIndex].insert(optKey, optControl);
}
- // Handle conditions
+ // Find condition chains (up to two levels supported)
+ // key: Option that has condition and is also specified in another condition as property
+ // value: List of extra widgets that are affected by key property via condition
+ QHash<QString, QList<QWidget *>> conditionChains;
auto it = conditionMap.constBegin();
while (it != conditionMap.constEnd()) {
const QString &option = it.key();
const QJsonArray &conditions = it.value();
+ if (!conditions.isEmpty()) {
+ const QString optItem = conditions[0].toObject().value("property").toString();
+ if (conditionMap.contains(optItem)) {
+ if (!conditionChains.contains(optItem))
+ conditionChains.insert(optItem, {});
+ QPair<QWidget *, QWidget *> widgetPair = optionToWidgetsMap.value(option);
+ if (widgetPair.first)
+ conditionChains[optItem].append(widgetPair.first);
+ if (widgetPair.second)
+ conditionChains[optItem].append(widgetPair.second);
+ }
+ }
+ ++it;
+ }
+
+ // Handle conditions
+ it = conditionMap.constBegin();
+ while (it != conditionMap.constEnd()) {
+ const QString &option = it.key();
+ const QJsonArray &conditions = it.value();
const auto &conWidgets = optionToWidgetsMap.value(option);
QWidget *conLabel = conWidgets.first;
QWidget *conControl = conWidgets.second;
@@ -620,21 +653,33 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
auto optSpin = qobject_cast<QDoubleSpinBox *>(optWidgets.second);
if (optCb) {
auto enableConditionally = [optValue](QCheckBox *cb, QWidget *w1,
- QWidget *w2, Mode mode) {
+ QWidget *w2, const QList<QWidget *> &extraWidgets, Mode mode) {
bool equals = (mode == Mode::equals) == optValue.toBool();
bool enable = cb->isChecked() == equals;
w1->setEnabled(enable);
w2->setEnabled(enable);
+ if (extraWidgets.isEmpty())
+ return;
+
+ if (auto conditionCb = qobject_cast<QCheckBox *>(w2)) {
+ for (const auto w : extraWidgets)
+ w->setEnabled(conditionCb->isChecked() && enable);
+ }
};
- enableConditionally(optCb, conLabel, conControl, mode);
+ // Only initialize conditional state if conditional control is enabled.
+ // If it is disabled, it is assumed that previous chained condition handling
+ // already handled this case.
+ if (optCb->isEnabled())
+ enableConditionally(optCb, conLabel, conControl, conditionChains[option], mode);
if (conditionalWidgetMap.contains(optCb))
conditionalWidgetMap.insert(optCb, nullptr);
else
conditionalWidgetMap.insert(optCb, conControl);
QObject::connect(
optCb, &QCheckBox::toggled, optCb,
- [optCb, conLabel, conControl, mode, enableConditionally]() {
- enableConditionally(optCb, conLabel, conControl, mode);
+ [optCb, conLabel, conControl, extraWidgets = conditionChains[option],
+ mode, enableConditionally]() {
+ enableConditionally(optCb, conLabel, conControl, extraWidgets, mode);
});
}
if (optSpin) {
@@ -802,6 +847,16 @@ bool ItemLibraryAssetImportDialog::isSimpleOption(const QString &id)
return simpleOptions.contains(id);
}
+bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id)
+{
+ static QList<QStringView> hiddenOptions {
+ qdsWorkaroundsKey,
+ expandValuesKey // Hidden because qdsWorkaroundsKey we force true implies this
+ };
+
+ return hiddenOptions.contains(id);
+}
+
void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
{
m_dialogHeight = event->size().height();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
index 4de3dba54c..c5da478232 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
@@ -67,6 +67,7 @@ private:
bool isSimpleGroup(const QString &id);
bool isSimpleOption(const QString &id);
+ bool isHiddenOption(const QString &id);
Ui::ItemLibraryAssetImportDialog *ui = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index d9b8241943..635a248152 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -17,7 +17,7 @@
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/algorithm.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <utils/qtcassert.h>
#include <QApplication>
@@ -684,7 +684,7 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
if (modelManager) {
QmlJS::PathsAndLanguages pathToScan;
pathToScan.maybeInsert(Utils::FilePath::fromString(m_importPath));
- result = Utils::runAsync(&QmlJS::ModelManagerInterface::importScan,
+ result = Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan,
modelManager->workingCopy(), pathToScan,
modelManager, true, true, true);
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
index 83bb483907..cdaa3e28a9 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
@@ -3,6 +3,8 @@
#include "itemlibraryiconimageprovider.h"
+#include <imagecacheimageresponse.h>
+
#include <projectexplorer/target.h>
#include <utils/stylehelper.h>
@@ -11,42 +13,15 @@
namespace QmlDesigner {
-class ImageRespose : public QQuickImageResponse
-{
-public:
- QQuickTextureFactory *textureFactory() const override
- {
- return QQuickTextureFactory::textureFactoryForImage(m_image);
- }
-
- void setImage(const QImage &image)
- {
- m_image = image;
-
- emit finished();
- }
-
- void abort()
- {
- m_image = QImage{
- Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")};
-
- emit finished();
- }
-
-private:
- QImage m_image;
-};
-
-
QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QString &id,
const QSize &)
{
- auto response = std::make_unique<ImageRespose>();
+ auto response = std::make_unique<ImageCacheImageResponse>(QImage{
+ Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")});
m_cache.requestSmallImage(
id,
- [response = QPointer<ImageRespose>(response.get())](const QImage &image) {
+ [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) {
QMetaObject::invokeMethod(
response,
[response, image] {
@@ -55,12 +30,14 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS
},
Qt::QueuedConnection);
},
- [response = QPointer<ImageRespose>(response.get())](ImageCache::AbortReason abortReason) {
+ [response = QPointer<ImageCacheImageResponse>(response.get())](
+ ImageCache::AbortReason abortReason) {
QMetaObject::invokeMethod(
response,
[response, abortReason] {
switch (abortReason) {
case ImageCache::AbortReason::Failed:
+ case ImageCache::AbortReason::NoEntry:
if (response)
response->abort();
break;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index 556d0c978f..96b95f7949 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -13,7 +13,7 @@
#include <nodehints.h>
#include <nodemetainfo.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
#include <utils/algorithm.h>
@@ -336,7 +336,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
Utils::FilePath qmlFileName = document->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
QString projectName = project ? project->displayName() : "";
QString materialBundlePrefix = QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 95b6476638..b5a0567114 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -14,7 +14,7 @@
#include <nodelistproperty.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <rewriterview.h>
#include <sqlitedatabase.h>
@@ -45,7 +45,12 @@ WidgetInfo ItemLibraryView::widgetInfo()
if (m_widget.isNull())
m_widget = new ItemLibraryWidget{m_imageCache};
- return createWidgetInfo(m_widget.data(), "Components", WidgetInfo::LeftPane, 0, tr("Components"));
+ return createWidgetInfo(m_widget.data(),
+ "Components",
+ WidgetInfo::LeftPane,
+ 0,
+ tr("Components"),
+ tr("Components view"));
}
void ItemLibraryView::modelAttached(Model *model)
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index c22435c18c..549f9e69c1 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -71,7 +71,7 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event)
Model *model = document ? document->documentModel() : nullptr;
if (event->type() == QEvent::FocusOut) {
- if (obj == m_itemsWidget.data())
+ if (obj == m_itemsWidget->quickWidget())
QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "closeContextMenu");
} else if (event->type() == QMouseEvent::MouseMove) {
if (m_itemToDrag.isValid()) {
@@ -105,6 +105,10 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event)
m_itemToDrag = {};
}
}
+ } else if (event->type() == QMouseEvent::MouseButtonRelease) {
+ m_itemToDrag = {};
+
+ setIsDragging(false);
}
return QObject::eventFilter(obj, event);
@@ -119,7 +123,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
: m_itemIconSize(24, 24)
, m_itemLibraryModel(new ItemLibraryModel(this))
, m_addModuleModel(new ItemLibraryAddImportModel(this))
- , m_itemsWidget(new QQuickWidget(this))
+ , m_itemsWidget(new StudioQuickWidget(this))
, m_imageCache{imageCache}
{
m_compressionTimer.setInterval(1000);
@@ -130,27 +134,17 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
setMinimumWidth(100);
// set up Component Library view and model
+ m_itemsWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COMPONENT_LIBRARY);
m_itemsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_itemsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
- m_itemsWidget->rootContext()->setContextProperties({
- {"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())},
- {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())},
- {"itemLibraryIconWidth", m_itemIconSize.width()},
- {"itemLibraryIconHeight", m_itemIconSize.height()},
- {"rootView", QVariant::fromValue(this)},
- {"widthLimit", HORIZONTAL_LAYOUT_WIDTH_LIMIT},
- {"highlightColor", Utils::StyleHelper::notTooBrightHighlightColor()},
- });
-
m_previewTooltipBackend = std::make_unique<PreviewTooltipBackend>(m_imageCache);
- m_itemsWidget->rootContext()->setContextProperty("tooltipBackend", m_previewTooltipBackend.get());
m_itemsWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
m_itemsWidget->engine()->addImageProvider(QStringLiteral("qmldesigner_itemlibrary"),
new Internal::ItemLibraryImageProvider);
Theme::setupTheme(m_itemsWidget->engine());
- m_itemsWidget->installEventFilter(this);
+ m_itemsWidget->quickWidget()->installEventFilter(this);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
@@ -173,6 +167,19 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_ITEMLIBRARY_TIME);
// init the first load of the QML UI elements
+
+ auto map = m_itemsWidget->registerPropertyMap("ItemLibraryBackend");
+
+ map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())},
+ {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())},
+ {"itemLibraryIconWidth", m_itemIconSize.width()},
+ {"itemLibraryIconHeight", m_itemIconSize.height()},
+ {"rootView", QVariant::fromValue(this)},
+ {"widthLimit", HORIZONTAL_LAYOUT_WIDTH_LIMIT},
+ {"highlightColor", Utils::StyleHelper::notTooBrightHighlightColor()},
+ {"tooltipBackend", QVariant::fromValue(m_previewTooltipBackend.get())}});
+
+
reloadQmlSource();
}
@@ -316,7 +323,6 @@ void ItemLibraryWidget::reloadQmlSource()
{
const QString itemLibraryQmlPath = qmlSourcesPath() + "/ItemsView.qml";
QTC_ASSERT(QFileInfo::exists(itemLibraryQmlPath), return);
- m_itemsWidget->engine()->clearComponentCache();
m_itemsWidget->setSource(QUrl::fromLocalFile(itemLibraryQmlPath));
}
@@ -366,12 +372,21 @@ void ItemLibraryWidget::handlePriorityImportsChanged()
}
}
+void ItemLibraryWidget::setIsDragging(bool val)
+{
+ if (m_isDragging != val) {
+ m_isDragging = val;
+ emit isDraggingChanged();
+ }
+}
+
void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos)
{
// Actual drag is created after mouse has moved to avoid a QDrag bug that causes drag to stay
// active (and blocks mouse release) if mouse is released at the same spot of the drag start.
m_itemToDrag = itemLibEntry;
m_dragStartPoint = mousePos.toPoint();
+ setIsDragging(true);
}
bool ItemLibraryWidget::subCompEditMode() const
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
index ccf26d42d8..394b3ea610 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
@@ -6,6 +6,8 @@
#include "itemlibraryinfo.h"
#include "import.h"
+#include <studioquickwidget.h>
+
#include <utils/fancylineedit.h>
#include <utils/dropsupport.h>
#include <previewtooltip/previewtooltipbackend.h>
@@ -14,7 +16,6 @@
#include <QFrame>
#include <QPointF>
#include <QQmlPropertyMap>
-#include <QQuickWidget>
#include <QTimer>
#include <QToolButton>
@@ -44,6 +45,9 @@ class ItemLibraryWidget : public QFrame
public:
Q_PROPERTY(bool subCompEditMode READ subCompEditMode NOTIFY subCompEditModeChanged)
+ // 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)
+
ItemLibraryWidget(AsynchronousImageCache &imageCache);
~ItemLibraryWidget();
@@ -76,6 +80,7 @@ public:
signals:
void itemActivated(const QString &itemName);
void subCompEditModeChanged();
+ void isDraggingChanged();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -86,6 +91,8 @@ private:
void updateSearch();
void handlePriorityImportsChanged();
+ void setIsDragging(bool val);
+
static QString getDependencyImport(const Import &import);
QTimer m_compressionTimer;
@@ -96,7 +103,7 @@ private:
QPointer<ItemLibraryModel> m_itemLibraryModel;
QPointer<ItemLibraryAddImportModel> m_addModuleModel;
- QScopedPointer<QQuickWidget> m_itemsWidget;
+ QScopedPointer<StudioQuickWidget> m_itemsWidget;
std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
@@ -107,6 +114,7 @@ private:
QString m_filterText;
QPoint m_dragStartPoint;
bool m_subCompEditMode = false;
+ bool m_isDragging = false;
inline static int HORIZONTAL_LAYOUT_WIDTH_LIMIT = 600;
};
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
index 7f288f906a..d4aedb1cec 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
@@ -3,13 +3,13 @@
#include "materialbrowsermodel.h"
-#include <bindingproperty.h>
-#include <designmodewidget.h>
-#include <qmldesignerplugin.h>
-#include <qmlobjectnode.h>
-#include <variantproperty.h>
-#include <qmltimelinekeyframegroup.h>
-#include "utils/qtcassert.h"
+#include "designmodewidget.h"
+#include "qmldesignerplugin.h"
+#include "qmlobjectnode.h"
+#include "variantproperty.h"
+#include "qmltimelinekeyframegroup.h"
+
+#include <utils/qtcassert.h>
namespace QmlDesigner {
@@ -42,7 +42,7 @@ QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
return m_materialList.at(index.row()).internalId();
if (roleName == "materialVisible")
- return isMaterialVisible(index.row());
+ return isVisible(index.row());
if (roleName == "materialType") {
QString matType = QString::fromLatin1(m_materialList.at(index.row()).type());
@@ -57,7 +57,7 @@ QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const
return {};
}
-bool MaterialBrowserModel::isMaterialVisible(int idx) const
+bool MaterialBrowserModel::isVisible(int idx) const
{
if (!isValidIndex(idx))
return false;
@@ -221,14 +221,14 @@ void MaterialBrowserModel::refreshSearch()
bool isEmpty = false;
// if selected material goes invisible, select nearest material
- if (!isMaterialVisible(m_selectedIndex)) {
+ if (!isVisible(m_selectedIndex)) {
int inc = 1;
int incCap = m_materialList.count();
while (!isEmpty && inc < incCap) {
- if (isMaterialVisible(m_selectedIndex - inc)) {
+ if (isVisible(m_selectedIndex - inc)) {
selectMaterial(m_selectedIndex - inc);
break;
- } else if (isMaterialVisible(m_selectedIndex + inc)) {
+ } else if (isVisible(m_selectedIndex + inc)) {
selectMaterial(m_selectedIndex + inc);
break;
}
@@ -236,7 +236,7 @@ void MaterialBrowserModel::refreshSearch()
isEmpty = !isValidIndex(m_selectedIndex + inc)
&& !isValidIndex(m_selectedIndex - inc);
}
- if (!isMaterialVisible(m_selectedIndex)) // handles the case of a single material
+ if (!isVisible(m_selectedIndex)) // handles the case of a single material
isEmpty = true;
}
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
index 5e07606789..23e6a68973 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
@@ -3,11 +3,10 @@
#pragma once
-#include "qjsonobject.h"
-#include <modelnode.h>
-#include <qmlobjectnode.h>
+#include "modelnode.h"
#include <QAbstractListModel>
+#include <QJsonObject>
#include <QObject>
#include <QPointer>
@@ -77,6 +76,7 @@ public:
Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false);
Q_INVOKABLE void openMaterialEditor();
Q_INVOKABLE bool isCopiedMaterialValid() const;
+ Q_INVOKABLE bool isVisible(int idx) const;
struct PropertyCopyData
{
@@ -105,7 +105,6 @@ signals:
bool all);
private:
- bool isMaterialVisible(int idx) const;
bool isValidIndex(int idx) const;
QString m_searchText;
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
index c76ff43d03..10b6660db5 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
@@ -43,7 +43,7 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role)
}
if (role == RoleTexVisible)
- return isTextureVisible(index.row());
+ return isVisible(index.row());
if (role == RoleTexHasDynamicProps)
return !m_textureList.at(index.row()).dynamicProperties().isEmpty();
@@ -69,7 +69,7 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role)
return {};
}
-bool MaterialBrowserTexturesModel::isTextureVisible(int idx) const
+bool MaterialBrowserTexturesModel::isVisible(int idx) const
{
if (!isValidIndex(idx))
return false;
@@ -117,14 +117,14 @@ void MaterialBrowserTexturesModel::refreshSearch()
bool isEmpty = false;
// if selected texture goes invisible, select nearest one
- if (!isTextureVisible(m_selectedIndex)) {
+ if (!isVisible(m_selectedIndex)) {
int inc = 1;
int incCap = m_textureList.count();
while (!isEmpty && inc < incCap) {
- if (isTextureVisible(m_selectedIndex - inc)) {
+ if (isVisible(m_selectedIndex - inc)) {
selectTexture(m_selectedIndex - inc);
break;
- } else if (isTextureVisible(m_selectedIndex + inc)) {
+ } else if (isVisible(m_selectedIndex + inc)) {
selectTexture(m_selectedIndex + inc);
break;
}
@@ -132,7 +132,7 @@ void MaterialBrowserTexturesModel::refreshSearch()
isEmpty = !isValidIndex(m_selectedIndex + inc)
&& !isValidIndex(m_selectedIndex - inc);
}
- if (!isTextureVisible(m_selectedIndex)) // handles the case of a single item
+ if (!isVisible(m_selectedIndex)) // handles the case of a single item
isEmpty = true;
}
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h
index 801febdf53..6d66ad13ec 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h
@@ -61,6 +61,7 @@ public:
Q_INVOKABLE void updateSceneEnvState();
Q_INVOKABLE void updateModelSelectionState();
Q_INVOKABLE void applyAsLightProbe(qint64 internalId);
+ Q_INVOKABLE bool isVisible(int idx) const;
signals:
void isEmptyChanged();
@@ -76,7 +77,6 @@ signals:
void applyAsLightProbeRequested(const QmlDesigner::ModelNode &texture);
private:
- bool isTextureVisible(int idx) const;
bool isValidIndex(int idx) const;
QString m_searchText;
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
index 672173b747..5265c70683 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
@@ -5,21 +5,18 @@
#include "bindingproperty.h"
#include "createtexture.h"
+#include "designmodecontext.h"
#include "materialbrowsermodel.h"
#include "materialbrowsertexturesmodel.h"
#include "materialbrowserwidget.h"
#include "nodeabstractproperty.h"
+#include "nodeinstanceview.h"
#include "nodemetainfo.h"
+#include "qmldesignerconstants.h"
#include "qmlobjectnode.h"
#include "variantproperty.h"
-#include <designmodecontext.h>
-#include <nodeinstanceview.h>
-#include <nodelistproperty.h>
-#include <qmldesignerconstants.h>
-
#include <coreplugin/icore.h>
-#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -28,7 +25,6 @@
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickView>
-#include <QRegularExpression>
#include <QTimer>
namespace QmlDesigner {
@@ -161,7 +157,7 @@ WidgetInfo MaterialBrowserView::widgetInfo()
MaterialBrowserTexturesModel *texturesModel = m_widget->materialBrowserTexturesModel().data();
connect(texturesModel, &MaterialBrowserTexturesModel::selectedIndexChanged, this, [&] (int idx) {
ModelNode texNode = m_widget->materialBrowserTexturesModel()->textureAt(idx);
- emitCustomNotification("selected_texture_changed", {texNode}, {});
+ emitCustomNotification("selected_texture_changed", {texNode});
});
connect(texturesModel, &MaterialBrowserTexturesModel::duplicateTextureTriggered, this,
[&] (const ModelNode &texture) {
@@ -212,7 +208,19 @@ WidgetInfo MaterialBrowserView::widgetInfo()
"MaterialBrowser",
WidgetInfo::LeftPane,
0,
- tr("Material Browser"));
+ tr("Material Browser"),
+ tr("Material Browser view"));
+}
+
+void MaterialBrowserView::createTextures(const QStringList &assetPaths)
+{
+ auto *create = new CreateTextures(this);
+
+ executeInTransaction("MaterialBrowserView::createTextures", [&]() {
+ create->execute(assetPaths, AddTextureMode::Texture, m_sceneId);
+ });
+
+ create->deleteLater();
}
void MaterialBrowserView::modelAttached(Model *model)
@@ -444,7 +452,7 @@ void MaterialBrowserView::requestPreviews()
m_previewRequests.clear();
}
-ModelNode MaterialBrowserView::getMaterialOfModel(const ModelNode &model)
+ModelNode MaterialBrowserView::getMaterialOfModel(const ModelNode &model, int idx)
{
QmlObjectNode qmlObjNode(model);
QString matExp = qmlObjNode.expression("materials");
@@ -455,12 +463,10 @@ ModelNode MaterialBrowserView::getMaterialOfModel(const ModelNode &model)
if (mats.isEmpty())
return {};
- for (const auto &matId : mats) {
- ModelNode mat = modelNodeForId(matId);
- if (mat.isValid())
- return mat;
- }
- return {};
+ ModelNode mat = modelNodeForId(mats.at(idx));
+ QTC_ASSERT(mat.isValid(), return {});
+
+ return mat;
}
void MaterialBrowserView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
@@ -482,20 +488,32 @@ void MaterialBrowserView::importsChanged([[maybe_unused]] const QList<Import> &a
void MaterialBrowserView::customNotification(const AbstractView *view,
const QString &identifier,
const QList<ModelNode> &nodeList,
- [[maybe_unused]] const QList<QVariant> &data)
+ const QList<QVariant> &data)
{
- if (view == this)
+ if (view == this && identifier != "select_texture")
return;
- if (identifier == "selected_material_changed") {
- int idx = m_widget->materialBrowserModel()->materialIndex(nodeList.first());
+ if (identifier == "select_material") {
+ ModelNode matNode;
+ if (!data.isEmpty() && !m_selectedModels.isEmpty()) {
+ ModelNode model3D = m_selectedModels.at(0);
+ QTC_ASSERT(model3D.isValid(), return);
+ matNode = getMaterialOfModel(model3D, data[0].toInt());
+ } else {
+ matNode = nodeList.first();
+ }
+ QTC_ASSERT(matNode.isValid(), return);
+
+ int idx = m_widget->materialBrowserModel()->materialIndex(matNode);
if (idx != -1)
m_widget->materialBrowserModel()->selectMaterial(idx);
- } else if (identifier == "selected_texture_changed") {
+ } else if (identifier == "select_texture") {
int idx = m_widget->materialBrowserTexturesModel()->textureIndex(nodeList.first());
if (idx != -1) {
m_widget->materialBrowserTexturesModel()->selectTexture(idx);
m_widget->materialBrowserTexturesModel()->refreshSearch();
+ if (!data.isEmpty() && data[0].toBool())
+ m_widget->focusMaterialSection(false);
}
} else if (identifier == "refresh_material_browser") {
QTimer::singleShot(0, model(), [this]() {
@@ -503,10 +521,15 @@ void MaterialBrowserView::customNotification(const AbstractView *view,
});
} else if (identifier == "delete_selected_material") {
m_widget->deleteSelectedItem();
+ } else if (identifier == "apply_asset_to_model3D") {
+ m_appliedTexturePath = data.at(0).toString();
+ applyTextureToModel3D(nodeList.at(0));
} else if (identifier == "apply_texture_to_model3D") {
applyTextureToModel3D(nodeList.at(0), nodeList.at(1));
} else if (identifier == "apply_texture_to_material") {
applyTextureToMaterial({nodeList.at(0)}, nodeList.at(1));
+ } else if (identifier == "focus_material_section") {
+ m_widget->focusMaterialSection(true);
}
}
@@ -556,7 +579,10 @@ void MaterialBrowserView::instancePropertyChanged(const QList<QPair<ModelNode, P
void MaterialBrowserView::applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture)
{
- if (!texture.isValid() || !model3D.isValid() || !model3D.modelNode().metaInfo().isQtQuick3DModel())
+ if (!texture.isValid() && m_appliedTexturePath.isEmpty())
+ return;
+
+ if (!model3D.isValid() || !model3D.modelNode().metaInfo().isQtQuick3DModel())
return;
BindingProperty matsProp = model3D.bindingProperty("materials");
@@ -572,42 +598,45 @@ void MaterialBrowserView::applyTextureToModel3D(const QmlObjectNode &model3D, co
void MaterialBrowserView::applyTextureToMaterial(const QList<ModelNode> &materials,
const ModelNode &texture)
{
- if (materials.size() > 0) {
+ if (materials.isEmpty())
+ return;
+
+ if (texture.isValid())
m_appliedTextureId = texture.id();
- m_textureModels.clear();
- QStringList materialsModel;
- for (const ModelNode &mat : std::as_const(materials)) {
- QString matName = mat.variantProperty("objectName").value().toString();
- materialsModel.append(QLatin1String("%1 (%2)").arg(matName, mat.id()));
- QList<PropertyName> texProps;
- for (const PropertyMetaInfo &p : mat.metaInfo().properties()) {
- if (p.propertyType().isQtQuick3DTexture())
- texProps.append(p.name());
- }
- m_textureModels.insert(mat.id(), texProps);
- }
- QString path = MaterialBrowserWidget::qmlSourcesPath() + "/ChooseMaterialProperty.qml";
-
- m_chooseMatPropsView = new QQuickView;
- m_chooseMatPropsView->setTitle(tr("Select a material property"));
- m_chooseMatPropsView->setResizeMode(QQuickView::SizeRootObjectToView);
- m_chooseMatPropsView->setMinimumSize({150, 100});
- m_chooseMatPropsView->setMaximumSize({600, 400});
- m_chooseMatPropsView->setWidth(450);
- m_chooseMatPropsView->setHeight(300);
- m_chooseMatPropsView->setFlags(Qt::Widget);
- m_chooseMatPropsView->setModality(Qt::ApplicationModal);
- m_chooseMatPropsView->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
- m_chooseMatPropsView->rootContext()->setContextProperties({
- {"rootView", QVariant::fromValue(this)},
- {"materialsModel", QVariant::fromValue(materialsModel)},
- {"propertiesModel", QVariant::fromValue(m_textureModels.value(materials.at(0).id()))},
- });
- m_chooseMatPropsView->setSource(QUrl::fromLocalFile(path));
- m_chooseMatPropsView->installEventFilter(this);
- m_chooseMatPropsView->show();
+ m_textureModels.clear();
+ QStringList materialsModel;
+ for (const ModelNode &mat : std::as_const(materials)) {
+ QString matName = mat.variantProperty("objectName").value().toString();
+ materialsModel.append(QLatin1String("%1 (%2)").arg(matName, mat.id()));
+ QList<PropertyName> texProps;
+ for (const PropertyMetaInfo &p : mat.metaInfo().properties()) {
+ if (p.propertyType().isQtQuick3DTexture())
+ texProps.append(p.name());
+ }
+ m_textureModels.insert(mat.id(), texProps);
}
+
+ QString path = MaterialBrowserWidget::qmlSourcesPath() + "/ChooseMaterialProperty.qml";
+
+ m_chooseMatPropsView = new QQuickView;
+ m_chooseMatPropsView->setTitle(tr("Select a material property"));
+ m_chooseMatPropsView->setResizeMode(QQuickView::SizeRootObjectToView);
+ m_chooseMatPropsView->setMinimumSize({150, 100});
+ m_chooseMatPropsView->setMaximumSize({600, 400});
+ m_chooseMatPropsView->setWidth(450);
+ m_chooseMatPropsView->setHeight(300);
+ m_chooseMatPropsView->setFlags(Qt::Widget);
+ m_chooseMatPropsView->setModality(Qt::ApplicationModal);
+ m_chooseMatPropsView->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
+ m_chooseMatPropsView->rootContext()->setContextProperties({
+ {"rootView", QVariant::fromValue(this)},
+ {"materialsModel", QVariant::fromValue(materialsModel)},
+ {"propertiesModel", QVariant::fromValue(m_textureModels.value(materials.at(0).id()))},
+ });
+ m_chooseMatPropsView->setSource(QUrl::fromLocalFile(path));
+ m_chooseMatPropsView->installEventFilter(this);
+ m_chooseMatPropsView->show();
}
void MaterialBrowserView::updatePropsModel(const QString &matId)
@@ -618,17 +647,27 @@ void MaterialBrowserView::updatePropsModel(const QString &matId)
void MaterialBrowserView::applyTextureToProperty(const QString &matId, const QString &propName)
{
- QTC_ASSERT(!m_appliedTextureId.isEmpty(), return);
+ executeInTransaction(__FUNCTION__, [&] {
+ if (m_appliedTextureId.isEmpty() && !m_appliedTexturePath.isEmpty()) {
+ auto texCreator = new CreateTexture(this);
+ ModelNode tex = texCreator->execute(m_appliedTexturePath, AddTextureMode::Texture);
+ m_appliedTextureId = tex.id();
+ m_appliedTexturePath.clear();
+ texCreator->deleteLater();
+ }
- QmlObjectNode mat = modelNodeForId(matId);
- QTC_ASSERT(mat.isValid(), return);
+ QTC_ASSERT(!m_appliedTextureId.isEmpty(), return);
- BindingProperty texProp = mat.bindingProperty(propName.toLatin1());
- QTC_ASSERT(texProp.isValid(), return);
+ QmlObjectNode mat = modelNodeForId(matId);
+ QTC_ASSERT(mat.isValid(), return);
- mat.setBindingProperty(propName.toLatin1(), m_appliedTextureId);
+ BindingProperty texProp = mat.bindingProperty(propName.toLatin1());
+ QTC_ASSERT(texProp.isValid(), return);
- closeChooseMatPropsView();
+ mat.setBindingProperty(propName.toLatin1(), m_appliedTextureId);
+
+ closeChooseMatPropsView();
+ });
}
void MaterialBrowserView::closeChooseMatPropsView()
@@ -647,6 +686,7 @@ bool MaterialBrowserView::eventFilter(QObject *obj, QEvent *event)
} else if (event->type() == QEvent::Close) {
if (obj == m_chooseMatPropsView) {
m_appliedTextureId.clear();
+ m_appliedTexturePath.clear();
m_chooseMatPropsView->deleteLater();
}
}
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h
index 0942f7a656..811d6679b6 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h
@@ -4,7 +4,6 @@
#pragma once
#include "abstractview.h"
-#include "createtexture.h"
#include <QPointer>
#include <QSet>
@@ -53,9 +52,10 @@ public:
void active3DSceneChanged(qint32 sceneId) override;
void currentStateChanged(const ModelNode &node) override;
- void applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture);
+ void applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture = {});
void applyTextureToMaterial(const QList<ModelNode> &materials, const ModelNode &texture);
+ void createTextures(const QStringList &assetPaths);
Q_INVOKABLE void updatePropsModel(const QString &matId);
Q_INVOKABLE void applyTextureToProperty(const QString &matId, const QString &propName);
@@ -71,7 +71,7 @@ private:
void loadPropertyGroups();
void requestPreviews();
ModelNode resolveSceneEnv();
- ModelNode getMaterialOfModel(const ModelNode &model);
+ ModelNode getMaterialOfModel(const ModelNode &model, int idx = 0);
AsynchronousImageCache &m_imageCache;
QPointer<MaterialBrowserWidget> m_widget;
@@ -87,6 +87,7 @@ private:
QPointer<QQuickView> m_chooseMatPropsView;
QHash<QString, QList<PropertyName>> m_textureModels;
QString m_appliedTextureId;
+ QString m_appliedTexturePath; // defers texture creation until dialog apply
int m_sceneId = -1;
};
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
index 372c10fb63..d0c5483bdd 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
@@ -3,42 +3,34 @@
#include "materialbrowserwidget.h"
+#include "asset.h"
+#include "assetimageprovider.h"
+#include "createtexture.h"
+#include "documentmanager.h"
+#include "hdrimage.h"
#include "materialbrowsermodel.h"
#include "materialbrowsertexturesmodel.h"
#include "materialbrowserview.h"
+#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
+#include "theme.h"
+#include "variantproperty.h"
-#include <designeractionmanager.h>
-#include <designermcumanager.h>
-#include <documentmanager.h>
-#include <propertyeditorimageprovider.h>
-#include <qmldesignerconstants.h>
-#include <qmldesignerplugin.h>
-#include <variantproperty.h>
+#include <coreplugin/icore.h>
-#include <theme.h>
+#include <studioquickwidget.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
-#include <utils/hdrimage.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
-#include <QMenu>
#include <QMimeData>
#include <QMouseEvent>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QQmlContext>
-#include <QQmlEngine>
#include <QQuickImageProvider>
#include <QQuickItem>
-#include <QQuickWidget>
#include <QShortcut>
-#include <QStackedWidget>
-#include <QTabBar>
-#include <QToolButton>
#include <QVBoxLayout>
-#include <QWheelEvent>
namespace QmlDesigner {
@@ -93,7 +85,7 @@ public:
bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::FocusOut) {
- if (obj == m_quickWidget.data())
+ if (obj == m_quickWidget->quickWidget())
QMetaObject::invokeMethod(m_quickWidget->rootObject(), "closeContextMenu");
} else if (event->type() == QMouseEvent::MouseMove) {
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -124,6 +116,8 @@ bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event)
const QString suffix = iconPath.split('.').last().toLower();
if (suffix == "hdr")
pixmap = HdrImage{iconPath}.toPixmap();
+ else if (suffix == "ktx")
+ pixmap = Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_ktx.png");
else
pixmap = Utils::StyleHelper::dpiSpecificImageFile(iconPath);
if (pixmap.isNull())
@@ -137,6 +131,8 @@ bool MaterialBrowserWidget::eventFilter(QObject *obj, QEvent *event)
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_materialToDrag = {};
m_textureToDrag = {};
+
+ setIsDragging(false);
}
return QObject::eventFilter(obj, event);
@@ -147,12 +143,12 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
: m_materialBrowserView(view)
, m_materialBrowserModel(new MaterialBrowserModel(this))
, m_materialBrowserTexturesModel(new MaterialBrowserTexturesModel(this))
- , m_quickWidget(new QQuickWidget(this))
+ , m_quickWidget(new StudioQuickWidget(this))
, m_previewImageProvider(new PreviewImageProvider())
{
QImage defaultImage;
defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png"));
- m_textureImageProvider = new PropertyEditorImageProvider(imageCache, defaultImage);
+ m_textureImageProvider = new AssetImageProvider(imageCache, defaultImage);
setWindowTitle(tr("Material Browser", "Title of material browser widget"));
setMinimumWidth(120);
@@ -162,21 +158,16 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
m_context->setContext(context);
m_context->setWidget(this);
+ m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_MATERIAL_BROWSER);
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
- m_quickWidget->rootContext()->setContextProperties({
- {"rootView", QVariant::fromValue(this)},
- {"materialBrowserModel", QVariant::fromValue(m_materialBrowserModel.data())},
- {"materialBrowserTexturesModel", QVariant::fromValue(m_materialBrowserTexturesModel.data())},
- });
-
m_quickWidget->engine()->addImageProvider("materialBrowser", m_previewImageProvider);
m_quickWidget->engine()->addImageProvider("materialBrowserTex", m_textureImageProvider);
Theme::setupTheme(m_quickWidget->engine());
- m_quickWidget->installEventFilter(this);
+ m_quickWidget->quickWidget()->installEventFilter(this);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
@@ -203,7 +194,17 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MATERIALBROWSER_TIME);
+ auto map = m_quickWidget->registerPropertyMap("MaterialBrowserBackend");
+
+ map->setProperties({
+ {"rootView", QVariant::fromValue(this)},
+ {"materialBrowserModel", QVariant::fromValue(m_materialBrowserModel.data())},
+ {"materialBrowserTexturesModel", QVariant::fromValue(m_materialBrowserTexturesModel.data())},
+ });
+
reloadQmlSource();
+
+ setFocusProxy(m_quickWidget.data());
}
void MaterialBrowserWidget::updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap)
@@ -247,22 +248,91 @@ void MaterialBrowserWidget::startDragMaterial(int index, const QPointF &mousePos
{
m_materialToDrag = m_materialBrowserModel->materialAt(index);
m_dragStartPoint = mousePos.toPoint();
+
+ setIsDragging(true);
}
void MaterialBrowserWidget::startDragTexture(int index, const QPointF &mousePos)
{
m_textureToDrag = m_materialBrowserTexturesModel->textureAt(index);
m_dragStartPoint = mousePos.toPoint();
+
+ setIsDragging(true);
}
void MaterialBrowserWidget::acceptBundleMaterialDrop()
{
m_materialBrowserView->emitCustomNotification("drop_bundle_material", {}, {}); // To ContentLibraryView
+ if (m_materialBrowserView->model())
+ m_materialBrowserView->model()->endDrag();
+}
+
+bool MaterialBrowserWidget::hasAcceptableAssets(const QList<QUrl> &urls)
+{
+ return Utils::anyOf(urls, [](const QUrl &url) {
+ return Asset(url.toLocalFile()).isValidTextureSource();
+ });
}
void MaterialBrowserWidget::acceptBundleTextureDrop()
{
m_materialBrowserView->emitCustomNotification("drop_bundle_texture", {}, {}); // To ContentLibraryView
+ if (m_materialBrowserView->model())
+ m_materialBrowserView->model()->endDrag();
+}
+
+void MaterialBrowserWidget::acceptBundleTextureDropOnMaterial(int matIndex, const QUrl &bundleTexPath)
+{
+ ModelNode mat = m_materialBrowserModel->materialAt(matIndex);
+ QTC_ASSERT(mat.isValid(), return);
+
+ auto *creator = new CreateTexture(m_materialBrowserView);
+
+ m_materialBrowserView->executeInTransaction(__FUNCTION__, [&] {
+ ModelNode tex = creator->execute(bundleTexPath.toLocalFile());
+ QTC_ASSERT(tex.isValid(), return);
+
+ m_materialBrowserModel->selectMaterial(matIndex);
+ m_materialBrowserView->applyTextureToMaterial({mat}, tex);
+ });
+
+ if (m_materialBrowserView->model())
+ m_materialBrowserView->model()->endDrag();
+
+ creator->deleteLater();
+}
+
+void MaterialBrowserWidget::acceptAssetsDrop(const QList<QUrl> &urls)
+{
+ QStringList assetPaths = Utils::transform(urls, [](const QUrl &url) { return url.toLocalFile(); });
+ m_materialBrowserView->createTextures(assetPaths);
+ if (m_materialBrowserView->model())
+ m_materialBrowserView->model()->endDrag();
+}
+
+void MaterialBrowserWidget::acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls)
+{
+ ModelNode mat = m_materialBrowserModel->materialAt(matIndex);
+ QTC_ASSERT(mat.isValid(), return);
+
+ auto *creator = new CreateTexture(m_materialBrowserView);
+
+ QString imageSrc = Utils::findOrDefault(urls, [] (const QUrl &url) {
+ return Asset(url.toLocalFile()).isValidTextureSource();
+ }).toLocalFile();
+
+ m_materialBrowserView->executeInTransaction(__FUNCTION__, [&] {
+ ModelNode tex = creator->execute(imageSrc);
+ QTC_ASSERT(tex.isValid(), return);
+
+ m_materialBrowserModel->selectMaterial(matIndex);
+ m_materialBrowserView->applyTextureToMaterial({mat}, tex);
+ });
+
+ if (m_materialBrowserView->model())
+ m_materialBrowserView->model()->endDrag();
+
+ creator->deleteLater();
}
void MaterialBrowserWidget::acceptTextureDropOnMaterial(int matIndex, const QString &texId)
@@ -274,6 +344,9 @@ void MaterialBrowserWidget::acceptTextureDropOnMaterial(int matIndex, const QStr
m_materialBrowserModel->selectMaterial(matIndex);
m_materialBrowserView->applyTextureToMaterial({mat}, tex);
}
+
+ if (m_materialBrowserView->model())
+ m_materialBrowserView->model()->endDrag();
}
void MaterialBrowserWidget::focusMaterialSection(bool focusMatSec)
@@ -304,7 +377,6 @@ void MaterialBrowserWidget::reloadQmlSource()
QTC_ASSERT(QFileInfo::exists(materialBrowserQmlPath), return);
- m_quickWidget->engine()->clearComponentCache();
m_quickWidget->setSource(QUrl::fromLocalFile(materialBrowserQmlPath));
}
@@ -315,7 +387,15 @@ void MaterialBrowserWidget::updateSearch()
m_quickWidget->update();
}
-QQuickWidget *MaterialBrowserWidget::quickWidget() const
+void MaterialBrowserWidget::setIsDragging(bool val)
+{
+ if (m_isDragging != val) {
+ m_isDragging = val;
+ emit isDraggingChanged();
+ }
+}
+
+StudioQuickWidget *MaterialBrowserWidget::quickWidget() const
{
return m_quickWidget.data();
}
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
index 2a3d36fe0a..ea0e5a212a 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
@@ -6,29 +6,24 @@
#include "modelnode.h"
#include <coreplugin/icontext.h>
-#include <utils/dropsupport.h>
-#include <utils/fancylineedit.h>
-#include <QFileIconProvider>
#include <QFrame>
-#include <QQmlPropertyMap>
-
-#include <memory>
QT_BEGIN_NAMESPACE
-class QQuickWidget;
class QPointF;
class QShortcut;
class QToolButton;
QT_END_NAMESPACE
+class StudioQuickWidget;
+
namespace QmlDesigner {
+class AssetImageProvider;
class MaterialBrowserView;
class MaterialBrowserModel;
class MaterialBrowserTexturesModel;
class PreviewImageProvider;
-class PropertyEditorImageProvider;
class MaterialBrowserWidget : public QFrame
{
@@ -36,6 +31,9 @@ class MaterialBrowserWidget : public QFrame
Q_PROPERTY(bool materialSectionFocused MEMBER m_materialSectionFocused NOTIFY materialSectionFocusedChanged)
+ // 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)
+
public:
MaterialBrowserWidget(class AsynchronousImageCache &imageCache, MaterialBrowserView *view);
~MaterialBrowserWidget() = default;
@@ -55,16 +53,21 @@ public:
Q_INVOKABLE void startDragMaterial(int index, const QPointF &mousePos);
Q_INVOKABLE void startDragTexture(int index, const QPointF &mousePos);
Q_INVOKABLE void acceptBundleMaterialDrop();
+ Q_INVOKABLE bool hasAcceptableAssets(const QList<QUrl> &urls);
Q_INVOKABLE void acceptBundleTextureDrop();
+ Q_INVOKABLE void acceptBundleTextureDropOnMaterial(int matIndex, const QUrl &bundleTexPath);
+ Q_INVOKABLE void acceptAssetsDrop(const QList<QUrl> &urls);
+ Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls);
Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId);
Q_INVOKABLE void focusMaterialSection(bool focusMatSec);
- QQuickWidget *quickWidget() const;
+ StudioQuickWidget *quickWidget() const;
void clearPreviewCache();
signals:
void materialSectionFocusedChanged();
+ void isDraggingChanged();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -73,14 +76,16 @@ private:
void reloadQmlSource();
void updateSearch();
+ void setIsDragging(bool val);
+
QPointer<MaterialBrowserView> m_materialBrowserView;
QPointer<MaterialBrowserModel> m_materialBrowserModel;
QPointer<MaterialBrowserTexturesModel> m_materialBrowserTexturesModel;
- QScopedPointer<QQuickWidget> m_quickWidget;
+ QScopedPointer<StudioQuickWidget> m_quickWidget;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
PreviewImageProvider *m_previewImageProvider = nullptr;
- PropertyEditorImageProvider *m_textureImageProvider = nullptr;
+ AssetImageProvider *m_textureImageProvider = nullptr;
Core::IContext *m_context = nullptr;
QString m_filterText;
@@ -90,6 +95,7 @@ private:
QPoint m_dragStartPoint;
bool m_materialSectionFocused = true;
+ bool m_isDragging = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp
new file mode 100644
index 0000000000..76924bedbc
--- /dev/null
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp
@@ -0,0 +1,71 @@
+// 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 "materialutils.h"
+
+#include "abstractview.h"
+#include "nodelistproperty.h"
+#include "nodemetainfo.h"
+#include "qmlobjectnode.h"
+#include "variantproperty.h"
+
+#include <utils/qtcassert.h>
+
+namespace QmlDesigner {
+
+// Assigns given material to a 3D model.
+// The assigned material is also inserted into material library if not already there.
+// If given material is not valid, first existing material from material library is used,
+// or if material library is empty, a new material is created.
+// This function should be called only from inside a transaction, as it potentially does many
+// changes to model.
+void MaterialUtils::assignMaterialTo3dModel(AbstractView *view, const ModelNode &modelNode,
+ const ModelNode &materialNode)
+{
+ QTC_ASSERT(modelNode.isValid() && modelNode.metaInfo().isQtQuick3DModel(), return);
+
+ ModelNode matLib = view->materialLibraryNode();
+
+ if (!matLib.isValid())
+ return;
+
+ ModelNode newMaterialNode;
+
+ if (materialNode.isValid() && materialNode.metaInfo().isQtQuick3DMaterial()) {
+ newMaterialNode = materialNode;
+ } else {
+ const QList<ModelNode> materials = matLib.directSubModelNodes();
+ if (materials.size() > 0) {
+ for (const ModelNode &mat : materials) {
+ if (mat.metaInfo().isQtQuick3DMaterial()) {
+ newMaterialNode = mat;
+ break;
+ }
+ }
+ }
+
+ // if no valid material, create a new default material
+ if (!newMaterialNode.isValid()) {
+ NodeMetaInfo metaInfo = view->model()->qtQuick3DPrincipledMaterialMetaInfo();
+ newMaterialNode = view->createModelNode("QtQuick3D.PrincipledMaterial",
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
+ newMaterialNode.validId();
+ }
+ }
+
+ QTC_ASSERT(newMaterialNode.isValid(), return);
+
+ VariantProperty matNameProp = newMaterialNode.variantProperty("objectName");
+ if (matNameProp.value().isNull())
+ matNameProp.setValue("New Material");
+
+ if (!newMaterialNode.hasParentProperty()
+ || newMaterialNode.parentProperty() != matLib.defaultNodeListProperty()) {
+ matLib.defaultNodeListProperty().reparentHere(newMaterialNode);
+ }
+
+ QmlObjectNode(modelNode).setBindingProperty("materials", newMaterialNode.id());
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialutils.h b/src/plugins/qmldesigner/components/materialbrowser/materialutils.h
new file mode 100644
index 0000000000..e273ef74a4
--- /dev/null
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialutils.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "modelnode.h"
+
+namespace QmlDesigner {
+
+class AbstractView;
+
+class MaterialUtils
+{
+public:
+ MaterialUtils();
+
+ static void assignMaterialTo3dModel(AbstractView *view, const ModelNode &modelNode,
+ const ModelNode &materialNode = {});
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.cpp
index 147ecd1d5f..c59ca61ff1 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.cpp
@@ -29,7 +29,7 @@
#include <materialeditorview.h>
-using namespace QmlDesigner;
+namespace QmlDesigner {
MaterialEditorDynamicPropertiesProxyModel::MaterialEditorDynamicPropertiesProxyModel(QObject *parent)
: DynamicPropertiesProxyModel(parent)
@@ -43,3 +43,5 @@ void MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType()
DynamicPropertiesProxyModel::registerDeclarativeType();
qmlRegisterType<MaterialEditorDynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "MaterialEditorDynamicPropertiesModel");
}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.h b/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.h
index 9d4e6aa9a4..d2add25c65 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditordynamicpropertiesproxymodel.h
@@ -27,6 +27,8 @@
#include "dynamicpropertiesproxymodel.h"
+namespace QmlDesigner {
+
class MaterialEditorDynamicPropertiesProxyModel : public DynamicPropertiesProxyModel
{
Q_OBJECT
@@ -35,3 +37,5 @@ public:
static void registerDeclarativeType();
};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
index c08e0c65c2..edae2377bf 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
@@ -84,6 +84,7 @@ MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialE
, m_contextObject(new MaterialEditorContextObject(m_view->rootContext()))
, m_materialEditorImageProvider(new MaterialEditorImageProvider())
{
+ m_view->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR);
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_view->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider);
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditortransaction.h b/src/plugins/qmldesigner/components/materialeditor/materialeditortransaction.h
index ba5ee7672e..aeda758809 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditortransaction.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditortransaction.h
@@ -4,6 +4,7 @@
#pragma once
#include "materialeditorview.h"
+#include "rewritertransaction.h"
namespace QmlDesigner {
@@ -12,7 +13,7 @@ class MaterialEditorTransaction : public QObject
Q_OBJECT
public:
- MaterialEditorTransaction(QmlDesigner::MaterialEditorView *materialEditor);
+ MaterialEditorTransaction(MaterialEditorView *materialEditor);
Q_INVOKABLE void start();
Q_INVOKABLE void end();
@@ -23,8 +24,8 @@ protected:
void timerEvent(QTimerEvent *event) override;
private:
- QmlDesigner::MaterialEditorView *m_materialEditor = nullptr;
- QmlDesigner::RewriterTransaction m_rewriterTransaction;
+ MaterialEditorView *m_materialEditor = nullptr;
+ RewriterTransaction m_rewriterTransaction;
int m_timerId = -1;
};
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
index b174caeb6b..47d47985eb 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
@@ -3,48 +3,39 @@
#include "materialeditorview.h"
+#include "asset.h"
+#include "bindingproperty.h"
+#include "auxiliarydataproperties.h"
+#include "designdocument.h"
+#include "designmodewidget.h"
+#include "dynamicpropertiesmodel.h"
+#include "itemlibraryinfo.h"
#include "materialeditorqmlbackend.h"
#include "materialeditorcontextobject.h"
#include "materialeditordynamicpropertiesproxymodel.h"
-#include "propertyeditorvalue.h"
#include "materialeditortransaction.h"
-#include "assetslibrarywidget.h"
-
-#include <auxiliarydataproperties.h>
-#include <bindingproperty.h>
-#include <dynamicpropertiesmodel.h>
-#include <metainfo.h>
-#include <nodeinstanceview.h>
-#include <nodelistproperty.h>
-#include <nodemetainfo.h>
-#include <nodeproperty.h>
-#include <rewritingexception.h>
-#include <variantproperty.h>
-#include <qmldesignerconstants.h>
-#include <qmldesignerplugin.h>
-#include <qmltimeline.h>
-
-#include <theme.h>
+#include "metainfo.h"
+#include "nodeinstanceview.h"
+#include "nodelistproperty.h"
+#include "nodemetainfo.h"
+#include "propertyeditorqmlbackend.h"
+#include "propertyeditorvalue.h"
+#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
+#include "qmltimeline.h"
+#include "variantproperty.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
-#include <designmodewidget.h>
-#include <propertyeditorqmlbackend.h>
+
#include <utils/environment.h>
-#include <utils/fileutils.h>
#include <utils/qtcassert.h>
-#include <qmldesignerplugin.h>
-#include <QApplication>
-#include <QDebug>
#include <QDir>
#include <QFileInfo>
-#include <QQuickWidget>
#include <QQuickItem>
-#include <QScopedPointer>
#include <QStackedWidget>
#include <QShortcut>
-#include <QTimer>
#include <QColorDialog>
namespace QmlDesigner {
@@ -52,7 +43,7 @@ namespace QmlDesigner {
MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies}
, m_stackedWidget(new QStackedWidget)
- , m_dynamicPropertiesModel(new Internal::DynamicPropertiesModel(true, this))
+ , m_dynamicPropertiesModel(new DynamicPropertiesModel(true, this))
{
m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F7), m_stackedWidget);
connect(m_updateShortcut, &QShortcut::activated, this, &MaterialEditorView::reloadQml);
@@ -60,7 +51,9 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe
m_ensureMatLibTimer.callOnTimeout([this] {
if (model() && model()->rewriterView() && !model()->rewriterView()->hasIncompleteTypeInformation()
&& model()->rewriterView()->errors().isEmpty()) {
- ensureMaterialLibraryNode();
+ DesignDocument *doc = QmlDesignerPlugin::instance()->currentDesignDocument();
+ if (doc && !doc->inFileComponentModelActive())
+ ensureMaterialLibraryNode();
if (m_qmlBackEnd && m_qmlBackEnd->contextObject())
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
m_ensureMatLibTimer.stop();
@@ -71,8 +64,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe
m_typeUpdateTimer.setInterval(500);
connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes);
- m_stackedWidget->setStyleSheet(Theme::replaceCssColors(
- QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
m_stackedWidget->setMinimumWidth(250);
QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME);
@@ -180,7 +171,7 @@ void MaterialEditorView::changeExpression(const QString &propertyName)
if (name.isNull() || locked() || noValidSelection())
return;
- executeInTransaction("MaterialEditorView::changeExpression", [this, name] {
+ executeInTransaction(__FUNCTION__, [this, name] {
PropertyName underscoreName(name);
underscoreName.replace('.', '_');
@@ -254,7 +245,7 @@ void MaterialEditorView::exportPropertyAsAlias(const QString &name)
if (name.isNull() || locked() || noValidSelection())
return;
- executeInTransaction("MaterialEditorView::exportPopertyAsAlias", [this, name] {
+ executeInTransaction(__FUNCTION__, [this, name] {
const QString id = m_selectedMaterial.validId();
QString upperCasePropertyName = name;
upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper());
@@ -276,7 +267,7 @@ void MaterialEditorView::removeAliasExport(const QString &name)
if (name.isNull() || locked() || noValidSelection())
return;
- executeInTransaction("MaterialEditorView::removeAliasExport", [this, name] {
+ executeInTransaction(__FUNCTION__, [this, name] {
const QString id = m_selectedMaterial.validId();
const QList<BindingProperty> bindingProps = rootModelNode().bindingProperties();
@@ -299,7 +290,7 @@ void MaterialEditorView::currentTimelineChanged(const ModelNode &)
m_qmlBackEnd->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this));
}
-Internal::DynamicPropertiesModel *MaterialEditorView::dynamicPropertiesModel() const
+DynamicPropertiesModel *MaterialEditorView::dynamicPropertiesModel() const
{
return m_dynamicPropertiesModel;
}
@@ -389,7 +380,7 @@ void MaterialEditorView::applyMaterialToSelectedModels(const ModelNode &material
return QString();
};
- executeInTransaction("MaterialEditorView::applyMaterialToSelectedModels", [&] {
+ executeInTransaction(__FUNCTION__, [&] {
for (const ModelNode &node : std::as_const(m_selectedModels)) {
QmlObjectNode qmlObjNode(node);
if (add) {
@@ -422,14 +413,15 @@ void MaterialEditorView::handleToolBarAction(int action)
case MaterialEditorContextObject::AddNewMaterial: {
if (!model())
break;
- executeInTransaction("MaterialEditorView:handleToolBarAction", [&] {
+ executeInTransaction(__FUNCTION__, [&] {
ModelNode matLib = materialLibraryNode();
if (!matLib.isValid())
return;
- NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.DefaultMaterial");
- ModelNode newMatNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
- metaInfo.minorVersion());
+ NodeMetaInfo metaInfo = model()->qtQuick3DPrincipledMaterialMetaInfo();
+ ModelNode newMatNode = createModelNode("QtQuick3D.PrincipledMaterial",
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
renameMaterial(newMatNode, "New Material");
matLib.defaultNodeListProperty().reparentHere(newMatNode);
});
@@ -451,7 +443,6 @@ void MaterialEditorView::handleToolBarAction(int action)
void MaterialEditorView::handlePreviewEnvChanged(const QString &envAndValue)
{
- Q_UNUSED(envAndValue);
if (envAndValue.isEmpty() || m_initializingPreviewData)
return;
@@ -514,7 +505,6 @@ void MaterialEditorView::handlePreviewEnvChanged(const QString &envAndValue)
void MaterialEditorView::handlePreviewModelChanged(const QString &modelStr)
{
- Q_UNUSED(modelStr);
if (modelStr.isEmpty() || m_initializingPreviewData)
return;
@@ -609,7 +599,7 @@ void MaterialEditorView::setupQmlBackend()
void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value)
{
m_locked = true;
- executeInTransaction("MaterialEditorView:commitVariantValueToModel", [&] {
+ executeInTransaction(__FUNCTION__, [&] {
QmlObjectNode(m_selectedMaterial).setVariantProperty(propertyName, value);
});
m_locked = false;
@@ -637,7 +627,7 @@ void MaterialEditorView::commitAuxValueToModel(const PropertyName &propertyName,
void MaterialEditorView::removePropertyFromModel(const PropertyName &propertyName)
{
m_locked = true;
- executeInTransaction("MaterialEditorView:removePropertyFromModel", [&] {
+ executeInTransaction(__FUNCTION__, [&] {
QmlObjectNode(m_selectedMaterial).removeProperty(propertyName);
});
m_locked = false;
@@ -692,6 +682,7 @@ 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()));
@@ -908,7 +899,8 @@ WidgetInfo MaterialEditorView::widgetInfo()
"MaterialEditor",
WidgetInfo::RightPane,
0,
- tr("Material Editor"));
+ tr("Material Editor"),
+ tr("Material Editor view"));
}
void MaterialEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
@@ -928,6 +920,7 @@ void MaterialEditorView::currentStateChanged(const ModelNode &node)
{
QmlModelState newQmlModelState(node);
Q_ASSERT(newQmlModelState.isValid());
+
resetView();
}
@@ -1001,7 +994,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN
if (objName.isValid() && objName.toString() == newName)
return;
- executeInTransaction("MaterialEditorView:renameMaterial", [&] {
+ executeInTransaction(__FUNCTION__, [&] {
material.setIdWithRefactoring(model()->generateIdFromName(newName, "material"));
VariantProperty objNameProp = material.variantProperty("objectName");
@@ -1034,7 +1027,8 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
// set name and id
QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy";
- duplicateMatNode.variantProperty("objectName").setValue(newName);
+ VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName");
+ objNameProp.setValue(newName);
duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material"));
// sync properties. Only the base state is duplicated.
@@ -1047,15 +1041,15 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
if (prop.isDynamic()) {
dynamicProps.append(prop);
} else {
- duplicateMatNode.variantProperty(prop.name())
- .setValue(prop.toVariantProperty().value());
+ VariantProperty variantProp = duplicateMatNode.variantProperty(prop.name());
+ variantProp.setValue(prop.toVariantProperty().value());
}
} else if (prop.isBindingProperty()) {
if (prop.isDynamic()) {
dynamicProps.append(prop);
} else {
- duplicateMatNode.bindingProperty(prop.name())
- .setExpression(prop.toBindingProperty().expression());
+ BindingProperty bindingProp = duplicateMatNode.bindingProperty(prop.name());
+ bindingProp.setExpression(prop.toBindingProperty().expression());
}
}
}
@@ -1070,13 +1064,13 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
executeInTransaction(__FUNCTION__, [&] {
for (const AbstractProperty &prop : std::as_const(dynamicProps)) {
if (prop.isVariantProperty()) {
- duplicateMatNode.variantProperty(prop.name())
- .setDynamicTypeNameAndValue(prop.dynamicTypeName(),
- prop.toVariantProperty().value());
+ VariantProperty variantProp = duplicateMatNode.variantProperty(prop.name());
+ variantProp.setDynamicTypeNameAndValue(prop.dynamicTypeName(),
+ prop.toVariantProperty().value());
} else if (prop.isBindingProperty()) {
- duplicateMatNode.bindingProperty(prop.name())
- .setDynamicTypeNameAndExpression(prop.dynamicTypeName(),
- prop.toBindingProperty().expression());
+ BindingProperty bindingProp = duplicateMatNode.bindingProperty(prop.name());
+ bindingProp.setDynamicTypeNameAndExpression(prop.dynamicTypeName(),
+ prop.toBindingProperty().expression());
}
}
});
@@ -1144,13 +1138,14 @@ void MaterialEditorView::dragStarted(QMimeData *mimeData)
{
if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) {
const QString assetPath = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',')[0];
- QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first;
+ Asset asset(assetPath);
- if (assetType != Constants::MIME_TYPE_ASSET_IMAGE) // currently only image assets have dnd-supported properties
+ if (!asset.isValidTextureSource()) // currently only image assets have dnd-supported properties
return;
highlightSupportedProperties();
- } else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)
+ || mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
highlightSupportedProperties();
}
}
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
index 77053c5db6..d47d030e57 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
@@ -3,28 +3,24 @@
#pragma once
-#include <abstractview.h>
-#include <itemlibraryinfo.h>
+#include "abstractview.h"
+#include "modelnode.h"
#include <QHash>
#include <QPointer>
#include <QTimer>
QT_BEGIN_NAMESPACE
+class QColorDialog;
class QShortcut;
class QStackedWidget;
-class QTimer;
-class QColorDialog;
QT_END_NAMESPACE
namespace QmlDesigner {
-class ModelNode;
-class MaterialEditorQmlBackend;
-
-namespace Internal {
class DynamicPropertiesModel;
-}
+class ItemLibraryInfo;
+class MaterialEditorQmlBackend;
class MaterialEditorView : public AbstractView
{
@@ -79,7 +75,7 @@ public:
void currentTimelineChanged(const ModelNode &node) override;
- Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
+ DynamicPropertiesModel *dynamicPropertiesModel() const;
static MaterialEditorView *instance();
@@ -133,7 +129,7 @@ private:
QPointer<QColorDialog> m_colorDialog;
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
- Internal::DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
+ DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
index 836e473f8a..35078859de 100644
--- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
+++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
@@ -22,6 +22,7 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
// -> SpriteParticle3D
// -> TextureInput
// -> SceneEnvironment
+ // -> Model
// Effect
// -> SceneEnvironment
// Shader, Command, Buffer
@@ -41,6 +42,8 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
// -> Model
// Effect
// -> Item
+ // BakedLightmap
+ // -> Model
if (insertInfo.isQtQuick3DTexture()) {
if (parentInfo.isQtQuick3DDefaultMaterial() || parentInfo.isQtQuick3DPrincipledMaterial()
@@ -63,6 +66,8 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
propertyList.append("skyBoxCubeMap");
else
propertyList.append("lightProbe");
+ } else if (parentInfo.isQtQuick3DModel()) {
+ propertyList.append("materials");
}
} else if (insertInfo.isQtQuick3DEffect()) {
if (parentInfo.isQtQuick3DSceneEnvironment())
@@ -98,6 +103,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
} else if (insertInfo.isEffectMaker()) {
if (parentInfo.isQtQuickItem())
propertyList.append("effect");
+ } else if (insertInfo.isQtQuick3DBakedLightmap()) {
+ if (parentInfo.isQtQuick3DModel())
+ propertyList.append("bakedLightmap");
}
}
diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
index 9f2f0854b3..e9fa8b4925 100644
--- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
+++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
@@ -13,6 +13,7 @@
#include <metainfo.h>
#include <modelnodecontextmenu.h>
+#include <qmldesignerconstants.h>
#include <qmlobjectnode.h>
#include <theme.h>
@@ -216,17 +217,28 @@ void NameItemDelegate::paint(QPainter *painter,
if (widget && !widget->dragType().isEmpty()) {
QByteArray dragType = widget->dragType();
const NodeMetaInfo metaInfo = node.metaInfo();
- const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType);
- ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true);
- if (!filter->propertyList.isEmpty()) {
+ bool validDrop = false;
+ if (dragType == Constants::MIME_TYPE_BUNDLE_TEXTURE) {
+ validDrop = metaInfo.isQtQuick3DModel();
+ } else if (dragType == Constants::MIME_TYPE_ASSET_TEXTURE3D) {
+ validDrop = metaInfo.isQtQuick3DModel() || metaInfo.isQtQuick3DTexture();
+ } else if (dragType == Constants::MIME_TYPE_ASSET_IMAGE) {
+ validDrop = metaInfo.isQtQuick3DModel() || metaInfo.isQtQuick3DTexture()
+ || metaInfo.isQtQuickImage() || metaInfo.isQtQuickBorderImage();
+ } else {
+ const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType);
+ ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true);
+ validDrop = !filter->propertyList.isEmpty();
+ delete filter;
+ }
+ if (validDrop) {
painter->setOpacity(0.5);
painter->fillRect(styleOption.rect.adjusted(0, delegateMargin, 0, -delegateMargin),
Theme::getColor(Theme::Color::DSnavigatorDropIndicatorBackground));
painter->setOpacity(1.0);
painter->setPen(Theme::getColor(Theme::Color::DSnavigatorTextSelected));
}
- delete filter;
}
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
index 50dd9e499d..360c893c2d 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
@@ -20,25 +20,22 @@ LineEdit::LineEdit(QWidget *parent)
clearButton = new QToolButton(this);
const QString fontName = "qtds_propertyIconFont.ttf";
- const int searchIconSize = 16;
- const int clearIconSize = 12;
- const QColor iconColor(QmlDesigner::Theme::getColor(QmlDesigner::Theme::DSiconColor));
-
- const QIcon searchIcon
- = Utils::StyleHelper::getIconFromIconFont(fontName,
- QmlDesigner::Theme::getIconUnicode(
- QmlDesigner::Theme::Icon::search),
- searchIconSize,
- searchIconSize,
- iconColor);
-
- const QIcon clearIcon
- = Utils::StyleHelper::getIconFromIconFont(fontName,
- QmlDesigner::Theme::getIconUnicode(
- QmlDesigner::Theme::Icon::closeCross),
- clearIconSize,
- clearIconSize,
- iconColor);
+ const int clearIconSize = 10;
+ const QColor iconColor(QmlDesigner::Theme::getColor(QmlDesigner::Theme::DStextColor));
+
+ const QIcon searchIcon = Utils::StyleHelper::getIconFromIconFont(
+ fontName,
+ QmlDesigner::Theme::getIconUnicode(QmlDesigner::Theme::Icon::search_small),
+ 10,
+ 16,
+ iconColor);
+
+ const QIcon clearIcon = Utils::StyleHelper::getIconFromIconFont(
+ fontName,
+ QmlDesigner::Theme::getIconUnicode(QmlDesigner::Theme::Icon::close_small),
+ clearIconSize,
+ clearIconSize,
+ iconColor);
addAction(searchIcon, QLineEdit::LeadingPosition);
@@ -46,9 +43,9 @@ LineEdit::LineEdit(QWidget *parent)
clearButton->setIconSize(QSize(clearIconSize, clearIconSize));
clearButton->setCursor(Qt::ArrowCursor);
clearButton->hide();
- clearButton->setStyleSheet(Theme::replaceCssColors(
- QString("QToolButton { border: none; padding: 0px; }"
- "QToolButton:hover { background: creatorTheme.DScontrolBackgroundHover; }")));
+ clearButton->setStyleSheet(
+ Theme::replaceCssColors(QString("QToolButton { border: none; padding: 0px; }"
+ "QToolButton:hover {}")));
setClearButtonEnabled(false);
@@ -56,8 +53,20 @@ LineEdit::LineEdit(QWidget *parent)
connect(this, &QLineEdit::textChanged, this, &LineEdit::updateClearButton);
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
- setStyleSheet(QString("QLineEdit { padding-right: %1px; } ")
- .arg(clearButton->sizeHint().width() + frameWidth + 8));
+ setStyleSheet(Theme::replaceCssColors(
+ QString("QLineEdit { padding-right: %1px; border-radius: 4;"
+ "color: creatorTheme.DStextColor;"
+ "border-color: creatorTheme.DScontrolOutline_topToolbarIdle;"
+ "background: creatorTheme.DStoolbarBackground; }"
+ "QLineEdit:hover {"
+ "color: creatorTheme.DStextColor;"
+ "border-color: creatorTheme.DScontrolOutline_topToolbarHover;"
+ "background: creatorTheme.DScontrolBackground_toolbarHover; }"
+ "QLineEdit:focus {"
+ "color: creatorTheme.DStextColor;"
+ "border-color: creatorTheme.DSinteraction;"
+ "background: creatorTheme.DStoolbarBackground; }")
+ .arg(clearButton->sizeHint().width() + frameWidth + 8)));
setFixedHeight(29);
}
@@ -81,7 +90,7 @@ NavigatorSearchWidget::NavigatorSearchWidget(QWidget *parent)
{
auto layout = new QBoxLayout(QBoxLayout::LeftToRight);
layout->setSpacing(0);
- layout->setContentsMargins(5, 5, 5, 3);
+ layout->setContentsMargins(10, 6, 10, 6);
setLayout(layout);
m_textField = new LineEdit;
@@ -91,6 +100,12 @@ NavigatorSearchWidget::NavigatorSearchWidget(QWidget *parent)
connect(m_textField, &QLineEdit::textChanged, this, &NavigatorSearchWidget::textChanged);
layout->addWidget(m_textField);
+
+ setFixedHeight(Theme::toolbarSize());
+ QPalette pal = QPalette();
+ pal.setColor(QPalette::Window, Theme::getColor(Utils::Theme::DStoolbarBackground));
+ setAutoFillBackground(true);
+ setPalette(pal);
}
void NavigatorSearchWidget::clear()
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index ccf9bafd9a..6286829028 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -17,6 +17,7 @@
#include <nodeproperty.h>
#include <variantproperty.h>
#include <metainfo.h>
+#include <materialutils.h>
#include <abstractview.h>
#include <invalididexception.h>
#include <rewritingexception.h>
@@ -244,7 +245,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
if (role == Qt::CheckStateRole)
return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked;
else if (role == Qt::ToolTipRole && !modelNodeForIndex(index).isRootNode())
- return tr("Toggles the visibility of this component in the 2D view.\n"
+ return tr("Toggles the visibility of this component in the 2D and 3D views.\n"
"This is independent of the visibility property.");
} else if (index.column() == ColumnType::Lock) { // lock
if (role == Qt::CheckStateRole)
@@ -451,7 +452,9 @@ QStringList NavigatorTreeModel::mimeTypes() const
{
const static QStringList types({Constants::MIME_TYPE_MODELNODE_LIST,
Constants::MIME_TYPE_ITEM_LIBRARY_INFO,
+ Constants::MIME_TYPE_TEXTURE,
Constants::MIME_TYPE_MATERIAL,
+ Constants::MIME_TYPE_BUNDLE_TEXTURE,
Constants::MIME_TYPE_BUNDLE_MATERIAL,
Constants::MIME_TYPE_ASSETS});
@@ -548,8 +551,15 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
if (dropModelIndex.model() == this) {
if (mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) {
handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex);
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
+ handleTextureDrop(mimeData, dropModelIndex);
} else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) {
- handleMaterialDrop(mimeData, rowNumber, dropModelIndex);
+ handleMaterialDrop(mimeData, dropModelIndex);
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
+ QByteArray filePath = mimeData->data(Constants::MIME_TYPE_BUNDLE_TEXTURE);
+ ModelNode targetNode(modelNodeForIndex(dropModelIndex));
+ if (targetNode.metaInfo().isQtQuick3DModel())
+ m_view->emitCustomNotification("apply_asset_to_model3D", {targetNode}, {filePath}); // To MaterialBrowserView
} else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) {
ModelNode targetNode(modelNodeForIndex(dropModelIndex));
if (targetNode.isValid())
@@ -627,7 +637,10 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
}
}
- return false; // don't let the view do drag&drop on its own
+ if (m_view && m_view->model())
+ m_view->model()->endDrag();
+
+ return true;
}
void NavigatorTreeModel::handleInternalDrop(const QMimeData *mimeData,
@@ -698,7 +711,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
newQmlObjectNode.destroy();
return;
}
- m_view->assignMaterialTo3dModel(targetNode, newModelNode);
+ MaterialUtils::assignMaterialTo3dModel(m_view, targetNode, newModelNode);
}
ChooseFromPropertyListDialog *dialog = ChooseFromPropertyListDialog::createIfNeeded(
@@ -739,9 +752,9 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
const QList<ModelNode> models = newModelNode.subModelNodesOfType(
m_view->model()->qtQuick3DModelMetaInfo());
QTC_ASSERT(models.size() == 1, return);
- m_view->assignMaterialTo3dModel(models.at(0));
+ MaterialUtils::assignMaterialTo3dModel(m_view, models.at(0));
} else if (newModelNode.metaInfo().isQtQuick3DModel()) {
- m_view->assignMaterialTo3dModel(newModelNode);
+ MaterialUtils::assignMaterialTo3dModel(m_view, newModelNode);
}
if (!validContainer) {
@@ -781,19 +794,42 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
}
}
-void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex)
+void NavigatorTreeModel::handleTextureDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex)
{
QTC_ASSERT(m_view, return);
const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
- int targetRowNumber = rowNumber;
- NodeAbstractProperty targetProperty;
-
- bool foundTarget = findTargetProperty(rowModelIndex, this, &targetProperty, &targetRowNumber, "materials");
- if (!foundTarget)
+ QmlObjectNode targetNode = modelNodeForIndex(rowModelIndex);
+ if (!targetNode.isValid())
return;
- ModelNode targetNode = targetProperty.parentModelNode();
+ qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt();
+ ModelNode texNode = m_view->modelNodeForInternalId(internalId);
+ QTC_ASSERT(texNode.isValid(), return);
+
+ if (targetNode.modelNode().metaInfo().isQtQuick3DModel()) {
+ m_view->emitCustomNotification("apply_texture_to_model3D", {targetNode, texNode});
+ } else {
+ auto *dialog = ChooseFromPropertyListDialog::createIfNeeded(targetNode, texNode, Core::ICore::dialogParent());
+ if (dialog) {
+ bool soloProperty = dialog->isSoloProperty();
+ if (!soloProperty)
+ dialog->exec();
+
+ if (soloProperty || dialog->result() == QDialog::Accepted)
+ targetNode.setBindingProperty(dialog->selectedProperty(), texNode.id());
+
+ delete dialog;
+ }
+ }
+}
+
+void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex)
+{
+ QTC_ASSERT(m_view, return);
+
+ const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
+ ModelNode targetNode = modelNodeForIndex(rowModelIndex);
if (!targetNode.metaInfo().isQtQuick3DModel())
return;
@@ -801,7 +837,7 @@ void NavigatorTreeModel::handleMaterialDrop(const QMimeData *mimeData, int rowNu
ModelNode matNode = m_view->modelNodeForInternalId(internalId);
m_view->executeInTransaction(__FUNCTION__, [&] {
- m_view->assignMaterialTo3dModel(targetNode, matNode);
+ MaterialUtils::assignMaterialTo3dModel(m_view, targetNode, matNode);
});
}
@@ -1088,6 +1124,16 @@ bool NavigatorTreeModel::dropAsImage3dTexture(const ModelNode &targetNode,
// if dropping an image on an existing texture, set the source
targetNode.variantProperty("source").setValue(imagePath);
return true;
+ } else if (targetNode.metaInfo().isQtQuick3DModel()) {
+ QTimer::singleShot(0, this, [this, targetNode, imagePath]() {
+ if (m_view && targetNode.isValid()) {
+ // To MaterialBrowserView. Done async to avoid custom notification in transaction
+ m_view->emitCustomNotification(
+ "apply_asset_to_model3D", {targetNode},
+ {DocumentManager::currentFilePath().absolutePath().pathAppended(imagePath).cleanPath().toString()});
+ }
+ });
+ return true;
}
return false;
@@ -1187,13 +1233,17 @@ Qt::DropActions NavigatorTreeModel::supportedDragActions() const
bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
+ QTC_ASSERT(m_view, return false);
ModelNode modelNode = modelNodeForIndex(index);
if (index.column() == ColumnType::Alias && role == Qt::CheckStateRole) {
- QTC_ASSERT(m_view, return false);
m_view->handleChangedExport(modelNode, value.toInt() != 0);
} else if (index.column() == ColumnType::Visibility && role == Qt::CheckStateRole) {
+ if (m_view->isPartOfMaterialLibrary(modelNode))
+ return false;
QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0);
} else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) {
+ if (m_view->isPartOfMaterialLibrary(modelNode))
+ return false;
modelNode.setLocked(value.toInt() != 0);
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
index 25122a1dd1..ab45294b75 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
@@ -93,7 +93,8 @@ private:
int targetIndex, bool executeInTransaction = true);
void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
- void handleMaterialDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
+ void handleTextureDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex);
+ void handleMaterialDrop(const QMimeData *mimeData, const QModelIndex &dropModelIndex);
ModelNode handleItemLibraryImageDrop(const QString &imagePath, NodeAbstractProperty targetProperty,
const QModelIndex &rowModelIndex, bool &outMoveNodesAfter);
ModelNode handleItemLibraryFontDrop(const QString &fontFamily, NodeAbstractProperty targetProperty,
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
index e6450d4562..38e6b9ada7 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
@@ -29,7 +29,7 @@
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <utils/algorithm.h>
@@ -117,7 +117,8 @@ WidgetInfo NavigatorView::widgetInfo()
QStringLiteral("Navigator"),
WidgetInfo::LeftPane,
0,
- tr("Navigator"));
+ tr("Navigator"),
+ tr("Navigator view"));
}
void NavigatorView::modelAttached(Model *model)
@@ -254,12 +255,21 @@ void NavigatorView::dragStarted(QMimeData *mimeData)
m_widget->setDragType(itemLibraryEntry.typeName());
m_widget->update();
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
+ qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt();
+ ModelNode texNode = modelNodeForInternalId(internalId);
+
+ m_widget->setDragType(texNode.metaInfo().typeName());
+ m_widget->update();
} else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) {
qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt();
ModelNode matNode = modelNodeForInternalId(internalId);
m_widget->setDragType(matNode.metaInfo().typeName());
m_widget->update();
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
+ m_widget->setDragType(Constants::MIME_TYPE_BUNDLE_TEXTURE);
+ m_widget->update();
} else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) {
QByteArray data = mimeData->data(Constants::MIME_TYPE_BUNDLE_MATERIAL);
QDataStream stream(data);
@@ -278,6 +288,12 @@ void NavigatorView::dragStarted(QMimeData *mimeData)
// specific type
m_widget->setDragType(Storage::Info::EffectMaker);
m_widget->update();
+ } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) {
+ m_widget->setDragType(Constants::MIME_TYPE_ASSET_TEXTURE3D);
+ m_widget->update();
+ } else if (assetType == Constants::MIME_TYPE_ASSET_IMAGE) {
+ m_widget->setDragType(Constants::MIME_TYPE_ASSET_IMAGE);
+ m_widget->update();
}
}
}
@@ -454,19 +470,22 @@ const ProjectExplorer::FileNode *NavigatorView::fileNodeForModelNode(const Model
{
QString filename = node.metaInfo().componentFileName();
Utils::FilePath filePath = Utils::FilePath::fromString(filename);
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(
filePath);
if (!currentProject) {
filePath = Utils::FilePath::fromString(node.model()->fileUrl().toLocalFile());
/* If the component does not belong to the project then we can fallback to the current file */
- currentProject = ProjectExplorer::SessionManager::projectForFile(filePath);
+ currentProject = ProjectExplorer::ProjectManager::projectForFile(filePath);
}
if (!currentProject)
return nullptr;
- return currentProject->nodeForFilePath(filePath)->asFileNode();
+ const auto fileNode = currentProject->nodeForFilePath(filePath);
+ QTC_ASSERT(fileNode, return nullptr);
+
+ return fileNode->asFileNode();
}
const ProjectExplorer::FileNode *NavigatorView::fileNodeForIndex(const QModelIndex &index) const
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp
index 991f99a059..9c04438d71 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp
@@ -21,8 +21,9 @@
#include <QToolButton>
#include <utils/fileutils.h>
-#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
+#include <utils/utilsicons.h>
namespace QmlDesigner {
@@ -44,15 +45,17 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view)
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
- QWidget *toolBar = createToolBar();
+ m_searchWidget = new NavigatorSearchWidget();
+ connect(m_searchWidget,
+ &NavigatorSearchWidget::textChanged,
+ this,
+ &NavigatorWidget::textFilterChanged);
+ layout->addWidget(m_searchWidget);
+ QWidget *toolBar = createToolBar();
toolBar->setParent(this);
layout->addWidget(toolBar);
- m_searchWidget = new NavigatorSearchWidget();
- connect(m_searchWidget, &NavigatorSearchWidget::textChanged, this, &NavigatorWidget::textFilterChanged);
- layout->addWidget(m_searchWidget);
-
layout->addWidget(m_treeView);
setLayout(layout);
@@ -63,6 +66,8 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view)
setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet)));
QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_NAVIGATORVIEW_TIME);
+
+ setFocusProxy(m_treeView);
}
void NavigatorWidget::setTreeModel(QAbstractItemModel *model)
@@ -75,73 +80,99 @@ QTreeView *NavigatorWidget::treeView() const
return m_treeView;
}
-QList<QToolButton *> NavigatorWidget::createToolBarWidgets()
+QList<QWidget *> NavigatorWidget::createToolBarWidgets()
{
- QList<QToolButton *> buttons;
+ QList<QWidget *> buttons;
+
+ auto empty = new QWidget();
+ empty->setFixedWidth(5);
+ empty->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ buttons.append(empty);
auto button = new QToolButton();
- button->setIcon(Icons::ARROW_LEFT.icon());
+ button->setIcon(Theme::iconFromName(Theme::Icon::moveUpwards_medium));
button->setToolTip(tr("Become last sibling of parent (CTRL + Left)."));
button->setShortcut(QKeySequence(Qt::Key_Left | Qt::CTRL));
connect(button, &QAbstractButton::clicked, this, &NavigatorWidget::leftButtonClicked);
buttons.append(button);
button = new QToolButton();
- button->setIcon(Icons::ARROW_RIGHT.icon());
+ button->setIcon(Theme::iconFromName(Theme::Icon::moveInwards_medium));
button->setToolTip(tr("Become child of last sibling (CTRL + Right)."));
button->setShortcut(QKeySequence(Qt::Key_Right | Qt::CTRL));
connect(button, &QAbstractButton::clicked, this, &NavigatorWidget::rightButtonClicked);
buttons.append(button);
button = new QToolButton();
- button->setIcon(Icons::ARROW_DOWN.icon());
+ button->setIcon(Theme::iconFromName(Theme::Icon::moveDown_medium));
button->setToolTip(tr("Move down (CTRL + Down)."));
button->setShortcut(QKeySequence(Qt::Key_Down | Qt::CTRL));
connect(button, &QAbstractButton::clicked, this, &NavigatorWidget::downButtonClicked);
buttons.append(button);
button = new QToolButton();
- button->setIcon(Icons::ARROW_UP.icon());
+ button->setIcon(Theme::iconFromName(Theme::Icon::moveUp_medium));
button->setToolTip(tr("Move up (CTRL + Up)."));
button->setShortcut(QKeySequence(Qt::Key_Up | Qt::CTRL));
connect(button, &QAbstractButton::clicked, this, &NavigatorWidget::upButtonClicked);
buttons.append(button);
- auto filter = new QToolButton;
- filter->setIcon(Utils::Icons::FILTER.icon());
- filter->setToolTip(tr("Filter Tree"));
- filter->setPopupMode(QToolButton::InstantPopup);
- filter->setProperty("noArrow", true);
- auto filterMenu = new QMenu(filter);
- auto filterAction = new QAction(tr("Show Only Visible Components"), nullptr);
- filterAction->setCheckable(true);
-
- bool filterFlag = QmlDesignerPlugin::settings().value(DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS).toBool();
- filterAction->setChecked(filterFlag);
+ empty = new QWidget();
+ empty->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ buttons.append(empty);
- connect(filterAction, &QAction::toggled, this, &NavigatorWidget::filterToggled);
- filterMenu->addAction(filterAction);
+ // Show Only Visible Components
+ auto visibleIcon = Theme::iconFromName(Theme::Icon::visible_medium);
+ auto invisibleIcon = Theme::iconFromName(Theme::Icon::invisible_medium,
+ Theme::getColor(Theme::Color::DStextSelectedTextColor));
+ QIcon vIcon;
+ vIcon.addPixmap(invisibleIcon.pixmap({16, 16}), QIcon::Normal, QIcon::On);
+ vIcon.addPixmap(visibleIcon.pixmap({16, 16}), QIcon::Normal, QIcon::Off);
- auto reverseAction = new QAction(tr("Reverse Component Order"), nullptr);
- reverseAction->setCheckable(true);
+ button = new QToolButton();
+ button->setIcon(vIcon);
+ button->setCheckable(true);
+ bool visibleFlag = QmlDesignerPlugin::settings()
+ .value(DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS)
+ .toBool();
+ button->setChecked(visibleFlag);
+ button->setToolTip(tr("Show Only Visible Components"));
+ connect(button, &QAbstractButton::toggled, this, &NavigatorWidget::filterToggled);
+ buttons.append(button);
- bool reverseFlag = QmlDesignerPlugin::settings().value(DesignerSettingsKey::NAVIGATOR_REVERSE_ITEM_ORDER).toBool();
- reverseAction->setChecked(reverseFlag);
+ // Reverse Component Order
+ auto reverseOffIcon = Theme::iconFromName(Theme::Icon::reverseOrder_medium);
+ auto reverseOnIcon = Theme::iconFromName(Theme::Icon::reverseOrder_medium,
+ Theme::getColor(Theme::Color::DStextSelectedTextColor));
+ QIcon rIcon;
+ rIcon.addPixmap(reverseOnIcon.pixmap({16, 16}), QIcon::Normal, QIcon::On);
+ rIcon.addPixmap(reverseOffIcon.pixmap({16, 16}), QIcon::Normal, QIcon::Off);
- connect(reverseAction, &QAction::toggled, this, &NavigatorWidget::reverseOrderToggled);
- filterMenu->addAction(reverseAction);
+ button = new QToolButton();
+ button->setIcon(rIcon);
+ button->setCheckable(true);
+ bool reverseFlag = QmlDesignerPlugin::settings()
+ .value(DesignerSettingsKey::NAVIGATOR_REVERSE_ITEM_ORDER)
+ .toBool();
+ button->setChecked(reverseFlag);
+ button->setToolTip(tr("Reverse Component Order"));
+ connect(button, &QAbstractButton::toggled, this, &NavigatorWidget::reverseOrderToggled);
+ buttons.append(button);
- filter->setMenu(filterMenu);
- buttons.append(filter);
+ empty = new QWidget();
+ empty->setFixedWidth(5);
+ empty->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ buttons.append(empty);
return buttons;
}
QToolBar *NavigatorWidget::createToolBar()
{
- const QList<QToolButton*> buttons = createToolBarWidgets();
+ const QList<QWidget *> buttons = createToolBarWidgets();
auto toolBar = new QToolBar();
+ toolBar->setFixedHeight(Theme::toolbarSize());
for (auto toolButton : buttons)
toolBar->addWidget(toolButton);
@@ -181,6 +212,7 @@ void NavigatorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
void NavigatorWidget::dropEvent(QDropEvent *dropEvent)
{
+ dropEvent->accept();
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
->viewManager().designerActionManager();
actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h
index b788b06986..beec6e42b4 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h
@@ -28,7 +28,7 @@ public:
void setTreeModel(QAbstractItemModel *model);
QTreeView *treeView() const;
- QList<QToolButton *> createToolBarWidgets();
+ QList<QWidget *> createToolBarWidgets();
QToolBar *createToolBar();
void contextHelp(const Core::IContext::HelpCallback &callback) const;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp b/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp
index 5d1900c494..f71f04fd39 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp
@@ -663,14 +663,16 @@ AlignDistribute::Dimension AlignDistribute::getDimension(Target target) const
bool AlignDistribute::executePixelPerfectDialog() const
{
- QDialogButtonBox::StandardButton pressed = Utils::CheckableMessageBox::doNotAskAgainQuestion(
+ auto decider = Utils::CheckableMessageBox::make_decider(Core::ICore::settings(),
+ "WarnAboutPixelPerfectDistribution");
+
+ QMessageBox::StandardButton pressed = Utils::CheckableMessageBox::question(
Core::ICore::dialogParent(),
tr("Cannot Distribute Perfectly"),
tr("These objects cannot be distributed to equal pixel values. "
"Do you want to distribute to the nearest possible values?"),
- Core::ICore::settings(),
- "WarnAboutPixelPerfectDistribution");
- return (pressed == QDialogButtonBox::Yes) ? true : false;
+ decider);
+ return (pressed == QMessageBox::Yes) ? true : false;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.cpp b/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.cpp
new file mode 100644
index 0000000000..5ecf048ab1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "assetimageprovider.h"
+
+#include <asset.h>
+#include <imagecacheimageresponse.h>
+
+#include <projectexplorer/target.h>
+#include <utils/hdrimage.h>
+#include <utils/stylehelper.h>
+
+#include <QMetaObject>
+#include <QQuickImageResponse>
+
+namespace QmlDesigner {
+
+QQuickImageResponse *AssetImageProvider::requestImageResponse(const QString &id,
+ const QSize &requestedSize)
+{
+ if (id.endsWith(".mesh"))
+ return m_imageCacheProvider.requestImageResponse(id, {});
+
+ if (id.endsWith(".builtin"))
+ return m_imageCacheProvider.requestImageResponse("#" + id.split('.').first(), {});
+
+ if (id.endsWith(".ktx")) {
+ auto response = std::make_unique<ImageCacheImageResponse>(m_imageCacheProvider.defaultImage());
+
+ QMetaObject::invokeMethod(
+ response.get(),
+ [response = QPointer<ImageCacheImageResponse>(response.get()), requestedSize] {
+ QImage ktxImage;
+ ktxImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_ktx.png"));
+ if (ktxImage.isNull())
+ ktxImage = response->image();
+ if (requestedSize.isValid())
+ response->setImage(ktxImage.scaled(requestedSize, Qt::KeepAspectRatio));
+ else
+ response->setImage(ktxImage);
+ },
+ Qt::QueuedConnection);
+
+ return response.release();
+ }
+
+ return m_imageCacheProvider.requestImageResponse(id, requestedSize);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h b/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h
index 627750b508..450086d323 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/assetimageprovider.h
@@ -1,26 +1,26 @@
-// Copyright (C) 2022 The Qt Company Ltd.
+// 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 "imagecache/smallimagecacheprovider.h"
+#include "imagecache/midsizeimagecacheprovider.h"
#include <QQuickAsyncImageProvider>
namespace QmlDesigner {
-class PropertyEditorImageProvider : public QQuickAsyncImageProvider
+class AssetImageProvider : public QQuickAsyncImageProvider
{
public:
- PropertyEditorImageProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {})
- : m_smallImageCacheProvider(imageCache, defaultImage)
+ AssetImageProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {})
+ : m_imageCacheProvider(imageCache, defaultImage)
{}
QQuickImageResponse *requestImageResponse(const QString &id,
const QSize &requestedSize) override;
private:
- SmallImageCacheProvider m_smallImageCacheProvider;
+ MidSizeImageCacheProvider m_imageCacheProvider;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
index ee89eebffe..689753ff8b 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
@@ -25,6 +25,7 @@
#include "dynamicpropertiesproxymodel.h"
+#include "bindingproperty.h"
#include "propertyeditorvalue.h"
#include <dynamicpropertiesmodel.h>
@@ -40,7 +41,7 @@
#include <QScopeGuard>
-using namespace QmlDesigner;
+namespace QmlDesigner {
static const int propertyNameRole = Qt::UserRole + 1;
static const int propertyTypeRole = Qt::UserRole + 2;
@@ -52,7 +53,7 @@ DynamicPropertiesProxyModel::DynamicPropertiesProxyModel(QObject *parent)
{
}
-void DynamicPropertiesProxyModel::initModel(QmlDesigner::Internal::DynamicPropertiesModel *model)
+void DynamicPropertiesProxyModel::initModel(DynamicPropertiesModel *model)
{
m_model = model;
@@ -79,14 +80,14 @@ void DynamicPropertiesProxyModel::initModel(QmlDesigner::Internal::DynamicProper
int DynamicPropertiesProxyModel::rowCount(const QModelIndex &) const
{
- return m_model->rowCount();
+ return m_model ? m_model->rowCount() : 0;
}
QHash<int, QByteArray> DynamicPropertiesProxyModel::roleNames() const
{
- static QHash<int, QByteArray> roleNames{{propertyNameRole, "propertyName"},
- {propertyTypeRole, "propertyType"},
- {propertyValueRole, "propertyValue"},
+ static QHash<int, QByteArray> roleNames{{propertyNameRole, "propertyName"},
+ {propertyTypeRole, "propertyType"},
+ {propertyValueRole, "propertyValue"},
{propertyBindingRole, "propertyBinding"}};
return roleNames;
@@ -99,26 +100,30 @@ QVariant DynamicPropertiesProxyModel::data(const QModelIndex &index, int role) c
QTC_ASSERT(property.isValid(), return QVariant());
- if (role == propertyNameRole) {
+ if (role == propertyNameRole)
return property.name();
- } else if (propertyTypeRole) {
+
+ if (propertyTypeRole)
return property.dynamicTypeName();
- } else if (role == propertyValueRole) {
+
+ if (role == propertyValueRole) {
QmlObjectNode objectNode = property.parentQmlObjectNode();
return objectNode.modelValue(property.name());
- } else if (role == propertyBindingRole) {
- if (property.isBindingProperty()) {
- QmlObjectNode objectNode = property.parentQmlObjectNode();
- return objectNode.expression(property.name());
- }
- return QVariant();
}
+
+ if (role == propertyBindingRole) {
+ if (property.isBindingProperty())
+ return property.parentQmlObjectNode().expression(property.name());
+
+ return {};
+ }
+
qWarning() << Q_FUNC_INFO << "invalid role";
} else {
qWarning() << Q_FUNC_INFO << "invalid index";
}
- return QVariant();
+ return {};
}
void DynamicPropertiesProxyModel::registerDeclarativeType()
@@ -128,17 +133,16 @@ void DynamicPropertiesProxyModel::registerDeclarativeType()
qmlRegisterType<DynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "DynamicPropertiesModel");
}
-QmlDesigner::Internal::DynamicPropertiesModel *DynamicPropertiesProxyModel::dynamicPropertiesModel() const
+DynamicPropertiesModel *DynamicPropertiesProxyModel::dynamicPropertiesModel() const
{
return m_model;
}
QString DynamicPropertiesProxyModel::newPropertyName() const
{
- auto propertiesModel = dynamicPropertiesModel();
+ DynamicPropertiesModel *propsModel = dynamicPropertiesModel();
- return QString::fromUtf8(propertiesModel->unusedProperty(
- propertiesModel->singleSelectedNode()));
+ return QString::fromUtf8(propsModel->unusedProperty(propsModel->singleSelectedNode()));
}
void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type)
@@ -158,23 +162,22 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr
return;
}
try {
- if (Internal::DynamicPropertiesModel::isValueType(typeName)) {
- QVariant value = Internal::DynamicPropertiesModel::defaultValueForType(typeName);
- modelNode.variantProperty(name.toUtf8())
- .setDynamicTypeNameAndValue(typeName, value);
+ if (DynamicPropertiesModel::isValueType(typeName)) {
+ QVariant value = DynamicPropertiesModel::defaultValueForType(typeName);
+ VariantProperty variantProp = modelNode.variantProperty(name.toUtf8());
+ variantProp.setDynamicTypeNameAndValue(typeName, value);
} else {
- QString expression = Internal::DynamicPropertiesModel::defaultExpressionForType(
- typeName);
+ QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName);
- modelNode.bindingProperty(name.toUtf8())
- .setDynamicTypeNameAndExpression(typeName, expression);
+ BindingProperty bindingProp = modelNode.bindingProperty(name.toUtf8());
+ bindingProp.setDynamicTypeNameAndExpression(typeName, expression);
}
} catch (Exception &e) {
e.showException();
}
}
} else {
- qWarning() << " BindingModel::addBindingForCurrentNode not one node selected";
+ qWarning() << __FUNCTION__ << ": not one node selected";
}
}
@@ -191,7 +194,7 @@ DynamicPropertyRow::DynamicPropertyRow()
&PropertyEditorValue::expressionChanged,
this,
[this](const QString &name) {
- if (!name.isEmpty()) //If name is empty the notifer is only for QML
+ if (!name.isEmpty()) // If name is empty the notifer is only for QML
commitExpression(m_backendValue->expression());
else if (m_backendValue->expression().isEmpty())
resetValue();
@@ -263,8 +266,7 @@ void DynamicPropertyRow::remove()
PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue()
{
-
- PropertyEditorValue *newValue = new PropertyEditorValue(this);
+ auto *newValue = new PropertyEditorValue(this);
m_proxyBackendValues.append(newValue);
return newValue;
@@ -281,7 +283,7 @@ void DynamicPropertyRow::setupBackendValue()
if (!m_model)
return;
- QmlDesigner::AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row);
+ AbstractProperty property = m_model->dynamicPropertiesModel()->abstractPropertyForRow(m_row);
if (!property.isValid())
return;
@@ -324,15 +326,14 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
auto propertiesModel = m_model->dynamicPropertiesModel();
VariantProperty variantProperty = propertiesModel->variantPropertyForRow(m_row);
- if (!Internal::DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName()))
+ if (!DynamicPropertiesModel::isValueType(variantProperty.dynamicTypeName()))
return;
m_lock = true;
auto unlock = qScopeGuard([this] { m_lock = false; });
auto view = propertiesModel->view();
- RewriterTransaction transaction = view->beginRewriterTransaction(
- QByteArrayLiteral("DynamicPropertiesModel::commitValue"));
+ RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__);
try {
QmlObjectNode objectNode = variantProperty.parentQmlObjectNode();
if (view->currentState().isBaseState()
@@ -345,7 +346,7 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
if (objectNode.isValid() && objectNode.modelValue(name) != value)
objectNode.setVariantProperty(name, value);
}
- transaction.commit(); //committing in the try block
+ transaction.commit(); // committing in the try block
} catch (Exception &e) {
e.showException();
}
@@ -353,10 +354,7 @@ void DynamicPropertyRow::commitValue(const QVariant &value)
void DynamicPropertyRow::commitExpression(const QString &expression)
{
- if (m_lock)
- return;
-
- if (m_row < 0)
+ if (m_lock || m_row < 0)
return;
auto propertiesModel = m_model->dynamicPropertiesModel();
@@ -367,7 +365,7 @@ void DynamicPropertyRow::commitExpression(const QString &expression)
const QVariant literal = BindingProperty::convertToLiteral(bindingProperty.dynamicTypeName(),
expression);
- if (literal.isValid()) { //If the string can be converted to a literal we set it as a literal/value
+ if (literal.isValid()) { // If the string can be converted to a literal we set it as a literal/value
commitValue(literal);
return;
}
@@ -376,8 +374,7 @@ void DynamicPropertyRow::commitExpression(const QString &expression)
auto unlock = qScopeGuard([this] { m_lock = false; });
auto view = propertiesModel->view();
- RewriterTransaction transaction = view->beginRewriterTransaction(
- QByteArrayLiteral("DynamicPropertyRow::commitExpression"));
+ RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__);
try {
QString theExpression = expression;
if (theExpression.isEmpty())
@@ -396,11 +393,10 @@ void DynamicPropertyRow::commitExpression(const QString &expression)
objectNode.setBindingProperty(name, theExpression);
}
- transaction.commit(); //committing in the try block
+ transaction.commit(); // committing in the try block
} catch (Exception &e) {
e.showException();
}
- return;
}
void DynamicPropertyRow::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &, const QList<int> &)
@@ -411,10 +407,7 @@ void DynamicPropertyRow::handleDataChanged(const QModelIndex &topLeft, const QMo
void DynamicPropertyRow::resetValue()
{
- if (m_lock)
- return;
-
- if (m_row < 0)
+ if (m_lock || m_row < 0)
return;
auto propertiesModel = m_model->dynamicPropertiesModel();
@@ -424,20 +417,18 @@ void DynamicPropertyRow::resetValue()
TypeName typeName = property.dynamicTypeName();
if (view->currentState().isBaseState()) {
- if (Internal::DynamicPropertiesModel::isValueType(typeName)) {
- QVariant value = Internal::DynamicPropertiesModel::defaultValueForType(typeName);
+ if (DynamicPropertiesModel::isValueType(typeName)) {
+ QVariant value = DynamicPropertiesModel::defaultValueForType(typeName);
commitValue(value);
} else {
- QString expression = Internal::DynamicPropertiesModel::defaultExpressionForType(
- typeName);
+ QString expression = DynamicPropertiesModel::defaultExpressionForType(typeName);
commitExpression(expression);
}
} else {
m_lock = true;
auto unlock = qScopeGuard([this] { m_lock = false; });
- RewriterTransaction transaction = view->beginRewriterTransaction(
- QByteArrayLiteral("DynamicPropertyRow::resetValue"));
+ RewriterTransaction transaction = view->beginRewriterTransaction(__FUNCTION__);
try {
QmlObjectNode objectNode = property.parentQmlObjectNode();
QTC_CHECK(objectNode.isValid());
@@ -445,9 +436,11 @@ void DynamicPropertyRow::resetValue()
if (objectNode.isValid() && objectNode.propertyAffectedByCurrentState(name))
objectNode.removeProperty(name);
- transaction.commit(); //committing in the try block
+ transaction.commit(); // committing in the try block
} catch (Exception &e) {
e.showException();
}
}
}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h
index 16a59d2cf2..ec16a466d4 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.h
@@ -30,16 +30,13 @@
#include <abstractview.h>
#include <qmlitemnode.h>
-#include <enumeration.h>
#include <QAbstractListModel>
#include <QColor>
#include <QtQml>
namespace QmlDesigner {
-namespace Internal {
+
class DynamicPropertiesModel;
-}
-} // namespace QmlDesigner
class DynamicPropertiesProxyModel : public QAbstractListModel
{
@@ -55,16 +52,16 @@ public:
static void registerDeclarativeType();
- QmlDesigner::Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
+ DynamicPropertiesModel *dynamicPropertiesModel() const;
Q_INVOKABLE QString newPropertyName() const;
Q_INVOKABLE void createProperty(const QString &name, const QString &type);
protected:
- void initModel(QmlDesigner::Internal::DynamicPropertiesModel *model);
+ void initModel(DynamicPropertiesModel *model);
private:
- QmlDesigner::Internal::DynamicPropertiesModel *m_model = nullptr;
+ DynamicPropertiesModel *m_model = nullptr;
};
class DynamicPropertyRow : public QObject
@@ -72,7 +69,7 @@ class DynamicPropertyRow : public QObject
Q_OBJECT
Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged FINAL)
- Q_PROPERTY(PropertyEditorValue *backendValue READ backendValue NOTIFY rowChanged FINAL)
+ Q_PROPERTY(QmlDesigner::PropertyEditorValue *backendValue READ backendValue NOTIFY rowChanged FINAL)
Q_PROPERTY(DynamicPropertiesProxyModel *model READ model WRITE setModel NOTIFY modelChanged FINAL)
public:
@@ -85,10 +82,10 @@ public:
int row() const;
void setModel(DynamicPropertiesProxyModel *model);
DynamicPropertiesProxyModel *model() const;
- PropertyEditorValue *backendValue() const;
+ QmlDesigner::PropertyEditorValue *backendValue() const;
Q_INVOKABLE void remove();
- Q_INVOKABLE PropertyEditorValue *createProxyBackendValue();
+ Q_INVOKABLE QmlDesigner::PropertyEditorValue *createProxyBackendValue();
Q_INVOKABLE void clearProxyBackendValues();
signals:
@@ -103,10 +100,12 @@ private:
void resetValue();
int m_row = -1;
- PropertyEditorValue *m_backendValue = nullptr;
+ QmlDesigner::PropertyEditorValue *m_backendValue = nullptr;
DynamicPropertiesProxyModel *m_model = nullptr;
- QList<PropertyEditorValue *> m_proxyBackendValues;
+ QList<QmlDesigner::PropertyEditorValue *> m_proxyBackendValues;
bool m_lock = false;
};
-QML_DECLARE_TYPE(DynamicPropertyRow)
+} // namespace QmlDesigner
+
+QML_DECLARE_TYPE(QmlDesigner::DynamicPropertyRow)
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
index 7164586fea..aae0867c91 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
@@ -15,7 +15,7 @@
#include <qmlmodelnodeproxy.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
static QString s_lastBrowserPath;
@@ -23,7 +23,7 @@ FileResourcesModel::FileResourcesModel(QObject *parent)
: QObject(parent)
, m_filter(QLatin1String("(*.*)"))
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(
QmlDesigner::DocumentManager::currentFilePath());
if (project) {
@@ -51,7 +51,7 @@ void FileResourcesModel::setModelNodeBackend(const QVariant &modelNodeBackend)
QmlDesigner::DocumentManager::currentProjectDirPath().toFileInfo().absoluteFilePath());
}
- setupModel();
+ refreshModel();
emit modelNodeBackendChanged();
}
@@ -78,7 +78,7 @@ void FileResourcesModel::setFileName(const QUrl &fileName)
void FileResourcesModel::setPath(const QUrl &url)
{
m_path = url;
- setupModel();
+ refreshModel();
emit pathChanged(url);
}
@@ -99,7 +99,7 @@ void FileResourcesModel::setFilter(const QString &filter)
return;
m_filter = filter;
- setupModel();
+ refreshModel();
emit filterChanged(filter);
}
@@ -201,33 +201,30 @@ bool filterMetaIcons(const QString &fileName)
return true;
}
-void FileResourcesModel::setupModel()
-{
- m_dirPath = QDir(m_path.toLocalFile());
- refreshModel();
-}
-
void FileResourcesModel::refreshModel()
{
m_model.clear();
- QStringList filterList = m_filter.split(QLatin1Char(' '));
-
- QDirIterator it(m_dirPath.absolutePath(), filterList, QDir::Files, QDirIterator::Subdirectories);
- while (it.hasNext()) {
- QString absolutePath = it.next();
- if (filterMetaIcons(absolutePath)) {
- QString relativeFilePath = m_docPath.relativeFilePath(absolutePath);
- m_model.append(
- FileResourcesItem(absolutePath,
- relativeFilePath,
- relativeFilePath.mid(relativeFilePath.lastIndexOf('/') + 1)));
+ if (m_path.isValid()) {
+ const QDir dirPath = QDir(m_path.toLocalFile());
+ const QStringList filterList = m_filter.split(QLatin1Char(' '));
+
+ QDirIterator it(dirPath.absolutePath(), filterList, QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ const QString absolutePath = it.next();
+ if (filterMetaIcons(absolutePath)) {
+ const QString relativeFilePath = m_docPath.relativeFilePath(absolutePath);
+ m_model.append(
+ FileResourcesItem(absolutePath,
+ relativeFilePath,
+ relativeFilePath.mid(relativeFilePath.lastIndexOf('/') + 1)));
+ }
}
- }
- Utils::sort(m_model, [](const FileResourcesItem &i1, const FileResourcesItem &i2) {
- return i1.fileName().toLower() < i2.fileName().toLower();
- });
+ Utils::sort(m_model, [](const FileResourcesItem &i1, const FileResourcesItem &i2) {
+ return i1.fileName().toLower() < i2.fileName().toLower();
+ });
+ }
emit modelChanged();
}
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
index 3426c67579..4bca3f531a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
@@ -67,7 +67,6 @@ public:
QString filter() const;
QList<FileResourcesItem> model() const;
- void setupModel();
void refreshModel();
Q_INVOKABLE void openFileDialog();
@@ -89,7 +88,6 @@ private:
private:
QUrl m_fileName;
QUrl m_path;
- QDir m_dirPath;
QDir m_docPath;
QString m_filter;
QString m_currentPath;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp
index 7957948f25..d52e47c367 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp
@@ -6,14 +6,28 @@
#include <abstractview.h>
#include <model.h>
#include <nodemetainfo.h>
+#include <qmlmodelnodeproxy.h>
+#include "variantproperty.h"
#include <QFileDialog>
#include <QDirIterator>
-#include <qmlmodelnodeproxy.h>
+#include <QMetaEnum>
+
+using namespace QmlDesigner;
+
+QHash<int, QByteArray> ItemFilterModel::m_roles;
-ItemFilterModel::ItemFilterModel(QObject *parent) :
- QObject(parent), m_typeFilter("QtQuick.Item"), m_lock(false), m_selectionOnly(false)
+ItemFilterModel::ItemFilterModel(QObject *parent)
+ : QAbstractListModel(parent)
+ , m_typeFilter("QtQuick.Item")
+ , m_selectionOnly(false)
{
+ if (m_roles.empty()) {
+ m_roles = QAbstractListModel::roleNames();
+ QMetaEnum roleEnum = QMetaEnum::fromType<Roles>();
+ for (int i = 0; i < roleEnum.keyCount(); i++)
+ m_roles.insert(roleEnum.value(i), roleEnum.key(i));
+ }
}
void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend)
@@ -22,7 +36,7 @@ void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend)
auto modelNodeBackendObject = modelNodeBackend.value<QObject*>();
const auto backendObjectCasted =
- qobject_cast<const QmlDesigner::QmlModelNodeProxy *>(modelNodeBackendObject);
+ qobject_cast<const QmlModelNodeProxy *>(modelNodeBackendObject);
if (backendObjectCasted)
m_modelNode = backendObjectCasted->qmlObjectNode().modelNode();
@@ -62,19 +76,79 @@ void ItemFilterModel::registerDeclarativeType()
qmlRegisterType<ItemFilterModel>("HelperWidgets",2,0,"ItemFilterModel");
}
+QModelIndex ItemFilterModel::index(int row, int column, const QModelIndex &parent) const
+{
+ return QAbstractListModel::index(row, column, parent);
+}
+
+int ItemFilterModel::rowCount(const QModelIndex &) const
+{
+ return m_modelInternalIds.size();
+}
+
+QVariant ItemFilterModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return {};
+
+ ModelNode node = modelNodeForRow(index.row());
+
+ QVariant value;
+ switch (role) {
+ case IdRole:
+ value = node.id();
+ break;
+ case NameRole:
+ value = node.variantProperty("objectName").value();
+ break;
+ case IdAndNameRole:
+ value = QString("%1 [%2]").arg(
+ node.variantProperty("objectName").value().toString()
+ ,node.id());
+ break;
+ default:
+ value = node.id();
+ break;
+ }
+
+ return value;
+}
+
+// TODO: Handle model data manipulation here.
+bool ItemFilterModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ return QAbstractListModel::setData(index, value, role);
+}
+
+QHash<int, QByteArray> ItemFilterModel::roleNames() const
+{
+ return m_roles;
+}
+
QVariant ItemFilterModel::modelNodeBackend() const
{
- return QVariant();
+ return {};
}
+ModelNode ItemFilterModel::modelNodeForRow(const int &row) const
+{
+ if (row < 0 || row >= m_modelInternalIds.size())
+ return {};
+
+ AbstractView *view = m_modelNode.view();
+ if (!view || !view->model())
+ return {};
+
+ return view->modelNodeForInternalId(m_modelInternalIds.at(row));
+}
void ItemFilterModel::setupModel()
{
if (!m_modelNode.isValid() || !m_modelNode.view()->isAttached())
return;
- m_lock = true;
- m_model.clear();
+ beginResetModel();
+ m_modelInternalIds.clear();
const auto nodes = m_selectionOnly ? m_modelNode.view()->selectedModelNodes()
: m_modelNode.view()->allModelNodes();
@@ -82,15 +156,22 @@ void ItemFilterModel::setupModel()
auto base = m_modelNode.model()->metaInfo(m_typeFilter.toUtf8());
for (const QmlDesigner::ModelNode &node : nodes) {
if (node.hasId() && node.metaInfo().isBasedOn(base))
- m_model.append(node.id());
+ m_modelInternalIds.append(node.internalId());
}
- m_lock = false;
-
+ endResetModel();
emit itemModelChanged();
}
QStringList ItemFilterModel::itemModel() const
{
- return m_model;
+ AbstractView *view = m_modelNode.view();
+ if (!view || !view->model())
+ return {};
+
+ QStringList retval;
+ for (const auto &internalId : std::as_const(m_modelInternalIds))
+ retval << view->modelNodeForInternalId(internalId).id();
+
+ return retval;
}
diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h
index 1b9ef1f18e..6876f1edff 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h
@@ -6,12 +6,13 @@
#include <qmlitemnode.h>
#include <QDir>
+#include <QHash>
#include <QObject>
#include <QStringList>
#include <QUrl>
#include <QtQml>
-class ItemFilterModel : public QObject
+class ItemFilterModel : public QAbstractListModel
{
Q_OBJECT
@@ -21,6 +22,13 @@ class ItemFilterModel : public QObject
Q_PROPERTY(bool selectionOnly READ selectionOnly WRITE setSelectionOnly NOTIFY selectionOnlyChanged)
public:
+ enum Roles {
+ IdRole = Qt::UserRole + 1,
+ NameRole,
+ IdAndNameRole
+ };
+ Q_ENUM(Roles)
+
explicit ItemFilterModel(QObject *parent = nullptr);
void setModelNodeBackend(const QVariant &modelNodeBackend);
@@ -33,6 +41,14 @@ public:
static void registerDeclarativeType();
+ // Make index accessible for Qml side since it's not accessible by default in QAbstractListModel
+ Q_INVOKABLE QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
+ Q_INVOKABLE virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ Q_INVOKABLE virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ Q_INVOKABLE virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ virtual QHash<int,QByteArray> roleNames() const override;
+
signals:
void modelNodeBackendChanged();
void itemModelChanged();
@@ -40,13 +56,14 @@ signals:
private:
QVariant modelNodeBackend() const;
+ QmlDesigner::ModelNode modelNodeForRow(const int &row) const;
private:
QString m_typeFilter;
- bool m_lock;
- QStringList m_model;
+ QList<qint32> m_modelInternalIds;
QmlDesigner::ModelNode m_modelNode;
bool m_selectionOnly;
+ static QHash<int, QByteArray> m_roles;
};
QML_DECLARE_TYPE(ItemFilterModel)
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
index 421db6c478..a8aebbfb07 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
@@ -19,6 +19,7 @@
#include <QApplication>
#include <QCursor>
#include <QFontDatabase>
+#include <QLoggingCategory>
#include <QMessageBox>
#include <QQmlContext>
#include <QWindow>
@@ -68,7 +69,9 @@ QColor convertColorFromString(const QString &s)
namespace QmlDesigner {
-PropertyEditorContextObject::PropertyEditorContextObject(QObject *parent)
+static Q_LOGGING_CATEGORY(urlSpecifics, "qtc.propertyeditor.specifics", QtWarningMsg)
+
+ PropertyEditorContextObject::PropertyEditorContextObject(QObject *parent)
: QObject(parent)
, m_isBaseState(false)
, m_selectionChanged(false)
@@ -443,6 +446,8 @@ void PropertyEditorContextObject::setSpecificsUrl(const QUrl &newSpecificsUrl)
if (newSpecificsUrl == m_specificsUrl)
return;
+ qCInfo(urlSpecifics) << Q_FUNC_INFO << newSpecificsUrl;
+
m_specificsUrl = newSpecificsUrl;
emit specificsUrlChanged();
}
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp
deleted file mode 100644
index 6cb65a9562..0000000000
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "propertyeditorimageprovider.h"
-#include "asset.h"
-
-#include <projectexplorer/target.h>
-#include <utils/hdrimage.h>
-#include <utils/stylehelper.h>
-
-#include <QMetaObject>
-#include <QQuickImageResponse>
-
-namespace QmlDesigner {
-
-QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QString &id,
- const QSize &requestedSize)
-{
- Asset asset(id);
-
- if (asset.suffix() == "*.mesh")
- return m_smallImageCacheProvider.requestImageResponse(id, requestedSize);
-
- if (asset.suffix() == "*.builtin")
- return m_smallImageCacheProvider.requestImageResponse("#" + id.split('.').first(),
- requestedSize);
-
- auto response = std::make_unique<QmlDesigner::ImageResponse>(m_smallImageCacheProvider.defaultImage());
-
- QMetaObject::invokeMethod(
- response.get(),
- [response = QPointer<QmlDesigner::ImageResponse>(response.get()), asset, requestedSize] {
- if (asset.isImage()) {
- QImage image = QImage(Utils::StyleHelper::dpiSpecificImageFile(asset.id()));
- if (!image.isNull()) {
- response->setImage(image.scaled(requestedSize, Qt::KeepAspectRatio));
- return;
- }
- } else if (asset.isHdrFile()) {
- HdrImage hdr{asset.id()};
- if (!hdr.image().isNull()) {
- response->setImage(hdr.image().scaled(requestedSize, Qt::KeepAspectRatio));
- return;
- }
- }
- response->setImage(response->image().scaled(requestedSize, Qt::KeepAspectRatio));
- },
- Qt::QueuedConnection);
-
- return response.release();
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
index 41cdb431b8..909530dbc3 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
@@ -365,6 +365,13 @@ void PropertyEditorQmlBackend::setValue(const QmlObjectNode & , const PropertyNa
}
}
+void PropertyEditorQmlBackend::setExpression(const PropertyName &propName, const QString &exp)
+{
+ PropertyEditorValue *propertyValue = propertyValueForName(QString::fromUtf8(propName));
+ if (propertyValue)
+ propertyValue->setExpression(exp);
+}
+
QQmlContext *PropertyEditorQmlBackend::context() {
return m_view->rootContext();
}
@@ -373,7 +380,7 @@ PropertyEditorContextObject* PropertyEditorQmlBackend::contextObject() {
return m_contextObject.data();
}
-QWidget *PropertyEditorQmlBackend::widget() {
+QQuickWidget *PropertyEditorQmlBackend::widget() {
return m_view;
}
@@ -637,7 +644,7 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
if (!superType.hasProperty(propertyName) // TODO add property.isLocalProperty()
&& property.isWritable() && dotPropertyHeuristic(node, metaType, propertyName)) {
- QString typeName = QString::fromUtf8(property.propertyType().typeName());
+ QString typeName = QString::fromUtf8(property.propertyType().simplifiedTypeName());
if (typeName == "alias" && node.isValid())
typeName = QString::fromUtf8(node.instanceType(propertyName));
@@ -649,10 +656,19 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
} else {
if (propertyName.contains('.')) {
const PropertyName parentPropertyName = propertyName.split('.').first();
- const PropertyMetaInfo parentProperty = metaType.property(
- parentPropertyName);
+ const PropertyMetaInfo parentProperty = metaType.property(parentPropertyName);
+
+ auto vectorFound = std::find(separateSectionProperties.begin(),
+ separateSectionProperties.end(),
+ parentProperty);
+
+ auto propertyMapFound = propertyMap.find(parentProperty);
+
+ const bool exists = propertyMapFound != propertyMap.end()
+ || vectorFound != separateSectionProperties.end();
- propertyMap[parentProperty].push_back(property);
+ if (!exists)
+ propertyMap[parentProperty].push_back(property);
} else {
propertyMap[property];
}
@@ -681,7 +697,7 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
PropertyName underscoreProperty = propertyName;
underscoreProperty.replace('.', '_');
- TypeName typeName = property.propertyType().typeName();
+ TypeName typeName = property.propertyType().simplifiedTypeName();
// alias resolution only possible with instance
if (!useProjectStorage() && typeName == "alias" && node.isValid())
typeName = node.instanceType(propertyName);
@@ -757,7 +773,7 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp
emptyTemplate = false;
for (auto &[property, properties] : propertyMap) {
// for (auto it = propertyMap.cbegin(); it != propertyMap.cend(); ++it) {
- TypeName parentTypeName = property.propertyType().typeName();
+ TypeName parentTypeName = property.propertyType().simplifiedTypeName();
// alias resolution only possible with instance
if (!useProjectStorage() && parentTypeName == "alias" && node.isValid())
parentTypeName = node.instanceType(property.name());
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
index 741eab394e..092d411dfa 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
@@ -35,10 +35,11 @@ public:
void setup(const QmlObjectNode &fxObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor);
void initialSetup(const TypeName &typeName, const QUrl &qmlSpecificsFile, PropertyEditorView *propertyEditor);
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
+ void setExpression(const PropertyName &propName, const QString &exp);
QQmlContext *context();
PropertyEditorContextObject* contextObject();
- QWidget *widget();
+ QQuickWidget *widget();
void setSource(const QUrl& url);
Internal::QmlAnchorBindingProxy &backendAnchorBinding();
DesignerPropertyMap &backendValuesPropertyMap();
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.h
index 90faa0f06d..a6fb43baf1 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditortransaction.h
@@ -4,6 +4,7 @@
#pragma once
#include "propertyeditorview.h"
+#include "rewritertransaction.h"
namespace QmlDesigner {
@@ -11,7 +12,7 @@ class PropertyEditorTransaction : public QObject
{
Q_OBJECT
public:
- PropertyEditorTransaction(QmlDesigner::PropertyEditorView *propertyEditor);
+ PropertyEditorTransaction(PropertyEditorView *propertyEditor);
Q_INVOKABLE void start();
Q_INVOKABLE void end();
@@ -22,8 +23,8 @@ protected:
void timerEvent(QTimerEvent *event) override;
private:
- QmlDesigner::PropertyEditorView *m_propertyEditor;
- QmlDesigner::RewriterTransaction m_rewriterTransaction;
+ PropertyEditorView *m_propertyEditor;
+ RewriterTransaction m_rewriterTransaction;
int m_timerId;
};
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
index 7a6147a209..6a9ce6164a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
@@ -5,14 +5,16 @@
#include "abstractview.h"
#include "bindingproperty.h"
+#include "createtexture.h"
#include "designermcumanager.h"
-#include "documentmanager.h"
-#include "nodelistproperty.h"
+#include "designmodewidget.h"
#include "nodemetainfo.h"
#include "nodeproperty.h"
#include "qmlitemnode.h"
#include "qmlobjectnode.h"
-#include "variantproperty.h"
+#include "qmldesignerplugin.h"
+
+#include <enumeration.h>
#include <utils/qtcassert.h>
@@ -20,12 +22,10 @@
#include <QScopedPointer>
#include <QUrl>
+namespace QmlDesigner {
+
PropertyEditorValue::PropertyEditorValue(QObject *parent)
: QObject(parent),
- m_isInSubState(false),
- m_isInModel(false),
- m_isBound(false),
- m_isValid(false),
m_complexNode(new PropertyEditorNodeWrapper(this))
{
}
@@ -33,16 +33,16 @@ PropertyEditorValue::PropertyEditorValue(QObject *parent)
QVariant PropertyEditorValue::value() const
{
QVariant returnValue = m_value;
- if (auto metaInfo = modelNode().metaInfo(); metaInfo.property(name()).propertyType().isUrl()) {
+ if (auto metaInfo = modelNode().metaInfo(); metaInfo.property(name()).propertyType().isUrl())
returnValue = returnValue.toUrl().toString();
- }
return returnValue;
}
static bool cleverDoubleCompare(const QVariant &value1, const QVariant &value2)
-{ //we ignore slight changes on doubles
- if ((value1.type() == QVariant::Double) && (value2.type() == QVariant::Double)) {
+{
+ if (value1.type() == QVariant::Double && value2.type() == QVariant::Double) {
+ // ignore slight changes on doubles
if (qFuzzyCompare(value1.toDouble(), value2.toDouble()))
return true;
}
@@ -51,31 +51,27 @@ static bool cleverDoubleCompare(const QVariant &value1, const QVariant &value2)
static bool cleverColorCompare(const QVariant &value1, const QVariant &value2)
{
- if ((value1.type() == QVariant::Color) && (value2.type() == QVariant::Color)) {
+ if (value1.type() == QVariant::Color && value2.type() == QVariant::Color) {
QColor c1 = value1.value<QColor>();
QColor c2 = value2.value<QColor>();
- QString a = c1.name();
- QString b = c2.name();
- if (a != b)
- return false;
- return (c1.alpha() == c2.alpha());
+ return c1.name() == c2.name() && c1.alpha() == c2.alpha();
}
- if ((value1.type() == QVariant::String) && (value2.type() == QVariant::Color))
+
+ if (value1.type() == QVariant::String && value2.type() == QVariant::Color)
return cleverColorCompare(QVariant(QColor(value1.toString())), value2);
- if ((value1.type() == QVariant::Color) && (value2.type() == QVariant::String))
+
+ if (value1.type() == QVariant::Color && value2.type() == QVariant::String)
return cleverColorCompare(value1, QVariant(QColor(value2.toString())));
+
return false;
}
-
-/* "red" is the same color as "#ff0000"
- To simplify editing we convert all explicit color names in the hash format */
-static void fixAmbigousColorNames(const QmlDesigner::ModelNode &modelNode,
- const QmlDesigner::PropertyName &name,
- QVariant *value)
+// "red" is the same color as "#ff0000"
+// To simplify editing we convert all explicit color names in the hash format
+static void fixAmbigousColorNames(const ModelNode &modelNode, const PropertyName &name, QVariant *value)
{
if (auto metaInfo = modelNode.metaInfo(); metaInfo.property(name).propertyType().isColor()) {
- if ((value->type() == QVariant::Color)) {
+ if (value->type() == QVariant::Color) {
QColor color = value->value<QColor>();
int alpha = color.alpha();
color = QColor(color.name());
@@ -87,7 +83,7 @@ static void fixAmbigousColorNames(const QmlDesigner::ModelNode &modelNode,
}
}
-static void fixUrl(const QmlDesigner::ModelNode &modelNode, const QmlDesigner::PropertyName &name, QVariant *value)
+static void fixUrl(const ModelNode &modelNode, const PropertyName &name, QVariant *value)
{
if (auto metaInfo = modelNode.metaInfo(); metaInfo.property(name).propertyType().isUrl()) {
if (!value->isValid())
@@ -95,28 +91,24 @@ static void fixUrl(const QmlDesigner::ModelNode &modelNode, const QmlDesigner::P
}
}
-static bool compareVariants(const QVariant &value1, const QVariant &value2)
/* The comparison of variants is not symmetric because of implicit conversion.
* QVariant(string) == QVariant(QColor) does for example ignore the alpha channel,
* because the color is converted to a string ignoring the alpha channel.
* By comparing the variants in both directions we gain a symmetric comparison.
*/
+static bool compareVariants(const QVariant &value1, const QVariant &value2)
{
- return (value1 == value2)
- && (value2 == value1);
+ return value1 == value2 && value2 == value1;
}
void PropertyEditorValue::setValueWithEmit(const QVariant &value)
{
- if (!compareVariants(value, m_value ) || isBound()) {
+ if (!compareVariants(value, m_value) || isBound()) {
QVariant newValue = value;
- if (auto metaInfo = modelNode().metaInfo(); metaInfo.property(name()).propertyType().isUrl()) {
+ if (auto metaInfo = modelNode().metaInfo(); metaInfo.property(name()).propertyType().isUrl())
newValue = QUrl(newValue.toString());
- }
- if (cleverDoubleCompare(newValue, m_value))
- return;
- if (cleverColorCompare(newValue, m_value))
+ if (cleverDoubleCompare(newValue, m_value) || cleverColorCompare(newValue, m_value))
return;
setValue(newValue);
@@ -133,9 +125,7 @@ void PropertyEditorValue::setValue(const QVariant &value)
{
const bool colorsEqual = cleverColorCompare(value, m_value);
- if (!compareVariants(m_value, value) &&
- !cleverDoubleCompare(value, m_value) &&
- !colorsEqual)
+ if (!compareVariants(m_value, value) && !cleverDoubleCompare(value, m_value) && !colorsEqual)
m_value = value;
fixAmbigousColorNames(modelNode(), name(), &m_value);
@@ -150,7 +140,7 @@ void PropertyEditorValue::setValue(const QVariant &value)
QString PropertyEditorValue::enumeration() const
{
- return m_value.value<QmlDesigner::Enumeration>().nameToString();
+ return m_value.value<Enumeration>().nameToString();
}
QString PropertyEditorValue::expression() const
@@ -160,10 +150,10 @@ QString PropertyEditorValue::expression() const
void PropertyEditorValue::setExpressionWithEmit(const QString &expression)
{
- if ( m_expression != expression) {
+ if (m_expression != expression) {
setExpression(expression);
m_value.clear();
- emit expressionChanged(nameAsQString()); //Note that we set the name in this case
+ emit expressionChanged(nameAsQString()); // Note that we set the name in this case
}
}
@@ -182,13 +172,14 @@ QString PropertyEditorValue::valueToString() const
bool PropertyEditorValue::isInSubState() const
{
- const QmlDesigner::QmlObjectNode objectNode(modelNode());
- return objectNode.isValid() && objectNode.currentState().isValid() && objectNode.propertyAffectedByCurrentState(name());
+ const QmlObjectNode objectNode(modelNode());
+ return objectNode.isValid() && objectNode.currentState().isValid()
+ && objectNode.propertyAffectedByCurrentState(name());
}
bool PropertyEditorValue::isBound() const
{
- const QmlDesigner::QmlObjectNode objectNode(modelNode());
+ const QmlObjectNode objectNode(modelNode());
return objectNode.isValid() && objectNode.hasBindingProperty(name());
}
@@ -197,7 +188,7 @@ bool PropertyEditorValue::isInModel() const
return modelNode().hasProperty(name());
}
-QmlDesigner::PropertyName PropertyEditorValue::name() const
+PropertyName PropertyEditorValue::name() const
{
return m_name;
}
@@ -207,12 +198,11 @@ QString PropertyEditorValue::nameAsQString() const
return QString::fromUtf8(m_name);
}
-void PropertyEditorValue::setName(const QmlDesigner::PropertyName &name)
+void PropertyEditorValue::setName(const PropertyName &name)
{
m_name = name;
}
-
bool PropertyEditorValue::isValid() const
{
return m_isValid;
@@ -229,16 +219,15 @@ bool PropertyEditorValue::isTranslated() const
if (auto metaInfo = modelNode().metaInfo();
metaInfo.isValid() && metaInfo.hasProperty(name())
&& metaInfo.property(name()).propertyType().isString()) {
- const QmlDesigner::QmlObjectNode objectNode(modelNode());
+ const QmlObjectNode objectNode(modelNode());
if (objectNode.hasBindingProperty(name())) {
const QRegularExpression rx(
QRegularExpression::anchoredPattern("qsTr(|Id|anslate)\\(\".*\"\\)"));
//qsTr()
- if (objectNode.propertyAffectedByCurrentState(name())) {
- return expression().contains(rx);
- } else {
+ if (objectNode.propertyAffectedByCurrentState(name()))
+ return m_expression.contains(rx);
+ else
return modelNode().bindingProperty(name()).expression().contains(rx);
- }
}
}
}
@@ -258,9 +247,7 @@ void PropertyEditorValue::setHasActiveDrag(bool val)
}
}
-static bool isAllowedSubclassType(const QString &type,
- const QmlDesigner::NodeMetaInfo &metaInfo,
- QmlDesigner::Model *model)
+static bool isAllowedSubclassType(const QString &type, const NodeMetaInfo &metaInfo, Model *model)
{
if (!metaInfo.isValid())
return false;
@@ -275,7 +262,7 @@ bool PropertyEditorValue::isAvailable() const
if (!m_modelNode.isValid())
return true;
- const QmlDesigner::DesignerMcuManager &mcuManager = QmlDesigner::DesignerMcuManager::instance();
+ const DesignerMcuManager &mcuManager = DesignerMcuManager::instance();
if (mcuManager.isMCUProject()) {
const QSet<QString> nonMcuProperties = mcuManager.bannedProperties();
@@ -293,11 +280,11 @@ bool PropertyEditorValue::isAvailable() const
const auto itemTypes = mcuAllowedItemProperties.keys();
for (const auto &itemType : itemTypes) {
if (isAllowedSubclassType(itemType, m_modelNode.metaInfo(), m_modelNode.model())) {
- const QmlDesigner::DesignerMcuManager::ItemProperties allowedItemProps =
+ const DesignerMcuManager::ItemProperties allowedItemProps =
mcuAllowedItemProperties.value(itemType);
if (allowedItemProps.properties.contains(pureNameStr)) {
- if (QmlDesigner::QmlItemNode::isValidQmlItemNode(m_modelNode)) {
- const bool itemHasChildren = QmlDesigner::QmlItemNode(m_modelNode).hasChildren();
+ if (QmlItemNode::isValidQmlItemNode(m_modelNode)) {
+ const bool itemHasChildren = QmlItemNode(m_modelNode).hasChildren();
if (itemHasChildren)
return allowedItemProps.allowChildren;
@@ -308,26 +295,24 @@ bool PropertyEditorValue::isAvailable() const
}
}
- //banned properties:
- //with prefixes:
+ // banned properties, with prefixes:
if (mcuBannedComplexProperties.value(pureNameStr).contains(endingStr))
return false;
- //general group:
+ // general group:
if (nonMcuProperties.contains(pureNameStr))
return false;
-
}
return true;
}
-QmlDesigner::ModelNode PropertyEditorValue::modelNode() const
+ModelNode PropertyEditorValue::modelNode() const
{
return m_modelNode;
}
-void PropertyEditorValue::setModelNode(const QmlDesigner::ModelNode &modelNode)
+void PropertyEditorValue::setModelNode(const ModelNode &modelNode)
{
if (modelNode != m_modelNode) {
m_modelNode = modelNode;
@@ -336,14 +321,14 @@ void PropertyEditorValue::setModelNode(const QmlDesigner::ModelNode &modelNode)
}
}
-PropertyEditorNodeWrapper* PropertyEditorValue::complexNode()
+PropertyEditorNodeWrapper *PropertyEditorValue::complexNode()
{
return m_complexNode;
}
void PropertyEditorValue::resetValue()
{
- if (m_value.isValid() || isBound()) {
+ if (m_value.isValid() || !m_expression.isEmpty() || isBound()) {
m_value = QVariant();
m_isBound = false;
m_expression = QString();
@@ -354,7 +339,7 @@ void PropertyEditorValue::resetValue()
void PropertyEditorValue::setEnumeration(const QString &scope, const QString &name)
{
- QmlDesigner::Enumeration newEnumeration(scope, name);
+ Enumeration newEnumeration(scope, name);
setValueWithEmit(QVariant::fromValue(newEnumeration));
}
@@ -377,19 +362,18 @@ bool PropertyEditorValue::hasPropertyAlias() const
QString id = modelNode().id();
- for (const QmlDesigner::BindingProperty &property : modelNode().view()->rootModelNode().bindingProperties())
- if (property.expression() == (id + "." + nameAsQString()))
+ const QList<BindingProperty> bindingProps = modelNode().view()->rootModelNode().bindingProperties();
+ for (const BindingProperty &property : bindingProps) {
+ if (property.expression() == (id + '.' + nameAsQString()))
return true;
+ }
return false;
}
bool PropertyEditorValue::isAttachedProperty() const
{
- if (nameAsQString().isEmpty())
- return false;
-
- return nameAsQString().at(0).isUpper();
+ return !nameAsQString().isEmpty() && nameAsQString().at(0).isUpper();
}
void PropertyEditorValue::removeAliasExport()
@@ -403,7 +387,7 @@ QString PropertyEditorValue::getTranslationContext() const
if (auto metaInfo = modelNode().metaInfo();
metaInfo.isValid() && metaInfo.hasProperty(name())
&& metaInfo.property(name()).propertyType().isString()) {
- const QmlDesigner::QmlObjectNode objectNode(modelNode());
+ const QmlObjectNode objectNode(modelNode());
if (objectNode.hasBindingProperty(name())) {
const QRegularExpression rx(QRegularExpression::anchoredPattern(
"qsTranslate\\(\"(.*)\"\\s*,\\s*\".*\"\\s*\\)"));
@@ -419,19 +403,19 @@ QString PropertyEditorValue::getTranslationContext() const
bool PropertyEditorValue::isIdList() const
{
if (modelNode().isValid() && modelNode().metaInfo().isValid() && modelNode().metaInfo().hasProperty(name())) {
- const QmlDesigner::QmlObjectNode objectNode(modelNode());
+ const QmlObjectNode objectNode(modelNode());
if (objectNode.hasBindingProperty(name())) {
static const QRegularExpression rx(QRegularExpression::anchoredPattern(
"^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+"));
- const QString exp = objectNode.propertyAffectedByCurrentState(name()) ? expression() : modelNode().bindingProperty(name()).expression();
- for (const auto &str : generateStringList(exp))
- {
- if (!str.contains(rx))
+ const QString exp = objectNode.propertyAffectedByCurrentState(name())
+ ? expression() : modelNode().bindingProperty(name()).expression();
+ const QStringList idList = generateStringList(exp);
+ for (const auto &id : idList) {
+ if (!id.contains(rx))
return false;
}
return true;
}
- return false;
}
return false;
}
@@ -443,8 +427,8 @@ QStringList PropertyEditorValue::getExpressionAsList() const
bool PropertyEditorValue::idListAdd(const QString &value)
{
- const QmlDesigner::QmlObjectNode objectNode(modelNode());
- if (!isIdList() && (objectNode.isValid() && objectNode.hasProperty(name())))
+ const QmlObjectNode objectNode(modelNode());
+ if (!isIdList() && objectNode.isValid() && objectNode.hasProperty(name()))
return false;
static const QRegularExpression rx(QRegularExpression::anchoredPattern(
@@ -467,8 +451,12 @@ bool PropertyEditorValue::idListRemove(int idx)
if (idx < 0 || idx >= stringList.size())
return false;
- stringList.removeAt(idx);
- setExpressionWithEmit(generateString(stringList));
+ if (stringList.size() == 1) {
+ resetValue();
+ } else {
+ stringList.removeAt(idx);
+ setExpressionWithEmit(generateString(stringList));
+ }
return true;
}
@@ -498,30 +486,11 @@ void PropertyEditorValue::commitDrop(const QString &dropData)
if (m_modelNode.metaInfo().isQtQuick3DMaterial()
&& m_modelNode.metaInfo().property(m_name).propertyType().isQtQuick3DTexture()) {
m_modelNode.view()->executeInTransaction(__FUNCTION__, [&] {
- QmlDesigner::ModelNode texture = m_modelNode.view()->modelNodeForInternalId(dropData.toInt());
+ ModelNode texture = m_modelNode.view()->modelNodeForInternalId(dropData.toInt());
if (!texture || !texture.metaInfo().isQtQuick3DTexture()) {
- Utils::FilePath imagePath = Utils::FilePath::fromString(dropData);
- Utils::FilePath currFilePath = QmlDesigner::DocumentManager::currentFilePath();
- QString sourceVal = imagePath.relativePathFrom(currFilePath).toString();
- texture = m_modelNode.view()->getTextureDefaultInstance(sourceVal);
-
- if (!texture.isValid()) {
- // create a texture node
- QmlDesigner::NodeMetaInfo metaInfo = m_modelNode.view()->model()->metaInfo("QtQuick3D.Texture");
- texture = m_modelNode.view()->createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(),
- metaInfo.minorVersion());
- texture.validId();
- m_modelNode.view()->materialLibraryNode().defaultNodeListProperty().reparentHere(texture);
- }
-
- // set texture source
- QmlDesigner::VariantProperty srcProp = texture.variantProperty("source");
- srcProp.setValue(sourceVal);
-
- QTimer::singleShot(0, this, [this, texture]() {
- if (m_modelNode.isValid() && texture.isValid() && m_modelNode.view())
- m_modelNode.view()->emitCustomNotification("selected_texture_changed", {texture});
- });
+ auto texCreator = new CreateTexture(m_modelNode.view());
+ texture = texCreator->execute(dropData, AddTextureMode::Texture);
+ texCreator->deleteLater();
}
// assign the texture to the property
@@ -532,6 +501,12 @@ void PropertyEditorValue::commitDrop(const QString &dropData)
m_modelNode.view()->model()->endDrag();
}
+void PropertyEditorValue::openMaterialEditor(int idx)
+{
+ QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor", true);
+ m_modelNode.view()->emitCustomNotification("select_material", {}, {idx});
+}
+
QStringList PropertyEditorValue::generateStringList(const QString &string) const
{
QString copy = string;
@@ -556,40 +531,40 @@ QString PropertyEditorValue::generateString(const QStringList &stringList) const
void PropertyEditorValue::registerDeclarativeTypes()
{
- qmlRegisterType<PropertyEditorValue>("HelperWidgets",2,0,"PropertyEditorValue");
- qmlRegisterType<PropertyEditorNodeWrapper>("HelperWidgets",2,0,"PropertyEditorNodeWrapper");
- qmlRegisterType<QQmlPropertyMap>("HelperWidgets",2,0,"QQmlPropertyMap");
+ qmlRegisterType<PropertyEditorValue>("HelperWidgets", 2, 0, "PropertyEditorValue");
+ qmlRegisterType<PropertyEditorNodeWrapper>("HelperWidgets", 2, 0, "PropertyEditorNodeWrapper");
+ qmlRegisterType<QQmlPropertyMap>("HelperWidgets", 2, 0, "QQmlPropertyMap");
}
-PropertyEditorNodeWrapper::PropertyEditorNodeWrapper(PropertyEditorValue* parent) : QObject(parent), m_valuesPropertyMap(this)
+PropertyEditorNodeWrapper::PropertyEditorNodeWrapper(PropertyEditorValue *parent)
+ : QObject(parent),
+ m_valuesPropertyMap(this)
{
m_editorValue = parent;
connect(m_editorValue, &PropertyEditorValue::modelNodeChanged, this, &PropertyEditorNodeWrapper::update);
}
-PropertyEditorNodeWrapper::PropertyEditorNodeWrapper(QObject *parent) : QObject(parent), m_editorValue(nullptr)
+PropertyEditorNodeWrapper::PropertyEditorNodeWrapper(QObject *parent)
+ : QObject(parent)
{
}
-bool PropertyEditorNodeWrapper::exists()
+bool PropertyEditorNodeWrapper::exists() const
{
- if (!(m_editorValue && m_editorValue->modelNode().isValid()))
- return false;
-
- return m_modelNode.isValid();
+ return m_editorValue && m_editorValue->modelNode().isValid() && m_modelNode.isValid();
}
-QString PropertyEditorNodeWrapper::type()
+QString PropertyEditorNodeWrapper::type() const
{
return m_modelNode.simplifiedTypeName();
}
-QmlDesigner::ModelNode PropertyEditorNodeWrapper::parentModelNode() const
+ModelNode PropertyEditorNodeWrapper::parentModelNode() const
{
return m_editorValue->modelNode();
}
-QmlDesigner::PropertyName PropertyEditorNodeWrapper::propertyName() const
+PropertyName PropertyEditorNodeWrapper::propertyName() const
{
return m_editorValue->name();
}
@@ -601,7 +576,7 @@ QQmlPropertyMap *PropertyEditorNodeWrapper::properties()
void PropertyEditorNodeWrapper::add(const QString &type)
{
- QmlDesigner::TypeName propertyType = type.toUtf8();
+ TypeName propertyType = type.toUtf8();
if ((m_editorValue && m_editorValue->modelNode().isValid())) {
if (propertyType.isEmpty()) {
@@ -611,7 +586,7 @@ void PropertyEditorNodeWrapper::add(const QString &type)
.propertyType()
.typeName();
}
- while (propertyType.contains('*')) //strip star
+ while (propertyType.contains('*')) // strip star
propertyType.chop(1);
m_modelNode = m_editorValue->modelNode().view()->createModelNode(propertyType, 4, 7);
m_editorValue->modelNode().nodeAbstractProperty(m_editorValue->name()).reparentHere(m_modelNode);
@@ -626,12 +601,12 @@ void PropertyEditorNodeWrapper::add(const QString &type)
void PropertyEditorNodeWrapper::remove()
{
if ((m_editorValue && m_editorValue->modelNode().isValid())) {
- QmlDesigner::QmlObjectNode(m_modelNode).destroy();
+ QmlObjectNode(m_modelNode).destroy();
m_editorValue->modelNode().removeProperty(m_editorValue->name());
} else {
qWarning("PropertyEditorNodeWrapper::remove failed - node invalid");
}
- m_modelNode = QmlDesigner::ModelNode();
+ m_modelNode = ModelNode();
const QStringList propertyNames = m_valuesPropertyMap.keys();
for (const QString &propertyName : propertyNames)
@@ -643,13 +618,14 @@ void PropertyEditorNodeWrapper::remove()
void PropertyEditorNodeWrapper::changeValue(const QString &propertyName)
{
- const QmlDesigner::PropertyName name = propertyName.toUtf8();
+ const PropertyName name = propertyName.toUtf8();
if (name.isNull())
return;
+
if (m_modelNode.isValid()) {
- QScopedPointer<QmlDesigner::QmlObjectNode> qmlObjectNode{
- QmlDesigner::QmlObjectNode::getQmlObjectNodeOfCorrectType(m_modelNode)};
+ QScopedPointer<QmlObjectNode> qmlObjectNode{
+ QmlObjectNode::getQmlObjectNodeOfCorrectType(m_modelNode)};
auto valueObject = qvariant_cast<PropertyEditorValue *>(m_valuesPropertyMap.value(QString::fromLatin1(name)));
@@ -664,14 +640,16 @@ void PropertyEditorNodeWrapper::setup()
{
Q_ASSERT(m_editorValue);
Q_ASSERT(m_editorValue->modelNode().isValid());
- if ((m_editorValue->modelNode().isValid() && m_modelNode.isValid())) {
+
+ if (m_editorValue->modelNode().isValid() && m_modelNode.isValid()) {
const QStringList propertyNames = m_valuesPropertyMap.keys();
for (const QString &propertyName : propertyNames)
m_valuesPropertyMap.clear(propertyName);
qDeleteAll(m_valuesPropertyMap.children());
- if (QmlDesigner::QmlObjectNode qmlObjectNode = m_modelNode) {
- for (const auto &property : m_modelNode.metaInfo().properties()) {
+ if (QmlObjectNode qmlObjectNode = m_modelNode) {
+ const PropertyMetaInfos props = m_modelNode.metaInfo().properties();
+ for (const auto &property : props) {
const auto &propertyName = property.name();
auto valueObject = new PropertyEditorValue(&m_valuesPropertyMap);
valueObject->setName(propertyName);
@@ -689,11 +667,17 @@ void PropertyEditorNodeWrapper::setup()
void PropertyEditorNodeWrapper::update()
{
- if (m_editorValue && m_editorValue->modelNode().isValid()) {
- if (m_editorValue->modelNode().hasProperty(m_editorValue->name()) && m_editorValue->modelNode().property(m_editorValue->name()).isNodeProperty())
- m_modelNode = m_editorValue->modelNode().nodeProperty(m_editorValue->name()).modelNode();
- setup();
- emit existsChanged();
- emit typeChanged();
+ if (!m_editorValue || !m_editorValue->modelNode().isValid())
+ return;
+
+ if (m_editorValue->modelNode().hasProperty(m_editorValue->name())
+ && m_editorValue->modelNode().property(m_editorValue->name()).isNodeProperty()) {
+ m_modelNode = m_editorValue->modelNode().nodeProperty(m_editorValue->name()).modelNode();
}
+
+ setup();
+ emit existsChanged();
+ emit typeChanged();
}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
index fa995bd619..9a4bbd280e 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
@@ -3,31 +3,34 @@
#pragma once
-#include <qmldesignercorelib_global.h>
+#include "modelnode.h"
+#include "qmldesignercorelib_global.h"
#include <QObject>
#include <QQmlPropertyMap>
#include <QtQml>
-#include <modelnode.h>
-#include <enumeration.h>
+
+namespace QmlDesigner {
class PropertyEditorValue;
-class PropertyEditorNodeWrapper : public QObject {
+class PropertyEditorNodeWrapper : public QObject
+{
Q_OBJECT
Q_PROPERTY(bool exists READ exists NOTIFY existsChanged)
- Q_PROPERTY(QQmlPropertyMap* properties READ properties NOTIFY propertiesChanged)
+ Q_PROPERTY(QQmlPropertyMap *properties READ properties NOTIFY propertiesChanged)
Q_PROPERTY(QString type READ type NOTIFY typeChanged)
public:
- PropertyEditorNodeWrapper(QObject *parent=nullptr);
- PropertyEditorNodeWrapper(PropertyEditorValue* parent);
- bool exists();
- QString type();
- QQmlPropertyMap* properties();
- QmlDesigner::ModelNode parentModelNode() const;
- QmlDesigner::PropertyName propertyName() const;
+ PropertyEditorNodeWrapper(QObject *parent = nullptr);
+ PropertyEditorNodeWrapper(PropertyEditorValue *parent);
+
+ bool exists() const;
+ QString type() const;
+ QQmlPropertyMap *properties();
+ ModelNode parentModelNode() const;
+ PropertyName propertyName() const;
public slots:
void add(const QString &type = QString());
@@ -43,14 +46,15 @@ signals:
private:
void setup();
- QmlDesigner::ModelNode m_modelNode;
+ ModelNode m_modelNode;
QQmlPropertyMap m_valuesPropertyMap;
- PropertyEditorValue* m_editorValue;
+ PropertyEditorValue *m_editorValue = nullptr;
};
class PropertyEditorValue : public QObject
{
Q_OBJECT
+
Q_PROPERTY(QVariant value READ value WRITE setValueWithEmit NOTIFY valueChangedQml)
Q_PROPERTY(QVariant enumeration READ enumeration NOTIFY valueChangedQml)
Q_PROPERTY(QString expression READ expression WRITE setExpressionWithEmit NOTIFY expressionChanged FINAL)
@@ -65,13 +69,13 @@ class PropertyEditorValue : public QObject
Q_PROPERTY(bool isIdList READ isIdList NOTIFY expressionChanged FINAL)
Q_PROPERTY(QStringList expressionAsList READ getExpressionAsList NOTIFY expressionChanged FINAL)
- Q_PROPERTY(QString name READ nameAsQString FINAL)
- Q_PROPERTY(PropertyEditorNodeWrapper* complexNode READ complexNode NOTIFY complexNodeChanged FINAL)
+ Q_PROPERTY(QString name READ nameAsQString CONSTANT FINAL)
+ Q_PROPERTY(PropertyEditorNodeWrapper *complexNode READ complexNode NOTIFY complexNodeChanged FINAL)
Q_PROPERTY(bool isAvailable READ isAvailable NOTIFY isBoundChanged)
public:
- PropertyEditorValue(QObject *parent=nullptr);
+ PropertyEditorValue(QObject *parent = nullptr);
QVariant value() const;
void setValueWithEmit(const QVariant &value);
@@ -101,14 +105,14 @@ public:
bool isAvailable() const;
- QmlDesigner::PropertyName name() const;
+ PropertyName name() const;
QString nameAsQString() const;
- void setName(const QmlDesigner::PropertyName &name);
+ void setName(const PropertyName &name);
- QmlDesigner::ModelNode modelNode() const;
- void setModelNode(const QmlDesigner::ModelNode &modelNode);
+ ModelNode modelNode() const;
+ void setModelNode(const ModelNode &modelNode);
- PropertyEditorNodeWrapper* complexNode();
+ PropertyEditorNodeWrapper *complexNode();
static void registerDeclarativeTypes();
@@ -126,19 +130,18 @@ public:
Q_INVOKABLE bool idListRemove(int idx);
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
Q_INVOKABLE void commitDrop(const QString &dropData);
+ Q_INVOKABLE void openMaterialEditor(int idx);
public slots:
void resetValue();
void setEnumeration(const QString &scope, const QString &name);
signals:
- void valueChanged(const QString &name, const QVariant&);
+ void valueChanged(const QString &name, const QVariant &);
void valueChangedQml();
- void expressionChanged(const QString &name); //HACK - We use the same notifer
- //for the backend and frontend.
- //If name is empty the signal is
- //used for QML.
+ void expressionChanged(const QString &name); // HACK - We use the same notifer for the backend and frontend.
+ // If name is empty the signal is used for QML.
void exportPropertyAsAliasRequested(const QString &name);
void removeAliasExportRequested(const QString &name);
@@ -155,17 +158,19 @@ private:
QStringList generateStringList(const QString &string) const;
QString generateString(const QStringList &stringList) const;
- QmlDesigner::ModelNode m_modelNode;
+ ModelNode m_modelNode;
QVariant m_value;
QString m_expression;
- QmlDesigner::PropertyName m_name;
- bool m_isInSubState;
- bool m_isInModel;
- bool m_isBound;
+ PropertyName m_name;
+ bool m_isInSubState = false;
+ bool m_isInModel = false;
+ bool m_isBound = false;
bool m_hasActiveDrag = false;
- bool m_isValid; // if the property value belongs to a non-existing complexProperty it is invalid
+ bool m_isValid = false; // if the property value belongs to a non-existing complexProperty it is invalid
PropertyEditorNodeWrapper *m_complexNode;
};
-QML_DECLARE_TYPE(PropertyEditorValue)
-QML_DECLARE_TYPE(PropertyEditorNodeWrapper)
+} // namespace QmlDesigner
+
+QML_DECLARE_TYPE(QmlDesigner::PropertyEditorValue)
+QML_DECLARE_TYPE(QmlDesigner::PropertyEditorNodeWrapper)
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
index 809ad79e7c..5dff76453a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
@@ -6,6 +6,7 @@
#include "propertyeditorqmlbackend.h"
#include "propertyeditortransaction.h"
#include "propertyeditorvalue.h"
+#include "propertyeditorwidget.h"
#include <auxiliarydataproperties.h>
#include <nodemetainfo.h>
@@ -33,6 +34,7 @@
#include <QFileSystemWatcher>
#include <QFileInfo>
#include <QDebug>
+#include <QQuickItem>
#include <QTimer>
#include <QShortcut>
#include <QApplication>
@@ -501,6 +503,7 @@ void PropertyEditorView::setupQmlBackend()
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
}
+ currentQmlBackend->widget()->installEventFilter(this);
m_stackedWidget->setCurrentWidget(currentQmlBackend->widget());
currentQmlBackend->contextObject()->triggerSelectionChanged();
@@ -629,7 +632,7 @@ void PropertyEditorView::modelAboutToBeDetached(Model *model)
resetView();
}
-void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty>& propertyList)
+void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
{
if (noValidSelection())
return;
@@ -641,7 +644,11 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty>& proper
m_qmlBackEndForCurrentType->contextObject()->setHasAliasExport(QmlObjectNode(m_selectedNode).isAliasExported());
if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
- setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
+ m_locked = true;
+ PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromUtf8(property.name()));
+ if (value)
+ value->resetValue();
+ m_locked = false;
if (propertyIsAttachedLayoutProperty(property.name())) {
m_qmlBackEndForCurrentType->setValueforLayoutAttachedProperties(m_selectedNode, property.name());
@@ -697,12 +704,9 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>&
}
}
-void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags /*propertyChange*/)
+void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags /*propertyChange*/)
{
- if (locked())
- return;
-
- if (noValidSelection())
+ if (locked() || noValidSelection())
return;
for (const BindingProperty &property : propertyList) {
@@ -714,11 +718,11 @@ void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty>&
if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
if (property.name().contains("anchor"))
m_qmlBackEndForCurrentType->backendAnchorBinding().invalidate(m_selectedNode);
- if ( QmlObjectNode(m_selectedNode).modelNode().property(property.name()).isBindingProperty())
- setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
- else
- setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
+ m_locked = true;
+ QString exp = QmlObjectNode(m_selectedNode).bindingProperty(property.name()).expression();
+ m_qmlBackEndForCurrentType->setExpression(property.name(), exp);
+ m_locked = false;
}
}
}
@@ -806,7 +810,12 @@ bool PropertyEditorView::hasWidget() const
WidgetInfo PropertyEditorView::widgetInfo()
{
- return createWidgetInfo(m_stackedWidget, QStringLiteral("Properties"), WidgetInfo::RightPane, 0, tr("Properties"));
+ return createWidgetInfo(m_stackedWidget,
+ QStringLiteral("Properties"),
+ WidgetInfo::RightPane,
+ 0,
+ tr("Properties"),
+ tr("Property Editor view"));
}
void PropertyEditorView::currentStateChanged(const ModelNode &node)
@@ -892,6 +901,15 @@ void PropertyEditorView::setValue(const QmlObjectNode &qmlObjectNode,
m_locked = false;
}
+bool PropertyEditorView::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::FocusOut) {
+ if (m_qmlBackEndForCurrentType && m_qmlBackEndForCurrentType->widget() == obj)
+ QMetaObject::invokeMethod(m_qmlBackEndForCurrentType->widget()->rootObject(), "closeContextMenu");
+ }
+ return AbstractView::eventFilter(obj, event);
+}
+
void PropertyEditorView::reloadQml()
{
m_qmlBackendHash.clear();
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
index 7964420251..bb2f9dc360 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
@@ -3,13 +3,15 @@
#pragma once
-#include <abstractview.h>
+#include "abstractview.h"
+
#include <QHash>
+#include <QObject>
#include <QTimer>
-#include "propertyeditorwidget.h"
QT_BEGIN_NAMESPACE
+class QEvent;
class QShortcut;
class QStackedWidget;
class QTimer;
@@ -17,14 +19,13 @@ QT_END_NAMESPACE
namespace QmlDesigner {
-class PropertyEditorTransaction;
class CollapseButton;
-class PropertyEditorWidget;
-class PropertyEditorView;
-class PropertyEditorQmlBackend;
class ModelNode;
+class PropertyEditorQmlBackend;
+class PropertyEditorView;
+class PropertyEditorWidget;
-class PropertyEditorView: public AbstractView
+class PropertyEditorView : public AbstractView
{
Q_OBJECT
@@ -84,6 +85,7 @@ protected:
void timerEvent(QTimerEvent *event) override;
void setupPane(const TypeName &typeName);
void setValue(const QmlObjectNode &fxObjectNode, const PropertyName &name, const QVariant &value);
+ bool eventFilter(QObject *obj, QEvent *event) override;
private: //functions
void reloadQml();
diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp
index 5a1e10220c..610c0e2731 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp
@@ -3,7 +3,10 @@
#include "quick2propertyeditorview.h"
+#include <qmldesignerconstants.h>
+
#include "aligndistribute.h"
+#include "assetimageprovider.h"
#include "annotationeditor/annotationeditor.h"
#include "bindingeditor/actioneditor.h"
#include "bindingeditor/bindingeditor.h"
@@ -16,7 +19,6 @@
#include "itemfiltermodel.h"
#include "propertychangesmodel.h"
#include "propertyeditorcontextobject.h"
-#include "propertyeditorimageprovider.h"
#include "propertyeditorqmlbackend.h"
#include "propertyeditorvalue.h"
#include "propertymodel.h"
@@ -30,10 +32,11 @@ namespace QmlDesigner {
Quick2PropertyEditorView::Quick2PropertyEditorView(AsynchronousImageCache &imageCache)
: QQuickWidget()
{
+ setObjectName(Constants::OBJECT_NAME_PROPERTY_EDITOR);
setResizeMode(QQuickWidget::SizeRootObjectToView);
Theme::setupTheme(engine());
engine()->addImageProvider("qmldesigner_thumbnails",
- new PropertyEditorImageProvider(imageCache));
+ new AssetImageProvider(imageCache));
}
void Quick2PropertyEditorView::registerQmlTypes()
diff --git a/src/plugins/qmldesigner/components/resources/dockwidgets.css b/src/plugins/qmldesigner/components/resources/dockwidgets.css
index 84865f4749..3404065521 100644
--- a/src/plugins/qmldesigner/components/resources/dockwidgets.css
+++ b/src/plugins/qmldesigner/components/resources/dockwidgets.css
@@ -29,6 +29,7 @@ ADS--DockWidgetTab {
border-color: creatorTheme.DStabSplitter;
border-style: solid;
border-width: 0 1px 0 0;
+ padding-left: 16px;
}
ADS--DockWidgetTab QLabel {
@@ -44,7 +45,7 @@ ADS--DockWidgetTab[activeTab="true"] QLabel {
}
ADS--DockWidgetTab[activeTab="true"] > #tabCloseButton:hover {
- background: creatorTheme.DStabActiveButtonHover;
+ background: creatorTheme.DStabActiveBackground;
}
ADS--DockWidgetTab[activeTab="true"] > #tabCloseButton:pressed {
@@ -105,7 +106,7 @@ QScrollArea#dockWidgetScrollArea {
}
#tabCloseButton:hover {
- background: creatorTheme.DStabInactiveButtonHover;
+ background: creatorTheme.DStabActiveBackground;
}
#tabCloseButton:pressed {
diff --git a/src/plugins/qmldesigner/components/resources/stylesheet.css b/src/plugins/qmldesigner/components/resources/stylesheet.css
index 501e9ab1d5..e8587d57b4 100644
--- a/src/plugins/qmldesigner/components/resources/stylesheet.css
+++ b/src/plugins/qmldesigner/components/resources/stylesheet.css
@@ -95,3 +95,44 @@ QLineEdit:focus {
background-color: creatorTheme.DScontrolBackgroundInteraction;
border-color: creatorTheme.DScontrolOutlineInteraction;
}
+
+QToolBar QLineEdit {
+ height: 27px; min-height: 27px; max-height: 27px;
+ border-radius: 4;
+ color: creatorTheme.DStextColor;
+ border: 1px solid creatorTheme.DScontrolOutline_topToolbarIdle;
+ background: creatorTheme.DStoolbarBackground;
+ /*font-size: 12px;*/
+}
+
+QToolBar QLineEdit:hover {
+ color: creatorTheme.DStextColor;
+ border: 1px solid creatorTheme.DScontrolOutline_topToolbarHover;
+ background: creatorTheme.DScontrolBackground_toolbarHover;
+}
+
+QToolBar QLineEdit:focus {
+ color: creatorTheme.DStextColor;
+ border: 1px solid creatorTheme.DSinteraction;
+ background: creatorTheme.DStoolbarBackground;
+}
+
+QToolBar QToolButton {
+ border: none;
+ padding: 0px;
+ width: 29px; min-width: 29px; max-width: 29px;
+ height: 29px; min-height: 29px; max-height: 29px;
+ border-radius: 4px;
+}
+
+QToolBar QToolButton:hover {
+ background-color: creatorTheme.DScontrolBackground_topToolbarHover;
+}
+
+QToolBar QToolButton:pressed {
+ background-color: creatorTheme.DSinteraction;
+}
+
+QToolBar QToolButton:checked {
+ background-color: creatorTheme.DSinteraction;
+}
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp
index 9f0e871044..55fcf61932 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp
@@ -6,14 +6,11 @@
#include <QDebug>
-namespace QmlDesigner {
-
-namespace Internal {
+namespace QmlDesigner::Internal {
StatesEditorImageProvider::StatesEditorImageProvider()
: QQuickImageProvider(QQuickImageProvider::Image)
-{
-}
+{}
QImage StatesEditorImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
@@ -28,7 +25,8 @@ QImage StatesEditorImageProvider::requestImage(const QString &id, QSize *size, c
bool canBeConverted;
int instanceId = imageId.toInt(&canBeConverted);
if (canBeConverted && m_nodeInstanceView->hasModelNodeForInternalId(instanceId)) {
- image = m_nodeInstanceView->statePreviewImage(m_nodeInstanceView->modelNodeForInternalId(instanceId));
+ image = m_nodeInstanceView->statePreviewImage(
+ m_nodeInstanceView->modelNodeForInternalId(instanceId));
}
}
}
@@ -37,7 +35,7 @@ QImage StatesEditorImageProvider::requestImage(const QString &id, QSize *size, c
//creating white QImage
QSize newSize = requestedSize;
if (newSize.isEmpty())
- newSize = QSize (100, 100);
+ newSize = QSize(100, 100);
QImage image(newSize, QImage::Format_ARGB32);
image.fill(0xFFFFFFFF);
@@ -54,7 +52,4 @@ void StatesEditorImageProvider::setNodeInstanceView(const NodeInstanceView *node
m_nodeInstanceView = nodeInstanceView;
}
-}
-
-}
-
+} // QmlDesigner::Internal
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h
index 29357c80f3..0bfb0bf330 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h
@@ -3,10 +3,10 @@
#pragma once
-#include"abstractview.h"
+#include "abstractview.h"
-#include <QQuickImageProvider>
#include <QPointer>
+#include <QQuickImageProvider>
namespace QmlDesigner {
namespace Internal {
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
index 0568d3c523..96ec3fa8e6 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
@@ -6,30 +6,26 @@
#include <QDebug>
-#include <nodelistproperty.h>
-#include <modelnode.h>
#include <bindingproperty.h>
-#include <variantproperty.h>
+#include <modelnode.h>
+#include <nodelistproperty.h>
#include <rewriterview.h>
+#include <variantproperty.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#include <QWidget>
-enum {
- debug = false
-};
-
+enum { debug = false };
namespace QmlDesigner {
StatesEditorModel::StatesEditorModel(StatesEditorView *view)
- : QAbstractListModel(view),
- m_statesEditorView(view),
- m_updateCounter(0)
-{
-}
+ : QAbstractListModel(view)
+ , m_statesEditorView(view)
+ , m_updateCounter(0)
+{}
int StatesEditorModel::count() const
{
@@ -43,9 +39,12 @@ QModelIndex StatesEditorModel::index(int row, int column, const QModelIndex &par
int internalNodeId = 0;
if (row > 0 && row < rowCount() - 1) // first and last rows are base state, add state
- internalNodeId = m_statesEditorView->acitveStatesGroupNode().nodeListProperty("states").at(row - 1).internalId();
+ internalNodeId = m_statesEditorView->acitveStatesGroupNode()
+ .nodeListProperty("states")
+ .at(row - 1)
+ .internalId();
- return hasIndex(row, column, parent) ? createIndex(row, column, internalNodeId) : QModelIndex();
+ return hasIndex(row, column, parent) ? createIndex(row, column, internalNodeId) : QModelIndex();
}
int StatesEditorModel::rowCount(const QModelIndex &parent) const
@@ -56,7 +55,8 @@ int StatesEditorModel::rowCount(const QModelIndex &parent) const
if (!m_statesEditorView->acitveStatesGroupNode().hasNodeListProperty("states"))
return 2; // base state + add new state
- return m_statesEditorView->acitveStatesGroupNode().nodeListProperty("states").count() + 2; // 2 = base state + add new state
+ return m_statesEditorView->acitveStatesGroupNode().nodeListProperty("states").count()
+ + 2; // 2 = base state + add new state
}
void StatesEditorModel::reset()
@@ -67,7 +67,8 @@ void StatesEditorModel::reset()
QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
{
- if (index.parent().isValid() || index.column() != 0 || m_statesEditorView.isNull() || !m_statesEditorView->hasModelNodeForInternalId(index.internalId()))
+ if (index.parent().isValid() || index.column() != 0 || m_statesEditorView.isNull()
+ || !m_statesEditorView->hasModelNodeForInternalId(index.internalId()))
return QVariant();
ModelNode stateNode;
@@ -121,16 +122,14 @@ QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
QHash<int, QByteArray> StatesEditorModel::roleNames() const
{
- static QHash<int, QByteArray> roleNames {
- {StateNameRole, "stateName"},
- {StateImageSourceRole, "stateImageSource"},
- {InternalNodeId, "internalNodeId"},
- {HasWhenCondition, "hasWhenCondition"},
- {WhenConditionString, "whenConditionString"},
- {IsDefault, "isDefault"},
- {ModelHasDefaultState, "modelHasDefaultState"},
- {StateType, "type"}
- };
+ static QHash<int, QByteArray> roleNames{{StateNameRole, "stateName"},
+ {StateImageSourceRole, "stateImageSource"},
+ {InternalNodeId, "internalNodeId"},
+ {HasWhenCondition, "hasWhenCondition"},
+ {WhenConditionString, "whenConditionString"},
+ {IsDefault, "isDefault"},
+ {ModelHasDefaultState, "modelHasDefaultState"},
+ {StateType, "type"}};
return roleNames;
}
@@ -167,19 +166,17 @@ void StatesEditorModel::renameState(int internalNodeId, const QString &newName)
if (newName == m_statesEditorView->currentStateName())
return;
- if (newName.isEmpty() ||! m_statesEditorView->validStateName(newName)) {
- QTimer::singleShot(0, [newName]{
+ if (newName.isEmpty() || !m_statesEditorView->validStateName(newName)) {
+ QTimer::singleShot(0, [newName] {
Core::AsynchronousMessageBox::warning(
- tr("Invalid State Name"),
- newName.isEmpty() ?
- tr("The empty string as a name is reserved for the base state.") :
- tr("Name already used in another state."));
+ tr("Invalid State Name"),
+ newName.isEmpty() ? tr("The empty string as a name is reserved for the base state.")
+ : tr("Name already used in another state."));
});
reset();
} else {
m_statesEditorView->renameState(internalNodeId, newName);
}
-
}
void StatesEditorModel::setWhenCondition(int internalNodeId, const QString &condition)
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h
index 83e3cb743e..832f0cdbff 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h
@@ -6,7 +6,6 @@
#include <QAbstractListModel>
#include <QPointer>
-
namespace QmlDesigner {
class StatesEditorView;
@@ -52,7 +51,6 @@ public:
void reset();
-
signals:
void changedToState(int n);
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
index 54711a4168..2b64138387 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
@@ -2,30 +2,30 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "stateseditorview.h"
-#include "stateseditorwidget.h"
-#include "stateseditormodel.h"
-#include <rewritingexception.h>
-#include <QDebug>
-#include <QRegularExpression>
-#include <QMessageBox>
-#include <cmath>
-#include <memory>
-
-#include <nodemetainfo.h>
+#include "stateseditormodel.h"
+#include "stateseditorwidget.h"
+#include <rewritingexception.h>
#include <bindingproperty.h>
-#include <variantproperty.h>
#include <nodelistproperty.h>
-
+#include <nodemetainfo.h>
+#include <variantproperty.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <qmlitemnode.h>
#include <qmlstate.h>
+
#include <annotationeditor/annotationeditor.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QDebug>
+#include <QMessageBox>
+#include <QRegularExpression>
+
+#include <cmath>
+#include <memory>
namespace QmlDesigner {
@@ -55,10 +55,17 @@ WidgetInfo StatesEditorView::widgetInfo()
if (!m_statesEditorWidget)
m_statesEditorWidget = new StatesEditorWidget(this, m_statesEditorModel.data());
- return createWidgetInfo(m_statesEditorWidget.data(), QLatin1String("StatesEditor"), WidgetInfo::BottomPane, 0, tr("States"));
+ return createWidgetInfo(m_statesEditorWidget.data(),
+ QLatin1String("StatesEditor"),
+ WidgetInfo::BottomPane,
+ 0,
+ tr("States"),
+ tr("States view"));
}
-void StatesEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/)
+void StatesEditorView::rootNodeTypeChanged(const QString & /*type*/,
+ int /*majorVersion*/,
+ int /*minorVersion*/)
{
checkForStatesAvailability();
}
@@ -89,13 +96,14 @@ void StatesEditorView::removeState(int nodeId)
QStringList lockedTargets;
const auto propertyChanges = modelState.propertyChanges();
- // confirm removing not empty states
+ // confirm removing not empty states
if (!propertyChanges.isEmpty()) {
QMessageBox msgBox;
msgBox.setTextFormat(Qt::RichText);
msgBox.setIcon(QMessageBox::Question);
msgBox.setWindowTitle(tr("Remove State"));
- msgBox.setText(tr("This state is not empty. Are you sure you want to remove it?"));
+ msgBox.setText(
+ tr("This state is not empty. Are you sure you want to remove it?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Yes);
@@ -124,8 +132,9 @@ void StatesEditorView::removeState(int nodeId)
msgBox.setTextFormat(Qt::RichText);
msgBox.setIcon(QMessageBox::Question);
msgBox.setWindowTitle(tr("Remove State"));
- msgBox.setText(QString(tr("Removing this state will modify locked components.") + "<br><br>%1")
- .arg(detailedText));
+ msgBox.setText(QString(tr("Removing this state will modify locked components.")
+ + "<br><br>%1")
+ .arg(detailedText));
msgBox.setInformativeText(tr("Continue by removing the state?"));
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
@@ -149,7 +158,7 @@ void StatesEditorView::removeState(int nodeId)
stateNode.destroy();
}
- } catch (const RewritingException &e) {
+ } catch (const RewritingException &e) {
e.showException();
}
}
@@ -480,12 +489,14 @@ void StatesEditorView::modelAboutToBeDetached(Model *model)
resetModel();
}
-void StatesEditorView::propertiesRemoved(const QList<AbstractProperty>& propertyList)
+void StatesEditorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
{
for (const AbstractProperty &property : propertyList) {
- if (property.name() == "states" && property.parentModelNode() == activeStateGroup().modelNode())
+ if (property.name() == "states"
+ && property.parentModelNode() == activeStateGroup().modelNode())
resetModel();
- if (property.name() == "when" && QmlModelState::isValidQmlModelState(property.parentModelNode()))
+ if (property.name() == "when"
+ && QmlModelState::isValidQmlModelState(property.parentModelNode()))
resetModel();
}
}
@@ -494,22 +505,29 @@ void StatesEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode)
{
if (removedNode.hasParentProperty()) {
const NodeAbstractProperty propertyParent = removedNode.parentProperty();
- if (propertyParent.parentModelNode() == activeStateGroup().modelNode() && propertyParent.name() == "states")
+ if (propertyParent.parentModelNode() == activeStateGroup().modelNode()
+ && propertyParent.name() == "states")
m_lastIndex = propertyParent.indexOf(removedNode);
}
if (currentState().isValid() && removedNode == currentState())
setCurrentState(baseState());
}
-void StatesEditorView::nodeRemoved(const ModelNode & /*removedNode*/, const NodeAbstractProperty &parentProperty, PropertyChangeFlags /*propertyChange*/)
+void StatesEditorView::nodeRemoved(const ModelNode & /*removedNode*/,
+ const NodeAbstractProperty &parentProperty,
+ PropertyChangeFlags /*propertyChange*/)
{
- if (parentProperty.isValid() && parentProperty.parentModelNode() == activeStateGroup().modelNode() && parentProperty.name() == "states") {
+ if (parentProperty.isValid() && parentProperty.parentModelNode() == activeStateGroup().modelNode()
+ && parentProperty.name() == "states") {
m_statesEditorModel->removeState(m_lastIndex);
m_lastIndex = -1;
}
}
-void StatesEditorView::nodeAboutToBeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags /*propertyChange*/)
+void StatesEditorView::nodeAboutToBeReparented(const ModelNode &node,
+ const NodeAbstractProperty & /*newPropertyParent*/,
+ const NodeAbstractProperty &oldPropertyParent,
+ AbstractView::PropertyChangeFlags /*propertyChange*/)
{
if (oldPropertyParent.isValid()
&& oldPropertyParent.parentModelNode() == activeStateGroup().modelNode()
@@ -517,8 +535,10 @@ void StatesEditorView::nodeAboutToBeReparented(const ModelNode &node, const Node
m_lastIndex = oldPropertyParent.indexOf(node);
}
-
-void StatesEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags /*propertyChange*/)
+void StatesEditorView::nodeReparented(const ModelNode &node,
+ const NodeAbstractProperty &newPropertyParent,
+ const NodeAbstractProperty &oldPropertyParent,
+ AbstractView::PropertyChangeFlags /*propertyChange*/)
{
if (oldPropertyParent.isValid()
&& oldPropertyParent.parentModelNode() == activeStateGroup().modelNode()
@@ -547,7 +567,8 @@ void StatesEditorView::bindingPropertiesChanged(
[[maybe_unused]] AbstractView::PropertyChangeFlags propertyChange)
{
for (const BindingProperty &property : propertyList) {
- if (property.name() == "when" && QmlModelState::isValidQmlModelState(property.parentModelNode()))
+ if (property.name() == "when"
+ && QmlModelState::isValidQmlModelState(property.parentModelNode()))
resetModel();
}
}
@@ -562,7 +583,8 @@ void StatesEditorView::variantPropertiesChanged(const QList<VariantProperty> &pr
auto guard = qScopeGuard([&]() { m_block = false; });
for (const VariantProperty &property : propertyList) {
- if (property.name() == "name" && QmlModelState::isValidQmlModelState(property.parentModelNode()))
+ if (property.name() == "name"
+ && QmlModelState::isValidQmlModelState(property.parentModelNode()))
resetModel();
else if (property.name() == "state"
&& property.parentModelNode() == activeStateGroup().modelNode())
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h
index f81940c592..52329b940d 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h
@@ -13,14 +13,15 @@ class StatesEditorModel;
class StatesEditorWidget;
class AnnotationEditor;
-class StatesEditorView : public AbstractView {
+class StatesEditorView : public AbstractView
+{
Q_OBJECT
public:
explicit StatesEditorView(ExternalDependenciesInterface &externalDependencies);
~StatesEditorView() override;
- void renameState(int internalNodeId,const QString &newName);
+ void renameState(int internalNodeId, const QString &newName);
void setWhenCondition(int internalNodeId, const QString &condition);
void resetWhenCondition(int internalNodeId);
void setStateAsDefault(int internalNodeId);
@@ -38,7 +39,7 @@ public:
// AbstractView
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
- void propertiesRemoved(const QList<AbstractProperty>& propertyList) override;
+ void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
void nodeRemoved(const ModelNode &removedNode,
const NodeAbstractProperty &parentProperty,
@@ -52,9 +53,10 @@ public:
const NodeAbstractProperty &oldPropertyParent,
AbstractView::PropertyChangeFlags propertyChange) override;
void nodeOrderChanged(const NodeListProperty &listProperty) override;
- void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) override;
- void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) override;
-
+ void bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
+ PropertyChangeFlags propertyChange) override;
+ void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
+ PropertyChangeFlags propertyChange) override;
// AbstractView
void currentStateChanged(const ModelNode &node) override;
@@ -68,7 +70,6 @@ public:
ModelNode acitveStatesGroupNode() const;
void setAcitveStatesGroupNode(const ModelNode &modelNode);
-
public slots:
void synchonizeCurrentStateFromWidget();
void createNewState();
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp
index 14fd7aee26..0501747d98 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "stateseditorwidget.h"
+#include "stateseditorimageprovider.h"
#include "stateseditormodel.h"
#include "stateseditorview.h"
-#include "stateseditorimageprovider.h"
#include <designersettings.h>
#include <theme.h>
@@ -13,8 +13,8 @@
#include <invalidqmlsourceexception.h>
-#include <coreplugin/messagebox.h>
#include <coreplugin/icore.h>
+#include <coreplugin/messagebox.h>
#include <utils/environment.h>
#include <utils/qtcassert.h>
@@ -22,17 +22,16 @@
#include <QApplication>
+#include <QBoxLayout>
#include <QFileInfo>
-#include <QShortcut>
#include <QKeySequence>
+#include <QShortcut>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItem>
-enum {
- debug = false
-};
+enum { debug = false };
namespace QmlDesigner {
@@ -69,10 +68,11 @@ void StatesEditorWidget::showAddNewStatesButton(bool showAddNewStatesButton)
rootContext()->setContextProperty(QLatin1String("canAddNewStates"), showAddNewStatesButton);
}
-StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView, StatesEditorModel *statesEditorModel)
- : m_statesEditorView(statesEditorView),
- m_imageProvider(nullptr),
- m_qmlSourceUpdateShortcut(nullptr)
+StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView,
+ StatesEditorModel *statesEditorModel)
+ : m_statesEditorView(statesEditorView)
+ , m_imageProvider(nullptr)
+ , m_qmlSourceUpdateShortcut(nullptr)
{
m_imageProvider = new Internal::StatesEditorImageProvider;
m_imageProvider->setNodeInstanceView(statesEditorView->nodeInstanceView());
@@ -88,11 +88,9 @@ StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView, State
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
rootContext()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"statesEditorModel"}, QVariant::fromValue(statesEditorModel)},
- {{"canAddNewStates"}, true}
- }
- );
+ QVector<QQmlContext::PropertyPair>{{{"statesEditorModel"},
+ QVariant::fromValue(statesEditorModel)},
+ {{"canAddNewStates"}, true}});
Theme::setupTheme(engine());
@@ -115,7 +113,7 @@ QString StatesEditorWidget::qmlSourcesPath()
void StatesEditorWidget::showEvent(QShowEvent *event)
{
- QQuickWidget::showEvent(event);
+ StudioQuickWidget::showEvent(event);
update();
}
@@ -123,13 +121,13 @@ void StatesEditorWidget::focusOutEvent(QFocusEvent *focusEvent)
{
QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_STATESEDITOR_TIME,
m_usageTimer.elapsed());
- QQuickWidget::focusOutEvent(focusEvent);
+ StudioQuickWidget::focusOutEvent(focusEvent);
}
void StatesEditorWidget::focusInEvent(QFocusEvent *focusEvent)
{
m_usageTimer.restart();
- QQuickWidget::focusInEvent(focusEvent);
+ StudioQuickWidget::focusInEvent(focusEvent);
}
void StatesEditorWidget::reloadQmlSource()
@@ -146,14 +144,17 @@ void StatesEditorWidget::reloadQmlSource()
Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"),
tr("StatesEditorWidget: %1 cannot be created.%2")
- .arg(qmlSourcesPath(), errorString));
+ .arg(qmlSourcesPath(), errorString));
return;
}
- connect(rootObject(), SIGNAL(currentStateInternalIdChanged()), m_statesEditorView.data(), SLOT(synchonizeCurrentStateFromWidget()));
+ connect(rootObject(),
+ SIGNAL(currentStateInternalIdChanged()),
+ m_statesEditorView.data(),
+ SLOT(synchonizeCurrentStateFromWidget()));
connect(rootObject(), SIGNAL(createNewState()), m_statesEditorView.data(), SLOT(createNewState()));
connect(rootObject(), SIGNAL(deleteState(int)), m_statesEditorView.data(), SLOT(removeState(int)));
m_statesEditorView.data()->synchonizeCurrentStateFromWidget();
}
-} // QmlDesigner
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h
index 16e7494794..fb83872a13 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.h
@@ -3,10 +3,11 @@
#pragma once
+#include <studioquickwidget.h>
+
#include <QElapsedTimer>
#include <QPointer>
#include <QQmlPropertyMap>
-#include <QQuickWidget>
QT_BEGIN_NAMESPACE
class QShortcut;
@@ -18,9 +19,11 @@ class StatesEditorModel;
class StatesEditorView;
class NodeInstanceView;
-namespace Internal { class StatesEditorImageProvider; }
+namespace Internal {
+class StatesEditorImageProvider;
+}
-class StatesEditorWidget : public QQuickWidget
+class StatesEditorWidget : public StudioQuickWidget
{
Q_OBJECT
@@ -51,4 +54,4 @@ private:
QElapsedTimer m_usageTimer;
};
-}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/stateseditornew/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditornew/stateseditorwidget.cpp
index c47f19e4cb..f3fd945ae6 100644
--- a/src/plugins/qmldesigner/components/stateseditornew/stateseditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/stateseditornew/stateseditorwidget.cpp
@@ -109,6 +109,7 @@ StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView,
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F10), this);
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &StatesEditorWidget::reloadQmlSource);
+ setObjectName(Constants::OBJECT_NAME_STATES_EDITOR);
setResizeMode(QQuickWidget::SizeRootObjectToView);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -162,7 +163,6 @@ void StatesEditorWidget::reloadQmlSource()
{
QString statesListQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml");
QTC_ASSERT(QFileInfo::exists(statesListQmlFilePath), return );
- engine()->clearComponentCache();
setSource(QUrl::fromLocalFile(statesListQmlFilePath));
if (!rootObject()) {
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
index ca9bdbbcb9..a7ada80ed7 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
@@ -124,7 +124,13 @@ void TextEditorView::nodeReparented(const ModelNode &/*node*/, const NodeAbstrac
WidgetInfo TextEditorView::widgetInfo()
{
- return createWidgetInfo(m_widget, "TextEditor", WidgetInfo::CentralPane, 0, tr("Code"), DesignerWidgetFlags::IgnoreErrors);
+ return createWidgetInfo(m_widget,
+ "TextEditor",
+ WidgetInfo::CentralPane,
+ 0,
+ tr("Code"),
+ tr("Code view"),
+ DesignerWidgetFlags::IgnoreErrors);
}
void TextEditorView::qmlJSEditorContextHelp(const Core::IContext::HelpCallback &callback) const
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp
index 0bf2146307..1529c0a87c 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp
@@ -173,15 +173,11 @@ bool TextEditorWidget::eventFilter(QObject *, QEvent *event)
static std::vector<int> overrideKeys = { Qt::Key_Delete, Qt::Key_Backspace, Qt::Key_Insert,
Qt::Key_Escape };
- static std::vector<QKeySequence> overrideSequences = { QKeySequence::SelectAll, QKeySequence::Cut,
- QKeySequence::Copy, QKeySequence::Delete,
- QKeySequence::Paste, QKeySequence::Undo,
- QKeySequence::Redo, QKeySequence(Qt::CTRL | Qt::ALT),
- QKeySequence(Qt::Key_Left | Qt::CTRL),
- QKeySequence(Qt::Key_Right | Qt::CTRL),
- QKeySequence(Qt::Key_Up | Qt::CTRL),
- QKeySequence(Qt::Key_Down | Qt::CTRL)
- };
+ static std::vector<QKeySequence> overrideSequences = {QKeySequence(Qt::CTRL | Qt::ALT),
+ QKeySequence(Qt::Key_Left | Qt::CTRL),
+ QKeySequence(Qt::Key_Right | Qt::CTRL),
+ QKeySequence(Qt::Key_Up | Qt::CTRL),
+ QKeySequence(Qt::Key_Down | Qt::CTRL)};
if (event->type() == QEvent::ShortcutOverride) {
auto keyEvent = static_cast<QKeyEvent *>(event);
diff --git a/src/plugins/qmldesigner/components/textureeditor/images/texture_ktx.png b/src/plugins/qmldesigner/components/textureeditor/images/texture_ktx.png
new file mode 100644
index 0000000000..cad32f6114
--- /dev/null
+++ b/src/plugins/qmldesigner/components/textureeditor/images/texture_ktx.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/textureeditor/images/texture_ktx@2x.png b/src/plugins/qmldesigner/components/textureeditor/images/texture_ktx@2x.png
new file mode 100644
index 0000000000..015ae045d7
--- /dev/null
+++ b/src/plugins/qmldesigner/components/textureeditor/images/texture_ktx@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditor.qrc b/src/plugins/qmldesigner/components/textureeditor/textureeditor.qrc
index 680fe7d82b..cb43ec1aa9 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditor.qrc
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditor.qrc
@@ -2,5 +2,7 @@
<qresource prefix="/textureeditor">
<file>images/texture_default.png</file>
<file>images/texture_default@2x.png</file>
+ <file>images/texture_ktx.png</file>
+ <file>images/texture_ktx@2x.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp
index 679e07b00c..1148e31e2b 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp
@@ -339,7 +339,8 @@ QString TextureEditorContextObject::resolveResourcePath(const QString &path)
{
if (Utils::FilePath::fromString(path).isAbsolutePath())
return path;
- return DocumentManager::currentResourcePath().path() + '/' + path;
+ return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()
+ ->fileName().absolutePath().pathAppended(path).cleanPath().toString();
}
} // QmlDesigner
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.cpp
index 9095be2d93..e9c9eafc71 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.cpp
@@ -6,7 +6,7 @@
#include "dynamicpropertiesmodel.h"
#include "textureeditorview.h"
-using namespace QmlDesigner;
+namespace QmlDesigner {
TextureEditorDynamicPropertiesProxyModel::TextureEditorDynamicPropertiesProxyModel(QObject *parent)
: DynamicPropertiesProxyModel(parent)
@@ -20,3 +20,5 @@ void TextureEditorDynamicPropertiesProxyModel::registerDeclarativeType()
DynamicPropertiesProxyModel::registerDeclarativeType();
qmlRegisterType<TextureEditorDynamicPropertiesProxyModel>("HelperWidgets", 2, 0, "TextureEditorDynamicPropertiesModel");
}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.h b/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.h
index 9832d6eb4f..f1f5a3059a 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.h
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditordynamicpropertiesproxymodel.h
@@ -5,6 +5,8 @@
#include "dynamicpropertiesproxymodel.h"
+namespace QmlDesigner {
+
class TextureEditorDynamicPropertiesProxyModel : public DynamicPropertiesProxyModel
{
Q_OBJECT
@@ -14,3 +16,5 @@ public:
static void registerDeclarativeType();
};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
index 553f32205b..e54909049e 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
@@ -3,10 +3,10 @@
#include "textureeditorqmlbackend.h"
+#include "assetimageprovider.h"
#include "bindingproperty.h"
#include "documentmanager.h"
#include "nodemetainfo.h"
-#include "propertyeditorimageprovider.h"
#include "propertyeditorvalue.h"
#include "qmldesignerconstants.h"
#include "qmlobjectnode.h"
@@ -49,8 +49,9 @@ TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEdito
{
QImage defaultImage;
defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png"));
- m_textureEditorImageProvider = new PropertyEditorImageProvider(imageCache, defaultImage);
+ m_textureEditorImageProvider = new AssetImageProvider(imageCache, defaultImage);
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_view->setObjectName(Constants::OBJECT_NAME_TEXTURE_EDITOR);
m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_view->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider);
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
index bc84a80a3c..241195bd70 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
@@ -17,9 +17,8 @@ QT_END_NAMESPACE
namespace QmlDesigner {
-class PropertyEditorImageProvider;
+class AssetImageProvider;
class TextureEditorContextObject;
-class TextureEditorImageProvider;
class TextureEditorTransaction;
class TextureEditorView;
@@ -65,7 +64,7 @@ private:
DesignerPropertyMap m_backendValuesPropertyMap;
QScopedPointer<TextureEditorTransaction> m_textureEditorTransaction;
QScopedPointer<TextureEditorContextObject> m_contextObject;
- PropertyEditorImageProvider *m_textureEditorImageProvider = nullptr;
+ AssetImageProvider *m_textureEditorImageProvider = nullptr;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditortransaction.h b/src/plugins/qmldesigner/components/textureeditor/textureeditortransaction.h
index 74565aefd7..ab071473cd 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditortransaction.h
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditortransaction.h
@@ -3,6 +3,7 @@
#pragma once
+#include "rewritertransaction.h"
#include "textureeditorview.h"
namespace QmlDesigner {
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
index 99b0a6e632..38664c146e 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
@@ -28,6 +28,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
+#include <designdocument.h>
#include <designmodewidget.h>
#include <propertyeditorqmlbackend.h>
#include <utils/environment.h>
@@ -54,7 +55,7 @@ TextureEditorView::TextureEditorView(AsynchronousImageCache &imageCache,
: AbstractView{externalDependencies}
, m_imageCache(imageCache)
, m_stackedWidget(new QStackedWidget)
- , m_dynamicPropertiesModel(new Internal::DynamicPropertiesModel(true, this))
+ , m_dynamicPropertiesModel(new DynamicPropertiesModel(true, this))
{
m_updateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F12), m_stackedWidget);
connect(m_updateShortcut, &QShortcut::activated, this, &TextureEditorView::reloadQml);
@@ -62,7 +63,9 @@ TextureEditorView::TextureEditorView(AsynchronousImageCache &imageCache,
m_ensureMatLibTimer.callOnTimeout([this] {
if (model() && model()->rewriterView() && !model()->rewriterView()->hasIncompleteTypeInformation()
&& model()->rewriterView()->errors().isEmpty()) {
- ensureMaterialLibraryNode();
+ DesignDocument *doc = QmlDesignerPlugin::instance()->currentDesignDocument();
+ if (doc && !doc->inFileComponentModelActive())
+ ensureMaterialLibraryNode();
if (m_qmlBackEnd && m_qmlBackEnd->contextObject())
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid());
m_ensureMatLibTimer.stop();
@@ -288,7 +291,7 @@ void TextureEditorView::currentTimelineChanged(const ModelNode &)
m_qmlBackEnd->contextObject()->setHasActiveTimeline(QmlTimeline::hasActiveTimeline(this));
}
-Internal::DynamicPropertiesModel *TextureEditorView::dynamicPropertiesModel() const
+DynamicPropertiesModel *TextureEditorView::dynamicPropertiesModel() const
{
return m_dynamicPropertiesModel;
}
@@ -670,7 +673,8 @@ WidgetInfo TextureEditorView::widgetInfo()
"TextureEditor",
WidgetInfo::RightPane,
0,
- tr("Texture Editor"));
+ tr("Texture Editor"),
+ tr("Texture Editor view"));
}
void TextureEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h
index d932d4c50e..b299a1e99a 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h
@@ -19,13 +19,10 @@ QT_END_NAMESPACE
namespace QmlDesigner {
+class DynamicPropertiesModel;
class ModelNode;
class TextureEditorQmlBackend;
-namespace Internal {
-class DynamicPropertiesModel;
-}
-
class TextureEditorView : public AbstractView
{
Q_OBJECT
@@ -77,7 +74,7 @@ public:
void currentTimelineChanged(const ModelNode &node) override;
- Internal::DynamicPropertiesModel *dynamicPropertiesModel() const;
+ DynamicPropertiesModel *dynamicPropertiesModel() const;
static TextureEditorView *instance();
@@ -123,7 +120,7 @@ private:
QPointer<QColorDialog> m_colorDialog;
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
- Internal::DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
+ DynamicPropertiesModel *m_dynamicPropertiesModel = nullptr;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
index 951f1a7841..8c0a9c9d73 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
@@ -182,8 +182,10 @@ TimelinePropertyItem *TimelinePropertyItem::create(const QmlTimelineKeyframeGrou
}
});
- QIcon autoKeyIcon = TimelineUtils::mergeIcons(TimelineIcons::GLOBAL_RECORD_KEYFRAMES,
- TimelineIcons::GLOBAL_RECORD_KEYFRAMES_OFF);
+ QIcon autoKeyIcon = TimelineUtils::mergeIcons(
+ Theme::iconFromName(Theme::recordFill_medium, Theme::getColor(Theme::Color::IconsStopColor)),
+ Theme::iconFromName(Theme::recordOutline_medium));
+
auto recact = new QAction(autoKeyIcon, tr("Auto Record"));
recact->setCheckable(true);
recact->setChecked(isRecording);
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
index 1933fb3eaf..b4ab0583c7 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
@@ -22,6 +22,7 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/stylehelper.h>
#include <QApplication>
#include <QIntValidator>
@@ -96,6 +97,7 @@ TimelineToolBar::TimelineToolBar(QWidget *parent)
, m_grp()
{
setContentsMargins(0, 0, 0, 0);
+ setFixedHeight(Theme::toolbarSize());
createLeftControls();
createCenterControls();
createRightControls();
@@ -220,7 +222,7 @@ void TimelineToolBar::createLeftControls()
addSpacingToGroup(5);
auto *settingsAction = createAction(TimelineConstants::C_SETTINGS,
- TimelineIcons::ANIMATION.icon(),
+ Theme::iconFromName(Theme::Icon::settings_medium),
tr("Timeline Settings"),
QKeySequence(Qt::Key_S));
@@ -255,7 +257,7 @@ void TimelineToolBar::createCenterControls()
addSpacing(5);
auto *toStart = createAction(TimelineConstants::C_TO_START,
- TimelineIcons::TO_FIRST_FRAME.icon(),
+ Theme::iconFromName(Theme::Icon::toStartFrame_medium),
tr("To Start"),
QKeySequence(Qt::Key_Home));
@@ -265,7 +267,7 @@ void TimelineToolBar::createCenterControls()
addSpacing(2);
auto *previous = createAction(TimelineConstants::C_PREVIOUS,
- TimelineIcons::BACK_ONE_FRAME.icon(),
+ Theme::iconFromName(Theme::Icon::toPrevFrame_medium),
tr("Previous"),
QKeySequence(Qt::Key_Comma));
@@ -273,8 +275,12 @@ void TimelineToolBar::createCenterControls()
addAction(previous);
addSpacing(2);
- QIcon playbackIcon = TimelineUtils::mergeIcons(TimelineIcons::PAUSE_PLAYBACK,
- TimelineIcons::START_PLAYBACK);
+ QIcon playbackIcon = TimelineUtils::mergeIcons(
+ Theme::iconFromName(Theme::Icon::pause,
+ Theme::getColor(Theme::Color::DStextSelectedTextColor)),
+ Theme::iconFromName(Theme::Icon::playOutline_medium,
+ Theme::getColor(Theme::Color::IconsRunColor)));
+
m_playing = createAction(TimelineConstants::C_PLAY,
playbackIcon,
tr("Play"),
@@ -286,7 +292,7 @@ void TimelineToolBar::createCenterControls()
addSpacing(2);
auto *next = createAction(TimelineConstants::C_NEXT,
- TimelineIcons::FORWARD_ONE_FRAME.icon(),
+ Theme::iconFromName(Theme::Icon::toNextFrame_medium),
tr("Next"),
QKeySequence(Qt::Key_Period));
@@ -296,7 +302,7 @@ void TimelineToolBar::createCenterControls()
addSpacing(2);
auto *toEnd = createAction(TimelineConstants::C_TO_END,
- TimelineIcons::TO_LAST_FRAME.icon(),
+ Theme::iconFromName(Theme::Icon::toEndFrame_medium),
tr("To End"),
QKeySequence(Qt::Key_End));
@@ -309,10 +315,12 @@ void TimelineToolBar::createCenterControls()
addSpacing(10);
- auto *loopAnimation = createAction(TimelineConstants::C_LOOP_PLAYBACK,
- TimelineIcons::LOOP_PLAYBACK.icon(),
- tr("Loop Playback"),
- QKeySequence((Qt::ControlModifier | Qt::ShiftModifier) + Qt::Key_Space)); // TODO: Toggles looping. Select shortcut for this QDS-4941
+ auto *loopAnimation = createAction(
+ TimelineConstants::C_LOOP_PLAYBACK,
+ Theme::iconFromName(Theme::Icon::loopPlayback_medium),
+ tr("Loop Playback"),
+ QKeySequence((Qt::ControlModifier | Qt::ShiftModifier)
+ + Qt::Key_Space)); // TODO: Toggles looping. Select shortcut for this QDS-4941
loopAnimation->setCheckable(true);
connect(loopAnimation, &QAction::toggled, [&](bool value) { emit loopPlaybackToggled(value);} );
@@ -349,8 +357,10 @@ void TimelineToolBar::createCenterControls()
addSpacing(10);
- QIcon autoKeyIcon = TimelineUtils::mergeIcons(TimelineIcons::GLOBAL_RECORD_KEYFRAMES,
- TimelineIcons::GLOBAL_RECORD_KEYFRAMES_OFF);
+ QIcon autoKeyIcon = TimelineUtils::mergeIcons(
+ Theme::iconFromName(Theme::Icon::recordFill_medium,
+ Theme::getColor(Theme::Color::IconsStopColor)),
+ Theme::iconFromName(Theme::Icon::recordOutline_medium));
m_recording = createAction(TimelineConstants::C_AUTO_KEYFRAME,
autoKeyIcon,
@@ -369,7 +379,7 @@ void TimelineToolBar::createCenterControls()
addSpacing(10);
auto *curvePicker = createAction(TimelineConstants::C_CURVE_PICKER,
- TimelineIcons::CURVE_EDITOR.icon(),
+ Theme::iconFromName(Theme::Icon::curveDesigner_medium),
tr("Easing Curve Editor"),
QKeySequence(Qt::Key_C));
@@ -408,7 +418,7 @@ void TimelineToolBar::createRightControls()
addSpacing(10);
auto *zoomOut = createAction(TimelineConstants::C_ZOOM_OUT,
- TimelineIcons::ZOOM_SMALL.icon(),
+ Theme::iconFromName(Theme::Icon::zoomOut_medium),
tr("Zoom Out"),
QKeySequence(QKeySequence::ZoomOut));
@@ -421,6 +431,8 @@ void TimelineToolBar::createRightControls()
m_scale = new QSlider(this);
m_scale->setOrientation(Qt::Horizontal);
+ Utils::StyleHelper::setPanelWidget(m_scale);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_scale);
m_scale->setMaximumWidth(200);
m_scale->setMinimumWidth(100);
m_scale->setMinimum(0);
@@ -433,7 +445,7 @@ void TimelineToolBar::createRightControls()
addSpacing(10);
auto *zoomIn = createAction(TimelineConstants::C_ZOOM_IN,
- TimelineIcons::ZOOM_BIG.icon(),
+ Theme::iconFromName(Theme::Icon::zoomIn_medium),
tr("Zoom In"),
QKeySequence(QKeySequence::ZoomIn));
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h b/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
index 16949295e9..088f6d8f89 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
@@ -44,11 +44,11 @@ inline T reverseLerp(const T &val, const T &lhs, const T &rhs)
return (val - rhs) / (lhs - rhs);
}
-inline QIcon mergeIcons(const Utils::Icon &on, const Utils::Icon &off)
+inline QIcon mergeIcons(const QIcon &on, const QIcon &off)
{
QIcon out;
- out.addPixmap(on.pixmap(), QIcon::Normal, QIcon::On);
- out.addPixmap(off.pixmap(), QIcon::Normal, QIcon::Off);
+ out.addPixmap(on.pixmap({16, 16}), QIcon::Normal, QIcon::On);
+ out.addPixmap(off.pixmap({16, 16}), QIcon::Normal, QIcon::Off);
return out;
}
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
index 0ad9a8741b..b68dc98eaf 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
@@ -648,7 +648,8 @@ WidgetInfo TimelineView::widgetInfo()
QStringLiteral("Timelines"),
WidgetInfo::BottomPane,
0,
- tr("Timeline"));
+ tr("Timeline"),
+ tr("Timeline view"));
}
bool TimelineView::hasQtQuickTimelineImport()
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
index 813adcc53c..64fce94848 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
@@ -110,8 +110,11 @@ TimelineWidget::TimelineWidget(TimelineView *view)
setWindowTitle(tr("Timeline", "Title of timeline view"));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- const QString css = Theme::replaceCssColors(QString::fromUtf8(
- Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")));
+ m_toolbar->setStyleSheet(Theme::replaceCssColors(
+ QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
+
+ const QString css = Theme::replaceCssColors(
+ QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")));
m_scrollbar->setStyleSheet(css);
m_scrollbar->setOrientation(Qt::Horizontal);
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp
new file mode 100644
index 0000000000..3326c889ad
--- /dev/null
+++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp
@@ -0,0 +1,132 @@
+// 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 "toolbar.h"
+#include "toolbarbackend.h"
+
+#include <studioquickwidget.h>
+
+#include <theme.h>
+#include <qmldesignerconstants.h>
+
+#include <coreplugin/icore.h>
+#include <utils/filepath.h>
+#include <utils/qtcassert.h>
+
+#include <QMainWindow>
+#include <QQmlEngine>
+#include <QStatusBar>
+#include <QToolBar>
+
+namespace QmlDesigner {
+
+static Utils::FilePath propertyEditorResourcesPath()
+{
+#ifdef SHARE_QML_PATH
+ if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
+ return Utils::FilePath::fromString(QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources");
+#endif
+ return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources");
+}
+
+Utils::FilePath qmlSourcesStatusBarPath()
+{
+#ifdef SHARE_QML_PATH
+ if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
+ return Utils::FilePath::fromString(QLatin1String(SHARE_QML_PATH) + "/statusbar");
+#endif
+ return Core::ICore::resourcePath("qmldesigner/statusbar");
+}
+
+Utils::FilePath qmlSourcesPath()
+{
+#ifdef SHARE_QML_PATH
+ if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
+ return Utils::FilePath::fromString(QLatin1String(SHARE_QML_PATH) + "/toolbar");
+#endif
+ return Core::ICore::resourcePath("qmldesigner/toolbar");
+}
+
+void ToolBar::create()
+{
+ if (!isVisible())
+ return;
+
+ ToolBarBackend::registerDeclarativeType();
+
+ auto window = Core::ICore::mainWindow();
+
+ //Core::ICore::statusBar()->hide();
+
+ auto toolBar = new QToolBar;
+ toolBar->setObjectName("QDS-TOOLBAR");
+
+ toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
+
+ toolBar->setFloatable(false);
+ toolBar->setMovable(false);
+
+ auto quickWidget = new StudioQuickWidget;
+
+ quickWidget->setFixedHeight(48);
+ quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ quickWidget->setMinimumWidth(200);
+ quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+
+ quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_TOP_TOOLBAR);
+
+ quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports");
+
+ Utils::FilePath qmlFilePath = qmlSourcesPath() / "Main.qml";
+ QTC_ASSERT(qmlFilePath.exists(), return);
+
+ Theme::setupTheme(quickWidget->engine());
+
+ quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString()));
+
+ toolBar->addWidget(quickWidget);
+ window->addToolBar(toolBar);
+}
+
+void ToolBar::createStatusBar()
+{
+ if (!isVisible())
+ return;
+
+ ToolBarBackend::registerDeclarativeType();
+
+ auto quickWidget = new StudioQuickWidget;
+
+ quickWidget->setFixedHeight(Theme::toolbarSize());
+ quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ quickWidget->setMinimumWidth(200);
+ quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+
+ quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_STATUSBAR);
+
+ quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports");
+
+ Utils::FilePath qmlFilePath = qmlSourcesStatusBarPath().pathAppended("/Main.qml");
+ QTC_ASSERT(qmlFilePath.exists(), return);
+
+ Theme::setupTheme(quickWidget->engine());
+
+ quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString()));
+
+ for (QWidget *w : Core::ICore::statusBar()->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly)) {
+ w->hide();
+ }
+
+ Core::ICore::statusBar()->addWidget(quickWidget);
+ Core::ICore::statusBar()->setFixedHeight(Theme::toolbarSize());
+}
+
+bool ToolBar::isVisible()
+{
+ QSettings *settings = Core::ICore::settings();
+ const QString qdsToolbarEntry = "QML/Designer/TopToolBar";
+
+ return settings->value(qdsToolbarEntry, false).toBool();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.h b/src/plugins/qmldesigner/components/toolbar/toolbar.h
new file mode 100644
index 0000000000..8537757cdf
--- /dev/null
+++ b/src/plugins/qmldesigner/components/toolbar/toolbar.h
@@ -0,0 +1,17 @@
+// 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
+
+#pragma once
+
+namespace QmlDesigner {
+
+class ToolBar
+{
+
+public:
+ static void create();
+ static void createStatusBar();
+ static bool isVisible();
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
new file mode 100644
index 0000000000..ce7d783a4e
--- /dev/null
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
@@ -0,0 +1,614 @@
+// 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 "toolbarbackend.h"
+
+#include <changestyleaction.h>
+#include <crumblebar.h>
+#include <designeractionmanager.h>
+#include <designmodewidget.h>
+#include <viewmanager.h>
+#include <zoomaction.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <qmleditormenu.h>
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/designmode.h>
+#include <coreplugin/editormanager/documentmodel.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/modemanager.h>
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/target.h>
+
+#include <qmlprojectmanager/qmlproject.h>
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
+
+#include <QQmlEngine>
+
+namespace QmlDesigner {
+
+static Internal::DesignModeWidget *designModeWidget()
+{
+ return QmlDesignerPlugin::instance()->mainWidget();
+}
+
+static DesignDocument *currentDesignDocument()
+{
+ QTC_ASSERT(QmlDesignerPlugin::instance(), return nullptr);
+
+ return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument();
+}
+
+static CrumbleBar *crumbleBar()
+{
+ return designModeWidget()->crumbleBar();
+}
+
+static Utils::FilePath getMainUiFile()
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ if (!project)
+ return {};
+
+ if (!project->activeTarget())
+ return {};
+
+ auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
+ project->activeTarget()->buildSystem());
+
+ if (!qmlBuildSystem)
+ return {};
+
+ return qmlBuildSystem->mainUiFilePath();
+}
+
+static void openUiFile()
+{
+ const Utils::FilePath mainUiFile = getMainUiFile();
+
+ if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists())
+ Core::EditorManager::openEditor(mainUiFile, Utils::Id());
+}
+
+void ToolBarBackend::triggerModeChange()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_MODE_CHANGE);
+ QTimer::singleShot(0, [this]() { //Do not trigger mode change directly from QML
+ bool qmlFileOpen = false;
+
+ if (!projectOpened()) {
+ Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
+ return;
+ }
+
+ auto document = Core::EditorManager::currentDocument();
+
+ if (document)
+ qmlFileOpen = document->filePath().fileName().endsWith(".qml");
+
+ if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN)
+ Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
+ else if (qmlFileOpen)
+ Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN);
+ else if (Core::ModeManager::currentModeId() == Core::Constants::MODE_WELCOME)
+ openUiFile();
+ else
+ Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
+ });
+}
+
+void ToolBarBackend::triggerProjectSettings()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_PROJECT_SETTINGS);
+ QTimer::singleShot(0, []() { //Do not trigger mode change directly from QML
+ Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION);
+ });
+}
+
+void ToolBarBackend::runProject()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_RUN_PROJECT);
+ ProjectExplorer::ProjectExplorerPlugin::runStartupProject(
+ ProjectExplorer::Constants::NORMAL_RUN_MODE);
+}
+
+void ToolBarBackend::goForward()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_GO_FORWARD);
+ QTC_ASSERT(designModeWidget(), return );
+ designModeWidget()->toolBarOnGoForwardClicked();
+}
+
+void ToolBarBackend::goBackward()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_GO_BACKWARD);
+ QTC_ASSERT(designModeWidget(), return );
+ designModeWidget()->toolBarOnGoBackClicked();
+}
+
+void ToolBarBackend::openFileByIndex(int i)
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_OPEN_FILE);
+ auto fileName = Core::DocumentModel::entries().at(i)->filePath();
+
+ Core::EditorManager::openEditor(fileName, Utils::Id(), Core::EditorManager::DoNotMakeVisible);
+}
+
+void ToolBarBackend::closeCurrentDocument()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_CLOSE_DOCUMENT);
+ Core::EditorManager::slotCloseCurrentEditorOrDocument();
+}
+
+void ToolBarBackend::shareApplicationOnline()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_SHARE_APPLICATION);
+ auto command = Core::ActionManager::command("QmlProject.ShareDesign");
+ if (command)
+ command->action()->trigger();
+}
+
+void ToolBarBackend::setCurrentWorkspace(const QString &workspace)
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_SET_CURRENT_WORKSPACE);
+ designModeWidget()->dockManager()->openWorkspace(workspace);
+}
+
+void ToolBarBackend::editGlobalAnnoation()
+{
+ launchGlobalAnnotations();
+}
+
+void ToolBarBackend::showZoomMenu(int x, int y)
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATUSBAR_SHOW_ZOOM);
+ ZoomAction *zoomAction = qobject_cast<ZoomAction *>(m_zoomAction->action());
+
+ QTC_ASSERT(zoomAction, return );
+
+ auto mainMenu = new QmlEditorMenu();
+
+ int currentIndex = zoomAction->currentIndex();
+ int i = 0;
+
+ for (double d : zoomAction->zoomLevels()) {
+ auto action = mainMenu->addAction(QString::number(d * 100) + "%");
+ action->setCheckable(true);
+ if (i == currentIndex)
+ action->setChecked(true);
+ ++i;
+ connect(action, &QAction::triggered, this, [zoomAction, d] { zoomAction->setZoomFactor(d); });
+ }
+
+ mainMenu->exec(QPoint(x, y));
+ mainMenu->deleteLater();
+}
+
+void ToolBarBackend::setCurrentStyle(int index)
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_STATUSBAR_SET_STYLE);
+ const QList<StyleWidgetEntry> items = ChangeStyleWidgetAction::getAllStyleItems();
+
+ QTC_ASSERT(items.count() > index, return );
+ QTC_ASSERT(index > 0, return );
+
+ QTC_ASSERT(currentDesignDocument(), return );
+
+ auto item = items.at(index);
+
+ auto view = currentDesignDocument()->rewriterView();
+
+ const QString qmlFile = view->model()->fileUrl().toLocalFile();
+
+ ChangeStyleWidgetAction::changeCurrentStyle(item.styleName, qmlFile);
+
+ view->resetPuppet();
+}
+
+void ToolBarBackend::setCurrentKit(int index)
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ QTC_ASSERT(project, return );
+
+ const auto kits = ProjectExplorer::KitManager::kits();
+
+ QTC_ASSERT(kits.count() > index, return );
+ QTC_ASSERT(index >= 0, return );
+
+ const auto kit = kits.at(index);
+
+ auto newTarget = project->target(kit);
+ if (!newTarget)
+ newTarget = project->addTargetForKit(kit);
+
+ project->setActiveTarget(newTarget, ProjectExplorer::SetActive::Cascade);
+
+ emit currentKitChanged();
+}
+
+bool ToolBarBackend::canGoBack() const
+{
+ QTC_ASSERT(designModeWidget(), return false);
+ return designModeWidget()->canGoBack();
+}
+
+bool ToolBarBackend::canGoForward() const
+{
+ QTC_ASSERT(designModeWidget(), return false);
+ return designModeWidget()->canGoForward();
+}
+
+ToolBarBackend::ToolBarBackend(QObject *parent)
+ : QObject(parent)
+{
+ ActionAddedInterface callback = [this](ActionInterface *interface) {
+ if (interface->menuId() == "PreviewZoom")
+ m_zoomAction = interface;
+ };
+
+ QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback);
+
+ connect(designModeWidget(),
+ &Internal::DesignModeWidget::navigationHistoryChanged,
+ this,
+ &ToolBarBackend::navigationHistoryChanged);
+
+ connect(Core::DocumentModel::model(),
+ &QAbstractItemModel::rowsInserted,
+ this,
+ &ToolBarBackend::updateDocumentModel);
+ connect(Core::DocumentModel::model(),
+ &QAbstractItemModel::rowsRemoved,
+ this,
+ &ToolBarBackend::updateDocumentModel);
+ connect(Core::DocumentModel::model(),
+ &QAbstractItemModel::dataChanged,
+ this,
+ &ToolBarBackend::updateDocumentModel);
+ connect(Core::DocumentModel::model(),
+ &QAbstractItemModel::modelReset,
+ this,
+ &ToolBarBackend::updateDocumentModel);
+
+ connect(Core::EditorManager::instance(),
+ &Core::EditorManager::currentEditorChanged,
+ this,
+ &ToolBarBackend::documentIndexChanged);
+
+ connect(Core::EditorManager::instance(),
+ &Core::EditorManager::currentEditorChanged,
+ this,
+ &ToolBarBackend::currentStyleChanged);
+
+ connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() {
+ const auto dockManager = designModeWidget()->dockManager();
+
+ connect(dockManager,
+ &ADS::DockManager::workspaceListChanged,
+ this,
+ &ToolBarBackend::setupWorkspaces);
+ connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) {
+ emit currentWorkspaceChanged();
+ });
+
+ setupWorkspaces();
+ });
+
+ auto editorManager = Core::EditorManager::instance();
+
+ connect(editorManager, &Core::EditorManager::documentClosed, this, [this]() {
+ if (isInDesignMode() && Core::DocumentModel::entryCount() == 0) {
+ QTimer::singleShot(0, []() { /* The mode change has to happen from event loop.
+ Otherwise we and up in an invalid state */
+ Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
+ });
+ }
+ });
+
+ connect(Core::ICore::instance(), &Core::ICore::coreAboutToOpen, this, [this] {
+ connect(Core::DesignMode::instance(), &Core::DesignMode::enabledStateChanged, this, [this] {
+ emit isDesignModeEnabledChanged();
+ });
+ });
+
+ connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() {
+ emit isInDesignModeChanged();
+ emit isInEditModeChanged();
+ emit isDesignModeEnabledChanged();
+ });
+
+ connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
+ [this](ProjectExplorer::Project *project) {
+ disconnect(m_kitConnection);
+ emit isQt6Changed();
+ emit projectOpenedChanged();
+ if (project) {
+ m_kitConnection = connect(project,
+ &ProjectExplorer::Project::activeTargetChanged,
+ this,
+ &ToolBarBackend::currentKitChanged);
+ emit currentKitChanged();
+ }
+ });
+
+ connect(ProjectExplorer::KitManager::instance(),
+ &ProjectExplorer::KitManager::kitsChanged,
+ this,
+ &ToolBarBackend::kitsChanged);
+}
+
+void ToolBarBackend::registerDeclarativeType()
+{
+ qmlRegisterType<ToolBarBackend>("ToolBar", 1, 0, "ToolBarBackend");
+ qmlRegisterType<ActionSubscriber>("ToolBar", 1, 0, "ActionSubscriber");
+ qmlRegisterType<CrumbleBarModel>("ToolBar", 1, 0, "CrumbleBarModel");
+}
+
+QStringList ToolBarBackend::documentModel() const
+{
+ return m_openDocuments;
+}
+
+void ToolBarBackend::updateDocumentModel()
+{
+ m_openDocuments.clear();
+ for (auto &entry : Core::DocumentModel::entries())
+ m_openDocuments.append(entry->displayName());
+
+ emit openDocumentsChanged();
+}
+
+int ToolBarBackend::documentIndex() const
+{
+ if (Core::EditorManager::currentDocument()) {
+ std::optional index = Core::DocumentModel::indexOfDocument(
+ Core::EditorManager::currentDocument());
+ return index.value_or(-1);
+ }
+
+ return -1;
+}
+
+QString ToolBarBackend::currentWorkspace() const
+{
+ if (designModeWidget() && designModeWidget()->dockManager())
+ return designModeWidget()->dockManager()->activeWorkspace();
+ return {};
+}
+
+QStringList ToolBarBackend::workspaces() const
+{
+ return m_workspaces;
+}
+
+QStringList ToolBarBackend::styles() const
+{
+ const QList<StyleWidgetEntry> items = ChangeStyleWidgetAction::getAllStyleItems();
+ QStringList list;
+ for (const auto &item : items)
+ list.append(item.displayName);
+
+ return list;
+}
+
+bool ToolBarBackend::isInDesignMode() const
+{
+ if (!Core::ModeManager::instance())
+ return false;
+
+ return Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN;
+}
+
+bool ToolBarBackend::isInEditMode() const
+{
+ if (!Core::ModeManager::instance())
+ return false;
+
+ return Core::ModeManager::currentModeId() == Core::Constants::MODE_EDIT;
+}
+
+void ToolBarBackend::launchGlobalAnnotations()
+{
+ QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION);
+ ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode();
+
+ if (node.isValid()) {
+ designModeWidget()->globalAnnotationEditor().setModelNode(node);
+ designModeWidget()->globalAnnotationEditor().showWidget();
+ }
+}
+
+bool ToolBarBackend::isDesignModeEnabled() const
+{
+ if (Core::DesignMode::instance())
+ return Core::DesignMode::instance()->isEnabled() || getMainUiFile().exists();
+
+ return false;
+}
+
+int ToolBarBackend::currentStyle() const
+{
+ if (!currentDesignDocument())
+ return 0;
+
+ auto view = currentDesignDocument()->rewriterView();
+
+ const QString qmlFile = view->model()->fileUrl().toLocalFile();
+
+ const int index = ChangeStyleWidgetAction::getCurrentStyle(qmlFile);
+
+ return index;
+}
+
+QStringList ToolBarBackend::kits() const
+{
+ return Utils::transform(ProjectExplorer::KitManager::kits(),
+ [](ProjectExplorer::Kit *kit) { return kit->displayName(); });
+}
+
+int ToolBarBackend::currentKit() const
+{
+ if (auto target = ProjectExplorer::ProjectManager::startupTarget()) {
+ auto kit = target->kit();
+ if (kit)
+ return kits().indexOf(kit->displayName());
+ }
+ return 0;
+}
+
+bool ToolBarBackend::isQt6() const
+{
+ if (!ProjectExplorer::ProjectManager::startupTarget())
+ return false;
+
+ const QmlProjectManager::QmlBuildSystem *buildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
+ ProjectExplorer::ProjectManager::startupTarget()->buildSystem());
+ QTC_ASSERT(buildSystem, return false);
+
+ const bool isQt6Project = buildSystem && buildSystem->qt6Project();
+
+ return isQt6Project;
+}
+
+bool ToolBarBackend::projectOpened() const
+{
+ return ProjectExplorer::ProjectManager::instance()->startupProject();
+}
+
+void ToolBarBackend::setupWorkspaces()
+{
+ m_workspaces.clear();
+ m_workspaces = designModeWidget()->dockManager()->workspaces();
+ Utils::sort(m_workspaces);
+ emit workspacesChanged();
+ emit currentWorkspaceChanged();
+}
+
+ActionSubscriber::ActionSubscriber(QObject *parent)
+ : QObject(parent)
+{
+ ActionAddedInterface callback = [this](ActionInterface *interface) {
+ if (interface->menuId() == m_actionId.toLatin1()) {
+ m_interface = interface;
+ setupNotifier();
+ }
+ };
+
+ QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback);
+}
+
+void ActionSubscriber::trigger()
+{
+ if (m_interface)
+ m_interface->action()->trigger();
+}
+
+bool ActionSubscriber::available() const
+{
+ if (m_interface)
+ return m_interface->action()->isEnabled();
+ return false;
+}
+
+bool ActionSubscriber::checked() const
+{
+ if (m_interface)
+ return m_interface->action()->isChecked();
+
+ return false;
+}
+
+QString ActionSubscriber::actionId() const
+{
+ return m_actionId;
+}
+
+void ActionSubscriber::setActionId(const QString &id)
+{
+ if (id == m_actionId)
+ return;
+
+ m_actionId = id;
+ emit actionIdChanged();
+ emit tooltipChanged();
+}
+
+QString ActionSubscriber::tooltip() const
+{
+ if (m_interface)
+ return m_interface->action()->text();
+ return {};
+}
+
+void ActionSubscriber::setupNotifier()
+{
+ if (!m_interface)
+ return;
+
+ connect(m_interface->action(), &QAction::enabledChanged, this, &ActionSubscriber::availableChanged);
+
+ emit tooltipChanged();
+}
+
+CrumbleBarModel::CrumbleBarModel(QObject *)
+{
+ connect(crumbleBar(), &CrumbleBar::pathChanged, this, &CrumbleBarModel::handleCrumblePathChanged);
+}
+
+int CrumbleBarModel::rowCount(const QModelIndex &) const
+{
+ return crumbleBar()->path().count();
+}
+
+QHash<int, QByteArray> CrumbleBarModel::roleNames() const
+{
+ static QHash<int, QByteArray> roleNames{{Qt::UserRole + 1, "fileName"},
+ {Qt::UserRole + 2, "fileAddress"}};
+
+ return roleNames;
+}
+
+QVariant CrumbleBarModel::data(const QModelIndex &index, int role) const
+{
+ if (index.isValid() && index.row() < rowCount()) {
+ auto info = crumbleBar()->infos().at(index.row());
+
+ if (role == Qt::UserRole + 1) {
+ return info.displayName;
+ } else if (role == Qt::UserRole + 2) {
+ return info.fileName.displayName();
+ } else {
+ qWarning() << Q_FUNC_INFO << "invalid role";
+ }
+ } else {
+ qWarning() << Q_FUNC_INFO << "invalid index";
+ }
+
+ return QVariant();
+}
+
+void CrumbleBarModel::handleCrumblePathChanged()
+{
+ beginResetModel();
+ endResetModel();
+}
+
+void CrumbleBarModel::onCrumblePathElementClicked(int i)
+{
+ if (i < rowCount()) {
+ auto info = crumbleBar()->infos().at(i);
+ crumbleBar()->onCrumblePathElementClicked(QVariant::fromValue(info));
+ }
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
new file mode 100644
index 0000000000..1aed5aeeaf
--- /dev/null
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
@@ -0,0 +1,159 @@
+// 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
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <QObject>
+
+namespace QmlDesigner {
+
+class ActionInterface;
+
+class CrumbleBarModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit CrumbleBarModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QHash<int, QByteArray> roleNames() const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ void handleCrumblePathChanged();
+
+ Q_INVOKABLE void onCrumblePathElementClicked(int i);
+
+private:
+};
+
+class ActionSubscriber : public QObject
+{
+ Q_OBJECT
+
+public:
+ ActionSubscriber(QObject *parent = nullptr);
+
+ Q_PROPERTY(QString actionId READ actionId WRITE setActionId NOTIFY actionIdChanged)
+ Q_PROPERTY(bool available READ available NOTIFY availableChanged)
+ Q_PROPERTY(bool checked READ checked NOTIFY checkedChanged)
+ Q_PROPERTY(QString tooltip READ tooltip NOTIFY tooltipChanged)
+
+ Q_INVOKABLE void trigger();
+
+ bool available() const;
+ bool checked() const;
+
+ QString actionId() const;
+ void setActionId(const QString &id);
+
+ QString tooltip() const;
+
+signals:
+ void actionIdChanged();
+ void availableChanged();
+ void checkedChanged();
+ void tooltipChanged();
+
+private:
+ void setupNotifier();
+
+ ActionInterface *m_interface = nullptr;
+ QString m_actionId;
+};
+
+class ToolBarBackend : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool canGoBack READ canGoBack NOTIFY navigationHistoryChanged)
+ Q_PROPERTY(bool canGoForward READ canGoForward NOTIFY navigationHistoryChanged)
+ Q_PROPERTY(QStringList documentModel READ documentModel NOTIFY openDocumentsChanged)
+ Q_PROPERTY(int documentIndex READ documentIndex NOTIFY documentIndexChanged)
+ Q_PROPERTY(QString currentWorkspace READ currentWorkspace NOTIFY currentWorkspaceChanged)
+ Q_PROPERTY(QStringList workspaces READ workspaces NOTIFY workspacesChanged)
+ Q_PROPERTY(QStringList styles READ styles CONSTANT)
+ Q_PROPERTY(bool isInDesignMode READ isInDesignMode NOTIFY isInDesignModeChanged)
+ Q_PROPERTY(bool isInEditMode READ isInEditMode NOTIFY isInEditModeChanged)
+ Q_PROPERTY(bool isDesignModeEnabled READ isDesignModeEnabled NOTIFY isDesignModeEnabledChanged)
+ Q_PROPERTY(int currentStyle READ currentStyle NOTIFY currentStyleChanged)
+ Q_PROPERTY(QStringList kits READ kits NOTIFY kitsChanged)
+ Q_PROPERTY(int currentKit READ currentKit NOTIFY currentKitChanged)
+ Q_PROPERTY(bool isQt6 READ isQt6 NOTIFY isQt6Changed)
+ Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged)
+
+public:
+ ToolBarBackend(QObject *parent = nullptr);
+ static void registerDeclarativeType();
+
+ Q_INVOKABLE void triggerModeChange();
+ Q_INVOKABLE void triggerProjectSettings();
+ Q_INVOKABLE void runProject();
+ Q_INVOKABLE void goForward();
+ Q_INVOKABLE void goBackward();
+ Q_INVOKABLE void openFileByIndex(int i);
+ Q_INVOKABLE void closeCurrentDocument();
+ Q_INVOKABLE void shareApplicationOnline();
+ Q_INVOKABLE void setCurrentWorkspace(const QString &workspace);
+ Q_INVOKABLE void editGlobalAnnoation();
+ Q_INVOKABLE void showZoomMenu(int x, int y);
+ Q_INVOKABLE void setCurrentStyle(int index);
+ Q_INVOKABLE void setCurrentKit(int index);
+
+ bool canGoBack() const;
+ bool canGoForward() const;
+
+ QStringList documentModel() const;
+
+ void updateDocumentModel();
+ int documentIndex() const;
+
+ QString currentWorkspace() const;
+ QStringList workspaces() const;
+
+ QStringList styles() const;
+
+ bool isInDesignMode() const;
+ bool isDesignModeEnabled() const;
+ int currentStyle() const;
+
+ QStringList kits() const;
+
+ int currentKit() const;
+
+ bool isQt6() const;
+
+ bool projectOpened() const;
+
+ bool isInEditMode() const;
+
+ static void launchGlobalAnnotations();
+
+signals:
+ void navigationHistoryChanged();
+ void openDocumentsChanged();
+ void documentIndexChanged();
+ void currentWorkspaceChanged();
+ void workspacesChanged();
+ void isInDesignModeChanged();
+ void isInEditModeChanged();
+ void isDesignModeEnabledChanged();
+ void currentStyleChanged();
+ void kitsChanged();
+ void currentKitChanged();
+ void isQt6Changed();
+ void projectOpenedChanged();
+
+private:
+ void setupWorkspaces();
+
+ ActionInterface *m_zoomAction;
+
+ QStringList m_openDocuments;
+ QStringList m_workspaces;
+ QMetaObject::Connection m_kitConnection;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp
index bc050582ad..194a4ad221 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp
@@ -23,6 +23,7 @@
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
+#include <utils/stylehelper.h>
#include <QApplication>
#include <QComboBox>
@@ -83,6 +84,7 @@ TransitionEditorToolBar::TransitionEditorToolBar(QWidget *parent)
: QToolBar(parent)
, m_grp()
{
+ setFixedHeight(Theme::toolbarSize());
setContentsMargins(0, 0, 0, 0);
createLeftControls();
createCenterControls();
@@ -165,7 +167,7 @@ void TransitionEditorToolBar::createLeftControls()
addSpacingToGroup(5);
auto *settingsAction = createAction(TransitionEditorConstants::C_SETTINGS,
- TimelineIcons::ANIMATION.icon(),
+ Theme::iconFromName(Theme::Icon::settings_medium),
tr("Transition Settings"),
QKeySequence(Qt::Key_S));
connect(settingsAction,
@@ -206,7 +208,7 @@ void TransitionEditorToolBar::createCenterControls()
addSpacing(10);
auto *curvePicker = createAction(TransitionEditorConstants::C_CURVE_PICKER,
- TimelineIcons::CURVE_EDITOR.icon(),
+ Theme::iconFromName(Theme::Icon::curveDesigner_medium),
tr("Easing Curve Editor"),
QKeySequence(Qt::Key_C));
@@ -236,7 +238,7 @@ void TransitionEditorToolBar::createRightControls()
addSpacing(10);
auto *zoomOut = createAction(TransitionEditorConstants::C_ZOOM_OUT,
- TimelineIcons::ZOOM_SMALL.icon(),
+ Theme::iconFromName(Theme::Icon::zoomOut_medium),
tr("Zoom Out"),
QKeySequence(QKeySequence::ZoomOut));
@@ -248,6 +250,8 @@ void TransitionEditorToolBar::createRightControls()
addSpacing(10);
m_scale = new QSlider(this);
+ Utils::StyleHelper::setPanelWidget(m_scale);
+ Utils::StyleHelper::setPanelWidgetSingleRow(m_scale);
m_scale->setOrientation(Qt::Horizontal);
m_scale->setMaximumWidth(200);
m_scale->setMinimumWidth(100);
@@ -261,7 +265,7 @@ void TransitionEditorToolBar::createRightControls()
addSpacing(10);
auto *zoomIn = createAction(TransitionEditorConstants::C_ZOOM_IN,
- TimelineIcons::ZOOM_BIG.icon(),
+ Theme::iconFromName(Theme::Icon::zoomIn_medium),
tr("Zoom In"),
QKeySequence(QKeySequence::ZoomIn));
@@ -279,6 +283,8 @@ void TransitionEditorToolBar::createRightControls()
auto emitEndChanged = [this]() { emit durationChanged(m_duration->text().toInt()); };
connect(m_duration, &QLineEdit::editingFinished, emitEndChanged);
+
+ addSpacing(5);
}
void TransitionEditorToolBar::addSpacing(int width)
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index eb183050e4..03ebf4738e 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -311,7 +311,8 @@ WidgetInfo TransitionEditorView::widgetInfo()
"TransitionEditor",
WidgetInfo::BottomPane,
0,
- tr("Transitions"));
+ tr("Transitions"),
+ tr("Transitions view"));
}
void TransitionEditorView::openSettingsDialog()
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp
index e532c6e741..adbe164201 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp
@@ -92,6 +92,9 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view)
setWindowTitle(tr("Transition", "Title of transition view"));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_toolbar->setStyleSheet(Theme::replaceCssColors(
+ QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
+
const QString css = Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")));
diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp
index f1964bf131..ed168b8a09 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp
@@ -44,14 +44,30 @@ void AsynchronousExplicitImageCache::request(Utils::SmallStringView name,
const auto id = extraId.empty() ? Utils::PathString{name}
: Utils::PathString::join({name, "+", extraId});
- const auto entry = requestType == RequestType::Image
- ? storage.fetchImage(id, Sqlite::TimeStamp{})
- : storage.fetchSmallImage(id, Sqlite::TimeStamp{});
-
- if (entry && !entry->isNull())
- captureCallback(*entry);
- else
- abortCallback(ImageCache::AbortReason::Failed);
+ auto requestImageFromStorage = [&](RequestType requestType) {
+ switch (requestType) {
+ case RequestType::Image:
+ return storage.fetchImage(id, Sqlite::TimeStamp{});
+ case RequestType::MidSizeImage:
+ return storage.fetchMidSizeImage(id, Sqlite::TimeStamp{});
+ case RequestType::SmallImage:
+ return storage.fetchSmallImage(id, Sqlite::TimeStamp{});
+ default:
+ break;
+ }
+
+ return storage.fetchImage(id, Sqlite::TimeStamp{});
+ };
+ const auto entry = requestImageFromStorage(requestType);
+
+ if (entry) {
+ if (entry->isNull())
+ abortCallback(ImageCache::AbortReason::Failed);
+ else
+ captureCallback(*entry);
+ } else {
+ abortCallback(ImageCache::AbortReason::NoEntry);
+ }
}
void AsynchronousExplicitImageCache::wait()
@@ -75,6 +91,19 @@ void AsynchronousExplicitImageCache::requestImage(Utils::PathString name,
m_condition.notify_all();
}
+void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::PathString name,
+ ImageCache::CaptureImageCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ Utils::SmallString extraId)
+{
+ addEntry(std::move(name),
+ std::move(extraId),
+ std::move(captureCallback),
+ std::move(abortCallback),
+ RequestType::MidSizeImage);
+ m_condition.notify_all();
+}
+
void AsynchronousExplicitImageCache::requestSmallImage(Utils::PathString name,
ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp
index 2e6e77b991..dec46f8725 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp
@@ -57,8 +57,21 @@ void AsynchronousImageCache::request(Utils::SmallStringView name,
: Utils::PathString::join({name, "+", extraId});
const auto timeStamp = timeStampProvider.timeStamp(name);
- const auto entry = requestType == RequestType::Image ? storage.fetchImage(id, timeStamp)
- : storage.fetchSmallImage(id, timeStamp);
+ auto requestImageFromStorage = [&](RequestType requestType) {
+ switch (requestType) {
+ case RequestType::Image:
+ return storage.fetchImage(id, timeStamp);
+ case RequestType::MidSizeImage:
+ return storage.fetchMidSizeImage(id, timeStamp);
+ case RequestType::SmallImage:
+ return storage.fetchSmallImage(id, timeStamp);
+ default:
+ break;
+ }
+
+ return storage.fetchImage(id, timeStamp);
+ };
+ const auto entry = requestImageFromStorage(requestType);
if (entry) {
if (entry->isNull())
@@ -66,10 +79,28 @@ void AsynchronousImageCache::request(Utils::SmallStringView name,
else
captureCallback(*entry);
} else {
- auto callback = [captureCallback = std::move(captureCallback),
- requestType](const QImage &image, const QImage &smallImage) {
- captureCallback(requestType == RequestType::Image ? image : smallImage);
- };
+ auto callback =
+ [captureCallback = std::move(captureCallback),
+ requestType](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) {
+ auto selectImage = [](RequestType requestType,
+ const QImage &image,
+ const QImage &midSizeImage,
+ const QImage &smallImage) {
+ switch (requestType) {
+ case RequestType::Image:
+ return image;
+ case RequestType::MidSizeImage:
+ return midSizeImage;
+ case RequestType::SmallImage:
+ return smallImage;
+ default:
+ break;
+ }
+
+ return image;
+ };
+ captureCallback(selectImage(requestType, image, midSizeImage, smallImage));
+ };
generator.generateImage(name,
extraId,
timeStamp,
@@ -102,6 +133,21 @@ void AsynchronousImageCache::requestImage(Utils::PathString name,
m_condition.notify_all();
}
+void AsynchronousImageCache::requestMidSizeImage(Utils::PathString name,
+ ImageCache::CaptureImageCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ Utils::SmallString extraId,
+ ImageCache::AuxiliaryData auxiliaryData)
+{
+ addEntry(std::move(name),
+ std::move(extraId),
+ std::move(captureCallback),
+ std::move(abortCallback),
+ std::move(auxiliaryData),
+ RequestType::MidSizeImage);
+ m_condition.notify_all();
+}
+
void AsynchronousImageCache::requestSmallImage(Utils::PathString name,
ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp
index 2807196f43..500359182a 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp
@@ -99,8 +99,8 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name,
if (currentModifiedTime < (storageModifiedTime + pause))
return;
- auto capture = [=](const QImage &image, const QImage &smallImage) {
- m_storage.storeImage(id, currentModifiedTime, image, smallImage);
+ auto capture = [=](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) {
+ m_storage.storeImage(id, currentModifiedTime, image, midSizeImage, smallImage);
};
collector.start(name,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp
index a1bd876cfd..1f28d4eca6 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp
@@ -2,51 +2,23 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "explicitimagecacheimageprovider.h"
+#include "imagecacheimageresponse.h"
#include <asynchronousexplicitimagecache.h>
#include <QMetaObject>
#include <QQuickImageResponse>
-namespace {
-
-class ImageResponse : public QQuickImageResponse
-{
-public:
- ImageResponse(const QImage &defaultImage)
- : m_image(defaultImage)
- {}
-
- QQuickTextureFactory *textureFactory() const override
- {
- return QQuickTextureFactory::textureFactoryForImage(m_image);
- }
-
- void setImage(const QImage &image)
- {
- m_image = image;
-
- emit finished();
- }
-
- void abort() { emit finished(); }
-
-private:
- QImage m_image;
-};
-
-} // namespace
-
namespace QmlDesigner {
QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const QString &id,
const QSize &)
{
- auto response = std::make_unique<::ImageResponse>(m_defaultImage);
+ auto response = std::make_unique<ImageCacheImageResponse>(m_defaultImage);
m_cache.requestImage(
id,
- [response = QPointer<::ImageResponse>(response.get())](const QImage &image) {
+ [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) {
QMetaObject::invokeMethod(
response,
[response, image] {
@@ -55,15 +27,20 @@ QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const
},
Qt::QueuedConnection);
},
- [response = QPointer<::ImageResponse>(response.get())](ImageCache::AbortReason abortReason) {
+ [response = QPointer<ImageCacheImageResponse>(response.get()),
+ failedImage = m_failedImage](ImageCache::AbortReason abortReason) {
QMetaObject::invokeMethod(
response,
- [response, abortReason] {
+ [response, abortReason, failedImage] {
switch (abortReason) {
- case ImageCache::AbortReason::Failed:
+ case ImageCache::AbortReason::NoEntry:
if (response)
response->abort();
break;
+ case ImageCache::AbortReason::Failed:
+ if (response)
+ response->setImage(failedImage);
+ break;
case ImageCache::AbortReason::Abort:
response->cancel();
break;
diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h
index 54a0df652e..bef22d68a7 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h
@@ -14,9 +14,11 @@ class ExplicitImageCacheImageProvider : public QQuickAsyncImageProvider
{
public:
ExplicitImageCacheImageProvider(AsynchronousExplicitImageCache &imageCache,
- const QImage &defaultImage)
+ const QImage &defaultImage,
+ const QImage &failedImage)
: m_cache(imageCache)
, m_defaultImage(defaultImage)
+ , m_failedImage(failedImage)
{}
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
@@ -24,6 +26,7 @@ public:
private:
AsynchronousExplicitImageCache &m_cache;
QImage m_defaultImage;
+ QImage m_failedImage;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
index 8bc1698524..344bd4a019 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
@@ -53,6 +53,22 @@ ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connection
ImageCacheCollector::~ImageCacheCollector() = default;
+namespace {
+QImage scaleImage(const QImage &image, QSize targetSize)
+{
+ if (image.isNull())
+ return {};
+
+ const qreal ratio = qGuiApp->devicePixelRatio();
+ if (ratio > 1.0)
+ targetSize *= qRound(ratio);
+ QSize scaledImageSize = image.size().scaled(targetSize.boundedTo(image.size()),
+ Qt::KeepAspectRatio);
+ return image.scaled(scaledImageSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+}
+} // namespace
+
void ImageCacheCollector::start(Utils::SmallStringView name,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData,
@@ -104,17 +120,9 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
auto callback = [=, captureCallback = std::move(captureCallback)](const QImage &image) {
if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage
|| !image.isNull()) {
- QSize targetSize {96, 96};
- const qreal ratio = qGuiApp->devicePixelRatio();
- if (ratio > 1.0)
- targetSize *= qRound(ratio);
- QSize smallImageSize = image.size().scaled(targetSize.boundedTo(image.size()),
- Qt::KeepAspectRatio);
- QImage smallImage = image.isNull() ? QImage{}
- : image.scaled(smallImageSize,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- captureCallback(image, smallImage);
+ QImage midSizeImage = scaleImage(image, QSize{300, 300});
+ QImage smallImage = scaleImage(midSizeImage, QSize{96, 96});
+ captureCallback(image, midSizeImage, smallImage);
}
};
@@ -138,9 +146,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
abortCallback(ImageCache::AbortReason::Failed);
}
-std::pair<QImage, QImage> ImageCacheCollector::createImage(Utils::SmallStringView,
- Utils::SmallStringView,
- const ImageCache::AuxiliaryData &)
+ImageCacheCollectorInterface::ImageTuple ImageCacheCollector::createImage(
+ Utils::SmallStringView, Utils::SmallStringView, const ImageCache::AuxiliaryData &)
{
return {};
}
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
index 753d92a37d..87ebedba04 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
@@ -43,9 +43,9 @@ public:
CaptureCallback captureCallback,
AbortCallback abortCallback) override;
- std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
- Utils::SmallStringView state,
- const ImageCache::AuxiliaryData &auxiliaryData) override;
+ ImageTuple createImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData) override;
QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView state,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
index 888fd281a0..057ab6f03e 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
@@ -14,9 +14,9 @@ namespace QmlDesigner {
class ImageCacheCollectorInterface
{
public:
- using CaptureCallback = ImageCache::CaptureImageWithSmallImageCallback;
+ using CaptureCallback = ImageCache::CaptureImageWithScaledImagesCallback;
using AbortCallback = ImageCache::AbortCallback;
- using ImagePair = std::pair<QImage, QImage>;
+ using ImageTuple = std::tuple<QImage, QImage, QImage>;
virtual void start(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
@@ -25,9 +25,9 @@ public:
AbortCallback abortCallback)
= 0;
- virtual ImagePair createImage(Utils::SmallStringView filePath,
- Utils::SmallStringView extraId,
- const ImageCache::AuxiliaryData &auxiliaryData)
+ virtual ImageTuple createImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView extraId,
+ const ImageCache::AuxiliaryData &auxiliaryData)
= 0;
virtual QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h
index ece8aa3480..274cf72ad6 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h
@@ -32,9 +32,9 @@ public:
m_collectors);
}
- std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
- Utils::SmallStringView state,
- const ImageCache::AuxiliaryData &auxiliaryData) override
+ ImageTuple createImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData) override
{
return std::apply(
[&](const auto &...entries) {
@@ -84,9 +84,10 @@ private:
Utils::SmallStringView,
const ImageCache::AuxiliaryData &,
CaptureCallback,
- AbortCallback)
+ AbortCallback abortCallback)
{
qWarning() << "ImageCacheDispatchCollector: cannot handle file type.";
+ abortCallback(ImageCache::AbortReason::Failed);
}
template<typename Collector, typename... Collectors>
@@ -113,11 +114,11 @@ private:
}
template<typename Collector, typename... Collectors>
- std::pair<QImage, QImage> dispatchCreateImage(Utils::SmallStringView filePath,
- Utils::SmallStringView state,
- const ImageCache::AuxiliaryData &auxiliaryData,
- const Collector &collector,
- const Collectors &...collectors)
+ ImageTuple dispatchCreateImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData,
+ const Collector &collector,
+ const Collectors &...collectors)
{
if (collector.first(filePath, state, auxiliaryData)) {
return collector.second->createImage(filePath, state, auxiliaryData);
@@ -126,9 +127,9 @@ private:
return dispatchCreateImage(filePath, state, auxiliaryData, collectors...);
}
- std::pair<QImage, QImage> dispatchCreateImage(Utils::SmallStringView,
- Utils::SmallStringView,
- const ImageCache::AuxiliaryData &)
+ ImageTuple dispatchCreateImage(Utils::SmallStringView,
+ Utils::SmallStringView,
+ const ImageCache::AuxiliaryData &)
{
qWarning() << "ImageCacheDispatchCollector: cannot handle file type.";
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp
index fb621411b5..d3363e2d63 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp
@@ -106,14 +106,14 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
QImage image = createFontImage(text, textColor, font, size);
if (!image.isNull()) {
- captureCallback(std::move(image), {});
+ captureCallback(std::move(image), {}, {});
return;
}
}
abortCallback(ImageCache::AbortReason::Failed);
}
-std::pair<QImage, QImage> ImageCacheFontCollector::createImage(
+ImageCacheCollectorInterface::ImageTuple ImageCacheFontCollector::createImage(
Utils::SmallStringView name,
Utils::SmallStringView,
const ImageCache::AuxiliaryData &auxiliaryDataValue)
@@ -128,7 +128,7 @@ std::pair<QImage, QImage> ImageCacheFontCollector::createImage(
QImage image = createFontImage(text, textColor, font, size);
if (!image.isNull())
- return {image, {}};
+ return {image, {}, {}};
}
return {};
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h
index 2129c80307..ff7f624e22 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h
@@ -20,9 +20,9 @@ public:
CaptureCallback captureCallback,
AbortCallback abortCallback) override;
- std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
- Utils::SmallStringView extraId,
- const ImageCache::AuxiliaryData &auxiliaryData) override;
+ ImageTuple createImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView extraId,
+ const ImageCache::AuxiliaryData &auxiliaryData) override;
QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
index 5e1fe49ea0..42aef4fd20 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
@@ -28,7 +28,7 @@ ImageCacheGenerator::~ImageCacheGenerator()
void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
Utils::SmallStringView extraId,
Sqlite::TimeStamp timeStamp,
- ImageCache::CaptureImageWithSmallImageCallback &&captureCallback,
+ ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback,
ImageCache::AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData)
{
@@ -113,21 +113,26 @@ void ImageCacheGenerator::startGeneration()
task.filePath,
task.extraId,
std::move(task.auxiliaryData),
- [this, task](const QImage &image, const QImage &smallImage) {
- if (image.isNull())
+ [this, task](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) {
+ if (image.isNull() && midSizeImage.isNull() && smallImage.isNull())
callCallbacks(task.abortCallbacks, ImageCache::AbortReason::Failed);
else
- callCallbacks(task.captureCallbacks, image, smallImage);
+ callCallbacks(task.captureCallbacks, image, midSizeImage, smallImage);
m_storage.storeImage(createId(task.filePath, task.extraId),
task.timeStamp,
image,
+ midSizeImage,
smallImage);
},
[this, task](ImageCache::AbortReason abortReason) {
callCallbacks(task.abortCallbacks, abortReason);
if (abortReason != ImageCache::AbortReason::Abort)
- m_storage.storeImage(createId(task.filePath, task.extraId), task.timeStamp, {}, {});
+ m_storage.storeImage(createId(task.filePath, task.extraId),
+ task.timeStamp,
+ {},
+ {},
+ {});
});
std::lock_guard lock{m_mutex};
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
index 5999a2558b..60d8195926 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
@@ -33,7 +33,7 @@ public:
void generateImage(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
Sqlite::TimeStamp timeStamp,
- ImageCache::CaptureImageWithSmallImageCallback &&captureCallback,
+ ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback,
ImageCache::AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData) override;
void clean() override;
@@ -48,7 +48,7 @@ private:
Utils::SmallStringView extraId,
ImageCache::AuxiliaryData &&auxiliaryData,
Sqlite::TimeStamp timeStamp,
- ImageCache::CaptureImageWithSmallImageCallback &&captureCallback,
+ ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback,
ImageCache::AbortCallback &&abortCallback)
: filePath(filePath)
, extraId(std::move(extraId))
@@ -61,7 +61,7 @@ private:
Utils::PathString filePath;
Utils::SmallString extraId;
ImageCache::AuxiliaryData auxiliaryData;
- std::vector<ImageCache::CaptureImageWithSmallImageCallback> captureCallbacks;
+ std::vector<ImageCache::CaptureImageWithScaledImagesCallback> captureCallbacks;
std::vector<ImageCache::AbortCallback> abortCallbacks;
Sqlite::TimeStamp timeStamp;
};
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
index 815fb8cc81..d18f0def32 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
@@ -17,7 +17,7 @@ public:
virtual void generateImage(Utils::SmallStringView name,
Utils::SmallStringView extraId,
Sqlite::TimeStamp timeStamp,
- ImageCache::CaptureImageWithSmallImageCallback &&captureCallback,
+ ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback,
ImageCache::AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData)
= 0;
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp
new file mode 100644
index 0000000000..38fc9491d2
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "imagecacheimageresponse.h"
+
+namespace QmlDesigner {
+
+QQuickTextureFactory *ImageCacheImageResponse::textureFactory() const
+{
+ return QQuickTextureFactory::textureFactoryForImage(m_image);
+}
+
+void ImageCacheImageResponse::setImage(const QImage &image)
+{
+ m_image = image;
+
+ emit finished();
+}
+
+void ImageCacheImageResponse::abort()
+{
+ emit finished();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h
new file mode 100644
index 0000000000..1a4400e989
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QQuickImageResponse>
+
+namespace QmlDesigner {
+
+class AsynchronousImageCache;
+
+class ImageCacheImageResponse : public QQuickImageResponse
+{
+public:
+ ImageCacheImageResponse(const QImage &defaultImage = {})
+ : m_image(defaultImage)
+ {}
+
+ QQuickTextureFactory *textureFactory() const override;
+
+ void setImage(const QImage &image);
+ QImage image() const { return m_image; }
+
+ void abort();
+
+private:
+ QImage m_image;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
index fb81a3405e..6b0df6f1c1 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
@@ -51,6 +51,26 @@ public:
}
}
+ ImageEntry fetchMidSizeImage(Utils::SmallStringView name,
+ Sqlite::TimeStamp minimumTimeStamp) const override
+ {
+ try {
+ Sqlite::DeferredTransaction transaction{database};
+
+ auto optionalBlob = selectMidSizeImageStatement.template optionalValue<Sqlite::ByteArrayBlob>(
+ name, minimumTimeStamp.value);
+
+ transaction.commit();
+
+ if (optionalBlob)
+ return {readImage(optionalBlob->byteArray)};
+
+ return {};
+ } catch (const Sqlite::StatementIsBusy &) {
+ return fetchMidSizeImage(name, minimumTimeStamp);
+ }
+ }
+
ImageEntry fetchSmallImage(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const override
{
@@ -95,22 +115,25 @@ public:
void storeImage(Utils::SmallStringView name,
Sqlite::TimeStamp newTimeStamp,
const QImage &image,
+ const QImage &midSizeImage,
const QImage &smallImage) override
{
try {
Sqlite::ImmediateTransaction transaction{database};
auto imageBuffer = createBuffer(image);
+ auto midSizeImageBuffer = createBuffer(midSizeImage);
auto smallImageBuffer = createBuffer(smallImage);
upsertImageStatement.write(name,
newTimeStamp.value,
createBlobView(imageBuffer.get()),
+ createBlobView(midSizeImageBuffer.get()),
createBlobView(smallImageBuffer.get()));
transaction.commit();
} catch (const Sqlite::StatementIsBusy &) {
- return storeImage(name, newTimeStamp, image, smallImage);
+ return storeImage(name, newTimeStamp, image, midSizeImage, smallImage);
}
}
@@ -158,12 +181,15 @@ private:
Sqlite::ExclusiveTransaction transaction{database};
createImagesTable(database);
+ database.setVersion(1);
transaction.commit();
database.setIsInitialized(true);
database.walCheckpointFull();
+ } else if (database.version() < 1) {
+ updateTableToVersion1(database);
}
}
@@ -179,6 +205,7 @@ private:
imageTable.addColumn("mtime", Sqlite::ColumnType::Integer);
imageTable.addColumn("image", Sqlite::ColumnType::Blob);
imageTable.addColumn("smallImage", Sqlite::ColumnType::Blob);
+ imageTable.addColumn("midSizeImage", Sqlite::ColumnType::Blob);
imageTable.initialize(database);
@@ -194,6 +221,17 @@ private:
iconTable.initialize(database);
}
+
+ void updateTableToVersion1(DatabaseType &database)
+ {
+ Sqlite::ExclusiveTransaction transaction{database};
+
+ database.execute("DELETE FROM images");
+ database.execute("ALTER TABLE images ADD COLUMN midSizeImage");
+ database.setVersion(1);
+
+ transaction.commit();
+ }
};
Sqlite::BlobView createBlobView(QBuffer *buffer)
@@ -264,14 +302,17 @@ public:
Sqlite::ImmediateNonThrowingDestructorTransaction<DatabaseType> transaction{database};
mutable ReadStatement<1, 2> selectImageStatement{
"SELECT image FROM images WHERE name=?1 AND mtime >= ?2", database};
+ mutable ReadStatement<1, 2> selectMidSizeImageStatement{
+ "SELECT midSizeImage FROM images WHERE name=?1 AND mtime >= ?2", database};
mutable ReadStatement<1, 2> selectSmallImageStatement{
"SELECT smallImage FROM images WHERE name=?1 AND mtime >= ?2", database};
mutable ReadStatement<1, 2> selectIconStatement{
"SELECT icon FROM icons WHERE name=?1 AND mtime >= ?2", database};
- WriteStatement<4> upsertImageStatement{
- "INSERT INTO images(name, mtime, image, smallImage) VALUES (?1, ?2, ?3, ?4) ON "
- "CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, "
- "smallImage=excluded.smallImage",
+ WriteStatement<5> upsertImageStatement{
+ "INSERT INTO images(name, mtime, image, midSizeImage, smallImage) "
+ "VALUES (?1, ?2, ?3, ?4, ?5) "
+ "ON CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, "
+ "midSizeImage=excluded.midSizeImage, smallImage=excluded.smallImage",
database};
WriteStatement<3> upsertIconStatement{
"INSERT INTO icons(name, mtime, icon) VALUES (?1, ?2, ?3) ON "
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
index c25cc9db75..4a2ff0944c 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
@@ -21,6 +21,8 @@ public:
virtual ImageEntry fetchImage(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const = 0;
+ virtual ImageEntry fetchMidSizeImage(Utils::SmallStringView name,
+ Sqlite::TimeStamp minimumTimeStamp) const = 0;
virtual ImageEntry fetchSmallImage(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const = 0;
virtual IconEntry fetchIcon(Utils::SmallStringView name,
@@ -28,6 +30,7 @@ public:
virtual void storeImage(Utils::SmallStringView name,
Sqlite::TimeStamp newTimeStamp,
const QImage &image,
+ const QImage &midSizeImage,
const QImage &smallImage)
= 0;
virtual void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) = 0;
diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp
index 12c05c4e6f..2063de8f3a 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp
@@ -66,9 +66,8 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name,
m_imageCacheCollector.start(path, state, auxiliaryData, captureCallback, abortCallback);
}
-std::pair<QImage, QImage> MeshImageCacheCollector::createImage(Utils::SmallStringView,
- Utils::SmallStringView,
- const ImageCache::AuxiliaryData &)
+ImageCacheCollectorInterface::ImageTuple MeshImageCacheCollector::createImage(
+ Utils::SmallStringView, Utils::SmallStringView, const ImageCache::AuxiliaryData &)
{
return {};
}
diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h
index 6c669d246b..17b1a412bc 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h
@@ -31,9 +31,9 @@ public:
CaptureCallback captureCallback,
AbortCallback abortCallback) override;
- std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
- Utils::SmallStringView state,
- const ImageCache::AuxiliaryData &auxiliaryData) override;
+ ImageTuple createImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData) override;
QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView state,
diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp
new file mode 100644
index 0000000000..fd37f02f04
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "midsizeimagecacheprovider.h"
+#include "imagecacheimageresponse.h"
+
+#include <asynchronousimagecache.h>
+
+#include <QMetaObject>
+
+namespace QmlDesigner {
+
+QQuickImageResponse *MidSizeImageCacheProvider::requestImageResponse(const QString &id, const QSize &)
+{
+ auto response = std::make_unique<ImageCacheImageResponse>(m_defaultImage);
+
+ m_cache.requestMidSizeImage(
+ id,
+ [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) {
+ QMetaObject::invokeMethod(
+ response,
+ [response, image] {
+ if (response)
+ response->setImage(image);
+ },
+ Qt::QueuedConnection);
+ },
+ [response = QPointer<ImageCacheImageResponse>(response.get())](
+ ImageCache::AbortReason abortReason) {
+ QMetaObject::invokeMethod(
+ response,
+ [response, abortReason] {
+ switch (abortReason) {
+ case ImageCache::AbortReason::Failed:
+ case ImageCache::AbortReason::NoEntry:
+ if (response)
+ response->abort();
+ break;
+ case ImageCache::AbortReason::Abort:
+ response->cancel();
+ break;
+ }
+ },
+ Qt::QueuedConnection);
+ });
+
+ return response.release();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h
new file mode 100644
index 0000000000..b3a84f0abd
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QQuickAsyncImageProvider>
+
+namespace QmlDesigner {
+
+class AsynchronousImageCache;
+
+class MidSizeImageCacheProvider : public QQuickAsyncImageProvider
+{
+public:
+ MidSizeImageCacheProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {})
+ : m_cache{imageCache}
+ , m_defaultImage(defaultImage)
+ {}
+
+ QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+ QImage defaultImage() const { return m_defaultImage; }
+
+private:
+ AsynchronousImageCache &m_cache;
+ QImage m_defaultImage;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp
index d0860054ca..3fae3bb175 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp
@@ -3,36 +3,21 @@
#include "smallimagecacheprovider.h"
+#include "imagecacheimageresponse.h"
+
#include <asynchronousimagecache.h>
#include <QMetaObject>
namespace QmlDesigner {
-QQuickTextureFactory *ImageResponse::textureFactory() const
-{
- return QQuickTextureFactory::textureFactoryForImage(m_image);
-}
-
-void ImageResponse::setImage(const QImage &image)
-{
- m_image = image;
-
- emit finished();
-}
-
-void ImageResponse::abort()
-{
- emit finished();
-}
-
QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString &id, const QSize &)
{
- auto response = std::make_unique<QmlDesigner::ImageResponse>(m_defaultImage);
+ auto response = std::make_unique<ImageCacheImageResponse>(m_defaultImage);
m_cache.requestSmallImage(
id,
- [response = QPointer<QmlDesigner::ImageResponse>(response.get())](const QImage &image) {
+ [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) {
QMetaObject::invokeMethod(
response,
[response, image] {
@@ -41,13 +26,14 @@ QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString
},
Qt::QueuedConnection);
},
- [response = QPointer<QmlDesigner::ImageResponse>(response.get())](
+ [response = QPointer<ImageCacheImageResponse>(response.get())](
ImageCache::AbortReason abortReason) {
QMetaObject::invokeMethod(
response,
[response, abortReason] {
switch (abortReason) {
case ImageCache::AbortReason::Failed:
+ case ImageCache::AbortReason::NoEntry:
if (response)
response->abort();
break;
diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h
index 03e0d089a1..40b542ebe4 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h
+++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h
@@ -4,30 +4,11 @@
#pragma once
#include <QQuickAsyncImageProvider>
-#include <QQuickImageResponse>
namespace QmlDesigner {
class AsynchronousImageCache;
-class ImageResponse : public QQuickImageResponse
-{
-public:
- ImageResponse(const QImage &defaultImage)
- : m_image(defaultImage)
- {}
-
- QQuickTextureFactory *textureFactory() const override;
-
- void setImage(const QImage &image);
- QImage image() const { return m_image; }
-
- void abort();
-
-private:
- QImage m_image;
-};
-
class SmallImageCacheProvider : public QQuickAsyncImageProvider
{
public:
diff --git a/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp
index ddb1cba666..e9a07b2b31 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp
@@ -32,13 +32,36 @@ QImage SynchronousImageCache::image(Utils::PathString filePath,
if (entry)
return *entry;
- const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData);
+ const auto &[image, midSizeImage, smallImage] = m_collector.createImage(filePath,
+ extraId,
+ auxiliaryData);
- m_storage.storeImage(id, timeStamp, image, smallImage);
+ m_storage.storeImage(id, timeStamp, image, midSizeImage, smallImage);
return image;
}
+QImage SynchronousImageCache::midSizeImage(Utils::PathString filePath,
+ Utils::SmallString extraId,
+ const ImageCache::AuxiliaryData &auxiliaryData)
+{
+ const auto id = createId(filePath, extraId);
+
+ const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
+ const auto entry = m_storage.fetchMidSizeImage(id, timeStamp);
+
+ if (entry)
+ return *entry;
+
+ const auto &[image, midSizeImage, smallImage] = m_collector.createImage(filePath,
+ extraId,
+ auxiliaryData);
+
+ m_storage.storeImage(id, timeStamp, image, midSizeImage, smallImage);
+
+ return midSizeImage;
+}
+
QImage SynchronousImageCache::smallImage(Utils::PathString filePath,
Utils::SmallString extraId,
const ImageCache::AuxiliaryData &auxiliaryData)
@@ -51,9 +74,11 @@ QImage SynchronousImageCache::smallImage(Utils::PathString filePath,
if (entry)
return *entry;
- const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData);
+ const auto &[image, midSizeImage, smallImage] = m_collector.createImage(filePath,
+ extraId,
+ auxiliaryData);
- m_storage.storeImage(id, timeStamp, image, smallImage);
+ m_storage.storeImage(id, timeStamp, image, midSizeImage, smallImage);
return smallImage;
}
diff --git a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp
new file mode 100644
index 0000000000..af310ae6e7
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.cpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "textureimagecachecollector.h"
+
+#include <utils/asset.h>
+#include <utils/hdrimage.h>
+#include <utils/smallstring.h>
+#include <utils/stylehelper.h>
+
+namespace QmlDesigner {
+
+TextureImageCacheCollector::TextureImageCacheCollector() = default;
+TextureImageCacheCollector::~TextureImageCacheCollector() = default;
+
+void TextureImageCacheCollector::start(Utils::SmallStringView name,
+ Utils::SmallStringView,
+ const ImageCache::AuxiliaryData &,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+{
+ Asset asset {QString(name)};
+ QImage image;
+
+ if (asset.isImage()) {
+ image = QImage(Utils::StyleHelper::dpiSpecificImageFile(asset.id()));
+ } else if (asset.isHdrFile()) {
+ HdrImage hdr{asset.id()};
+ if (!hdr.image().isNull())
+ image = hdr.image().copy(); // Copy to ensure image data survives HdrImage destruction
+ }
+
+ if (image.isNull())
+ abortCallback(ImageCache::AbortReason::Failed);
+ else
+ image = image.scaled(QSize{300, 300}, Qt::KeepAspectRatio);
+
+ captureCallback({}, image, {});
+}
+
+ImageCacheCollectorInterface::ImageTuple TextureImageCacheCollector::createImage(
+ Utils::SmallStringView, Utils::SmallStringView, const ImageCache::AuxiliaryData &)
+{
+ return {};
+}
+
+QIcon TextureImageCacheCollector::createIcon(Utils::SmallStringView,
+ Utils::SmallStringView,
+ const ImageCache::AuxiliaryData &)
+{
+ return {};
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h
new file mode 100644
index 0000000000..67876d7641
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/textureimagecachecollector.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "imagecachecollectorinterface.h"
+
+namespace QmlDesigner {
+
+class TextureImageCacheCollector final : public ImageCacheCollectorInterface
+{
+public:
+ TextureImageCacheCollector();
+ ~TextureImageCacheCollector();
+
+ void start(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback) override;
+
+ ImageTuple createImage(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData) override;
+
+ QIcon createIcon(Utils::SmallStringView filePath,
+ Utils::SmallStringView state,
+ const ImageCache::AuxiliaryData &auxiliaryData) override;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h
index 8f18da1544..2d886d959b 100644
--- a/src/plugins/qmldesigner/designercore/include/abstractview.h
+++ b/src/plugins/qmldesigner/designercore/include/abstractview.h
@@ -3,43 +3,35 @@
#pragma once
-#include <qmldesignercorelib_global.h>
-
-#include <model.h>
-#include <modelnode.h>
-#include <abstractproperty.h>
-#include <documentmessage.h>
-#include <rewritertransaction.h>
+#include "model.h"
+#include "modelnode.h"
+#include "qmldesignercorelib_global.h"
#include <commondefines.h>
#include <QObject>
#include <QPointer>
-#include <QVector3D>
-
-#include <functional>
-#include <memory>
QT_BEGIN_NAMESPACE
-class QStyle;
-class QToolButton;
class QImage;
class QPixmap;
+class QVector3D;
QT_END_NAMESPACE
namespace QmlDesigner {
- namespace Internal {
- class InternalNode;
- using InternalNodePointer = std::shared_ptr<InternalNode>;
- }
-}
-
-namespace QmlDesigner {
+class AbstractProperty;
+class DocumentMessage;
+class ExternalDependenciesInterface;
class NodeInstanceView;
-class RewriterView;
class QmlModelState;
class QmlTimeline;
-class ExternalDependenciesInterface;
+class RewriterTransaction;
+class RewriterView;
+
+namespace Internal {
+class InternalNode;
+using InternalNodePointer = std::shared_ptr<InternalNode>;
+}
enum DesignerWidgetFlags {
DisableOnError,
@@ -60,6 +52,7 @@ public:
QString uniqueId;
QString tabName;
+ QString feedbackDisplayName;
QWidget *widget = nullptr;
int placementPriority;
PlacementHint placementHint;
@@ -74,18 +67,19 @@ public:
Q_FLAGS(PropertyChangeFlag PropertyChangeFlags)
enum PropertyChangeFlag {
- NoAdditionalChanges = 0x0,
- PropertiesAdded = 0x1,
+ NoAdditionalChanges = 0x0,
+ PropertiesAdded = 0x1,
EmptyPropertiesRemoved = 0x2
};
Q_DECLARE_FLAGS(PropertyChangeFlags, PropertyChangeFlag)
+
AbstractView(ExternalDependenciesInterface &externalDependencies)
: m_externalDependencies{externalDependencies}
{}
~AbstractView() override;
- Model* model() const;
+ Model *model() const;
bool isAttached() const;
RewriterTransaction beginRewriterTransaction(const QByteArray &identifier);
@@ -127,7 +121,7 @@ public:
QList<ModelNode> allModelNodes() const;
QList<ModelNode> allModelNodesOfType(const NodeMetaInfo &typeName) const;
- void emitDocumentMessage(const QList<DocumentMessage> &errors, const QList<DocumentMessage> &warnings = QList<DocumentMessage>());
+ void emitDocumentMessage(const QList<DocumentMessage> &errors, const QList<DocumentMessage> &warnings = {});
void emitDocumentMessage(const QString &error);
void emitCustomNotification(const QString &identifier);
void emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList);
@@ -155,19 +149,25 @@ public:
virtual void nodeCreated(const ModelNode &createdNode);
virtual void nodeAboutToBeRemoved(const ModelNode &removedNode);
- virtual void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange);
- virtual void nodeAboutToBeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange);
- virtual void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange);
- virtual void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId);
- virtual void propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList);
- virtual void propertiesRemoved(const QList<AbstractProperty>& propertyList);
- virtual void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange);
+ virtual void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
+ PropertyChangeFlags propertyChange);
+ virtual void nodeAboutToBeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,
+ const NodeAbstractProperty &oldPropertyParent, PropertyChangeFlags propertyChange);
+ virtual void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent,
+ const NodeAbstractProperty &oldPropertyParent, PropertyChangeFlags propertyChange);
+ virtual void nodeIdChanged(const ModelNode &node, const QString &newId, const QString &oldId);
+ virtual void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList);
+ virtual void propertiesRemoved(const QList<AbstractProperty> &propertyList);
+ virtual void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
+ PropertyChangeFlags propertyChange);
virtual void bindingPropertiesAboutToBeChanged(const QList<BindingProperty> &propertyList);
- virtual void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange);
- virtual void signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty>& propertyList, PropertyChangeFlags propertyChange);
- virtual void signalDeclarationPropertiesChanged(const QVector<SignalDeclarationProperty>& propertyList, PropertyChangeFlags propertyChange);
+ virtual void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags propertyChange);
+ virtual void signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &propertyList,
+ PropertyChangeFlags propertyChange);
+ virtual void signalDeclarationPropertiesChanged(const QVector<SignalDeclarationProperty> &propertyList,
+ PropertyChangeFlags propertyChange);
virtual void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion);
- virtual void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion);
+ virtual void nodeTypeChanged(const ModelNode &node, const TypeName &type, int majorVersion, int minorVersion);
virtual void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList);
virtual void instanceErrorChanged(const QVector<ModelNode> &errorNodeList);
@@ -228,8 +228,8 @@ public:
void ensureMaterialLibraryNode();
ModelNode materialLibraryNode();
+ bool isPartOfMaterialLibrary(const ModelNode &node);
ModelNode active3DSceneNode();
- void assignMaterialTo3dModel(const ModelNode &modelNode, const ModelNode &materialNode = {});
ModelNode getTextureDefaultInstance(const QString &source);
const NodeInstanceView *nodeInstanceView() const;
@@ -244,7 +244,6 @@ public:
int minorQtQuickVersion() const;
void resetView();
-
void resetPuppet();
virtual bool hasWidget() const;
@@ -286,16 +285,18 @@ public:
protected:
void setModel(Model *model);
void removeModel();
- static WidgetInfo createWidgetInfo(QWidget *widget = nullptr,
- const QString &uniqueId = QString(),
- WidgetInfo::PlacementHint placementHint = WidgetInfo::NoPane,
- int placementPriority = 0,
- const QString &tabName = QString(), DesignerWidgetFlags widgetFlags = DesignerWidgetFlags::DisableOnError);
+ static WidgetInfo createWidgetInfo(
+ QWidget *widget = nullptr,
+ const QString &uniqueId = QString(),
+ WidgetInfo::PlacementHint placementHint = WidgetInfo::NoPane,
+ int placementPriority = 0,
+ const QString &tabName = QString(),
+ const QString &feedbackDisplayName = QString(),
+ DesignerWidgetFlags widgetFlags = DesignerWidgetFlags::DisableOnError);
-private: //functions
+private:
QList<ModelNode> toModelNodeList(const QList<Internal::InternalNodePointer> &nodeList) const;
-private:
QPointer<Model> m_model;
ExternalDependenciesInterface &m_externalDependencies;
bool m_enabled = true;
@@ -304,5 +305,4 @@ private:
QMLDESIGNERCORE_EXPORT QList<Internal::InternalNodePointer> toInternalNodeList(const QList<ModelNode> &nodeList);
QMLDESIGNERCORE_EXPORT QList<ModelNode> toModelNodeList(const QList<Internal::InternalNodePointer> &nodeList, AbstractView *view);
-
-}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h
index a8d9ff17b7..7cddfd29d9 100644
--- a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h
+++ b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h
@@ -27,6 +27,10 @@ public:
ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback,
Utils::SmallString extraId = {});
+ void requestMidSizeImage(Utils::PathString name,
+ ImageCache::CaptureImageCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ Utils::SmallString extraId = {});
void requestSmallImage(Utils::PathString name,
ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback,
@@ -35,7 +39,7 @@ public:
void clean();
private:
- enum class RequestType { Image, SmallImage, Icon };
+ enum class RequestType { Image, MidSizeImage, SmallImage, Icon };
struct RequestEntry
{
RequestEntry() = default;
diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h
index aff814b8d8..04882d61eb 100644
--- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h
+++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h
@@ -33,6 +33,11 @@ public:
ImageCache::AbortCallback abortCallback,
Utils::SmallString extraId = {},
ImageCache::AuxiliaryData auxiliaryData = {}) override;
+ void requestMidSizeImage(Utils::PathString name,
+ ImageCache::CaptureImageCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ Utils::SmallString extraId = {},
+ ImageCache::AuxiliaryData auxiliaryData = {}) override;
void requestSmallImage(Utils::PathString name,
ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback,
@@ -42,7 +47,7 @@ public:
void clean();
private:
- enum class RequestType { Image, SmallImage, Icon };
+ enum class RequestType { Image, MidSizeImage, SmallImage, Icon };
struct Entry
{
Entry() = default;
diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h
index c71b6ab758..da8bda079e 100644
--- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h
+++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h
@@ -20,6 +20,12 @@ public:
Utils::SmallString extraId = {},
ImageCache::AuxiliaryData auxiliaryData = {})
= 0;
+ virtual void requestMidSizeImage(Utils::PathString name,
+ ImageCache::CaptureImageCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ Utils::SmallString extraId = {},
+ ImageCache::AuxiliaryData auxiliaryData = {})
+ = 0;
virtual void requestSmallImage(Utils::PathString name,
ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback,
diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
index ed4fe77b57..7b2b8a2c55 100644
--- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
+++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h
@@ -4,6 +4,7 @@
#pragma once
#include <instances/puppetstartdata.h>
+#include <utils/filepath.h>
namespace QmlDesigner {
@@ -38,6 +39,7 @@ public:
virtual bool hasStartupTarget() const = 0;
virtual PuppetStartData puppetStartData(const class Model &model) const = 0;
virtual bool instantQmlTextUpdate() const = 0;
+ virtual Utils::FilePath qmlPuppetPath() const = 0;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h
index 3c688c5dc5..c147fb0753 100644
--- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h
+++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h
@@ -43,10 +43,11 @@ using AuxiliaryData = std::variant<std::monostate,
FontCollectorSizeAuxiliaryData,
FontCollectorSizesAuxiliaryData>;
-enum class AbortReason : char { Abort, Failed };
+enum class AbortReason : char { Abort, Failed, NoEntry };
using CaptureImageCallback = std::function<void(const QImage &)>;
-using CaptureImageWithSmallImageCallback = std::function<void(const QImage &image, const QImage &smallImage)>;
+using CaptureImageWithScaledImagesCallback = std::function<
+ void(const QImage &image, const QImage &midSizeImage, const QImage &smallImage)>;
using AbortCallback = std::function<void(ImageCache::AbortReason)>;
} // namespace ImageCache
diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h
index 2d4378e937..9e6c7ca36a 100644
--- a/src/plugins/qmldesigner/designercore/include/model.h
+++ b/src/plugins/qmldesigner/designercore/include/model.h
@@ -91,6 +91,7 @@ public:
NodeMetaInfo qtQuick3DMaterialMetaInfo() const;
NodeMetaInfo qtQuick3DModelMetaInfo() const;
NodeMetaInfo qtQuick3DNodeMetaInfo() const;
+ NodeMetaInfo qtQuick3DPrincipledMaterialMetaInfo() const;
NodeMetaInfo qtQuick3DTextureMetaInfo() const;
NodeMetaInfo qtQuickConnectionsMetaInfo() const;
NodeMetaInfo qtQuickControlsTextAreaMetaInfo() const;
@@ -145,7 +146,9 @@ public:
bool hasId(const QString &id) const;
bool hasImport(const QString &importUrl) const;
- QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element") const;
+ QString generateNewId(const QString &prefixName,
+ const QString &fallbackPrefix = "element",
+ std::optional<std::function<bool(const QString &)>> isDuplicate = {}) const;
QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const;
void setActive3DSceneId(qint32 sceneId);
diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h
index 4bf86fbf43..a48713ee86 100644
--- a/src/plugins/qmldesigner/designercore/include/modelnode.h
+++ b/src/plugins/qmldesigner/designercore/include/modelnode.h
@@ -223,6 +223,7 @@ public:
static bool isThisOrAncestorLocked(const ModelNode &node);
static ModelNode lowestCommonAncestor(const QList<ModelNode> &nodes);
+ static QList<ModelNode> pruneChildren(const QList<ModelNode> &nodes);
qint32 internalId() const;
diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
index 82d5bf21d4..632dbfce07 100644
--- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
@@ -6,6 +6,7 @@
#include "qmldesignercorelib_global.h"
#include "abstractview.h"
#include "modelcache.h"
+#include "rewritertransaction.h"
#include <modelnode.h>
#include <nodeinstance.h>
@@ -33,7 +34,7 @@ class Target;
}
namespace Utils {
-class QtcProcess;
+class Process;
}
namespace QmlDesigner {
@@ -227,7 +228,7 @@ private: // functions
void updateWatcher(const QString &path);
void handleShaderChanges();
- void handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader);
+ void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader);
void updateQsbPathToFilterMap();
void updateRotationBlocks();
void maybeResetOnPropertyChange(const PropertyName &name, const ModelNode &node,
diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
index 35f2c25f46..69ac5f2a97 100644
--- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
+++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
@@ -118,6 +118,7 @@ public:
bool isQtMultimediaSoundEffect() const;
bool isQtObject() const;
bool isQtQuick3D() const;
+ bool isQtQuick3DBakedLightmap() const;
bool isQtQuick3DBuffer() const;
bool isQtQuick3DCamera() const;
bool isQtQuick3DCommand() const;
diff --git a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h
index ce9b766fa9..6cda405f61 100644
--- a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h
+++ b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h
@@ -51,6 +51,12 @@ public:
bool isPointer() const;
QVariant castedValue(const QVariant &value) const;
+ friend bool operator==(const PropertyMetaInfo &first, const PropertyMetaInfo &second)
+ {
+ return first.m_nodeMetaInfoPrivateData == second.m_nodeMetaInfoPrivateData
+ && first.name() == second.name();
+ }
+
private:
const Storage::Info::PropertyDeclaration &propertyData() const;
TypeName propertyTypeName() const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
index c4e0c55a9e..1eea5063c9 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
@@ -29,6 +29,7 @@ public:
QmlObjectNode(const ModelNode &modelNode)
: QmlModelNodeFacade(modelNode)
{}
+ virtual ~QmlObjectNode() = default;
static bool isValidQmlObjectNode(const ModelNode &modelNode);
bool isValid() const override;
diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h
index 85ef99c6af..9387fdb345 100644
--- a/src/plugins/qmldesigner/designercore/include/rewriterview.h
+++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h
@@ -4,9 +4,9 @@
#pragma once
#include "qmldesignercorelib_global.h"
-#include "exception.h"
#include "abstractview.h"
#include "documentmessage.h"
+#include "rewritertransaction.h"
#include <QScopedPointer>
#include <QTimer>
@@ -170,6 +170,8 @@ public:
bool possibleImportsEnabled() const;
void setPossibleImportsEnabled(bool b);
+ void forceAmend();
+
signals:
void modelInterfaceProjectUpdated();
diff --git a/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h
index db91faa843..a2d7a13410 100644
--- a/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h
+++ b/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h
@@ -39,6 +39,9 @@ public:
QImage image(Utils::PathString filePath,
Utils::SmallString extraId = {},
const ImageCache::AuxiliaryData &auxiliaryData = {});
+ QImage midSizeImage(Utils::PathString filePath,
+ Utils::SmallString extraId = {},
+ const ImageCache::AuxiliaryData &auxiliaryData = {});
QImage smallImage(Utils::PathString filePath,
Utils::SmallString extraId = {},
const ImageCache::AuxiliaryData &auxiliaryData = {});
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
index 5f9cd51670..2893d0e131 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
@@ -46,12 +46,13 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
+#include <nanotrace/nanotrace.h>
#include <projectexplorer/kit.h>
#include <utils/hostosinfo.h>
+#include <utils/qtcassert.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
-#include <nanotrace/nanotrace.h>
#include <QCoreApplication>
#include <QDir>
@@ -132,6 +133,7 @@ void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
} else if (command.userType() == SyncNanotraceCommandType) {
// ignore.
} else {
+ QTC_ASSERT(false, );
Q_ASSERT(false);
}
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 7aaed70c9d..8ebfaf5204 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -75,8 +75,8 @@
#include <qmlprojectmanager/qmlproject.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/theme/theme.h>
#include <utils/threadutils.h>
@@ -870,7 +870,7 @@ NodeInstance NodeInstanceView::activeStateInstance() const
void NodeInstanceView::updateChildren(const NodeAbstractProperty &newPropertyParent)
{
- const QVector<ModelNode> childNodeVector = newPropertyParent.directSubNodes().toVector();
+ const QList<ModelNode> childNodeVector = newPropertyParent.directSubNodes();
qint32 parentInstanceId = newPropertyParent.parentModelNode().internalId();
@@ -1506,7 +1506,7 @@ void NodeInstanceView::pixmapChanged(const PixmapChangedCommand &command)
m_nodeInstanceServer->benchmark(Q_FUNC_INFO + QString::number(renderImageChangeSet.count()));
if (!renderImageChangeSet.isEmpty())
- emitInstancesRenderImageChanged(Utils::toList(renderImageChangeSet).toVector());
+ emitInstancesRenderImageChanged(Utils::toList(renderImageChangeSet));
if (!containerVector.isEmpty()) {
QMultiHash<ModelNode, InformationName> informationChangeHash = informationChanged(
@@ -2066,7 +2066,7 @@ void NodeInstanceView::updateWatcher(const QString &path)
m_generateQsbFilesTimer.start();
}
-void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader)
+void NodeInstanceView::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader)
{
--m_remainingQsbTargets;
@@ -2167,8 +2167,8 @@ void NodeInstanceView::handleShaderChanges()
QStringList args = baseArgs;
args.append(outPath.toString());
args.append(shader);
- auto qsbProcess = new Utils::QtcProcess(this);
- connect(qsbProcess, &Utils::QtcProcess::done, this, [this, qsbProcess, shader] {
+ auto qsbProcess = new Utils::Process(this);
+ connect(qsbProcess, &Utils::Process::done, this, [this, qsbProcess, shader] {
handleQsbProcessExit(qsbProcess, shader);
});
qsbProcess->setWorkingDirectory(srcPath);
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp b/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp
deleted file mode 100644
index 53489fa5b8..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "puppetbuildprogressdialog.h"
-#include "ui_puppetbuildprogressdialog.h"
-
-#include <QtDebug>
-#include <QApplication>
-#include <coreplugin/icore.h>
-
-namespace QmlDesigner {
-
-PuppetBuildProgressDialog::PuppetBuildProgressDialog() :
- QDialog(Core::ICore::dialogParent()),
- ui(new Ui::PuppetBuildProgressDialog),
- m_lineCount(0),
- m_useFallbackPuppet(false)
-{
- setWindowFlags(Qt::SplashScreen);
- setWindowModality(Qt::ApplicationModal);
- ui->setupUi(this);
- ui->buildProgressBar->setMaximum(85);
- connect(ui->useFallbackPuppetPushButton, &QAbstractButton::clicked, this, &PuppetBuildProgressDialog::setUseFallbackPuppet);
-}
-
-PuppetBuildProgressDialog::~PuppetBuildProgressDialog()
-{
- delete ui;
-}
-
-void PuppetBuildProgressDialog::setProgress(int progress)
-{
- ui->buildProgressBar->setValue(progress);
-}
-
-bool PuppetBuildProgressDialog::useFallbackPuppet() const
-{
- return m_useFallbackPuppet;
-}
-
-void PuppetBuildProgressDialog::setErrorOutputFile(const QString &filePath)
-{
- ui->openErrorOutputFileLabel->setText(QString::fromLatin1("<a href='file:///%1'>%2</a>").arg(
- filePath, ui->openErrorOutputFileLabel->text()));
-}
-
-void PuppetBuildProgressDialog::setErrorMessage(const QString &message)
-{
- ui->label->setText(QString::fromLatin1("<font color='red'>%1</font>").arg(message));
- ui->useFallbackPuppetPushButton->setText(tr("OK"));
- connect(ui->useFallbackPuppetPushButton, &QAbstractButton::clicked, this, &QDialog::accept);
-}
-
-void PuppetBuildProgressDialog::setUseFallbackPuppet()
-{
- m_useFallbackPuppet = true;
-}
-
-void PuppetBuildProgressDialog::newBuildOutput(const QByteArray &standardOutput)
-{
- m_lineCount += standardOutput.count('\n');
- setProgress(m_lineCount);
-}
-
-}
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h b/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h
deleted file mode 100644
index c7db04eaec..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QDialog>
-
-namespace QmlDesigner {
-
-namespace Ui {
-class PuppetBuildProgressDialog;
-}
-
-
-class PuppetBuildProgressDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit PuppetBuildProgressDialog();
- ~PuppetBuildProgressDialog() override;
-
- void setProgress(int progress);
- void newBuildOutput(const QByteArray &standardOutput);
- bool useFallbackPuppet() const;
- void setErrorOutputFile(const QString &filePath);
- void setErrorMessage(const QString &message);
-
-private:
- void setUseFallbackPuppet();
-
-private:
- Ui::PuppetBuildProgressDialog *ui;
- int m_lineCount;
- bool m_useFallbackPuppet;
-};
-
-}
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui b/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui
deleted file mode 100644
index 626eb6b557..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetbuildprogressdialog.ui
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QmlDesigner::PuppetBuildProgressDialog</class>
- <widget class="QDialog" name="QmlDesigner::PuppetBuildProgressDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>695</width>
- <height>99</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Build Progress</string>
- </property>
- <property name="modal">
- <bool>true</bool>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Building Adapter for the current Qt. Happens only once for every Qt installation.</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QProgressBar" name="buildProgressBar">
- <property name="value">
- <number>0</number>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QLabel" name="openErrorOutputFileLabel">
- <property name="text">
- <string>Open error output file</string>
- </property>
- <property name="openExternalLinks">
- <bool>true</bool>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="useFallbackPuppetPushButton">
- <property name="text">
- <string>Use Fallback QML Emulation Layer</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp b/src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp
deleted file mode 100644
index 935b502168..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetdialog.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "puppetdialog.h"
-#include "ui_puppetdialog.h"
-
-namespace QmlDesigner {
-
-PuppetDialog::PuppetDialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::PuppetDialog)
-{
- ui->setupUi(this);
-}
-
-PuppetDialog::~PuppetDialog()
-{
- delete ui;
-}
-
-void PuppetDialog::setDescription(const QString &description)
-{
- ui->descriptionLabel->setText(description);
-}
-
-void PuppetDialog::setCopyAndPasteCode(const QString &text)
-{
- ui->copyAndPasteTextEdit->setText(text);
-}
-
-void PuppetDialog::warning(QWidget *parent, const QString &title, const QString &description, const QString &copyAndPasteCode)
-{
- PuppetDialog dialog(parent);
-
- dialog.setWindowTitle(title);
- dialog.setDescription(description);
- dialog.setCopyAndPasteCode(copyAndPasteCode);
-
- dialog.exec();
-}
-
-} //QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetdialog.h b/src/plugins/qmldesigner/designercore/instances/puppetdialog.h
deleted file mode 100644
index 32d0b8deb4..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetdialog.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QDialog>
-
-namespace QmlDesigner {
-
-namespace Ui {
-class PuppetDialog;
-}
-
-
-class PuppetDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit PuppetDialog(QWidget *parent = nullptr);
- ~PuppetDialog() override;
-
- void setDescription(const QString &description);
- void setCopyAndPasteCode(const QString &text);
-
- static void warning(QWidget *parent,
- const QString &title,
- const QString &description,
- const QString &copyAndPasteCode);
-
-private:
- Ui::PuppetDialog *ui;
-};
-
-} //QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetdialog.ui b/src/plugins/qmldesigner/designercore/instances/puppetdialog.ui
deleted file mode 100644
index b7efa8c26c..0000000000
--- a/src/plugins/qmldesigner/designercore/instances/puppetdialog.ui
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>QmlDesigner::PuppetDialog</class>
- <widget class="QDialog" name="QmlDesigner::PuppetDialog">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1148</width>
- <height>344</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Dialog</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>12</number>
- </property>
- <item>
- <widget class="QLabel" name="descriptionLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QTextEdit" name="copyAndPasteTextEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>1</verstretch>
- </sizepolicy>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>QmlDesigner::PuppetDialog</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>QmlDesigner::PuppetDialog</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- </connections>
-</ui>
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
index d15ef9227c..f4b505c4e7 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
@@ -2245,7 +2245,17 @@ bool NodeMetaInfo::isQtQuick3DCamera() const
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Camera>(m_projectStorage, m_typeId);
} else {
- return isValid() && isSubclassOf("QQuick3D.Camera");
+ return isValid() && isSubclassOf("QtQuick3D.Camera");
+ }
+}
+
+bool NodeMetaInfo::isQtQuick3DBakedLightmap() const
+{
+ if constexpr (useProjectStorage()) {
+ using namespace Storage::Info;
+ return isBasedOnCommonType<QtQuick3D, BakedLightmap>(m_projectStorage, m_typeId);
+ } else {
+ return isValid() && isSubclassOf("QtQuick3D.BakedLightmap");
}
}
@@ -2255,7 +2265,7 @@ bool NodeMetaInfo::isQtQuick3DBuffer() const
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, Buffer>(m_projectStorage, m_typeId);
} else {
- return isValid() && isSubclassOf("QQuick3D.Buffer");
+ return isValid() && isSubclassOf("QtQuick3D.Buffer");
}
}
@@ -2265,7 +2275,7 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, InstanceListEntry>(m_projectStorage, m_typeId);
} else {
- return isValid() && isSubclassOf("QQuick3D.InstanceListEntry");
+ return isValid() && isSubclassOf("QtQuick3D.InstanceListEntry");
}
}
@@ -2275,7 +2285,7 @@ bool NodeMetaInfo::isQtQuick3DInstanceList() const
using namespace Storage::Info;
return isBasedOnCommonType<QtQuick3D, InstanceList>(m_projectStorage, m_typeId);
} else {
- return isValid() && isSubclassOf("QQuick3D.InstanceList");
+ return isValid() && isSubclassOf("QtQuick3D.InstanceList");
}
}
@@ -2564,7 +2574,7 @@ bool NodeMetaInfo::isBool() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
return type == "bool" || type == "boolean";
}
@@ -2579,7 +2589,7 @@ bool NodeMetaInfo::isInteger() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
return type == "int" || type == "integer";
}
@@ -2597,7 +2607,7 @@ bool NodeMetaInfo::isFloat() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
return type == "qreal" || type == "double" || type == "float";
}
@@ -2609,7 +2619,7 @@ bool NodeMetaInfo::isVariant() const
using namespace Storage::Info;
return isTypeId(m_typeId, m_projectStorage->builtinTypeId<QVariant>());
} else {
- return isValid() && m_privateData->qualfiedTypeName() == "QVariant";
+ return isValid() && simplifiedTypeName() == "QVariant";
}
}
@@ -2622,7 +2632,7 @@ bool NodeMetaInfo::isString() const
if (!isValid())
return false;
- auto type = m_privateData->qualfiedTypeName();
+ auto type = simplifiedTypeName();
return type == "string" || type == "QString";
}
diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
index 21112df36a..b9f0315c19 100644
--- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
@@ -4,24 +4,23 @@
#include "abstractview.h"
#include "auxiliarydataproperties.h"
+#include "bindingproperty.h"
+#include "internalnode_p.h"
#include "model.h"
#include "model_p.h"
-#include "internalnode_p.h"
#include "nodeinstanceview.h"
-#include <qmlstate.h>
-#include <qmltimeline.h>
-#include <nodemetainfo.h>
-#include <qmldesignerconstants.h>
-#include <nodelistproperty.h>
-#include <variantproperty.h>
-#include <bindingproperty.h>
-
-#include <coreplugin/helpmanager.h>
+#include "nodelistproperty.h"
+#include "nodemetainfo.h"
+#include "qmlstate.h"
+#include "qmltimeline.h"
+#include "qmldesignerconstants.h"
+#include "rewritertransaction.h"
+#include "variantproperty.h"
+
#include <utils/qtcassert.h>
#include <utils/algorithm.h>
#include <QWidget>
-#include <QtGui/qimage.h>
namespace QmlDesigner {
@@ -78,35 +77,27 @@ ModelNode AbstractView::createModelNode(const TypeName &typeName,
ModelNode::NodeSourceType nodeSourceType,
const QString &behaviorPropertyName)
{
- return ModelNode(model()->d->createNode(typeName, majorVersion, minorVersion, propertyList, auxPropertyList, nodeSource, nodeSourceType, behaviorPropertyName), model(), this);
+ return ModelNode(model()->d->createNode(typeName, majorVersion, minorVersion, propertyList,
+ auxPropertyList, nodeSource, nodeSourceType,
+ behaviorPropertyName), model(), this);
}
-/*!
- Returns the constant root model node.
-*/
-
+// Returns the constant root model node.
ModelNode AbstractView::rootModelNode() const
{
Q_ASSERT(model());
- return ModelNode(model()->d->rootNode(), model(), const_cast<AbstractView*>(this));
+ return ModelNode(model()->d->rootNode(), model(), const_cast<AbstractView *>(this));
}
-
-/*!
- Returns the root model node.
-*/
-
+// Returns the root model node.
ModelNode AbstractView::rootModelNode()
{
Q_ASSERT(model());
return ModelNode(model()->d->rootNode(), model(), this);
}
-/*!
- Sets the reference to a model to a null pointer.
-
-*/
+// Sets the reference to a model to a null pointer.
void AbstractView::removeModel()
{
m_model.clear();
@@ -117,6 +108,7 @@ WidgetInfo AbstractView::createWidgetInfo(QWidget *widget,
WidgetInfo::PlacementHint placementHint,
int placementPriority,
const QString &tabName,
+ const QString &feedbackDisplayName,
DesignerWidgetFlags widgetFlags)
{
WidgetInfo widgetInfo;
@@ -126,14 +118,13 @@ WidgetInfo AbstractView::createWidgetInfo(QWidget *widget,
widgetInfo.placementHint = placementHint;
widgetInfo.placementPriority = placementPriority;
widgetInfo.tabName = tabName;
+ widgetInfo.feedbackDisplayName = feedbackDisplayName;
widgetInfo.widgetFlags = widgetFlags;
return widgetInfo;
}
-/*!
- Returns the model of the view.
-*/
+// Returns the model of the view.
Model* AbstractView::model() const
{
return m_model.data();
@@ -185,7 +176,6 @@ void AbstractView::modelAboutToBeDetached(Model *)
\value EmptyPropertiesRemoved
Empty properties were removed.
*/
-
void AbstractView::instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &/*propertyList*/)
{
}
@@ -259,72 +249,60 @@ void AbstractView::nodeOrderChanged(const NodeListProperty &listProperty, const
nodeOrderChanged(listProperty);
}
-/*!
-\fn void AbstractView::nodeAboutToBeRemoved(const ModelNode &removedNode)
-Called when the node specified by \a removedNode will be removed.
-*/
void AbstractView::nodeAboutToBeRemoved(const ModelNode &/*removedNode*/)
{
}
-void AbstractView::nodeRemoved(const ModelNode &/*removedNode*/, const NodeAbstractProperty &/*parentProperty*/, PropertyChangeFlags /*propertyChange*/)
+void AbstractView::nodeRemoved(const ModelNode &/*removedNode*/, const NodeAbstractProperty &/*parentProperty*/,
+ PropertyChangeFlags /*propertyChange*/)
{
}
-void AbstractView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/)
+void AbstractView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &/*propertyList*/)
{
}
-/*!
-Called when the properties specified by \a propertyList are removed.
-*/
-void AbstractView::propertiesRemoved(const QList<AbstractProperty>& /*propertyList*/)
+void AbstractView::propertiesRemoved(const QList<AbstractProperty> &/*propertyList*/)
{
}
-/*!
-\fn void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange)
-Called when the parent of \a node will be changed from \a oldPropertyParent to
-\a newPropertyParent.
-*/
-
-/*!
-\fn void QmlDesigner::AbstractView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
- const QList<ModelNode> &lastSelectedNodeList)
-Called when the selection is changed from \a lastSelectedNodeList to
-\a selectedNodeList.
-*/
void AbstractView::selectedNodesChanged(const QList<ModelNode> &/*selectedNodeList*/, const QList<ModelNode> &/*lastSelectedNodeList*/)
{
}
-void AbstractView::nodeAboutToBeReparented(const ModelNode &/*node*/, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
+void AbstractView::nodeAboutToBeReparented(const ModelNode &/*node*/, const NodeAbstractProperty &/*newPropertyParent*/,
+ const NodeAbstractProperty &/*oldPropertyParent*/, PropertyChangeFlags /*propertyChange*/)
{
}
-void AbstractView::nodeReparented(const ModelNode &/*node*/, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
+void AbstractView::nodeReparented(const ModelNode &/*node*/, const NodeAbstractProperty &/*newPropertyParent*/,
+ const NodeAbstractProperty &/*oldPropertyParent*/, PropertyChangeFlags /*propertyChange*/)
{
}
-void AbstractView::nodeIdChanged(const ModelNode& /*node*/, const QString& /*newId*/, const QString& /*oldId*/)
+void AbstractView::nodeIdChanged(const ModelNode &/*node*/, const QString &/*newId*/, const QString &/*oldId*/)
{
}
-void AbstractView::variantPropertiesChanged(const QList<VariantProperty>& /*propertyList*/, PropertyChangeFlags /*propertyChange*/)
+void AbstractView::variantPropertiesChanged(const QList<VariantProperty>& /*propertyList*/,
+ PropertyChangeFlags /*propertyChange*/)
{
}
void AbstractView::bindingPropertiesAboutToBeChanged(const QList<BindingProperty> &) {}
-void AbstractView::bindingPropertiesChanged(const QList<BindingProperty>& /*propertyList*/, PropertyChangeFlags /*propertyChange*/)
+void AbstractView::bindingPropertiesChanged(const QList<BindingProperty>& /*propertyList*/,
+ PropertyChangeFlags /*propertyChange*/)
{
}
-void AbstractView::signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty>& /*propertyList*/, PropertyChangeFlags /*propertyChange*/)
+void AbstractView::signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &/*propertyList*/,
+ PropertyChangeFlags /*propertyChange*/)
{
}
-void AbstractView::signalDeclarationPropertiesChanged(const QVector<SignalDeclarationProperty>& /*propertyList*/, PropertyChangeFlags /*propertyChange*/)
+void AbstractView::signalDeclarationPropertiesChanged(const QVector<SignalDeclarationProperty> &/*propertyList*/,
+ PropertyChangeFlags /*propertyChange*/)
{
}
@@ -349,13 +327,14 @@ void AbstractView::usedImportsChanged(const QList<Import> &/*usedImports*/)
{
}
-void AbstractView::auxiliaryDataChanged(const ModelNode & /*node*/,
+void AbstractView::auxiliaryDataChanged(const ModelNode &/*node*/,
AuxiliaryDataKeyView /*key*/,
- const QVariant & /*data*/)
+ const QVariant &/*data*/)
{
}
-void AbstractView::customNotification(const AbstractView * /*view*/, const QString & /*identifier*/, const QList<ModelNode> & /*nodeList*/, const QList<QVariant> & /*data*/)
+void AbstractView::customNotification(const AbstractView * /*view*/, const QString & /*identifier*/,
+ const QList<ModelNode> & /*nodeList*/, const QList<QVariant> & /*data*/)
{
}
@@ -367,27 +346,27 @@ void AbstractView::documentMessagesChanged(const QList<DocumentMessage> &/*error
{
}
-void AbstractView::currentTimelineChanged(const ModelNode & /*node*/)
+void AbstractView::currentTimelineChanged(const ModelNode &/*node*/)
{
}
-void AbstractView::renderImage3DChanged(const QImage & /*image*/)
+void AbstractView::renderImage3DChanged(const QImage &/*image*/)
{
}
-void AbstractView::updateActiveScene3D(const QVariantMap & /*sceneState*/)
+void AbstractView::updateActiveScene3D(const QVariantMap &/*sceneState*/)
{
}
-void AbstractView::updateImport3DSupport(const QVariantMap & /*supportMap*/)
+void AbstractView::updateImport3DSupport(const QVariantMap &/*supportMap*/)
{
}
// a Quick3DNode that is picked at the requested view position in the 3D Editor and the 3D scene
// position of the requested view position.
-void AbstractView::nodeAtPosReady(const ModelNode & /*modelNode*/, const QVector3D &/*pos3d*/) {}
+void AbstractView::nodeAtPosReady(const ModelNode &/*modelNode*/, const QVector3D &/*pos3d*/) {}
-void AbstractView::modelNodePreviewPixmapChanged(const ModelNode & /*node*/, const QPixmap & /*pixmap*/)
+void AbstractView::modelNodePreviewPixmapChanged(const ModelNode &/*node*/, const QPixmap &/*pixmap*/)
{
}
@@ -446,9 +425,6 @@ void AbstractView::setSelectedModelNode(const ModelNode &modelNode)
setSelectedModelNodes({modelNode});
}
-/*!
- Clears the selection.
-*/
void AbstractView::clearSelectedModelNodes()
{
model()->d->clearSelectedNodes();
@@ -469,10 +445,6 @@ bool AbstractView::isSelectedModelNode(const ModelNode &modelNode) const
return model()->d->selectedNodes().contains(modelNode.internalNode());
}
-/*!
- Sets the list of nodes to the actual selected nodes. Returns a list of the
- selected nodes.
-*/
QList<ModelNode> AbstractView::selectedModelNodes() const
{
return toModelNodeList(model()->d->selectedNodes());
@@ -494,18 +466,12 @@ ModelNode AbstractView::singleSelectedModelNode() const
return ModelNode();
}
-/*!
- Adds \a node to the selection list.
-*/
void AbstractView::selectModelNode(const ModelNode &modelNode)
{
QTC_ASSERT(modelNode.isInHierarchy(), return);
model()->d->selectNode(modelNode.internalNode());
}
-/*!
- Removes \a node from the selection list.
-*/
void AbstractView::deselectModelNode(const ModelNode &node)
{
model()->d->deselectNode(node.internalNode());
@@ -535,22 +501,23 @@ const NodeInstanceView *AbstractView::nodeInstanceView() const
{
if (model())
return model()->d->nodeInstanceView();
- else
- return nullptr;
+
+ return nullptr;
}
RewriterView *AbstractView::rewriterView() const
{
if (model())
return model()->d->rewriterView();
- else
- return nullptr;
+
+ return nullptr;
}
void AbstractView::resetView()
{
if (!model())
return;
+
Model *currentModel = model();
currentModel->detachView(this);
@@ -628,7 +595,7 @@ void AbstractView::deactivateTimelineRecording()
model()->d->notifyCurrentTimelineChanged(ModelNode());
}
-bool AbstractView::executeInTransaction(const QByteArray &identifier, const AbstractView::OperationBlock &lambda)
+bool AbstractView::executeInTransaction(const QByteArray &identifier, const OperationBlock &lambda)
{
try {
RewriterTransaction transaction = beginRewriterTransaction(identifier);
@@ -851,6 +818,18 @@ ModelNode AbstractView::materialLibraryNode()
return modelNodeForId(Constants::MATERIAL_LIB_ID);
}
+bool AbstractView::isPartOfMaterialLibrary(const ModelNode &node)
+{
+ if (!node.isValid())
+ return false;
+
+ ModelNode matLib = materialLibraryNode();
+
+ return matLib.isValid()
+ && (node == matLib
+ || (node.hasParentProperty() && node.parentProperty().parentModelNode() == matLib));
+}
+
ModelNode AbstractView::active3DSceneNode()
{
auto activeSceneAux = rootModelNode().auxiliaryData(active3dSceneProperty);
@@ -864,59 +843,6 @@ ModelNode AbstractView::active3DSceneNode()
return {};
}
-// Assigns given material to a 3D model.
-// The assigned material is also inserted into material library if not already there.
-// If given material is not valid, first existing material from material library is used,
-// or if material library is empty, a new material is created.
-// This function should be called only from inside a transaction, as it potentially does many
-// changes to model.
-void AbstractView::assignMaterialTo3dModel(const ModelNode &modelNode, const ModelNode &materialNode)
-{
- QTC_ASSERT(modelNode.isValid() && modelNode.metaInfo().isQtQuick3DModel(), return );
-
- ModelNode matLib = materialLibraryNode();
-
- if (!matLib.isValid())
- return;
-
- ModelNode newMaterialNode;
-
- if (materialNode.isValid() && materialNode.metaInfo().isQtQuick3DMaterial()) {
- newMaterialNode = materialNode;
- } else {
- const QList<ModelNode> materials = matLib.directSubModelNodes();
- if (materials.size() > 0) {
- for (const ModelNode &mat : materials) {
- if (mat.metaInfo().isQtQuick3DMaterial()) {
- newMaterialNode = mat;
- break;
- }
- }
- }
-
- // if no valid material, create a new default material
- if (!newMaterialNode.isValid()) {
- NodeMetaInfo metaInfo = model()->qtQuick3DDefaultMaterialMetaInfo();
- newMaterialNode = createModelNode("QtQuick3D.DefaultMaterial", metaInfo.majorVersion(),
- metaInfo.minorVersion());
- newMaterialNode.validId();
- }
- }
-
- QTC_ASSERT(newMaterialNode.isValid(), return);
-
- VariantProperty matNameProp = newMaterialNode.variantProperty("objectName");
- if (matNameProp.value().isNull())
- matNameProp.setValue("New Material");
-
- if (!newMaterialNode.hasParentProperty()
- || newMaterialNode.parentProperty() != matLib.defaultNodeListProperty()) {
- matLib.defaultNodeListProperty().reparentHere(newMaterialNode);
- }
- BindingProperty modelMatsProp = modelNode.bindingProperty("materials");
- modelMatsProp.setExpression(newMaterialNode.id());
-}
-
ModelNode AbstractView::getTextureDefaultInstance(const QString &source)
{
ModelNode matLib = materialLibraryNode();
@@ -946,7 +872,7 @@ ModelNode AbstractView::currentStateNode() const
if (model())
return ModelNode(m_model.data()->d->currentStateNode(), m_model.data(), const_cast<AbstractView*>(this));
- return ModelNode();
+ return {};
}
QmlModelState AbstractView::currentState() const
@@ -956,12 +882,13 @@ QmlModelState AbstractView::currentState() const
QmlTimeline AbstractView::currentTimeline() const
{
- if (model())
+ if (model()) {
return QmlTimeline(ModelNode(m_model.data()->d->currentTimelineNode(),
- m_model.data(),
- const_cast<AbstractView*>(this)));
+ m_model.data(),
+ const_cast<AbstractView *>(this)));
+ }
- return QmlTimeline();
+ return {};
}
static int getMinorVersionFromImport(const Model *model)
@@ -1001,13 +928,14 @@ static int getMajorVersionFromNode(const ModelNode &modelNode)
if (modelNode.metaInfo().isValid()) {
for (const NodeMetaInfo &info : modelNode.metaInfo().classHierarchy()) {
if (info.typeName() == "QtQml.QtObject"
- || info.typeName() == "QtQuick.QtObject"
- || info.typeName() == "QtQuick.Item")
+ || info.typeName() == "QtQuick.QtObject"
+ || info.typeName() == "QtQuick.Item") {
return info.majorVersion();
+ }
}
}
- return 1; //default
+ return 1; // default
}
static int getMinorVersionFromNode(const ModelNode &modelNode)
@@ -1020,7 +948,7 @@ static int getMinorVersionFromNode(const ModelNode &modelNode)
}
}
- return 1; //default
+ return 1; // default
}
int AbstractView::majorQtQuickVersion() const
@@ -1041,5 +969,4 @@ int AbstractView::minorQtQuickVersion() const
return getMinorVersionFromNode(rootModelNode());
}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
index f07b74f869..69c74ec6cf 100644
--- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
+++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "basetexteditmodifier.h"
+#include "qmljseditor/qmljseditor.h"
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/parser/qmljsast_p.h>
@@ -111,7 +112,10 @@ bool BaseTextEditModifier::moveToComponent(int nodeOffset)
if (!object)
return false;
- QmlJSEditor::performComponentFromObjectDef(document->filePath().toString(), object);
+ QmlJSEditor::performComponentFromObjectDef(qobject_cast<QmlJSEditor::QmlJSEditorWidget *>(
+ m_textEdit),
+ document->filePath().toString(),
+ object);
return true;
}
}
diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp
index 4fb8a7a75a..a234c11e1f 100644
--- a/src/plugins/qmldesigner/designercore/model/model.cpp
+++ b/src/plugins/qmldesigner/designercore/model/model.cpp
@@ -1516,7 +1516,9 @@ static QString firstCharToLower(const QString &string)
return resultString;
}
-QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const
+QString Model::generateNewId(const QString &prefixName,
+ const QString &fallbackPrefix,
+ std::optional<std::function<bool(const QString &)>> 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.
@@ -1538,7 +1540,10 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP
QString newId = newBaseId;
- while (!ModelNode::isValidId(newId) || hasId(newId)
+ if (!isDuplicate.has_value())
+ isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1);
+
+ while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId)
|| d->rootNode()->hasProperty(newId.toUtf8())) {
++counter;
newId = QString(QStringLiteral("%1%2")).arg(firstCharToLower(newBaseId)).arg(counter);
@@ -1954,6 +1959,16 @@ NodeMetaInfo Model::qtQuick3DDefaultMaterialMetaInfo() const
}
}
+NodeMetaInfo Model::qtQuick3DPrincipledMaterialMetaInfo() const
+{
+ if constexpr (useProjectStorage()) {
+ using namespace Storage::Info;
+ return createNodeMetaInfo<QtQuick3D, PrincipledMaterial>();
+ } else {
+ return metaInfo("QtQuick3D.PrincipledMaterial");
+ }
+}
+
NodeMetaInfo Model::qtQuickTimelineTimelineMetaInfo() const
{
if constexpr (useProjectStorage()) {
diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
index af612ca3f4..aab0d13c24 100644
--- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
@@ -19,6 +19,7 @@
#include <rewriterview.h>
#include <utils/algorithm.h>
+#include <utils/ranges.h>
#include <QHash>
#include <QRegularExpression>
@@ -1402,6 +1403,31 @@ ModelNode ModelNode::lowestCommonAncestor(const QList<ModelNode> &nodes)
return accumulatedNode;
}
+QList<ModelNode> ModelNode::pruneChildren(const QList<ModelNode> &nodes)
+{
+ QList<ModelNode> forwardNodes;
+ QList<ModelNode> backNodes;
+
+ auto pushIfIsNotChild = [] (QList<ModelNode> &container, const ModelNode &node) {
+ bool hasAncestor = Utils::anyOf(container,
+ [node] (const ModelNode &testNode) -> bool {
+ return testNode.isAncestorOf(node);
+ });
+ if (!hasAncestor)
+ container.append(node);
+ };
+
+ for (const ModelNode &node : nodes | Utils::views::reverse) {
+ if (node)
+ pushIfIsNotChild(forwardNodes, node);
+ }
+
+ for (const ModelNode &node : forwardNodes | Utils::views::reverse)
+ pushIfIsNotChild(backNodes, node);
+
+ return backNodes;
+}
+
void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
{
model()->d->setScriptFunctions(m_internalNode, scriptFunctionList);
diff --git a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp
index 83310b963a..5029fdc79d 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp
@@ -3,6 +3,7 @@
#include "qmlchangeset.h"
#include "bindingproperty.h"
+#include "rewritertransaction.h"
#include "variantproperty.h"
#include "abstractview.h"
#include <metainfo.h>
diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
index 54978bc4a8..6d79375b6d 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
@@ -214,6 +214,9 @@ void QmlItemNode::placeEffectNode(NodeAbstractProperty &parentProperty, const Qm
} else {
parentProperty.parentModelNode().variantProperty("layer.enabled").setValue(true);
}
+
+ if (effectNode.modelNode().metaInfo().hasProperty("timeRunning"))
+ effectNode.modelNode().variantProperty("timeRunning").setValue(true);
}
bool QmlItemNode::isValid() const
diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
index 33f91a137b..d8088de64b 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
@@ -703,6 +703,12 @@ void RewriterView::setPossibleImportsEnabled(bool b)
m_possibleImportsEnabled = b;
}
+void RewriterView::forceAmend()
+{
+ m_amendTimer.stop();
+ amendQmlText();
+}
+
Internal::ModelNodePositionStorage *RewriterView::positionStorage() const
{
return m_positionStorage.data();
diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
index cb92f7df2c..2a6b9f0fbf 100644
--- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp
@@ -555,6 +555,7 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString,
templateRewriterView->setTextModifier(&textModifierTemplate);
templateModel->attachView(templateRewriterView.data());
templateRewriterView->setCheckSemanticErrors(false);
+ templateRewriterView->setPossibleImportsEnabled(false);
ModelNode templateRootNode = templateRewriterView->rootModelNode();
QTC_ASSERT(templateRootNode.isValid(), return );
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index ca321b5687..4469793db8 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -36,11 +36,12 @@
#include <utils/qrcparser.h>
#include <utils/qtcassert.h>
-#include <QSet>
#include <QDir>
+#include <QElapsedTimer>
#include <QLoggingCategory>
#include <QRegularExpression>
-#include <QElapsedTimer>
+#include <QScopeGuard>
+#include <QSet>
#include <memory>
@@ -428,91 +429,43 @@ public:
void leaveScope()
{ m_scopeBuilder.pop(); }
- void lookup(AST::UiQualifiedId *astTypeNode, QString &typeName, int &majorVersion,
- int &minorVersion, QString &defaultPropertyName)
+ NodeMetaInfo lookup(AST::UiQualifiedId *astTypeNode)
{
- const ObjectValue *value = m_context->lookupType(m_doc.data(), astTypeNode);
- defaultPropertyName = m_context->defaultPropertyName(value);
-
- const CppComponentValue *qmlValue = value_cast<CppComponentValue>(value);
- if (qmlValue) {
- typeName = qmlValue->moduleName() + QStringLiteral(".") + qmlValue->className();
-
- majorVersion = qmlValue->componentVersion().majorVersion();
- minorVersion = qmlValue->componentVersion().minorVersion();
- } else {
- for (AST::UiQualifiedId *iter = astTypeNode; iter; iter = iter->next)
- if (!iter->next && !iter->name.isEmpty())
- typeName = iter->name.toString();
-
- QString fullTypeName;
- for (AST::UiQualifiedId *iter = astTypeNode; iter; iter = iter->next)
- if (!iter->name.isEmpty())
- fullTypeName += iter->name.toString() + QLatin1Char('.');
-
- if (fullTypeName.endsWith(QLatin1Char('.')))
- fullTypeName.chop(1);
-
- majorVersion = ComponentVersion::NoVersion;
- minorVersion = ComponentVersion::NoVersion;
-
- const Imports *imports = m_context->imports(m_doc.data());
- ImportInfo importInfo = imports->info(fullTypeName, m_context.data());
- if (importInfo.isValid() && importInfo.type() == ImportType::Library) {
- QString name = importInfo.name();
- majorVersion = importInfo.version().majorVersion();
- minorVersion = importInfo.version().minorVersion();
- typeName.prepend(name + QLatin1Char('.'));
- } else if (importInfo.isValid() && importInfo.type() == ImportType::Directory) {
- const Utils::FilePath path = Utils::FilePath::fromString(importInfo.path());
- const Utils::FilePath dir = m_doc->path();
- // should probably try to make it relative to some import path, not to the document path
- const Utils::FilePath relativePath = path.relativeChildPath(dir);
- QString name = relativePath.path().replace(QLatin1Char('/'), QLatin1Char('.'));
- if (!name.isEmpty() && name != QLatin1String("."))
- typeName.prepend(name + QLatin1Char('.'));
- } else if (importInfo.isValid() && importInfo.type() == ImportType::QrcDirectory) {
- QString path = Utils::QrcParser::normalizedQrcDirectoryPath(importInfo.path());
- path = path.mid(1, path.size() - ((path.size() > 1) ? 2 : 1));
- const QString name = path.replace(QLatin1Char('/'), QLatin1Char('.'));
- if (!name.isEmpty())
- typeName.prepend(name + QLatin1Char('.'));
- }
- }
-
- {
- TypeName fullTypeName;
- for (AST::UiQualifiedId *iter = astTypeNode; iter; iter = iter->next)
- if (!iter->name.isEmpty())
- fullTypeName += iter->name.toUtf8() + '.';
-
- if (fullTypeName.endsWith('.'))
- fullTypeName.chop(1);
+ TypeName fullTypeName;
+ for (AST::UiQualifiedId *iter = astTypeNode; iter; iter = iter->next)
+ if (!iter->name.isEmpty())
+ fullTypeName += iter->name.toUtf8() + '.';
- NodeMetaInfo metaInfo = m_model->metaInfo(fullTypeName);
+ if (fullTypeName.endsWith('.'))
+ fullTypeName.chop(1);
- bool ok = metaInfo.typeName() == typeName.toUtf8()
- && metaInfo.majorVersion() == majorVersion
- && metaInfo.minorVersion() == minorVersion;
+ NodeMetaInfo metaInfo = m_model->metaInfo(fullTypeName);
+ return metaInfo;
+ }
+ bool lookupProperty(const QString &propertyPrefix,
+ const ModelNode &node,
+ const AST::UiQualifiedId *propertyId)
+ {
+ const QString propertyName = propertyPrefix.isEmpty() ? propertyId->name.toString()
+ : propertyPrefix;
- if (!ok) {
- qDebug() << Q_FUNC_INFO;
- qDebug() << astTypeNode->name.toString() << typeName;
- qDebug() << metaInfo.isValid() << metaInfo.typeName();
- }
+ if (propertyName == QStringLiteral("id") && !propertyId->next)
+ return false; // ### should probably be a special value
- typeName = QString::fromUtf8(metaInfo.typeName());
- majorVersion = metaInfo.majorVersion();
- minorVersion = metaInfo.minorVersion();
- }
+ //compare to lookupProperty(propertyPrefix, propertyId);
+ return node.metaInfo().hasProperty(propertyName.toUtf8());
}
/// When something is changed here, also change Check::checkScopeObjectMember in
/// qmljscheck.cpp
/// ### Maybe put this into the context as a helper function.
- bool lookupProperty(const QString &prefix, const AST::UiQualifiedId *id, const Value **property = nullptr,
- const ObjectValue **parentObject = nullptr, QString *name = nullptr)
+ ///
+ bool lookupProperty(const QString &prefix,
+ const AST::UiQualifiedId *id,
+ const Value **property = nullptr,
+ const ObjectValue **parentObject = nullptr,
+ QString *name = nullptr)
{
QList<const ObjectValue *> scopeObjects = m_scopeChain.qmlScopeObjects();
if (scopeObjects.isEmpty())
@@ -685,7 +638,11 @@ public:
return value;
}
- QVariant convertToEnum(AST::Statement *rhs, const QString &propertyPrefix, AST::UiQualifiedId *propertyId, const QString &astValue)
+ QVariant convertToEnum(AST::Statement *rhs,
+ const NodeMetaInfo &metaInfo,
+ const QString &propertyPrefix,
+ AST::UiQualifiedId *propertyId,
+ const QString &astValue)
{
QStringList astValueList = astValue.split(QStringLiteral("."));
@@ -704,42 +661,12 @@ public:
if (!eStmt || !eStmt->expression)
return QVariant();
- const ObjectValue *containingObject = nullptr;
- QString name;
- if (!lookupProperty(propertyPrefix, propertyId, nullptr, &containingObject, &name))
- return QVariant();
+ const QString propertyName = propertyPrefix.isEmpty() ? propertyId->name.toString()
+ : propertyPrefix;
- if (containingObject)
- containingObject->lookupMember(name, m_context, &containingObject);
- const CppComponentValue * lhsCppComponent = value_cast<CppComponentValue>(containingObject);
- if (!lhsCppComponent)
- return QVariant();
- const QString lhsPropertyTypeName = lhsCppComponent->propertyType(name);
-
- const ObjectValue *rhsValueObject = nullptr;
- QString rhsValueName;
- if (auto idExp = AST::cast<AST::IdentifierExpression *>(eStmt->expression)) {
- if (!m_scopeChain.qmlScopeObjects().isEmpty())
- rhsValueObject = m_scopeChain.qmlScopeObjects().constLast();
- if (!idExp->name.isEmpty())
- rhsValueName = idExp->name.toString();
- } else if (auto memberExp = AST::cast<AST::FieldMemberExpression *>(eStmt->expression)) {
- Evaluate evaluate(&m_scopeChain);
- const Value *result = evaluate(memberExp->base);
- rhsValueObject = result->asObjectValue();
-
- if (!memberExp->name.isEmpty())
- rhsValueName = memberExp->name.toString();
- }
-
- if (rhsValueObject)
- rhsValueObject->lookupMember(rhsValueName, m_context, &rhsValueObject);
-
- const CppComponentValue *rhsCppComponentValue = value_cast<CppComponentValue>(rhsValueObject);
- if (!rhsCppComponentValue)
- return QVariant();
+ const PropertyMetaInfo pInfo = metaInfo.property(propertyName.toUtf8());
- if (rhsCppComponentValue->getEnum(lhsPropertyTypeName).hasKey(rhsValueName))
+ if (pInfo.isEnumType())
return QVariant::fromValue(Enumeration(astValue));
else
return QVariant();
@@ -1092,6 +1019,9 @@ Document::MutablePtr TextToModelMerger::createParsedDocument(const QUrl &url, co
bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceHandler)
{
+ QmlJS::ScopeChain::setSkipmakeComponentChain(true);
+ QScopeGuard unSkip([]() { QmlJS::ScopeChain::setSkipmakeComponentChain(false); });
+
qCInfo(rewriterBenchmark) << Q_FUNC_INFO;
const bool justSanityCheck = !differenceHandler.isAmender();
@@ -1228,23 +1158,21 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
m_rewriterView->positionStorage()->setNodeOffset(modelNode, astObjectType->identifierToken.offset);
- QString typeNameString;
- QString defaultPropertyNameString;
- int majorVersion;
- int minorVersion;
- context->lookup(astObjectType, typeNameString, majorVersion, minorVersion, defaultPropertyNameString);
+ NodeMetaInfo info = context->lookup(astObjectType);
+ if (!info.isValid()) {
+ qWarning() << "Skipping node with unknown type" << toString(astObjectType) << info.typeName();
+ return;
+ }
- TypeName typeName = typeNameString.toUtf8();
- PropertyName defaultPropertyName = defaultPropertyNameString.toUtf8();
+ int majorVersion = info.majorVersion();
+ int minorVersion = info.minorVersion();
+
+ TypeName typeName = info.typeName();
+ PropertyName defaultPropertyName = info.defaultPropertyName();
if (defaultPropertyName.isEmpty()) //fallback and use the meta system of the model
defaultPropertyName = modelNode.metaInfo().defaultPropertyName();
- if (typeName.isEmpty()) {
- qWarning() << "Skipping node with unknown type" << toString(astObjectType);
- return;
- }
-
if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && isComponentType(typeName)) {
for (AST::UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) {
if (auto def = AST::cast<AST::UiObjectDefinition *>(iter->member)) {
@@ -1536,7 +1464,11 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN
}
}
- const QVariant enumValue = context->convertToEnum(script->statement, prefix, script->qualifiedId, astValue);
+ const QVariant enumValue = context->convertToEnum(script->statement,
+ modelNode.metaInfo(),
+ prefix,
+ script->qualifiedId,
+ astValue);
if (enumValue.isValid()) { // It is a qualified enum:
AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8());
syncVariantProperty(modelProperty, enumValue, TypeName(), differenceHandler); // TODO: parse type
@@ -1583,20 +1515,16 @@ void TextToModelMerger::syncNodeProperty(AbstractProperty &modelProperty,
const TypeName &dynamicPropertyType,
DifferenceHandler &differenceHandler)
{
+ NodeMetaInfo info = context->lookup(binding->qualifiedTypeNameId);
- QString typeNameString;
- QString dummy;
- int majorVersion;
- int minorVersion;
- context->lookup(binding->qualifiedTypeNameId, typeNameString, majorVersion, minorVersion, dummy);
-
- TypeName typeName = typeNameString.toUtf8();
-
-
- if (typeName.isEmpty()) {
- qWarning() << "Skipping node with unknown type" << toString(binding->qualifiedTypeNameId);
+ if (!info.isValid()) {
+ qWarning() << "SNP"
+ << "Skipping node with unknown type" << toString(binding->qualifiedTypeNameId);
return;
}
+ TypeName typeName = info.typeName();
+ int majorVersion = info.majorVersion();
+ int minorVersion = info.minorVersion();
if (modelProperty.isNodeProperty() && dynamicPropertyType == modelProperty.dynamicTypeName()) {
ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode();
@@ -2114,18 +2042,14 @@ ModelNode ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProp
if (!astObjectType || !astInitializer)
return ModelNode();
- QString typeNameString;
- QString dummy;
- int majorVersion;
- int minorVersion;
- context->lookup(astObjectType, typeNameString, majorVersion, minorVersion, dummy);
-
- TypeName typeName = typeNameString.toUtf8();
-
- if (typeName.isEmpty()) {
+ NodeMetaInfo info = context->lookup(astObjectType);
+ if (!info.isValid()) {
qWarning() << "Skipping node with unknown type" << toString(astObjectType);
- return ModelNode();
+ return {};
}
+ TypeName typeName = info.typeName();
+ int majorVersion = info.majorVersion();
+ int minorVersion = info.minorVersion();
const bool propertyTakesComponent = propertyIsComponentType(modelProperty, typeName, m_merger->view()->model());
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
index 4878b59783..3cd7f3a016 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h
@@ -23,6 +23,7 @@ namespace QmlDesigner::Storage::Info {
inline constexpr char Affector3D[] = "Affector3D";
inline constexpr char Attractor3D[] = "Attractor3D";
+inline constexpr char BakedLightmap[] = "BakedLightmap";
inline constexpr char BoolType[] = "bool";
inline constexpr char BorderImage[] = "BorderImage";
inline constexpr char Buffer[] = "Buffer";
@@ -184,6 +185,7 @@ class CommonTypeCache
CacheType<QtQuick, vector2d>,
CacheType<QtQuick, vector3d>,
CacheType<QtQuick, vector4d>,
+ CacheType<QtQuick3D, BakedLightmap>,
CacheType<QtQuick3D, Buffer>,
CacheType<QtQuick3D, Camera>,
CacheType<QtQuick3D, Command>,
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
new file mode 100644
index 0000000000..d574ef5475
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "projectstorageexceptions.h"
+
+namespace QmlDesigner {
+
+const char *NoSourcePathForInvalidSourceId::what() const noexcept
+{
+ return "You cannot get a file path for an invalid file path id!";
+}
+
+const char *NoSourceContextPathForInvalidSourceContextId::what() const noexcept
+{
+ return "You cannot get a directory path for an invalid directory path id!";
+}
+
+const char *SourceContextIdDoesNotExists::what() const noexcept
+{
+ return "The source context id does not exist in the database!";
+}
+
+const char *SourceIdDoesNotExists::what() const noexcept
+{
+ return "The source id does not exist in the database!";
+}
+
+const char *TypeHasInvalidSourceId::what() const noexcept
+{
+ return "The source id is invalid!";
+}
+
+const char *ModuleDoesNotExists::what() const noexcept
+{
+ return "The module does not exist!";
+}
+
+const char *ModuleAlreadyExists::what() const noexcept
+{
+ return "The module does already exist!";
+}
+
+const char *ExportedTypeCannotBeInserted::what() const noexcept
+{
+ return "The exported type cannot be inserted!";
+}
+
+const char *TypeNameDoesNotExists::what() const noexcept
+{
+ return "The type name does not exist!";
+}
+
+const char *PropertyNameDoesNotExists::what() const noexcept
+{
+ return "The property name does not exist!";
+}
+
+const char *PrototypeChainCycle::what() const noexcept
+{
+ return "There is a prototype chain cycle!";
+}
+
+const char *AliasChainCycle::what() const noexcept
+{
+ return "There is a prototype chain cycle!";
+}
+
+const char *CannotParseQmlTypesFile::what() const noexcept
+{
+ return "Cannot parse qml types file!";
+}
+
+const char *CannotParseQmlDocumentFile::what() const noexcept
+{
+ return "Cannot parse qml types file!";
+}
+
+const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept
+{
+ return "The project source id is invalid!";
+}
+
+const char *ProjectDataHasInvalidSourceId::what() const noexcept
+{
+ return "The source id is invalid!";
+}
+
+const char *ProjectDataHasInvalidModuleId::what() const noexcept
+{
+ return "The module id is invalid!";
+}
+
+const char *FileStatusHasInvalidSourceId::what() const noexcept
+{
+ return "The source id in file status is invalid!";
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
index bf540fb64d..d78283ca18 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h
@@ -3,131 +3,118 @@
#pragma once
+#include "../include/qmldesignercorelib_global.h"
+
#include <exception>
namespace QmlDesigner {
-class NoSourcePathForInvalidSourceId : std::exception
+class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : std::exception
{
public:
- const char *what() const noexcept override
- {
- return "You cannot get a file path for an invalid file path id!";
- }
+ const char *what() const noexcept override;
};
-class NoSourceContextPathForInvalidSourceContextId : std::exception
+class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : std::exception
{
public:
- const char *what() const noexcept override
- {
- return "You cannot get a directory path for an invalid directory path id!";
- }
+ const char *what() const noexcept override;
};
-class SourceContextIdDoesNotExists : std::exception
+class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : std::exception
{
public:
- const char *what() const noexcept override
- {
- return "The source context id does not exist in the database!";
- }
+ const char *what() const noexcept override;
};
-class SourceIdDoesNotExists : std::exception
+class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : std::exception
{
public:
- const char *what() const noexcept override
- {
- return "The source id does not exist in the database!";
- }
+ const char *what() const noexcept override;
};
-class TypeHasInvalidSourceId : std::exception
+class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : std::exception
{
public:
- const char *what() const noexcept override { return "The source id is invalid!"; }
+ const char *what() const noexcept override;
};
-class ModuleDoesNotExists : std::exception
+class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : std::exception
{
public:
- const char *what() const noexcept override { return "The module does not exist!"; }
+ const char *what() const noexcept override;
};
-class ModuleAlreadyExists : std::exception
+class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : std::exception
{
public:
- const char *what() const noexcept override { return "The module does already exist!"; }
+ const char *what() const noexcept override;
};
-class ExportedTypeCannotBeInserted : std::exception
+class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : std::exception
{
public:
- const char *what() const noexcept override { return "The exported type cannot be inserted!"; }
+ const char *what() const noexcept override;
};
-class TypeNameDoesNotExists : std::exception
+class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : std::exception
{
public:
- const char *what() const noexcept override { return "The type name does not exist!"; }
+ const char *what() const noexcept override;
};
-class PropertyNameDoesNotExists : std::exception
+class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : std::exception
{
public:
- const char *what() const noexcept override { return "The property name does not exist!"; }
+ const char *what() const noexcept override;
};
-class PrototypeChainCycle : std::exception
+class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : std::exception
{
public:
- const char *what() const noexcept override { return "There is a prototype chain cycle!"; }
+ const char *what() const noexcept override;
};
-class AliasChainCycle : std::exception
+class QMLDESIGNERCORE_EXPORT AliasChainCycle : std::exception
{
public:
- const char *what() const noexcept override { return "There is a prototype chain cycle!"; }
+ const char *what() const noexcept override;
};
-class CannotParseQmlTypesFile : std::exception
+class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : std::exception
{
public:
- const char *what() const noexcept override { return "Cannot parse qml types file!"; }
+ const char *what() const noexcept override;
};
-class CannotParseQmlDocumentFile : std::exception
+class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : std::exception
{
public:
- const char *what() const noexcept override { return "Cannot parse qml types file!"; }
+ const char *what() const noexcept override;
};
-class ProjectDataHasInvalidProjectSourceId : std::exception
+class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : std::exception
{
public:
- const char *what() const noexcept override { return "The project source id is invalid!"; }
+ const char *what() const noexcept override;
};
-class ProjectDataHasInvalidSourceId : std::exception
+class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : std::exception
{
public:
- const char *what() const noexcept override { return "The source id is invalid!"; }
+ const char *what() const noexcept override;
};
-class ProjectDataHasInvalidModuleId : std::exception
+class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : std::exception
{
public:
- const char *what() const noexcept override { return "The module id is invalid!"; }
+ const char *what() const noexcept override;
};
-class FileStatusHasInvalidSourceId : std::exception
+class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : std::exception
{
public:
- const char *what() const noexcept override
- {
- return "The source id in file status is invalid!";
- }
+ const char *what() const noexcept override;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h
index c27dcc0363..0d3bc730f8 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h
@@ -32,7 +32,6 @@ void set_greedy_intersection_call(
}
}
-
template<typename FileSystemWatcher, typename Timer, class SourcePathCache>
class ProjectStoragePathWatcher : public ProjectStoragePathWatcherInterface
{
@@ -61,10 +60,31 @@ public:
void updateIdPaths(const std::vector<IdPaths> &idPaths) override
{
- auto entriesAndIds = convertIdPathsToWatcherEntriesAndIds(idPaths);
+ const auto &[entires, ids] = convertIdPathsToWatcherEntriesAndIds(idPaths);
+
+ addEntries(entires);
+
+ auto notContainsdId = [&, &ids = ids](WatcherEntry entry) {
+ return !std::binary_search(ids.begin(), ids.end(), entry.id);
+ };
+ removeUnusedEntries(entires, notContainsdId);
+ }
+
+ void updateContextIdPaths(const std::vector<IdPaths> &idPaths,
+ const SourceContextIds &sourceContextIds)
+ {
+ const auto &[entires, ids] = convertIdPathsToWatcherEntriesAndIds(idPaths);
+
+ addEntries(entires);
- addEntries(entriesAndIds.first);
- removeUnusedEntries(entriesAndIds.first, entriesAndIds.second);
+ auto notContainsdId = [&, &ids = ids](WatcherEntry entry) {
+ return !std::binary_search(ids.begin(), ids.end(), entry.id)
+ || !std::binary_search(sourceContextIds.begin(),
+ sourceContextIds.end(),
+ entry.sourceContextId);
+ };
+
+ removeUnusedEntries(entires, notContainsdId);
}
void removeIds(const ProjectPartIds &ids) override
@@ -134,9 +154,10 @@ public:
m_fileSystemWatcher.addPaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
}
- void removeUnusedEntries(const WatcherEntries &entries, const ProjectChunkIds &ids)
+ template<typename Filter>
+ void removeUnusedEntries(const WatcherEntries &entries, Filter filter)
{
- auto oldEntries = notAnymoreWatchedEntriesWithIds(entries, ids);
+ auto oldEntries = notAnymoreWatchedEntriesWithIds(entries, filter);
removeFromWatchedEntries(oldEntries);
@@ -195,10 +216,8 @@ public:
return notWatchedDirectoryIds;
}
- template <typename Compare>
- WatcherEntries notAnymoreWatchedEntries(
- const WatcherEntries &newEntries,
- Compare compare) const
+ template<typename Compare>
+ WatcherEntries notAnymoreWatchedEntries(const WatcherEntries &newEntries, Compare compare) const
{
WatcherEntries notAnymoreWatchedEntries;
notAnymoreWatchedEntries.reserve(m_watchedEntries.size());
@@ -213,16 +232,12 @@ public:
return notAnymoreWatchedEntries;
}
- WatcherEntries notAnymoreWatchedEntriesWithIds(const WatcherEntries &newEntries,
- const ProjectChunkIds &ids) const
+ template<typename Filter>
+ WatcherEntries notAnymoreWatchedEntriesWithIds(const WatcherEntries &newEntries, Filter filter) const
{
auto oldEntries = notAnymoreWatchedEntries(newEntries, std::less<WatcherEntry>());
- auto newEnd = std::remove_if(oldEntries.begin(),
- oldEntries.end(),
- [&] (WatcherEntry entry) {
- return !std::binary_search(ids.begin(), ids.end(), entry.id);
- });
+ auto newEnd = std::remove_if(oldEntries.begin(), oldEntries.end(), filter);
oldEntries.erase(newEnd, oldEntries.end());
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
index a15482120f..a9185d91e8 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h
@@ -10,7 +10,7 @@
namespace QmlDesigner {
-enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir };
+enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir, Directory };
class ProjectChunkId
{
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
index fd22a7b26d..f98c69734a 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp
@@ -6,6 +6,7 @@
#include "filestatuscache.h"
#include "filesysteminterface.h"
#include "projectstorage.h"
+#include "projectstoragepathwatcherinterface.h"
#include "qmldocumentparserinterface.h"
#include "qmltypesparserinterface.h"
#include "sourcepath.h"
@@ -59,11 +60,11 @@ ProjectStorageUpdater::Components createComponents(
ModuleId moduleId,
ModuleId pathModuleId,
FileSystemInterface &fileSystem,
- const QString &directory)
+ const Utils::PathString &directory)
{
ProjectStorageUpdater::Components components;
- auto qmlFileNames = fileSystem.qmlFileNames(directory);
+ auto qmlFileNames = fileSystem.qmlFileNames(QString{directory});
components.reserve(static_cast<std::size_t>(qmlDirParserComponents.size() + qmlFileNames.size()));
@@ -160,28 +161,39 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i
} // namespace
-void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths)
+void ProjectStorageUpdater::update(QStringList directories,
+ QStringList qmlTypesPaths,
+ ProjectPartId projectPartId)
{
Storage::Synchronization::SynchronizationPackage package;
+ SourceIdsData sourceIdsData{static_cast<std::size_t>(directories.size())};
+ std::vector<IdPaths> idPaths;
+ idPaths.reserve(4);
- SourceIds notUpdatedFileStatusSourceIds;
- SourceIds notUpdatedSourceIds;
-
- updateDirectories(directories, package, notUpdatedFileStatusSourceIds, notUpdatedSourceIds);
- updateQmlTypes(qmlTypesPaths, package, notUpdatedFileStatusSourceIds, notUpdatedSourceIds);
+ updateDirectories(directories, package, sourceIdsData);
+ updateQmlTypes(qmlTypesPaths, package, sourceIdsData);
package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds),
- std::move(notUpdatedSourceIds));
+ std::move(sourceIdsData.notUpdatedSourceIds));
package.updatedFileStatusSourceIds = filterNotUpdatedSourceIds(
- std::move(package.updatedFileStatusSourceIds), std::move(notUpdatedFileStatusSourceIds));
+ std::move(package.updatedFileStatusSourceIds),
+ std::move(sourceIdsData.notUpdatedFileStatusSourceIds));
m_projectStorage.synchronize(std::move(package));
+
+ idPaths.push_back(
+ {projectPartId, SourceType::Directory, std::move(sourceIdsData.watchedDirectorySourceIds)});
+ idPaths.push_back(
+ {projectPartId, SourceType::QmlDir, std::move(sourceIdsData.watchedQmldirSourceIds)});
+ idPaths.push_back({projectPartId, SourceType::Qml, std::move(sourceIdsData.watchedQmlSourceIds)});
+ idPaths.push_back(
+ {projectPartId, SourceType::QmlTypes, std::move(sourceIdsData.watchedQmltypesSourceIds)});
+ m_pathWatcher.updateIdPaths(idPaths);
}
void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds)
+ SourceIdsData &sourceIdsData)
{
if (qmlTypesPaths.empty())
return;
@@ -190,6 +202,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
for (const QString &qmlTypesPath : qmlTypesPaths) {
SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath});
+ sourceIdsData.watchedQmltypesSourceIds.push_back(sourceId);
Storage::Synchronization::ProjectData projectData{sourceId,
sourceId,
@@ -199,8 +212,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths,
FileState state = parseTypeInfo(projectData,
Utils::PathString{qmlTypesPath},
package,
- notUpdatedFileStatusSourceIds,
- notUpdatedSourceIds);
+ sourceIdsData);
if (state == FileState::Changed)
package.projectDatas.push_back(std::move(projectData));
@@ -223,113 +235,118 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates)
void ProjectStorageUpdater::updateDirectories(const QStringList &directories,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds)
+ SourceIdsData &sourceIdsData)
+{
+ for (const QString &directory : directories)
+ updateDirectory({directory}, package, sourceIdsData);
+}
+
+void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath,
+ Storage::Synchronization::SynchronizationPackage &package,
+ SourceIdsData &sourceIdsData)
{
- for (const QString &directory : directories) {
- Utils::PathString directoryPath = directory;
- SourcePath qmldirSourcePath{directory + "/qmldir"};
- auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath);
-
- SourcePath directorySourcePath{directory + "/."};
- auto directorySourceId = m_pathCache.sourceId(directorySourcePath);
-
- auto directoryState = fileState(directorySourceId,
- package.fileStatuses,
- package.updatedFileStatusSourceIds,
- notUpdatedFileStatusSourceIds);
-
- auto qmldirState = fileState(qmlDirSourceId,
- package.fileStatuses,
- package.updatedFileStatusSourceIds,
- notUpdatedFileStatusSourceIds);
-
- switch (combineState(directoryState, qmldirState)) {
- case FileState::Changed: {
- QmlDirParser parser;
+ SourcePath qmldirSourcePath{directoryPath + "/qmldir"};
+ auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath);
+
+ SourcePath directorySourcePath{directoryPath + "/."};
+ auto directorySourceId = m_pathCache.sourceId(directorySourcePath);
+ auto directoryState = fileState(directorySourceId, package, sourceIdsData);
+ if (directoryState != FileState::NotExists)
+ sourceIdsData.watchedDirectorySourceIds.push_back(directorySourceId);
+
+ auto qmldirState = fileState(qmlDirSourceId, package, sourceIdsData);
+ if (qmldirState != FileState::NotExists)
+ sourceIdsData.watchedQmldirSourceIds.push_back(qmlDirSourceId);
+
+ switch (combineState(directoryState, qmldirState)) {
+ case FileState::Changed: {
+ QmlDirParser parser;
+ if (qmldirState != FileState::NotExists)
parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath}));
+ if (qmldirState != FileState::NotChanged)
package.updatedSourceIds.push_back(qmlDirSourceId);
- Utils::PathString moduleName{parser.typeNamespace()};
- ModuleId moduleId = m_projectStorage.moduleId(moduleName);
- ModuleId cppModuleId = m_projectStorage.moduleId(moduleName + "-cppnative");
- ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
-
- auto imports = filterMultipleEntries(parser.imports());
-
- addModuleExportedImports(package.moduleExportedImports,
- moduleId,
- cppModuleId,
- imports,
- m_projectStorage);
- package.updatedModuleIds.push_back(moduleId);
-
- const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId);
- addSourceIds(package.updatedSourceIds, qmlProjectDatas);
- addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas);
-
- auto qmlTypes = filterMultipleEntries(parser.typeInfos());
-
- if (!qmlTypes.isEmpty()) {
- parseTypeInfos(qmlTypes,
- filterMultipleEntries(parser.dependencies()),
- imports,
- qmlDirSourceId,
- directoryPath,
- cppModuleId,
- package,
- notUpdatedFileStatusSourceIds,
- notUpdatedSourceIds);
- }
- parseQmlComponents(
- createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directory),
- qmlDirSourceId,
- directoryId,
- package,
- notUpdatedFileStatusSourceIds);
- package.updatedProjectSourceIds.push_back(qmlDirSourceId);
- break;
+ Utils::PathString moduleName{parser.typeNamespace()};
+ ModuleId moduleId = m_projectStorage.moduleId(moduleName);
+ ModuleId cppModuleId = m_projectStorage.moduleId(moduleName + "-cppnative");
+ ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath);
+
+ auto imports = filterMultipleEntries(parser.imports());
+
+ addModuleExportedImports(package.moduleExportedImports,
+ moduleId,
+ cppModuleId,
+ imports,
+ m_projectStorage);
+ package.updatedModuleIds.push_back(moduleId);
+
+ const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
+ addSourceIds(package.updatedSourceIds, qmlProjectDatas);
+ addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas);
+
+ auto qmlTypes = filterMultipleEntries(parser.typeInfos());
+
+ if (!qmlTypes.isEmpty()) {
+ parseTypeInfos(qmlTypes,
+ filterMultipleEntries(parser.dependencies()),
+ imports,
+ directorySourceId,
+ directoryPath,
+ cppModuleId,
+ package,
+ sourceIdsData);
}
- case FileState::NotChanged: {
- parseProjectDatas(m_projectStorage.fetchProjectDatas(qmlDirSourceId),
- package,
- notUpdatedFileStatusSourceIds,
- notUpdatedSourceIds,
- directoryPath);
- break;
+ parseQmlComponents(
+ createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath),
+ directorySourceId,
+ directoryId,
+ package,
+ sourceIdsData,
+ qmldirState);
+ package.updatedProjectSourceIds.push_back(directorySourceId);
+ break;
+ }
+ case FileState::NotChanged: {
+ parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), package, sourceIdsData);
+ break;
+ }
+ case FileState::NotExists: {
+ package.updatedFileStatusSourceIds.push_back(directorySourceId);
+ package.updatedFileStatusSourceIds.push_back(qmlDirSourceId);
+ package.updatedProjectSourceIds.push_back(directorySourceId);
+ package.updatedSourceIds.push_back(qmlDirSourceId);
+ auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId);
+ for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) {
+ package.updatedSourceIds.push_back(projectData.sourceId);
+ package.updatedFileStatusSourceIds.push_back(projectData.sourceId);
}
- case FileState::NotExists: {
- package.updatedSourceIds.push_back(qmlDirSourceId);
- auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(qmlDirSourceId);
- for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) {
- package.updatedSourceIds.push_back(projectData.sourceId);
- }
- break;
- }
- }
+ break;
+ }
}
}
-void ProjectStorageUpdater::pathsWithIdsChanged([[maybe_unused]] const std::vector<IdPaths> &idPaths)
-{}
+void ProjectStorageUpdater::pathsWithIdsChanged([[maybe_unused]] const std::vector<IdPaths> &) {}
+
+void ProjectStorageUpdater::pathsChanged(const SourceIds &) {}
void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos,
const QList<QmlDirParser::Import> &qmldirDependencies,
const QList<QmlDirParser::Import> &qmldirImports,
- SourceId qmldirSourceId,
+ SourceId directorySourceId,
Utils::SmallStringView directoryPath,
ModuleId moduleId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds)
+ SourceIdsData &sourceIdData)
{
for (const QString &typeInfo : typeInfos) {
Utils::PathString qmltypesPath = Utils::PathString::join(
{directoryPath, "/", Utils::SmallString{typeInfo}});
SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath});
+ sourceIdData.watchedQmltypesSourceIds.push_back(sourceId);
+
addDependencies(package.moduleDependencies,
sourceId,
joinImports(qmldirDependencies, qmldirImports),
@@ -337,43 +354,29 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos,
package.updatedModuleDependencySourceIds.push_back(sourceId);
auto projectData = package.projectDatas.emplace_back(
- qmldirSourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes);
+ directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes);
- parseTypeInfo(projectData,
- qmltypesPath,
- package,
- notUpdatedFileStatusSourceIds,
- notUpdatedSourceIds);
+ parseTypeInfo(projectData, qmltypesPath, package, sourceIdData);
}
}
void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds,
- Utils::SmallStringView directoryPath)
+ SourceIdsData &sourceIdData)
{
for (const Storage::Synchronization::ProjectData &projectData : projectDatas) {
switch (projectData.fileType) {
case Storage::Synchronization::FileType::QmlTypes: {
- auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId);
+ sourceIdData.watchedQmltypesSourceIds.push_back(projectData.sourceId);
- parseTypeInfo(projectData,
- qmltypesPath,
- package,
- notUpdatedFileStatusSourceIds,
- notUpdatedSourceIds);
+ auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId);
+ parseTypeInfo(projectData, qmltypesPath, package, sourceIdData);
break;
}
case Storage::Synchronization::FileType::QmlDocument: {
- SourcePath qmlDocumentPath = m_pathCache.sourcePath(projectData.sourceId);
-
- parseQmlComponent(qmlDocumentPath.name(),
- qmlDocumentPath,
- directoryPath,
- projectData.sourceId,
- package,
- notUpdatedFileStatusSourceIds);
+ sourceIdData.watchedQmlSourceIds.push_back(projectData.sourceId);
+
+ parseQmlComponent(projectData.sourceId, package, sourceIdData);
}
};
}
@@ -382,13 +385,9 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr
auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData,
Utils::SmallStringView qmltypesPath,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds) -> FileState
+ SourceIdsData &sourceIdData) -> FileState
{
- auto state = fileState(projectData.sourceId,
- package.fileStatuses,
- package.updatedFileStatusSourceIds,
- notUpdatedFileStatusSourceIds);
+ auto state = fileState(projectData.sourceId, package, sourceIdData);
switch (state) {
case FileState::Changed: {
package.updatedSourceIds.push_back(projectData.sourceId);
@@ -398,10 +397,11 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
break;
}
case FileState::NotChanged: {
- notUpdatedSourceIds.push_back(projectData.sourceId);
+ sourceIdData.notUpdatedSourceIds.push_back(projectData.sourceId);
break;
}
case FileState::NotExists:
+ throw CannotParseQmlTypesFile{};
break;
}
@@ -411,9 +411,10 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec
void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFilePath,
Utils::SmallStringView directoryPath,
Storage::Synchronization::ExportedTypes exportedTypes,
- SourceId qmldirSourceId,
+ SourceId directorySourceId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds)
+ SourceIdsData &sourceIdData,
+ FileState qmldirState)
{
if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end())
return;
@@ -422,13 +423,21 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmlFilePath});
Storage::Synchronization::Type type;
+ auto state = fileState(sourceId, package, sourceIdData);
+
+ sourceIdData.watchedQmlSourceIds.push_back(sourceId);
- auto state = fileState(sourceId,
- package.fileStatuses,
- package.updatedFileStatusSourceIds,
- notUpdatedFileStatusSourceIds);
switch (state) {
case FileState::NotChanged:
+ if (qmldirState == FileState::NotExists) {
+ sourceIdData.notUpdatedSourceIds.emplace_back(sourceId);
+ package.projectDatas.emplace_back(directorySourceId,
+ sourceId,
+ ModuleId{},
+ Storage::Synchronization::FileType::QmlDocument);
+
+ return;
+ }
type.changeLevel = Storage::Synchronization::ChangeLevel::Minimal;
break;
case FileState::NotExists:
@@ -439,7 +448,7 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
break;
}
- package.projectDatas.emplace_back(qmldirSourceId,
+ package.projectDatas.emplace_back(directorySourceId,
sourceId,
ModuleId{},
Storage::Synchronization::FileType::QmlDocument);
@@ -454,28 +463,22 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil
package.types.push_back(std::move(type));
}
-void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView fileName,
- Utils::SmallStringView filePath,
- Utils::SmallStringView directoryPath,
- SourceId sourceId,
+void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds)
+ SourceIdsData &sourceIdData)
{
- auto state = fileState(sourceId,
- package.fileStatuses,
- package.updatedFileStatusSourceIds,
- notUpdatedFileStatusSourceIds);
+ auto state = fileState(sourceId, package, sourceIdData);
if (state != FileState::Changed)
return;
package.updatedSourceIds.push_back(sourceId);
- SourcePath sourcePath{filePath};
+ SourcePath sourcePath = m_pathCache.sourcePath(sourceId);
- const auto content = m_fileSystem.contentAsQString(QString{filePath});
- auto type = m_qmlDocumentParser.parse(content, package.imports, sourceId, directoryPath);
+ const auto content = m_fileSystem.contentAsQString(QString{sourcePath});
+ auto type = m_qmlDocumentParser.parse(content, package.imports, sourceId, sourcePath.directory());
- type.typeName = fileName;
+ type.typeName = sourcePath.name();
type.traits = Storage::TypeTraits::Reference;
type.sourceId = sourceId;
type.changeLevel = Storage::Synchronization::ChangeLevel::ExcludeExportedTypes;
@@ -520,10 +523,11 @@ Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdate
} // namespace
void ProjectStorageUpdater::parseQmlComponents(Components components,
- SourceId qmldirSourceId,
+ SourceId directorySourceId,
SourceContextId directoryId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds)
+ SourceIdsData &sourceIdsData,
+ FileState qmldirState)
{
std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) {
return first.fileName < second.fileName;
@@ -537,33 +541,36 @@ void ProjectStorageUpdater::parseQmlComponents(Components components,
parseQmlComponent(fileName,
directoryPath,
createExportedTypes(componentsWithSameFileName),
- qmldirSourceId,
+ directorySourceId,
package,
- notUpdatedFileStatusSourceIds);
+ sourceIdsData,
+ qmldirState);
};
rangeForTheSameFileName(components, callback);
}
-ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState(SourceId sourceId,
- FileStatuses &fileStatuses,
- SourceIds &updatedSourceIds,
- SourceIds &notUpdatedSourceIds) const
+ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState(
+ SourceId sourceId,
+ Storage::Synchronization::SynchronizationPackage &package,
+ SourceIdsData &sourceIdData) const
{
auto currentFileStatus = m_fileStatusCache.find(sourceId);
- if (!currentFileStatus.isValid())
+ if (!currentFileStatus.isValid()) {
+ package.updatedFileStatusSourceIds.push_back(sourceId);
return FileState::NotExists;
+ }
auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId);
if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) {
- fileStatuses.push_back(currentFileStatus);
- updatedSourceIds.push_back(currentFileStatus.sourceId);
+ package.fileStatuses.push_back(currentFileStatus);
+ package.updatedFileStatusSourceIds.push_back(sourceId);
return FileState::Changed;
}
- notUpdatedSourceIds.push_back(currentFileStatus.sourceId);
+ sourceIdData.notUpdatedFileStatusSourceIds.push_back(sourceId);
return FileState::NotChanged;
}
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
index 417ec715e8..90ce4d543d 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h
@@ -6,8 +6,10 @@
#include "filestatus.h"
#include "nonlockingmutex.h"
#include "projectstorageids.h"
+#include "projectstoragepathwatchernotifierinterface.h"
#include "projectstoragepathwatchertypes.h"
#include "projectstoragetypes.h"
+#include "sourcepath.h"
#include <qmljs/parser/qmldirparser_p.h>
@@ -30,7 +32,7 @@ class ProjectStorage;
class QmlDocumentParserInterface;
class QmlTypesParserInterface;
-class ProjectStorageUpdater
+class ProjectStorageUpdater : public ProjectStoragePathWatcherNotifierInterface
{
public:
using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>;
@@ -40,17 +42,22 @@ public:
FileStatusCache &fileStatusCache,
PathCache &pathCache,
QmlDocumentParserInterface &qmlDocumentParser,
- QmlTypesParserInterface &qmlTypesParser)
+ QmlTypesParserInterface &qmlTypesParser,
+ class ProjectStoragePathWatcherInterface &pathWatcher)
: m_fileSystem{fileSystem}
, m_projectStorage{projectStorage}
, m_fileStatusCache{fileStatusCache}
, m_pathCache{pathCache}
, m_qmlDocumentParser{qmlDocumentParser}
, m_qmlTypesParser{qmlTypesParser}
+ , m_pathWatcher{pathWatcher}
{}
- void update(QStringList directories, QStringList qmlTypesPaths);
- void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths);
+ void update(QStringList directories,
+ QStringList qmlTypesPaths,
+ ProjectPartId projectPartId = ProjectPartId{});
+ void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) override;
+ void pathsChanged(const SourceIds &filePathIds) override;
struct Component
{
@@ -90,57 +97,73 @@ public:
};
private:
+ struct SourceIdsData
+ {
+ SourceIdsData(std::size_t reserve)
+ {
+ notUpdatedFileStatusSourceIds.reserve(reserve * 30);
+ notUpdatedSourceIds.reserve(reserve * 30);
+ watchedDirectorySourceIds.reserve(reserve);
+ watchedQmldirSourceIds.reserve(reserve);
+ watchedQmlSourceIds.reserve(reserve * 30);
+ watchedQmltypesSourceIds.reserve(reserve * 30);
+ }
+
+ SourceIds notUpdatedFileStatusSourceIds;
+ SourceIds notUpdatedSourceIds;
+ SourceIds watchedDirectorySourceIds;
+ SourceIds watchedQmldirSourceIds;
+ SourceIds watchedQmlSourceIds;
+ SourceIds watchedQmltypesSourceIds;
+ };
void updateQmlTypes(const QStringList &qmlTypesPaths,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds);
+ SourceIdsData &sourceIdData);
+
void updateDirectories(const QStringList &directories,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds);
+ SourceIdsData &sourceIdData);
+
+ void updateDirectory(const Utils::PathString &directory,
+ Storage::Synchronization::SynchronizationPackage &package,
+ SourceIdsData &sourceIdData);
void parseTypeInfos(const QStringList &typeInfos,
const QList<QmlDirParser::Import> &qmldirDependencies,
const QList<QmlDirParser::Import> &qmldirImports,
- SourceId qmldirSourceId,
+ SourceId directorySourceId,
Utils::SmallStringView directoryPath,
ModuleId moduleId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds);
+ SourceIdsData &sourceIdData);
void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds,
- Utils::SmallStringView directoryPath);
+ SourceIdsData &sourceIdData);
FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData,
Utils::SmallStringView qmltypesPath,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds,
- SourceIds &notUpdatedSourceIds);
+ SourceIdsData &sourceIdData);
void parseQmlComponents(Components components,
- SourceId qmldirSourceId,
+ SourceId directorySourceId,
SourceContextId directoryId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds);
+ SourceIdsData &sourceIdData,
+ FileState qmldirState);
void parseQmlComponent(Utils::SmallStringView fileName,
Utils::SmallStringView directory,
Storage::Synchronization::ExportedTypes exportedTypes,
- SourceId qmldirSourceId,
+ SourceId directorySourceId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds);
- void parseQmlComponent(Utils::SmallStringView fileName,
- Utils::SmallStringView filePath,
- Utils::SmallStringView directoryPath,
- SourceId sourceId,
+ SourceIdsData &sourceIdData,
+ FileState qmldirState);
+ void parseQmlComponent(SourceId sourceId,
Storage::Synchronization::SynchronizationPackage &package,
- SourceIds &notUpdatedFileStatusSourceIds);
+ SourceIdsData &sourceIdData);
FileState fileState(SourceId sourceId,
- FileStatuses &fileStatuses,
- SourceIds &updatedSourceIds,
- SourceIds &notUpdatedSourceIds) const;
+ Storage::Synchronization::SynchronizationPackage &package,
+ SourceIdsData &sourceIdData) const;
private:
FileSystemInterface &m_fileSystem;
@@ -149,6 +172,7 @@ private:
PathCache &m_pathCache;
QmlDocumentParserInterface &m_qmlDocumentParser;
QmlTypesParserInterface &m_qmlTypesParser;
+ ProjectStoragePathWatcherInterface &m_pathWatcher;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
index 89d9b1bf0b..97aac6a857 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp
@@ -11,7 +11,7 @@
#include <sqlitedatabase.h>
#ifdef QDS_HAS_QMLDOM
-#include <qmldom/qqmldomtop_p.h>
+#include <private/qqmldomtop_p.h>
#endif
#include <filesystem>
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
index a1e189d9fb..744825aadc 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h
@@ -4,10 +4,9 @@
#pragma once
#include "nonlockingmutex.h"
+#include "projectstoragefwd.h"
#include "qmldocumentparserinterface.h"
-#include <projectstoragefwd.h>
-
namespace QmlDesigner {
template<typename ProjectStorage, typename Mutex>
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
index de299347a3..bca7b3279d 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp
@@ -9,8 +9,8 @@
#include <sqlitedatabase.h>
#ifdef QDS_HAS_QMLDOM
-#include <qmlcompiler/qqmljstypedescriptionreader_p.h>
-#include <qmldom/qqmldomtop_p.h>
+#include <private/qqmldomtop_p.h>
+#include <private/qqmljstypedescriptionreader_p.h>
#endif
#include <QDateTime>
@@ -21,28 +21,30 @@
namespace QmlDesigner {
#ifdef QDS_HAS_QMLDOM
+
namespace QmlDom = QQmlJS::Dom;
namespace {
using ComponentWithoutNamespaces = QMap<QString, QString>;
-ComponentWithoutNamespaces createComponentNameWithoutNamespaces(
- const QHash<QString, QQmlJSExportedScope> &objects)
+using Storage::TypeNameString;
+
+ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQmlJSExportedScope> &objects)
{
ComponentWithoutNamespaces componentWithoutNamespaces;
- for (auto current = objects.keyBegin(), end = objects.keyEnd(); current != end; ++current) {
- const QString &key = *current;
+ for (const auto &object : objects) {
+ const QString &name = object.scope->internalName();
QString searchTerm{"::"};
- auto found = std::search(key.cbegin(), key.cend(), searchTerm.cbegin(), searchTerm.cend());
+ auto found = std::search(name.cbegin(), name.cend(), searchTerm.cbegin(), searchTerm.cend());
- if (found == key.cend())
+ if (found == name.cend())
continue;
- componentWithoutNamespaces.insert(QStringView{std::next(found, 2), key.cend()}.toString(),
- key);
+ componentWithoutNamespaces.insert(QStringView{std::next(found, 2), name.cend()}.toString(),
+ name);
}
return componentWithoutNamespaces;
@@ -79,21 +81,20 @@ void addImports(Storage::Synchronization::Imports &imports,
imports.emplace_back(qmlCppModuleId, Storage::Synchronization::Version{}, sourceId);
}
-Storage::Synchronization::TypeTraits createTypeTraits(
- QQmlJSScope::AccessSemantics accessSematics)
+Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics)
{
switch (accessSematics) {
case QQmlJSScope::AccessSemantics::Reference:
- return Storage::Synchronization::TypeTraits::Reference;
+ return Storage::TypeTraits::Reference;
case QQmlJSScope::AccessSemantics::Value:
- return Storage::Synchronization::TypeTraits::Value;
+ return Storage::TypeTraits::Value;
case QQmlJSScope::AccessSemantics::None:
- return Storage::Synchronization::TypeTraits::None;
+ return Storage::TypeTraits::None;
case QQmlJSScope::AccessSemantics::Sequence:
- return Storage::Synchronization::TypeTraits::Sequence;
+ return Storage::TypeTraits::Sequence;
}
- return Storage::Synchronization::TypeTraits::None;
+ return Storage::TypeTraits::None;
}
Storage::Synchronization::Version createVersion(QTypeRevision qmlVersion)
@@ -227,16 +228,9 @@ Storage::Synchronization::ParameterDeclarations createParameters(
{
Storage::Synchronization::ParameterDeclarations parameterDeclarations;
- const QStringList &parameterNames = qmlMethod.parameterNames();
- const QStringList &parameterTypeNames = qmlMethod.parameterTypeNames();
- auto currentName = parameterNames.begin();
- auto currentType = parameterTypeNames.begin();
- auto nameEnd = parameterNames.end();
- auto typeEnd = parameterTypeNames.end();
-
- for (; currentName != nameEnd && currentType != typeEnd; ++currentName, ++currentType) {
- parameterDeclarations.emplace_back(Utils::SmallString{*currentName},
- fullyQualifiedTypeName(*currentType,
+ for (const auto &parameter : qmlMethod.parameters()) {
+ parameterDeclarations.emplace_back(Utils::SmallString{parameter.name()},
+ fullyQualifiedTypeName(parameter.typeName(),
componentNameWithoutNamespace));
}
@@ -347,8 +341,8 @@ void addEnumerationType(EnumerationTypes &enumerationTypes,
auto fullTypeName = addEnumerationType(enumerationTypes, typeName, enumerationName);
types.emplace_back(fullTypeName,
Storage::Synchronization::ImportedType{TypeNameString{}},
- Storage::Synchronization::TypeTraits::Value
- | Storage::Synchronization::TypeTraits::IsEnum,
+ Storage::Synchronization::ImportedType{},
+ Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum,
sourceId,
createCppEnumerationExports(typeName,
cppModuleId,
@@ -416,22 +410,22 @@ void addType(Storage::Synchronization::Types &types,
auto exports = exportScope.exports;
auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations);
- types.emplace_back(Utils::SmallStringView{typeName},
- Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}},
- createTypeTraits(component.traits()),
- sourceId,
- createExports(exports, typeName, storage, cppModuleId),
- createProperties(component.ownProperties(),
- enumerationTypes,
- componentNameWithoutNamespace),
- std::move(functionsDeclarations),
- std::move(signalDeclarations),
- createEnumeration(enumerations));
+ types.emplace_back(
+ Utils::SmallStringView{typeName},
+ Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}},
+ Storage::Synchronization::ImportedType{TypeNameString{component.extensionTypeName()}},
+ createTypeTraits(component.accessSemantics()),
+ sourceId,
+ createExports(exports, typeName, storage, cppModuleId),
+ createProperties(component.ownProperties(), enumerationTypes, componentNameWithoutNamespace),
+ std::move(functionsDeclarations),
+ std::move(signalDeclarations),
+ createEnumeration(enumerations));
}
void addTypes(Storage::Synchronization::Types &types,
const Storage::Synchronization::ProjectData &projectData,
- const QHash<QString, QQmlJSExportedScope> &objects,
+ const QList<QQmlJSExportedScope> &objects,
QmlTypesParser::ProjectStorage &storage,
const ComponentWithoutNamespaces &componentNameWithoutNamespaces)
{
@@ -454,7 +448,7 @@ void QmlTypesParser::parse(const QString &sourceContent,
const Storage::Synchronization::ProjectData &projectData)
{
QQmlJSTypeDescriptionReader reader({}, sourceContent);
- QHash<QString, QQmlJSExportedScope> components;
+ QList<QQmlJSExportedScope> components;
QStringList dependencies;
bool isValid = reader(&components, &dependencies);
if (!isValid)
diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
index 902d3c5a44..09513bb74e 100644
--- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
+++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
@@ -42,7 +42,7 @@ public:
private:
// m_pathCache and m_storage are only used when compiled for QDS
#ifdef QDS_HAS_QMLDOM
- PathCache &m_pathCache;
+ [[maybe_unused]] PathCache &m_pathCache;
ProjectStorage &m_storage;
#endif
};
diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp
index 3249e1bc18..ca1b927c3a 100644
--- a/src/plugins/qmldesigner/designmodecontext.cpp
+++ b/src/plugins/qmldesigner/designmodecontext.cpp
@@ -2,12 +2,13 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "designmodecontext.h"
-#include "qmldesignerconstants.h"
+#include "assetslibrarywidget.h"
#include "designmodewidget.h"
-#include "formeditorwidget.h"
#include "edit3dwidget.h"
+#include "formeditorwidget.h"
#include "materialbrowserwidget.h"
#include "navigatorwidget.h"
+#include "qmldesignerconstants.h"
#include "texteditorwidget.h"
namespace QmlDesigner {
@@ -61,6 +62,18 @@ void MaterialBrowserContext::contextHelp(const HelpCallback &callback) const
qobject_cast<MaterialBrowserWidget *>(m_widget)->contextHelp(callback);
}
+AssetsLibraryContext::AssetsLibraryContext(QWidget *widget)
+ : IContext(widget)
+{
+ setWidget(widget);
+ setContext(Core::Context(Constants::C_QMLASSETSLIBRARY, Constants::C_QT_QUICK_TOOLS_MENU));
+}
+
+void AssetsLibraryContext::contextHelp(const HelpCallback &callback) const
+{
+ qobject_cast<AssetsLibraryWidget *>(m_widget)->contextHelp(callback);
+}
+
NavigatorContext::NavigatorContext(QWidget *widget)
: IContext(widget)
{
diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h
index c8c728657a..fab7fe0ea3 100644
--- a/src/plugins/qmldesigner/designmodecontext.h
+++ b/src/plugins/qmldesigner/designmodecontext.h
@@ -47,6 +47,15 @@ public:
void contextHelp(const Core::IContext::HelpCallback &callback) const override;
};
+class AssetsLibraryContext : public Core::IContext
+{
+ Q_OBJECT
+
+public:
+ AssetsLibraryContext(QWidget *widget);
+ void contextHelp(const Core::IContext::HelpCallback &callback) const override;
+};
+
class NavigatorContext : public Core::IContext
{
Q_OBJECT
diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp
index d203e2c8ff..2acdff1823 100644
--- a/src/plugins/qmldesigner/designmodewidget.cpp
+++ b/src/plugins/qmldesigner/designmodewidget.cpp
@@ -12,6 +12,7 @@
#include <nodeinstanceview.h>
#include <itemlibrarywidget.h>
#include <theme.h>
+#include <toolbar.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -90,6 +91,7 @@ DesignModeWidget::DesignModeWidget()
: m_toolBar(new Core::EditorToolBar(this))
, m_crumbleBar(new CrumbleBar(this))
{
+ setAcceptDrops(true);
}
DesignModeWidget::~DesignModeWidget()
@@ -189,11 +191,11 @@ void DesignModeWidget::setup()
const QSize size = QSize(28, 28);
auto tabCloseIconNormal = Utils::StyleHelper::IconFontHelper(
- closeUnicode, Theme::getColor(Theme::DStabInactiveIcon), size, QIcon::Normal, QIcon::Off);
+ closeUnicode, Theme::getColor(Theme::DSdockWidgetTitleBar), size, QIcon::Normal, QIcon::Off);
auto tabCloseIconActive = Utils::StyleHelper::IconFontHelper(
- closeUnicode, Theme::getColor(Theme::DStabActiveIcon), size, QIcon::Active, QIcon::Off);
+ closeUnicode, Theme::getColor(Theme::DSdockWidgetTitleBar), size, QIcon::Active, QIcon::Off);
auto tabCloseIconFocus = Utils::StyleHelper::IconFontHelper(
- closeUnicode, Theme::getColor(Theme::DStabFocusIcon), size, QIcon::Selected, QIcon::Off);
+ closeUnicode, Theme::getColor(Theme::DSdockWidgetTitleBar), size, QIcon::Selected, QIcon::Off);
const QIcon tabsCloseIcon = Utils::StyleHelper::getIconFromIconFont(
fontName, {tabCloseIconNormal,
@@ -326,8 +328,19 @@ void DesignModeWidget::setup()
command->setAttribute(Core::Command::CA_Hide);
viewCommands.append(command);
- connect(outputPanePlaceholder, &Core::OutputPanePlaceHolder::visibilityChangeRequested,
- m_outputPaneDockWidget, &ADS::DockWidget::toggleView);
+ connect(command->action(), &QAction::triggered, this, [command]() {
+ if (!command->action()->isChecked())
+ return;
+
+ auto cmd = Core::ActionManager::command("QtCreator.Pane.ApplicationOutput");
+ QTC_ASSERT(cmd, return);
+ cmd->action()->trigger();
+ });
+
+ connect(outputPanePlaceholder,
+ &Core::OutputPanePlaceHolder::visibilityChangeRequested,
+ m_outputPaneDockWidget,
+ &ADS::DockWidget::toggleView);
}
std::sort(viewCommands.begin(), viewCommands.end(), [](Core::Command *first, Core::Command *second){
@@ -338,27 +351,69 @@ void DesignModeWidget::setup()
mviews->addAction(command);
// Create toolbars
- auto toolBar = new QToolBar();
-
- toolBar->addAction(viewManager().componentViewAction());
- toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
- DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar);
-
- designerToolBar->layout()->addWidget(toolBar);
+ if (!ToolBar::isVisible()) {
+ auto toolBar = new QToolBar();
+
+ toolBar->addAction(viewManager().componentViewAction());
+ toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar);
+
+ designerToolBar->layout()->addWidget(toolBar);
+
+ m_toolBar->addCenterToolBar(designerToolBar);
+ m_toolBar->setMinimumWidth(320);
+ m_toolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone);
+ m_toolBar->setNavigationVisible(true);
+
+ connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked);
+ connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked);
+
+
+ QToolBar* toolBarWrapper = new QToolBar();
+ toolBarWrapper->addWidget(m_toolBar);
+ toolBarWrapper->addWidget(createCrumbleBarFrame());
+ toolBarWrapper->setMovable(false);
+ addToolBar(Qt::TopToolBarArea, toolBarWrapper);
+
+
+ addSpacerToToolBar(toolBar);
+
+ auto workspaceComboBox = new QComboBox();
+ workspaceComboBox->setMinimumWidth(120);
+ workspaceComboBox->setToolTip(tr("Switch the active workspace."));
+ auto sortedWorkspaces = m_dockManager->workspaces();
+ Utils::sort(sortedWorkspaces);
+ workspaceComboBox->addItems(sortedWorkspaces);
+ workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
+ toolBar->addWidget(workspaceComboBox);
+
+ connect(m_dockManager, &ADS::DockManager::workspaceListChanged,
+ workspaceComboBox, [this, workspaceComboBox]() {
+ workspaceComboBox->clear();
+ auto sortedWorkspaces = m_dockManager->workspaces();
+ Utils::sort(sortedWorkspaces);
+ workspaceComboBox->addItems(sortedWorkspaces);
+ workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
+ });
+ connect(m_dockManager, &ADS::DockManager::workspaceLoaded, workspaceComboBox, &QComboBox::setCurrentText);
+ connect(workspaceComboBox, &QComboBox::activated,
+ m_dockManager, [this, workspaceComboBox]([[maybe_unused]] int index) {
+ m_dockManager->openWorkspace(workspaceComboBox->currentText());
+ });
- m_toolBar->addCenterToolBar(designerToolBar);
- m_toolBar->setMinimumWidth(320);
- m_toolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone);
- m_toolBar->setNavigationVisible(true);
+ const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont(
+ fontName, Theme::getIconUnicode(Theme::Icon::annotationBubble),
+ 36, 36, Theme::getColor(Theme::IconsBaseColor));
+ toolBar->addAction(gaIcon, tr("Edit global annotation for current file."), [&](){
+ ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode();
- connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked);
- connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked);
+ if (node.isValid()) {
+ m_globalAnnotationEditor.setModelNode(node);
+ m_globalAnnotationEditor.showWidget();
+ }
+ });
- QToolBar* toolBarWrapper = new QToolBar();
- toolBarWrapper->addWidget(m_toolBar);
- toolBarWrapper->addWidget(createCrumbleBarFrame());
- toolBarWrapper->setMovable(false);
- addToolBar(Qt::TopToolBarArea, toolBarWrapper);
+ }
if (currentDesignDocument())
setupNavigatorHistory(currentDesignDocument()->textEditor());
@@ -389,43 +444,6 @@ void DesignModeWidget::setup()
}
});
- addSpacerToToolBar(toolBar);
-
- auto workspaceComboBox = new QComboBox();
- workspaceComboBox->setMinimumWidth(120);
- workspaceComboBox->setToolTip(tr("Switch the active workspace."));
- auto sortedWorkspaces = m_dockManager->workspaces();
- Utils::sort(sortedWorkspaces);
- workspaceComboBox->addItems(sortedWorkspaces);
- workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
- toolBar->addWidget(workspaceComboBox);
-
- connect(m_dockManager, &ADS::DockManager::workspaceListChanged,
- workspaceComboBox, [this, workspaceComboBox]() {
- workspaceComboBox->clear();
- auto sortedWorkspaces = m_dockManager->workspaces();
- Utils::sort(sortedWorkspaces);
- workspaceComboBox->addItems(sortedWorkspaces);
- workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
- });
- connect(m_dockManager, &ADS::DockManager::workspaceLoaded, workspaceComboBox, &QComboBox::setCurrentText);
- connect(workspaceComboBox, &QComboBox::activated,
- m_dockManager, [this, workspaceComboBox]([[maybe_unused]] int index) {
- m_dockManager->openWorkspace(workspaceComboBox->currentText());
- });
-
- const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont(
- fontName, Theme::getIconUnicode(Theme::Icon::annotationBubble),
- 36, 36, Theme::getColor(Theme::IconsBaseColor));
- toolBar->addAction(gaIcon, tr("Edit global annotation for current file."), [&](){
- ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode();
-
- if (node.isValid()) {
- m_globalAnnotationEditor.setModelNode(node);
- m_globalAnnotationEditor.showWidget();
- }
- });
-
viewManager().enableWidgets();
@@ -497,6 +515,32 @@ void DesignModeWidget::toolBarOnGoForwardClicked()
}
}
+bool DesignModeWidget::canGoForward()
+{
+ return m_canGoForward;
+}
+
+bool DesignModeWidget::canGoBack()
+{
+ return m_canGoBack;
+}
+
+ADS::DockManager *DesignModeWidget::dockManager() const
+{
+ return m_dockManager;
+}
+
+GlobalAnnotationEditor &DesignModeWidget::globalAnnotationEditor()
+{
+ return m_globalAnnotationEditor;
+}
+
+void DesignModeWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ event->accept();
+ event->setDropAction(Qt::IgnoreAction);
+}
+
DesignDocument *DesignModeWidget::currentDesignDocument() const
{
return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument();
@@ -512,11 +556,14 @@ void DesignModeWidget::setupNavigatorHistory(Core::IEditor *editor)
if (!m_keepNavigatorHistory)
addNavigatorHistoryEntry(editor->document()->filePath());
- const bool canGoBack = m_navigatorHistoryCounter > 0;
- const bool canGoForward = m_navigatorHistoryCounter < (m_navigatorHistory.size() - 1);
- m_toolBar->setCanGoBack(canGoBack);
- m_toolBar->setCanGoForward(canGoForward);
- m_toolBar->setCurrentEditor(editor);
+ m_canGoBack = m_navigatorHistoryCounter > 0;
+ m_canGoForward = m_navigatorHistoryCounter < (m_navigatorHistory.size() - 1);
+ m_toolBar->setCanGoBack(m_canGoBack);
+ m_toolBar->setCanGoForward(m_canGoForward);
+ if (!ToolBar::isVisible())
+ m_toolBar->setCurrentEditor(editor);
+
+ emit navigationHistoryChanged();
}
void DesignModeWidget::addNavigatorHistoryEntry(const Utils::FilePath &fileName)
@@ -573,6 +620,8 @@ void DesignModeWidget::initialize()
}
m_initStatus = Initialized;
+
+ emit initialized();
}
} // namespace Internal
diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h
index ec0e916d53..1dfa79a0b3 100644
--- a/src/plugins/qmldesigner/designmodewidget.h
+++ b/src/plugins/qmldesigner/designmodewidget.h
@@ -64,12 +64,26 @@ public:
static QWidget *createProjectExplorerWidget(QWidget *parent);
-private:
- enum InitializeStatus { NotInitialized, Initializing, Initialized };
-
void toolBarOnGoBackClicked();
void toolBarOnGoForwardClicked();
+ bool canGoForward();
+ bool canGoBack();
+
+ ADS::DockManager *dockManager() const;
+
+ GlobalAnnotationEditor &globalAnnotationEditor();
+
+signals:
+ void navigationHistoryChanged();
+ void initialized();
+
+protected:
+ virtual void dragEnterEvent(QDragEnterEvent *event) override;
+
+private:
+ enum InitializeStatus { NotInitialized, Initializing, Initialized };
+
void setup();
bool isInNodeDefinition(int nodeOffset, int nodeLength, int cursorPos) const;
QmlDesigner::ModelNode nodeForPosition(int cursorPos) const;
@@ -96,6 +110,9 @@ private:
ADS::DockManager *m_dockManager = nullptr;
ADS::DockWidget *m_outputPaneDockWidget = nullptr;
GlobalAnnotationEditor m_globalAnnotationEditor;
+
+ bool m_canGoForward = false;
+ bool m_canGoBack = false;
};
} // namespace Internal
diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp
index d2bf9eac51..e3460622e7 100644
--- a/src/plugins/qmldesigner/documentmanager.cpp
+++ b/src/plugins/qmldesigner/documentmanager.cpp
@@ -21,16 +21,18 @@
#include <coreplugin/vcsmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
-#include <projectexplorer/projectnodes.h>
+
#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <qmakeprojectmanager/qmakenodes.h>
#include <qmakeprojectmanager/qmakeproject.h>
#include <QMessageBox>
+using namespace ProjectExplorer;
using namespace Utils;
namespace QmlDesigner {
@@ -335,11 +337,11 @@ Utils::FilePath DocumentManager::currentProjectDirPath()
Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
if (project)
return project->projectDirectory();
- const QList projects = ProjectExplorer::SessionManager::projects();
+ const QList projects = ProjectExplorer::ProjectManager::projects();
for (auto p : projects) {
if (qmlFileName.startsWith(p->projectDirectory().toString()))
return p->projectDirectory();
@@ -402,7 +404,7 @@ void DocumentManager::findPathToIsoProFile(bool *iconResourceFileAlreadyExists,
QString *resourceFileProPath, const QString &isoIconsQrcFile)
{
Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(qmlFileName)->parentFolderNode();
ProjectExplorer::Node *iconQrcFileNode = nullptr;
@@ -412,8 +414,10 @@ void DocumentManager::findPathToIsoProFile(bool *iconResourceFileAlreadyExists,
if (node->isVirtualFolderType() && node->displayName() == "Resources") {
ProjectExplorer::FolderNode *virtualFolderNode = node->asFolderNode();
if (QTC_GUARD(virtualFolderNode)) {
- for (int subFolderIndex = 0; subFolderIndex < virtualFolderNode->folderNodes().size() && !iconQrcFileNode; ++subFolderIndex) {
- ProjectExplorer::FolderNode *subFolderNode = virtualFolderNode->folderNodes().at(subFolderIndex);
+ QList<FolderNode *> folderNodes;
+ virtualFolderNode->forEachFolderNode([&](FolderNode *fn) { folderNodes.append(fn); });
+ for (int subFolderIndex = 0; subFolderIndex < folderNodes.size() && !iconQrcFileNode; ++subFolderIndex) {
+ ProjectExplorer::FolderNode *subFolderNode = folderNodes.at(subFolderIndex);
qCDebug(documentManagerLog) << "Checking if" << subFolderNode->displayName() << "("
<< subFolderNode << ") is" << isoIconsQrcFile;
@@ -492,7 +496,7 @@ bool DocumentManager::belongsToQmakeProject()
return false;
Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName);
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(qmlFileName);
if (!project)
return false;
diff --git a/src/plugins/qmldesigner/dynamiclicensecheck.h b/src/plugins/qmldesigner/dynamiclicensecheck.h
index c26267c881..a3ddf6c192 100644
--- a/src/plugins/qmldesigner/dynamiclicensecheck.h
+++ b/src/plugins/qmldesigner/dynamiclicensecheck.h
@@ -32,6 +32,26 @@ inline ExtensionSystem::IPlugin *licenseCheckerPlugin()
return pluginSpec->plugin();
return nullptr;
}
+
+inline ExtensionSystem::IPlugin *dsLicenseCheckerPlugin()
+{
+ const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault(
+ ExtensionSystem::PluginManager::plugins(),
+ Utils::equal(&ExtensionSystem::PluginSpec::name, QString("DSLicense")));
+
+ if (pluginSpec)
+ return pluginSpec->plugin();
+ return nullptr;
+}
+
+inline bool dsLicenseCheckerPluginExists()
+{
+ const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault(
+ ExtensionSystem::PluginManager::plugins(),
+ Utils::equal(&ExtensionSystem::PluginSpec::name, QString("DSLicense")));
+
+ return pluginSpec;
+}
} // namespace Internal
inline FoundLicense checkLicense()
@@ -94,4 +114,23 @@ inline QString licenseeEmail()
return {};
}
+inline bool checkEnterpriseLicense()
+{
+ if (auto plugin = Internal::dsLicenseCheckerPlugin()) {
+ bool retVal = false;
+ bool success = QMetaObject::invokeMethod(plugin,
+ "checkEnterpriseLicense",
+ Qt::DirectConnection,
+ Q_RETURN_ARG(bool, retVal));
+
+ if (success)
+ return retVal;
+ }
+
+ if (Internal::dsLicenseCheckerPluginExists())
+ return false;
+
+ return true;
+}
+
} // namespace Utils
diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp
index 1901a8e2ea..439e124b70 100644
--- a/src/plugins/qmldesigner/generateresource.cpp
+++ b/src/plugins/qmldesigner/generateresource.cpp
@@ -12,8 +12,8 @@
#include <coreplugin/messagemanager.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlprojectmanagerconstants.h>
@@ -22,9 +22,9 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-#include <utils/qtcprocess.h>
#include <QAction>
#include <QByteArray>
@@ -177,7 +177,7 @@ QList<GenerateResource::ResourceFile> getFilesFromQrc(QFile *file, bool inProjec
static bool runRcc(const CommandLine &command, const FilePath &workingDir,
const QString &resourceFile)
{
- Utils::QtcProcess rccProcess;
+ Utils::Process rccProcess;
rccProcess.setWorkingDirectory(workingDir);
rccProcess.setCommand(command);
rccProcess.start();
@@ -223,16 +223,16 @@ void GenerateResource::generateMenuEntry(QObject *parent)
auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
"Generate QRC Resource File..."),
parent);
- action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
+ action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
// todo make it more intelligent when it gets enabled
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
- action->setEnabled(ProjectExplorer::SessionManager::startupProject());
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
+ action->setEnabled(ProjectExplorer::ProjectManager::startupProject());
});
Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource");
QObject::connect(action, &QAction::triggered, [] () {
- auto currentProject = ProjectExplorer::SessionManager::startupProject();
+ auto currentProject = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(currentProject, return);
const FilePath projectPath = currentProject->projectFilePath().parentDir();
@@ -331,16 +331,16 @@ void GenerateResource::generateMenuEntry(QObject *parent)
auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
"Generate Deployable Package..."),
parent);
- rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, [rccAction]() {
- rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject());
+ rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr);
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, [rccAction]() {
+ rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject());
});
Core::Command *cmd2 = Core::ActionManager::registerAction(rccAction,
"QmlProject.CreateRCCResource");
QObject::connect(rccAction, &QAction::triggered, []() {
- auto currentProject = ProjectExplorer::SessionManager::startupProject();
+ auto currentProject = ProjectExplorer::ProjectManager::startupProject();
QTC_ASSERT(currentProject, return);
const FilePath projectPath = currentProject->projectFilePath().parentDir();
diff --git a/src/plugins/qmldesigner/openuiqmlfiledialog.cpp b/src/plugins/qmldesigner/openuiqmlfiledialog.cpp
index 6c1dcf132c..eacb81aa00 100644
--- a/src/plugins/qmldesigner/openuiqmlfiledialog.cpp
+++ b/src/plugins/qmldesigner/openuiqmlfiledialog.cpp
@@ -30,7 +30,7 @@ OpenUiQmlFileDialog::OpenUiQmlFileDialog(QWidget *parent) :
m_listWidget = new QListWidget;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
tr("You are opening a .qml file in the designer. Do you want to open a .ui.qml file instead?"),
diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
index 8ce2712817..7d7957753c 100644
--- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
+++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp
@@ -33,22 +33,6 @@ void filterOutQtBaseImportPath(QStringList *stringList)
&& !dir.entryInfoList(QStringList("QtTest"), QDir::Dirs).isEmpty();
});
}
-
-Utils::FilePath pathForBinPuppet(ProjectExplorer::Target *target)
-{
- if (!target || !target->kit())
- return {};
-
- QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
-
- if (currentQtVersion)
- return currentQtVersion->binPath()
- .pathAppended(QString{"qml2puppet-"} + Core::Constants::IDE_VERSION_LONG)
- .withExecutableSuffix();
-
- return {};
-}
-
} // namespace
QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const
@@ -74,9 +58,12 @@ QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const
}
QProcessEnvironment PuppetEnvironmentBuilder::createEnvironment(
- ProjectExplorer::Target *target, const DesignerSettings &designerSettings, const Model &model)
+ ProjectExplorer::Target *target,
+ const DesignerSettings &designerSettings,
+ const Model &model,
+ const Utils::FilePath &qmlPuppetPath)
{
- PuppetEnvironmentBuilder builder{target, designerSettings, model};
+ PuppetEnvironmentBuilder builder{target, designerSettings, model, qmlPuppetPath};
return builder.processEnvironment();
}
@@ -244,7 +231,7 @@ void PuppetEnvironmentBuilder::addCustomFileSelectors() const
PuppetType PuppetEnvironmentBuilder::determinePuppetType() const
{
if (m_target && m_target->kit() && m_target->kit()->isValid()) {
- if (pathForBinPuppet(m_target).isExecutableFile())
+ if (m_qmlPuppetPath.isExecutableFile())
return PuppetType::Kit;
}
diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.h b/src/plugins/qmldesigner/puppetenvironmentbuilder.h
index 22629db82c..e3ef33a721 100644
--- a/src/plugins/qmldesigner/puppetenvironmentbuilder.h
+++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.h
@@ -20,17 +20,20 @@ class PuppetEnvironmentBuilder
public:
PuppetEnvironmentBuilder(ProjectExplorer::Target *target,
const class DesignerSettings &designerSettings,
- const class Model &model)
+ const class Model &model,
+ const Utils::FilePath &qmlPuppetPath)
: m_target(target)
, m_designerSettings(designerSettings)
, m_model(model)
+ , m_qmlPuppetPath(qmlPuppetPath)
{}
QProcessEnvironment processEnvironment() const;
static QProcessEnvironment createEnvironment(ProjectExplorer::Target *target,
const class DesignerSettings &designerSettings,
- const class Model &model);
+ const class Model &model,
+ const Utils::FilePath &qmlPuppetPath);
private:
PuppetType determinePuppetType() const;
@@ -53,6 +56,7 @@ private:
const Model &m_model;
mutable PuppetType m_availablePuppetType = {};
mutable Utils::Environment m_environment;
+ Utils::FilePath m_qmlPuppetPath;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h
index 2b6953ca1f..1803918750 100644
--- a/src/plugins/qmldesigner/qmldesignerconstants.h
+++ b/src/plugins/qmldesigner/qmldesignerconstants.h
@@ -17,6 +17,7 @@ const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D";
const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator";
const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor";
const char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser";
+const char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary";
// Special context for preview menu, shared b/w designer and text editor
const char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu";
@@ -56,6 +57,7 @@ const char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] = "QmlDesigner.Editor3D.TogglePar
const char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView";
const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle";
const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay";
+const char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker";
const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart";
const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles";
const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions";
@@ -123,13 +125,42 @@ const char EVENT_NAVIGATORVIEW_TIME[] = "navigatorView";
const char EVENT_DESIGNMODE_TIME[] = "designMode";
const char EVENT_MATERIALEDITOR_TIME[] = "materialEditor";
const char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser";
+const char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary";
const char EVENT_INSIGHT_TIME[] = "insight";
+const char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange";
+const char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings";
+const char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject";
+const char EVENT_TOOLBAR_GO_FORWARD[] = "ToolBarGoForward";
+const char EVENT_TOOLBAR_GO_BACKWARD[] = "ToolBarGoBackward";
+const char EVENT_TOOLBAR_OPEN_FILE[] = "ToolBarOpenFile";
+const char EVENT_TOOLBAR_CLOSE_DOCUMENT[] = "ToolBarCloseCurrentDocument";
+const char EVENT_TOOLBAR_SHARE_APPLICATION[] = "ToolBarShareApplication";
+const char EVENT_TOOLBAR_SET_CURRENT_WORKSPACE[] = "ToolBarSetCurrentWorkspace";
+const char EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION[] = "ToolBarEditGlobalAnnotation";
+const char EVENT_STATUSBAR_SHOW_ZOOM[] = "StatusBarShowZoomMenu";
+const char EVENT_STATUSBAR_SET_STYLE[] = "StatusBarSetCurrentStyle";
const char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal";
// Copy/Paste Headers
const char HEADER_3DPASTE_CONTENT[] = "// __QmlDesigner.Editor3D.Paste__ \n";
+const char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary";
+const char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary";
+const char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator";
+const char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary";
+const char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser";
+const char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor";
+const char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor";
+const char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor";
+const char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor";
+const char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar";
+const char OBJECT_NAME_STATUSBAR[] = "QQuickWidgetStatusbar";
+const char OBJECT_NAME_TOP_FEEDBACK[] = "QQuickWidgetQDSFeedback";
+const char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog";
+const char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen";
+const char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage";
+
namespace Internal {
enum { debug = 0 };
}
diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
index e332f1d49c..8282ea419a 100644
--- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
+++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
@@ -9,9 +9,10 @@
#include <edit3d/edit3dviewconfig.h>
#include <itemlibraryimport.h>
#include <projectexplorer/kit.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <puppetenvironmentbuilder.h>
+#include <qmlpuppetpaths.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
@@ -109,7 +110,7 @@ QString ExternalDependencies::itemLibraryImportUserComponentsTitle() const
bool ExternalDependencies::isQt6Import() const
{
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
if (target) {
QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (currentQtVersion && currentQtVersion->isValid()) {
@@ -122,7 +123,7 @@ bool ExternalDependencies::isQt6Import() const
bool ExternalDependencies::hasStartupTarget() const
{
- auto target = ProjectExplorer::SessionManager::startupTarget();
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
if (target) {
QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (currentQtVersion && currentQtVersion->isValid()) {
@@ -135,54 +136,6 @@ bool ExternalDependencies::hasStartupTarget() const
namespace {
-Utils::FilePath qmlPuppetExecutablePath(const Utils::FilePath &workingDirectory)
-{
- return workingDirectory.pathAppended(QString{"qml2puppet-"} + Core::Constants::IDE_VERSION_LONG)
- .withExecutableSuffix();
-}
-
-Utils::FilePath qmlPuppetFallbackDirectory(const DesignerSettings &settings)
-{
- auto puppetFallbackDirectory = Utils::FilePath::fromString(
- settings.value(DesignerSettingsKey::PUPPET_DEFAULT_DIRECTORY).toString());
- if (puppetFallbackDirectory.isEmpty() || !puppetFallbackDirectory.exists())
- return Core::ICore::libexecPath();
- return puppetFallbackDirectory;
-}
-
-std::pair<Utils::FilePath, Utils::FilePath> qmlPuppetFallbackPaths(const DesignerSettings &settings)
-{
- auto workingDirectory = qmlPuppetFallbackDirectory(settings);
-
- return {workingDirectory, qmlPuppetExecutablePath(workingDirectory)};
-}
-
-std::pair<Utils::FilePath, Utils::FilePath> pathsForKitPuppet(ProjectExplorer::Target *target)
-{
- if (!target || !target->kit())
- return {};
-
- QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
-
- if (currentQtVersion) {
- auto path = currentQtVersion->binPath();
- return {path, qmlPuppetExecutablePath(path)};
- }
-
- return {};
-}
-
-std::pair<Utils::FilePath, Utils::FilePath> qmlPuppetPaths(ProjectExplorer::Target *target,
- const DesignerSettings &settings)
-{
- auto [workingDirectoryPath, puppetPath] = pathsForKitPuppet(target);
-
- if (workingDirectoryPath.isEmpty() || !puppetPath.exists())
- return qmlPuppetFallbackPaths(settings);
-
- return {workingDirectoryPath, puppetPath};
-}
-
bool isForcingFreeType(ProjectExplorer::Target *target)
{
if (Utils::HostOsInfo::isWindowsHost() && target) {
@@ -208,12 +161,12 @@ QString createFreeTypeOption(ProjectExplorer::Target *target)
PuppetStartData ExternalDependencies::puppetStartData(const Model &model) const
{
PuppetStartData data;
- auto target = ProjectExplorer::SessionManager::startupTarget();
- auto [workingDirectory, puppetPath] = qmlPuppetPaths(target, m_designerSettings);
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
+ auto [workingDirectory, puppetPath] = QmlPuppetPaths::qmlPuppetPaths(target, m_designerSettings);
data.puppetPath = puppetPath.toString();
data.workingDirectoryPath = workingDirectory.toString();
- data.environment = PuppetEnvironmentBuilder::createEnvironment(target, m_designerSettings, model);
+ data.environment = PuppetEnvironmentBuilder::createEnvironment(target, m_designerSettings, model, qmlPuppetPath());
data.debugPuppet = m_designerSettings.value(DesignerSettingsKey::DEBUG_PUPPET).toString();
data.freeTypeOption = createFreeTypeOption(target);
data.forwardOutput = m_designerSettings.value(DesignerSettingsKey::FORWARD_PUPPET_OUTPUT).toString();
@@ -226,4 +179,11 @@ bool ExternalDependencies::instantQmlTextUpdate() const
return false;
}
+Utils::FilePath ExternalDependencies::qmlPuppetPath() const
+{
+ auto target = ProjectExplorer::ProjectManager::startupTarget();
+ auto [workingDirectory, puppetPath] = QmlPuppetPaths::qmlPuppetPaths(target, m_designerSettings);
+ return puppetPath;
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h
index 84a532bb26..3e57847ce9 100644
--- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h
+++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h
@@ -35,6 +35,7 @@ public:
bool hasStartupTarget() const override;
PuppetStartData puppetStartData(const class Model &model) const override;
bool instantQmlTextUpdate() const override;
+ Utils::FilePath qmlPuppetPath() const override;
private:
const DesignerSettings &m_designerSettings;
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 3fe639b5d8..039528475a 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -3,19 +3,21 @@
#include "qmldesignerplugin.h"
#include "qmldesignertr.h"
+
#include "coreplugin/iwizardfactory.h"
#include "designmodecontext.h"
#include "designmodewidget.h"
#include "dynamiclicensecheck.h"
#include "exception.h"
#include "generateresource.h"
-#include "nodeinstanceview.h"
#include "openuiqmlfiledialog.h"
#include "qmldesignerconstants.h"
#include "qmldesignerexternaldependencies.h"
#include "qmldesignerprojectmanager.h"
#include "quick2propertyeditorview.h"
#include "settingspage.h"
+#include "shortcutmanager.h"
+#include "toolbar.h"
#include <colortool/colortool.h>
#include <connectionview.h>
@@ -38,9 +40,10 @@
#include <qmlprojectmanager/qmlproject.h>
+#include <app/app_version.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
-#include <coreplugin/coreconstants.h>
#include <coreplugin/coreplugintr.h>
#include <coreplugin/designmode.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -54,10 +57,11 @@
#include <extensionsystem/pluginspec.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
#include <sqlitelibraryinitializer.h>
+#include <qmldesignerbase/qmldesignerbaseplugin.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
@@ -67,6 +71,7 @@
#include <QApplication>
#include <QDebug>
#include <QProcessEnvironment>
+#include <QQuickItem>
#include <QScreen>
#include <QTimer>
#include <QWindow>
@@ -130,8 +135,7 @@ QtQuickDesignerFactory::QtQuickDesignerFactory()
class QmlDesignerPluginPrivate
{
public:
- DesignerSettings settings{Core::ICore::instance()->settings()};
- ExternalDependencies externalDependencies{settings};
+ ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()};
QmlDesignerProjectManager projectManager{externalDependencies};
ViewManager viewManager{projectManager.asynchronousImageCache(), externalDependencies};
DocumentManager documentManager{projectManager, externalDependencies};
@@ -231,6 +235,15 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
Sqlite::LibraryInitializer::initialize();
QDir{}.mkpath(Core::ICore::cacheResourcePath().toString());
+ QAction *action = new QAction(tr("Give Feedback..."), this);
+ Core::Command *cmd = Core::ActionManager::registerAction(action, "Help.GiveFeedback");
+ Core::ActionManager::actionContainer(Core::Constants::M_HELP)
+ ->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
+
+ connect(action, &QAction::triggered, this, [this] {
+ lauchFeedbackPopup(Core::Constants::IDE_DISPLAY_NAME);
+ });
+
if (!Utils::HostOsInfo::canCreateOpenGLContext(errorMessage))
return false;
d = new QmlDesignerPluginPrivate;
@@ -247,7 +260,7 @@ 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();
- if (QmlDesigner::checkLicense() == QmlDesigner::FoundLicense::enterprise)
+ if (checkEnterpriseLicense())
Core::IWizardFactory::registerFeatureProvider(new EnterpriseFeatureProvider);
Exception::setWarnAboutException(!QmlDesignerPlugin::instance()
->settings()
@@ -259,6 +272,11 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
Core::AsynchronousMessageBox::warning(composedTitle, description.toString());
});
+ if (QmlProjectManager::QmlProject::isQtDesignStudio()) {
+ ToolBar::create();
+ ToolBar::createStatusBar();
+ }
+
return true;
}
@@ -274,29 +292,29 @@ bool QmlDesignerPlugin::delayedInitialize()
MetaInfo::setPluginPaths(pluginPaths);
d->viewManager.registerView(
- std::make_unique<QmlDesigner::Internal::ConnectionView>(d->externalDependencies));
+ std::make_unique<ConnectionView>(d->externalDependencies));
auto timelineView = d->viewManager.registerView(
- std::make_unique<QmlDesigner::TimelineView>(d->externalDependencies));
+ std::make_unique<TimelineView>(d->externalDependencies));
timelineView->registerActions();
d->viewManager.registerView(
- std::make_unique<QmlDesigner::CurveEditorView>(d->externalDependencies));
+ std::make_unique<CurveEditorView>(d->externalDependencies));
auto eventlistView = d->viewManager.registerView(
- std::make_unique<QmlDesigner::EventListPluginView>(d->externalDependencies));
+ std::make_unique<EventListPluginView>(d->externalDependencies));
eventlistView->registerActions();
auto transitionEditorView = d->viewManager.registerView(
- std::make_unique<QmlDesigner::TransitionEditorView>(d->externalDependencies));
+ std::make_unique<TransitionEditorView>(d->externalDependencies));
transitionEditorView->registerActions();
- d->viewManager.registerFormEditorTool(std::make_unique<QmlDesigner::SourceTool>());
- d->viewManager.registerFormEditorTool(std::make_unique<QmlDesigner::ColorTool>());
- d->viewManager.registerFormEditorTool(std::make_unique<QmlDesigner::TextTool>());
+ d->viewManager.registerFormEditorTool(std::make_unique<SourceTool>());
+ d->viewManager.registerFormEditorTool(std::make_unique<ColorTool>());
+ d->viewManager.registerFormEditorTool(std::make_unique<TextTool>());
d->viewManager.registerFormEditorTool(
- std::make_unique<QmlDesigner::PathTool>(d->externalDependencies));
- d->viewManager.registerFormEditorTool(std::make_unique<QmlDesigner::TransitionTool>());
+ std::make_unique<PathTool>(d->externalDependencies));
+ d->viewManager.registerFormEditorTool(std::make_unique<TransitionTool>());
if (QmlProjectManager::QmlProject::isQtDesignStudio()) {
d->mainWidget.initialize();
@@ -347,7 +365,7 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown()
static QStringList allUiQmlFilesforCurrentProject(const Utils::FilePath &fileName)
{
QStringList list;
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName);
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName);
if (currentProject) {
const QList<Utils::FilePath> fileNames = currentProject->files(ProjectExplorer::Project::SourceFiles);
@@ -363,7 +381,7 @@ static QStringList allUiQmlFilesforCurrentProject(const Utils::FilePath &fileNam
static QString projectPath(const Utils::FilePath &fileName)
{
QString path;
- ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName);
+ ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName);
if (currentProject)
path = currentProject->projectDirectory().toString();
@@ -380,17 +398,18 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget)
Core::Context qmlDesignerEditor3dContext(Constants::C_QMLEDITOR3D);
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
+ Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
context->context().add(qmlDesignerMainContext);
context->context().add(qmlDesignerFormEditorContext);
context->context().add(qmlDesignerEditor3dContext);
context->context().add(qmlDesignerNavigatorContext);
context->context().add(qmlDesignerMaterialBrowserContext);
+ context->context().add(qmlDesignerAssetsLibraryContext);
context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext,
- qmlDesignerEditor3dContext, qmlDesignerNavigatorContext,
- qmlDesignerMaterialBrowserContext);
+ qmlDesignerEditor3dContext, qmlDesignerNavigatorContext);
const QStringList mimeTypes = { QmlJSTools::Constants::QML_MIMETYPE,
QmlJSTools::Constants::QMLUI_MIMETYPE };
@@ -476,7 +495,7 @@ void QmlDesignerPlugin::hideDesigner()
d->shortCutManager.disconnectUndoActions(currentDesignDocument());
d->documentManager.setCurrentDesignDocument(nullptr);
d->shortCutManager.updateUndoActions(nullptr);
- emitUsageStatisticsTime(QmlDesigner::Constants::EVENT_DESIGNMODE_TIME, m_usageTimer.elapsed());
+ emitUsageStatisticsTime(Constants::EVENT_DESIGNMODE_TIME, m_usageTimer.elapsed());
}
void QmlDesignerPlugin::changeEditor()
@@ -576,6 +595,15 @@ void QmlDesignerPlugin::resetModelSelection()
rewriterView()->setSelectedModelNodes(QList<ModelNode>());
}
+QString QmlDesignerPlugin::identiferToDisplayString(const QString &identifier)
+{
+ for (AbstractView *view : viewManager().views())
+ if (view->widgetInfo().uniqueId.toLower() == identifier.toLower())
+ return view->widgetInfo().feedbackDisplayName;
+
+ return identifier;
+}
+
RewriterView *QmlDesignerPlugin::rewriterView() const
{
return currentDesignDocument()->rewriterView();
@@ -701,8 +729,61 @@ void QmlDesignerPlugin::trackWidgetFocusTime(QWidget *widget, const QString &ide
});
}
+void QmlDesignerPlugin::lauchFeedbackPopup(const QString &identifier)
+{
+ m_feedbackWidget = new QQuickWidget(Core::ICore::dialogParent());
+ m_feedbackWidget->setObjectName(Constants::OBJECT_NAME_TOP_FEEDBACK);
+
+ const QString qmlPath = Core::ICore::resourcePath("qmldesigner/feedback/FeedbackPopup.qml").toString();
+
+ m_feedbackWidget->setSource(QUrl::fromLocalFile(qmlPath));
+ if (!m_feedbackWidget->errors().isEmpty()) {
+ qDebug() << qmlPath;
+ qDebug() << m_feedbackWidget->errors().first().toString();
+ }
+ m_feedbackWidget->setWindowModality(Qt::ApplicationModal);
+ if (Utils::HostOsInfo::isMacHost())
+ m_feedbackWidget->setWindowFlags(Qt::Dialog);
+ else
+ m_feedbackWidget->setWindowFlags(Qt::SplashScreen);
+ m_feedbackWidget->setAttribute(Qt::WA_DeleteOnClose);
+
+ QQuickItem *root = m_feedbackWidget->rootObject();
+
+ QTC_ASSERT(root, return );
+
+ QObject *title = root->findChild<QObject *>("title");
+ QString name = QmlDesignerPlugin::tr("Enjoying the %1?").arg(identiferToDisplayString(identifier));
+ title->setProperty("text", name);
+ root->setProperty("identifier", identifier);
+
+ connect(root, SIGNAL(closeClicked()), this, SLOT(closeFeedbackPopup()));
+
+ QObject::connect(root,
+ SIGNAL(submitFeedback(QString, int)),
+ this,
+ SLOT(handleFeedback(QString, int)));
+
+ m_feedbackWidget->show();
+}
+
+void QmlDesignerPlugin::handleFeedback(const QString &feedback, int rating)
+{
+ const QString identifier = sender()->property("identifier").toString();
+ emit usageStatisticsInsertFeedback(identifier, feedback, rating);
+}
+
+void QmlDesignerPlugin::closeFeedbackPopup()
+{
+ if (m_feedbackWidget) {
+ m_feedbackWidget->deleteLater();
+ m_feedbackWidget = nullptr;
+ }
+}
+
void QmlDesignerPlugin::emitUsageStatisticsTime(const QString &identifier, int elapsed)
{
+
QTC_ASSERT(instance(), return);
emit instance()->usageStatisticsUsageTimer(normalizeIdentifier(identifier), elapsed);
}
@@ -744,7 +825,7 @@ ExternalDependenciesInterface &QmlDesignerPlugin::externalDependenciesForPluginI
DesignerSettings &QmlDesignerPlugin::settings()
{
- return instance()->d->settings;
+ return QmlDesignerBasePlugin::settings();
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h
index 2c9531b991..78bf02ac04 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.h
+++ b/src/plugins/qmldesigner/qmldesignerplugin.h
@@ -5,7 +5,6 @@
#include "documentmanager.h"
#include "qmldesigner_global.h"
-#include "shortcutmanager.h"
#include <designersettings.h>
#include <viewmanager.h>
@@ -13,10 +12,12 @@
#include <extensionsystem/iplugin.h>
+#include <qmldesignerbase/qmldesignerbaseplugin.h>
#include <QElapsedTimer>
QT_FORWARD_DECLARE_CLASS(QQmlEngine)
+QT_FORWARD_DECLARE_CLASS(QQuickWidget)
namespace Core {
class IEditor;
@@ -85,8 +86,14 @@ public:
signals:
void usageStatisticsNotifier(const QString &identifier);
void usageStatisticsUsageTimer(const QString &identifier, int elapsed);
+ void usageStatisticsInsertFeedback(const QString &identifier, const QString &feedback, int rating);
void assetChanged(const QString &assetPath);
+private slots:
+ void closeFeedbackPopup();
+ void lauchFeedbackPopup(const QString &identifier);
+ void handleFeedback(const QString &feedback, int rating);
+
private: // functions
void integrateIntoQtCreator(QWidget *modeWidget);
void showDesigner();
@@ -97,13 +104,17 @@ private: // functions
void activateAutoSynchronization();
void deactivateAutoSynchronization();
void resetModelSelection();
+ QString identiferToDisplayString(const QString &identifier);
+
RewriterView *rewriterView() const;
Model *currentModel() const;
+ QQuickWidget *m_feedbackWidget = nullptr;
private: // variables
QmlDesignerPluginPrivate *d = nullptr;
static QmlDesignerPlugin *m_instance;
QElapsedTimer m_usageTimer;
+ StudioConfigSettingsPage m_settingsPage;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
index e708e8ebf1..a80b3148b3 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp
@@ -6,12 +6,13 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectstorage/filestatuscache.h>
#include <projectstorage/filesystem.h>
#include <projectstorage/nonlockingmutex.h>
#include <projectstorage/projectstorage.h>
+#include <projectstorage/projectstoragepathwatcher.h>
#include <projectstorage/projectstorageupdater.h>
#include <projectstorage/qmldocumentparser.h>
#include <projectstorage/qmltypesparser.h>
@@ -31,10 +32,14 @@
#include <imagecache/imagecachegenerator.h>
#include <imagecache/imagecachestorage.h>
#include <imagecache/meshimagecachecollector.h>
+#include <imagecache/textureimagecachecollector.h>
#include <imagecache/timestampprovider.h>
+#include <utils/asset.h>
+
#include <coreplugin/icore.h>
+#include <QFileSystemWatcher>
#include <QQmlEngine>
namespace QmlDesigner {
@@ -49,11 +54,16 @@ ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project)
return {};
}
-QString defaultImagePath()
+QString previewDefaultImagePath()
{
return Core::ICore::resourcePath("qmldesigner/welcomepage/images/newThumbnail.png").toString();
}
+QString previewBrokenImagePath()
+{
+ return Core::ICore::resourcePath("qmldesigner/welcomepage/images/noPreview.png").toString();
+}
+
::QmlProjectManager::QmlBuildSystem *getQmlBuildSystem(::ProjectExplorer::Target *target)
{
return qobject_cast<::QmlProjectManager::QmlBuildSystem *>(target->buildSystem());
@@ -76,8 +86,9 @@ public:
}
};
-auto makeCollecterDispatcherChain(ImageCacheCollector &nodeInstanceCollector,
- MeshImageCacheCollector &meshImageCollector)
+auto makeCollectorDispatcherChain(ImageCacheCollector &nodeInstanceCollector,
+ MeshImageCacheCollector &meshImageCollector,
+ TextureImageCacheCollector &textureImageCollector)
{
return std::make_tuple(
std::make_pair([](Utils::SmallStringView filePath,
@@ -91,8 +102,15 @@ auto makeCollecterDispatcherChain(ImageCacheCollector &nodeInstanceCollector,
[[maybe_unused]] const QmlDesigner::ImageCache::AuxiliaryData &auxiliaryData) {
return filePath.endsWith(".mesh") || filePath.startsWith("#");
},
- &meshImageCollector));
-}
+ &meshImageCollector),
+ std::make_pair(
+ [](Utils::SmallStringView filePath,
+ [[maybe_unused]] Utils::SmallStringView state,
+ [[maybe_unused]] const QmlDesigner::ImageCache::AuxiliaryData &auxiliaryData) {
+ return Asset{QString(filePath)}.isValidTextureSource();
+ },
+ &textureImageCollector));
+ }
} // namespace
class QmlDesignerProjectManager::ImageCacheData
@@ -111,10 +129,14 @@ public:
ImageCacheStorage<Sqlite::Database> storage{database};
ImageCacheConnectionManager connectionManager;
MeshImageCacheCollector meshImageCollector;
+ TextureImageCacheCollector textureImageCollector;
ImageCacheCollector nodeInstanceCollector;
- ImageCacheDispatchCollector<decltype(makeCollecterDispatcherChain(nodeInstanceCollector,
- meshImageCollector))>
- dispatchCollector{makeCollecterDispatcherChain(nodeInstanceCollector, meshImageCollector)};
+ ImageCacheDispatchCollector<decltype(makeCollectorDispatcherChain(nodeInstanceCollector,
+ meshImageCollector,
+ textureImageCollector))>
+ dispatchCollector{makeCollectorDispatcherChain(nodeInstanceCollector,
+ meshImageCollector,
+ textureImageCollector)};
ImageCacheGenerator generator{dispatchCollector, storage};
TimeStampProvider timeStampProvider;
AsynchronousImageCache asynchronousImageCache{storage, generator, timeStampProvider};
@@ -128,8 +150,10 @@ public:
QSize{300, 300},
QSize{1000, 1000},
externalDependencies,
- ImageCacheCollectorNullImageHandling::DontCaptureNullImage}
- {}
+ ImageCacheCollectorNullImageHandling::CaptureNullImage}
+ {
+ timer.setSingleShot(true);
+ }
public:
Sqlite::Database database{Utils::PathString{
@@ -142,8 +166,10 @@ public:
PreviewTimeStampProvider timeStampProvider;
AsynchronousExplicitImageCache cache{storage};
AsynchronousImageFactory factory{storage, timeStampProvider, collector};
+ QTimer timer;
};
+namespace {
class ProjectStorageData
{
public:
@@ -158,10 +184,27 @@ public:
FileStatusCache fileStatusCache{fileSystem};
QmlDocumentParser qmlDocumentParser{storage, pathCache};
QmlTypesParser qmlTypesParser{pathCache, storage};
- ProjectStorageUpdater updater{
- fileSystem, storage, fileStatusCache, pathCache, qmlDocumentParser, qmlTypesParser};
+ ProjectStoragePathWatcher<QFileSystemWatcher, QTimer, ProjectStorageUpdater::PathCache>
+ pathWatcher{pathCache, fileSystem, &updater};
+ ProjectStorageUpdater updater{fileSystem,
+ storage,
+ fileStatusCache,
+ pathCache,
+ qmlDocumentParser,
+ qmlTypesParser,
+ pathWatcher};
};
+std::unique_ptr<ProjectStorageData> createProjectStorageData(::ProjectExplorer::Project *project)
+{
+ if constexpr (useProjectStorage()) {
+ return std::make_unique<ProjectStorageData>(project);
+ } else {
+ return {};
+ }
+}
+} // namespace
+
class QmlDesignerProjectManager::QmlDesignerProjectManagerProjectData
{
public:
@@ -174,14 +217,14 @@ public:
externalDependencies,
ImageCacheCollectorNullImageHandling::CaptureNullImage}
, factory{storage, timeStampProvider, collector}
- , projectStorageData{project}
+ , projectStorageData{createProjectStorageData(project)}
{}
ImageCacheConnectionManager connectionManager;
ImageCacheCollector collector;
PreviewTimeStampProvider timeStampProvider;
AsynchronousImageFactory factory;
- ProjectStorageData projectStorageData;
+ std::unique_ptr<ProjectStorageData> projectStorageData;
QPointer<::ProjectExplorer::Target> activeTarget;
};
@@ -199,18 +242,18 @@ QmlDesignerProjectManager::QmlDesignerProjectManager(ExternalDependenciesInterfa
QObject::connect(editorManager, &::Core::EditorManager::editorsClosed, [&](const auto &editors) {
editorsClosed(editors);
});
- auto sessionManager = ::ProjectExplorer::SessionManager::instance();
+ auto sessionManager = ::ProjectExplorer::ProjectManager::instance();
QObject::connect(sessionManager,
- &::ProjectExplorer::SessionManager::projectAdded,
+ &::ProjectExplorer::ProjectManager::projectAdded,
[&](auto *project) { projectAdded(project); });
QObject::connect(sessionManager,
- &::ProjectExplorer::SessionManager::aboutToRemoveProject,
+ &::ProjectExplorer::ProjectManager::aboutToRemoveProject,
[&](auto *project) { aboutToRemoveProject(project); });
QObject::connect(sessionManager,
- &::ProjectExplorer::SessionManager::projectRemoved,
+ &::ProjectExplorer::ProjectManager::projectRemoved,
[&](auto *project) { projectRemoved(project); });
- QObject::connect(&m_previewTimer,
+ QObject::connect(&m_previewImageCacheData->timer,
&QTimer::timeout,
this,
&QmlDesignerProjectManager::generatePreview);
@@ -220,8 +263,10 @@ QmlDesignerProjectManager::~QmlDesignerProjectManager() = default;
void QmlDesignerProjectManager::registerPreviewImageProvider(QQmlEngine *engine) const
{
- auto imageProvider = std::make_unique<ExplicitImageCacheImageProvider>(m_previewImageCacheData->cache,
- QImage{defaultImagePath()});
+ auto imageProvider = std::make_unique<ExplicitImageCacheImageProvider>(
+ m_previewImageCacheData->cache,
+ QImage{previewDefaultImagePath()},
+ QImage{previewBrokenImagePath()});
engine->addImageProvider("project_preview", imageProvider.release());
}
@@ -231,19 +276,29 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache()
return imageCacheData()->asynchronousImageCache;
}
+namespace {
+ProjectStorage<Sqlite::Database> *dummyProjectStorage()
+{
+ return nullptr;
+}
+
+} // namespace
+
ProjectStorage<Sqlite::Database> &QmlDesignerProjectManager::projectStorage()
{
- return m_projectData->projectStorageData.storage;
+ if constexpr (useProjectStorage()) {
+ return m_projectData->projectStorageData->storage;
+ } else {
+ return *dummyProjectStorage();
+ }
}
void QmlDesignerProjectManager::editorOpened(::Core::IEditor *) {}
void QmlDesignerProjectManager::currentEditorChanged(::Core::IEditor *)
{
- if (!m_projectData || !m_projectData->activeTarget)
- return;
-
- m_previewTimer.start(10000);
+ using namespace std::chrono_literals;
+ m_previewImageCacheData->timer.start(10s);
}
void QmlDesignerProjectManager::editorsClosed(const QList<::Core::IEditor *> &) {}
@@ -365,26 +420,25 @@ QStringList qmlTypes(::ProjectExplorer::Target *target)
void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project)
{
- if (qEnvironmentVariableIsSet("QDS_ACTIVATE_PROJECT_STORAGE")) {
- m_projectData = std::make_unique<QmlDesignerProjectManagerProjectData>(
- m_previewImageCacheData->storage, project, m_externalDependencies);
- m_projectData->activeTarget = project->activeTarget();
+ m_projectData = std::make_unique<QmlDesignerProjectManagerProjectData>(m_previewImageCacheData->storage,
+ project,
+ m_externalDependencies);
+ m_projectData->activeTarget = project->activeTarget();
- QObject::connect(project, &::ProjectExplorer::Project::fileListChanged, [&]() {
- fileListChanged();
- });
+ QObject::connect(project, &::ProjectExplorer::Project::fileListChanged, [&]() {
+ fileListChanged();
+ });
- QObject::connect(project,
- &::ProjectExplorer::Project::activeTargetChanged,
- [&](auto *target) { activeTargetChanged(target); });
+ QObject::connect(project, &::ProjectExplorer::Project::activeTargetChanged, [&](auto *target) {
+ activeTargetChanged(target);
+ });
- QObject::connect(project,
- &::ProjectExplorer::Project::aboutToRemoveTarget,
- [&](auto *target) { aboutToRemoveTarget(target); });
+ QObject::connect(project, &::ProjectExplorer::Project::aboutToRemoveTarget, [&](auto *target) {
+ aboutToRemoveTarget(target);
+ });
- if (auto target = project->activeTarget(); target)
- activeTargetChanged(target);
- }
+ if (auto target = project->activeTarget(); target)
+ activeTargetChanged(target);
}
void QmlDesignerProjectManager::aboutToRemoveProject(::ProjectExplorer::Project *)
@@ -428,7 +482,7 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache
imageCacheData->nodeInstanceCollector.setTarget(target);
};
- if (auto project = ProjectExplorer::SessionManager::startupProject(); project) {
+ if (auto project = ProjectExplorer::ProjectManager::startupProject(); project) {
// TODO wrap in function in image cache data
m_imageCacheData->meshImageCollector.setTarget(project->activeTarget());
m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget());
@@ -437,8 +491,8 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache
this,
setTargetInImageCache);
}
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
this,
[=](ProjectExplorer::Project *project) {
setTargetInImageCache(activeTarget(project));
@@ -492,11 +546,11 @@ void QmlDesignerProjectManager::projectChanged()
void QmlDesignerProjectManager::update()
{
- if (!m_projectData)
+ if (!m_projectData || !m_projectData->projectStorageData)
return;
- m_projectData->projectStorageData.updater.update(qmlDirs(m_projectData->activeTarget),
- qmlTypes(m_projectData->activeTarget));
+ m_projectData->projectStorageData->updater.update(qmlDirs(m_projectData->activeTarget),
+ qmlTypes(m_projectData->activeTarget));
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h
index 97a14c21ce..e1803ed14d 100644
--- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h
+++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h
@@ -68,7 +68,6 @@ private:
std::unique_ptr<ImageCacheData> m_imageCacheData;
std::unique_ptr<PreviewImageCacheData> m_previewImageCacheData;
std::unique_ptr<QmlDesignerProjectManagerProjectData> m_projectData;
- QTimer m_previewTimer;
ExternalDependenciesInterface &m_externalDependencies;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
index a90c624012..ccd9a97009 100644
--- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
@@ -13,7 +13,7 @@
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
@@ -36,7 +36,7 @@ static void handleAction(const SelectionContext &context)
if (context.view()->isAttached()) {
if (context.toggled()) {
bool skipDeploy = false;
- if (const Target *startupTarget = SessionManager::startupTarget()) {
+ if (const Target *startupTarget = ProjectManager::startupTarget()) {
const Kit *kit = startupTarget->kit();
if (kit
&& (kit->supportedPlatforms().contains(Android::Constants::ANDROID_DEVICE_TYPE)
@@ -70,9 +70,9 @@ QmlPreviewAction::QmlPreviewAction() : ModelNodeAction(livePreviewId,
&SelectionContextFunctors::always)
{
if (!QmlPreviewWidgetPlugin::getPreviewPlugin())
- defaultAction()->setVisible(false);
+ action()->setVisible(false);
- defaultAction()->setCheckable(true);
+ action()->setCheckable(true);
}
void QmlPreviewAction::updateContext()
@@ -80,7 +80,7 @@ void QmlPreviewAction::updateContext()
if (selectionContext().view()->isAttached())
QmlPreviewWidgetPlugin::setQmlFile();
- defaultAction()->setSelectionContext(selectionContext());
+ pureAction()->setSelectionContext(selectionContext());
}
ActionInterface::Type QmlPreviewAction::type() const
@@ -113,7 +113,7 @@ QByteArray ZoomPreviewAction::category() const
QByteArray ZoomPreviewAction::menuId() const
{
- return QByteArray();
+ return "PreviewZoom";
}
int ZoomPreviewAction::priority() const
@@ -241,10 +241,10 @@ QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent)
}
}
};
- connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::startupProjectChanged,
+ connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged,
comboBox, refreshComboBoxFunction);
- if (auto project = SessionManager::startupProject())
+ if (auto project = ProjectManager::startupProject())
refreshComboBoxFunction(project);
// do this after refreshComboBoxFunction so we do not get currentLocaleChanged signals at initialization
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp
index 70d62f71c6..d6cf9f115b 100644
--- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp
@@ -19,6 +19,9 @@
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/icore.h>
+
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/runcontrol.h>
@@ -56,10 +59,26 @@ QmlPreviewWidgetPlugin::QmlPreviewWidgetPlugin()
auto zoomAction = new ZoomPreviewAction;
designerActionManager.addDesignerAction(zoomAction);
- auto separator = new SeperatorDesignerAction(ComponentCoreConstants::qmlPreviewCategory, 0);
+ auto separator = new SeparatorDesignerAction(ComponentCoreConstants::qmlPreviewCategory, 0);
designerActionManager.addDesignerAction(separator);
- m_previewToggleAction = previewAction->defaultAction();
+ m_previewToggleAction = previewAction->action();
+
+ Core::Context globalContext;
+ auto registerCommand = [&globalContext](ActionInterface *action){
+ const QString id = QStringLiteral("QmlPreview.%1").arg(QString::fromLatin1(action->menuId()));
+ Core::Command *cmd = Core::ActionManager::registerAction(action->action(),
+ id.toLatin1().constData(),
+ globalContext);
+
+ cmd->setDefaultKeySequence(action->action()->shortcut());
+ cmd->setDescription(action->action()->toolTip());
+
+ action->action()->setToolTip(cmd->action()->toolTip());
+ action->action()->setShortcut(cmd->action()->shortcut());
+ };
+ // Only register previewAction as others don't have keyboard shortcuts for them
+ registerCommand(previewAction);
if (s_previewPlugin) {
auto fpsAction = new FpsAction;
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-16.png b/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-16.png
new file mode 100644
index 0000000000..6b16d81397
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24.png b/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24.png
new file mode 100644
index 0000000000..0549a84758
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24@2x.png
new file mode 100644
index 0000000000..8876f95ae6
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/ambient-sound-24@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-16.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-16.png
new file mode 100644
index 0000000000..da40bc69a2
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24.png
new file mode 100644
index 0000000000..b3ebdf745b
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24@2x.png
new file mode 100644
index 0000000000..476df8640f
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-engine-24@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-16.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-16.png
new file mode 100644
index 0000000000..ecc583b859
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24.png
new file mode 100644
index 0000000000..ee181f57cc
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24@2x.png
new file mode 100644
index 0000000000..2588277e53
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-listener-24@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-room-16.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-room-16.png
new file mode 100644
index 0000000000..98f245d624
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-room-16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-room-24.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-room-24.png
new file mode 100644
index 0000000000..294d1574ae
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-room-24.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/audio-room-24@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/audio-room-24@2x.png
new file mode 100644
index 0000000000..bef7f80e3e
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/audio-room-24@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/default3d.png b/src/plugins/qmldesigner/qtquickplugin/images/default3d.png
new file mode 100644
index 0000000000..a3b6c7f6f2
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/default3d.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/default3d16.png b/src/plugins/qmldesigner/qtquickplugin/images/default3d16.png
new file mode 100644
index 0000000000..de8906a724
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/default3d16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/default3d@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/default3d@2x.png
new file mode 100644
index 0000000000..7ca04a01ea
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/default3d@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-16.png b/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-16.png
new file mode 100644
index 0000000000..676fe13404
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-16.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24.png b/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24.png
new file mode 100644
index 0000000000..29f7f14db3
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24@2x.png
new file mode 100644
index 0000000000..a518cada63
--- /dev/null
+++ b/src/plugins/qmldesigner/qtquickplugin/images/spatial-audio-24@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
index 1e78c02b77..30d705ad98 100644
--- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
+++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc
@@ -101,5 +101,23 @@
<file>images/timer-16px.png</file>
<file>images/timer-24px.png</file>
<file>images/timer-24px@2x.png</file>
+ <file>images/default3d.png</file>
+ <file>images/default3d@2x.png</file>
+ <file>images/default3d16.png</file>
+ <file>images/ambient-sound-16.png</file>
+ <file>images/ambient-sound-24.png</file>
+ <file>images/ambient-sound-24@2x.png</file>
+ <file>images/audio-engine-16.png</file>
+ <file>images/audio-engine-24.png</file>
+ <file>images/audio-engine-24@2x.png</file>
+ <file>images/audio-listener-16.png</file>
+ <file>images/audio-listener-24.png</file>
+ <file>images/audio-listener-24@2x.png</file>
+ <file>images/audio-room-16.png</file>
+ <file>images/audio-room-24.png</file>
+ <file>images/audio-room-24@2x.png</file>
+ <file>images/spatial-audio-16.png</file>
+ <file>images/spatial-audio-24.png</file>
+ <file>images/spatial-audio-24@2x.png</file>
</qresource>
</RCC>
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index 87b9edcbc0..45e0162128 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -545,7 +545,7 @@ MetaInfo {
ItemLibraryEntry {
name: "Component"
- category: "e.Qt Quick - Component"
+ category: "e.Qt Quick - Instancers"
libraryIcon: ":/qtquickplugin/images/component-icon.png"
version: "1.0"
@@ -565,7 +565,7 @@ MetaInfo {
ItemLibraryEntry {
name: "Component 3D"
- category: "Qt Quick 3D Component"
+ category: "Instancers"
libraryIcon: ":/qtquickplugin/images/component-icon.png"
version: "1.0"
requiredImport: "QtQuick3D"
@@ -580,7 +580,7 @@ MetaInfo {
ItemLibraryEntry {
name: "Loader"
- category: "e.Qt Quick - Component"
+ category: "e.Qt Quick - Instancers"
libraryIcon: ":/qtquickplugin/images/loader-icon.png"
version: "2.0"
Property { name: "width"; type: "int"; value: 200; }
@@ -600,7 +600,7 @@ MetaInfo {
ItemLibraryEntry {
name: "Repeater"
- category: "e.Qt Quick - Component"
+ category: "e.Qt Quick - Instancers"
libraryIcon: ":/qtquickplugin/images/repeater-icon.png"
version: "2.0"
toolTip: qsTr("Creates a number of copies of the same item.")
@@ -689,4 +689,128 @@ MetaInfo {
Property { name: "height"; type: "int"; value: 200; }
}
}
+
+ Type {
+ name: "QtQuick3D.SpatialAudio.AmbientSound"
+ icon: ":/qtquickplugin/images/ambient-sound-16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: false
+ canBeContainer: false
+ }
+
+ ItemLibraryEntry {
+ name: "Ambient Sound"
+ category: "Spatial Audio"
+ libraryIcon: ":/qtquickplugin/images/ambient-sound-24.png"
+ version: "6.0"
+ requiredImport: "QtQuick3D.SpatialAudio"
+ toolTip: qsTr("An ambient background sound.")
+ }
+ }
+
+ Type {
+ name: "QtQuick3D.SpatialAudio.AudioEngine"
+ icon: ":/qtquickplugin/images/audio-engine-16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: false
+ canBeContainer: false
+ }
+
+ ItemLibraryEntry {
+ name: "Audio Engine"
+ category: "Spatial Audio"
+ libraryIcon: ":/qtquickplugin/images/audio-engine-24.png"
+ version: "6.0"
+ requiredImport: "QtQuick3D.SpatialAudio"
+ toolTip: qsTr("Manages sound objects inside a 3D scene.")
+ }
+ }
+
+ Type {
+ name: "QtQuick3D.SpatialAudio.AudioListener"
+ icon: ":/qtquickplugin/images/audio-listener-16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: true
+ }
+
+ ItemLibraryEntry {
+ name: "Audio Listener"
+ category: "Spatial Audio"
+ libraryIcon: ":/qtquickplugin/images/audio-listener-24.png"
+ version: "6.0"
+ requiredImport: "QtQuick3D.SpatialAudio"
+ toolTip: qsTr("Sets the position and orientation of listening.")
+ }
+ }
+
+ Type {
+ name: "QtQuick3D.SpatialAudio.AudioRoom"
+ icon: ":/qtquickplugin/images/audio-room-16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: true
+ }
+
+ ItemLibraryEntry {
+ name: "Audio Room"
+ category: "Spatial Audio"
+ libraryIcon: ":/qtquickplugin/images/audio-room-24.png"
+ version: "6.0"
+ requiredImport: "QtQuick3D.SpatialAudio"
+ toolTip: qsTr("Sets up a room for the spatial audio engine.")
+ }
+ }
+
+ Type {
+ name: "QtQuick3D.SpatialAudio.SpatialSound"
+ icon: ":/qtquickplugin/images/spatial-audio-16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: true
+ }
+
+ ItemLibraryEntry {
+ name: "Spatial Sound"
+ category: "Spatial Audio"
+ libraryIcon: ":/qtquickplugin/images/spatial-audio-24.png"
+ version: "6.0"
+ requiredImport: "QtQuick3D.SpatialAudio"
+ toolTip: qsTr("A sound object in 3D space.")
+ }
+ }
+
+ Type {
+ name: "QtQuick3D.BakedLightmap"
+ icon: ":/ItemLibrary/images/item-default-icon.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: false
+ }
+
+ ItemLibraryEntry {
+ name: "Baked Lightmap"
+ category: "Components"
+ libraryIcon: ":/ItemLibrary/images/item-default-icon.png"
+ version: "6.5"
+ requiredImport: "QtQuick3D"
+ toolTip: qsTr("An object to specify details about baked lightmap of a model.")
+
+ Property { name: "loadPrefix"; type: "string"; value: "lightmaps"; }
+ }
+ }
}
diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp
index 6acb228421..873811ee28 100644
--- a/src/plugins/qmldesigner/settingspage.cpp
+++ b/src/plugins/qmldesigner/settingspage.cpp
@@ -196,7 +196,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie
m_debugPuppetComboBox = new QComboBox;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_useDefaultPuppetRadioButton,
diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp
index 11e27e2d1e..efb3bf49ee 100644
--- a/src/plugins/qmldesigner/shortcutmanager.cpp
+++ b/src/plugins/qmldesigner/shortcutmanager.cpp
@@ -5,6 +5,8 @@
#include <designersettings.h>
+#include <toolbarbackend.h>
+
#include <viewmanager.h>
#include <designeractionmanagerview.h>
#include <componentcore_constants.h>
@@ -59,11 +61,8 @@ ShortCutManager::ShortCutManager()
void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContext,
const Core::Context &qmlDesignerFormEditorContext,
const Core::Context &qmlDesignerEditor3DContext,
- const Core::Context &qmlDesignerNavigatorContext,
- const Core::Context &qmlDesignerMaterialBrowserContext)
+ const Core::Context &qmlDesignerNavigatorContext)
{
- Q_UNUSED(qmlDesignerMaterialBrowserContext)
-
Core::ActionContainer *editMenu = Core::ActionManager::actionContainer(Core::Constants::M_EDIT);
connect(&m_undoAction, &QAction::triggered, this, &ShortCutManager::undo);
@@ -107,6 +106,13 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
QmlDesignerPlugin::instance()->viewManager().exportAsImage();
});
+ QAction *action = new QAction(tr("Edit Global Annotations..."), this);
+ command = Core::ActionManager::registerAction(action, "Edit.Annotations", qmlDesignerMainContext);
+ Core::ActionManager::actionContainer(Core::Constants::M_EDIT)
+ ->addAction(command, Core::Constants::G_EDIT_OTHER);
+
+ connect(action, &QAction::triggered, this, [] { ToolBarBackend::launchGlobalAnnotations(); });
+
Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer(
QmlProjectManager::Constants::EXPORT_MENU);
@@ -136,7 +142,7 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
command->setDefaultKeySequence(QKeySequence::Redo);
designerActionManager.addCreatorCommand(command, ComponentCoreConstants::editCategory, 2, Utils::Icons::REDO_TOOLBAR.icon());
- designerActionManager.addDesignerAction(new SeperatorDesignerAction(ComponentCoreConstants::editCategory, 10));
+ designerActionManager.addDesignerAction(new SeparatorDesignerAction(ComponentCoreConstants::editCategory, 10));
//Edit Menu
m_deleteAction.setIcon(QIcon::fromTheme(QLatin1String("edit-cut"), Utils::Icons::EDIT_CLEAR_TOOLBAR.icon()));
@@ -196,10 +202,11 @@ 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);
if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D)
&& !context.contains(Constants::C_QMLNAVIGATOR)) {
- m_deleteAction.setEnabled(isMatBrowserActive);
+ m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive);
m_cutAction.setEnabled(false);
m_copyAction.setEnabled(false);
m_pasteAction.setEnabled(false);
@@ -254,6 +261,9 @@ void ShortCutManager::deleteSelected()
if (isMatBrowserActive) {
DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager();
designerActionManager.view()->emitCustomNotification("delete_selected_material");
+ } else if (isAssetsLibraryActive) {
+ DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager();
+ designerActionManager.view()->emitCustomNotification("delete_selected_assets");
} else if (currentDesignDocument()) {
currentDesignDocument()->deleteSelected();
}
diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h
index fe13e5a913..9b4762748e 100644
--- a/src/plugins/qmldesigner/shortcutmanager.h
+++ b/src/plugins/qmldesigner/shortcutmanager.h
@@ -25,8 +25,7 @@ public:
void registerActions(const Core::Context &qmlDesignerMainContext,
const Core::Context &qmlDesignerFormEditorContext,
const Core::Context &qmlDesignerEditor3DContext,
- const Core::Context &qmlDesignerNavigatorContext,
- const Core::Context &qmlDesignerMaterialBrowserContext);
+ const Core::Context &qmlDesignerNavigatorContext);
void connectUndoActions(DesignDocument *designDocument);
void disconnectUndoActions(DesignDocument *designDocument);
@@ -67,6 +66,7 @@ private:
QAction m_escapeAction;
bool isMatBrowserActive = false;
+ bool isAssetsLibraryActive = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo
index 207cdbb977..94f14c296d 100644
--- a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo
+++ b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo
@@ -27,6 +27,7 @@ MetaInfo {
"QtQuick.Controls.Universal",
"QtQuick.Controls.Material",
"QtQuick.Controls.NativeStyle",
+ "QtQuick.Controls.Windows",
"QtQuick.NativeStyle",
"QtRemoteObjects",
"Qt5Compat.GraphicalEffects",
diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp
index 5ec84d5bbc..5d6f42336c 100644
--- a/src/plugins/qmldesigner/utils/asset.cpp
+++ b/src/plugins/qmldesigner/utils/asset.cpp
@@ -10,7 +10,11 @@ namespace QmlDesigner {
Asset::Asset(const QString &filePath)
: m_filePath(filePath)
{
- m_suffix = "*." + filePath.split('.').last().toLower();
+ const QStringList split = filePath.split('.');
+ if (split.size() > 1)
+ m_suffix = "*." + split.last().toLower();
+
+ resolveType();
}
@@ -31,6 +35,12 @@ const QStringList &Asset::supportedFragmentShaderSuffixes()
return retList;
}
+const QStringList &Asset::supportedVertexShaderSuffixes()
+{
+ static const QStringList retList {"*.vert", "*.glsl", "*.glslv", "*.vsh"};
+ return retList;
+}
+
const QStringList &Asset::supportedShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.vert",
@@ -90,68 +100,54 @@ const QSet<QString> &Asset::supportedSuffixes()
return allSuffixes;
}
-Asset::Type Asset::type() const
+bool Asset::isSupported(const QString &path)
{
- if (supportedImageSuffixes().contains(m_suffix))
- return Asset::Type::Image;
-
- if (supportedFragmentShaderSuffixes().contains(m_suffix))
- return Asset::Type::FragmentShader;
-
- if (supportedShaderSuffixes().contains(m_suffix))
- return Asset::Type::Shader;
-
- if (supportedFontSuffixes().contains(m_suffix))
- return Asset::Type::Font;
-
- if (supportedAudioSuffixes().contains(m_suffix))
- return Asset::Type::Audio;
-
- if (supportedVideoSuffixes().contains(m_suffix))
- return Asset::Type::Video;
-
- if (supportedTexture3DSuffixes().contains(m_suffix))
- return Asset::Type::Texture3D;
-
- if (supportedEffectMakerSuffixes().contains(m_suffix))
- return Asset::Type::Effect;
+ return supportedSuffixes().contains(path);
+}
- return Asset::Type::Unknown;
+Asset::Type Asset::type() const
+{
+ return m_type;
}
bool Asset::isImage() const
{
- return type() == Asset::Type::Image;
+ return m_type == Asset::Type::Image;
}
bool Asset::isFragmentShader() const
{
- return type() == Asset::Type::FragmentShader;
+ return m_type == Asset::Type::FragmentShader;
+}
+
+bool Asset::isVertexShader() const
+{
+ return m_type == Asset::Type::VertexShader;
}
bool Asset::isShader() const
{
- return type() == Asset::Type::Shader;
+ return isFragmentShader() || isVertexShader();
}
bool Asset::isFont() const
{
- return type() == Asset::Type::Font;
+ return m_type == Asset::Type::Font;
}
bool Asset::isAudio() const
{
- return type() == Asset::Type::Audio;
+ return m_type == Asset::Type::Audio;
}
bool Asset::isVideo() const
{
- return type() == Asset::Type::Video;
+ return m_type == Asset::Type::Video;
}
bool Asset::isTexture3D() const
{
- return type() == Asset::Type::Texture3D;
+ return m_type == Asset::Type::Texture3D;
}
bool Asset::isHdrFile() const
@@ -159,9 +155,14 @@ bool Asset::isHdrFile() const
return m_suffix == "*.hdr";
}
+bool Asset::isKtxFile() const
+{
+ return m_suffix == "*.ktx";
+}
+
bool Asset::isEffect() const
{
- return type() == Asset::Type::Effect;
+ return m_type == Asset::Type::Effect;
}
const QString Asset::suffix() const
@@ -176,7 +177,35 @@ const QString Asset::id() const
bool Asset::isSupported() const
{
- return supportedSuffixes().contains(m_filePath);
+ return m_type != Asset::Type::Unknown;
+}
+
+bool Asset::isValidTextureSource()
+{
+ return isImage() || isTexture3D();
+}
+
+void Asset::resolveType()
+{
+ if (m_suffix.isEmpty())
+ return;
+
+ if (supportedImageSuffixes().contains(m_suffix))
+ m_type = Asset::Type::Image;
+ else if (supportedFragmentShaderSuffixes().contains(m_suffix))
+ m_type = Asset::Type::FragmentShader;
+ else if (supportedVertexShaderSuffixes().contains(m_suffix))
+ m_type = Asset::Type::VertexShader;
+ else if (supportedFontSuffixes().contains(m_suffix))
+ m_type = Asset::Type::Font;
+ else if (supportedAudioSuffixes().contains(m_suffix))
+ m_type = Asset::Type::Audio;
+ else if (supportedVideoSuffixes().contains(m_suffix))
+ m_type = Asset::Type::Video;
+ else if (supportedTexture3DSuffixes().contains(m_suffix))
+ m_type = Asset::Type::Texture3D;
+ else if (supportedEffectMakerSuffixes().contains(m_suffix))
+ m_type = Asset::Type::Effect;
}
bool Asset::hasSuffix() const
diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h
index 3e6d105ec4..e779d4a734 100644
--- a/src/plugins/qmldesigner/utils/asset.h
+++ b/src/plugins/qmldesigner/utils/asset.h
@@ -3,17 +3,29 @@
#pragma once
+#include <QString>
+
namespace QmlDesigner {
class Asset
{
public:
- enum Type { Unknown, Image, MissingImage, FragmentShader, Font, Audio, Video, Texture3D, Effect, Shader };
+ enum Type { Unknown,
+ Image,
+ MissingImage,
+ FragmentShader,
+ VertexShader,
+ Font,
+ Audio,
+ Video,
+ Texture3D,
+ Effect };
Asset(const QString &filePath);
static const QStringList &supportedImageSuffixes();
static const QStringList &supportedFragmentShaderSuffixes();
+ static const QStringList &supportedVertexShaderSuffixes();
static const QStringList &supportedShaderSuffixes();
static const QStringList &supportedFontSuffixes();
static const QStringList &supportedAudioSuffixes();
@@ -21,6 +33,7 @@ public:
static const QStringList &supportedTexture3DSuffixes();
static const QStringList &supportedEffectMakerSuffixes();
static const QSet<QString> &supportedSuffixes();
+ static bool isSupported(const QString &path);
const QString suffix() const;
const QString id() const;
@@ -29,18 +42,24 @@ public:
Type type() const;
bool isImage() const;
bool isFragmentShader() const;
+ bool isVertexShader() const;
bool isShader() const;
bool isFont() const;
bool isAudio() const;
bool isVideo() const;
bool isTexture3D() const;
bool isHdrFile() const;
+ bool isKtxFile() const;
bool isEffect() const;
bool isSupported() const;
+ bool isValidTextureSource();
private:
+ void resolveType();
+
QString m_filePath;
QString m_suffix;
+ Type m_type = Unknown;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/designersettings.h b/src/plugins/qmldesigner/utils/designersettings.h
deleted file mode 100644
index a97f982063..0000000000
--- a/src/plugins/qmldesigner/utils/designersettings.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "qmldesignerutils_global.h"
-
-#include <QHash>
-#include <QVariant>
-#include <QByteArray>
-#include <QMutex>
-
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
-
-namespace QmlDesigner {
-
-namespace DesignerSettingsKey {
-const char ITEMSPACING[] = "ItemSpacing";
-const char CONTAINERPADDING[] = "ContainerPadding";
-const char CANVASWIDTH[] = "CanvasWidth";
-const char CANVASHEIGHT[] = "CanvasHeight";
-const char ROOT_ELEMENT_INIT_WIDTH[] = "RootElementInitWidth";
-const char ROOT_ELEMENT_INIT_HEIGHT[] = "RootElementInitHeight";
-const char WARNING_FOR_FEATURES_IN_DESIGNER[] = "WarnAboutQtQuickFeaturesInDesigner";
-const char WARNING_FOR_QML_FILES_INSTEAD_OF_UIQML_FILES[] = "WarnAboutQmlFilesInsteadOfUiQmlFiles";
-const char WARNING_FOR_DESIGNER_FEATURES_IN_EDITOR[] = "WarnAboutQtQuickDesignerFeaturesInCodeEditor";
-const char SHOW_DEBUGVIEW[] = "ShowQtQuickDesignerDebugView";
-const char ENABLE_DEBUGVIEW[] = "EnableQtQuickDesignerDebugView";
-const char EDIT3DVIEW_BACKGROUND_COLOR[] = "Edit3DViewBackgroundColor";
-const char EDIT3DVIEW_GRID_COLOR[] = "Edit3DViewGridLineColor";
-const char ALWAYS_SAVE_IN_CRUMBLEBAR[] = "AlwaysSaveInCrumbleBar";
-const char USE_DEFAULT_PUPPET[] = "UseDefaultQml2Puppet";
-const char PUPPET_TOPLEVEL_BUILD_DIRECTORY[] = "PuppetToplevelBuildDirectory";
-const char PUPPET_DEFAULT_DIRECTORY[] = "PuppetDefaultDirectory";
-const char CONTROLS_STYLE[] = "ControlsStyle";
-const char TYPE_OF_QSTR_FUNCTION[] = "TypeOfQsTrFunction";
-const char SHOW_PROPERTYEDITOR_WARNINGS[] = "ShowPropertyEditorWarnings";
-const char ENABLE_MODEL_EXCEPTION_OUTPUT[] = "WarnException";
-const char PUPPET_KILL_TIMEOUT[] = "PuppetKillTimeout";
-const char DEBUG_PUPPET[] = "DebugPuppet";
-const char FORWARD_PUPPET_OUTPUT[] = "ForwardPuppetOutput";
-const char NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS[] = "NavigatorShowOnlyVisibleItems";
-const char NAVIGATOR_REVERSE_ITEM_ORDER[] = "NavigatorReverseItemOrder";
-const char REFORMAT_UI_QML_FILES[] = "ReformatUiQmlFiles"; /* These settings are not exposed in ui. */
-const char IGNORE_DEVICE_PIXEL_RATIO[] = "IgnoreDevicePixelRaio"; /* The settings can be used to turn off the feature, if there are serious issues */
-const char SHOW_DEBUG_SETTINGS[] = "ShowDebugSettings";
-const char ENABLE_TIMELINEVIEW[] = "EnableTimelineView";
-const char COLOR_PALETTE_RECENT[] = "ColorPaletteRecent";
-const char COLOR_PALETTE_FAVORITE[] = "ColorPaletteFavorite";
-const char ALWAYS_DESIGN_MODE[] = "AlwaysDesignMode";
-const char DISABLE_ITEM_LIBRARY_UPDATE_TIMER[] = "DisableItemLibraryUpdateTimer";
-const char ASK_BEFORE_DELETING_ASSET[] = "AskBeforeDeletingAsset";
-const char SMOOTH_RENDERING[] = "SmoothRendering";
-const char OLD_STATES_EDITOR[] = "ForceOldStatesEditor";
-const char EDITOR_ZOOM_FACTOR[] = "EditorZoomFactor";
-}
-
-class QMLDESIGNERUTILS_EXPORT DesignerSettings
-{
-public:
- DesignerSettings(QSettings *settings);
-
- void insert(const QByteArray &key, const QVariant &value);
- void insert(const QHash<QByteArray, QVariant> &settingsHash);
- QVariant value(const QByteArray &key, const QVariant &defaultValue = {}) const;
-
-private:
- void fromSettings(QSettings *);
- void toSettings(QSettings *) const;
-
- void restoreValue(QSettings *settings, const QByteArray &key,
- const QVariant &defaultValue = QVariant());
- void storeValue(QSettings *settings, const QByteArray &key, const QVariant &value) const;
-
- QSettings *m_settings;
- QHash<QByteArray, QVariant> m_cache;
- mutable QMutex m_mutex;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp
new file mode 100644
index 0000000000..b5c735d87a
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/filedownloader.cpp
@@ -0,0 +1,288 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#include "filedownloader.h"
+
+#include <private/qqmldata_p.h>
+#include <utils/networkaccessmanager.h>
+#include <utils/filepath.h>
+
+#include <QDir>
+#include <QQmlEngine>
+#include <QRandomGenerator>
+
+namespace QmlDesigner {
+
+FileDownloader::FileDownloader(QObject *parent)
+ : QObject(parent)
+{
+ QObject::connect(this, &FileDownloader::downloadFailed, this, [this]() {
+ if (m_outputFile.exists())
+ m_outputFile.remove();
+ });
+
+ QObject::connect(this, &FileDownloader::downloadCanceled, this, [this]() {
+ if (m_outputFile.exists())
+ m_outputFile.remove();
+ });
+}
+
+FileDownloader::~FileDownloader()
+{
+ // Delete the temp file only if a target Path was set (i.e. file will be moved)
+ if (deleteFileAtTheEnd() && m_outputFile.exists())
+ m_outputFile.remove();
+}
+
+bool FileDownloader::deleteFileAtTheEnd() const
+{
+ return m_targetFilePath.isEmpty();
+}
+
+void FileDownloader::start()
+{
+ emit downloadStarting();
+
+ auto uniqueText = QByteArray::number(QRandomGenerator::global()->generate(), 16);
+ QString tempFileName = QDir::tempPath() + "/.qds_" + uniqueText + "_download_" + url().fileName();
+
+ m_outputFile.setFileName(tempFileName);
+ m_outputFile.open(QIODevice::WriteOnly);
+
+ auto request = QNetworkRequest(m_url);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
+ QNetworkRequest::UserVerifiedRedirectPolicy);
+ QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request);
+ m_reply = reply;
+
+ QNetworkReply::connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
+ bool isDownloadingFile = false;
+ QString contentType;
+ if (!reply->hasRawHeader("Content-Type")) {
+ isDownloadingFile = true;
+ } else {
+ contentType = QString::fromUtf8(reply->rawHeader("Content-Type"));
+
+ if (contentType.startsWith("application/")
+ || contentType.startsWith("image/")
+ || contentType.startsWith("binary/")) {
+ isDownloadingFile = true;
+ } else {
+ qWarning() << "FileDownloader: Content type '" << contentType << "' is not supported";
+ }
+ }
+
+ if (isDownloadingFile)
+ m_outputFile.write(reply->readAll());
+ else
+ reply->close();
+ });
+
+ QNetworkReply::connect(reply,
+ &QNetworkReply::downloadProgress,
+ this,
+ [this](qint64 current, qint64 max) {
+ if (max <= 0) {
+ // NOTE: according to doc, we might have the second arg
+ // of QNetworkReply::downloadProgress less than 0.
+ return;
+ }
+
+ m_progress = current * 100 / max;
+ emit progressChanged();
+ });
+
+ QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &) {
+ emit reply->redirectAllowed();
+ });
+
+ QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ if (reply->error()) {
+ if (reply->error() != QNetworkReply::OperationCanceledError) {
+ qWarning() << Q_FUNC_INFO << m_url << reply->errorString();
+ emit downloadFailed();
+ } else {
+ emit downloadCanceled();
+ }
+ } else {
+ m_outputFile.flush();
+ m_outputFile.close();
+
+ QString dirPath = QFileInfo(m_targetFilePath).dir().absolutePath();
+ if (!deleteFileAtTheEnd()) {
+ if (!QDir{}.mkpath(dirPath)) {
+ emit downloadFailed();
+ return;
+ }
+
+ if (!QFileInfo().exists(m_targetFilePath) && !m_outputFile.rename(m_targetFilePath)) {
+ emit downloadFailed();
+ return;
+ }
+ }
+
+ m_finished = true;
+ emit outputFileChanged();
+ emit finishedChanged();
+ }
+
+ reply->deleteLater();
+ m_reply = nullptr;
+ });
+}
+
+void FileDownloader::setProbeUrl(bool value)
+{
+ if (m_probeUrl == value)
+ return;
+
+ m_probeUrl = value;
+ emit probeUrlChanged();
+}
+
+bool FileDownloader::probeUrl() const
+{
+ return m_probeUrl;
+}
+
+void FileDownloader::cancel()
+{
+ if (m_reply)
+ m_reply->abort();
+}
+
+void FileDownloader::setUrl(const QUrl &url)
+{
+ if (m_url != url) {
+ m_url = url;
+ emit urlChanged();
+ }
+
+ if (m_probeUrl)
+ doProbeUrl();
+}
+
+QUrl FileDownloader::url() const
+{
+ return m_url;
+}
+
+void FileDownloader::setDownloadEnabled(bool value)
+{
+ if (m_downloadEnabled == value)
+ return;
+
+ m_downloadEnabled = value;
+ emit downloadEnabledChanged();
+
+ if (!m_url.isEmpty() && m_probeUrl)
+ doProbeUrl();
+}
+
+bool FileDownloader::downloadEnabled() const
+{
+ return m_downloadEnabled;
+}
+
+bool FileDownloader::finished() const
+{
+ return m_finished;
+}
+
+bool FileDownloader::error() const
+{
+ return m_error;
+}
+
+QString FileDownloader::name() const
+{
+ const QFileInfo fileInfo(m_url.path());
+ return fileInfo.baseName();
+}
+
+QString FileDownloader::completeBaseName() const
+{
+ return QFileInfo(m_url.path()).completeBaseName();
+}
+
+int FileDownloader::progress() const
+{
+ return m_progress;
+}
+
+QString FileDownloader::outputFile() const
+{
+ return QFileInfo(m_outputFile).canonicalFilePath();
+}
+
+QDateTime FileDownloader::lastModified() const
+{
+ return m_lastModified;
+}
+
+bool FileDownloader::available() const
+{
+ return m_available;
+}
+
+void FileDownloader::doProbeUrl()
+{
+ if (!m_probeUrl)
+ return;
+
+ if (!m_downloadEnabled) {
+ m_available = false;
+ emit availableChanged();
+ return;
+ }
+
+ auto request = QNetworkRequest(m_url);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
+ QNetworkRequest::UserVerifiedRedirectPolicy);
+ QNetworkReply *reply = Utils::NetworkAccessManager::instance()->head(request);
+
+ QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &) {
+ emit reply->redirectAllowed();
+ });
+
+ QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ if (reply->error())
+ return;
+
+ m_lastModified = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
+ emit lastModifiedChanged();
+
+ m_available = true;
+ emit availableChanged();
+ });
+
+ QNetworkReply::connect(reply,
+ &QNetworkReply::errorOccurred,
+ this,
+ [this](QNetworkReply::NetworkError) {
+ QQmlData *data = QQmlData::get(this, false);
+ if (!data) {
+ qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr.";
+ return;
+ }
+
+ if (QQmlData::wasDeleted(this)) {
+ qDebug() << Q_FUNC_INFO << "FileDownloader was deleted.";
+ return;
+ }
+
+ m_available = false;
+ emit availableChanged();
+ });
+}
+
+void FileDownloader::setTargetFilePath(const QString &path)
+{
+ m_targetFilePath = path;
+}
+
+QString FileDownloader::targetFilePath() const
+{
+ return m_targetFilePath;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/filedownloader.h b/src/plugins/qmldesigner/utils/filedownloader.h
new file mode 100644
index 0000000000..4ecb6b4967
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/filedownloader.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include <QDateTime>
+#include <QFile>
+#include <QNetworkReply>
+#include <QObject>
+#include <QUrl>
+
+namespace QmlDesigner {
+
+class FileDownloader : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool downloadEnabled WRITE setDownloadEnabled READ downloadEnabled NOTIFY downloadEnabledChanged)
+ Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
+ Q_PROPERTY(QString targetFilePath READ targetFilePath WRITE setTargetFilePath NOTIFY targetFilePathChanged)
+ Q_PROPERTY(bool probeUrl READ probeUrl WRITE setProbeUrl NOTIFY probeUrlChanged)
+ Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
+ Q_PROPERTY(bool error READ error NOTIFY errorChanged)
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+ Q_PROPERTY(QString completeBaseName READ completeBaseName NOTIFY nameChanged)
+ Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
+ Q_PROPERTY(QString outputFile READ outputFile NOTIFY outputFileChanged)
+ Q_PROPERTY(QDateTime lastModified READ lastModified NOTIFY lastModifiedChanged)
+ Q_PROPERTY(bool available READ available NOTIFY availableChanged)
+
+public:
+ explicit FileDownloader(QObject *parent = nullptr);
+
+ ~FileDownloader();
+
+ void setUrl(const QUrl &url);
+ QUrl url() const;
+ void setTargetFilePath(const QString &path);
+ QString targetFilePath() const;
+ bool finished() const;
+ bool error() const;
+ QString name() const;
+ QString completeBaseName() const;
+ int progress() const;
+ QString outputFile() const;
+ QDateTime lastModified() const;
+ bool available() const;
+ void setDownloadEnabled(bool value);
+ bool downloadEnabled() const;
+
+ void setProbeUrl(bool value);
+ bool probeUrl() const;
+
+ Q_INVOKABLE void start();
+ Q_INVOKABLE void cancel();
+
+signals:
+ void finishedChanged();
+ void errorChanged();
+ void nameChanged();
+ void urlChanged();
+ void progressChanged();
+ void outputFileChanged();
+ void downloadFailed();
+ void lastModifiedChanged();
+ void availableChanged();
+ void downloadEnabledChanged();
+
+ void downloadStarting();
+ void downloadCanceled();
+ void probeUrlChanged();
+ void targetFilePathChanged();
+
+private:
+ void doProbeUrl();
+ bool deleteFileAtTheEnd() const;
+
+ QUrl m_url;
+ bool m_probeUrl = false;
+ bool m_finished = false;
+ bool m_error = false;
+ int m_progress = 0;
+ QFile m_outputFile;
+ QDateTime m_lastModified;
+ bool m_available = false;
+
+ QNetworkReply *m_reply = nullptr;
+ bool m_downloadEnabled = false;
+ QString m_targetFilePath;
+};
+
+} // namespace QmlDesigner
+
diff --git a/src/plugins/qmldesigner/utils/fileextractor.cpp b/src/plugins/qmldesigner/utils/fileextractor.cpp
new file mode 100644
index 0000000000..c6aa83fb7a
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/fileextractor.cpp
@@ -0,0 +1,236 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#include "fileextractor.h"
+
+#include <QQmlEngine>
+#include <QStorageInfo>
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+
+#include <utils/algorithm.h>
+#include <utils/archive.h>
+#include <utils/fileutils.h>
+#include <utils/qtcassert.h>
+
+namespace QmlDesigner {
+
+FileExtractor::FileExtractor(QObject *parent)
+ : QObject(parent)
+{
+ m_timer.setInterval(100);
+ m_timer.setSingleShot(false);
+
+ QObject::connect(this, &FileExtractor::targetFolderExistsChanged, this, [this]() {
+ if (targetFolderExists())
+ m_birthTime = QFileInfo(m_targetPath.toString() + "/" + m_archiveName).birthTime();
+ else
+ m_birthTime = QDateTime();
+
+ emit birthTimeChanged();
+ });
+
+ QObject::connect(
+ &m_timer, &QTimer::timeout, this, [this]() {
+ static QHash<QString, int> hash;
+ QDirIterator it(m_targetFolder, {"*.*"}, QDir::Files, QDirIterator::Subdirectories);
+
+ int count = 0;
+ while (it.hasNext()) {
+ if (!hash.contains(it.fileName())) {
+ m_currentFile = it.fileName();
+ hash.insert(m_currentFile, 0);
+ emit currentFileChanged();
+ }
+ it.next();
+ count++;
+ }
+
+ qint64 currentSize = m_bytesBefore
+ - QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable();
+
+ // We can not get the uncompressed size of the archive yet, that is why we use an
+ // approximation. We assume a 50% compression rate.
+ int progress = std::min(100ll, currentSize * 100 / m_compressedSize * 2);
+ if (progress >= 0) {
+ m_progress = progress;
+ emit progressChanged();
+ } else {
+ qWarning() << "FileExtractor has got negative progress. Likely due to QStorageInfo.";
+ }
+
+ m_size = QString::number(currentSize);
+ m_count = QString::number(count);
+ emit sizeChanged();
+ });
+}
+
+FileExtractor::~FileExtractor() {}
+
+void FileExtractor::changeTargetPath(const QString &path)
+{
+ m_targetPath = Utils::FilePath::fromString(path);
+ emit targetPathChanged();
+ emit targetFolderExistsChanged();
+}
+
+QString FileExtractor::targetPath() const
+{
+ return m_targetPath.toUserOutput();
+}
+
+void FileExtractor::setTargetPath(const QString &path)
+{
+ m_targetPath = Utils::FilePath::fromString(path);
+
+ QDir dir(m_targetPath.toString());
+
+ if (!path.isEmpty() && !dir.exists()) {
+ // Even though m_targetPath will be created eventually, we need to make sure the path exists
+ // before m_bytesBefore is being calculated.
+ dir.mkpath(m_targetPath.toString());
+ }
+}
+
+void FileExtractor::browse()
+{
+ const Utils::FilePath path =
+ Utils::FileUtils::getExistingDirectory(nullptr, tr("Choose Directory"), m_targetPath);
+
+ if (!path.isEmpty())
+ m_targetPath = path;
+
+ emit targetPathChanged();
+ emit targetFolderExistsChanged();
+}
+
+void FileExtractor::setSourceFile(const QString &sourceFilePath)
+{
+ m_sourceFile = Utils::FilePath::fromString(sourceFilePath);
+ emit targetFolderExistsChanged();
+}
+
+void FileExtractor::setArchiveName(const QString &filePath)
+{
+ m_archiveName = filePath;
+ emit targetFolderExistsChanged();
+}
+
+const QString FileExtractor::detailedText() const
+{
+ return m_detailedText;
+}
+
+void FileExtractor::setClearTargetPathContents(bool value)
+{
+ if (m_clearTargetPathContents != value) {
+ m_clearTargetPathContents = value;
+ emit clearTargetPathContentsChanged();
+ }
+}
+
+bool FileExtractor::clearTargetPathContents() const
+{
+ return m_clearTargetPathContents;
+}
+
+void FileExtractor::setAlwaysCreateDir(bool value)
+{
+ if (m_alwaysCreateDir != value) {
+ m_alwaysCreateDir = value;
+ emit alwaysCreateDirChanged();
+ }
+}
+
+bool FileExtractor::alwaysCreateDir() const
+{
+ return m_alwaysCreateDir;
+}
+
+bool FileExtractor::finished() const
+{
+ return m_finished;
+}
+
+QString FileExtractor::currentFile() const
+{
+ return m_currentFile;
+}
+
+QString FileExtractor::size() const
+{
+ return m_size;
+}
+
+QString FileExtractor::count() const
+{
+ return m_count;
+}
+
+bool FileExtractor::targetFolderExists() const
+{
+ return QFileInfo::exists(m_targetPath.toString() + "/" + m_archiveName);
+}
+
+int FileExtractor::progress() const
+{
+ return m_progress;
+}
+
+QDateTime FileExtractor::birthTime() const
+{
+ return m_birthTime;
+}
+
+QString FileExtractor::archiveName() const
+{
+ return m_archiveName;
+}
+
+QString FileExtractor::sourceFile() const
+{
+ return m_sourceFile.toString();
+}
+
+void FileExtractor::extract()
+{
+ m_targetFolder = m_targetPath.toString() + "/" + m_archiveName;
+
+ // If the target directory already exists, remove it and its content
+ QDir targetDir(m_targetFolder);
+ if (targetDir.exists() && m_clearTargetPathContents)
+ targetDir.removeRecursively();
+
+ if (m_alwaysCreateDir) {
+ // Create a new directory to generate a proper creation date
+ targetDir.mkdir(m_targetFolder);
+ }
+
+ Utils::Archive *archive = new Utils::Archive(m_sourceFile, m_targetPath);
+ QTC_ASSERT(archive->isValid(), delete archive; return);
+
+ m_timer.start();
+ m_bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable();
+ m_compressedSize = QFileInfo(m_sourceFile.toString()).size();
+
+ QObject::connect(archive, &Utils::Archive::outputReceived, this, [this](const QString &output) {
+ m_detailedText += output;
+ emit detailedTextChanged();
+ });
+
+ QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) {
+ archive->deleteLater();
+ m_finished = ret;
+ m_timer.stop();
+
+ m_progress = 100;
+ emit progressChanged();
+
+ emit targetFolderExistsChanged();
+ emit finishedChanged();
+ QTC_CHECK(ret);
+ });
+ archive->unarchive();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/fileextractor.h b/src/plugins/qmldesigner/utils/fileextractor.h
new file mode 100644
index 0000000000..5dbf12e903
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/fileextractor.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include <QDateTime>
+#include <QObject>
+#include <QTimer>
+
+#include <utils/filepath.h>
+
+namespace QmlDesigner {
+
+class FileExtractor : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString targetPath READ targetPath WRITE setTargetPath NOTIFY targetPathChanged)
+ Q_PROPERTY(QString archiveName READ archiveName WRITE setArchiveName)
+ Q_PROPERTY(QString detailedText READ detailedText NOTIFY detailedTextChanged)
+ Q_PROPERTY(QString currentFile READ currentFile NOTIFY currentFileChanged)
+ Q_PROPERTY(QString size READ size NOTIFY sizeChanged)
+ Q_PROPERTY(QString count READ count NOTIFY sizeChanged)
+ Q_PROPERTY(QString sourceFile READ sourceFile WRITE setSourceFile)
+ Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
+ Q_PROPERTY(bool targetFolderExists READ targetFolderExists NOTIFY targetFolderExistsChanged)
+ Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
+ Q_PROPERTY(QDateTime birthTime READ birthTime NOTIFY birthTimeChanged)
+ Q_PROPERTY(bool clearTargetPathContents READ clearTargetPathContents WRITE setClearTargetPathContents NOTIFY clearTargetPathContentsChanged)
+ Q_PROPERTY(bool alwaysCreateDir READ alwaysCreateDir WRITE setAlwaysCreateDir NOTIFY alwaysCreateDirChanged)
+
+public:
+ explicit FileExtractor(QObject *parent = nullptr);
+ ~FileExtractor();
+
+ Q_INVOKABLE void changeTargetPath(const QString &path);
+
+ QString targetPath() const;
+ void setTargetPath(const QString &path);
+ void setSourceFile(const QString &sourceFilePath);
+ void setArchiveName(const QString &filePath);
+ const QString detailedText() const;
+ bool finished() const;
+ QString currentFile() const;
+ QString size() const;
+ QString count() const;
+ bool targetFolderExists() const;
+ int progress() const;
+ QDateTime birthTime() const;
+ void setClearTargetPathContents(bool value);
+ bool clearTargetPathContents() const;
+ void setAlwaysCreateDir(bool value);
+ bool alwaysCreateDir() const;
+
+ QString sourceFile() const;
+ QString archiveName() const;
+
+ Q_INVOKABLE void browse();
+ Q_INVOKABLE void extract();
+
+signals:
+ void targetPathChanged();
+ void detailedTextChanged();
+ void finishedChanged();
+ void currentFileChanged();
+ void sizeChanged();
+ void targetFolderExistsChanged();
+ void progressChanged();
+ void birthTimeChanged();
+ void clearTargetPathContentsChanged();
+ void alwaysCreateDirChanged();
+
+private:
+ Utils::FilePath m_targetPath;
+ QString m_targetFolder; // The same as m_targetPath, but with the archive name also.
+ Utils::FilePath m_sourceFile;
+ QString m_detailedText;
+ bool m_finished = false;
+ QTimer m_timer;
+ QString m_currentFile;
+ QString m_size;
+ QString m_count;
+ QString m_archiveName;
+ int m_progress = 0;
+ QDateTime m_birthTime;
+ bool m_clearTargetPathContents = false;
+ bool m_alwaysCreateDir = false;
+
+ qint64 m_bytesBefore = 0;
+ qint64 m_compressedSize = 0;
+};
+
+} // QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/hdrimage.cpp b/src/plugins/qmldesigner/utils/hdrimage.cpp
index 227c6ab671..74d3acde50 100644
--- a/src/plugins/qmldesigner/utils/hdrimage.cpp
+++ b/src/plugins/qmldesigner/utils/hdrimage.cpp
@@ -21,7 +21,7 @@ typedef unsigned char RGBE[4];
constexpr float GAMMA = 1.f / 2.2f;
-QByteArray fileToByteArray(QString const &filename)
+static QByteArray fileToByteArray(QString const &filename)
{
QFile file(filename);
QFileInfo info(file);
diff --git a/src/plugins/qmldesigner/utils/imageutils.cpp b/src/plugins/qmldesigner/utils/imageutils.cpp
index 97660b2aa3..8fa3131cd3 100644
--- a/src/plugins/qmldesigner/utils/imageutils.cpp
+++ b/src/plugins/qmldesigner/utils/imageutils.cpp
@@ -3,12 +3,23 @@
#include "imageutils.h"
+#include "ktximage.h"
+
#include <QFile>
#include <QFileInfo>
#include <QImageReader>
namespace QmlDesigner {
+QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes)
+{
+ return QLatin1String("%1 x %2\n%3")
+ .arg(QString::number(dimensions.width()),
+ QString::number(dimensions.height()),
+ QLocale::system().formattedDataSize(
+ sizeInBytes, 2, QLocale::DataSizeTraditionalFormat));
+}
+
QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
{
QFileInfo info(path);
@@ -17,7 +28,8 @@ QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
int width = 0;
int height = 0;
- if (info.suffix() == "hdr") {
+ const QString suffix = info.suffix();
+ if (suffix == "hdr") {
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return {};
@@ -27,20 +39,20 @@ QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
if (sscanf(line.constData(), "-Y %d +X %d", &height, &width))
break;
}
+ } else if (suffix == "ktx") {
+ KtxImage ktx(path);
+ width = ktx.dimensions().width();
+ height = ktx.dimensions().height();
} else {
QSize size = QImageReader(path).size();
width = size.width();
height = size.height();
}
- if (width == 0 && height == 0)
+ if (width <= 0 || height <= 0)
return {};
- return QLatin1String("%1 x %2\n%3 (%4)")
- .arg(QString::number(width),
- QString::number(height),
- QLocale::system().formattedDataSize(info.size(), 2, QLocale::DataSizeTraditionalFormat),
- info.suffix());
+ return imageInfo(QSize(width, height), info.size());
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/imageutils.h b/src/plugins/qmldesigner/utils/imageutils.h
index 2e1030d48c..a4036614a3 100644
--- a/src/plugins/qmldesigner/utils/imageutils.h
+++ b/src/plugins/qmldesigner/utils/imageutils.h
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
+#include <QSize>
#include <QString>
namespace QmlDesigner {
@@ -11,6 +12,7 @@ class ImageUtils
public:
ImageUtils();
+ static QString imageInfo(const QSize &dimensions, qint64 sizeInBytes);
static QString imageInfo(const QString &path);
};
diff --git a/src/plugins/qmldesigner/utils/ktximage.cpp b/src/plugins/qmldesigner/utils/ktximage.cpp
new file mode 100644
index 0000000000..1bb607403f
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/ktximage.cpp
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "ktximage.h"
+
+#include <QFile>
+#include <QFileInfo>
+#include <QDebug>
+
+#include <cmath>
+
+namespace QmlDesigner {
+
+// Ktx images currently support only image metadata
+
+static QByteArray fileToByteArray(QString const &filename)
+{
+ QFile file(filename);
+ QFileInfo info(file);
+
+ if (info.exists() && file.open(QFile::ReadOnly)) {
+ // Read data until we have what we need
+ // Content is:
+ // Byte[12] identifier
+ // UInt32 endianness
+ // UInt32
+ // UInt32
+ // UInt32
+ // Uint32
+ // Uint32
+ // UInt32 pixelWidth
+ // UInt32 pixelHeight
+ // ...
+ return file.read(44);
+ }
+
+ return {};
+}
+
+KtxImage::KtxImage(const QString &fileName)
+ : m_fileName(fileName)
+{
+ loadKtx();
+}
+
+QSize KtxImage::dimensions() const
+{
+ return m_dim;
+}
+
+void KtxImage::loadKtx()
+{
+ QByteArray buf(fileToByteArray(m_fileName));
+
+ auto handleError = [this](const QString &error) {
+ qWarning() << QStringLiteral("Failed to load KTX image '%1': %2.").arg(m_fileName, error).toUtf8();
+ };
+
+ if (buf.isEmpty()) {
+ handleError("File open failed");
+ return;
+ }
+
+ constexpr char ktxIdentifier[12] = {
+ '\xAB', 'K', 'T', 'X', ' ', '1',
+ '1', '\xBB', '\r', '\n', '\x1A', '\n'
+ };
+
+ if (!buf.startsWith(ktxIdentifier)) {
+ handleError("Non-KTX file");
+ return;
+ }
+
+ if (buf.size() < 44) {
+ handleError("Missing metadata");
+ return;
+ }
+
+ quint32 w = 0;
+ quint32 h = 0;
+
+ if (*reinterpret_cast<const quint32 *>(buf.data() + 12) == 0x01020304) {
+ // File endianness is different from our endianness
+ QByteArray convBuf(4, 0);
+ auto convertEndianness = [&convBuf, &buf](int idx) -> quint32 {
+ for (int i = 0; i < 4; ++i)
+ convBuf[i] = buf[idx + 3 - i];
+ return *reinterpret_cast<const quint32 *>(convBuf.data());
+ };
+ w = convertEndianness(36);
+ h = convertEndianness(40);
+ } else {
+ w = *reinterpret_cast<const quint32 *>(buf.data() + 36);
+ h = *reinterpret_cast<const quint32 *>(buf.data() + 40);
+ }
+
+ m_dim = QSize(w, h);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/ktximage.h b/src/plugins/qmldesigner/utils/ktximage.h
new file mode 100644
index 0000000000..19b8132d27
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/ktximage.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include "qmldesignerutils_global.h"
+
+#include <QImage>
+#include <QPixmap>
+#include <QSize>
+#include <QString>
+
+namespace QmlDesigner {
+
+class QMLDESIGNERUTILS_EXPORT KtxImage
+{
+public:
+ KtxImage(const QString &fileName);
+
+ QString fileName() const { return m_fileName; }
+ QSize dimensions() const;
+
+private:
+ void loadKtx();
+
+ QString m_fileName;
+ QSize m_dim;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.cpp b/src/plugins/qmldesigner/utils/multifiledownloader.cpp
new file mode 100644
index 0000000000..c36476f05d
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/multifiledownloader.cpp
@@ -0,0 +1,137 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#include "multifiledownloader.h"
+#include "filedownloader.h"
+
+namespace QmlDesigner {
+
+MultiFileDownloader::MultiFileDownloader(QObject *parent)
+ : QObject(parent)
+{}
+
+MultiFileDownloader::~MultiFileDownloader()
+{}
+
+void MultiFileDownloader::setDownloader(FileDownloader *downloader)
+{
+ m_downloader = downloader;
+
+ QObject::connect(this, &MultiFileDownloader::downloadStarting, [this]() {
+ m_nextFile = 0;
+ if (m_files.length() > 0)
+ m_downloader->start();
+ });
+
+ QObject::connect(m_downloader, &FileDownloader::progressChanged, this, [this]() {
+ m_progress = (m_nextFile + m_downloader->progress()) / m_files.count();
+ });
+
+ QObject::connect(m_downloader, &FileDownloader::downloadFailed, this, [this]() {
+ m_failed = true;
+ emit downloadFailed();
+ });
+
+ QObject::connect(m_downloader, &FileDownloader::downloadCanceled, this, [this]() {
+ m_canceled = true;
+ emit downloadCanceled();
+ });
+
+ QObject::connect(m_downloader, &FileDownloader::finishedChanged, this, [this]() {
+ switchToNextFile();
+ });
+}
+
+FileDownloader *MultiFileDownloader::downloader()
+{
+ return m_downloader;
+}
+
+void MultiFileDownloader::start()
+{
+ emit downloadStarting();
+}
+
+void MultiFileDownloader::cancel()
+{
+ m_canceled = true;
+ m_downloader->cancel();
+}
+
+void MultiFileDownloader::setBaseUrl(const QUrl &baseUrl)
+{
+ if (m_baseUrl != baseUrl) {
+ m_baseUrl = baseUrl;
+ emit baseUrlChanged();
+ }
+}
+
+QUrl MultiFileDownloader::baseUrl() const
+{
+ return m_baseUrl;
+}
+
+bool MultiFileDownloader::finished() const
+{
+ return m_finished;
+}
+
+int MultiFileDownloader::progress() const
+{
+ return m_progress;
+}
+
+void MultiFileDownloader::setTargetDirPath(const QString &path)
+{
+ m_targetDirPath = path;
+}
+
+QString MultiFileDownloader::targetDirPath() const
+{
+ return m_targetDirPath;
+}
+
+QString MultiFileDownloader::nextUrl() const
+{
+ if (m_nextFile >= m_files.length())
+ return {};
+
+ return m_baseUrl.toString() + "/" + m_files[m_nextFile];
+}
+
+QString MultiFileDownloader::nextTargetPath() const
+{
+ if (m_nextFile >= m_files.length())
+ return {};
+
+ return m_targetDirPath + "/" + m_files[m_nextFile];
+}
+
+void MultiFileDownloader::setFiles(const QStringList &files)
+{
+ m_files = files;
+}
+
+QStringList MultiFileDownloader::files() const
+{
+ return m_files;
+}
+
+void MultiFileDownloader::switchToNextFile()
+{
+ ++m_nextFile;
+
+ if (m_nextFile < m_files.length()) {
+ if (m_canceled) {
+ emit downloadCanceled();
+ } else {
+ emit nextUrlChanged();
+ emit nextTargetPathChanged();
+ m_downloader->start();
+ }
+ } else {
+ m_finished = true;
+ emit finishedChanged();
+ }
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.h b/src/plugins/qmldesigner/utils/multifiledownloader.h
new file mode 100644
index 0000000000..794f85538c
--- /dev/null
+++ b/src/plugins/qmldesigner/utils/multifiledownloader.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include <QObject>
+#include <QUrl>
+
+namespace QmlDesigner {
+
+class FileDownloader;
+
+class MultiFileDownloader : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(FileDownloader *downloader READ downloader WRITE setDownloader)
+ Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
+ Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
+ Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged)
+ Q_PROPERTY(QString targetDirPath READ targetDirPath WRITE setTargetDirPath NOTIFY targetDirPathChanged)
+
+ Q_PROPERTY(QString nextUrl READ nextUrl NOTIFY nextUrlChanged)
+ Q_PROPERTY(QString nextTargetPath READ nextTargetPath NOTIFY nextTargetPathChanged)
+ Q_PROPERTY(QStringList files READ files WRITE setFiles NOTIFY filesChanged)
+
+public:
+ explicit MultiFileDownloader(QObject *parent = nullptr);
+
+ ~MultiFileDownloader();
+
+ void setBaseUrl(const QUrl &url);
+ QUrl baseUrl() const;
+
+ void setTargetDirPath(const QString &path);
+ QString targetDirPath() const;
+ void setDownloader(FileDownloader *downloader);
+ FileDownloader *downloader();
+
+ bool finished() const;
+ int progress() const;
+
+ QString nextUrl() const;
+ QString nextTargetPath() const;
+
+ void setFiles(const QStringList &files);
+ QStringList files() const;
+ void switchToNextFile();
+
+ Q_INVOKABLE void start();
+ Q_INVOKABLE void cancel();
+
+signals:
+ void finishedChanged();
+ void baseUrlChanged();
+ void progressChanged();
+ void downloadFailed();
+
+ void downloadStarting();
+ void downloadCanceled();
+ void targetDirPathChanged();
+ void nextUrlChanged();
+ void filesChanged();
+ void nextTargetPathChanged();
+
+private:
+ QUrl m_baseUrl;
+ bool m_finished = false;
+ int m_progress = 0;
+ QString m_targetDirPath;
+ FileDownloader *m_downloader = nullptr;
+ bool m_canceled = false;
+ bool m_failed = false;
+ QStringList m_files;
+ int m_nextFile = 0;
+};
+
+} // namespace QmlDesigner
+
diff --git a/src/plugins/qmldesignerbase/CMakeLists.txt b/src/plugins/qmldesignerbase/CMakeLists.txt
new file mode 100644
index 0000000000..f03498f28d
--- /dev/null
+++ b/src/plugins/qmldesignerbase/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_qtc_plugin(QmlDesignerBase
+ PROPERTIES COMPILE_WARNING_AS_ERROR ON
+ DEPENDS Qt::Core Qt::QuickWidgets
+ PLUGIN_DEPENDS Core ProjectExplorer QtSupport
+ SOURCES
+ qmldesignerbase_global.h
+ qmldesignerbaseplugin.cpp qmldesignerbaseplugin.h
+)
+
+extend_qtc_plugin(QmlDesignerBase
+ PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils
+ SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils
+ SOURCES
+ designersettings.cpp designersettings.h
+ qmlpuppetpaths.cpp qmlpuppetpaths.h
+ studioquickwidget.cpp studioquickwidget.h
+)
diff --git a/src/plugins/qmldesignerbase/QmlDesignerBase.json.in b/src/plugins/qmldesignerbase/QmlDesignerBase.json.in
new file mode 100644
index 0000000000..38fb427ae1
--- /dev/null
+++ b/src/plugins/qmldesignerbase/QmlDesignerBase.json.in
@@ -0,0 +1,19 @@
+{
+ \"Name\" : \"QmlDesignerBase\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
+ ],
+ \"Category\" : \"Qt Quick\",
+ \"Description\" : \"Provides support code for the qml designer and co..\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/qmldesignerbase/qmldesignerbase.qbs b/src/plugins/qmldesignerbase/qmldesignerbase.qbs
new file mode 100644
index 0000000000..937f180c32
--- /dev/null
+++ b/src/plugins/qmldesignerbase/qmldesignerbase.qbs
@@ -0,0 +1,29 @@
+import qbs
+
+QtcPlugin {
+ name: "QmlDesignerBase"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "QtSupport" }
+ Depends { name: "app_version_header" }
+ Depends { name: "Qt.quickwidgets" }
+
+ files: [
+ "qmldesignerbase_global.h",
+ "qmldesignerbaseplugin.cpp",
+ "qmldesignerbaseplugin.h",
+ ]
+
+ Group {
+ prefix: "utils/"
+ files: [
+ "designersettings.cpp",
+ "designersettings.h",
+ "qmlpuppetpaths.cpp",
+ "qmlpuppetpaths.h",
+ "studioquickwidget.cpp",
+ "studioquickwidget.h",
+ ]
+ }
+}
diff --git a/src/plugins/qmldesignerbase/qmldesignerbase_global.h b/src/plugins/qmldesignerbase/qmldesignerbase_global.h
new file mode 100644
index 0000000000..89b5dc4a9d
--- /dev/null
+++ b/src/plugins/qmldesignerbase/qmldesignerbase_global.h
@@ -0,0 +1,14 @@
+// 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
+
+#pragma once
+
+#include <QtGlobal>
+
+#if defined(QMLDESIGNERBASE_LIBRARY)
+#define QMLDESIGNERBASE_EXPORT Q_DECL_EXPORT
+#elif defined(QMLDESIGNERBASE_STATIC_LIBRARY)
+#define QMLDESIGNERBASE_EXPORT
+#else
+#define QMLDESIGNERBASE_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
new file mode 100644
index 0000000000..0a9ef8483a
--- /dev/null
+++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
@@ -0,0 +1,272 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "qmldesignerbaseplugin.h"
+
+#include "utils/designersettings.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/dialogs/restartdialog.h>
+#include <coreplugin/icore.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <utils/hostosinfo.h>
+
+#include <QCheckBox>
+#include <QGroupBox>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QSpacerItem>
+#include <QStandardPaths>
+#include <QVBoxLayout>
+
+namespace QmlDesigner {
+
+const char EXAMPLES_DOWNLOAD_PATH[] = "StudioConfig/ExamplesDownloadPath";
+const char BUNDLES_DOWNLOAD_PATH[] = "StudioConfig/BundlesDownloadPath";
+
+class QmlDesignerBasePlugin::Data
+{
+public:
+ DesignerSettings settings{Core::ICore::instance()->settings()};
+};
+
+namespace {
+QmlDesignerBasePlugin *global;
+}
+
+QmlDesignerBasePlugin::QmlDesignerBasePlugin()
+{
+ global = this;
+};
+
+QmlDesignerBasePlugin *QmlDesignerBasePlugin::instance()
+{
+ return global;
+};
+
+
+QmlDesignerBasePlugin::~QmlDesignerBasePlugin() = default;
+
+DesignerSettings &QmlDesignerBasePlugin::settings()
+{
+ return global->d->settings;
+}
+
+bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *)
+{
+ d = std::make_unique<Data>();
+
+ return true;
+}
+
+Utils::FilePath QmlDesignerBasePlugin::defaultExamplesPath()
+{
+ QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost()
+ ? QStandardPaths::HomeLocation
+ : QStandardPaths::DocumentsLocation;
+
+ return Utils::FilePath::fromString(QStandardPaths::writableLocation(location))
+ .pathAppended("QtDesignStudio/examples");
+}
+
+Utils::FilePath QmlDesignerBasePlugin::defaultBundlesPath()
+{
+ QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost()
+ ? QStandardPaths::HomeLocation
+ : QStandardPaths::DocumentsLocation;
+
+ return Utils::FilePath::fromString(QStandardPaths::writableLocation(location))
+ .pathAppended("QtDesignStudio/bundles");
+}
+
+QString QmlDesignerBasePlugin::examplesPathSetting()
+{
+ return Core::ICore::settings()
+ ->value(EXAMPLES_DOWNLOAD_PATH, defaultExamplesPath().toString())
+ .toString();
+}
+
+QString QmlDesignerBasePlugin::bundlesPathSetting()
+{
+ return Core::ICore::settings()
+ ->value(BUNDLES_DOWNLOAD_PATH, defaultBundlesPath().toString())
+ .toString();
+}
+
+static bool hideBuildMenuSetting()
+{
+ return Core::ICore::settings()
+ ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD, false)
+ .toBool();
+}
+
+static bool hideDebugMenuSetting()
+{
+ return Core::ICore::settings()
+ ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, false)
+ .toBool();
+}
+
+static bool hideAnalyzeMenuSetting()
+{
+ return Core::ICore::settings()
+ ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE, false)
+ .toBool();
+}
+
+static bool hideToolsMenuSetting()
+{
+ return Core::ICore::settings()->value(Core::Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool();
+}
+
+void setSettingIfDifferent(const QString &key, bool value, bool &dirty)
+{
+ QSettings *s = Core::ICore::settings();
+ if (s->value(key, false).toBool() != value) {
+ dirty = true;
+ s->setValue(key, value);
+ }
+}
+
+StudioSettingsPage::StudioSettingsPage()
+ : m_buildCheckBox(new QCheckBox(tr("Build")))
+ , m_debugCheckBox(new QCheckBox(tr("Debug")))
+ , m_analyzeCheckBox(new QCheckBox(tr("Analyze")))
+ , m_toolsCheckBox(new QCheckBox(tr("Tools")))
+ , m_pathChooserExamples(new Utils::PathChooser())
+ , m_pathChooserBundles(new Utils::PathChooser())
+{
+ const QString toolTip = tr(
+ "Hide top-level menus with advanced functionality to simplify the UI. <b>Build</b> is "
+ "generally not required in the context of Qt Design Studio. <b>Debug</b> and "
+ "<b>Analyze</b> "
+ "are only required for debugging and profiling. <b>Tools</b> can be useful for bookmarks "
+ "and git integration.");
+
+ QVBoxLayout *boxLayout = new QVBoxLayout();
+ setLayout(boxLayout);
+ auto groupBox = new QGroupBox(tr("Hide Menu"));
+ groupBox->setToolTip(toolTip);
+ boxLayout->addWidget(groupBox);
+
+ auto verticalLayout = new QVBoxLayout();
+ groupBox->setLayout(verticalLayout);
+
+ m_buildCheckBox->setToolTip(toolTip);
+ m_debugCheckBox->setToolTip(toolTip);
+ m_analyzeCheckBox->setToolTip(toolTip);
+ m_toolsCheckBox->setToolTip(toolTip);
+
+ verticalLayout->addWidget(m_buildCheckBox);
+ verticalLayout->addWidget(m_debugCheckBox);
+ verticalLayout->addWidget(m_analyzeCheckBox);
+ verticalLayout->addWidget(m_toolsCheckBox);
+
+ verticalLayout->addSpacerItem(
+ new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum));
+
+ m_buildCheckBox->setChecked(hideBuildMenuSetting());
+ m_debugCheckBox->setChecked(hideDebugMenuSetting());
+ m_analyzeCheckBox->setChecked(hideAnalyzeMenuSetting());
+ m_toolsCheckBox->setChecked(hideToolsMenuSetting());
+
+ // Examples path setting
+ auto examplesGroupBox = new QGroupBox(tr("Examples"));
+ boxLayout->addWidget(examplesGroupBox);
+
+ auto examplesLayout = new QHBoxLayout(this);
+ examplesGroupBox->setLayout(examplesLayout);
+
+ auto examplesLabel = new QLabel(tr("Examples path:"));
+ m_pathChooserExamples->setFilePath(
+ Utils::FilePath::fromString(QmlDesignerBasePlugin::examplesPathSetting()));
+ auto examplesResetButton = new QPushButton(tr("Reset Path"));
+
+ connect(examplesResetButton, &QPushButton::clicked, this, [this]() {
+ m_pathChooserExamples->setFilePath(QmlDesignerBasePlugin::defaultExamplesPath());
+ });
+
+ examplesLayout->addWidget(examplesLabel);
+ examplesLayout->addWidget(m_pathChooserExamples);
+ examplesLayout->addWidget(examplesResetButton);
+
+ // Bundles path setting
+ auto bundlesGroupBox = new QGroupBox(tr("Bundles"));
+ boxLayout->addWidget(bundlesGroupBox);
+
+ auto bundlesLayout = new QHBoxLayout(this);
+ bundlesGroupBox->setLayout(bundlesLayout);
+
+ QLabel *bundlesLabel = new QLabel(tr("Bundles path:"));
+ m_pathChooserBundles->setFilePath(
+ Utils::FilePath::fromString(QmlDesignerBasePlugin::bundlesPathSetting()));
+ QPushButton *bundlesResetButton = new QPushButton(tr("Reset Path"));
+
+ connect(bundlesResetButton, &QPushButton::clicked, this, [this]() {
+ m_pathChooserBundles->setFilePath(QmlDesignerBasePlugin::defaultBundlesPath());
+ });
+
+ bundlesLayout->addWidget(bundlesLabel);
+ bundlesLayout->addWidget(m_pathChooserBundles);
+ bundlesLayout->addWidget(bundlesResetButton);
+
+ boxLayout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding));
+}
+
+void StudioSettingsPage::apply()
+{
+ bool dirty = false;
+
+ setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD,
+ m_buildCheckBox->isChecked(),
+ dirty);
+
+ setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG,
+ m_debugCheckBox->isChecked(),
+ dirty);
+
+ setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE,
+ m_analyzeCheckBox->isChecked(),
+ dirty);
+
+ setSettingIfDifferent(Core::Constants::SETTINGS_MENU_HIDE_TOOLS,
+ m_toolsCheckBox->isChecked(),
+ dirty);
+
+ if (dirty) {
+ const QString restartText = tr("The menu visibility change will take effect after restart.");
+ Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
+ restartDialog.exec();
+ }
+
+ QSettings *s = Core::ICore::settings();
+ const QString value = m_pathChooserExamples->filePath().toString();
+
+ if (s->value(EXAMPLES_DOWNLOAD_PATH, false).toString() != value) {
+ s->setValue(EXAMPLES_DOWNLOAD_PATH, value);
+ emit global->examplesDownloadPathChanged(value);
+ }
+
+ const QString bundlesPath = m_pathChooserBundles->filePath().toString();
+
+ if (s->value(BUNDLES_DOWNLOAD_PATH).toString() != bundlesPath) {
+ s->setValue(BUNDLES_DOWNLOAD_PATH, bundlesPath);
+ emit global->bundlesDownloadPathChanged(bundlesPath);
+
+ const QString restartText = tr("Changing bundle path will take effect after restart.");
+ Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
+ restartDialog.exec();
+ }
+}
+
+StudioConfigSettingsPage::StudioConfigSettingsPage()
+{
+ setId("Z.StudioConfig.Settings");
+ setDisplayName(StudioSettingsPage::tr("Qt Design Studio Configuration"));
+ setCategory(Core::Constants::SETTINGS_CATEGORY_CORE);
+ setWidgetCreator([] { return new StudioSettingsPage; });
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
new file mode 100644
index 0000000000..3676a04e8c
--- /dev/null
+++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qmldesignerbase_global.h"
+
+#include <coreplugin/dialogs/ioptionspage.h>
+#include <extensionsystem/iplugin.h>
+#include <utils/pathchooser.h>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QCheckBox)
+
+namespace QmlDesigner {
+
+class StudioSettingsPage : public Core::IOptionsPageWidget
+{
+ Q_OBJECT
+
+public:
+ void apply() final;
+
+ StudioSettingsPage();
+
+private:
+ QCheckBox *m_buildCheckBox;
+ QCheckBox *m_debugCheckBox;
+ QCheckBox *m_analyzeCheckBox;
+ QCheckBox *m_toolsCheckBox;
+ Utils::PathChooser *m_pathChooserExamples;
+ Utils::PathChooser *m_pathChooserBundles;
+};
+
+class QMLDESIGNERBASE_EXPORT StudioConfigSettingsPage : public Core::IOptionsPage
+{
+public:
+ StudioConfigSettingsPage();
+};
+
+class QMLDESIGNERBASE_EXPORT QmlDesignerBasePlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlDesignerBase.json")
+
+public:
+ QmlDesignerBasePlugin();
+ ~QmlDesignerBasePlugin();
+
+ static QmlDesignerBasePlugin *instance();
+
+ static Utils::FilePath defaultExamplesPath();
+ static Utils::FilePath defaultBundlesPath();
+ static QString examplesPathSetting();
+ static QString bundlesPathSetting();
+
+ static class DesignerSettings &settings();
+
+signals:
+ void examplesDownloadPathChanged(const QString &path);
+ void bundlesDownloadPathChanged(const QString &path);
+
+private:
+ bool initialize(const QStringList &arguments, QString *errorMessage) override;
+
+private:
+ class Data;
+ std::unique_ptr<Data> d;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp
index 3e6d5ffb44..fe1ee169c7 100644
--- a/src/plugins/qmldesigner/utils/designersettings.cpp
+++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp
@@ -87,6 +87,9 @@ void DesignerSettings::fromSettings(QSettings *settings)
restoreValue(settings, DesignerSettingsKey::SHOW_DEBUG_SETTINGS, false);
restoreValue(settings, DesignerSettingsKey::OLD_STATES_EDITOR, false);
restoreValue(settings, DesignerSettingsKey::EDITOR_ZOOM_FACTOR, 1.0);
+ restoreValue(settings, DesignerSettingsKey::ACTIONS_MERGE_TEMPLATE_ENABLED, false);
+ restoreValue(settings, DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL,
+ "https://cdn.qt.io/designstudio/bundles");
settings->endGroup();
settings->endGroup();
diff --git a/src/plugins/qmldesignerbase/utils/designersettings.h b/src/plugins/qmldesignerbase/utils/designersettings.h
new file mode 100644
index 0000000000..78b4370526
--- /dev/null
+++ b/src/plugins/qmldesignerbase/utils/designersettings.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../qmldesignerbase_global.h"
+
+#include <QHash>
+#include <QVariant>
+#include <QByteArray>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+class QSettings;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+namespace DesignerSettingsKey {
+inline constexpr char ITEMSPACING[] = "ItemSpacing";
+inline constexpr char CONTAINERPADDING[] = "ContainerPadding";
+inline constexpr char CANVASWIDTH[] = "CanvasWidth";
+inline constexpr char CANVASHEIGHT[] = "CanvasHeight";
+inline constexpr char ROOT_ELEMENT_INIT_WIDTH[] = "RootElementInitWidth";
+inline constexpr char ROOT_ELEMENT_INIT_HEIGHT[] = "RootElementInitHeight";
+inline constexpr char WARNING_FOR_FEATURES_IN_DESIGNER[] = "WarnAboutQtQuickFeaturesInDesigner";
+inline constexpr char WARNING_FOR_QML_FILES_INSTEAD_OF_UIQML_FILES[]
+ = "WarnAboutQmlFilesInsteadOfUiQmlFiles";
+inline constexpr char WARNING_FOR_DESIGNER_FEATURES_IN_EDITOR[]
+ = "WarnAboutQtQuickDesignerFeaturesInCodeEditor";
+inline constexpr char SHOW_DEBUGVIEW[] = "ShowQtQuickDesignerDebugView";
+inline constexpr char ENABLE_DEBUGVIEW[] = "EnableQtQuickDesignerDebugView";
+inline constexpr char EDIT3DVIEW_BACKGROUND_COLOR[] = "Edit3DViewBackgroundColor";
+inline constexpr char EDIT3DVIEW_GRID_COLOR[] = "Edit3DViewGridLineColor";
+inline constexpr char ALWAYS_SAVE_IN_CRUMBLEBAR[] = "AlwaysSaveInCrumbleBar";
+inline constexpr char USE_DEFAULT_PUPPET[] = "UseDefaultQml2Puppet";
+inline constexpr char PUPPET_TOPLEVEL_BUILD_DIRECTORY[] = "PuppetToplevelBuildDirectory";
+inline constexpr char PUPPET_DEFAULT_DIRECTORY[] = "PuppetDefaultDirectory";
+inline constexpr char CONTROLS_STYLE[] = "ControlsStyle";
+inline constexpr char TYPE_OF_QSTR_FUNCTION[] = "TypeOfQsTrFunction";
+inline constexpr char SHOW_PROPERTYEDITOR_WARNINGS[] = "ShowPropertyEditorWarnings";
+inline constexpr char ENABLE_MODEL_EXCEPTION_OUTPUT[] = "WarnException";
+inline constexpr char PUPPET_KILL_TIMEOUT[] = "PuppetKillTimeout";
+inline constexpr char DEBUG_PUPPET[] = "DebugPuppet";
+inline constexpr char FORWARD_PUPPET_OUTPUT[] = "ForwardPuppetOutput";
+inline constexpr char NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS[] = "NavigatorShowOnlyVisibleItems";
+inline constexpr char NAVIGATOR_REVERSE_ITEM_ORDER[] = "NavigatorReverseItemOrder";
+inline constexpr char REFORMAT_UI_QML_FILES[]
+ = "ReformatUiQmlFiles"; /* These settings are not exposed in ui. */
+inline constexpr char IGNORE_DEVICE_PIXEL_RATIO[]
+ = "IgnoreDevicePixelRaio"; /* The settings can be used to turn off the feature, if there are serious issues */
+inline constexpr char SHOW_DEBUG_SETTINGS[] = "ShowDebugSettings";
+inline constexpr char ENABLE_TIMELINEVIEW[] = "EnableTimelineView";
+inline constexpr char COLOR_PALETTE_RECENT[] = "ColorPaletteRecent";
+inline constexpr char COLOR_PALETTE_FAVORITE[] = "ColorPaletteFavorite";
+inline constexpr char ALWAYS_DESIGN_MODE[] = "AlwaysDesignMode";
+inline constexpr char DISABLE_ITEM_LIBRARY_UPDATE_TIMER[] = "DisableItemLibraryUpdateTimer";
+inline constexpr char ASK_BEFORE_DELETING_ASSET[] = "AskBeforeDeletingAsset";
+inline constexpr char SMOOTH_RENDERING[] = "SmoothRendering";
+inline constexpr char OLD_STATES_EDITOR[] = "ForceOldStatesEditor";
+inline constexpr char EDITOR_ZOOM_FACTOR[] = "EditorZoomFactor";
+inline constexpr char ACTIONS_MERGE_TEMPLATE_ENABLED[] = "ActionsMergeTemplateEnabled";
+inline constexpr char DOWNLOADABLE_BUNDLES_URL[] = "DownloadableBundlesLocation";
+}
+
+class QMLDESIGNERBASE_EXPORT DesignerSettings
+{
+public:
+ DesignerSettings(QSettings *settings);
+
+ void insert(const QByteArray &key, const QVariant &value);
+ void insert(const QHash<QByteArray, QVariant> &settingsHash);
+ QVariant value(const QByteArray &key, const QVariant &defaultValue = {}) const;
+
+private:
+ void fromSettings(QSettings *);
+ void toSettings(QSettings *) const;
+
+ void restoreValue(QSettings *settings, const QByteArray &key,
+ const QVariant &defaultValue = QVariant());
+ void storeValue(QSettings *settings, const QByteArray &key, const QVariant &value) const;
+
+ QSettings *m_settings;
+ QHash<QByteArray, QVariant> m_cache;
+ mutable QMutex m_mutex;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp b/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp
new file mode 100644
index 0000000000..d73ea62ea9
--- /dev/null
+++ b/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "qmlpuppetpaths.h"
+
+#include "designersettings.h"
+
+#include <app/app_version.h>
+#include <coreplugin/icore.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/target.h>
+#include <qtsupport/baseqtversion.h>
+#include <qtsupport/qtkitinformation.h>
+
+namespace QmlDesigner {
+namespace QmlPuppetPaths {
+
+namespace {
+
+Utils::FilePath qmlPuppetExecutablePath(const Utils::FilePath &workingDirectory)
+{
+ return workingDirectory.pathAppended(QString{"qml2puppet-"} + Core::Constants::IDE_VERSION_LONG)
+ .withExecutableSuffix();
+}
+
+Utils::FilePath qmlPuppetFallbackDirectory(const DesignerSettings &settings)
+{
+ auto puppetFallbackDirectory = Utils::FilePath::fromString(
+ settings.value(DesignerSettingsKey::PUPPET_DEFAULT_DIRECTORY).toString());
+ if (puppetFallbackDirectory.isEmpty() || !puppetFallbackDirectory.exists())
+ return Core::ICore::libexecPath();
+ return puppetFallbackDirectory;
+}
+
+std::pair<Utils::FilePath, Utils::FilePath> qmlPuppetFallbackPaths(const DesignerSettings &settings)
+{
+ auto workingDirectory = qmlPuppetFallbackDirectory(settings);
+
+ return {workingDirectory, qmlPuppetExecutablePath(workingDirectory)};
+}
+
+std::pair<Utils::FilePath, Utils::FilePath> pathsForKitPuppet(ProjectExplorer::Target *target)
+{
+ if (!target || !target->kit())
+ return {};
+
+ QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
+
+ if (currentQtVersion) {
+ auto path = currentQtVersion->binPath();
+ return {path, qmlPuppetExecutablePath(path)};
+ }
+
+ return {};
+}
+} // namespace
+
+std::pair<Utils::FilePath, Utils::FilePath> qmlPuppetPaths(ProjectExplorer::Target *target,
+ const DesignerSettings &settings)
+{
+ auto [workingDirectoryPath, puppetPath] = pathsForKitPuppet(target);
+
+ if (workingDirectoryPath.isEmpty() || !puppetPath.exists())
+ return qmlPuppetFallbackPaths(settings);
+
+ return {workingDirectoryPath, puppetPath};
+}
+
+} // namespace QmlPuppetPaths
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.h b/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.h
new file mode 100644
index 0000000000..a9b3b0b890
--- /dev/null
+++ b/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../qmldesignerbase_global.h"
+
+#include <utils/filepath.h>
+
+namespace ProjectExplorer {
+class Target;
+}
+namespace QmlDesigner {
+class DesignerSettings;
+
+namespace QmlPuppetPaths {
+QMLDESIGNERBASE_EXPORT std::pair<Utils::FilePath, Utils::FilePath> qmlPuppetPaths(
+ ProjectExplorer::Target *target, const DesignerSettings &settings);
+}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesignerbase/utils/studioquickwidget.cpp b/src/plugins/qmldesignerbase/utils/studioquickwidget.cpp
new file mode 100644
index 0000000000..2cfe5aa974
--- /dev/null
+++ b/src/plugins/qmldesignerbase/utils/studioquickwidget.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "studioquickwidget.h"
+
+#include <QQuickItem>
+#include <QVBoxLayout>
+#include <QtQml/QQmlEngine>
+
+QQmlEngine *s_engine = nullptr;
+
+StudioQuickWidget::StudioQuickWidget(QWidget *parent)
+ : QWidget{parent}
+{
+ if (!s_engine)
+ s_engine = new QQmlEngine;
+
+ m_quickWidget = new QQuickWidget(s_engine, this);
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ setLayout(layout);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(m_quickWidget);
+}
+
+QQmlEngine *StudioQuickWidget::engine() const
+{
+ return m_quickWidget->engine();
+}
+
+QQmlContext *StudioQuickWidget::rootContext() const
+{
+ return m_quickWidget->rootContext();
+}
+
+QQuickItem *StudioQuickWidget::rootObject() const
+{
+ return m_quickWidget->rootObject();
+}
+
+void StudioQuickWidget::setResizeMode(QQuickWidget::ResizeMode mode)
+{
+ m_quickWidget->setResizeMode(mode);
+}
+
+void StudioQuickWidget::setSource(const QUrl &url)
+{
+ m_quickWidget->setSource(url);
+}
+
+void StudioQuickWidget::refresh() {}
+
+void StudioQuickWidget::setClearColor(const QColor &color)
+{
+ m_quickWidget->setClearColor(color);
+}
+
+QList<QQmlError> StudioQuickWidget::errors() const
+{
+ return m_quickWidget->errors();
+}
+
+StudioPropertyMap *StudioQuickWidget::registerPropertyMap(const QByteArray &name)
+{
+ StudioPropertyMap *map = new StudioPropertyMap(this);
+ [[maybe_unused]] const int typeIndex = qmlRegisterSingletonType<StudioPropertyMap>(
+ name.data(), 1, 0, name.data(), [map](QQmlEngine *, QJSEngine *) { return map; });
+ return map;
+}
+
+QQuickWidget *StudioQuickWidget::quickWidget() const
+{
+ return m_quickWidget;
+}
+
+StudioPropertyMap::StudioPropertyMap(QObject *parent)
+ : QQmlPropertyMap(parent)
+{}
+
+void StudioPropertyMap::setProperties(const QList<PropertyPair> &properties)
+{
+ for (const PropertyPair &pair : properties)
+ insert(pair.name, pair.value);
+}
diff --git a/src/plugins/qmldesignerbase/utils/studioquickwidget.h b/src/plugins/qmldesignerbase/utils/studioquickwidget.h
new file mode 100644
index 0000000000..ef64fb6a69
--- /dev/null
+++ b/src/plugins/qmldesignerbase/utils/studioquickwidget.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../qmldesignerbase_global.h"
+
+#include <QObject>
+#include <QQmlPropertyMap>
+#include <QtQuickWidgets/QQuickWidget>
+
+class QMLDESIGNERBASE_EXPORT StudioPropertyMap : public QQmlPropertyMap
+{
+public:
+ struct PropertyPair
+ {
+ QString name;
+ QVariant value;
+ };
+
+ explicit StudioPropertyMap(QObject *parent = 0);
+
+ void setProperties(const QList<PropertyPair> &properties);
+};
+
+class QMLDESIGNERBASE_EXPORT StudioQuickWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit StudioQuickWidget(QWidget *parent = nullptr);
+
+ QQmlEngine *engine() const;
+ QQmlContext *rootContext() const;
+
+ QQuickItem *rootObject() const;
+
+ void setResizeMode(QQuickWidget::ResizeMode mode);
+
+ void setSource(const QUrl &url);
+
+ void refresh();
+
+ void setClearColor(const QColor &color);
+
+ QList<QQmlError> errors() const;
+
+ StudioPropertyMap *registerPropertyMap(const QByteArray &name);
+ QQuickWidget *quickWidget() const;
+
+private:
+ QQuickWidget *m_quickWidget = nullptr;
+};
diff --git a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp
index 98713d1a36..b29d1627db 100644
--- a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp
+++ b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp
@@ -246,13 +246,16 @@ void matchComponentFromObjectDefQuickFix(const QmlJSQuickFixAssistInterface *int
}
}
-void performComponentFromObjectDef(const QString &fileName, QmlJS::AST::UiObjectDefinition *objDef)
+void performComponentFromObjectDef(QmlJSEditorWidget *editor,
+ const QString &fileName,
+ QmlJS::AST::UiObjectDefinition *objDef)
{
QmlJSRefactoringChanges refactoring(QmlJS::ModelManagerInterface::instance(),
QmlJS::ModelManagerInterface::instance()->snapshot());
QmlJSRefactoringFilePtr current = refactoring.file(Utils::FilePath::fromString(fileName));
- Operation operation(nullptr, objDef);
+ QmlJSQuickFixAssistInterface interface(editor, TextEditor::AssistReason::ExplicitlyInvoked);
+ Operation operation(&interface, objDef);
operation.performChanges(current, refactoring);
}
diff --git a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h
index 8ac708aed2..72a0f50e20 100644
--- a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h
+++ b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.h
@@ -8,10 +8,13 @@
namespace QmlJSEditor {
+class QmlJSEditorWidget;
+
QMLJSEDITOR_EXPORT void matchComponentFromObjectDefQuickFix(
const Internal::QmlJSQuickFixAssistInterface *interface, QuickFixOperations &result);
-QMLJSEDITOR_EXPORT void performComponentFromObjectDef
- (const QString &fileName, QmlJS::AST::UiObjectDefinition *objDef);
+QMLJSEDITOR_EXPORT void performComponentFromObjectDef(QmlJSEditorWidget *editor,
+ const QString &fileName,
+ QmlJS::AST::UiObjectDefinition *objDef);
} // namespace QmlJSEditor
diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp
index ad370633bd..91bcc0ac89 100644
--- a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp
+++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp
@@ -34,7 +34,7 @@ ComponentNameDialog::ComponentNameDialog(QWidget *parent) :
m_checkBox = new QCheckBox(Tr::tr("ui.qml file"));
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
Tr::tr("Component name:"), m_componentNameEdit, br,
diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
index 2a0ecb4177..bfb5a002cf 100644
--- a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
+++ b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp
@@ -7,18 +7,24 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
+#include <qmljs/qmljscheck.h>
+#include <qmljs/qmljsstaticanalysismessage.h>
+#include <qmljstools/qmljstoolsconstants.h>
+#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
#include <utils/macroexpander.h>
#include <utils/qtcsettings.h>
+#include <utils/treemodel.h>
#include <utils/variablechooser.h>
-#include <qmljstools/qmljstoolsconstants.h>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
+#include <QMenu>
#include <QSettings>
#include <QTextStream>
+#include <QTreeView>
const char AUTO_FORMAT_ON_SAVE[] = "QmlJSEditor.AutoFormatOnSave";
const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "QmlJSEditor.AutoFormatOnlyCurrentProject";
@@ -31,11 +37,30 @@ const char UIQML_OPEN_MODE[] = "QmlJSEditor.openUiQmlMode";
const char FORMAT_COMMAND[] = "QmlJSEditor.formatCommand";
const char FORMAT_COMMAND_OPTIONS[] = "QmlJSEditor.formatCommandOptions";
const char CUSTOM_COMMAND[] = "QmlJSEditor.useCustomFormatCommand";
+const char CUSTOM_ANALYZER[] = "QmlJSEditor.useCustomAnalyzer";
+const char DISABLED_MESSAGES[] = "QmlJSEditor.disabledMessages";
+const char DISABLED_MESSAGES_NONQUICKUI[] = "QmlJSEditor.disabledMessagesNonQuickUI";
const char DEFAULT_CUSTOM_FORMAT_COMMAND[] = "%{CurrentDocument:Project:QT_HOST_BINS}/qmlformat";
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
+static QList<int> defaultDisabledMessages()
+{
+ static const QList<int> disabledByDefault = Utils::transform(
+ QmlJS::Check::defaultDisabledMessages(),
+ [](QmlJS::StaticAnalysis::Type t) { return int(t); });
+ return disabledByDefault;
+}
+
+static QList<int> defaultDisabledMessagesNonQuickUi()
+{
+ static const QList<int> disabledForNonQuickUi = Utils::transform(
+ QmlJS::Check::defaultDisabledMessagesForNonQuickUi(),
+ [](QmlJS::StaticAnalysis::Type t){ return int(t); });
+ return disabledForNonQuickUi;
+}
+
void QmlJsEditingSettings::set()
{
if (get() != *this)
@@ -57,6 +82,17 @@ void QmlJsEditingSettings::fromSettings(QSettings *settings)
m_formatCommand = settings->value(FORMAT_COMMAND, {}).toString();
m_formatCommandOptions = settings->value(FORMAT_COMMAND_OPTIONS, {}).toString();
m_useCustomFormatCommand = settings->value(CUSTOM_COMMAND, QVariant(false)).toBool();
+ m_useCustomAnalyzer = settings->value(CUSTOM_ANALYZER, QVariant(false)).toBool();
+
+ m_disabledMessages = Utils::transform<QSet>(
+ settings->value(DISABLED_MESSAGES,
+ QVariant::fromValue(defaultDisabledMessages())).toList(),
+ [](const QVariant &v){ return v.toInt(); });
+ m_disabledMessagesForNonQuickUi = Utils::transform<QSet>(
+ settings->value(DISABLED_MESSAGES_NONQUICKUI,
+ QVariant::fromValue(defaultDisabledMessagesNonQuickUi())).toList(),
+ [](const QVariant &v) { return v.toInt(); });
+
settings->endGroup();
}
@@ -80,6 +116,18 @@ void QmlJsEditingSettings::toSettings(QSettings *settings) const
CUSTOM_COMMAND,
m_useCustomFormatCommand,
false);
+ Utils::QtcSettings::setValueWithDefault(settings,
+ CUSTOM_ANALYZER,
+ m_useCustomAnalyzer,
+ false);
+ Utils::QtcSettings::setValueWithDefault(settings,
+ DISABLED_MESSAGES,
+ Utils::sorted(Utils::toList(m_disabledMessages)),
+ defaultDisabledMessages());
+ Utils::QtcSettings::setValueWithDefault(settings,
+ DISABLED_MESSAGES_NONQUICKUI,
+ Utils::sorted(Utils::toList(m_disabledMessagesForNonQuickUi)),
+ defaultDisabledMessagesNonQuickUi());
settings->endGroup();
QmllsSettingsManager::instance()->checkForChanges();
}
@@ -93,7 +141,10 @@ bool QmlJsEditingSettings::equals(const QmlJsEditingSettings &other) const
&& m_foldAuxData == other.m_foldAuxData && m_qmllsSettings == other.m_qmllsSettings
&& m_uiQmlOpenMode == other.m_uiQmlOpenMode && m_formatCommand == other.m_formatCommand
&& m_formatCommandOptions == other.m_formatCommandOptions
- && m_useCustomFormatCommand == other.m_useCustomFormatCommand;
+ && m_useCustomFormatCommand == other.m_useCustomFormatCommand
+ && m_useCustomAnalyzer == other.m_useCustomAnalyzer
+ && m_disabledMessages == other.m_disabledMessages
+ && m_disabledMessagesForNonQuickUi == other.m_disabledMessagesForNonQuickUi;
}
bool QmlJsEditingSettings::enableContextPane() const
@@ -181,12 +232,12 @@ void QmlJsEditingSettings::setUseCustomFormatCommand(bool customCommand)
m_useCustomFormatCommand = customCommand;
}
-QmllsSettings &QmlJsEditingSettings::qmllsSettigs()
+QmllsSettings &QmlJsEditingSettings::qmllsSettings()
{
return m_qmllsSettings;
}
-const QmllsSettings &QmlJsEditingSettings::qmllsSettigs() const
+const QmllsSettings &QmlJsEditingSettings::qmllsSettings() const
{
return m_qmllsSettings;
}
@@ -201,6 +252,92 @@ void QmlJsEditingSettings::setUiQmlOpenMode(const QString &mode)
m_uiQmlOpenMode = mode;
}
+bool QmlJsEditingSettings::useCustomAnalyzer() const
+{
+ return m_useCustomAnalyzer;
+}
+
+void QmlJsEditingSettings::setUseCustomAnalyzer(bool customAnalyzer)
+{
+ m_useCustomAnalyzer = customAnalyzer;
+}
+
+QSet<int> QmlJsEditingSettings::disabledMessages() const
+{
+ return m_disabledMessages;
+}
+
+void QmlJsEditingSettings::setDisabledMessages(const QSet<int> &disabled)
+{
+ m_disabledMessages = disabled;
+}
+
+QSet<int> QmlJsEditingSettings::disabledMessagesForNonQuickUi() const
+{
+ return m_disabledMessagesForNonQuickUi;
+}
+
+void QmlJsEditingSettings::setDisabledMessagesForNonQuickUi(const QSet<int> &disabled)
+{
+ m_disabledMessagesForNonQuickUi = disabled;
+}
+
+class AnalyzerMessageItem final : public Utils::TreeItem
+{
+public:
+ AnalyzerMessageItem() = default;
+ AnalyzerMessageItem(int number, const QString &message)
+ : m_messageNumber(number)
+ , m_message(message)
+ {}
+
+ QVariant data(int column, int role) const final
+ {
+ if (role == Qt::DisplayRole) {
+ if (column == 0)
+ return QString("M%1").arg(m_messageNumber);
+ if (column == 2)
+ return m_message.split('\n').first();
+ } else if (role == Qt::CheckStateRole) {
+ if (column == 0)
+ return m_checked ? Qt::Checked : Qt::Unchecked;
+ if (column == 1)
+ return m_disabledInNonQuickUi ? Qt::Checked : Qt::Unchecked;
+ }
+ return TreeItem::data(column, role);
+ }
+
+ bool setData(int column, const QVariant &value, int role) final
+ {
+ if (role == Qt::CheckStateRole) {
+ if (column == 0) {
+ m_checked = value.toBool();
+ return true;
+ }
+ if (column == 1) {
+ m_disabledInNonQuickUi = value.toBool();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Qt::ItemFlags flags(int column) const final
+ {
+ if (column == 0 || column == 1)
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
+ else
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ }
+
+ int messageNumber() const { return m_messageNumber; }
+private:
+ int m_messageNumber = -1;
+ QString m_message;
+ bool m_checked = true;
+ bool m_disabledInNonQuickUi = false;
+};
+
class QmlJsEditingSettingsPageWidget final : public Core::IOptionsPageWidget
{
public:
@@ -239,17 +376,38 @@ public:
uiQmlOpenComboBox->setSizeAdjustPolicy(QComboBox::QComboBox::AdjustToContents);
useQmlls = new QCheckBox(Tr::tr("Use qmlls (EXPERIMENTAL!)"));
- useQmlls->setChecked(s.qmllsSettigs().useQmlls);
+ useQmlls->setChecked(s.qmllsSettings().useQmlls);
useLatestQmlls = new QCheckBox(Tr::tr("Always use latest qmlls"));
- useLatestQmlls->setChecked(s.qmllsSettigs().useLatestQmlls);
- useLatestQmlls->setEnabled(s.qmllsSettigs().useQmlls);
+ useLatestQmlls->setChecked(s.qmllsSettings().useLatestQmlls);
+ useLatestQmlls->setEnabled(s.qmllsSettings().useQmlls);
QObject::connect(useQmlls, &QCheckBox::stateChanged, this, [this](int checked) {
useLatestQmlls->setEnabled(checked != Qt::Unchecked);
});
- using namespace Utils::Layouting;
+
+ useCustomAnalyzer = new QCheckBox(Tr::tr("Use customized static analyzer"));
+ useCustomAnalyzer->setChecked(s.useCustomAnalyzer());
+ analyzerMessageModel = new Utils::TreeModel<AnalyzerMessageItem>(this);
+ analyzerMessageModel->setHeader({Tr::tr("Enabled"),
+ Tr::tr("Disabled for non Qt Quick UI"),
+ Tr::tr("Message")});
+ analyzerMessagesView = new QTreeView;
+ analyzerMessagesView->setModel(analyzerMessageModel);
+ analyzerMessagesView->setEnabled(s.useCustomAnalyzer());
+ QObject::connect(useCustomAnalyzer, &QCheckBox::stateChanged, this, [this](int checked){
+ analyzerMessagesView->setEnabled(checked != Qt::Unchecked);
+ });
+ analyzerMessagesView->setToolTip(Tr::tr("Enabled checks can be disabled for non Qt Quick UI"
+ " files,\nbut disabled checks cannot get explicitly"
+ " enabled for non Qt Quick UI files."));
+ analyzerMessagesView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(analyzerMessagesView, &QTreeView::customContextMenuRequested,
+ this, &QmlJsEditingSettingsPageWidget::showContextMenu);
+ using namespace Layouting;
// clang-format off
- const auto formattingGroup =
+ QWidget *formattingGroup = nullptr;
+ Column {
Group {
+ bindTo(&formattingGroup),
title(Tr::tr("Automatic Formatting on File Save")),
Column {
autoFormatOnSave,
@@ -260,10 +418,7 @@ public:
formatCommandOptionsLabel, formatCommandOptions
}
},
- };
-
- Column {
- formattingGroup,
+ },
Group {
title(Tr::tr("Qt Quick Toolbars")),
Column { pinContextPane, enableContextPane },
@@ -279,16 +434,19 @@ public:
title(Tr::tr("Language Server")),
Column{useQmlls, useLatestQmlls},
},
+ Group {
+ title(Tr::tr("Static Analyzer")),
+ Column{ useCustomAnalyzer, analyzerMessagesView },
+ },
st,
}.attachTo(this);
// clang-format on
- Utils::VariableChooser::addSupportForChildWidgets(formattingGroup.widget,
+ Utils::VariableChooser::addSupportForChildWidgets(formattingGroup,
Utils::globalMacroExpander());
const auto updateFormatCommandState = [&, formatCommandLabel, formatCommandOptionsLabel] {
- const bool enabled = useCustomFormatCommand->isChecked()
- && autoFormatOnSave->isChecked();
+ const bool enabled = useCustomFormatCommand->isChecked();
formatCommandLabel->setEnabled(enabled);
formatCommand->setEnabled(enabled);
formatCommandOptionsLabel->setEnabled(enabled);
@@ -298,10 +456,11 @@ public:
connect(autoFormatOnSave, &QCheckBox::toggled, this, [&, updateFormatCommandState]() {
autoFormatOnlyCurrentProject->setEnabled(autoFormatOnSave->isChecked());
- useCustomFormatCommand->setEnabled(autoFormatOnSave->isChecked());
updateFormatCommandState();
});
connect(useCustomFormatCommand, &QCheckBox::toggled, this, updateFormatCommandState);
+
+ populateAnalyzerMessages(s.disabledMessages(), s.disabledMessagesForNonQuickUi());
}
void apply() final
@@ -316,12 +475,55 @@ public:
s.setFormatCommandOptions(formatCommandOptions->text());
s.setFoldAuxData(foldAuxData->isChecked());
s.setUiQmlOpenMode(uiQmlOpenComboBox->currentData().toString());
- s.qmllsSettigs().useQmlls = useQmlls->isChecked();
- s.qmllsSettigs().useLatestQmlls = useLatestQmlls->isChecked();
+ s.qmllsSettings().useQmlls = useQmlls->isChecked();
+ s.qmllsSettings().useLatestQmlls = useLatestQmlls->isChecked();
+ s.setUseCustomAnalyzer(useCustomAnalyzer->isChecked());
+ QSet<int> disabled;
+ QSet<int> disabledForNonQuickUi;
+ analyzerMessageModel->forAllItems(
+ [&disabled, &disabledForNonQuickUi](AnalyzerMessageItem *item){
+ if (item->data(0, Qt::CheckStateRole) == Qt::Unchecked)
+ disabled.insert(item->messageNumber());
+ if (item->data(1, Qt::CheckStateRole) == Qt::Checked)
+ disabledForNonQuickUi.insert(item->messageNumber());
+ });
+ s.setDisabledMessages(disabled);
+ s.setDisabledMessagesForNonQuickUi(disabledForNonQuickUi);
s.set();
}
private:
+ void populateAnalyzerMessages(const QSet<int> &disabled, const QSet<int> &disabledForNonQuickUi)
+ {
+ using namespace QmlJS::StaticAnalysis;
+ auto knownMessages = Utils::sorted(Message::allMessageTypes());
+ auto root = analyzerMessageModel->rootItem();
+ for (auto msgType : knownMessages) {
+ const QString msg = Message::prototypeForMessageType(msgType).message;
+ auto item = new AnalyzerMessageItem(msgType, msg);
+ item->setData(0, !disabled.contains(msgType), Qt::CheckStateRole);
+ item->setData(1, disabledForNonQuickUi.contains(msgType), Qt::CheckStateRole);
+ root->appendChild(item);
+ }
+
+ for (int column = 0; column < 3; ++column)
+ analyzerMessagesView->resizeColumnToContents(column);
+ }
+
+ void showContextMenu(const QPoint &position)
+ {
+ QMenu menu;
+ QAction *reset = new QAction(Tr::tr("Reset to Default"), &menu);
+ menu.addAction(reset);
+ connect(reset, &QAction::triggered, this, [this](){
+ analyzerMessageModel->clear();
+ populateAnalyzerMessages(Utils::toSet(defaultDisabledMessages()),
+ Utils::toSet(defaultDisabledMessagesNonQuickUi()));
+
+ });
+ menu.exec(analyzerMessagesView->mapToGlobal(position));
+ }
+
QCheckBox *autoFormatOnSave;
QCheckBox *autoFormatOnlyCurrentProject;
QCheckBox *useCustomFormatCommand;
@@ -333,6 +535,9 @@ private:
QCheckBox *useQmlls;
QCheckBox *useLatestQmlls;
QComboBox *uiQmlOpenComboBox;
+ QCheckBox *useCustomAnalyzer;
+ QTreeView *analyzerMessagesView;
+ Utils::TreeModel<AnalyzerMessageItem> *analyzerMessageModel;
};
diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.h b/src/plugins/qmljseditor/qmljseditingsettingspage.h
index 43aa9ee336..d7f8f78bd3 100644
--- a/src/plugins/qmljseditor/qmljseditingsettingspage.h
+++ b/src/plugins/qmljseditor/qmljseditingsettingspage.h
@@ -52,12 +52,21 @@ public:
bool useCustomFormatCommand() const;
void setUseCustomFormatCommand(bool customCommand);
- QmllsSettings &qmllsSettigs();
- const QmllsSettings &qmllsSettigs() const;
+ QmllsSettings &qmllsSettings();
+ const QmllsSettings &qmllsSettings() const;
const QString uiQmlOpenMode() const;
void setUiQmlOpenMode(const QString &mode);
+ bool useCustomAnalyzer() const;
+ void setUseCustomAnalyzer(bool customAnalyzer);
+
+ QSet<int> disabledMessages() const;
+ void setDisabledMessages(const QSet<int> &disabled);
+
+ QSet<int> disabledMessagesForNonQuickUi() const;
+ void setDisabledMessagesForNonQuickUi(const QSet<int> &disabled);
+
friend bool operator==(const QmlJsEditingSettings &s1, const QmlJsEditingSettings &s2)
{ return s1.equals(s2); }
friend bool operator!=(const QmlJsEditingSettings &s1, const QmlJsEditingSettings &s2)
@@ -70,10 +79,13 @@ private:
bool m_autoFormatOnlyCurrentProject = false;
bool m_foldAuxData = true;
bool m_useCustomFormatCommand = false;
+ bool m_useCustomAnalyzer = false;
QmllsSettings m_qmllsSettings;
QString m_uiQmlOpenMode;
QString m_formatCommand;
QString m_formatCommandOptions;
+ QSet<int> m_disabledMessages;
+ QSet<int> m_disabledMessagesForNonQuickUi;
};
namespace Internal {
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp
index b37d0e8758..5e72cd642b 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.cpp
+++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp
@@ -12,8 +12,8 @@
#include <extensionsystem/pluginmanager.h>
#include <texteditor/basefilefind.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/filesearch.h>
-#include <utils/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsbind.h>
@@ -26,19 +26,10 @@
#include <qmljs/parser/qmljsast_p.h>
#include <qmljstools/qmljsmodelmanager.h>
-#include "qmljseditorconstants.h"
-
-#include <QApplication>
#include <QDebug>
-#include <QDir>
#include <QFuture>
-#include <QLabel>
-#include <QTime>
-#include <QTimer>
#include <QtConcurrentMap>
-#include <functional>
-
using namespace Core;
using namespace QmlJS;
using namespace QmlJS::AST;
@@ -704,7 +695,7 @@ class ProcessFile
using Usage = FindReferences::Usage;
const QString name;
const ObjectValue *scope;
- QFutureInterface<Usage> *future;
+ QPromise<Usage> &m_promise;
public:
// needed by QtConcurrent
@@ -714,16 +705,15 @@ public:
ProcessFile(const ContextPtr &context,
const QString &name,
const ObjectValue *scope,
- QFutureInterface<Usage> *future)
- : context(context), name(name), scope(scope), future(future)
+ QPromise<Usage> &promise)
+ : context(context), name(name), scope(scope), m_promise(promise)
{ }
QList<Usage> operator()(const Utils::FilePath &fileName)
{
QList<Usage> usages;
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise.suspendIfRequested();
+ if (m_promise.isCanceled())
return usages;
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
Document::Ptr doc = context->snapshot().document(fileName);
@@ -739,8 +729,7 @@ public:
loc.startLine,
loc.startColumn - 1,
loc.length));
- if (future->isPaused())
- future->waitForResume();
+ m_promise.suspendIfRequested();
return usages;
}
};
@@ -751,7 +740,7 @@ class SearchFileForType
using Usage = FindReferences::Usage;
const QString name;
const ObjectValue *scope;
- QFutureInterface<Usage> *future;
+ QPromise<Usage> &m_promise;
public:
// needed by QtConcurrent
@@ -761,16 +750,15 @@ public:
SearchFileForType(const ContextPtr &context,
const QString &name,
const ObjectValue *scope,
- QFutureInterface<Usage> *future)
- : context(context), name(name), scope(scope), future(future)
+ QPromise<Usage> &promise)
+ : context(context), name(name), scope(scope), m_promise(promise)
{ }
QList<Usage> operator()(const Utils::FilePath &fileName)
{
QList<Usage> usages;
- if (future->isPaused())
- future->waitForResume();
- if (future->isCanceled())
+ m_promise.suspendIfRequested();
+ if (m_promise.isCanceled())
return usages;
Document::Ptr doc = context->snapshot().document(fileName);
if (!doc)
@@ -781,8 +769,7 @@ public:
const FindTypeUsages::Result results = findUsages(name, scope);
for (const SourceLocation &loc : results)
usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
- if (future->isPaused())
- future->waitForResume();
+ m_promise.suspendIfRequested();
return usages;
}
};
@@ -790,7 +777,7 @@ public:
class UpdateUI
{
using Usage = FindReferences::Usage;
- QFutureInterface<Usage> *future;
+ QPromise<Usage> &m_promise;
public:
// needed by QtConcurrent
@@ -798,14 +785,13 @@ public:
using second_argument_type = const QList<Usage> &;
using result_type = void;
- UpdateUI(QFutureInterface<Usage> *future): future(future) {}
+ UpdateUI(QPromise<Usage> &promise): m_promise(promise) {}
void operator()(QList<Usage> &, const QList<Usage> &usages)
{
for (const Usage &u : usages)
- future->reportResult(u);
-
- future->setProgressValue(future->progressValue() + 1);
+ m_promise.addResult(u);
+ m_promise.setProgressValue(m_promise.future().progressValue() + 1);
}
};
@@ -817,12 +803,11 @@ FindReferences::FindReferences(QObject *parent)
m_watcher.setPendingResultsLimit(1);
connect(&m_watcher, &QFutureWatcherBase::resultsReadyAt, this, &FindReferences::displayResults);
connect(&m_watcher, &QFutureWatcherBase::finished, this, &FindReferences::searchFinished);
- m_synchronizer.setCancelOnWait(true);
}
FindReferences::~FindReferences() = default;
-static void find_helper(QFutureInterface<FindReferences::Usage> &future,
+static void find_helper(QPromise<FindReferences::Usage> &promise,
const ModelManagerInterface::WorkingCopy &workingCopy,
Snapshot snapshot,
const Utils::FilePath &fileName,
@@ -885,7 +870,7 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
}
files = Utils::filteredUnique(files);
- future.setProgressRange(0, files.size());
+ promise.setProgressRange(0, files.size());
// report a dummy usage to indicate the search is starting
FindReferences::Usage searchStarting(Utils::FilePath::fromString(replacement), name, 0, 0, 0);
@@ -894,10 +879,10 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
const ObjectValue *typeValue = value_cast<ObjectValue>(findTarget.targetValue());
if (!typeValue)
return;
- future.reportResult(searchStarting);
+ promise.addResult(searchStarting);
- SearchFileForType process(context, name, typeValue, &future);
- UpdateUI reduce(&future);
+ SearchFileForType process(context, name, typeValue, promise);
+ UpdateUI reduce(promise);
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
} else {
@@ -909,21 +894,21 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
return;
if (!scope->className().isEmpty())
searchStarting.lineText.prepend(scope->className() + QLatin1Char('.'));
- future.reportResult(searchStarting);
+ promise.addResult(searchStarting);
- ProcessFile process(context, name, scope, &future);
- UpdateUI reduce(&future);
+ ProcessFile process(context, name, scope, promise);
+ UpdateUI reduce(promise);
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
}
- future.setProgressValue(files.size());
+ promise.setProgressValue(files.size());
}
void FindReferences::findUsages(const Utils::FilePath &fileName, quint32 offset)
{
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
- QFuture<Usage> result = Utils::runAsync(&find_helper, ModelManagerInterface::workingCopy(),
+ QFuture<Usage> result = Utils::asyncRun(&find_helper, ModelManagerInterface::workingCopy(),
modelManager->snapshot(), fileName, offset, QString());
m_watcher.setFuture(result);
m_synchronizer.addFuture(result);
@@ -940,7 +925,7 @@ void FindReferences::renameUsages(const Utils::FilePath &fileName,
if (newName.isNull())
newName = QLatin1String("");
- QFuture<Usage> result = Utils::runAsync(&find_helper, ModelManagerInterface::workingCopy(),
+ QFuture<Usage> result = Utils::asyncRun(&find_helper, ModelManagerInterface::workingCopy(),
modelManager->snapshot(), fileName, offset, newName);
m_watcher.setFuture(result);
m_synchronizer.addFuture(result);
@@ -1007,7 +992,7 @@ void FindReferences::displayResults(int first, int last)
this, &FindReferences::onReplaceButtonClicked);
}
connect(m_currentSearch.data(), &SearchResult::activated,
- [](const Core::SearchResultItem& item) {
+ [](const Utils::SearchResultItem &item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
connect(m_currentSearch.data(), &SearchResult::canceled, this, &FindReferences::cancel);
@@ -1028,7 +1013,7 @@ void FindReferences::displayResults(int first, int last)
}
for (int index = first; index != last; ++index) {
Usage result = m_watcher.future().resultAt(index);
- SearchResultItem item;
+ Utils::SearchResultItem item;
item.setFilePath(result.path);
item.setLineText(result.lineText);
item.setMainRange(result.line, result.col, result.len);
@@ -1056,7 +1041,8 @@ void FindReferences::setPaused(bool paused)
m_watcher.setPaused(paused);
}
-void FindReferences::onReplaceButtonClicked(const QString &text, const QList<SearchResultItem> &items, bool preserveCase)
+void FindReferences::onReplaceButtonClicked(const QString &text,
+ const Utils::SearchResultItems &items, bool preserveCase)
{
const Utils::FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(text,
items,
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.h b/src/plugins/qmljseditor/qmljsfindreferences.h
index 22bdff0a95..c45d77ac18 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.h
+++ b/src/plugins/qmljseditor/qmljsfindreferences.h
@@ -7,6 +7,7 @@
#include <utils/filepath.h>
#include <utils/futuresynchronizer.h>
+#include <utils/searchresultitem.h>
#include <QObject>
#include <QFutureWatcher>
@@ -14,10 +15,7 @@
QT_FORWARD_DECLARE_CLASS(QTimer)
-namespace Core {
-class SearchResultItem;
-class SearchResult;
-} // namespace Core
+namespace Core { class SearchResult; }
namespace QmlJSEditor {
@@ -64,7 +62,8 @@ private:
void searchFinished();
void cancel();
void setPaused(bool paused);
- void onReplaceButtonClicked(const QString &text, const QList<Core::SearchResultItem> &items, bool preserveCase);
+ void onReplaceButtonClicked(const QString &text, const Utils::SearchResultItems &items,
+ bool preserveCase);
QPointer<Core::SearchResult> m_currentSearch;
QFutureWatcher<Usage> m_watcher;
diff --git a/src/plugins/qmljseditor/qmljsquickfix.h b/src/plugins/qmljseditor/qmljsquickfix.h
index 643334a94c..b050ba4d1f 100644
--- a/src/plugins/qmljseditor/qmljsquickfix.h
+++ b/src/plugins/qmljseditor/qmljsquickfix.h
@@ -44,7 +44,7 @@ protected:
const QmlJSTools::SemanticInfo &semanticInfo() const;
- /// \returns The name of the file for for which this operation is invoked.
+ /// Returns The name of the file for for which this operation is invoked.
Utils::FilePath fileName() const;
private:
diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
index 0ca826cb00..67e14dfbcd 100644
--- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
+++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp
@@ -20,9 +20,9 @@
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/fontsettings.h>
+#include <utils/async.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
#include <QDebug>
#include <QTextDocument>
@@ -156,11 +156,11 @@ public:
AddMessagesHighlights,
SkipMessagesHighlights,
};
- CollectionTask(QFutureInterface<SemanticHighlighter::Use> &futureInterface,
+ CollectionTask(QPromise<SemanticHighlighter::Use> &promise,
const QmlJSTools::SemanticInfo &semanticInfo,
const TextEditor::FontSettings &fontSettings,
Flags flags)
- : m_futureInterface(futureInterface)
+ : m_promise(promise)
, m_semanticInfo(semanticInfo)
, m_fontSettings(fontSettings)
, m_scopeChain(semanticInfo.scopeChain())
@@ -211,7 +211,7 @@ public:
protected:
void accept(Node *ast)
{
- if (m_futureInterface.isCanceled())
+ if (m_promise.isCanceled())
return;
if (ast)
ast->accept(this);
@@ -219,7 +219,7 @@ protected:
void scopedAccept(Node *ast, Node *child)
{
- if (m_futureInterface.isCanceled())
+ if (m_promise.isCanceled())
return;
m_scopeBuilder.push(ast);
accept(child);
@@ -510,12 +510,13 @@ private:
return;
Utils::sort(m_uses, sortByLinePredicate);
- m_futureInterface.reportResults(m_uses);
+ for (const SemanticHighlighter::Use &use : std::as_const(m_uses))
+ m_promise.addResult(use);
m_uses.clear();
m_uses.reserve(chunkSize);
}
- QFutureInterface<SemanticHighlighter::Use> &m_futureInterface;
+ QPromise<SemanticHighlighter::Use> &m_promise;
const QmlJSTools::SemanticInfo &m_semanticInfo;
const TextEditor::FontSettings &m_fontSettings;
ScopeChain m_scopeChain;
@@ -541,7 +542,6 @@ SemanticHighlighter::SemanticHighlighter(QmlJSEditorDocument *document)
this, &SemanticHighlighter::applyResults);
connect(&m_watcher, &QFutureWatcherBase::finished,
this, &SemanticHighlighter::finished);
- m_futureSynchronizer.setCancelOnWait(true);
}
void SemanticHighlighter::rerun(const QmlJSTools::SemanticInfo &semanticInfo)
@@ -549,11 +549,8 @@ void SemanticHighlighter::rerun(const QmlJSTools::SemanticInfo &semanticInfo)
m_watcher.cancel();
m_startRevision = m_document->document()->revision();
- auto future = Utils::runAsync(QThread::LowestPriority,
- &SemanticHighlighter::run,
- this,
- semanticInfo,
- TextEditor::TextEditorSettings::fontSettings());
+ auto future = Utils::asyncRun(QThread::LowestPriority, &SemanticHighlighter::run, this,
+ semanticInfo, TextEditor::TextEditorSettings::fontSettings());
m_watcher.setFuture(future);
m_futureSynchronizer.addFuture(future);
}
@@ -590,11 +587,11 @@ void SemanticHighlighter::finished()
m_document->syntaxHighlighter(), m_watcher.future());
}
-void SemanticHighlighter::run(QFutureInterface<SemanticHighlighter::Use> &futureInterface,
+void SemanticHighlighter::run(QPromise<Use> &promise,
const QmlJSTools::SemanticInfo &semanticInfo,
const TextEditor::FontSettings &fontSettings)
{
- CollectionTask task(futureInterface,
+ CollectionTask task(promise,
semanticInfo,
fontSettings,
(m_enableWarnings ? CollectionTask::AddMessagesHighlights
diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.h b/src/plugins/qmljseditor/qmljssemantichighlighter.h
index 6ea1e85c6e..74c51d12d7 100644
--- a/src/plugins/qmljseditor/qmljssemantichighlighter.h
+++ b/src/plugins/qmljseditor/qmljssemantichighlighter.h
@@ -62,7 +62,7 @@ public:
private:
void applyResults(int from, int to);
void finished();
- void run(QFutureInterface<Use> &futureInterface,
+ void run(QPromise<Use> &promise,
const QmlJSTools::SemanticInfo &semanticInfo,
const TextEditor::FontSettings &fontSettings);
diff --git a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp
index e0a20c1845..764f125e54 100644
--- a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp
+++ b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp
@@ -104,7 +104,7 @@ QmlJSTools::SemanticInfo SemanticInfoUpdater::makeNewSemanticInfo(const QmlJS::D
semanticInfo.staticAnalysisMessages = jsonChecker(schema);
}
} else {
- Check checker(doc, semanticInfo.context);
+ Check checker(doc, semanticInfo.context, Core::ICore::settings());
semanticInfo.staticAnalysisMessages = checker();
}
diff --git a/src/plugins/qmljseditor/qmllssettings.cpp b/src/plugins/qmljseditor/qmllssettings.cpp
index 572d8aec78..eb98c041fa 100644
--- a/src/plugins/qmljseditor/qmllssettings.cpp
+++ b/src/plugins/qmljseditor/qmllssettings.cpp
@@ -90,7 +90,7 @@ void QmllsSettingsManager::setupAutoupdate()
void QmllsSettingsManager::checkForChanges()
{
FilePath newLatest = evaluateLatestQmlls();
- QmllsSettings newSettings = QmlJsEditingSettings::get().qmllsSettigs();
+ QmllsSettings newSettings = QmlJsEditingSettings::get().qmllsSettings();
if (m_lastSettings == newSettings && newLatest == m_latestQmlls)
return;
qCDebug(qmllsLog) << "qmlls settings changed:" << newSettings.useQmlls
diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp
index 08019019da..ca985f4022 100644
--- a/src/plugins/qmljseditor/qmltaskmanager.cpp
+++ b/src/plugins/qmljseditor/qmltaskmanager.cpp
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmltaskmanager.h"
-#include "qmljseditor.h"
#include "qmljseditorconstants.h"
+#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/taskhub.h>
@@ -13,7 +13,7 @@
#include <qmljs/qmljsconstants.h>
#include <qmljs/qmljslink.h>
#include <qmljs/qmljscheck.h>
-#include <utils/runextensions.h>
+#include <utils/async.h>
#include <QDebug>
#include <QtConcurrentRun>
@@ -57,7 +57,7 @@ static Tasks convertToTasks(const QList<StaticAnalysis::Message> &messages, cons
return convertToTasks(diagnostics, fileName, category);
}
-void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future,
+void QmlTaskManager::collectMessages(QPromise<FileErrorMessages> &promise,
Snapshot snapshot,
const QList<ModelManagerInterface::ProjectInfo> &projectInfos,
ViewerContext vContext,
@@ -88,7 +88,7 @@ void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future
fileName,
Constants::TASK_CATEGORY_QML_ANALYSIS);
- Check checker(document, context);
+ Check checker(document, context, Core::ICore::settings());
result.tasks += convertToTasks(checker(),
fileName,
Constants::TASK_CATEGORY_QML_ANALYSIS);
@@ -96,8 +96,8 @@ void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future
}
if (!result.tasks.isEmpty())
- future.reportResult(result);
- if (future.isCanceled())
+ promise.addResult(result);
+ if (promise.isCanceled())
break;
}
}
@@ -127,8 +127,7 @@ void QmlTaskManager::updateMessagesNow(bool updateSemantic)
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
// process them
- QFuture<FileErrorMessages> future =
- Utils::runAsync(
+ QFuture<FileErrorMessages> future = Utils::asyncRun(
&collectMessages, modelManager->newestSnapshot(), modelManager->projectInfos(),
modelManager->defaultVContext(Dialect::AnyLanguage), updateSemantic);
m_messageCollector.setFuture(future);
diff --git a/src/plugins/qmljseditor/qmltaskmanager.h b/src/plugins/qmljseditor/qmltaskmanager.h
index 26fa3bd347..226fd2203d 100644
--- a/src/plugins/qmljseditor/qmltaskmanager.h
+++ b/src/plugins/qmljseditor/qmltaskmanager.h
@@ -45,7 +45,7 @@ private:
Utils::FilePath fileName;
ProjectExplorer::Tasks tasks;
};
- static void collectMessages(QFutureInterface<FileErrorMessages> &future,
+ static void collectMessages(QPromise<FileErrorMessages> &promise,
QmlJS::Snapshot snapshot,
const QList<QmlJS::ModelManagerInterface::ProjectInfo> &projectInfos,
QmlJS::ViewerContext vContext,
diff --git a/src/plugins/qmljstools/qmljsbundleprovider.cpp b/src/plugins/qmljstools/qmljsbundleprovider.cpp
index b4902aa960..623a6f4684 100644
--- a/src/plugins/qmljstools/qmljsbundleprovider.cpp
+++ b/src/plugins/qmljstools/qmljsbundleprovider.cpp
@@ -25,7 +25,8 @@ BasicBundleProvider::BasicBundleProvider(QObject *parent) :
IBundleProvider(parent)
{ }
-QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
+QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName,
+ QtSupport::QtVersion *qtVersion)
{
static bool wroteErrors = false;
QmlBundle res;
@@ -37,7 +38,8 @@ QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
return res;
}
QStringList errors;
- if (!res.readFrom(defaultBundlePath.toString(), &errors) && !wroteErrors) {
+ bool stripVersions = qtVersion && qtVersion->qtVersion().majorVersion() > 5;
+ if (!res.readFrom(defaultBundlePath.toString(), stripVersions, &errors) && !wroteErrors) {
qWarning() << "BasicBundleProvider: ERROR reading " << defaultBundlePath
<< " : " << errors;
wroteErrors = true;
@@ -45,24 +47,31 @@ QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
return res;
}
-QmlBundle BasicBundleProvider::defaultQt5QtQuick2Bundle()
+QmlBundle BasicBundleProvider::defaultQt5QtQuick2Bundle(QtSupport::QtVersion *qtVersion)
{
- return defaultBundle(QLatin1String("qt5QtQuick2-bundle.json"));
+ QmlBundle result = defaultBundle(QLatin1String("qt5QtQuick2-bundle.json"), qtVersion);
+ if (!qtVersion || qtVersion->qtVersion().majorVersion() < 6)
+ return result;
+ if (Utils::HostOsInfo::isMacHost())
+ result.merge(defaultBundle(QLatin1String("qt5QtQuick2ext-macos-bundle.json"), qtVersion));
+ if (Utils::HostOsInfo::isWindowsHost())
+ result.merge(defaultBundle(QLatin1String("qt5QtQuick2ext-win-bundle.json"), qtVersion));
+ return result;
}
QmlBundle BasicBundleProvider::defaultQbsBundle()
{
- return defaultBundle(QLatin1String("qbs-bundle.json"));
+ return defaultBundle(QLatin1String("qbs-bundle.json"), nullptr);
}
QmlBundle BasicBundleProvider::defaultQmltypesBundle()
{
- return defaultBundle(QLatin1String("qmltypes-bundle.json"));
+ return defaultBundle(QLatin1String("qmltypes-bundle.json"), nullptr);
}
QmlBundle BasicBundleProvider::defaultQmlprojectBundle()
{
- return defaultBundle(QLatin1String("qmlproject-bundle.json"));
+ return defaultBundle(QLatin1String("qmlproject-bundle.json"), nullptr);
}
void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
@@ -77,7 +86,7 @@ void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit);
if (!qtVersion) {
- QmlBundle b2(defaultQt5QtQuick2Bundle());
+ QmlBundle b2(defaultQt5QtQuick2Bundle(qtVersion));
bundles.mergeBundleForLanguage(Dialect::Qml, b2);
bundles.mergeBundleForLanguage(Dialect::QmlQtQuick2, b2);
bundles.mergeBundleForLanguage(Dialect::QmlQtQuick2Ui, b2);
@@ -90,17 +99,18 @@ void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
qtQuick2Bundles.setNameFilters(QStringList(QLatin1String("*-bundle.json")));
QmlBundle qtQuick2Bundle;
QFileInfoList list = qtQuick2Bundles.entryInfoList();
+ bool stripVersions = qtVersion->qtVersion().majorVersion() > 5;
for (int i = 0; i < list.size(); ++i) {
QmlBundle bAtt;
QStringList errors;
- if (!bAtt.readFrom(list.value(i).filePath(), &errors))
+ if (!bAtt.readFrom(list.value(i).filePath(), stripVersions, &errors))
qWarning() << "BasicBundleProvider: ERROR reading " << list[i].filePath() << " : "
<< errors;
qtQuick2Bundle.merge(bAtt);
}
if (!qtQuick2Bundle.supportedImports().contains(QLatin1String("QtQuick 2."),
PersistentTrie::Partial)) {
- qtQuick2Bundle.merge(defaultQt5QtQuick2Bundle());
+ qtQuick2Bundle.merge(defaultQt5QtQuick2Bundle(qtVersion));
}
qtQuick2Bundle.replaceVars(myReplacements);
bundles.mergeBundleForLanguage(Dialect::Qml, qtQuick2Bundle);
diff --git a/src/plugins/qmljstools/qmljsbundleprovider.h b/src/plugins/qmljstools/qmljsbundleprovider.h
index 1f7e6868d0..d8cd835150 100644
--- a/src/plugins/qmljstools/qmljsbundleprovider.h
+++ b/src/plugins/qmljstools/qmljsbundleprovider.h
@@ -19,6 +19,10 @@ class QmlLanguageBundles;
class QmlBundle;
} // namespace QmlJS
+namespace QtSupport {
+class QtVersion;
+}
+
namespace QmlJSTools {
class QMLJSTOOLS_EXPORT IBundleProvider : public QObject
@@ -43,8 +47,9 @@ public:
void mergeBundlesForKit(ProjectExplorer::Kit *kit, QmlJS::QmlLanguageBundles &bundles,
const QHash<QString,QString> &replacements) override;
- static QmlJS::QmlBundle defaultBundle(const QString &bundleInfoName);
- static QmlJS::QmlBundle defaultQt5QtQuick2Bundle();
+ static QmlJS::QmlBundle defaultBundle(const QString &bundleInfoName,
+ QtSupport::QtVersion *qtVersion);
+ static QmlJS::QmlBundle defaultQt5QtQuick2Bundle(QtSupport::QtVersion *qtVersion);
static QmlJS::QmlBundle defaultQbsBundle();
static QmlJS::QmlBundle defaultQmltypesBundle();
static QmlJS::QmlBundle defaultQmlprojectBundle();
diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp
index cef7df08c3..fc97aa53c8 100644
--- a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp
+++ b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp
@@ -5,7 +5,6 @@
#include "qmljscodestylepreferences.h"
#include "qmljscodestylepreferenceswidget.h"
-#include "qmljsindenter.h"
#include "qmljsqtstylecodeformatter.h"
#include "qmljstoolsconstants.h"
#include "qmljstoolssettings.h"
@@ -25,13 +24,13 @@
#include <utils/layoutbuilder.h>
#include <QTextStream>
+#include <QVBoxLayout>
using namespace TextEditor;
-namespace QmlJSTools {
-namespace Internal {
+namespace QmlJSTools::Internal {
-// ------------------ CppCodeStyleSettingsWidget
+// QmlJSCodeStylePreferencesWidget
QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(
const TextEditor::ICodeStylePreferencesFactory *factory, QWidget *parent)
@@ -47,7 +46,7 @@ QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(
decorateEditor(TextEditorSettings::fontSettings());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
Column {
m_tabPreferencesWidget,
@@ -55,7 +54,8 @@ QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(
st,
},
m_previewTextEdit,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
this, &QmlJSCodeStylePreferencesWidget::decorateEditor);
@@ -120,59 +120,60 @@ void QmlJSCodeStylePreferencesWidget::updatePreview()
tc.endEditBlock();
}
-// ------------------ CppCodeStyleSettingsPage
+// QmlJSCodeStyleSettingsPageWidget
-QmlJSCodeStyleSettingsPage::QmlJSCodeStyleSettingsPage()
+class QmlJSCodeStyleSettingsPageWidget : public Core::IOptionsPageWidget
{
- setId(Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
- setDisplayName(Tr::tr(Constants::QML_JS_CODE_STYLE_SETTINGS_NAME));
- setCategory(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML);
- setDisplayCategory(Tr::tr("Qt Quick"));
- setCategoryIconPath(":/qmljstools/images/settingscategory_qml.png");
-}
+public:
+ QmlJSCodeStyleSettingsPageWidget()
+ {
-QWidget *QmlJSCodeStyleSettingsPage::widget()
-{
- if (!m_widget) {
QmlJSCodeStylePreferences *originalPreferences
= QmlJSToolsSettings::globalCodeStyle();
- m_preferences = new QmlJSCodeStylePreferences(m_widget);
- m_preferences->setDelegatingPool(originalPreferences->delegatingPool());
- m_preferences->setCodeStyleSettings(originalPreferences->codeStyleSettings());
- m_preferences->setTabSettings(originalPreferences->tabSettings());
- m_preferences->setCurrentDelegate(originalPreferences->currentDelegate());
- m_preferences->setId(originalPreferences->id());
- m_widget = new CodeStyleEditor(TextEditorSettings::codeStyleFactory(QmlJSTools::Constants::QML_JS_SETTINGS_ID),
- m_preferences);
+ m_preferences.setDelegatingPool(originalPreferences->delegatingPool());
+ m_preferences.setCodeStyleSettings(originalPreferences->codeStyleSettings());
+ m_preferences.setTabSettings(originalPreferences->tabSettings());
+ m_preferences.setCurrentDelegate(originalPreferences->currentDelegate());
+ m_preferences.setId(originalPreferences->id());
+
+ auto vbox = new QVBoxLayout(this);
+ vbox->addWidget(new CodeStyleEditor(
+ TextEditorSettings::codeStyleFactory(QmlJSTools::Constants::QML_JS_SETTINGS_ID),
+ &m_preferences));
}
- return m_widget;
-}
-void QmlJSCodeStyleSettingsPage::apply()
-{
- if (m_widget) {
+ void apply() final
+ {
QSettings *s = Core::ICore::settings();
QmlJSCodeStylePreferences *originalPreferences = QmlJSToolsSettings::globalCodeStyle();
- if (originalPreferences->codeStyleSettings() != m_preferences->codeStyleSettings()) {
- originalPreferences->setCodeStyleSettings(m_preferences->codeStyleSettings());
+ if (originalPreferences->codeStyleSettings() != m_preferences.codeStyleSettings()) {
+ originalPreferences->setCodeStyleSettings(m_preferences.codeStyleSettings());
originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
}
- if (originalPreferences->tabSettings() != m_preferences->tabSettings()) {
- originalPreferences->setTabSettings(m_preferences->tabSettings());
+ if (originalPreferences->tabSettings() != m_preferences.tabSettings()) {
+ originalPreferences->setTabSettings(m_preferences.tabSettings());
originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
}
- if (originalPreferences->currentDelegate() != m_preferences->currentDelegate()) {
- originalPreferences->setCurrentDelegate(m_preferences->currentDelegate());
+ if (originalPreferences->currentDelegate() != m_preferences.currentDelegate()) {
+ originalPreferences->setCurrentDelegate(m_preferences.currentDelegate());
originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
}
}
-}
-void QmlJSCodeStyleSettingsPage::finish()
+ QmlJSCodeStylePreferences m_preferences;
+};
+
+// QmlJSCodeStyleSettingsPage
+
+QmlJSCodeStyleSettingsPage::QmlJSCodeStyleSettingsPage()
{
- delete m_widget;
+ setId(Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
+ setDisplayName(Tr::tr(Constants::QML_JS_CODE_STYLE_SETTINGS_NAME));
+ setCategory(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML);
+ setDisplayCategory(Tr::tr("Qt Quick"));
+ setCategoryIconPath(":/qmljstools/images/settingscategory_qml.png");
+ setWidgetCreator([] { return new QmlJSCodeStyleSettingsPageWidget; });
}
-} // namespace Internal
-} // namespace QmlJSTools
+} // QmlJSTools::Internal
diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.h b/src/plugins/qmljstools/qmljscodestylesettingspage.h
index e9b244865b..853d482b95 100644
--- a/src/plugins/qmljstools/qmljscodestylesettingspage.h
+++ b/src/plugins/qmljstools/qmljscodestylesettingspage.h
@@ -6,16 +6,8 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <texteditor/icodestylepreferencesfactory.h>
-#include <QWidget>
-#include <QPointer>
-
-QT_BEGIN_NAMESPACE
-class QSettings;
-QT_END_NAMESPACE
-
namespace TextEditor {
class FontSettings;
- class CodeStyleEditor;
class SimpleCodeStylePreferencesWidget;
class SnippetEditorWidget;
}
@@ -54,14 +46,6 @@ class QmlJSCodeStyleSettingsPage : public Core::IOptionsPage
{
public:
QmlJSCodeStyleSettingsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QmlJSCodeStylePreferences *m_preferences = nullptr;
- QPointer<TextEditor::CodeStyleEditor> m_widget;
};
} // namespace Internal
diff --git a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp
index 455ffcd0f8..34919f6144 100644
--- a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp
+++ b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp
@@ -19,15 +19,16 @@ QmlJSCodeStyleSettingsWidget::QmlJSCodeStyleSettingsWidget(QWidget *parent)
m_lineLengthSpinBox->setMinimum(0);
m_lineLengthSpinBox->setMaximum(999);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
title(Tr::tr("Qml JS Code Style")),
Form {
Tr::tr("&Line length:"), m_lineLengthSpinBox, br,
}
- }
- }.attachTo(this, WithoutMargins);
+ },
+ noMargin
+ }.attachTo(this);
connect(m_lineLengthSpinBox, &QSpinBox::valueChanged,
this, &QmlJSCodeStyleSettingsWidget::slotSettingsChanged);
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.cpp b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
index 4d4ce05fb7..34a60040f3 100644
--- a/src/plugins/qmljstools/qmljsfunctionfilter.cpp
+++ b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
@@ -5,80 +5,85 @@
#include "qmljslocatordata.h"
#include "qmljstoolstr.h"
-#include <coreplugin/editormanager/editormanager.h>
+#include <extensionsystem/pluginmanager.h>
+
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <QRegularExpression>
-#include <numeric>
-
+using namespace Core;
using namespace QmlJSTools::Internal;
+using namespace Utils;
Q_DECLARE_METATYPE(LocatorData::Entry)
-FunctionFilter::FunctionFilter(LocatorData *data, QObject *parent)
- : Core::ILocatorFilter(parent)
- , m_data(data)
+QmlJSFunctionsFilter::QmlJSFunctionsFilter(LocatorData *data)
+ : m_data(data)
{
setId("Functions");
setDisplayName(Tr::tr("QML Functions"));
+ setDescription(Tr::tr("Locates QML functions in any open project."));
setDefaultShortcutString("m");
- setDefaultIncludedByDefault(false);
}
-FunctionFilter::~FunctionFilter() = default;
-
-QList<Core::LocatorFilterEntry> FunctionFilter::matchesFor(
- QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry)
+static void matches(QPromise<void> &promise, const LocatorStorage &storage,
+ const QHash<FilePath, QList<LocatorData::Entry>> &locatorEntries)
{
- QList<Core::LocatorFilterEntry> entries[int(MatchLevel::Count)];
- const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
-
- const QRegularExpression regexp = createRegExp(entry);
+ const QString input = storage.input();
+ const Qt::CaseSensitivity caseSensitivityForPrefix = ILocatorFilter::caseSensitivity(input);
+ const QRegularExpression regexp = ILocatorFilter::createRegExp(input);
if (!regexp.isValid())
- return {};
+ return;
- const QHash<Utils::FilePath, QList<LocatorData::Entry>> locatorEntries = m_data->entries();
+ LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)];
for (const QList<LocatorData::Entry> &items : locatorEntries) {
- if (future.isCanceled())
- break;
-
for (const LocatorData::Entry &info : items) {
+ if (promise.isCanceled())
+ return;
+
if (info.type != LocatorData::Function)
continue;
const QRegularExpressionMatch match = regexp.match(info.symbolName);
if (match.hasMatch()) {
- QVariant id = QVariant::fromValue(info);
- Core::LocatorFilterEntry filterEntry(this, info.displayName, id/*, info.icon*/);
+ LocatorFilterEntry filterEntry;
+ filterEntry.displayName = info.displayName;
+ filterEntry.linkForEditor = {info.fileName, info.line, info.column};
filterEntry.extraInfo = info.extraInfo;
- filterEntry.highlightInfo = highlightInfo(match);
+ filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match);
- if (filterEntry.displayName.startsWith(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Best)].append(filterEntry);
- else if (filterEntry.displayName.contains(entry, caseSensitivityForPrefix))
- entries[int(MatchLevel::Better)].append(filterEntry);
+ if (filterEntry.displayName.startsWith(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Best)].append(filterEntry);
+ else if (filterEntry.displayName.contains(input, caseSensitivityForPrefix))
+ entries[int(ILocatorFilter::MatchLevel::Better)].append(filterEntry);
else
- entries[int(MatchLevel::Good)].append(filterEntry);
+ entries[int(ILocatorFilter::MatchLevel::Good)].append(filterEntry);
}
}
}
for (auto &entry : entries) {
+ if (promise.isCanceled())
+ return;
+
if (entry.size() < 1000)
- Utils::sort(entry, Core::LocatorFilterEntry::compareLexigraphically);
+ Utils::sort(entry, LocatorFilterEntry::compareLexigraphically);
}
-
- return std::accumulate(std::begin(entries), std::end(entries), QList<Core::LocatorFilterEntry>());
+ storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries),
+ LocatorFilterEntries()));
}
-void FunctionFilter::accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
+LocatorMatcherTasks QmlJSFunctionsFilter::matchers()
{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- const LocatorData::Entry entry = qvariant_cast<LocatorData::Entry>(selection.internalData);
- Core::EditorManager::openEditorAt({entry.fileName, entry.line, entry.column});
+ using namespace Tasking;
+
+ TreeStorage<LocatorStorage> storage;
+
+ const auto onSetup = [storage, entries = m_data->entries()](Async<void> &async) {
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(matches, *storage, entries);
+ };
+
+ return {{AsyncTask<void>(onSetup), storage}};
}
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.h b/src/plugins/qmljstools/qmljsfunctionfilter.h
index 83b93e8f36..3775259fb2 100644
--- a/src/plugins/qmljstools/qmljsfunctionfilter.h
+++ b/src/plugins/qmljstools/qmljsfunctionfilter.h
@@ -5,27 +5,19 @@
#include <coreplugin/locator/ilocatorfilter.h>
-namespace QmlJSTools {
-namespace Internal {
+namespace QmlJSTools::Internal {
class LocatorData;
-class FunctionFilter : public Core::ILocatorFilter
+class QmlJSFunctionsFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit FunctionFilter(LocatorData *data, QObject *parent = nullptr);
- ~FunctionFilter() override;
-
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+ QmlJSFunctionsFilter(LocatorData *data);
private:
+ Core::LocatorMatcherTasks matchers() final;
+
LocatorData *m_data = nullptr;
};
-} // namespace Internal
-} // namespace QmlJSTools
+} // namespace QmlJSTools::Internal
diff --git a/src/plugins/qmljstools/qmljslocatordata.cpp b/src/plugins/qmljstools/qmljslocatordata.cpp
index 679a230e8e..477447c9e3 100644
--- a/src/plugins/qmljstools/qmljslocatordata.cpp
+++ b/src/plugins/qmljstools/qmljslocatordata.cpp
@@ -4,7 +4,7 @@
#include "qmljslocatordata.h"
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsutils.h>
@@ -40,10 +40,10 @@ LocatorData::LocatorData()
connect(manager, &ModelManagerInterface::aboutToRemoveFiles,
this, &LocatorData::onAboutToRemoveFiles);
- ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance();
+ ProjectExplorer::ProjectManager *session = ProjectExplorer::ProjectManager::instance();
if (session)
connect(session,
- &ProjectExplorer::SessionManager::projectRemoved,
+ &ProjectExplorer::ProjectManager::projectRemoved,
this,
[this](ProjectExplorer::Project *) { m_entries.clear(); });
}
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index da1d82b73d..914a4f7794 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -18,10 +18,11 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsbind.h>
@@ -273,9 +274,9 @@ void ModelManager::delayedInitialization()
connect(cppModelManager, &CppEditor::CppModelManager::documentUpdated,
this, &ModelManagerInterface::maybeQueueCppQmlTypeUpdate, Qt::DirectConnection);
- connect(SessionManager::instance(), &SessionManager::projectRemoved,
+ connect(ProjectManager::instance(), &ProjectManager::projectRemoved,
this, &ModelManager::removeProjectInfo);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &ModelManager::updateDefaultProjectInfo);
ViewerContext qbsVContext;
@@ -323,7 +324,7 @@ ModelManagerInterface::WorkingCopy ModelManager::workingCopyInternal() const
void ModelManager::updateDefaultProjectInfo()
{
// needs to be performed in the ui thread
- Project *currentProject = SessionManager::startupProject();
+ Project *currentProject = ProjectManager::startupProject();
setDefaultProject(containsProject(currentProject)
? projectInfo(currentProject)
: defaultProjectInfoForProject(currentProject, {}),
diff --git a/src/plugins/qmljstools/qmljsrefactoringchanges.h b/src/plugins/qmljstools/qmljsrefactoringchanges.h
index d16564832b..33545e2bfc 100644
--- a/src/plugins/qmljstools/qmljsrefactoringchanges.h
+++ b/src/plugins/qmljstools/qmljsrefactoringchanges.h
@@ -24,7 +24,7 @@ public:
QmlJS::Document::Ptr qmljsDocument() const;
/*!
- \returns the offset in the document for the start position of the given
+ Returns the offset in the document for the start position of the given
source location.
*/
unsigned startOf(const QmlJS::SourceLocation &loc) const;
diff --git a/src/plugins/qmljstools/qmljstools.qbs b/src/plugins/qmljstools/qmljstools.qbs
index 68c9a9dfc1..d13342e8dd 100644
--- a/src/plugins/qmljstools/qmljstools.qbs
+++ b/src/plugins/qmljstools/qmljstools.qbs
@@ -54,9 +54,7 @@ QtcPlugin {
"qmljstools.qrc"
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: ["qmljstools_test.cpp"]
}
diff --git a/src/plugins/qmljstools/qmljstoolsplugin.cpp b/src/plugins/qmljstools/qmljstoolsplugin.cpp
index 574c73190b..88c72b29e6 100644
--- a/src/plugins/qmljstools/qmljstoolsplugin.cpp
+++ b/src/plugins/qmljstools/qmljstoolsplugin.cpp
@@ -38,7 +38,7 @@ public:
QAction resetCodeModelAction{Tr::tr("Reset Code Model"), nullptr};
LocatorData locatorData;
- FunctionFilter functionFilter{&locatorData};
+ QmlJSFunctionsFilter functionsFilter{&locatorData};
QmlJSCodeStyleSettingsPage codeStyleSettingsPage;
BasicBundleProvider basicBundleProvider;
};
diff --git a/src/plugins/qmljstools/qmljstoolssettings.cpp b/src/plugins/qmljstools/qmljstoolssettings.cpp
index 093fb676c2..caeb6dfa26 100644
--- a/src/plugins/qmljstools/qmljstoolssettings.cpp
+++ b/src/plugins/qmljstools/qmljstoolssettings.cpp
@@ -71,45 +71,6 @@ QmlJSToolsSettings::QmlJSToolsSettings()
QSettings *s = Core::ICore::settings();
m_globalCodeStyle->fromSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
- // legacy handling start (Qt Creator Version < 2.4)
- const bool legacyTransformed =
- s->value(QLatin1String("QmlJSTabPreferences/LegacyTransformed"), false).toBool();
-
- if (!legacyTransformed) {
- // creator 2.4 didn't mark yet the transformation (first run of creator 2.4)
-
- // we need to transform the settings only if at least one from
- // below settings was already written - otherwise we use
- // defaults like it would be the first run of creator 2.4 without stored settings
- const QStringList groups = s->childGroups();
- const bool needTransform = groups.contains(QLatin1String("textTabPreferences")) ||
- groups.contains(QLatin1String("QmlJSTabPreferences"));
-
- if (needTransform) {
- const QString currentFallback = s->value(QLatin1String("QmlJSTabPreferences/CurrentFallback")).toString();
- TabSettings legacyTabSettings;
- if (currentFallback == QLatin1String("QmlJSGlobal")) {
- // no delegate, global overwritten
- Utils::fromSettings(QLatin1String("QmlJSTabPreferences"),
- QString(), s, &legacyTabSettings);
- } else {
- // delegating to global
- legacyTabSettings = TextEditorSettings::codeStyle()->currentTabSettings();
- }
-
- // create custom code style out of old settings
- ICodeStylePreferences *oldCreator = pool->createCodeStyle(
- "legacy", legacyTabSettings, QVariant(), Tr::tr("Old Creator"));
-
- // change the current delegate and save
- m_globalCodeStyle->setCurrentDelegate(oldCreator);
- m_globalCodeStyle->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s);
- }
- // mark old settings as transformed
- s->setValue(QLatin1String("QmlJSTabPreferences/LegacyTransformed"), true);
- // legacy handling stop
- }
-
// mimetypes to be handled
TextEditorSettings::registerMimeTypeForLanguageId(Constants::QML_MIMETYPE, Constants::QML_JS_SETTINGS_ID);
TextEditorSettings::registerMimeTypeForLanguageId(Constants::QMLUI_MIMETYPE, Constants::QML_JS_SETTINGS_ID);
diff --git a/src/plugins/qmlpreview/qmlpreview.qbs b/src/plugins/qmlpreview/qmlpreview.qbs
index 7b0f96dde7..e44f432c41 100644
--- a/src/plugins/qmlpreview/qmlpreview.qbs
+++ b/src/plugins/qmlpreview/qmlpreview.qbs
@@ -38,9 +38,7 @@ QtcPlugin {
]
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "tests/"
files: [
"qmlpreviewclient_test.cpp",
diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
index 6175eac2a2..8803475d8a 100644
--- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
@@ -25,10 +25,10 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsdocument.h>
@@ -150,15 +150,15 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent)
Constants::M_BUILDPROJECT);
QAction *action = new QAction(Tr::tr("QML Preview"), this);
action->setToolTip(Tr::tr("Preview changes to QML code live in your application."));
- action->setEnabled(SessionManager::startupProject() != nullptr);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged, action,
+ action->setEnabled(ProjectManager::startupProject() != nullptr);
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, action,
&QAction::setEnabled);
connect(action, &QAction::triggered, this, [this]() {
if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current())
m_localeIsoCode = multiLanguageAspect->currentLocale();
bool skipDeploy = false;
- const Kit *kit = SessionManager::startupTarget()->kit();
- if (SessionManager::startupTarget() && kit)
+ const Kit *kit = ProjectManager::startupTarget()->kit();
+ if (ProjectManager::startupTarget() && kit)
skipDeploy = kit->supportedPlatforms().contains(Android::Constants::ANDROID_DEVICE_TYPE)
|| DeviceTypeKitAspect::deviceTypeId(kit) == Android::Constants::ANDROID_DEVICE_TYPE;
ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE, skipDeploy);
diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
index 57088ad19b..ca43b07a42 100644
--- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
@@ -9,7 +9,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
@@ -18,7 +18,7 @@
#include <utils/filepath.h>
#include <utils/port.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/url.h>
using namespace ProjectExplorer;
@@ -88,11 +88,14 @@ QmlPreviewRunner::QmlPreviewRunner(RunControl *runControl, const QmlPreviewRunne
if (!runControl->isRunning())
return;
- this->connect(runControl, &RunControl::stopped, [this, runControl] {
- auto rc = new RunControl(ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE);
- rc->copyDataFromRunControl(runControl);
- ProjectExplorerPlugin::startRunControl(rc);
- });
+ this->connect(runControl,
+ &RunControl::stopped,
+ ProjectExplorerPlugin::instance(),
+ [runControl] {
+ auto rc = new RunControl(ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE);
+ rc->copyDataFromRunControl(runControl);
+ ProjectExplorerPlugin::startRunControl(rc);
+ });
runControl->initiateStop();
});
@@ -124,7 +127,7 @@ QUrl QmlPreviewRunner::serverUrl() const
QmlPreviewRunWorkerFactory::QmlPreviewRunWorkerFactory(QmlPreviewPlugin *plugin,
const QmlPreviewRunnerSetting *runnerSettings)
{
- setProducer([this, plugin, runnerSettings](RunControl *runControl) {
+ setProducer([plugin, runnerSettings](RunControl *runControl) {
auto runner = new QmlPreviewRunner(runControl, *runnerSettings);
QObject::connect(plugin, &QmlPreviewPlugin::updatePreviews,
runner, &QmlPreviewRunner::loadFile);
diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs
index 5cc3604a07..56a1e00cd6 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.qbs
+++ b/src/plugins/qmlprofiler/qmlprofiler.qbs
@@ -73,9 +73,7 @@ QtcPlugin {
files: "qml/**"
}
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
prefix: "tests/"
files: [
"debugmessagesmodel_test.cpp", "debugmessagesmodel_test.h",
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
index 396c830568..e9f0b1a780 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
@@ -6,7 +6,7 @@
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/runconfiguration.h>
#include <qmljs/parser/qmljsast_p.h>
diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
index f5c45789d8..41b67edda7 100644
--- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp
@@ -20,8 +20,8 @@
#include <qmldebug/qmldebugcommandlinearguments.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/url.h>
#include <QMessageBox>
diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.cpp b/src/plugins/qmlprofiler/qmlprofilersettings.cpp
index 481a88ab32..1002479baa 100644
--- a/src/plugins/qmlprofiler/qmlprofilersettings.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilersettings.cpp
@@ -17,24 +17,32 @@
using namespace Utils;
-namespace QmlProfiler {
-namespace Internal {
+namespace QmlProfiler::Internal {
-static QWidget *createQmlConfigWidget(QmlProfilerSettings *settings)
+class QmlProfilerOptionsPageWidget : public Core::IOptionsPageWidget
{
- QmlProfilerSettings &s = *settings;
- using namespace Layouting;
-
- return Form {
- s.flushEnabled,
- s.flushInterval,
- s.aggregateTraces
- }.emerge();
-}
+public:
+ explicit QmlProfilerOptionsPageWidget(QmlProfilerSettings *settings)
+ {
+ QmlProfilerSettings &s = *settings;
+
+ using namespace Layouting;
+ Form {
+ s.flushEnabled, br,
+ s.flushInterval, br,
+ s.aggregateTraces, br,
+ }.attachTo(this);
+ }
+
+ void apply() final
+ {
+ QmlProfilerPlugin::globalSettings()->writeGlobalSettings();
+ }
+};
QmlProfilerSettings::QmlProfilerSettings()
{
- setConfigWidgetCreator([this] { return createQmlConfigWidget(this); });
+ setConfigWidgetCreator([this] { return new QmlProfilerOptionsPageWidget(this); });
setSettingsGroup(Constants::ANALYZER);
@@ -85,25 +93,9 @@ QmlProfilerOptionsPage::QmlProfilerOptionsPage()
setCategory("T.Analyzer");
setDisplayCategory(::Debugger::Tr::tr("Analyzer"));
setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER);
+ setWidgetCreator([] {
+ return new QmlProfilerOptionsPageWidget(QmlProfilerPlugin::globalSettings());
+ });
}
-QWidget *QmlProfilerOptionsPage::widget()
-{
- // We cannot parent the widget to the options page as it expects a QWidget as parent
- if (!m_widget)
- m_widget = createQmlConfigWidget(QmlProfilerPlugin::globalSettings());
- return m_widget;
-}
-
-void QmlProfilerOptionsPage::apply()
-{
- QmlProfilerPlugin::globalSettings()->writeGlobalSettings();
-}
-
-void QmlProfilerOptionsPage::finish()
-{
- delete m_widget;
-}
-
-} // Internal
-} // QmlProfiler
+} // QmlProfiler::Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.h b/src/plugins/qmlprofiler/qmlprofilersettings.h
index 3b4481cac8..14e3567621 100644
--- a/src/plugins/qmlprofiler/qmlprofilersettings.h
+++ b/src/plugins/qmlprofiler/qmlprofilersettings.h
@@ -7,15 +7,10 @@
#include <projectexplorer/runconfiguration.h>
-#include <QPointer>
-
-namespace QmlProfiler {
-namespace Internal {
+namespace QmlProfiler::Internal {
class QmlProfilerSettings : public ProjectExplorer::ISettingsAspect
{
- Q_OBJECT
-
public:
QmlProfilerSettings();
@@ -31,14 +26,6 @@ class QmlProfilerOptionsPage final : public Core::IOptionsPage
{
public:
QmlProfilerOptionsPage();
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- QPointer<QWidget> m_widget;
};
-} // Internal
-} // QmlProfiler
+} // QmlProfiler::Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 579a870a4b..f003d5f0e4 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -39,8 +39,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/session.h>
#include <qtsupport/qtkitinformation.h>
@@ -49,6 +49,7 @@
#include <utils/fancymainwindow.h>
#include <utils/fileinprojectfinder.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <utils/url.h>
#include <utils/utilsicons.h>
@@ -190,14 +191,14 @@ QmlProfilerTool::QmlProfilerTool()
d->m_displayFeaturesButton->setIcon(Utils::Icons::FILTER.icon());
d->m_displayFeaturesButton->setToolTip(Tr::tr("Hide or show event categories."));
d->m_displayFeaturesButton->setPopupMode(QToolButton::InstantPopup);
- d->m_displayFeaturesButton->setProperty("noArrow", true);
+ d->m_displayFeaturesButton->setProperty(StyleHelper::C_NO_ARROW, true);
d->m_displayFeaturesMenu = new QMenu(d->m_displayFeaturesButton);
d->m_displayFeaturesButton->setMenu(d->m_displayFeaturesMenu);
connect(d->m_displayFeaturesMenu, &QMenu::triggered,
this, &QmlProfilerTool::toggleVisibleFeature);
d->m_timeLabel = new QLabel();
- d->m_timeLabel->setProperty("panelwidget", true);
+ StyleHelper::setPanelWidget(d->m_timeLabel);
d->m_timeLabel->setIndent(10);
updateTimeDisplay();
connect(d->m_timeLabel, &QObject::destroyed, &d->m_recordingTimer, &QTimer::stop);
@@ -540,7 +541,7 @@ ProjectExplorer::RunControl *QmlProfilerTool::attachToWaitingApplication()
d->m_viewContainer->perspective()->select();
auto runControl = new RunControl(ProjectExplorer::Constants::QML_PROFILER_RUN_MODE);
- runControl->copyDataFromRunConfiguration(SessionManager::startupRunConfiguration());
+ runControl->copyDataFromRunConfiguration(ProjectManager::startupRunConfiguration());
auto profiler = new QmlProfilerRunner(runControl);
profiler->setServerUrl(serverUrl);
diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
index b14b4962e7..366a6028bb 100644
--- a/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
+++ b/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
@@ -105,9 +105,9 @@ void QmlProfilerClientManagerTest::testConnectionFailure()
QFETCH(QmlProfilerStateManager *, stateManager);
QFETCH(QUrl, serverUrl);
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -137,9 +137,9 @@ void QmlProfilerClientManagerTest::testConnectionFailure()
void QmlProfilerClientManagerTest::testUnresponsiveTcp()
{
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -150,7 +150,7 @@ void QmlProfilerClientManagerTest::testUnresponsiveTcp()
QTcpServer server;
server.listen(QHostAddress(serverUrl.host()), serverUrl.port());
- QSignalSpy connectionSpy(&server, SIGNAL(newConnection()));
+ QSignalSpy connectionSpy(&server, &QTcpServer::newConnection);
clientManager.connectToServer(serverUrl);
@@ -165,9 +165,9 @@ void QmlProfilerClientManagerTest::testUnresponsiveTcp()
void QmlProfilerClientManagerTest::testUnresponsiveLocal()
{
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -176,7 +176,7 @@ void QmlProfilerClientManagerTest::testUnresponsiveLocal()
QUrl socketUrl = Utils::urlFromLocalSocket();
QLocalSocket socket;
- QSignalSpy connectionSpy(&socket, SIGNAL(connected()));
+ QSignalSpy connectionSpy(&socket, &QLocalSocket::connected);
clientManager.connectToServer(socketUrl);
@@ -209,8 +209,8 @@ void QmlProfilerClientManagerTest::testResponsiveTcp()
QUrl serverUrl = Utils::urlFromLocalHostAndFreePort();
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
QVERIFY(!clientManager.isConnected());
@@ -267,8 +267,8 @@ void QmlProfilerClientManagerTest::testResponsiveLocal()
QUrl socketUrl = Utils::urlFromLocalSocket();
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
QVERIFY(!clientManager.isConnected());
@@ -320,9 +320,9 @@ void QmlProfilerClientManagerTest::testInvalidData()
MessageHandler handler(&invalidHelloMessageHandler);
Q_UNUSED(handler)
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
- QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
+ QSignalSpy failedSpy(&clientManager, &QmlProfilerClientManager::connectionFailed);
QVERIFY(!clientManager.isConnected());
@@ -365,8 +365,8 @@ void QmlProfilerClientManagerTest::testStopRecording()
QmlProfilerClientManager clientManager;
clientManager.setRetryInterval(10);
clientManager.setMaximumRetries(10);
- QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
- QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
+ QSignalSpy openedSpy(&clientManager, &QmlProfilerClientManager::connectionOpened);
+ QSignalSpy closedSpy(&clientManager, &QmlProfilerClientManager::connectionClosed);
QVERIFY(!clientManager.isConnected());
diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp
index e01b6ab5ef..d71836319c 100644
--- a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp
+++ b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp
@@ -3,15 +3,16 @@
#include "qmlprofilerdetailsrewriter_test.h"
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/customexecutablerunconfiguration.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/target.h>
+#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
-#include <projectexplorer/session.h>
-#include <projectexplorer/kitinformation.h>
-#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/target.h>
+
#include <utils/filepath.h>
#include <QLibraryInfo>
@@ -157,15 +158,15 @@ void QmlProfilerDetailsRewriterTest::testPopulateFileFinder()
// Test that the rewriter will populate from available projects if given nullptr as parameter.
DummyProject *project1 = new DummyProject(":/nix.nix");
- ProjectExplorer::SessionManager::addProject(project1);
+ ProjectExplorer::ProjectManager::addProject(project1);
DummyProject *project2 = new DummyProject(":/qmlprofiler/tests/Test.qml");
- ProjectExplorer::SessionManager::addProject(project2);
+ ProjectExplorer::ProjectManager::addProject(project2);
m_rewriter.populateFileFinder(nullptr);
QCOMPARE(m_rewriter.getLocalFile("Test.qml"),
Utils::FilePath::fromString(":/qmlprofiler/tests/Test.qml"));
- ProjectExplorer::SessionManager::removeProject(project1);
- ProjectExplorer::SessionManager::removeProject(project2);
+ ProjectExplorer::ProjectManager::removeProject(project1);
+ ProjectExplorer::ProjectManager::removeProject(project2);
}
void QmlProfilerDetailsRewriterTest::seedRewriter()
@@ -174,12 +175,11 @@ void QmlProfilerDetailsRewriterTest::seedRewriter()
m_modelManager = new QmlJS::ModelManagerInterface(this);
QString filename = ":/qmlprofiler/tests/Test.qml";
- QFutureInterface<void> result;
QmlJS::PathsAndLanguages lPaths;
lPaths.maybeInsert(
Utils::FilePath::fromString(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)),
QmlJS::Dialect::Qml);
- QmlJS::ModelManagerInterface::importScan(result, QmlJS::ModelManagerInterface::workingCopy(),
+ QmlJS::ModelManagerInterface::importScan(QmlJS::ModelManagerInterface::workingCopy(),
lPaths, m_modelManager, false);
QFile file(filename);
@@ -196,11 +196,11 @@ void QmlProfilerDetailsRewriterTest::seedRewriter()
ProjectExplorer::SysRootKitAspect::setSysRoot(kit.get(), "/nowhere");
DummyProject *project = new DummyProject(Utils::FilePath::fromString(filename));
- ProjectExplorer::SessionManager::addProject(project);
+ ProjectExplorer::ProjectManager::addProject(project);
m_rewriter.populateFileFinder(project->addTargetForKit(kit.get()));
- ProjectExplorer::SessionManager::removeProject(project);
+ ProjectExplorer::ProjectManager::removeProject(project);
}
} // namespace Internal
diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt
index 7d3aa4c10d..4119bae734 100644
--- a/src/plugins/qmlprojectmanager/CMakeLists.txt
+++ b/src/plugins/qmlprojectmanager/CMakeLists.txt
@@ -1,8 +1,9 @@
add_qtc_plugin(QmlProjectManager
CONDITION TARGET Qt::QuickWidgets
+ PROPERTIES COMPILE_WARNING_AS_ERROR ON
PLUGIN_CLASS QmlProjectPlugin
DEPENDS QmlJS Qt::QuickWidgets
- PLUGIN_DEPENDS Core ProjectExplorer QtSupport
+ PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesignerBase
SOURCES
fileformat/filefilteritems.cpp fileformat/filefilteritems.h
fileformat/qmlprojectfileformat.cpp fileformat/qmlprojectfileformat.h
diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
index 99995bc2ab..21a538c7c5 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
#include "cmakeprojectconverter.h"
#include "cmakeprojectconverterdialog.h"
#include "generatecmakelists.h"
@@ -11,7 +12,7 @@
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlprojectmanagerconstants.h>
@@ -41,10 +42,10 @@ void CmakeProjectConverter::generateMenuEntry(QObject *parent)
Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ConvertToCmakeProject");
exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_CONVERT);
- action->setEnabled(isProjectConvertable(ProjectExplorer::SessionManager::startupProject()));
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged, [action]() {
- action->setEnabled(isProjectConvertable(ProjectExplorer::SessionManager::startupProject()));
+ action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() {
+ action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject()));
});
}
@@ -83,7 +84,7 @@ bool CmakeProjectConverter::isProjectCurrentFormat(const ProjectExplorer::Projec
void CmakeProjectConverter::onConvertProject()
{
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
const QmlProjectManager::QmlProject *qmlProject =
qobject_cast<const QmlProjectManager::QmlProject*>(project);
if (qmlProject) {
diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
index ef83206d98..a9f35c2c5e 100644
--- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
+++ b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "generatecmakelists.h"
+
#include "generatecmakelistsconstants.h"
#include "cmakegeneratordialog.h"
#include "../qmlprojectmanagertr.h"
@@ -10,9 +11,9 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <projectexplorer/buildsystem.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlmainfileaspect.h>
@@ -59,6 +60,16 @@ enum ProjectDirectoryError {
const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files...");
+const QmlBuildSystem *getBuildSystem()
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ if (project && project->activeTarget() && project->activeTarget()->buildSystem()) {
+ return qobject_cast<QmlProjectManager::QmlBuildSystem *>(
+ project->activeTarget()->buildSystem());
+ }
+ return nullptr;
+}
+
void generateMenuEntry(QObject *parent)
{
Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE);
@@ -79,18 +90,17 @@ void generateMenuEntry(QObject *parent)
exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE);
action->setEnabled(false);
- QObject::connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
+ QObject::connect(ProjectExplorer::ProjectManager::instance(),
+ &ProjectExplorer::ProjectManager::startupProjectChanged,
[action]() {
- auto qmlProject = qobject_cast<QmlProject *>(
- ProjectExplorer::SessionManager::startupProject());
- action->setEnabled(qmlProject != nullptr);
+ if (auto buildSystem = getBuildSystem())
+ action->setEnabled(!buildSystem->qtForMCUs());
});
}
void onGenerateCmakeLists()
{
- FilePath rootDir = ProjectExplorer::SessionManager::startupProject()->projectDirectory();
+ FilePath rootDir = ProjectExplorer::ProjectManager::startupProject()->projectDirectory();
int projectDirErrors = isProjectCorrectlyFormed(rootDir);
if (projectDirErrors != NoError) {
@@ -246,17 +256,15 @@ const QString projectEnvironmentVariable(const QString &key)
{
QString value = {};
- auto *target = ProjectExplorer::SessionManager::startupProject()->activeTarget();
- if (target && target->buildSystem()) {
- auto buildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(target->buildSystem());
- if (buildSystem) {
- auto envItems = buildSystem->environment();
- auto confEnv = std::find_if(envItems.begin(), envItems.end(),
- [key](NameValueItem &item){return item.name == key;});
- if (confEnv != envItems.end())
- value = confEnv->value;
- }
+ if (auto buildSystem = getBuildSystem()) {
+ auto envItems = buildSystem->environment();
+ auto confEnv = std::find_if(envItems.begin(), envItems.end(), [key](NameValueItem &item) {
+ return item.name == key;
+ });
+ if (confEnv != envItems.end())
+ value = confEnv->value;
}
+
return value;
}
@@ -304,7 +312,7 @@ const char ADD_SUBDIR[] = "add_subdirectory(%1)\n";
void CmakeFileGenerator::generateMainCmake(const FilePath &rootDir)
{
//TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all.
- QString projectName = ProjectExplorer::SessionManager::startupProject()->displayName();
+ QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName();
QString appName = projectName + "App";
QString fileSection = "";
@@ -523,7 +531,7 @@ bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir)
bool CmakeFileGenerator::includeFile(const FilePath &filePath)
{
if (m_checkFileIsInProject) {
- ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
if (!project->isKnownFile(filePath))
return false;
}
@@ -531,7 +539,6 @@ bool CmakeFileGenerator::includeFile(const FilePath &filePath)
return !isFileBlacklisted(filePath.fileName());
}
-
bool CmakeFileGenerator::generateEntryPointFiles(const FilePath &dir)
{
const QString qtcontrolsConf = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF);
@@ -570,22 +577,19 @@ bool CmakeFileGenerator::generateMainCpp(const FilePath &dir)
bool envHeaderOk = true;
QString environment;
- auto *target = ProjectExplorer::SessionManager::startupProject()->activeTarget();
- if (target && target->buildSystem()) {
- auto buildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(target->buildSystem());
- if (buildSystem) {
- for (EnvironmentItem &envItem : buildSystem->environment()) {
- QString key = envItem.name;
- QString value = envItem.value;
- if (isFileResource(value))
- value.prepend(":/");
- environment.append(QString(ENV_HEADER_VARIABLE_LINE).arg(key).arg(value));
- }
- QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH)
- .arg(environment);
- FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER);
- envHeaderOk = m_fileQueue.queueFile(envHeaderPath, envHeaderContent);
+
+ if (auto buildSystem = getBuildSystem()) {
+ for (EnvironmentItem &envItem : buildSystem->environment()) {
+ QString key = envItem.name;
+ QString value = envItem.value;
+ if (isFileResource(value))
+ value.prepend(":/");
+ environment.append(QString(ENV_HEADER_VARIABLE_LINE).arg(key).arg(value));
}
+ QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH)
+ .arg(environment);
+ FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER);
+ envHeaderOk = m_fileQueue.queueFile(envHeaderPath, envHeaderContent);
}
return cppOk && pluginHeaderOk && envHeaderOk;
@@ -608,6 +612,8 @@ bool CmakeFileGenerator::isFileResource(const QString &relativeFilePath)
return false;
}
+
+
} //GenerateCmake
} //QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp b/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp
index 8ae0efe311..8cde4e2c22 100644
--- a/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp
+++ b/src/plugins/qmlprojectmanager/projectfilecontenttools.cpp
@@ -18,7 +18,7 @@ QRegularExpression qdsVerRegexp(R"x(qdsVersion: "(.*)")x");
const Utils::FilePaths rootCmakeFiles(ProjectExplorer::Project *project)
{
if (!project)
- project = ProjectExplorer::SessionManager::startupProject();
+ project = ProjectExplorer::ProjectManager::startupProject();
if (!project)
return {};
return project->projectDirectory().dirEntries({QList<QString>({"CMakeLists.txt"}), QDir::Files});
diff --git a/src/plugins/qmlprojectmanager/projectfilecontenttools.h b/src/plugins/qmlprojectmanager/projectfilecontenttools.h
index 843912eb7c..3c3fcb5847 100644
--- a/src/plugins/qmlprojectmanager/projectfilecontenttools.h
+++ b/src/plugins/qmlprojectmanager/projectfilecontenttools.h
@@ -6,7 +6,7 @@
#include "qmlprojectmanager_global.h"
#include <projectexplorer/projectmanager.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/fileutils.h>
diff --git a/src/plugins/qmlprojectmanager/qdslandingpage.cpp b/src/plugins/qmlprojectmanager/qdslandingpage.cpp
index 0e0c5a1c7d..19581d9660 100644
--- a/src/plugins/qmlprojectmanager/qdslandingpage.cpp
+++ b/src/plugins/qmlprojectmanager/qdslandingpage.cpp
@@ -23,6 +23,7 @@ namespace QmlProjectManager {
namespace Internal {
const char INSTALL_QDS_URL[] = "https://www.qt.io/product/ui-design-tools";
+const char OBJECT_NAME_LANDING_PAGE[] = "QQuickWidgetQDSLandingPage";
QdsLandingPageWidget::QdsLandingPageWidget(QWidget* parent)
: QWidget(parent)
@@ -50,6 +51,7 @@ QQuickWidget *QdsLandingPageWidget::widget()
QdsLandingPageTheme::setupTheme(m_widget->engine());
m_widget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_widget->setObjectName(OBJECT_NAME_LANDING_PAGE);
m_widget->engine()->addImportPath(landingPath + "/imports");
m_widget->engine()->addImportPath(resourcePath);
m_widget->engine()->addImportPath("qrc:/studiofonts");
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
index 279b48ccb1..405e63965f 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp
@@ -55,7 +55,7 @@ QmlMainFileAspect::~QmlMainFileAspect()
delete m_fileListCombo;
}
-void QmlMainFileAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void QmlMainFileAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_ASSERT(!m_fileListCombo, delete m_fileListCombo);
m_fileListCombo = new QComboBox;
@@ -67,7 +67,7 @@ void QmlMainFileAspect::addToLayout(Layouting::LayoutBuilder &builder)
this, &QmlMainFileAspect::updateFileComboBox);
connect(m_fileListCombo, &QComboBox::activated, this, &QmlMainFileAspect::setMainScript);
- builder.addItems({Tr::tr("Main QML file:"), m_fileListCombo.data()});
+ parent.addItems({Tr::tr("Main QML file:"), m_fileListCombo.data()});
}
void QmlMainFileAspect::toMap(QVariantMap &map) const
diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
index b71fa6784b..3c75e4744a 100644
--- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
+++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h
@@ -42,7 +42,7 @@ public:
Utils::FilePath currentFile;
};
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) final;
+ void addToLayout(Layouting::LayoutItem &parent) final;
void toMap(QVariantMap &map) const final;
void fromMap(const QVariantMap &map) final;
diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
index 4ca9f9aa62..db0001ac70 100644
--- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
@@ -10,8 +10,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
static bool isMultilanguagePresent()
@@ -114,7 +114,7 @@ void QmlMultiLanguageAspect::fromMap(const QVariantMap &map)
QmlMultiLanguageAspect *QmlMultiLanguageAspect::current()
{
- if (auto project = ProjectExplorer::SessionManager::startupProject())
+ if (auto project = ProjectExplorer::ProjectManager::startupProject())
return current(project);
return {};
}
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index c283ec73e3..77e3cc82d4 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -10,6 +10,7 @@
#include "qmlprojectmanagertr.h"
#include "qmlprojectnodes.h"
+#include <coreplugin/coreconstants.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -17,13 +18,14 @@
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <coreplugin/modemanager.h>
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qtsupport/baseqtversion.h>
@@ -36,8 +38,8 @@
#include <utils/algorithm.h>
#include <utils/infobar.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QLoggingCategory>
@@ -75,15 +77,20 @@ static bool allowOnlySingleProject()
return !settings->value(qdsAllowMultipleProjects, false).toBool();
}
-Utils::FilePaths QmlProject::getUiQmlFilesForFolder(const Utils::FilePath &folder)
+Utils::FilePaths QmlProject::collectUiQmlFilesForFolder(const Utils::FilePath &folder) const
{
const Utils::FilePaths uiFiles = files([&](const ProjectExplorer::Node *node) {
return node->filePath().completeSuffix() == "ui.qml"
- && node->filePath().parentDir() == folder;
+ && node->filePath().parentDir() == folder;
});
return uiFiles;
}
+FilePaths QmlProject::collectQmlFiles() const
+{
+ return files([](const Node *node) { return node->filePath().suffix() == "qml"; });
+}
+
QmlProject::QmlProject(const Utils::FilePath &fileName)
: Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName)
{
@@ -97,7 +104,7 @@ QmlProject::QmlProject(const Utils::FilePath &fileName)
if (QmlProject::isQtDesignStudio()) {
if (allowOnlySingleProject()) {
EditorManager::closeAllDocuments();
- SessionManager::closeAllProjects();
+ ProjectManager::closeAllProjects();
}
m_openFileConnection
@@ -125,10 +132,13 @@ QmlProject::QmlProject(const Utils::FilePath &fileName)
Utils::Id());
});
} else {
- Utils::FilePaths uiFiles = getUiQmlFilesForFolder(projectDirectory()
- / "content");
+ Utils::FilePaths uiFiles = collectUiQmlFilesForFolder(
+ projectDirectory().pathAppended("content"));
+ if (uiFiles.isEmpty())
+ uiFiles = collectUiQmlFilesForFolder(projectDirectory());
+
if (uiFiles.isEmpty())
- uiFiles = getUiQmlFilesForFolder(projectDirectory());
+ uiFiles = collectQmlFiles();
if (!uiFiles.isEmpty()) {
Utils::FilePath currentFile;
@@ -139,6 +149,8 @@ QmlProject::QmlProject(const Utils::FilePath &fileName)
QTimer::singleShot(1000, [uiFiles]() {
Core::EditorManager::openEditor(uiFiles.first(),
Utils::Id());
+ Core::ModeManager::activateMode(
+ Core::Constants::MODE_DESIGN);
});
}
}
@@ -346,12 +358,22 @@ FilePath QmlBuildSystem::mainUiFile() const
FilePath QmlBuildSystem::mainFilePath() const
{
- return projectDirectory().resolvePath(mainFile());
+ const auto mainFileString = mainFile();
+
+ if (mainFileString.isEmpty())
+ return {};
+
+ return projectDirectory().resolvePath(mainFileString);
}
FilePath QmlBuildSystem::mainUiFilePath() const
{
- return projectDirectory().resolvePath(mainUiFile());
+ const auto mainUiFileString = mainUiFile();
+
+ if (mainUiFileString.isEmpty())
+ return {};
+
+ return projectDirectory().resolvePath(mainUiFileString);
}
bool QmlBuildSystem::setMainFileInProjectFile(const FilePath &newMainFilePath)
diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h
index e7387e030a..f00a4f2b36 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.h
+++ b/src/plugins/qmlprojectmanager/qmlproject.h
@@ -150,7 +150,8 @@ protected:
private:
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
- Utils::FilePaths getUiQmlFilesForFolder(const Utils::FilePath &folder);
+ Utils::FilePaths collectUiQmlFilesForFolder(const Utils::FilePath &folder) const;
+ Utils::FilePaths collectQmlFiles() const;
QMetaObject::Connection m_openFileConnection;
};
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
index 9eac76dc6c..d4938c8d79 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs
@@ -9,6 +9,7 @@ QtcPlugin {
Depends { name: "Core" }
Depends { name: "ProjectExplorer" }
+ Depends { name: "QmlDesignerBase" }
Depends { name: "QtSupport" }
Depends { name: "TextEditor" }
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
index 3c156311b4..2ead9de6da 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
@@ -26,7 +26,7 @@
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -41,7 +41,7 @@
#include <utils/fileutils.h>
#include <utils/fsengine/fileiconprovider.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QAction>
#include <QDesktopServices>
@@ -116,10 +116,10 @@ void QmlProjectPlugin::openQDS(const Utils::FilePath &fileName)
qputenv(Constants::enviromentLaunchedQDS, "true");
//-a and -client arguments help to append project to open design studio application
if (Utils::HostOsInfo::isMacHost())
- qdsStarted = Utils::QtcProcess::startDetached(
+ qdsStarted = Utils::Process::startDetached(
{"/usr/bin/open", {"-a", qdsPath.path(), fileName.toString()}});
else
- qdsStarted = Utils::QtcProcess::startDetached({qdsPath, {"-client", fileName.toString()}});
+ qdsStarted = Utils::Process::startDetached({qdsPath, {"-client", fileName.toString()}});
if (!qdsStarted) {
QMessageBox::warning(Core::ICore::dialogParent(),
@@ -181,7 +181,7 @@ const Utils::FilePath findQmlProjectUpwards(const Utils::FilePath &folder)
static bool findAndOpenProject(const Utils::FilePath &filePath)
{
ProjectExplorer::Project *project
- = ProjectExplorer::SessionManager::projectForFile(filePath);
+ = ProjectExplorer::ProjectManager::projectForFile(filePath);
if (project) {
if (project->projectFilePath().suffix() == "qmlproject") {
@@ -437,7 +437,7 @@ void QmlProjectPlugin::updateQmlLandingPageProjectInfo(const Utils::FilePath &pr
Utils::FilePath QmlProjectPlugin::projectFilePath()
{
- auto project = ProjectExplorer::SessionManager::startupProject();
+ auto project = ProjectExplorer::ProjectManager::startupProject();
const QmlProjectManager::QmlProject *qmlProject = qobject_cast<const QmlProjectManager::QmlProject*>(project);
if (qmlProject) {
return qmlProject->projectFilePath();
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index 8e8527b32c..1cce1650ce 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -21,9 +21,12 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <qmldesignerbase/qmldesignerbaseplugin.h>
+#include <qmldesignerbase/utils/qmlpuppetpaths.h>
+
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
@@ -31,7 +34,7 @@
#include <utils/aspects.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/winutils.h>
#include <qmljstools/qmljstoolsconstants.h>
@@ -62,6 +65,7 @@ private:
QmlMainFileAspect *m_qmlMainFileAspect = nullptr;
QmlMultiLanguageAspect *m_multiLanguageAspect = nullptr;
SelectionAspect *m_qtversionAspect = nullptr;
+ mutable bool usePuppetAsQmlRuntime = false;
};
QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id)
@@ -80,6 +84,8 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id)
setCommandLineGetter([this, target] {
const FilePath qmlRuntime = qmlRuntimeFilePath();
CommandLine cmd(qmlRuntime);
+ if (usePuppetAsQmlRuntime)
+ cmd.addArg("--qml-runtime");
// arguments in .user file
cmd.addArgs(aspect<ArgumentsAspect>()->arguments(), CommandLine::Raw);
@@ -193,6 +199,7 @@ QString QmlProjectRunConfiguration::disabledReason() const
FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const
{
+ usePuppetAsQmlRuntime = false;
// Give precedence to the manual override in the run configuration.
const FilePath qmlViewer = m_qmlViewerAspect->filePath();
if (!qmlViewer.isEmpty())
@@ -212,6 +219,14 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const
// i.e. not necessarily something the device can use, but the
// device had its chance above.
if (QtVersion *version = QtKitAspect::qtVersion(kit)) {
+ if (version->qtVersion().majorVersion() > 5) {
+ auto [workingDirectoryPath, puppetPath] = QmlDesigner::QmlPuppetPaths::qmlPuppetPaths(
+ target(), QmlDesigner::QmlDesignerBasePlugin::settings());
+ if (!puppetPath.isEmpty()) {
+ usePuppetAsQmlRuntime = true;
+ return puppetPath;
+ }
+ }
const FilePath qmlRuntime = version->qmlRuntimeFilePath();
if (!qmlRuntime.isEmpty())
return qmlRuntime;
@@ -274,7 +289,7 @@ void QmlProjectRunConfiguration::createQtVersionAspect()
if (!newTarget)
newTarget = project->addTargetForKit(kits.first());
- SessionManager::setActiveTarget(project, newTarget, SetActive::Cascade);
+ project->setActiveTarget(newTarget, SetActive::Cascade);
/* Reset the aspect. We changed the target and this aspect should not change. */
m_qtversionAspect->blockSignals(true);
diff --git a/src/plugins/qnx/CMakeLists.txt b/src/plugins/qnx/CMakeLists.txt
index f6c5907b4e..8d0c6751b3 100644
--- a/src/plugins/qnx/CMakeLists.txt
+++ b/src/plugins/qnx/CMakeLists.txt
@@ -4,16 +4,11 @@ add_qtc_plugin(Qnx
SOURCES
qnx.qrc
qnxanalyzesupport.cpp qnxanalyzesupport.h
- qnxconfiguration.cpp qnxconfiguration.h
- qnxconfigurationmanager.cpp qnxconfigurationmanager.h
qnxconstants.h
qnxdebugsupport.cpp qnxdebugsupport.h
qnxdeployqtlibrariesdialog.cpp qnxdeployqtlibrariesdialog.h
qnxdevice.cpp qnxdevice.h
- qnxdeviceprocesslist.cpp qnxdeviceprocesslist.h
- qnxdeviceprocesssignaloperation.cpp qnxdeviceprocesssignaloperation.h
qnxdevicetester.cpp qnxdevicetester.h
- qnxdevicewizard.cpp qnxdevicewizard.h
qnxplugin.cpp
qnxqtversion.cpp qnxqtversion.h
qnxrunconfiguration.cpp qnxrunconfiguration.h
diff --git a/src/plugins/qnx/qnx.qbs b/src/plugins/qnx/qnx.qbs
index 0389f542a6..ad4eae91c8 100644
--- a/src/plugins/qnx/qnx.qbs
+++ b/src/plugins/qnx/qnx.qbs
@@ -20,24 +20,14 @@ QtcPlugin {
"qnxtoolchain.h",
"qnx.qrc",
"qnxconstants.h",
- "qnxconfiguration.cpp",
- "qnxconfiguration.h",
"qnxanalyzesupport.cpp",
"qnxanalyzesupport.h",
"qnxdebugsupport.cpp",
"qnxdebugsupport.h",
"qnxdevice.cpp",
"qnxdevice.h",
- "qnxdevicewizard.cpp",
- "qnxdevicewizard.h",
- "qnxdeviceprocesslist.cpp",
- "qnxdeviceprocesslist.h",
- "qnxdeviceprocesssignaloperation.cpp",
- "qnxdeviceprocesssignaloperation.h",
"qnxdevicetester.cpp",
"qnxdevicetester.h",
- "qnxconfigurationmanager.cpp",
- "qnxconfigurationmanager.h",
"qnxsettingspage.cpp",
"qnxsettingspage.h",
"qnxtr.h",
diff --git a/src/plugins/qnx/qnxanalyzesupport.cpp b/src/plugins/qnx/qnxanalyzesupport.cpp
index 29ea42b7cd..4d5a73b2ab 100644
--- a/src/plugins/qnx/qnxanalyzesupport.cpp
+++ b/src/plugins/qnx/qnxanalyzesupport.cpp
@@ -9,8 +9,7 @@
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
diff --git a/src/plugins/qnx/qnxconfiguration.cpp b/src/plugins/qnx/qnxconfiguration.cpp
deleted file mode 100644
index c1b0e498f0..0000000000
--- a/src/plugins/qnx/qnxconfiguration.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxconfiguration.h"
-
-#include "qnxqtversion.h"
-#include "qnxutils.h"
-#include "qnxtoolchain.h"
-#include "qnxtr.h"
-
-#include "debugger/debuggeritem.h"
-
-#include <coreplugin/icore.h>
-
-#include <projectexplorer/toolchainmanager.h>
-#include <projectexplorer/toolchain.h>
-#include <projectexplorer/kit.h>
-#include <projectexplorer/kitmanager.h>
-
-#include <qtsupport/baseqtversion.h>
-#include <qtsupport/qtversionmanager.h>
-#include <qtsupport/qtkitinformation.h>
-
-#include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
-
-#include <debugger/debuggeritem.h>
-#include <debugger/debuggeritemmanager.h>
-#include <debugger/debuggerkitinformation.h>
-
-#include <utils/algorithm.h>
-
-#include <QDebug>
-#include <QDomDocument>
-#include <QMessageBox>
-
-using namespace ProjectExplorer;
-using namespace QtSupport;
-using namespace Utils;
-using namespace Debugger;
-
-namespace Qnx::Internal {
-
-const QLatin1String QNXEnvFileKey("EnvFile");
-const QLatin1String QNXVersionKey("QNXVersion");
-// For backward compatibility
-const QLatin1String SdpEnvFileKey("NDKEnvFile");
-
-const QLatin1String QNXConfiguration("QNX_CONFIGURATION");
-const QLatin1String QNXTarget("QNX_TARGET");
-const QLatin1String QNXHost("QNX_HOST");
-
-QnxConfiguration::QnxConfiguration() = default;
-
-QnxConfiguration::QnxConfiguration(const FilePath &sdpEnvFile)
-{
- setDefaultConfiguration(sdpEnvFile);
- readInformation();
-}
-
-QnxConfiguration::QnxConfiguration(const QVariantMap &data)
-{
- QString envFilePath = data.value(QNXEnvFileKey).toString();
- if (envFilePath.isEmpty())
- envFilePath = data.value(SdpEnvFileKey).toString();
-
- m_version = QnxVersionNumber(data.value(QNXVersionKey).toString());
-
- setDefaultConfiguration(FilePath::fromString(envFilePath));
- readInformation();
-}
-
-FilePath QnxConfiguration::envFile() const
-{
- return m_envFile;
-}
-
-FilePath QnxConfiguration::qnxTarget() const
-{
- return m_qnxTarget;
-}
-
-FilePath QnxConfiguration::qnxHost() const
-{
- return m_qnxHost;
-}
-
-FilePath QnxConfiguration::qccCompilerPath() const
-{
- return m_qccCompiler;
-}
-
-EnvironmentItems QnxConfiguration::qnxEnv() const
-{
- return m_qnxEnv;
-}
-
-QnxVersionNumber QnxConfiguration::version() const
-{
- return m_version;
-}
-
-QVariantMap QnxConfiguration::toMap() const
-{
- QVariantMap data;
- data.insert(QLatin1String(QNXEnvFileKey), m_envFile.toString());
- data.insert(QLatin1String(QNXVersionKey), m_version.toString());
- return data;
-}
-
-bool QnxConfiguration::isValid() const
-{
- return !m_qccCompiler.isEmpty() && !m_targets.isEmpty();
-}
-
-QString QnxConfiguration::displayName() const
-{
- return m_configName;
-}
-
-bool QnxConfiguration::activate()
-{
- if (isActive())
- return true;
-
- if (!isValid()) {
- QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Cannot Set Up QNX Configuration"),
- validationErrorMessage(), QMessageBox::Ok);
- return false;
- }
-
- for (const Target &target : std::as_const(m_targets))
- createTools(target);
-
- return true;
-}
-
-void QnxConfiguration::deactivate()
-{
- if (!isActive())
- return;
-
- const Toolchains toolChainsToRemove =
- ToolChainManager::toolchains(Utils::equal(&ToolChain::compilerCommand, qccCompilerPath()));
-
- QList<DebuggerItem> debuggersToRemove;
- const QList<DebuggerItem> debuggerItems = DebuggerItemManager::debuggers();
- for (const DebuggerItem &debuggerItem : debuggerItems) {
- if (findTargetByDebuggerPath(debuggerItem.command()))
- debuggersToRemove.append(debuggerItem);
- }
-
- const QList<Kit *> kits = KitManager::kits();
- for (Kit *kit : kits) {
- if (kit->isAutoDetected()
- && DeviceTypeKitAspect::deviceTypeId(kit) == Constants::QNX_QNX_OS_TYPE
- && toolChainsToRemove.contains(ToolChainKitAspect::cxxToolChain(kit))) {
- KitManager::deregisterKit(kit);
- }
- }
-
- for (ToolChain *tc : toolChainsToRemove)
- ToolChainManager::deregisterToolChain(tc);
-
- for (const DebuggerItem &debuggerItem : std::as_const(debuggersToRemove))
- DebuggerItemManager::deregisterDebugger(debuggerItem.id());
-}
-
-bool QnxConfiguration::isActive() const
-{
- const bool hasToolChain = ToolChainManager::toolChain(Utils::equal(&ToolChain::compilerCommand,
- qccCompilerPath()));
- const bool hasDebugger = Utils::contains(DebuggerItemManager::debuggers(), [this](const DebuggerItem &di) {
- return findTargetByDebuggerPath(di.command());
- });
-
- return hasToolChain && hasDebugger;
-}
-
-FilePath QnxConfiguration::sdpPath() const
-{
- return envFile().parentDir();
-}
-
-QnxQtVersion *QnxConfiguration::qnxQtVersion(const Target &target) const
-{
- const QtVersions versions = QtVersionManager::versions(
- Utils::equal(&QtVersion::type, QString::fromLatin1(Constants::QNX_QNX_QT)));
- for (QtVersion *version : versions) {
- auto qnxQt = dynamic_cast<QnxQtVersion *>(version);
- if (qnxQt && qnxQt->sdpPath() == sdpPath()) {
- const Abis abis = version->qtAbis();
- for (const Abi &qtAbi : abis) {
- if ((qtAbi == target.m_abi) && (qnxQt->cpuDir() == target.cpuDir()))
- return qnxQt;
- }
- }
- }
- return nullptr;
-}
-
-QList<ToolChain *> QnxConfiguration::autoDetect(const QList<ToolChain *> &alreadyKnown)
-{
- QList<ToolChain *> result;
-
- for (const Target &target : std::as_const(m_targets))
- result += findToolChain(alreadyKnown, target.m_abi);
-
- return result;
-}
-
-void QnxConfiguration::createTools(const Target &target)
-{
- QnxToolChainMap toolchainMap = createToolChain(target);
- QVariant debuggerId = createDebugger(target);
- createKit(target, toolchainMap, debuggerId);
-}
-
-QVariant QnxConfiguration::createDebugger(const Target &target)
-{
- Environment sysEnv = m_qnxHost.deviceEnvironment();
- sysEnv.modify(qnxEnvironmentItems());
-
- Debugger::DebuggerItem debugger;
- debugger.setCommand(target.m_debuggerPath);
- debugger.reinitializeFromFile(nullptr, &sysEnv);
- debugger.setUnexpandedDisplayName(Tr::tr("Debugger for %1 (%2)")
- .arg(displayName())
- .arg(target.shortDescription()));
- return Debugger::DebuggerItemManager::registerDebugger(debugger);
-}
-
-QnxConfiguration::QnxToolChainMap QnxConfiguration::createToolChain(const Target &target)
-{
- QnxToolChainMap toolChainMap;
-
- for (auto language : { ProjectExplorer::Constants::C_LANGUAGE_ID,
- ProjectExplorer::Constants::CXX_LANGUAGE_ID}) {
- auto toolChain = new QnxToolChain;
- toolChain->setDetection(ToolChain::AutoDetection);
- toolChain->setLanguage(language);
- toolChain->setTargetAbi(target.m_abi);
- toolChain->setDisplayName(Tr::tr("QCC for %1 (%2)")
- .arg(displayName())
- .arg(target.shortDescription()));
- toolChain->setSdpPath(sdpPath());
- toolChain->setCpuDir(target.cpuDir());
- toolChain->resetToolChain(qccCompilerPath());
- ToolChainManager::registerToolChain(toolChain);
-
- toolChainMap.insert({language, toolChain});
- }
-
- return toolChainMap;
-}
-
-QList<ToolChain *> QnxConfiguration::findToolChain(const QList<ToolChain *> &alreadyKnown,
- const Abi &abi)
-{
- return Utils::filtered(alreadyKnown, [this, abi](ToolChain *tc) {
- return tc->typeId() == Constants::QNX_TOOLCHAIN_ID
- && tc->targetAbi() == abi
- && tc->compilerCommand() == m_qccCompiler;
- });
-}
-
-void QnxConfiguration::createKit(const Target &target, const QnxToolChainMap &toolChainMap,
- const QVariant &debugger)
-{
- QnxQtVersion *qnxQt = qnxQtVersion(target); // nullptr is ok.
-
- const auto init = [&](Kit *k) {
- QtKitAspect::setQtVersion(k, qnxQt);
- ToolChainKitAspect::setToolChain(k, toolChainMap.at(ProjectExplorer::Constants::C_LANGUAGE_ID));
- ToolChainKitAspect::setToolChain(k, toolChainMap.at(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
-
- if (debugger.isValid())
- DebuggerKitAspect::setDebugger(k, debugger);
-
- DeviceTypeKitAspect::setDeviceTypeId(k, Constants::QNX_QNX_OS_TYPE);
- // TODO: Add sysroot?
-
- k->setUnexpandedDisplayName(Tr::tr("Kit for %1 (%2)")
- .arg(displayName())
- .arg(target.shortDescription()));
-
- k->setAutoDetected(false);
- k->setAutoDetectionSource(envFile().toString());
- k->setMutable(DeviceKitAspect::id(), true);
-
- k->setSticky(ToolChainKitAspect::id(), true);
- k->setSticky(DeviceTypeKitAspect::id(), true);
- k->setSticky(SysRootKitAspect::id(), true);
- k->setSticky(DebuggerKitAspect::id(), true);
- k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true);
-
- EnvironmentKitAspect::setEnvironmentChanges(k, qnxEnvironmentItems());
- };
-
- // add kit with device and qt version not sticky
- KitManager::registerKit(init);
-}
-
-QString QnxConfiguration::validationErrorMessage() const
-{
- if (isValid())
- return {};
-
- QStringList errorStrings
- = {Tr::tr("The following errors occurred while activating the QNX configuration:")};
- if (m_qccCompiler.isEmpty())
- errorStrings << Tr::tr("- No GCC compiler found.");
- if (m_targets.isEmpty())
- errorStrings << Tr::tr("- No targets found.");
- return errorStrings.join('\n');
-}
-
-void QnxConfiguration::setVersion(const QnxVersionNumber &version)
-{
- m_version = version;
-}
-
-void QnxConfiguration::readInformation()
-{
- const FilePath configPath = m_qnxConfiguration / "qconfig";
- if (!configPath.isDir())
- return;
-
- configPath.iterateDirectory([this, configPath](const FilePath &sdpFile) {
- QFile xmlFile(sdpFile.toFSPathString());
- if (!xmlFile.open(QIODevice::ReadOnly))
- return IterationPolicy::Continue;
-
- QDomDocument doc;
- if (!doc.setContent(&xmlFile)) // Skip error message
- return IterationPolicy::Continue;
-
- QDomElement docElt = doc.documentElement();
- if (docElt.tagName() != QLatin1String("qnxSystemDefinition"))
- return IterationPolicy::Continue;
-
- QDomElement childElt = docElt.firstChildElement(QLatin1String("installation"));
- // The file contains only one installation node
- if (childElt.isNull()) // The file contains only one base node
- return IterationPolicy::Continue;
-
- FilePath host = configPath.withNewPath(
- childElt.firstChildElement(QLatin1String("host")).text()).canonicalPath();
- if (m_qnxHost != host)
- return IterationPolicy::Continue;
-
- FilePath target = configPath.withNewPath(
- childElt.firstChildElement(QLatin1String("target")).text()).canonicalPath();
- if (m_qnxTarget != target)
- return IterationPolicy::Continue;
-
- m_configName = childElt.firstChildElement(QLatin1String("name")).text();
- QString version = childElt.firstChildElement(QLatin1String("version")).text();
- setVersion(QnxVersionNumber(version));
- return IterationPolicy::Stop;
- }, {{"*.xml"}, QDir::Files});
-}
-
-void QnxConfiguration::setDefaultConfiguration(const FilePath &envScript)
-{
- QTC_ASSERT(!envScript.isEmpty(), return);
- m_envFile = envScript;
- m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile);
- for (const EnvironmentItem &item : std::as_const(m_qnxEnv)) {
- if (item.name == QNXConfiguration)
- m_qnxConfiguration = envScript.withNewPath(item.value).canonicalPath();
- else if (item.name == QNXTarget)
- m_qnxTarget = envScript.withNewPath(item.value).canonicalPath();
- else if (item.name == QNXHost)
- m_qnxHost = envScript.withNewPath(item.value).canonicalPath();
- }
-
- const FilePath qccPath = m_qnxHost.pathAppended("usr/bin/qcc").withExecutableSuffix();
- if (qccPath.exists())
- m_qccCompiler = qccPath;
-
- // Some fall back in case the qconfig dir with .xml files is not found later
- if (m_configName.isEmpty())
- m_configName = QString("%1 - %2").arg(m_qnxHost.fileName(), m_qnxTarget.fileName());
-
- updateTargets();
- assignDebuggersToTargets();
-
- // Remove debuggerless targets.
- Utils::erase(m_targets, [](const Target &target) {
- if (target.m_debuggerPath.isEmpty())
- qWarning() << "No debugger found for" << target.m_path << "... discarded";
- return target.m_debuggerPath.isEmpty();
- });
-}
-
-EnvironmentItems QnxConfiguration::qnxEnvironmentItems() const
-{
- Utils::EnvironmentItems envList;
- envList.push_back(EnvironmentItem(QNXConfiguration, m_qnxConfiguration.path()));
- envList.push_back(EnvironmentItem(QNXTarget, m_qnxTarget.path()));
- envList.push_back(EnvironmentItem(QNXHost, m_qnxHost.path()));
-
- return envList;
-}
-
-const QnxConfiguration::Target *QnxConfiguration::findTargetByDebuggerPath(
- const FilePath &path) const
-{
- const auto it = std::find_if(m_targets.begin(), m_targets.end(),
- [path](const Target &target) { return target.m_debuggerPath == path; });
- return it == m_targets.end() ? nullptr : &(*it);
-}
-
-void QnxConfiguration::updateTargets()
-{
- m_targets.clear();
- const QList<QnxTarget> targets = QnxUtils::findTargets(m_qnxTarget);
- for (const QnxTarget &target : targets)
- m_targets.append(Target(target.m_abi, target.m_path));
-}
-
-void QnxConfiguration::assignDebuggersToTargets()
-{
- const FilePath hostUsrBinDir = m_qnxHost.pathAppended("usr/bin");
- QString pattern = "nto*-gdb";
- if (m_qnxHost.osType() == Utils::OsTypeWindows)
- pattern += ".exe";
-
- const FilePaths debuggerNames = hostUsrBinDir.dirEntries({{pattern}, QDir::Files});
- Environment sysEnv = m_qnxHost.deviceEnvironment();
- sysEnv.modify(qnxEnvironmentItems());
-
- for (const FilePath &debuggerPath : debuggerNames) {
- DebuggerItem item;
- item.setCommand(debuggerPath);
- item.reinitializeFromFile(nullptr, &sysEnv);
- bool found = false;
- for (const Abi &abi : item.abis()) {
- for (Target &target : m_targets) {
- if (target.m_abi.isCompatibleWith(abi)) {
- found = true;
-
- if (target.m_debuggerPath.isEmpty()) {
- target.m_debuggerPath = debuggerPath;
- } else {
- qWarning() << debuggerPath << "has the same ABI as" << target.m_debuggerPath
- << "... discarded";
- break;
- }
- }
- }
- }
- if (!found)
- qWarning() << "No target found for" << debuggerPath.toUserOutput() << "... discarded";
- }
-}
-
-QString QnxConfiguration::Target::shortDescription() const
-{
- return QnxUtils::cpuDirShortDescription(cpuDir());
-}
-
-QString QnxConfiguration::Target::cpuDir() const
-{
- return m_path.fileName();
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconfiguration.h b/src/plugins/qnx/qnxconfiguration.h
deleted file mode 100644
index 34056536c3..0000000000
--- a/src/plugins/qnx/qnxconfiguration.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "qnxconstants.h"
-#include "qnxversionnumber.h"
-
-#include <utils/fileutils.h>
-#include <utils/environment.h>
-
-#include <projectexplorer/abi.h>
-
-#include <debugger/debuggeritemmanager.h>
-
-#include <QVariant>
-
-namespace ProjectExplorer
-{
- class Kit;
- class ToolChain;
-}
-
-namespace Qnx::Internal {
-
-class QnxToolChain;
-class QnxQtVersion;
-
-class QnxConfiguration
-{
-public:
- QnxConfiguration();
- QnxConfiguration(const Utils::FilePath &sdpEnvFile);
- QnxConfiguration(const QVariantMap &data);
-
- Utils::FilePath envFile() const;
- Utils::FilePath qnxTarget() const;
- Utils::FilePath qnxHost() const;
- Utils::FilePath qccCompilerPath() const;
- Utils::EnvironmentItems qnxEnv() const;
- QnxVersionNumber version() const;
- QVariantMap toMap() const;
-
- bool isValid() const;
-
- QString displayName() const;
- bool activate();
- void deactivate();
- bool isActive() const;
- Utils::FilePath sdpPath() const;
-
- QList<ProjectExplorer::ToolChain *> autoDetect(
- const QList<ProjectExplorer::ToolChain *> &alreadyKnown);
-
-private:
- QList<ProjectExplorer::ToolChain *> findToolChain(
- const QList<ProjectExplorer::ToolChain *> &alreadyKnown,
- const ProjectExplorer::Abi &abi);
-
- QString validationErrorMessage() const;
-
- void setVersion(const QnxVersionNumber& version);
-
- void readInformation();
-
- void setDefaultConfiguration(const Utils::FilePath &envScript);
-
- Utils::EnvironmentItems qnxEnvironmentItems() const;
-
- QString m_configName;
-
- Utils::FilePath m_envFile;
- Utils::FilePath m_qnxConfiguration;
- Utils::FilePath m_qnxTarget;
- Utils::FilePath m_qnxHost;
- Utils::FilePath m_qccCompiler;
- Utils::EnvironmentItems m_qnxEnv;
- QnxVersionNumber m_version;
-
- class Target
- {
- public:
- Target(const ProjectExplorer::Abi &abi, const Utils::FilePath &path)
- : m_abi(abi), m_path(path)
- {
- }
-
- QString shortDescription() const;
- QString cpuDir() const;
-
- ProjectExplorer::Abi m_abi;
- Utils::FilePath m_path;
- Utils::FilePath m_debuggerPath;
- };
-
- QList<Target> m_targets;
-
- QnxQtVersion *qnxQtVersion(const Target &target) const;
-
- void createTools(const Target &target);
- QVariant createDebugger(const Target &target);
-
- using QnxToolChainMap = std::map<const char*, QnxToolChain*>;
-
- QnxToolChainMap createToolChain(const Target &target);
- void createKit(const Target &target, const QnxToolChainMap &toolChain, const QVariant &debugger);
-
- const Target *findTargetByDebuggerPath(const Utils::FilePath &path) const;
-
- void updateTargets();
- void assignDebuggersToTargets();
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconfigurationmanager.cpp b/src/plugins/qnx/qnxconfigurationmanager.cpp
deleted file mode 100644
index da585b69cf..0000000000
--- a/src/plugins/qnx/qnxconfigurationmanager.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxconfigurationmanager.h"
-
-#include "qnxconfiguration.h"
-
-#include <coreplugin/icore.h>
-
-#include <utils/persistentsettings.h>
-#include <utils/qtcassert.h>
-
-using namespace Utils;
-
-namespace Qnx::Internal {
-
-const QLatin1String QNXConfigDataKey("QNXConfiguration.");
-const QLatin1String QNXConfigCountKey("QNXConfiguration.Count");
-const QLatin1String QNXConfigsFileVersionKey("Version");
-
-static FilePath qnxConfigSettingsFileName()
-{
- return Core::ICore::userResourcePath("qnx/qnxconfigurations.xml");
-}
-
-static QnxConfigurationManager *m_instance = nullptr;
-
-QnxConfigurationManager::QnxConfigurationManager()
-{
- m_instance = this;
- m_writer = new PersistentSettingsWriter(qnxConfigSettingsFileName(), "QnxConfigurations");
- connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
- this, &QnxConfigurationManager::saveConfigs);
-}
-
-QnxConfigurationManager *QnxConfigurationManager::instance()
-{
- return m_instance;
-}
-
-QnxConfigurationManager::~QnxConfigurationManager()
-{
- m_instance = nullptr;
- qDeleteAll(m_configurations);
- delete m_writer;
-}
-
-QList<QnxConfiguration *> QnxConfigurationManager::configurations() const
-{
- return m_configurations;
-}
-
-void QnxConfigurationManager::removeConfiguration(QnxConfiguration *config)
-{
- if (m_configurations.removeAll(config)) {
- delete config;
- emit configurationsListUpdated();
- }
-}
-
-bool QnxConfigurationManager::addConfiguration(QnxConfiguration *config)
-{
- if (!config || !config->isValid())
- return false;
-
- for (QnxConfiguration *c : std::as_const(m_configurations)) {
- if (c->envFile() == config->envFile())
- return false;
- }
-
- m_configurations.append(config);
- emit configurationsListUpdated();
- return true;
-}
-
-QnxConfiguration *QnxConfigurationManager::configurationFromEnvFile(const FilePath &envFile) const
-{
- for (QnxConfiguration *c : m_configurations) {
- if (c->envFile() == envFile)
- return c;
- }
-
- return nullptr;
-}
-
-void QnxConfigurationManager::saveConfigs()
-{
- QTC_ASSERT(m_writer, return);
- QVariantMap data;
- data.insert(QLatin1String(QNXConfigsFileVersionKey), 1);
- int count = 0;
- for (QnxConfiguration *config : std::as_const(m_configurations)) {
- QVariantMap tmp = config->toMap();
- if (tmp.isEmpty())
- continue;
-
- data.insert(QNXConfigDataKey + QString::number(count), tmp);
- ++count;
- }
-
- data.insert(QLatin1String(QNXConfigCountKey), count);
- m_writer->save(data, Core::ICore::dialogParent());
-}
-
-void QnxConfigurationManager::restoreConfigurations()
-{
- PersistentSettingsReader reader;
- if (!reader.load(qnxConfigSettingsFileName()))
- return;
-
- QVariantMap data = reader.restoreValues();
- int count = data.value(QNXConfigCountKey, 0).toInt();
- for (int i = 0; i < count; ++i) {
- const QString key = QNXConfigDataKey + QString::number(i);
- if (!data.contains(key))
- continue;
-
- const QVariantMap dMap = data.value(key).toMap();
- auto configuration = new QnxConfiguration(dMap);
- addConfiguration(configuration);
- }
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconfigurationmanager.h b/src/plugins/qnx/qnxconfigurationmanager.h
deleted file mode 100644
index bc0d02dd98..0000000000
--- a/src/plugins/qnx/qnxconfigurationmanager.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <utils/fileutils.h>
-
-namespace Utils { class PersistentSettingsWriter; }
-
-namespace Qnx::Internal {
-
-class QnxConfiguration;
-class QnxPlugin;
-
-class QnxConfigurationManager: public QObject
-{
- Q_OBJECT
-public:
- QnxConfigurationManager();
- ~QnxConfigurationManager() override;
-
- void restoreConfigurations();
-
- static QnxConfigurationManager *instance();
- QList<QnxConfiguration*> configurations() const;
- void removeConfiguration(QnxConfiguration *config);
- bool addConfiguration(QnxConfiguration *config);
- QnxConfiguration* configurationFromEnvFile(const Utils::FilePath &envFile) const;
-
-protected slots:
- void saveConfigs();
-
-signals:
- void configurationsListUpdated();
-
-private:
- QList<QnxConfiguration*> m_configurations;
- Utils::PersistentSettingsWriter *m_writer;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxconstants.h b/src/plugins/qnx/qnxconstants.h
index b70db8c87e..3b8bdd2699 100644
--- a/src/plugins/qnx/qnxconstants.h
+++ b/src/plugins/qnx/qnxconstants.h
@@ -15,5 +15,6 @@ const char QNX_QNX_OS_TYPE[] = "QnxOsType"; // Also used for device type.
const char QNX_TOOLCHAIN_ID[] = "Qnx.QccToolChain";
const char QNX_TMP_DIR[] = "/tmp"; // /var/run is root:root drwxr-xr-x
+const char QNX_DIRECT_UPLOAD_STEP_ID[] ="Qnx.DirectUploadStep";
} // Qnx::Constants
diff --git a/src/plugins/qnx/qnxdebugsupport.cpp b/src/plugins/qnx/qnxdebugsupport.cpp
index cb3932daab..919e9ef1fc 100644
--- a/src/plugins/qnx/qnxdebugsupport.cpp
+++ b/src/plugins/qnx/qnxdebugsupport.cpp
@@ -21,8 +21,8 @@
#include <projectexplorer/kitchooser.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
@@ -33,9 +33,9 @@
#include <utils/fileutils.h>
#include <utils/pathchooser.h>
#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/processinfo.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFormLayout>
@@ -223,7 +223,7 @@ void showAttachToProcessDialog()
return;
// FIXME: That should be somehow related to the selected kit.
- auto runConfig = SessionManager::startupRunConfiguration();
+ auto runConfig = ProjectManager::startupRunConfiguration();
const int pid = dlg.currentProcess().processId;
// QString projectSourceDirectory = dlg.projectSource();
diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
index 78d752efb0..2a7f56d2f3 100644
--- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
+++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp
@@ -16,9 +16,9 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QDir>
@@ -31,8 +31,8 @@
using namespace ProjectExplorer;
using namespace QtSupport;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace Qnx::Internal {
@@ -119,12 +119,12 @@ QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable)
TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
m_deployLogWindow->appendPlainText(Tr::tr("Checking existence of \"%1\"")
.arg(fullRemoteDirectory()));
process.setCommand({m_device->filePath("test"), {"-d", fullRemoteDirectory()}});
};
- const auto doneHandler = [this](const QtcProcess &process) {
+ const auto doneHandler = [this](const Process &process) {
Q_UNUSED(process)
const int answer = QMessageBox::question(q, q->windowTitle(),
Tr::tr("The remote directory \"%1\" already exists.\n"
@@ -133,7 +133,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask()
QMessageBox::Yes | QMessageBox::No);
m_checkResult = answer == QMessageBox::Yes ? CheckResult::RemoveDir : CheckResult::Abort;
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
if (process.result() != ProcessResult::FinishedWithError) {
m_deployLogWindow->appendPlainText(Tr::tr("Connection failed: %1")
.arg(process.errorString()));
@@ -142,24 +142,24 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask()
}
m_checkResult = CheckResult::SkipRemoveDir;
};
- return Process(setupHandler, doneHandler, errorHandler);
+ return ProcessTask(setupHandler, doneHandler, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::removeDirTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
if (m_checkResult != CheckResult::RemoveDir)
return TaskAction::StopWithDone;
m_deployLogWindow->appendPlainText(Tr::tr("Removing \"%1\"").arg(fullRemoteDirectory()));
process.setCommand({m_device->filePath("rm"), {"-rf", fullRemoteDirectory()}});
return TaskAction::Continue;
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
QTC_ASSERT(process.exitCode() == 0, return);
m_deployLogWindow->appendPlainText(Tr::tr("Connection failed: %1")
.arg(process.errorString()));
};
- return Process(setupHandler, {}, errorHandler);
+ return ProcessTask(setupHandler, {}, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
@@ -193,16 +193,16 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
const auto errorHandler = [this](const FileTransfer &transfer) {
emitErrorMessage(transfer.resultData().m_errorString);
};
- return Transfer(setupHandler, {}, errorHandler);
+ return FileTransferTask(setupHandler, {}, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file)
{
- const auto setupHandler = [=](QtcProcess &process) {
+ const auto setupHandler = [=](Process &process) {
process.setCommand({m_device->filePath("chmod"),
{"a+x", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
};
- const auto errorHandler = [=](const QtcProcess &process) {
+ const auto errorHandler = [=](const Process &process) {
const QString error = process.errorString();
if (!error.isEmpty()) {
emitWarningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
@@ -212,7 +212,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file
.arg(file.remoteFilePath(), process.cleanedStdErr()));
}
};
- return Process(setupHandler, {}, errorHandler);
+ return ProcessTask(setupHandler, {}, errorHandler);
}
TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTree()
@@ -230,7 +230,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTree()
}
tree.setupRoot(chmodList);
};
- return Tree{setupChmodHandler};
+ return TaskTreeTask{setupChmodHandler};
}
Group QnxDeployQtLibrariesDialogPrivate::deployRecipe()
diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp
index 0c7c99a5c3..306b87e861 100644
--- a/src/plugins/qnx/qnxdevice.cpp
+++ b/src/plugins/qnx/qnxdevice.cpp
@@ -6,18 +6,21 @@
#include "qnxconstants.h"
#include "qnxdeployqtlibrariesdialog.h"
#include "qnxdevicetester.h"
-#include "qnxdeviceprocesslist.h"
-#include "qnxdeviceprocesssignaloperation.h"
-#include "qnxdevicewizard.h"
#include "qnxtr.h"
-#include <remotelinux/sshprocessinterface.h>
+#include <coreplugin/icore.h>
+
+#include <projectexplorer/devicesupport/sshparameters.h>
+
+#include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h>
+#include <remotelinux/remotelinuxsignaloperation.h>
+#include <remotelinux/linuxdevice.h>
#include <utils/port.h>
+#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-
-#include <QRegularExpression>
+#include <utils/wizard.h>
using namespace ProjectExplorer;
using namespace RemoteLinux;
@@ -25,149 +28,91 @@ using namespace Utils;
namespace Qnx::Internal {
-class QnxProcessImpl final : public SshProcessInterface
-{
-public:
- QnxProcessImpl(const LinuxDevice *linuxDevice);
- ~QnxProcessImpl() { killIfRunning(); }
-
-private:
- QString fullCommandLine(const CommandLine &commandLine) const final;
- void handleSendControlSignal(Utils::ControlSignal controlSignal) final;
-
- const QString m_pidFile;
-};
-
-static std::atomic_int s_pidFileCounter = 1;
-
-QnxProcessImpl::QnxProcessImpl(const LinuxDevice *linuxDevice)
- : SshProcessInterface(linuxDevice)
- , m_pidFile(QString("%1/qtc.%2.pid").arg(Constants::QNX_TMP_DIR).arg(s_pidFileCounter.fetch_add(1)))
+static QString signalProcessByNameQnxCommandLine(const QString &filePath, int sig)
{
+ QString executable = filePath;
+ return QString::fromLatin1("for PID in $(ps -f -o pid,comm | grep %1 | awk '/%1/ {print $1}'); "
+ "do "
+ "kill -%2 $PID; "
+ "done").arg(executable.replace(QLatin1String("/"), QLatin1String("\\/"))).arg(sig);
}
-QString QnxProcessImpl::fullCommandLine(const CommandLine &commandLine) const
+class QnxDeviceProcessSignalOperation : public RemoteLinuxSignalOperation
{
- QStringList args = ProcessArgs::splitArgs(commandLine.arguments());
- args.prepend(commandLine.executable().toString());
- const QString cmd = ProcessArgs::createUnixArgs(args).toString();
-
- QString fullCommandLine =
- "test -f /etc/profile && . /etc/profile ; "
- "test -f $HOME/profile && . $HOME/profile ; ";
-
- if (!m_setup.m_workingDirectory.isEmpty())
- fullCommandLine += QString::fromLatin1("cd %1 ; ").arg(
- ProcessArgs::quoteArg(m_setup.m_workingDirectory.toString()));
-
- const Environment env = m_setup.m_environment;
- for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
- fullCommandLine += QString::fromLatin1("%1='%2' ")
- .arg(env.key(it)).arg(env.expandedValueForKey(env.key(it)));
+public:
+ explicit QnxDeviceProcessSignalOperation(const IDeviceConstPtr &device)
+ : RemoteLinuxSignalOperation(device)
+ {}
+
+ QString killProcessByNameCommandLine(const QString &filePath) const override
+ {
+ return QString::fromLatin1("%1; %2").arg(signalProcessByNameQnxCommandLine(filePath, 15),
+ signalProcessByNameQnxCommandLine(filePath, 9));
}
- fullCommandLine += QString::fromLatin1("%1 & echo $! > %2").arg(cmd).arg(m_pidFile);
-
- return fullCommandLine;
-}
-
-void QnxProcessImpl::handleSendControlSignal(Utils::ControlSignal controlSignal)
-{
- QTC_ASSERT(controlSignal != ControlSignal::KickOff, return);
- const QString args = QString::fromLatin1("-%1 `cat %2`")
- .arg(controlSignalToInt(controlSignal)).arg(m_pidFile);
- CommandLine command = { "kill", args, CommandLine::Raw };
- // Note: This blocking call takes up to 2 ms for local remote.
- runInShell(command);
-}
-
-const char QnxVersionKey[] = "QnxVersion";
-
-QnxDevice::QnxDevice()
-{
- setDisplayType(Tr::tr("QNX"));
- setDefaultDisplayName(Tr::tr("QNX Device"));
- setOsType(OsTypeOtherUnix);
-
- addDeviceAction({Tr::tr("Deploy Qt libraries..."), [](const IDevice::Ptr &device, QWidget *parent) {
- QnxDeployQtLibrariesDialog dialog(device, parent);
- dialog.exec();
- }});
-}
+ QString interruptProcessByNameCommandLine(const QString &filePath) const override
+ {
+ return signalProcessByNameQnxCommandLine(filePath, 2);
+ }
+};
-int QnxDevice::qnxVersion() const
+class QnxDevice final : public LinuxDevice
{
- if (m_versionNumber == 0)
- updateVersionNumber();
-
- return m_versionNumber;
-}
+public:
+ QnxDevice()
+ {
+ setDisplayType(Tr::tr("QNX"));
+ setDefaultDisplayName(Tr::tr("QNX Device"));
+ setOsType(OsTypeOtherUnix);
+ setupId(IDevice::ManuallyAdded);
+ setType(Constants::QNX_QNX_OS_TYPE);
+ setMachineType(IDevice::Hardware);
+ SshParameters sshParams;
+ sshParams.timeout = 10;
+ setSshParameters(sshParams);
+ setFreePorts(PortList::fromString("10000-10100"));
+
+ addDeviceAction({Tr::tr("Deploy Qt libraries..."), [](const IDevice::Ptr &device, QWidget *parent) {
+ QnxDeployQtLibrariesDialog dialog(device, parent);
+ dialog.exec();
+ }});
+ }
-void QnxDevice::updateVersionNumber() const
-{
- QtcProcess versionNumberProcess;
-
- versionNumberProcess.setCommand({filePath("uname"), {"-r"}});
- versionNumberProcess.runBlocking(EventLoopMode::On);
-
- QByteArray output = versionNumberProcess.readAllRawStandardOutput();
- QString versionMessage = QString::fromLatin1(output);
- const QRegularExpression versionNumberRegExp("(\\d+)\\.(\\d+)\\.(\\d+)");
- const QRegularExpressionMatch match = versionNumberRegExp.match(versionMessage);
- if (match.hasMatch()) {
- int major = match.captured(1).toInt();
- int minor = match.captured(2).toInt();
- int patch = match.captured(3).toInt();
- m_versionNumber = (major << 16)|(minor<<8)|(patch);
+ DeviceProcessSignalOperation::Ptr signalOperation() const final
+ {
+ return DeviceProcessSignalOperation::Ptr(new QnxDeviceProcessSignalOperation(sharedFromThis()));
}
-}
-void QnxDevice::fromMap(const QVariantMap &map)
-{
- m_versionNumber = map.value(QLatin1String(QnxVersionKey), 0).toInt();
- LinuxDevice::fromMap(map);
-}
+ DeviceTester *createDeviceTester() const final { return new QnxDeviceTester; }
+};
-QVariantMap QnxDevice::toMap() const
+class QnxDeviceWizard : public Wizard
{
- QVariantMap map(LinuxDevice::toMap());
- map.insert(QLatin1String(QnxVersionKey), m_versionNumber);
- return map;
-}
+public:
+ QnxDeviceWizard() : Wizard(Core::ICore::dialogParent())
+ {
+ setWindowTitle(Tr::tr("New QNX Device Configuration Setup"));
-PortsGatheringMethod QnxDevice::portsGatheringMethod() const
-{
- return {
- // TODO: The command is probably needlessly complicated because the parsing method
- // used to be fixed. These two can now be matched to each other.
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- Q_UNUSED(protocol)
- return {filePath("netstat"), {"-na"}};
- },
-
- &Port::parseFromNetstatOutput
- };
-}
+ addPage(&m_setupPage);
+ addPage(&m_keyDeploymentPage);
+ addPage(&m_finalPage);
+ m_finalPage.setCommitPage(true);
-DeviceProcessList *QnxDevice::createProcessListModel(QObject *parent) const
-{
- return new QnxDeviceProcessList(sharedFromThis(), parent);
-}
+ m_device.reset(new QnxDevice);
-DeviceTester *QnxDevice::createDeviceTester() const
-{
- return new QnxDeviceTester;
-}
+ m_setupPage.setDevice(m_device);
+ m_keyDeploymentPage.setDevice(m_device);
+ }
-Utils::ProcessInterface *QnxDevice::createProcessInterface() const
-{
- return new QnxProcessImpl(this);
-}
+ IDevice::Ptr device() const { return m_device; }
-DeviceProcessSignalOperation::Ptr QnxDevice::signalOperation() const
-{
- return DeviceProcessSignalOperation::Ptr(new QnxDeviceProcessSignalOperation(sharedFromThis()));
-}
+private:
+ GenericLinuxDeviceConfigurationWizardSetupPage m_setupPage;
+ GenericLinuxDeviceConfigurationWizardKeyDeploymentPage m_keyDeploymentPage;
+ GenericLinuxDeviceConfigurationWizardFinalPage m_finalPage;
+
+ LinuxDevice::Ptr m_device;
+};
// Factory
@@ -176,7 +121,8 @@ QnxDeviceFactory::QnxDeviceFactory() : IDeviceFactory(Constants::QNX_QNX_OS_TYPE
setDisplayName(Tr::tr("QNX Device"));
setCombinedIcon(":/qnx/images/qnxdevicesmall.png",
":/qnx/images/qnxdevice.png");
- setConstructionFunction(&QnxDevice::create);
+ setQuickCreationAllowed(true);
+ setConstructionFunction([] { return IDevice::Ptr(new QnxDevice); });
setCreator([] {
QnxDeviceWizard wizard;
if (wizard.exec() != QDialog::Accepted)
diff --git a/src/plugins/qnx/qnxdevice.h b/src/plugins/qnx/qnxdevice.h
index f4efa3e1f2..c4b484f478 100644
--- a/src/plugins/qnx/qnxdevice.h
+++ b/src/plugins/qnx/qnxdevice.h
@@ -3,42 +3,10 @@
#pragma once
-#include <remotelinux/linuxdevice.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace Qnx::Internal {
-class QnxDevice final : public RemoteLinux::LinuxDevice
-{
-public:
- using Ptr = QSharedPointer<QnxDevice>;
- using ConstPtr = QSharedPointer<const QnxDevice>;
-
- static Ptr create() { return Ptr(new QnxDevice); }
-
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
- ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
- ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
-
- ProjectExplorer::DeviceTester *createDeviceTester() const override;
- Utils::ProcessInterface *createProcessInterface() const override;
-
- int qnxVersion() const;
-
-protected:
- void fromMap(const QVariantMap &map) final;
- QVariantMap toMap() const final;
-
- QString interruptProcessByNameCommandLine(const QString &filePath) const;
- QString killProcessByNameCommandLine(const QString &filePath) const;
-
-private:
- QnxDevice();
-
- void updateVersionNumber() const;
-
- mutable int m_versionNumber = 0;
-};
-
class QnxDeviceFactory final : public ProjectExplorer::IDeviceFactory
{
public:
diff --git a/src/plugins/qnx/qnxdeviceprocesslist.cpp b/src/plugins/qnx/qnxdeviceprocesslist.cpp
deleted file mode 100644
index 3973532993..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesslist.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxdeviceprocesslist.h"
-
-#include <projectexplorer/devicesupport/idevice.h>
-#include <utils/algorithm.h>
-#include <utils/fileutils.h>
-#include <utils/processinfo.h>
-
-#include <QRegularExpression>
-#include <QStringList>
-
-using namespace Utils;
-
-namespace Qnx::Internal {
-
-QnxDeviceProcessList::QnxDeviceProcessList(
- const ProjectExplorer::IDevice::ConstPtr &device, QObject *parent)
- : ProjectExplorer::SshDeviceProcessList(device, parent)
-{
-}
-
-QString QnxDeviceProcessList::listProcessesCommandLine() const
-{
- return QLatin1String("pidin -F '%a %A {/%n}'");
-}
-
-QList<ProcessInfo> QnxDeviceProcessList::buildProcessList(const QString &listProcessesReply) const
-{
- QList<ProcessInfo> processes;
- QStringList lines = listProcessesReply.split(QLatin1Char('\n'));
- if (lines.isEmpty())
- return processes;
-
- lines.pop_front(); // drop headers
- const QRegularExpression re("\\s*(\\d+)\\s+(.*){(.*)}");
-
- for (const QString &line : std::as_const(lines)) {
- const QRegularExpressionMatch match = re.match(line);
- if (match.hasMatch()) {
- const QStringList captures = match.capturedTexts();
- if (captures.size() == 4) {
- const int pid = captures[1].toInt();
- const QString args = captures[2];
- const QString exe = captures[3];
- ProcessInfo deviceProcess;
- deviceProcess.processId = pid;
- deviceProcess.executable = exe.trimmed();
- deviceProcess.commandLine = args.trimmed();
- processes.append(deviceProcess);
- }
- }
- }
-
- return Utils::sorted(std::move(processes));
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdeviceprocesslist.h b/src/plugins/qnx/qnxdeviceprocesslist.h
deleted file mode 100644
index 0e71ae7ab0..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesslist.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
-
-namespace Qnx::Internal {
-
-class QnxDeviceProcessList : public ProjectExplorer::SshDeviceProcessList
-{
-public:
- explicit QnxDeviceProcessList(
- const ProjectExplorer::IDeviceConstPtr &device, QObject *parent = nullptr);
-
-private:
- QString listProcessesCommandLine() const override;
- QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const override;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp b/src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp
deleted file mode 100644
index 56df01d8a9..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesssignaloperation.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxdeviceprocesssignaloperation.h"
-
-namespace Qnx::Internal {
-
-QnxDeviceProcessSignalOperation::QnxDeviceProcessSignalOperation(
- const ProjectExplorer::IDeviceConstPtr &device)
- : RemoteLinux::RemoteLinuxSignalOperation(device)
-{
-}
-
-static QString signalProcessByNameQnxCommandLine(const QString &filePath, int sig)
-{
- QString executable = filePath;
- return QString::fromLatin1("for PID in $(ps -f -o pid,comm | grep %1 | awk '/%1/ {print $1}'); "
- "do "
- "kill -%2 $PID; "
- "done").arg(executable.replace(QLatin1String("/"), QLatin1String("\\/"))).arg(sig);
-}
-
-QString QnxDeviceProcessSignalOperation::killProcessByNameCommandLine(
- const QString &filePath) const
-{
- return QString::fromLatin1("%1; %2").arg(signalProcessByNameQnxCommandLine(filePath, 15),
- signalProcessByNameQnxCommandLine(filePath, 9));
-}
-
-QString QnxDeviceProcessSignalOperation::interruptProcessByNameCommandLine(
- const QString &filePath) const
-{
- return signalProcessByNameQnxCommandLine(filePath, 2);
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdeviceprocesssignaloperation.h b/src/plugins/qnx/qnxdeviceprocesssignaloperation.h
deleted file mode 100644
index 271aea6a4e..0000000000
--- a/src/plugins/qnx/qnxdeviceprocesssignaloperation.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <remotelinux/remotelinuxsignaloperation.h>
-
-namespace Qnx::Internal {
-
-class QnxDeviceProcessSignalOperation : public RemoteLinux::RemoteLinuxSignalOperation
-{
-protected:
- explicit QnxDeviceProcessSignalOperation(const ProjectExplorer::IDeviceConstPtr &device);
-
-private:
- QString killProcessByNameCommandLine(const QString &filePath) const override;
- QString interruptProcessByNameCommandLine(const QString &filePath) const override;
-
- friend class QnxDevice;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp
index fa60bfe69b..cbead1aa3d 100644
--- a/src/plugins/qnx/qnxdevicetester.cpp
+++ b/src/plugins/qnx/qnxdevicetester.cpp
@@ -4,87 +4,66 @@
#include "qnxdevicetester.h"
#include "qnxconstants.h"
-#include "qnxdevice.h"
#include "qnxtr.h"
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace Utils;
namespace Qnx::Internal {
QnxDeviceTester::QnxDeviceTester(QObject *parent)
- : ProjectExplorer::DeviceTester(parent)
-{
- m_genericTester = new RemoteLinux::GenericLinuxDeviceTester(this);
- connect(m_genericTester, &DeviceTester::progressMessage,
- this, &DeviceTester::progressMessage);
- connect(m_genericTester, &DeviceTester::errorMessage,
- this, &DeviceTester::errorMessage);
- connect(m_genericTester, &DeviceTester::finished,
- this, &QnxDeviceTester::finished);
-}
+ : RemoteLinux::GenericLinuxDeviceTester(parent)
+{}
-static QStringList versionSpecificCommandsToTest(int versionNumber)
+void QnxDeviceTester::testDevice(const ProjectExplorer::IDevice::Ptr &device)
{
- if (versionNumber > 0x060500)
- return {"slog2info"};
- return {};
-}
+ static const QStringList commandsToTest {
+ "awk",
+ "cat",
+ "cut",
+ "df",
+ "grep",
+ "kill",
+ "netstat",
+ "mkdir",
+ "print",
+ "printf",
+ "pidin",
+ "read",
+ "rm",
+ "sed",
+ "sleep",
+ "tail",
+ "uname",
+ "slog2info"
+ };
-void QnxDeviceTester::testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration)
-{
- static const QStringList s_commandsToTest = {"awk",
- "cat",
- "cut",
- "df",
- "grep",
- "kill",
- "netstat",
- "mkdir",
- "print",
- "printf",
- "pidin",
- "read",
- "rm",
- "sed",
- "sleep",
- "tail",
- "uname"};
- m_deviceConfiguration = deviceConfiguration;
- QnxDevice::ConstPtr qnxDevice = m_deviceConfiguration.dynamicCast<const QnxDevice>();
- m_genericTester->setExtraCommandsToTest(
- s_commandsToTest + versionSpecificCommandsToTest(qnxDevice->qnxVersion()));
+ setExtraCommandsToTest(commandsToTest);
using namespace Tasking;
- auto setupHandler = [this](QtcProcess &process) {
+ auto setupHandler = [device, this](Process &process) {
emit progressMessage(Tr::tr("Checking that files can be created in %1...")
.arg(Constants::QNX_TMP_DIR));
const QString pidFile = QString("%1/qtc_xxxx.pid").arg(Constants::QNX_TMP_DIR);
- const CommandLine cmd(m_deviceConfiguration->filePath("/bin/sh"),
+ const CommandLine cmd(device->filePath("/bin/sh"),
{"-c", QLatin1String("rm %1 > /dev/null 2>&1; echo ABC > %1 && rm %1").arg(pidFile)});
process.setCommand(cmd);
};
- auto doneHandler = [this](const QtcProcess &) {
+ auto doneHandler = [this](const Process &) {
emit progressMessage(Tr::tr("Files can be created in /var/run.") + '\n');
};
- auto errorHandler = [this](const QtcProcess &process) {
+ auto errorHandler = [this](const Process &process) {
const QString message = process.result() == ProcessResult::StartFailed
? Tr::tr("An error occurred while checking that files can be created in %1.")
.arg(Constants::QNX_TMP_DIR) + '\n' + process.errorString()
: Tr::tr("Files cannot be created in %1.").arg(Constants::QNX_TMP_DIR);
emit errorMessage(message + '\n');
};
- m_genericTester->setExtraTests({Process(setupHandler, doneHandler, errorHandler)});
-
- m_genericTester->testDevice(deviceConfiguration);
-}
+ setExtraTests({ProcessTask(setupHandler, doneHandler, errorHandler)});
-void QnxDeviceTester::stopTest()
-{
- m_genericTester->stopTest();
+ RemoteLinux::GenericLinuxDeviceTester::testDevice(device);
}
} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicetester.h b/src/plugins/qnx/qnxdevicetester.h
index 96f31f3841..f8fc4be882 100644
--- a/src/plugins/qnx/qnxdevicetester.h
+++ b/src/plugins/qnx/qnxdevicetester.h
@@ -5,23 +5,14 @@
#include <remotelinux/linuxdevicetester.h>
-namespace Qnx {
-namespace Internal {
+namespace Qnx::Internal {
-class QnxDeviceTester : public ProjectExplorer::DeviceTester
+class QnxDeviceTester : public RemoteLinux::GenericLinuxDeviceTester
{
- Q_OBJECT
-
public:
explicit QnxDeviceTester(QObject *parent = nullptr);
- void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override;
- void stopTest() override;
-
-private:
- RemoteLinux::GenericLinuxDeviceTester *m_genericTester = nullptr;
- ProjectExplorer::IDevice::ConstPtr m_deviceConfiguration;
+ void testDevice(const ProjectExplorer::IDevice::Ptr &device) override;
};
-} // namespace Internal
-} // namespace Qnx
+} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicewizard.cpp b/src/plugins/qnx/qnxdevicewizard.cpp
deleted file mode 100644
index 1e7d1cfcdd..0000000000
--- a/src/plugins/qnx/qnxdevicewizard.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "qnxdevicewizard.h"
-
-#include "qnxconstants.h"
-#include "qnxtr.h"
-
-#include <projectexplorer/devicesupport/sshparameters.h>
-#include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h>
-#include <utils/portlist.h>
-
-using namespace ProjectExplorer;
-
-namespace Qnx::Internal {
-
-QnxDeviceWizard::QnxDeviceWizard(QWidget *parent) :
- Utils::Wizard(parent)
-{
- setWindowTitle(Tr::tr("New QNX Device Configuration Setup"));
-
- m_setupPage = new RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage(this);
- m_keyDeploymentPage
- = new RemoteLinux::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(this);
- m_finalPage = new RemoteLinux::GenericLinuxDeviceConfigurationWizardFinalPage(this);
-
- setPage(SetupPageId, m_setupPage);
- setPage(KeyDeploymenPageId, m_keyDeploymentPage);
- setPage(FinalPageId, m_finalPage);
- m_finalPage->setCommitPage(true);
- SshParameters sshParams;
- sshParams.timeout = 10;
- m_device = QnxDevice::create();
- m_device->setupId(IDevice::ManuallyAdded);
- m_device->setType(Constants::QNX_QNX_OS_TYPE);
- m_device->setMachineType(IDevice::Hardware);
- m_device->setSshParameters(sshParams);
- m_device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100")));
- m_setupPage->setDevice(m_device);
- m_keyDeploymentPage->setDevice(m_device);
-}
-
-IDevice::Ptr QnxDeviceWizard::device()
-{
- return m_device;
-}
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxdevicewizard.h b/src/plugins/qnx/qnxdevicewizard.h
deleted file mode 100644
index ce30658ce1..0000000000
--- a/src/plugins/qnx/qnxdevicewizard.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "qnxdevice.h"
-
-#include <utils/wizard.h>
-
-namespace RemoteLinux {
-class GenericLinuxDeviceConfigurationWizardSetupPage;
-class GenericLinuxDeviceConfigurationWizardKeyDeploymentPage;
-class GenericLinuxDeviceConfigurationWizardFinalPage;
-}
-
-namespace Qnx::Internal {
-
-class QnxDeviceWizard : public Utils::Wizard
-{
-public:
- explicit QnxDeviceWizard(QWidget *parent = nullptr);
-
- ProjectExplorer::IDevice::Ptr device();
-
-private:
- enum PageId {
- SetupPageId,
- KeyDeploymenPageId,
- FinalPageId
- };
-
- RemoteLinux::GenericLinuxDeviceConfigurationWizardSetupPage *m_setupPage;
- RemoteLinux::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage *m_keyDeploymentPage;
- RemoteLinux::GenericLinuxDeviceConfigurationWizardFinalPage *m_finalPage;
- QnxDevice::Ptr m_device;
-};
-
-} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxplugin.cpp b/src/plugins/qnx/qnxplugin.cpp
index c6664b9f60..09fd528086 100644
--- a/src/plugins/qnx/qnxplugin.cpp
+++ b/src/plugins/qnx/qnxplugin.cpp
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qnxanalyzesupport.h"
-#include "qnxconfigurationmanager.h"
#include "qnxconstants.h"
#include "qnxdebugsupport.h"
#include "qnxdevice.h"
@@ -33,8 +32,6 @@
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
-#include <remotelinux/genericdirectuploadstep.h>
-#include <remotelinux/makeinstallstep.h>
#include <remotelinux/remotelinux_constants.h>
#include <QAction>
@@ -43,21 +40,12 @@ using namespace ProjectExplorer;
namespace Qnx::Internal {
-class QnxUploadStep : public RemoteLinux::GenericDirectUploadStep
+class QnxDeployStepFactory : public BuildStepFactory
{
public:
- QnxUploadStep(BuildStepList *bsl, Utils::Id id) : GenericDirectUploadStep(bsl, id, false) {}
- static Utils::Id stepId() { return "Qnx.DirectUploadStep"; }
-};
-
-template <class Step>
-class GenericQnxDeployStepFactory : public BuildStepFactory
-{
-public:
- GenericQnxDeployStepFactory()
+ QnxDeployStepFactory(Utils::Id existingStepId, Utils::Id overrideId = {})
{
- registerStep<Step>(Step::stepId());
- setDisplayName(Step::displayName());
+ cloneStepCreator(existingStepId, overrideId);
setSupportedConfiguration(Constants::QNX_QNX_DEPLOYCONFIGURATION_ID);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
}
@@ -78,8 +66,8 @@ public:
return prj->deploymentKnowledge() == DeploymentKnowledge::Bad
&& prj->hasMakeInstallEquivalent();
});
- addInitialStep(DeviceCheckBuildStep::stepId());
- addInitialStep(QnxUploadStep::stepId());
+ addInitialStep(ProjectExplorer::Constants::DEVICE_CHECK_STEP);
+ addInitialStep(Constants::QNX_DIRECT_UPLOAD_STEP_ID);
}
};
@@ -91,15 +79,14 @@ public:
QAction *m_debugSeparator = nullptr;
QAction m_attachToQnxApplication{Tr::tr("Attach to remote QNX application..."), nullptr};
- QnxConfigurationManager configurationManager;
+ QnxSettingsPage settingsPage;
QnxQtVersionFactory qtVersionFactory;
QnxDeviceFactory deviceFactory;
QnxDeployConfigurationFactory deployConfigFactory;
- GenericQnxDeployStepFactory<QnxUploadStep> directUploadDeployFactory;
- GenericQnxDeployStepFactory<RemoteLinux::MakeInstallStep> makeInstallDeployFactory;
- GenericQnxDeployStepFactory<DeviceCheckBuildStep> checkBuildDeployFactory;
+ QnxDeployStepFactory directUploadDeployFactory{RemoteLinux::Constants::DirectUploadStepId,
+ Constants::QNX_DIRECT_UPLOAD_STEP_ID};
+ QnxDeployStepFactory makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
QnxRunConfigurationFactory runConfigFactory;
- QnxSettingsPage settingsPage;
QnxToolChainFactory toolChainFactory;
SimpleTargetRunnerFactory runWorkerFactory{{runConfigFactory.runConfigurationId()}};
QnxDebugWorkerFactory debugWorkerFactory;
@@ -123,10 +110,6 @@ private:
void QnxPlugin::extensionsInitialized()
{
- // Can't do yet as not all devices are around.
- connect(DeviceManager::instance(), &DeviceManager::devicesLoaded,
- &d->configurationManager, &QnxConfigurationManager::restoreConfigurations);
-
// Attach support
connect(&d->m_attachToQnxApplication, &QAction::triggered, this, &showAttachToProcessDialog);
diff --git a/src/plugins/qnx/qnxqtversion.cpp b/src/plugins/qnx/qnxqtversion.cpp
index b5dd0518d8..a13f96148b 100644
--- a/src/plugins/qnx/qnxqtversion.cpp
+++ b/src/plugins/qnx/qnxqtversion.cpp
@@ -116,7 +116,7 @@ QVariantMap QnxQtVersion::toMap() const
return result;
}
-void QnxQtVersion::fromMap(const QVariantMap &map)
+void QnxQtVersion::fromMap(const QVariantMap &map, const Utils::FilePath &)
{
QtVersion::fromMap(map);
setSdpPath(FilePath::fromSettings(map.value(SDP_PATH_KEY)));
diff --git a/src/plugins/qnx/qnxqtversion.h b/src/plugins/qnx/qnxqtversion.h
index d881697a16..bcea9b3be3 100644
--- a/src/plugins/qnx/qnxqtversion.h
+++ b/src/plugins/qnx/qnxqtversion.h
@@ -28,7 +28,7 @@ public:
QString cpuDir() const;
QVariantMap toMap() const override;
- void fromMap(const QVariantMap &map) override;
+ void fromMap(const QVariantMap &map, const Utils::FilePath &filePath) override;
ProjectExplorer::Abis detectQtAbis() const override;
diff --git a/src/plugins/qnx/qnxsettingspage.cpp b/src/plugins/qnx/qnxsettingspage.cpp
index 0d35403773..992ba9b87d 100644
--- a/src/plugins/qnx/qnxsettingspage.cpp
+++ b/src/plugins/qnx/qnxsettingspage.cpp
@@ -3,29 +3,512 @@
#include "qnxsettingspage.h"
-#include "qnxconfiguration.h"
-#include "qnxconfigurationmanager.h"
+#include "qnxqtversion.h"
+#include "qnxtoolchain.h"
#include "qnxtr.h"
+#include "qnxutils.h"
+#include "qnxversionnumber.h"
#include <coreplugin/icore.h>
+#include <debugger/debuggeritem.h>
+#include <debugger/debuggeritemmanager.h>
+#include <debugger/debuggerkitinformation.h>
+
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/toolchainmanager.h>
+#include <projectexplorer/toolchain.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/kitmanager.h>
+#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtversionmanager.h>
+#include <qtsupport/qtkitinformation.h>
+
+#include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
+#include <utils/algorithm.h>
#include <utils/layoutbuilder.h>
+#include <utils/persistentsettings.h>
+#include <utils/qtcassert.h>
#include <QCheckBox>
#include <QComboBox>
+#include <QDebug>
+#include <QDomDocument>
+#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
+using namespace ProjectExplorer;
+using namespace QtSupport;
using namespace Utils;
+using namespace Debugger;
namespace Qnx::Internal {
+const QLatin1String QNXEnvFileKey("EnvFile");
+const QLatin1String QNXVersionKey("QNXVersion");
+// For backward compatibility
+const QLatin1String SdpEnvFileKey("NDKEnvFile");
+
+const QLatin1String QNXConfiguration("QNX_CONFIGURATION");
+const QLatin1String QNXTarget("QNX_TARGET");
+const QLatin1String QNXHost("QNX_HOST");
+
+const QLatin1String QNXConfigDataKey("QNXConfiguration.");
+const QLatin1String QNXConfigCountKey("QNXConfiguration.Count");
+const QLatin1String QNXConfigsFileVersionKey("Version");
+
+static FilePath qnxConfigSettingsFileName()
+{
+ return Core::ICore::userResourcePath("qnx/qnxconfigurations.xml");
+}
+
+class QnxConfiguration
+{
+public:
+ QnxConfiguration() = default;
+ explicit QnxConfiguration(const FilePath &envFile) { m_envFile = envFile; }
+
+ void fromMap(const QVariantMap &data)
+ {
+ QString envFilePath = data.value(QNXEnvFileKey).toString();
+ if (envFilePath.isEmpty())
+ envFilePath = data.value(SdpEnvFileKey).toString();
+
+ m_version = QnxVersionNumber(data.value(QNXVersionKey).toString());
+ m_envFile = FilePath::fromString(envFilePath);
+ }
+
+ QVariantMap toMap() const
+ {
+ QVariantMap data;
+ data.insert(QLatin1String(QNXEnvFileKey), m_envFile.toString());
+ data.insert(QLatin1String(QNXVersionKey), m_version.toString());
+ return data;
+ }
+
+ bool isValid() const
+ {
+ return !m_qccCompiler.isEmpty() && !m_targets.isEmpty();
+ }
+
+ bool isActive() const
+ {
+ const bool hasToolChain = ToolChainManager::toolChain(Utils::equal(&ToolChain::compilerCommand,
+ m_qccCompiler));
+ const bool hasDebugger = Utils::contains(DebuggerItemManager::debuggers(), [this](const DebuggerItem &di) {
+ return findTargetByDebuggerPath(di.command());
+ });
+ return hasToolChain && hasDebugger;
+ }
+
+ void activate();
+ void deactivate();
+
+ void ensureContents() const;
+ void mutableEnsureContents();
+
+ QString architectureNames() const
+ {
+ return transform(m_targets, &QnxTarget::shortDescription).join(", ");
+ }
+
+ EnvironmentItems qnxEnvironmentItems() const;
+
+ QnxQtVersion *qnxQtVersion(const QnxTarget &target) const;
+
+ void createKit(const QnxTarget &target);
+ QVariant createDebugger(const QnxTarget &target);
+ Toolchains createToolChains(const QnxTarget &target);
+
+ const QnxTarget *findTargetByDebuggerPath(const Utils::FilePath &path) const;
+
+ bool m_hasContents = false;
+ QString m_configName;
+
+ FilePath m_envFile;
+ FilePath m_qnxConfiguration;
+ FilePath m_qnxTarget;
+ FilePath m_qnxHost;
+ FilePath m_qccCompiler;
+ EnvironmentItems m_qnxEnv;
+ QnxVersionNumber m_version;
+
+ QList<QnxTarget> m_targets;
+};
+
+void QnxConfiguration::activate()
+{
+ ensureContents();
+
+ if (!isValid()) {
+ QStringList errorStrings
+ = {Tr::tr("The following errors occurred while activating the QNX configuration:")};
+ if (m_qccCompiler.isEmpty())
+ errorStrings << Tr::tr("- No GCC compiler found.");
+ if (m_targets.isEmpty())
+ errorStrings << Tr::tr("- No targets found.");
+ const QString msg = errorStrings.join('\n');
+
+ QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Cannot Set Up QNX Configuration"),
+ msg, QMessageBox::Ok);
+ return;
+ }
+
+ for (const QnxTarget &target : std::as_const(m_targets))
+ createKit(target);
+}
+
+void QnxConfiguration::deactivate()
+{
+ QTC_ASSERT(isActive(), return);
+
+ const Toolchains toolChainsToRemove =
+ ToolChainManager::toolchains(Utils::equal(&ToolChain::compilerCommand, m_qccCompiler));
+
+ QList<DebuggerItem> debuggersToRemove;
+ const QList<DebuggerItem> debuggerItems = DebuggerItemManager::debuggers();
+ for (const DebuggerItem &debuggerItem : debuggerItems) {
+ if (findTargetByDebuggerPath(debuggerItem.command()))
+ debuggersToRemove.append(debuggerItem);
+ }
+
+ const QList<Kit *> kits = KitManager::kits();
+ for (Kit *kit : kits) {
+ if (kit->isAutoDetected()
+ && DeviceTypeKitAspect::deviceTypeId(kit) == Constants::QNX_QNX_OS_TYPE
+ && toolChainsToRemove.contains(ToolChainKitAspect::cxxToolChain(kit))) {
+ KitManager::deregisterKit(kit);
+ }
+ }
+
+ for (ToolChain *tc : toolChainsToRemove)
+ ToolChainManager::deregisterToolChain(tc);
+
+ for (const DebuggerItem &debuggerItem : std::as_const(debuggersToRemove))
+ DebuggerItemManager::deregisterDebugger(debuggerItem.id());
+}
+
+QnxQtVersion *QnxConfiguration::qnxQtVersion(const QnxTarget &target) const
+{
+ const QtVersions versions = QtVersionManager::versions(
+ Utils::equal(&QtVersion::type, QString::fromLatin1(Constants::QNX_QNX_QT)));
+ for (QtVersion *version : versions) {
+ auto qnxQt = dynamic_cast<QnxQtVersion *>(version);
+ if (qnxQt && qnxQt->sdpPath() == m_envFile.parentDir()) {
+ const Abis abis = version->qtAbis();
+ for (const Abi &qtAbi : abis) {
+ if (qtAbi == target.m_abi && qnxQt->cpuDir() == target.cpuDir())
+ return qnxQt;
+ }
+ }
+ }
+ return nullptr;
+}
+
+QVariant QnxConfiguration::createDebugger(const QnxTarget &target)
+{
+ Environment sysEnv = m_qnxHost.deviceEnvironment();
+ sysEnv.modify(qnxEnvironmentItems());
+
+ Debugger::DebuggerItem debugger;
+ debugger.setCommand(target.m_debuggerPath);
+ debugger.reinitializeFromFile(nullptr, &sysEnv);
+ debugger.setUnexpandedDisplayName(Tr::tr("Debugger for %1 (%2)")
+ .arg(m_configName)
+ .arg(target.shortDescription()));
+ return Debugger::DebuggerItemManager::registerDebugger(debugger);
+}
+
+Toolchains QnxConfiguration::createToolChains(const QnxTarget &target)
+{
+ Toolchains toolChains;
+
+ for (const Id language : {ProjectExplorer::Constants::C_LANGUAGE_ID,
+ ProjectExplorer::Constants::CXX_LANGUAGE_ID}) {
+ auto toolChain = new QnxToolChain;
+ toolChain->setDetection(ToolChain::ManualDetection);
+ toolChain->setLanguage(language);
+ toolChain->setTargetAbi(target.m_abi);
+ toolChain->setDisplayName(Tr::tr("QCC for %1 (%2)")
+ .arg(m_configName)
+ .arg(target.shortDescription()));
+ toolChain->setSdpPath(m_envFile.parentDir());
+ toolChain->setCpuDir(target.cpuDir());
+ toolChain->resetToolChain(m_qccCompiler);
+ ToolChainManager::registerToolChain(toolChain);
+
+ toolChains.append(toolChain);
+ }
+
+ return toolChains;
+}
+
+void QnxConfiguration::createKit(const QnxTarget &target)
+{
+ Toolchains toolChains = createToolChains(target);
+ QVariant debugger = createDebugger(target);
+
+ QnxQtVersion *qnxQt = qnxQtVersion(target); // nullptr is ok.
+
+ const auto init = [&](Kit *k) {
+ QtKitAspect::setQtVersion(k, qnxQt);
+ ToolChainKitAspect::setToolChain(k, toolChains[0]);
+ ToolChainKitAspect::setToolChain(k, toolChains[1]);
+
+ if (debugger.isValid())
+ DebuggerKitAspect::setDebugger(k, debugger);
+
+ DeviceTypeKitAspect::setDeviceTypeId(k, Constants::QNX_QNX_OS_TYPE);
+ // TODO: Add sysroot?
+
+ k->setUnexpandedDisplayName(Tr::tr("Kit for %1 (%2)")
+ .arg(m_configName)
+ .arg(target.shortDescription()));
+
+ k->setAutoDetected(false);
+ k->setAutoDetectionSource(m_envFile.toString());
+ k->setMutable(DeviceKitAspect::id(), true);
+
+ k->setSticky(ToolChainKitAspect::id(), true);
+ k->setSticky(DeviceTypeKitAspect::id(), true);
+ k->setSticky(SysRootKitAspect::id(), true);
+ k->setSticky(DebuggerKitAspect::id(), true);
+ k->setSticky(QmakeProjectManager::Constants::KIT_INFORMATION_ID, true);
+
+ EnvironmentKitAspect::setEnvironmentChanges(k, qnxEnvironmentItems());
+ };
+
+ // add kit with device and qt version not sticky
+ KitManager::registerKit(init);
+}
+
+void QnxConfiguration::ensureContents() const
+{
+ if (!m_hasContents)
+ const_cast<QnxConfiguration *>(this)->mutableEnsureContents();
+}
+
+void QnxConfiguration::mutableEnsureContents()
+{
+ QTC_ASSERT(!m_envFile.isEmpty(), return);
+ m_hasContents = true;
+
+ m_qnxEnv = QnxUtils::qnxEnvironmentFromEnvFile(m_envFile);
+ for (const EnvironmentItem &item : std::as_const(m_qnxEnv)) {
+ if (item.name == QNXConfiguration)
+ m_qnxConfiguration = m_envFile.withNewPath(item.value).canonicalPath();
+ else if (item.name == QNXTarget)
+ m_qnxTarget = m_envFile.withNewPath(item.value).canonicalPath();
+ else if (item.name == QNXHost)
+ m_qnxHost = m_envFile.withNewPath(item.value).canonicalPath();
+ }
+
+ const FilePath qccPath = m_qnxHost.pathAppended("usr/bin/qcc").withExecutableSuffix();
+ if (qccPath.exists())
+ m_qccCompiler = qccPath;
+
+ // Some fallback in case the qconfig dir with .xml files is not found later.
+ if (m_configName.isEmpty())
+ m_configName = QString("%1 - %2").arg(m_qnxHost.fileName(), m_qnxTarget.fileName());
+
+ m_targets = QnxUtils::findTargets(m_qnxTarget);
+
+ // Assign debuggers.
+ const FilePath hostUsrBinDir = m_qnxHost.pathAppended("usr/bin");
+ QString pattern = "nto*-gdb";
+ if (m_qnxHost.osType() == Utils::OsTypeWindows)
+ pattern += ".exe";
+
+ const FilePaths debuggerNames = hostUsrBinDir.dirEntries({{pattern}, QDir::Files});
+ Environment sysEnv = m_qnxHost.deviceEnvironment();
+ sysEnv.modify(qnxEnvironmentItems());
+
+ for (const FilePath &debuggerPath : debuggerNames) {
+ DebuggerItem item;
+ item.setCommand(debuggerPath);
+ item.reinitializeFromFile(nullptr, &sysEnv);
+ bool found = false;
+ for (const Abi &abi : item.abis()) {
+ for (QnxTarget &target : m_targets) {
+ if (target.m_abi.isCompatibleWith(abi)) {
+ found = true;
+
+ if (target.m_debuggerPath.isEmpty()) {
+ target.m_debuggerPath = debuggerPath;
+ } else {
+ qWarning() << debuggerPath << "has the same ABI as" << target.m_debuggerPath
+ << "... discarded";
+ break;
+ }
+ }
+ }
+ }
+ if (!found)
+ qWarning() << "No target found for" << debuggerPath.toUserOutput() << "... discarded";
+ }
+
+ // Remove debuggerless targets.
+ Utils::erase(m_targets, [](const QnxTarget &target) {
+ if (target.m_debuggerPath.isEmpty())
+ qWarning() << "No debugger found for" << target.m_path << "... discarded";
+ return target.m_debuggerPath.isEmpty();
+ });
+
+ const FilePath configPath = m_qnxConfiguration / "qconfig";
+ if (!configPath.isDir())
+ return;
+
+ configPath.iterateDirectory([this, configPath](const FilePath &sdpFile) {
+ QFile xmlFile(sdpFile.toFSPathString());
+ if (!xmlFile.open(QIODevice::ReadOnly))
+ return IterationPolicy::Continue;
+
+ QDomDocument doc;
+ if (!doc.setContent(&xmlFile)) // Skip error message
+ return IterationPolicy::Continue;
+
+ QDomElement docElt = doc.documentElement();
+ if (docElt.tagName() != QLatin1String("qnxSystemDefinition"))
+ return IterationPolicy::Continue;
+
+ QDomElement childElt = docElt.firstChildElement(QLatin1String("installation"));
+ // The file contains only one installation node
+ if (childElt.isNull()) // The file contains only one base node
+ return IterationPolicy::Continue;
+
+ FilePath host = configPath.withNewPath(
+ childElt.firstChildElement(QLatin1String("host")).text()).canonicalPath();
+ if (m_qnxHost != host)
+ return IterationPolicy::Continue;
+
+ FilePath target = configPath.withNewPath(
+ childElt.firstChildElement(QLatin1String("target")).text()).canonicalPath();
+ if (m_qnxTarget != target)
+ return IterationPolicy::Continue;
+
+ m_configName = childElt.firstChildElement(QLatin1String("name")).text();
+ QString version = childElt.firstChildElement(QLatin1String("version")).text();
+ m_version = QnxVersionNumber(version);
+ return IterationPolicy::Stop;
+ }, {{"*.xml"}, QDir::Files});
+}
+
+EnvironmentItems QnxConfiguration::qnxEnvironmentItems() const
+{
+ ensureContents();
+ return {
+ {QNXConfiguration, m_qnxConfiguration.path()},
+ {QNXTarget, m_qnxTarget.path()},
+ {QNXHost, m_qnxHost.path()}
+ };
+}
+
+const QnxTarget *QnxConfiguration::findTargetByDebuggerPath(
+ const FilePath &path) const
+{
+ const auto it = std::find_if(m_targets.begin(), m_targets.end(),
+ [path](const QnxTarget &target) { return target.m_debuggerPath == path; });
+ return it == m_targets.end() ? nullptr : &(*it);
+}
+
+
+// QnxSettingsPagePrivate
+
+class QnxSettingsPagePrivate : public QObject
+{
+public:
+ QnxSettingsPagePrivate()
+ {
+ connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
+ this, &QnxSettingsPagePrivate::saveConfigs);
+ // Can't do yet as not all devices are around.
+ connect(DeviceManager::instance(), &DeviceManager::devicesLoaded,
+ this, &QnxSettingsPagePrivate::restoreConfigurations);
+ }
+
+ void saveConfigs()
+ {
+ QVariantMap data;
+ data.insert(QLatin1String(QNXConfigsFileVersionKey), 1);
+ int count = 0;
+ for (const QnxConfiguration &config : std::as_const(m_configurations)) {
+ QVariantMap tmp = config.toMap();
+ if (tmp.isEmpty())
+ continue;
+
+ data.insert(QNXConfigDataKey + QString::number(count), tmp);
+ ++count;
+ }
+
+ data.insert(QLatin1String(QNXConfigCountKey), count);
+ m_writer.save(data, Core::ICore::dialogParent());
+ }
+
+ void restoreConfigurations()
+ {
+ PersistentSettingsReader reader;
+ if (!reader.load(qnxConfigSettingsFileName()))
+ return;
+
+ QVariantMap data = reader.restoreValues();
+ int count = data.value(QNXConfigCountKey, 0).toInt();
+ for (int i = 0; i < count; ++i) {
+ const QString key = QNXConfigDataKey + QString::number(i);
+ if (!data.contains(key))
+ continue;
+
+ QnxConfiguration config;
+ config.fromMap(data.value(key).toMap());
+ m_configurations[config.m_envFile] = config;
+ }
+ }
+
+ QnxConfiguration *configurationFromEnvFile(const FilePath &envFile)
+ {
+ auto it = m_configurations.find(envFile);
+ return it == m_configurations.end() ? nullptr : &*it;
+ }
+
+ QHash<FilePath, QnxConfiguration> m_configurations;
+ PersistentSettingsWriter m_writer{qnxConfigSettingsFileName(), "QnxConfigurations"};
+};
+
+static QnxSettingsPagePrivate *dd = nullptr;
+
+
+// QnxSettingsWidget
+
+class ArchitecturesList final : public QWidget
+{
+public:
+ void setConfiguration(const FilePath &envFile)
+ {
+ m_envFile = envFile;
+ delete layout();
+
+ QnxConfiguration *config = dd->configurationFromEnvFile(envFile);
+ if (!config)
+ return;
+
+ auto l = new QHBoxLayout(this);
+ for (const QnxTarget &target : config->m_targets) {
+ auto button = new QPushButton(tr("Create Kit for %1").arg(target.cpuDir()));
+ connect(button, &QPushButton::clicked, this, [config, target] {
+ config->createKit(target);
+ });
+ l->addWidget(button);
+ }
+ }
+
+ FilePath m_envFile;
+};
+
class QnxSettingsWidget final : public Core::IOptionsPageWidget
{
public:
@@ -42,10 +525,10 @@ public:
public:
bool operator ==(const ConfigState &cs) const
{
- return config == cs.config && state == cs.state;
+ return envFile == cs.envFile && state == cs.state;
}
- QnxConfiguration *config;
+ FilePath envFile;
State state;
};
@@ -53,73 +536,69 @@ public:
void addConfiguration();
void removeConfiguration();
- void generateKits(bool checked);
void updateInformation();
void populateConfigsCombo();
- void setConfigState(QnxConfiguration *config, State state);
+ void setConfigState(const FilePath &envFile, State state);
private:
QComboBox *m_configsCombo = new QComboBox;
- QCheckBox *m_generateKitsCheckBox = new QCheckBox(Tr::tr("Generate kits"));
QLabel *m_configName = new QLabel;
QLabel *m_configVersion = new QLabel;
QLabel *m_configHost = new QLabel;
QLabel *m_configTarget = new QLabel;
+ QLabel *m_compiler = new QLabel;
+ QLabel *m_architectures = new QLabel;
+
+ ArchitecturesList *m_kitCreation = new ArchitecturesList;
- QnxConfigurationManager *m_qnxConfigManager = QnxConfigurationManager::instance();
QList<ConfigState> m_changedConfigs;
};
QnxSettingsWidget::QnxSettingsWidget()
{
- auto addButton = new QPushButton(Tr::tr("Add..."));
- auto removeButton = new QPushButton(Tr::tr("Remove"));
-
using namespace Layouting;
Row {
Column {
m_configsCombo,
- Row { m_generateKitsCheckBox, st },
Group {
title(Tr::tr("Configuration Information:")),
Form {
Tr::tr("Name:"), m_configName, br,
Tr::tr("Version:"), m_configVersion, br,
Tr::tr("Host:"), m_configHost, br,
- Tr::tr("Target:"), m_configTarget
+ Tr::tr("Target:"), m_configTarget, br,
+ Tr::tr("Compiler:"), m_compiler, br,
+ Tr::tr("Architectures:"), m_architectures
}
},
+ Row { m_kitCreation, st },
st
},
Column {
- addButton,
- removeButton,
+ PushButton {
+ text(Tr::tr("Add...")),
+ onClicked([this] { addConfiguration(); }, this)
+ },
+ PushButton {
+ text(Tr::tr("Remove")),
+ onClicked([this] { removeConfiguration(); }, this)
+ },
st
}
}.attachTo(this);
populateConfigsCombo();
- connect(addButton, &QAbstractButton::clicked,
- this, &QnxSettingsWidget::addConfiguration);
- connect(removeButton, &QAbstractButton::clicked,
- this, &QnxSettingsWidget::removeConfiguration);
+
connect(m_configsCombo, &QComboBox::currentIndexChanged,
this, &QnxSettingsWidget::updateInformation);
- connect(m_generateKitsCheckBox, &QAbstractButton::toggled,
- this, &QnxSettingsWidget::generateKits);
- connect(m_qnxConfigManager, &QnxConfigurationManager::configurationsListUpdated,
- this, &QnxSettingsWidget::populateConfigsCombo);
- connect(QtSupport::QtVersionManager::instance(),
- &QtSupport::QtVersionManager::qtVersionsChanged,
- this, &QnxSettingsWidget::updateInformation);
}
void QnxSettingsWidget::addConfiguration()
{
QString filter;
- if (Utils::HostOsInfo::isWindowsHost())
+ if (HostOsInfo::isWindowsHost())
filter = "*.bat file";
else
filter = "*.sh file";
@@ -129,82 +608,85 @@ void QnxSettingsWidget::addConfiguration()
if (envFile.isEmpty())
return;
- QnxConfiguration *config = new QnxConfiguration(envFile);
- if (m_qnxConfigManager->configurations().contains(config) || !config->isValid()) {
+ if (dd->m_configurations.contains(envFile)) {
QMessageBox::warning(Core::ICore::dialogParent(),
Tr::tr("Warning"),
- Tr::tr("Configuration already exists or is invalid."));
- delete config;
+ Tr::tr("Configuration already exists."));
return;
}
- setConfigState(config, Added);
- m_configsCombo->addItem(config->displayName(),
- QVariant::fromValue(static_cast<void*>(config)));
+ // Temporary to be able to check
+ QnxConfiguration config(envFile);
+ config.ensureContents();
+ if (!config.isValid()) {
+ QMessageBox::warning(Core::ICore::dialogParent(),
+ Tr::tr("Warning"),
+ Tr::tr("Configuration is not valid."));
+ return;
+ }
+
+ setConfigState(envFile, Added);
+ m_configsCombo->addItem(config.m_configName, QVariant::fromValue(envFile));
}
void QnxSettingsWidget::removeConfiguration()
{
- const int currentIndex = m_configsCombo->currentIndex();
- auto config = static_cast<QnxConfiguration*>(
- m_configsCombo->itemData(currentIndex).value<void*>());
+ const FilePath envFile = m_configsCombo->currentData().value<FilePath>();
+ QTC_ASSERT(!envFile.isEmpty(), return);
- if (!config)
- return;
+ QnxConfiguration *config = dd->configurationFromEnvFile(envFile);
+ QTC_ASSERT(config, return);
+
+ config->ensureContents();
QMessageBox::StandardButton button =
QMessageBox::question(Core::ICore::dialogParent(),
Tr::tr("Remove QNX Configuration"),
- Tr::tr("Are you sure you want to remove:\n %1?").arg(config->displayName()),
+ Tr::tr("Are you sure you want to remove:\n %1?")
+ .arg(config->m_configName),
QMessageBox::Yes | QMessageBox::No);
if (button == QMessageBox::Yes) {
- setConfigState(config, Removed);
- m_configsCombo->removeItem(currentIndex);
+ setConfigState(envFile, Removed);
+ m_configsCombo->removeItem(m_configsCombo->currentIndex());
+ updateInformation();
}
}
-void QnxSettingsWidget::generateKits(bool checked)
-{
- const int currentIndex = m_configsCombo->currentIndex();
- auto config = static_cast<QnxConfiguration*>(
- m_configsCombo->itemData(currentIndex).value<void*>());
- if (!config)
- return;
-
- setConfigState(config, checked ? Activated : Deactivated);
-}
-
void QnxSettingsWidget::updateInformation()
{
- const int currentIndex = m_configsCombo->currentIndex();
-
- auto config = static_cast<QnxConfiguration*>(
- m_configsCombo->itemData(currentIndex).value<void*>());
-
- // update the checkbox
- m_generateKitsCheckBox->setEnabled(config ? config->isValid() : false);
- m_generateKitsCheckBox->setChecked(config ? config->isActive() : false);
-
- // update information
- m_configName->setText(config ? config->displayName() : QString());
- m_configVersion->setText(config ? config->version().toString() : QString());
- m_configHost->setText(config ? config->qnxHost().toString() : QString());
- m_configTarget->setText(config ? config->qnxTarget().toString() : QString());
+ const FilePath envFile = m_configsCombo->currentData().value<FilePath>();
+
+ if (QnxConfiguration *config = dd->configurationFromEnvFile(envFile)) {
+ config->ensureContents();
+ m_configName->setText(config->m_configName);
+ m_configVersion->setText(config->m_version.toString());
+ m_configHost->setText(config->m_qnxHost.toString());
+ m_configTarget->setText(config->m_qnxTarget.toString());
+ m_compiler->setText(config->m_qccCompiler.toUserOutput());
+ m_architectures->setText(config->architectureNames());
+ m_kitCreation->setConfiguration(envFile);
+ } else {
+ m_configName->setText({});
+ m_configVersion->setText({});
+ m_configHost->setText({});
+ m_compiler->setText({});
+ m_architectures->setText({});
+ m_kitCreation->setConfiguration({});
+ }
}
void QnxSettingsWidget::populateConfigsCombo()
{
m_configsCombo->clear();
- const QList<QnxConfiguration *> configList = m_qnxConfigManager->configurations();
- for (QnxConfiguration *config : configList) {
- m_configsCombo->addItem(config->displayName(),
- QVariant::fromValue(static_cast<void*>(config)));
+ for (const QnxConfiguration &config : std::as_const(dd->m_configurations)) {
+ config.ensureContents();
+ m_configsCombo->addItem(config.m_configName, QVariant::fromValue(config.m_envFile));
}
updateInformation();
}
-void QnxSettingsWidget::setConfigState(QnxConfiguration *config, State state)
+void QnxSettingsWidget::setConfigState(const FilePath &envFile, State state)
{
State stateToRemove = Activated;
switch (state) {
@@ -223,45 +705,79 @@ void QnxSettingsWidget::setConfigState(QnxConfiguration *config, State state)
}
for (const ConfigState &configState : std::as_const(m_changedConfigs)) {
- if (configState.config == config && configState.state == stateToRemove)
+ if (configState.envFile == envFile && configState.state == stateToRemove)
m_changedConfigs.removeAll(configState);
}
- m_changedConfigs.append(ConfigState{config, state});
+ m_changedConfigs.append(ConfigState{envFile, state});
}
void QnxSettingsWidget::apply()
{
for (const ConfigState &configState : std::as_const(m_changedConfigs)) {
switch (configState.state) {
- case Activated :
- configState.config->activate();
+ case Activated: {
+ QnxConfiguration *config = dd->configurationFromEnvFile(configState.envFile);
+ QTC_ASSERT(config, break);
+ config->activate();
break;
- case Deactivated:
- configState.config->deactivate();
+ }
+ case Deactivated: {
+ QnxConfiguration *config = dd->configurationFromEnvFile(configState.envFile);
+ QTC_ASSERT(config, break);
+ config->deactivate();
break;
- case Added:
- m_qnxConfigManager->addConfiguration(configState.config);
+ }
+ case Added: {
+ QnxConfiguration config(configState.envFile);
+ config.ensureContents();
+ dd->m_configurations.insert(configState.envFile, config);
break;
+ }
case Removed:
- configState.config->deactivate();
- m_qnxConfigManager->removeConfiguration(configState.config);
+ QnxConfiguration *config = dd->configurationFromEnvFile(configState.envFile);
+ QTC_ASSERT(config, break);
+ config->deactivate();
+ dd->m_configurations.remove(configState.envFile);
break;
}
}
m_changedConfigs.clear();
+ populateConfigsCombo();
}
-
// QnxSettingsPage
+QList<ToolChain *> QnxSettingsPage::autoDetect(const QList<ToolChain *> &alreadyKnown)
+{
+ QList<ToolChain *> result;
+ for (const QnxConfiguration &config : std::as_const(dd->m_configurations)) {
+ config.ensureContents();
+ for (const QnxTarget &target : std::as_const(config.m_targets)) {
+ result += Utils::filtered(alreadyKnown, [config, target](ToolChain *tc) {
+ return tc->typeId() == Constants::QNX_TOOLCHAIN_ID
+ && tc->targetAbi() == target.m_abi
+ && tc->compilerCommand() == config.m_qccCompiler;
+ });
+ }
+ }
+ return result;
+}
+
QnxSettingsPage::QnxSettingsPage()
{
setId("DD.Qnx Configuration");
setDisplayName(Tr::tr("QNX"));
setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
setWidgetCreator([] { return new QnxSettingsWidget; });
+
+ dd = new QnxSettingsPagePrivate;
+}
+
+QnxSettingsPage::~QnxSettingsPage()
+{
+ delete dd;
}
} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxsettingspage.h b/src/plugins/qnx/qnxsettingspage.h
index cdf6f1ba86..f127d5f35e 100644
--- a/src/plugins/qnx/qnxsettingspage.h
+++ b/src/plugins/qnx/qnxsettingspage.h
@@ -5,12 +5,18 @@
#include <coreplugin/dialogs/ioptionspage.h>
+namespace ProjectExplorer { class ToolChain; }
+
namespace Qnx::Internal {
class QnxSettingsPage final : public Core::IOptionsPage
{
public:
QnxSettingsPage();
+ ~QnxSettingsPage();
+
+ static QList<ProjectExplorer::ToolChain *> autoDetect(
+ const QList<ProjectExplorer::ToolChain *> &alreadyKnown);
};
} // Qnx::Internal
diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp
index 773efc0c74..8176b3d17c 100644
--- a/src/plugins/qnx/qnxtoolchain.cpp
+++ b/src/plugins/qnx/qnxtoolchain.cpp
@@ -3,9 +3,8 @@
#include "qnxtoolchain.h"
-#include "qnxconfiguration.h"
-#include "qnxconfigurationmanager.h"
#include "qnxconstants.h"
+#include "qnxsettingspage.h"
#include "qnxtr.h"
#include "qnxutils.h"
@@ -54,7 +53,7 @@ static Abis detectTargetAbis(const FilePath &sdpPath)
const EnvironmentItems environment = QnxUtils::qnxEnvironment(sdpPath);
for (const EnvironmentItem &item : environment) {
if (item.name == QLatin1String("QNX_TARGET"))
- qnxTarget = FilePath::fromString(item.value);
+ qnxTarget = sdpPath.withNewPath(item.value);
}
}
@@ -222,10 +221,7 @@ Toolchains QnxToolChainFactory::autoDetect(const ToolchainDetector &detector) co
if (detector.device)
return {};
- Toolchains tcs;
- const auto configurations = QnxConfigurationManager::instance()->configurations();
- for (QnxConfiguration *configuration : configurations)
- tcs += configuration->autoDetect(detector.alreadyKnown);
+ Toolchains tcs = QnxSettingsPage::autoDetect(detector.alreadyKnown);
return tcs;
}
diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp
index 697e95bebc..ba2c41b345 100644
--- a/src/plugins/qnx/qnxutils.cpp
+++ b/src/plugins/qnx/qnxutils.cpp
@@ -5,7 +5,7 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporaryfile.h>
#include <QDebug>
@@ -16,6 +16,15 @@ using namespace Utils;
namespace Qnx::Internal {
+QnxTarget::QnxTarget(const Utils::FilePath &path, const ProjectExplorer::Abi &abi) :
+ m_path(path), m_abi(abi)
+{}
+
+QString QnxTarget::shortDescription() const
+{
+ return QnxUtils::cpuDirShortDescription(cpuDir());
+}
+
QString QnxUtils::cpuDirFromAbi(const Abi &abi)
{
if (abi.os() != Abi::OS::QnxOS)
@@ -89,7 +98,7 @@ EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const FilePath &filePath)
tmpFile->writeFileContents(content.toUtf8());
// running wrapper script
- QtcProcess process;
+ Process process;
if (isWindows)
process.setCommand({filePath.withNewPath("cmd.exe"), {"/C", tmpFile->path()}});
else
diff --git a/src/plugins/qnx/qnxutils.h b/src/plugins/qnx/qnxutils.h
index 4f523953e4..2fea51ccc6 100644
--- a/src/plugins/qnx/qnxutils.h
+++ b/src/plugins/qnx/qnxutils.h
@@ -14,12 +14,14 @@ namespace Qnx::Internal {
class QnxTarget
{
public:
- QnxTarget(const Utils::FilePath &path, const ProjectExplorer::Abi &abi) :
- m_path(path), m_abi(abi)
- {
- }
+ QnxTarget(const Utils::FilePath &path, const ProjectExplorer::Abi &abi);
+
+ QString shortDescription() const;
+ QString cpuDir() const { return m_path.fileName(); }
+
Utils::FilePath m_path;
ProjectExplorer::Abi m_abi;
+ Utils::FilePath m_debuggerPath;
};
namespace QnxUtils {
diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp
index 58509782c5..77700804b2 100644
--- a/src/plugins/qnx/slog2inforunner.cpp
+++ b/src/plugins/qnx/slog2inforunner.cpp
@@ -3,13 +3,13 @@
#include "slog2inforunner.h"
-#include "qnxdevice.h"
#include "qnxtr.h"
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QRegularExpression>
@@ -31,50 +31,47 @@ Slog2InfoRunner::Slog2InfoRunner(RunControl *runControl)
void Slog2InfoRunner::start()
{
- using namespace Utils::Tasking;
+ using namespace Tasking;
QTC_CHECK(!m_taskTree);
- const auto testStartHandler = [this](QtcProcess &process) {
+ const auto testStartHandler = [this](Process &process) {
process.setCommand({device()->filePath("slog2info"), {}});
};
- const auto testDoneHandler = [this](const QtcProcess &) {
+ const auto testDoneHandler = [this](const Process &) {
m_found = true;
};
- const auto testErrorHandler = [this](const QtcProcess &) {
- QnxDevice::ConstPtr qnxDevice = device().dynamicCast<const QnxDevice>();
- if (qnxDevice && qnxDevice->qnxVersion() > 0x060500) {
- appendMessage(Tr::tr("Warning: \"slog2info\" is not found on the device, "
- "debug output not available."), ErrorMessageFormat);
- }
+ const auto testErrorHandler = [this](const Process &) {
+ appendMessage(Tr::tr("Warning: \"slog2info\" is not found on the device, "
+ "debug output not available."), ErrorMessageFormat);
};
- const auto launchTimeStartHandler = [this](QtcProcess &process) {
+ const auto launchTimeStartHandler = [this](Process &process) {
process.setCommand({device()->filePath("date"), "+\"%d %H:%M:%S\"", CommandLine::Raw});
};
- const auto launchTimeDoneHandler = [this](const QtcProcess &process) {
+ const auto launchTimeDoneHandler = [this](const Process &process) {
QTC_CHECK(!m_applicationId.isEmpty());
QTC_CHECK(m_found);
m_launchDateTime = QDateTime::fromString(process.cleanedStdOut().trimmed(), "dd HH:mm:ss");
};
- const auto logStartHandler = [this](QtcProcess &process) {
+ const auto logStartHandler = [this](Process &process) {
process.setCommand({device()->filePath("slog2info"), {"-w"}});
- connect(&process, &QtcProcess::readyReadStandardOutput, this, [&] {
+ connect(&process, &Process::readyReadStandardOutput, this, [&] {
processLogInput(QString::fromLatin1(process.readAllRawStandardOutput()));
});
- connect(&process, &QtcProcess::readyReadStandardError, this, [&] {
+ connect(&process, &Process::readyReadStandardError, this, [&] {
appendMessage(QString::fromLatin1(process.readAllRawStandardError()), StdErrFormat);
});
};
- const auto logErrorHandler = [this](const QtcProcess &process) {
+ const auto logErrorHandler = [this](const Process &process) {
appendMessage(Tr::tr("Cannot show slog2info output. Error: %1").arg(process.errorString()),
StdErrFormat);
};
- const Tasking::Group root {
- Process(testStartHandler, testDoneHandler, testErrorHandler),
- Process(launchTimeStartHandler, launchTimeDoneHandler),
- Process(logStartHandler, {}, logErrorHandler)
+ const Group root {
+ ProcessTask(testStartHandler, testDoneHandler, testErrorHandler),
+ ProcessTask(launchTimeStartHandler, launchTimeDoneHandler),
+ ProcessTask(logStartHandler, {}, logErrorHandler)
};
m_taskTree.reset(new TaskTree(root));
diff --git a/src/plugins/qnx/slog2inforunner.h b/src/plugins/qnx/slog2inforunner.h
index e89b80b697..83d8d29333 100644
--- a/src/plugins/qnx/slog2inforunner.h
+++ b/src/plugins/qnx/slog2inforunner.h
@@ -7,7 +7,7 @@
#include <QDateTime>
-namespace Utils { class TaskTree; }
+namespace Tasking { class TaskTree; }
namespace Qnx::Internal {
@@ -33,7 +33,7 @@ private:
bool m_currentLogs = false;
QString m_remainingData;
- std::unique_ptr<Utils::TaskTree> m_taskTree;
+ std::unique_ptr<Tasking::TaskTree> m_taskTree;
};
} // Qnx::Internal
diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp
index 6b1d64600f..179649c53b 100644
--- a/src/plugins/qtsupport/baseqtversion.cpp
+++ b/src/plugins/qtsupport/baseqtversion.cpp
@@ -21,7 +21,8 @@
#include <projectexplorer/headerpath.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/toolchainmanager.h>
@@ -32,8 +33,8 @@
#include <utils/fileinprojectfinder.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/winutils.h>
@@ -639,7 +640,7 @@ bool QtVersion::hasReleaseBuild() const
return !d->m_defaultConfigIsDebug || d->m_defaultConfigIsDebugAndRelease;
}
-void QtVersion::fromMap(const QVariantMap &map)
+void QtVersion::fromMap(const QVariantMap &map, const FilePath &filePath)
{
d->m_id = map.value(Constants::QTVERSIONID).toInt();
if (d->m_id == -1) // this happens on adding from installer, see updateFromInstaller => get a new unique id
@@ -664,6 +665,7 @@ void QtVersion::fromMap(const QVariantMap &map)
d->m_qmakeCommand = BuildableHelperLibrary::qtChooserToQmakePath(qmake);
}
}
+ d->m_qmakeCommand = filePath.resolvePath(d->m_qmakeCommand);
d->m_data.qtSources = FilePath::fromSettings(map.value(QTVERSIONSOURCEPATH));
@@ -1251,7 +1253,7 @@ void QtVersionPrivate::updateVersionInfo()
m_qmakeIsExecutable = true;
auto fileProperty = [this](const QByteArray &name) {
- return FilePath::fromUserInput(qmakeProperty(name)).onDevice(m_qmakeCommand);
+ return m_qmakeCommand.withNewPath(qmakeProperty(name)).cleanPath();
};
m_data.prefix = fileProperty("QT_INSTALL_PREFIX");
@@ -1542,10 +1544,10 @@ void QtVersion::populateQmlFileFinder(FileInProjectFinder *finder, const Target
// ... else try the session manager's global startup project ...
if (!startupProject)
- startupProject = SessionManager::startupProject();
+ startupProject = ProjectManager::startupProject();
// ... and if that is null, use the first project available.
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
QTC_CHECK(projects.isEmpty() || startupProject);
FilePath projectDirectory;
@@ -1682,7 +1684,7 @@ static QByteArray runQmakeQuery(const FilePath &binary, const Environment &env,
// Prevent e.g. qmake 4.x on MinGW to show annoying errors about missing dll's.
WindowsCrashDialogBlocker crashDialogBlocker;
- QtcProcess process;
+ Process process;
process.setEnvironment(env);
process.setCommand({binary, {"-query"}});
process.start();
@@ -1772,7 +1774,7 @@ FilePath QtVersionPrivate::mkspecDirectoryFromVersionInfo(const QHash<ProKey, Pr
QString dataDir = qmakeProperty(versionInfo, "QT_HOST_DATA", PropertyVariantSrc);
if (dataDir.isEmpty())
return FilePath();
- return FilePath::fromUserInput(dataDir + "/mkspecs").onDevice(qmakeCommand);
+ return qmakeCommand.withNewPath(dataDir + "/mkspecs").cleanPath();
}
FilePath QtVersionPrivate::mkspecFromVersionInfo(const QHash<ProKey, ProString> &versionInfo,
@@ -2308,12 +2310,12 @@ bool QtVersionFactory::canRestore(const QString &type)
return type == m_supportedType;
}
-QtVersion *QtVersionFactory::restore(const QString &type, const QVariantMap &data)
+QtVersion *QtVersionFactory::restore(const QString &type, const QVariantMap &data, const FilePath &filePath)
{
QTC_ASSERT(canRestore(type), return nullptr);
QTC_ASSERT(m_creator, return nullptr);
QtVersion *version = create();
- version->fromMap(data);
+ version->fromMap(data, filePath);
return version;
}
diff --git a/src/plugins/qtsupport/baseqtversion.h b/src/plugins/qtsupport/baseqtversion.h
index 2c52b80357..b590d15c28 100644
--- a/src/plugins/qtsupport/baseqtversion.h
+++ b/src/plugins/qtsupport/baseqtversion.h
@@ -49,7 +49,7 @@ public:
virtual ~QtVersion();
- virtual void fromMap(const QVariantMap &map);
+ virtual void fromMap(const QVariantMap &map, const Utils::FilePath &filePath = {});
virtual bool equals(QtVersion *other);
bool isAutodetected() const;
diff --git a/src/plugins/qtsupport/codegensettingspage.cpp b/src/plugins/qtsupport/codegensettingspage.cpp
index 8e0a3cfca0..4a30e5f969 100644
--- a/src/plugins/qtsupport/codegensettingspage.cpp
+++ b/src/plugins/qtsupport/codegensettingspage.cpp
@@ -39,12 +39,10 @@ private:
CodeGenSettingsPageWidget::CodeGenSettingsPageWidget()
{
- resize(340, 232);
-
CodeGenSettings parameters;
parameters.fromSettings(Core::ICore::settings());
- using namespace Utils::Layouting;
+ using namespace Layouting;
m_ptrAggregationRadioButton = new QRadioButton(Tr::tr("Aggregation as a pointer member"));
m_ptrAggregationRadioButton->setChecked
diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp
index d3cf3535dd..ebb90381c7 100644
--- a/src/plugins/qtsupport/exampleslistmodel.cpp
+++ b/src/plugins/qtsupport/exampleslistmodel.cpp
@@ -255,7 +255,8 @@ static QPixmap fetchPixmapAndUpdatePixmapCache(const QString &url)
img.convertTo(QImage::Format_RGB32);
const int dpr = qApp->devicePixelRatio();
// boundedTo -> don't scale thumbnails up
- const QSize scaledSize = Core::ListModel::defaultImageSize.boundedTo(img.size()) * dpr;
+ const QSize scaledSize =
+ WelcomePageHelpers::GridItemImageSize.boundedTo(img.size()) * dpr;
pixmap = QPixmap::fromImage(
img.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
pixmap.setDevicePixelRatio(dpr);
@@ -335,7 +336,7 @@ static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second)
return first->name.compare(second->name, Qt::CaseInsensitive) < 0;
}
-static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
+static QList<std::pair<Section, QList<ExampleItem *>>> getCategories(
const QList<ExampleItem *> &items, bool sortIntoCategories)
{
static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples");
@@ -352,7 +353,7 @@ static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
other.append(item);
}
}
- QList<std::pair<QString, QList<ExampleItem *>>> categories;
+ QList<std::pair<Section, QList<ExampleItem *>>> categories;
if (categoryMap.isEmpty()) {
// The example set doesn't define categories. Consider the "highlighted" ones as "featured"
QList<ExampleItem *> featured;
@@ -361,15 +362,19 @@ static QList<std::pair<QString, QList<ExampleItem *>>> getCategories(
return i->isHighlighted;
});
if (!featured.isEmpty())
- categories.append({Tr::tr("Featured", "Category for highlighted examples"), featured});
+ categories.append(
+ {{Tr::tr("Featured", "Category for highlighted examples"), 0}, featured});
if (!allOther.isEmpty())
- categories.append({otherDisplayName, allOther});
+ categories.append({{otherDisplayName, 1}, allOther});
} else {
+ int index = 0;
const auto end = categoryMap.constKeyValueEnd();
- for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it)
- categories.append(*it);
+ for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) {
+ categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second});
+ ++index;
+ }
if (!other.isEmpty())
- categories.append({otherDisplayName, other});
+ categories.append({{otherDisplayName, index, /*maxRows=*/1}, other});
}
const auto end = categories.end();
for (auto it = categories.begin(); it != end; ++it)
@@ -423,10 +428,10 @@ void ExamplesViewController::updateExamples()
}
const bool sortIntoCategories = qtVersion >= *minQtVersionForCategories;
- const QList<std::pair<QString, QList<ExampleItem *>>> sections
+ const QList<std::pair<Section, QList<ExampleItem *>>> sections
= getCategories(items, sortIntoCategories);
for (int i = 0; i < sections.size(); ++i) {
- m_view->addSection({sections.at(i).first, i},
+ m_view->addSection(sections.at(i).first,
static_container_cast<ListItem *>(sections.at(i).second));
}
}
@@ -535,7 +540,7 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath,
const QStringList demosPattern(QLatin1String("demos-manifest.xml"));
QFileInfoList fis;
const QFileInfoList subDirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (QFileInfo subDir : subDirs) {
+ for (const QFileInfo &subDir : subDirs) {
fis << QDir(subDir.absoluteFilePath()).entryInfoList(examplesPattern);
fis << QDir(subDir.absoluteFilePath()).entryInfoList(demosPattern);
}
diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp
index 5fd3161777..c44919f2e1 100644
--- a/src/plugins/qtsupport/examplesparser.cpp
+++ b/src/plugins/qtsupport/examplesparser.cpp
@@ -24,8 +24,8 @@ static FilePath relativeOrInstallPath(const FilePath &path,
return relativeResolvedPath;
if (installResolvedPath.exists())
return installResolvedPath;
- // doesn't exist, just return relative
- return relativeResolvedPath;
+ // doesn't exist, return the preferred resolved install path
+ return installResolvedPath;
}
static QString fixStringForTags(const QString &string)
diff --git a/src/plugins/qtsupport/externaleditors.cpp b/src/plugins/qtsupport/externaleditors.cpp
index bed8b7618f..dffd730d28 100644
--- a/src/plugins/qtsupport/externaleditors.cpp
+++ b/src/plugins/qtsupport/externaleditors.cpp
@@ -8,8 +8,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/session.h>
#include <qtsupport/qtkitinformation.h>
@@ -19,8 +19,8 @@
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDebug>
#include <QMap>
@@ -181,7 +181,7 @@ static bool getEditorLaunchData(const CommandForQtVersion &commandForQtVersion,
// As fallback check PATH
data->workingDirectory.clear();
QVector<QtSupport::QtVersion *> qtVersionsToCheck; // deduplicated after being filled
- if (const Project *project = SessionManager::projectForFile(filePath)) {
+ if (const Project *project = ProjectManager::projectForFile(filePath)) {
data->workingDirectory = project->projectDirectory();
// active kit
if (const Target *target = project->activeTarget()) {
@@ -224,7 +224,7 @@ static bool startEditorProcess(const LaunchData &data, QString *errorMessage)
if (debug)
qDebug() << Q_FUNC_INFO << '\n' << data.binary << data.arguments << data.workingDirectory;
qint64 pid = 0;
- if (!QtcProcess::startDetached({FilePath::fromString(data.binary), data.arguments}, data.workingDirectory, &pid)) {
+ if (!Process::startDetached({FilePath::fromString(data.binary), data.arguments}, data.workingDirectory, &pid)) {
*errorMessage = msgStartFailed(data.binary, data.arguments);
return false;
}
diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
index bf19233c21..7bc7192185 100644
--- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
+++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
@@ -280,8 +280,8 @@ public:
// for macOS dark mode
pal.setColor(QPalette::Text, Utils::creatorTheme()->color(Theme::Welcome_TextColor));
exampleSetSelector->setPalette(pal);
- exampleSetSelector->setMinimumWidth(Core::ListItemDelegate::GridItemWidth);
- exampleSetSelector->setMaximumWidth(Core::ListItemDelegate::GridItemWidth);
+ exampleSetSelector->setMinimumWidth(Core::WelcomePageHelpers::GridItemWidth);
+ exampleSetSelector->setMaximumWidth(Core::WelcomePageHelpers::GridItemWidth);
exampleSetSelector->setModel(s_exampleSetModel);
exampleSetSelector->setCurrentIndex(s_exampleSetModel->selectedExampleSet());
connect(exampleSetSelector,
diff --git a/src/plugins/qtsupport/qscxmlcgenerator.cpp b/src/plugins/qtsupport/qscxmlcgenerator.cpp
index 85c467281d..7cb67f6171 100644
--- a/src/plugins/qtsupport/qscxmlcgenerator.cpp
+++ b/src/plugins/qtsupport/qscxmlcgenerator.cpp
@@ -91,7 +91,7 @@ bool QScxmlcGenerator::prepareToRun(const QByteArray &sourceContents)
return true;
}
-FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Utils::QtcProcess *process)
+FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Utils::Process *process)
{
Q_UNUSED(process)
const Utils::FilePath wd = workingDirectory();
diff --git a/src/plugins/qtsupport/qscxmlcgenerator.h b/src/plugins/qtsupport/qscxmlcgenerator.h
index 633fb1231b..342db3cb09 100644
--- a/src/plugins/qtsupport/qscxmlcgenerator.h
+++ b/src/plugins/qtsupport/qscxmlcgenerator.h
@@ -23,7 +23,7 @@ protected:
private:
Utils::FilePath tmpFile() const;
- ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) override;
+ ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::Process *process) override;
bool prepareToRun(const QByteArray &sourceContents) override;
ProjectExplorer::Tasks parseIssues(const QByteArray &processStderr) override;
diff --git a/src/plugins/qtsupport/qtbuildaspects.cpp b/src/plugins/qtsupport/qtbuildaspects.cpp
index 95f2c8d432..210ea58ee5 100644
--- a/src/plugins/qtsupport/qtbuildaspects.cpp
+++ b/src/plugins/qtsupport/qtbuildaspects.cpp
@@ -30,13 +30,13 @@ QmlDebuggingAspect::QmlDebuggingAspect(BuildConfiguration *buildConfig)
setValue(ProjectExplorerPlugin::buildPropertiesSettings().qmlDebugging.value());
}
-void QmlDebuggingAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void QmlDebuggingAspect::addToLayout(Layouting::LayoutItem &parent)
{
- SelectionAspect::addToLayout(builder);
+ SelectionAspect::addToLayout(parent);
const auto warningLabel = createSubWidget<InfoLabel>(QString(), InfoLabel::Warning);
warningLabel->setElideMode(Qt::ElideNone);
warningLabel->setVisible(false);
- builder.addRow({{}, warningLabel});
+ parent.addRow({{}, warningLabel});
const auto changeHandler = [this, warningLabel] {
QString warningText;
QTC_ASSERT(m_buildConfig, return);
@@ -67,13 +67,13 @@ QtQuickCompilerAspect::QtQuickCompilerAspect(BuildConfiguration *buildConfig)
setValue(ProjectExplorerPlugin::buildPropertiesSettings().qtQuickCompiler.value());
}
-void QtQuickCompilerAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void QtQuickCompilerAspect::addToLayout(Layouting::LayoutItem &parent)
{
- SelectionAspect::addToLayout(builder);
+ SelectionAspect::addToLayout(parent);
const auto warningLabel = createSubWidget<InfoLabel>(QString(), InfoLabel::Warning);
warningLabel->setElideMode(Qt::ElideNone);
warningLabel->setVisible(false);
- builder.addRow({{}, warningLabel});
+ parent.addRow({{}, warningLabel});
const auto changeHandler = [this, warningLabel] {
QString warningText;
QTC_ASSERT(m_buildConfig, return);
diff --git a/src/plugins/qtsupport/qtbuildaspects.h b/src/plugins/qtsupport/qtbuildaspects.h
index e66fe9bbce..e5e0b3332e 100644
--- a/src/plugins/qtsupport/qtbuildaspects.h
+++ b/src/plugins/qtsupport/qtbuildaspects.h
@@ -18,7 +18,7 @@ class QTSUPPORT_EXPORT QmlDebuggingAspect : public Utils::TriStateAspect
public:
explicit QmlDebuggingAspect(ProjectExplorer::BuildConfiguration *buildConfig);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
private:
const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr;
@@ -32,7 +32,7 @@ public:
QtQuickCompilerAspect(ProjectExplorer::BuildConfiguration *buildConfig);
private:
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr;
};
diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp
index 2489ce5839..7fab3fbbcc 100644
--- a/src/plugins/qtsupport/qtkitinformation.cpp
+++ b/src/plugins/qtsupport/qtkitinformation.cpp
@@ -60,11 +60,11 @@ public:
private:
void makeReadOnly() final { m_combo->setEnabled(false); }
- void addToLayout(Layouting::LayoutBuilder &builder)
+ void addToLayout(Layouting::LayoutItem &parent)
{
addMutableAction(m_combo);
- builder.addItem(m_combo);
- builder.addItem(m_manageButton);
+ parent.addItem(m_combo);
+ parent.addItem(m_manageButton);
}
void refresh() final
diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp
index 25a9f5f3a7..92b439ea1a 100644
--- a/src/plugins/qtsupport/qtoptionspage.cpp
+++ b/src/plugins/qtsupport/qtoptionspage.cpp
@@ -232,8 +232,6 @@ QtOptionsPageWidget::QtOptionsPageWidget()
, m_warningVersionIcon(Utils::Icons::WARNING.icon())
, m_configurationWidget(nullptr)
{
- resize(446, 450);
-
m_qtdirList = new QTreeView(this);
m_qtdirList->setObjectName("qtDirList");
m_qtdirList->setUniformRowHeights(true);
@@ -261,15 +259,16 @@ QtOptionsPageWidget::QtOptionsPageWidget()
m_errorLabel = new QLabel;
- using namespace Utils::Layouting;
+ using namespace Layouting;
auto versionInfoWidget = new QWidget;
// clang-format off
Form {
Tr::tr("Name:"), m_nameEdit, br,
Tr::tr("qmake path:"), Row { m_qmakePath, m_editPathPushButton }, br,
- Span(2, m_errorLabel)
- }.attachTo(versionInfoWidget, WithoutMargins);
+ Span(2, m_errorLabel),
+ noMargin
+ }.attachTo(versionInfoWidget);
// clang-format on
m_formLayout = qobject_cast<QFormLayout*>(versionInfoWidget->layout());
diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp
index c54c1bfc23..3fc9fe8fed 100644
--- a/src/plugins/qtsupport/qtsupportplugin.cpp
+++ b/src/plugins/qtsupport/qtsupportplugin.cpp
@@ -24,8 +24,8 @@
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <proparser/qmakeevaluator.h>
@@ -33,7 +33,7 @@
#include <utils/filepath.h>
#include <utils/infobar.h>
#include <utils/macroexpander.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace Core;
using namespace Utils;
@@ -76,7 +76,7 @@ static void processRunnerCallback(ProcessData *data)
{
FilePath rootPath = FilePath::fromString(data->deviceRoot);
- QtcProcess proc;
+ Process proc;
proc.setProcessChannelMode(data->processChannelMode);
proc.setCommand({rootPath.withNewPath("/bin/sh"), {QString("-c"), data->command}});
proc.setWorkingDirectory(FilePath::fromString(data->workingDirectory));
@@ -170,7 +170,7 @@ void QtSupportPlugin::extensionsInitialized()
});
static const auto activeQtVersion = []() -> const QtVersion * {
- ProjectExplorer::Project *project = SessionManager::startupProject();
+ ProjectExplorer::Project *project = ProjectManager::startupProject();
if (!project || !project->activeTarget())
return nullptr;
return QtKitAspect::qtVersion(project->activeTarget()->kit());
@@ -208,7 +208,7 @@ void QtSupportPlugin::extensionsInitialized()
const FilePath filePath = item.filePath();
if (filePath.isEmpty())
return links;
- const Project *project = SessionManager::projectForFile(filePath);
+ const Project *project = ProjectManager::projectForFile(filePath);
Target *target = project ? project->activeTarget() : nullptr;
QtVersion *qt = target ? QtKitAspect::qtVersion(target->kit()) : nullptr;
if (!qt)
diff --git a/src/plugins/qtsupport/qtversionfactory.h b/src/plugins/qtsupport/qtversionfactory.h
index bce5f1b4cf..1f29cf0394 100644
--- a/src/plugins/qtsupport/qtversionfactory.h
+++ b/src/plugins/qtsupport/qtversionfactory.h
@@ -22,7 +22,7 @@ public:
static const QList<QtVersionFactory *> allQtVersionFactories();
bool canRestore(const QString &type);
- QtVersion *restore(const QString &type, const QVariantMap &data);
+ QtVersion *restore(const QString &type, const QVariantMap &data, const Utils::FilePath &workingDirectory);
/// factories with higher priority are asked first to identify
/// a qtversion, the priority of the desktop factory is 0 and
diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp
index c46a45c763..eedc79e686 100644
--- a/src/plugins/qtsupport/qtversionmanager.cpp
+++ b/src/plugins/qtsupport/qtversionmanager.cpp
@@ -21,8 +21,8 @@
#include <utils/filesystemwatcher.h>
#include <utils/hostosinfo.h>
#include <utils/persistentsettings.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDir>
#include <QFile>
@@ -193,7 +193,7 @@ static bool restoreQtVersions()
bool restored = false;
for (QtVersionFactory *f : factories) {
if (f->canRestore(type)) {
- if (QtVersion *qtv = f->restore(type, qtversionMap)) {
+ if (QtVersion *qtv = f->restore(type, qtversionMap, reader.filePath())) {
if (m_versions.contains(qtv->uniqueId())) {
// This shouldn't happen, we are restoring the same id multiple times?
qWarning() << "A Qt version with id"<<qtv->uniqueId()<<"already exists";
@@ -287,7 +287,7 @@ void QtVersionManager::updateFromInstaller(bool emitSignal)
qtversionMap[Constants::QTVERSIONNAME] = v->unexpandedDisplayName();
delete v;
- if (QtVersion *qtv = factory->restore(type, qtversionMap)) {
+ if (QtVersion *qtv = factory->restore(type, qtversionMap, reader.filePath())) {
Q_ASSERT(qtv->isAutodetected());
m_versions.insert(id, qtv);
restored = true;
@@ -301,7 +301,7 @@ void QtVersionManager::updateFromInstaller(bool emitSignal)
// Create a new qtversion
if (!restored) { // didn't replace any existing versions
qCDebug(log) << " No Qt version found matching" << autoDetectionSource << " => Creating new version";
- if (QtVersion *qtv = factory->restore(type, qtversionMap)) {
+ if (QtVersion *qtv = factory->restore(type, qtversionMap, reader.filePath())) {
Q_ASSERT(qtv->isAutodetected());
m_versions.insert(qtv->uniqueId(), qtv);
added << qtv->uniqueId();
@@ -368,7 +368,7 @@ static void saveQtVersions()
// Executes qtchooser with arguments in a process and returns its output
static QList<QByteArray> runQtChooser(const QString &qtchooser, const QStringList &arguments)
{
- QtcProcess p;
+ Process p;
p.setCommand({FilePath::fromString(qtchooser), arguments});
p.start();
p.waitForFinished();
diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp
index 36c109e7b4..f1ff905b83 100644
--- a/src/plugins/qtsupport/uicgenerator.cpp
+++ b/src/plugins/qtsupport/uicgenerator.cpp
@@ -2,20 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "uicgenerator.h"
+
#include "baseqtversion.h"
#include "qtkitinformation.h"
+#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/buildconfiguration.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <QFileInfo>
+#include <QDateTime>
#include <QDir>
+#include <QFileInfo>
#include <QLoggingCategory>
-#include <QDateTime>
using namespace ProjectExplorer;
@@ -48,7 +49,7 @@ QStringList UicGenerator::arguments() const
return {"-p"};
}
-FileNameToContentsHash UicGenerator::handleProcessFinished(Utils::QtcProcess *process)
+FileNameToContentsHash UicGenerator::handleProcessFinished(Utils::Process *process)
{
FileNameToContentsHash result;
if (process->exitStatus() != QProcess::NormalExit && process->exitCode() != 0)
diff --git a/src/plugins/qtsupport/uicgenerator.h b/src/plugins/qtsupport/uicgenerator.h
index 1d9344634a..d68b6814c2 100644
--- a/src/plugins/qtsupport/uicgenerator.h
+++ b/src/plugins/qtsupport/uicgenerator.h
@@ -18,7 +18,7 @@ public:
protected:
Utils::FilePath command() const override;
QStringList arguments() const override;
- ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::QtcProcess *process) override;
+ ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::Process *process) override;
};
class UicGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory
diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt
index 08581c2f67..f81f75350a 100644
--- a/src/plugins/remotelinux/CMakeLists.txt
+++ b/src/plugins/remotelinux/CMakeLists.txt
@@ -5,7 +5,6 @@ add_qtc_plugin(RemoteLinux
abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h
customcommanddeploystep.cpp customcommanddeploystep.h
deploymenttimeinfo.cpp deploymenttimeinfo.h
- genericdirectuploadservice.cpp genericdirectuploadservice.h
genericdirectuploadstep.cpp genericdirectuploadstep.h
genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h
genericlinuxdeviceconfigurationwizard.cpp genericlinuxdeviceconfigurationwizard.h
@@ -29,7 +28,6 @@ add_qtc_plugin(RemoteLinux
remotelinuxtr.h
rsyncdeploystep.cpp rsyncdeploystep.h
sshkeycreationdialog.cpp sshkeycreationdialog.h
- sshprocessinterface.h
tarpackagecreationstep.cpp tarpackagecreationstep.h
tarpackagedeploystep.cpp tarpackagedeploystep.h
)
diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp
index 7e7e3d9a19..afd4e0e96a 100644
--- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp
+++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp
@@ -12,149 +12,74 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
+#include <solutions/tasking/tasktree.h>
+
#include <utils/qtcassert.h>
-#include <utils/tasktree.h>
#include <QDateTime>
#include <QPointer>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
namespace RemoteLinux {
namespace Internal {
-class AbstractRemoteLinuxDeployServicePrivate
-{
-public:
- IDevice::ConstPtr deviceConfiguration;
- QPointer<Target> target;
-
- DeploymentTimeInfo deployTimes;
- std::unique_ptr<TaskTree> m_taskTree;
-};
-
class AbstractRemoteLinuxDeployStepPrivate
{
public:
bool hasError;
std::function<CheckResult()> internalInit;
std::function<void()> runPreparer;
- AbstractRemoteLinuxDeployService *deployService = nullptr;
+
+ DeploymentTimeInfo deployTimes;
+ std::unique_ptr<TaskTree> m_taskTree;
};
} // Internal
using namespace Internal;
-AbstractRemoteLinuxDeployService::AbstractRemoteLinuxDeployService(QObject *parent)
- : QObject(parent), d(new AbstractRemoteLinuxDeployServicePrivate)
-{
-}
+AbstractRemoteLinuxDeployStep::AbstractRemoteLinuxDeployStep(BuildStepList *bsl, Id id)
+ : BuildStep(bsl, id), d(new AbstractRemoteLinuxDeployStepPrivate)
+{}
-AbstractRemoteLinuxDeployService::~AbstractRemoteLinuxDeployService()
+AbstractRemoteLinuxDeployStep::~AbstractRemoteLinuxDeployStep()
{
delete d;
}
-const Target *AbstractRemoteLinuxDeployService::target() const
-{
- return d->target;
-}
-
-const Kit *AbstractRemoteLinuxDeployService::kit() const
-{
- return d->target ? d->target->kit() : nullptr;
-}
-
-IDevice::ConstPtr AbstractRemoteLinuxDeployService::deviceConfiguration() const
+IDevice::ConstPtr AbstractRemoteLinuxDeployStep::deviceConfiguration() const
{
- return d->deviceConfiguration;
+ return DeviceKitAspect::device(kit());
}
-void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile,
+void AbstractRemoteLinuxDeployStep::saveDeploymentTimeStamp(const DeployableFile &deployableFile,
const QDateTime &remoteTimestamp)
{
d->deployTimes.saveDeploymentTimeStamp(deployableFile, kit(), remoteTimestamp);
}
-bool AbstractRemoteLinuxDeployService::hasLocalFileChanged(
+bool AbstractRemoteLinuxDeployStep::hasLocalFileChanged(
const DeployableFile &deployableFile) const
{
return d->deployTimes.hasLocalFileChanged(deployableFile, kit());
}
-bool AbstractRemoteLinuxDeployService::hasRemoteFileChanged(
+bool AbstractRemoteLinuxDeployStep::hasRemoteFileChanged(
const DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const
{
return d->deployTimes.hasRemoteFileChanged(deployableFile, kit(), remoteTimestamp);
}
-void AbstractRemoteLinuxDeployService::setTarget(Target *target)
-{
- d->target = target;
- d->deviceConfiguration = DeviceKitAspect::device(kit());
-}
-
-void AbstractRemoteLinuxDeployService::start()
-{
- QTC_ASSERT(!d->m_taskTree, return);
-
- const CheckResult check = isDeploymentPossible();
- if (!check) {
- emit errorMessage(check.errorMessage());
- emit finished();
- return;
- }
-
- if (!isDeploymentNecessary()) {
- emit progressMessage(Tr::tr("No deployment action necessary. Skipping."));
- emit finished();
- return;
- }
-
- d->m_taskTree.reset(new TaskTree(deployRecipe()));
- const auto endHandler = [this] {
- d->m_taskTree.release()->deleteLater();
- emit finished();
- };
- connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler);
- connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler);
- d->m_taskTree->start();
-}
-
-void AbstractRemoteLinuxDeployService::stop()
-{
- if (!d->m_taskTree)
- return;
- d->m_taskTree.reset();
- emit finished();
-}
-
-CheckResult AbstractRemoteLinuxDeployService::isDeploymentPossible() const
+CheckResult AbstractRemoteLinuxDeployStep::isDeploymentPossible() const
{
if (!deviceConfiguration())
return CheckResult::failure(Tr::tr("No device configuration set."));
return CheckResult::success();
}
-QVariantMap AbstractRemoteLinuxDeployService::exportDeployTimes() const
-{
- return d->deployTimes.exportDeployTimes();
-}
-
-void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map)
-{
- d->deployTimes.importDeployTimes(map);
-}
-
-
-
-AbstractRemoteLinuxDeployStep::AbstractRemoteLinuxDeployStep(BuildStepList *bsl, Utils::Id id)
- : BuildStep(bsl, id), d(new Internal::AbstractRemoteLinuxDeployStepPrivate)
-{
-}
-
void AbstractRemoteLinuxDeployStep::setInternalInitializer(const std::function<CheckResult ()> &init)
{
d->internalInit = init;
@@ -165,36 +90,23 @@ void AbstractRemoteLinuxDeployStep::setRunPreparer(const std::function<void ()>
d->runPreparer = prep;
}
-void AbstractRemoteLinuxDeployStep::setDeployService(AbstractRemoteLinuxDeployService *service)
-{
- d->deployService = service;
-}
-
-AbstractRemoteLinuxDeployStep::~AbstractRemoteLinuxDeployStep()
-{
- delete d->deployService;
- delete d;
-}
-
bool AbstractRemoteLinuxDeployStep::fromMap(const QVariantMap &map)
{
if (!BuildStep::fromMap(map))
return false;
- d->deployService->importDeployTimes(map);
+ d->deployTimes.importDeployTimes(map);
return true;
}
QVariantMap AbstractRemoteLinuxDeployStep::toMap() const
{
QVariantMap map = BuildStep::toMap();
- map.insert(d->deployService->exportDeployTimes());
+ map.insert(d->deployTimes.exportDeployTimes());
return map;
}
bool AbstractRemoteLinuxDeployStep::init()
{
- d->deployService->setTarget(target());
-
QTC_ASSERT(d->internalInit, return false);
const CheckResult canDeploy = d->internalInit();
if (!canDeploy) {
@@ -209,21 +121,31 @@ void AbstractRemoteLinuxDeployStep::doRun()
if (d->runPreparer)
d->runPreparer();
- connect(d->deployService, &AbstractRemoteLinuxDeployService::errorMessage,
- this, &AbstractRemoteLinuxDeployStep::handleErrorMessage);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::progressMessage,
- this, &AbstractRemoteLinuxDeployStep::handleProgressMessage);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::warningMessage,
- this, &AbstractRemoteLinuxDeployStep::handleWarningMessage);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::stdOutData,
- this, &AbstractRemoteLinuxDeployStep::handleStdOutData);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::stdErrData,
- this, &AbstractRemoteLinuxDeployStep::handleStdErrData);
- connect(d->deployService, &AbstractRemoteLinuxDeployService::finished,
- this, &AbstractRemoteLinuxDeployStep::handleFinished);
-
d->hasError = false;
- d->deployService->start();
+
+ QTC_ASSERT(!d->m_taskTree, return);
+
+ const CheckResult check = isDeploymentPossible();
+ if (!check) {
+ addErrorMessage(check.errorMessage());
+ handleFinished();
+ return;
+ }
+
+ if (!isDeploymentNecessary()) {
+ addProgressMessage(Tr::tr("No deployment action necessary. Skipping."));
+ handleFinished();
+ return;
+ }
+
+ d->m_taskTree.reset(new TaskTree(deployRecipe()));
+ const auto endHandler = [this] {
+ d->m_taskTree.release()->deleteLater();
+ handleFinished();
+ };
+ connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler);
+ connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler);
+ d->m_taskTree->start();
}
void AbstractRemoteLinuxDeployStep::doCancel()
@@ -234,22 +156,26 @@ void AbstractRemoteLinuxDeployStep::doCancel()
emit addOutput(Tr::tr("User requests deployment to stop; cleaning up."),
OutputFormat::NormalMessage);
d->hasError = true;
- d->deployService->stop();
+
+ if (!d->m_taskTree)
+ return;
+ d->m_taskTree.reset();
+ handleFinished();
}
-void AbstractRemoteLinuxDeployStep::handleProgressMessage(const QString &message)
+void AbstractRemoteLinuxDeployStep::addProgressMessage(const QString &message)
{
emit addOutput(message, OutputFormat::NormalMessage);
}
-void AbstractRemoteLinuxDeployStep::handleErrorMessage(const QString &message)
+void AbstractRemoteLinuxDeployStep::addErrorMessage(const QString &message)
{
emit addOutput(message, OutputFormat::ErrorMessage);
emit addTask(DeploymentTask(Task::Error, message), 1); // TODO correct?
d->hasError = true;
}
-void AbstractRemoteLinuxDeployStep::handleWarningMessage(const QString &message)
+void AbstractRemoteLinuxDeployStep::addWarningMessage(const QString &message)
{
emit addOutput(message, OutputFormat::ErrorMessage);
emit addTask(DeploymentTask(Task::Warning, message), 1); // TODO correct?
@@ -261,7 +187,7 @@ void AbstractRemoteLinuxDeployStep::handleFinished()
emit addOutput(Tr::tr("Deploy step failed."), OutputFormat::ErrorMessage);
else
emit addOutput(Tr::tr("Deploy step finished."), OutputFormat::NormalMessage);
- disconnect(d->deployService, nullptr, this, nullptr);
+
emit finished(!d->hasError);
}
@@ -275,4 +201,14 @@ void AbstractRemoteLinuxDeployStep::handleStdErrData(const QString &data)
emit addOutput(data, OutputFormat::Stderr, DontAppendNewline);
}
+bool AbstractRemoteLinuxDeployStep::isDeploymentNecessary() const
+{
+ return true;
+}
+
+Group AbstractRemoteLinuxDeployStep::deployRecipe()
+{
+ return {};
+}
+
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h
index 67855064bb..b0af97f5d2 100644
--- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h
+++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h
@@ -8,55 +8,14 @@
#include <projectexplorer/buildstep.h>
#include <projectexplorer/devicesupport/idevicefwd.h>
-#include <QtCore/qcontainerfwd.h>
#include <QObject>
-namespace ProjectExplorer {
-class DeployableFile;
-class Kit;
-class Target;
-}
-
-namespace Utils::Tasking { class Group; }
+namespace ProjectExplorer { class DeployableFile; }
+namespace Tasking { class Group; }
namespace RemoteLinux {
-class AbstractRemoteLinuxDeployService;
-class CheckResult;
-
namespace Internal { class AbstractRemoteLinuxDeployStepPrivate; }
-namespace Internal { class AbstractRemoteLinuxDeployServicePrivate; }
-
-class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployStep : public ProjectExplorer::BuildStep
-{
- Q_OBJECT
-
-public:
- ~AbstractRemoteLinuxDeployStep() override;
-
-protected:
- bool fromMap(const QVariantMap &map) override;
- QVariantMap toMap() const override;
- bool init() override;
- void doRun() final;
- void doCancel() override;
-
- explicit AbstractRemoteLinuxDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
-
- void setInternalInitializer(const std::function<CheckResult()> &init);
- void setRunPreparer(const std::function<void()> &prep);
- void setDeployService(AbstractRemoteLinuxDeployService *service);
-
-private:
- void handleProgressMessage(const QString &message);
- void handleErrorMessage(const QString &message);
- void handleWarningMessage(const QString &message);
- void handleFinished();
- void handleStdOutData(const QString &data);
- void handleStdErrData(const QString &data);
-
- Internal::AbstractRemoteLinuxDeployStepPrivate *d;
-};
class REMOTELINUX_EXPORT CheckResult
{
@@ -74,36 +33,28 @@ private:
QString m_error;
};
-class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployService : public QObject
+class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployStep : public ProjectExplorer::BuildStep
{
- Q_OBJECT
- Q_DISABLE_COPY(AbstractRemoteLinuxDeployService)
public:
- explicit AbstractRemoteLinuxDeployService(QObject *parent = nullptr);
- ~AbstractRemoteLinuxDeployService() override;
-
- void setTarget(ProjectExplorer::Target *bc);
-
- void start();
- void stop();
+ explicit AbstractRemoteLinuxDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
+ ~AbstractRemoteLinuxDeployStep() override;
- QVariantMap exportDeployTimes() const;
- void importDeployTimes(const QVariantMap &map);
+ ProjectExplorer::IDeviceConstPtr deviceConfiguration() const;
virtual CheckResult isDeploymentPossible() const;
-signals:
- void errorMessage(const QString &message);
- void progressMessage(const QString &message);
- void warningMessage(const QString &message);
- void stdOutData(const QString &data);
- void stdErrData(const QString &data);
- void finished(); // Used by Qnx.
+ void handleStdOutData(const QString &data);
+ void handleStdErrData(const QString &data);
protected:
- const ProjectExplorer::Target *target() const;
- const ProjectExplorer::Kit *kit() const;
- ProjectExplorer::IDeviceConstPtr deviceConfiguration() const;
+ bool fromMap(const QVariantMap &map) override;
+ QVariantMap toMap() const override;
+ bool init() override;
+ void doRun() final;
+ void doCancel() override;
+
+ void setInternalInitializer(const std::function<CheckResult()> &init);
+ void setRunPreparer(const std::function<void()> &prep);
void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile,
const QDateTime &remoteTimestamp);
@@ -111,11 +62,17 @@ protected:
bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile,
const QDateTime &remoteTimestamp) const;
+ void addProgressMessage(const QString &message);
+ void addErrorMessage(const QString &message);
+ void addWarningMessage(const QString &message);
+
+ void handleFinished();
+
private:
- virtual bool isDeploymentNecessary() const = 0;
- virtual Utils::Tasking::Group deployRecipe() = 0;
+ virtual bool isDeploymentNecessary() const;
+ virtual Tasking::Group deployRecipe();
- Internal::AbstractRemoteLinuxDeployServicePrivate * const d;
+ Internal::AbstractRemoteLinuxDeployStepPrivate *d;
};
} // RemoteLinux
diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp
index b7abdd8304..32076d34f2 100644
--- a/src/plugins/remotelinux/customcommanddeploystep.cpp
+++ b/src/plugins/remotelinux/customcommanddeploystep.cpp
@@ -11,95 +11,79 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux::Internal {
-class CustomCommandDeployService : public AbstractRemoteLinuxDeployService
+class CustomCommandDeployStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setCommandLine(const QString &commandLine);
- CheckResult isDeploymentPossible() const final;
+ CustomCommandDeployStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ auto commandLine = addAspect<StringAspect>();
+ commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine");
+ commandLine->setLabelText(Tr::tr("Command line:"));
+ commandLine->setDisplayStyle(StringAspect::LineEditDisplay);
+ commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History");
-protected:
- Group deployRecipe() final;
+ setInternalInitializer([this, commandLine] {
+ m_commandLine = commandLine->value().trimmed();
+ return isDeploymentPossible();
+ });
+
+ addMacroExpander();
+ }
+
+ CheckResult isDeploymentPossible() const final;
private:
- bool isDeploymentNecessary() const final { return true; }
+ Group deployRecipe() final;
QString m_commandLine;
};
-void CustomCommandDeployService::setCommandLine(const QString &commandLine)
-{
- m_commandLine = commandLine;
-}
-
-CheckResult CustomCommandDeployService::isDeploymentPossible() const
+CheckResult CustomCommandDeployStep::isDeploymentPossible() const
{
if (m_commandLine.isEmpty())
return CheckResult::failure(Tr::tr("No command line given."));
- return AbstractRemoteLinuxDeployService::isDeploymentPossible();
+ return AbstractRemoteLinuxDeployStep::isDeploymentPossible();
}
-Group CustomCommandDeployService::deployRecipe()
+Group CustomCommandDeployStep::deployRecipe()
{
- const auto setupHandler = [this](QtcProcess &process) {
- emit progressMessage(Tr::tr("Starting remote command \"%1\"...").arg(m_commandLine));
+ const auto setupHandler = [this](Process &process) {
+ addProgressMessage(Tr::tr("Starting remote command \"%1\"...").arg(m_commandLine));
process.setCommand({deviceConfiguration()->filePath("/bin/sh"),
{"-c", m_commandLine}});
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [this, proc] {
- emit stdOutData(proc->readAllStandardOutput());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
+ handleStdOutData(proc->readAllStandardOutput());
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [this, proc] {
- emit stdErrData(proc->readAllStandardError());
+ connect(proc, &Process::readyReadStandardError, this, [this, proc] {
+ handleStdErrData(proc->readAllStandardError());
});
};
- const auto doneHandler = [this](const QtcProcess &) {
- emit progressMessage(Tr::tr("Remote command finished successfully."));
+ const auto doneHandler = [this](const Process &) {
+ addProgressMessage(Tr::tr("Remote command finished successfully."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
if (process.error() != QProcess::UnknownError
|| process.exitStatus() != QProcess::NormalExit) {
- emit errorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
+ addErrorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString()));
} else if (process.exitCode() != 0) {
- emit errorMessage(Tr::tr("Remote process finished with exit code %1.")
+ addErrorMessage(Tr::tr("Remote process finished with exit code %1.")
.arg(process.exitCode()));
}
};
- return Group { Process(setupHandler, doneHandler, errorHandler) };
+ return Group { ProcessTask(setupHandler, doneHandler, errorHandler) };
}
-class CustomCommandDeployStep : public AbstractRemoteLinuxDeployStep
-{
-public:
- CustomCommandDeployStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new CustomCommandDeployService;
- setDeployService(service);
-
- auto commandLine = addAspect<StringAspect>();
- commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine");
- commandLine->setLabelText(Tr::tr("Command line:"));
- commandLine->setDisplayStyle(StringAspect::LineEditDisplay);
- commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History");
-
- setInternalInitializer([service, commandLine] {
- service->setCommandLine(commandLine->value().trimmed());
- return service->isDeploymentPossible();
- });
-
- addMacroExpander();
- }
-};
-
// CustomCommandDeployStepFactory
diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp
index fcc8477974..00fdd82f36 100644
--- a/src/plugins/remotelinux/filesystemaccess_test.cpp
+++ b/src/plugins/remotelinux/filesystemaccess_test.cpp
@@ -9,6 +9,9 @@
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/sshparameters.h>
#include <utils/filepath.h>
+#include <utils/filestreamer.h>
+#include <utils/filestreamermanager.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <QDebug>
@@ -86,6 +89,45 @@ void FileSystemAccessTest::initTestCase()
QVERIFY(!filePath.exists());
QVERIFY(filePath.createDir());
QVERIFY(filePath.exists());
+
+ const QString streamerLocalDir("streamerLocalDir");
+ const QString streamerRemoteDir("streamerRemoteDir");
+ const QString sourceDir("source");
+ const QString destDir("dest");
+ const QString localDir("local");
+ const QString remoteDir("remote");
+ const FilePath localRoot;
+ const FilePath remoteRoot = m_device->rootPath();
+ const FilePath localTempDir = *localRoot.tmpDir();
+ const FilePath remoteTempDir = *remoteRoot.tmpDir();
+ m_localStreamerDir = localTempDir / streamerLocalDir;
+ m_remoteStreamerDir = remoteTempDir / streamerRemoteDir;
+ m_localSourceDir = m_localStreamerDir / sourceDir;
+ m_remoteSourceDir = m_remoteStreamerDir / sourceDir;
+ m_localDestDir = m_localStreamerDir / destDir;
+ m_remoteDestDir = m_remoteStreamerDir / destDir;
+ m_localLocalDestDir = m_localDestDir / localDir;
+ m_localRemoteDestDir = m_localDestDir / remoteDir;
+ m_remoteLocalDestDir = m_remoteDestDir / localDir;
+ m_remoteRemoteDestDir = m_remoteDestDir / remoteDir;
+
+ QVERIFY(m_localSourceDir.createDir());
+ QVERIFY(m_remoteSourceDir.createDir());
+ QVERIFY(m_localDestDir.createDir());
+ QVERIFY(m_remoteDestDir.createDir());
+ QVERIFY(m_localLocalDestDir.createDir());
+ QVERIFY(m_localRemoteDestDir.createDir());
+ QVERIFY(m_remoteLocalDestDir.createDir());
+ QVERIFY(m_remoteRemoteDestDir.createDir());
+
+ QVERIFY(m_localSourceDir.exists());
+ QVERIFY(m_remoteSourceDir.exists());
+ QVERIFY(m_localDestDir.exists());
+ QVERIFY(m_remoteDestDir.exists());
+ QVERIFY(m_localLocalDestDir.exists());
+ QVERIFY(m_localRemoteDestDir.exists());
+ QVERIFY(m_remoteLocalDestDir.exists());
+ QVERIFY(m_remoteRemoteDestDir.exists());
}
void FileSystemAccessTest::cleanupTestCase()
@@ -94,6 +136,14 @@ void FileSystemAccessTest::cleanupTestCase()
return;
QVERIFY(baseFilePath().exists());
QVERIFY(baseFilePath().removeRecursively());
+
+ QVERIFY(m_localStreamerDir.removeRecursively());
+ QVERIFY(m_remoteStreamerDir.removeRecursively());
+
+ QVERIFY(!m_localStreamerDir.exists());
+ QVERIFY(!m_remoteStreamerDir.exists());
+
+ FileStreamerManager::stopAll();
}
void FileSystemAccessTest::testCreateRemoteFile_data()
@@ -102,13 +152,13 @@ void FileSystemAccessTest::testCreateRemoteFile_data()
QTest::newRow("Spaces") << QByteArray("Line with spaces");
QTest::newRow("Newlines") << QByteArray("Some \n\n newlines \n");
- QTest::newRow("Carriage return") << QByteArray("Line with carriage \r return");
+ QTest::newRow("CarriageReturn") << QByteArray("Line with carriage \r return");
QTest::newRow("Tab") << QByteArray("Line with \t tab");
QTest::newRow("Apostrophe") << QByteArray("Line with apostrophe's character");
- QTest::newRow("Quotation marks") << QByteArray("Line with \"quotation marks\"");
- QTest::newRow("Backslash 1") << QByteArray("Line with \\ backslash");
- QTest::newRow("Backslash 2") << QByteArray("Line with \\\" backslash");
- QTest::newRow("Command output") << QByteArray("The date is: $(date +%D)");
+ QTest::newRow("QuotationMarks") << QByteArray("Line with \"quotation marks\"");
+ QTest::newRow("Backslash1") << QByteArray("Line with \\ backslash");
+ QTest::newRow("Backslash2") << QByteArray("Line with \\\" backslash");
+ QTest::newRow("CommandOutput") << QByteArray("The date is: $(date +%D)");
const int charSize = sizeof(char) * 0x100;
QByteArray charString(charSize, Qt::Uninitialized);
@@ -132,6 +182,21 @@ void FileSystemAccessTest::testCreateRemoteFile()
QVERIFY(!testFilePath.exists());
}
+void FileSystemAccessTest::testWorkingDirectory()
+{
+ const FilePath dir = baseFilePath() / "testdir with space and 'various' \"quotes\" here";
+ QVERIFY(dir.ensureWritableDir());
+ Process proc;
+ proc.setCommand({"pwd", {}});
+ proc.setWorkingDirectory(dir);
+ proc.start();
+ QVERIFY(proc.waitForFinished());
+ const QString out = proc.readAllStandardOutput().trimmed();
+ QCOMPARE(out, dir.path());
+ const QString err = proc.readAllStandardOutput();
+ QVERIFY(err.isEmpty());
+}
+
void FileSystemAccessTest::testDirStatus()
{
FilePath filePath = baseFilePath();
@@ -201,6 +266,8 @@ void FileSystemAccessTest::testFileTransfer_data()
QTest::addColumn<FileTransferMethod>("fileTransferMethod");
QTest::addRow("Sftp") << FileTransferMethod::Sftp;
+ // TODO: By default rsync doesn't support creating target directories,
+ // needs to be done manually - see RsyncDeployService.
// QTest::addRow("Rsync") << FileTransferMethod::Rsync;
}
@@ -282,5 +349,320 @@ void FileSystemAccessTest::testFileTransfer()
QVERIFY2(remoteDir.removeRecursively(&errorString), qPrintable(errorString));
}
+void FileSystemAccessTest::testFileStreamer_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QByteArray>("data");
+
+ const QByteArray spaces("Line with spaces");
+ const QByteArray newlines("Some \n\n newlines \n");
+ const QByteArray carriageReturn("Line with carriage \r return");
+ const QByteArray tab("Line with \t tab");
+ const QByteArray apostrophe("Line with apostrophe's character");
+ const QByteArray quotationMarks("Line with \"quotation marks\"");
+ const QByteArray backslash1("Line with \\ backslash");
+ const QByteArray backslash2("Line with \\\" backslash");
+ const QByteArray commandOutput("The date is: $(date +%D)");
+
+ const int charSize = sizeof(char) * 0x100;
+ QByteArray charString(charSize, Qt::Uninitialized);
+ char *data = charString.data();
+ for (int c = 0; c < charSize; ++c)
+ data[c] = c;
+
+ const int bigSize = 1024 * 1024; // = 256 * 1024 * 1024 = 268.435.456 bytes
+ QByteArray bigString;
+ for (int i = 0; i < bigSize; ++i)
+ bigString += charString;
+
+ QTest::newRow("Spaces") << QString("spaces") << spaces;
+ QTest::newRow("Newlines") << QString("newlines") << newlines;
+ QTest::newRow("CarriageReturn") << QString("carriageReturn") << carriageReturn;
+ QTest::newRow("Tab") << QString("tab") << tab;
+ QTest::newRow("Apostrophe") << QString("apostrophe") << apostrophe;
+ QTest::newRow("QuotationMarks") << QString("quotationMarks") << quotationMarks;
+ QTest::newRow("Backslash1") << QString("backslash1") << backslash1;
+ QTest::newRow("Backslash2") << QString("backslash2") << backslash2;
+ QTest::newRow("CommandOutput") << QString("commandOutput") << commandOutput;
+ QTest::newRow("AllCharacters") << QString("charString") << charString;
+ QTest::newRow("BigString") << QString("bigString") << bigString;
+}
+
+void FileSystemAccessTest::testFileStreamer()
+{
+ QElapsedTimer timer;
+ timer.start();
+
+ QFETCH(QString, fileName);
+ QFETCH(QByteArray, data);
+
+ const FilePath localSourcePath = m_localSourceDir / fileName;
+ const FilePath remoteSourcePath = m_remoteSourceDir / fileName;
+ const FilePath localLocalDestPath = m_localDestDir / "local" / fileName;
+ const FilePath localRemoteDestPath = m_localDestDir / "remote" / fileName;
+ const FilePath remoteLocalDestPath = m_remoteDestDir / "local" / fileName;
+ const FilePath remoteRemoteDestPath = m_remoteDestDir / "remote" / fileName;
+
+ localSourcePath.removeFile();
+ remoteSourcePath.removeFile();
+ localLocalDestPath.removeFile();
+ localRemoteDestPath.removeFile();
+ remoteLocalDestPath.removeFile();
+ remoteRemoteDestPath.removeFile();
+
+ QVERIFY(!localSourcePath.exists());
+ QVERIFY(!remoteSourcePath.exists());
+ QVERIFY(!localLocalDestPath.exists());
+ QVERIFY(!localRemoteDestPath.exists());
+ QVERIFY(!remoteLocalDestPath.exists());
+ QVERIFY(!remoteRemoteDestPath.exists());
+
+ std::optional<QByteArray> localData;
+ std::optional<QByteArray> remoteData;
+ std::optional<QByteArray> localLocalData;
+ std::optional<QByteArray> localRemoteData;
+ std::optional<QByteArray> remoteLocalData;
+ std::optional<QByteArray> remoteRemoteData;
+
+ using namespace Tasking;
+
+ const auto localWriter = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Writer);
+ streamer.setDestination(localSourcePath);
+ streamer.setWriteData(data);
+ };
+ return FileStreamerTask(setup);
+ };
+ const auto remoteWriter = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Writer);
+ streamer.setDestination(remoteSourcePath);
+ streamer.setWriteData(data);
+ };
+ return FileStreamerTask(setup);
+ };
+ const auto localReader = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Reader);
+ streamer.setSource(localSourcePath);
+ };
+ const auto onDone = [&](const FileStreamer &streamer) {
+ localData = streamer.readData();
+ };
+ return FileStreamerTask(setup, onDone);
+ };
+ const auto remoteReader = [&] {
+ const auto setup = [&](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Reader);
+ streamer.setSource(remoteSourcePath);
+ };
+ const auto onDone = [&](const FileStreamer &streamer) {
+ remoteData = streamer.readData();
+ };
+ return FileStreamerTask(setup, onDone);
+ };
+ const auto transfer = [](const FilePath &source, const FilePath &dest,
+ std::optional<QByteArray> *result) {
+ const auto setupTransfer = [=](FileStreamer &streamer) {
+ streamer.setSource(source);
+ streamer.setDestination(dest);
+ };
+ const auto setupReader = [=](FileStreamer &streamer) {
+ streamer.setStreamMode(StreamMode::Reader);
+ streamer.setSource(dest);
+ };
+ const auto onReaderDone = [result](const FileStreamer &streamer) {
+ *result = streamer.readData();
+ };
+ const Group root {
+ FileStreamerTask(setupTransfer),
+ FileStreamerTask(setupReader, onReaderDone)
+ };
+ return root;
+ };
+
+ // In total: 5 local reads, 3 local writes, 5 remote reads, 3 remote writes
+ const Group root {
+ Group {
+ parallel,
+ localWriter(),
+ remoteWriter()
+ },
+ Group {
+ parallel,
+ localReader(),
+ remoteReader()
+ },
+ Group {
+ parallel,
+ transfer(localSourcePath, localLocalDestPath, &localLocalData),
+ transfer(remoteSourcePath, localRemoteDestPath, &localRemoteData),
+ transfer(localSourcePath, remoteLocalDestPath, &remoteLocalData),
+ transfer(remoteSourcePath, remoteRemoteDestPath, &remoteRemoteData),
+ }
+ };
+
+ QEventLoop eventLoop;
+ TaskTree taskTree(root);
+ int doneCount = 0;
+ int errorCount = 0;
+ connect(&taskTree, &TaskTree::done, this, [&doneCount, &eventLoop] {
+ ++doneCount;
+ eventLoop.quit();
+ });
+ connect(&taskTree, &TaskTree::errorOccurred, this, [&errorCount, &eventLoop] {
+ ++errorCount;
+ eventLoop.quit();
+ });
+ taskTree.start();
+ QVERIFY(taskTree.isRunning());
+
+ QTimer timeoutTimer;
+ bool timedOut = false;
+ connect(&timeoutTimer, &QTimer::timeout, &eventLoop, [&eventLoop, &timedOut] {
+ timedOut = true;
+ eventLoop.quit();
+ });
+ timeoutTimer.setInterval(10000);
+ timeoutTimer.setSingleShot(true);
+ timeoutTimer.start();
+ eventLoop.exec();
+ QCOMPARE(timedOut, false);
+ QCOMPARE(taskTree.isRunning(), false);
+ QCOMPARE(doneCount, 1);
+ QCOMPARE(errorCount, 0);
+
+ QVERIFY(localData);
+ QCOMPARE(*localData, data);
+ QVERIFY(remoteData);
+ QCOMPARE(*remoteData, data);
+
+ QVERIFY(localLocalData);
+ QCOMPARE(*localLocalData, data);
+ QVERIFY(localRemoteData);
+ QCOMPARE(*localRemoteData, data);
+ QVERIFY(remoteLocalData);
+ QCOMPARE(*remoteLocalData, data);
+ QVERIFY(remoteRemoteData);
+ QCOMPARE(*remoteRemoteData, data);
+
+ qDebug() << "Elapsed time:" << timer.elapsed() << "ms.";
+}
+
+void FileSystemAccessTest::testFileStreamerManager_data()
+{
+ testFileStreamer_data();
+}
+
+void FileSystemAccessTest::testFileStreamerManager()
+{
+ QElapsedTimer timer;
+ timer.start();
+
+ QFETCH(QString, fileName);
+ QFETCH(QByteArray, data);
+
+ const FilePath localSourcePath = m_localSourceDir / fileName;
+ const FilePath remoteSourcePath = m_remoteSourceDir / fileName;
+ const FilePath localLocalDestPath = m_localDestDir / "local" / fileName;
+ const FilePath localRemoteDestPath = m_localDestDir / "remote" / fileName;
+ const FilePath remoteLocalDestPath = m_remoteDestDir / "local" / fileName;
+ const FilePath remoteRemoteDestPath = m_remoteDestDir / "remote" / fileName;
+
+ localSourcePath.removeFile();
+ remoteSourcePath.removeFile();
+ localLocalDestPath.removeFile();
+ localRemoteDestPath.removeFile();
+ remoteLocalDestPath.removeFile();
+ remoteRemoteDestPath.removeFile();
+
+ QVERIFY(!localSourcePath.exists());
+ QVERIFY(!remoteSourcePath.exists());
+ QVERIFY(!localLocalDestPath.exists());
+ QVERIFY(!localRemoteDestPath.exists());
+ QVERIFY(!remoteLocalDestPath.exists());
+ QVERIFY(!remoteRemoteDestPath.exists());
+
+ std::optional<QByteArray> localData;
+ std::optional<QByteArray> remoteData;
+ std::optional<QByteArray> localLocalData;
+ std::optional<QByteArray> localRemoteData;
+ std::optional<QByteArray> remoteLocalData;
+ std::optional<QByteArray> remoteRemoteData;
+
+ QEventLoop eventLoop1;
+ QEventLoop *loop = &eventLoop1;
+ int counter = 0;
+ int *hitCount = &counter;
+
+ const auto writeAndRead = [hitCount, loop, data](const FilePath &destination,
+ std::optional<QByteArray> *result) {
+ const auto onWrite = [hitCount, loop, destination, result]
+ (const expected_str<qint64> &writeResult) {
+ QVERIFY(writeResult);
+ const auto onRead = [hitCount, loop, result]
+ (const expected_str<QByteArray> &readResult) {
+ QVERIFY(readResult);
+ *result = *readResult;
+ ++(*hitCount);
+ if (*hitCount == 2)
+ loop->quit();
+ };
+ FileStreamerManager::read(destination, onRead);
+ };
+ FileStreamerManager::write(destination, data, onWrite);
+ };
+
+ writeAndRead(localSourcePath, &localData);
+ writeAndRead(remoteSourcePath, &remoteData);
+ loop->exec();
+
+ QVERIFY(localData);
+ QCOMPARE(*localData, data);
+ QVERIFY(remoteData);
+ QCOMPARE(*remoteData, data);
+
+ QEventLoop eventLoop2;
+ loop = &eventLoop2;
+ counter = 0;
+
+ const auto transferAndRead = [hitCount, loop, data](const FilePath &source,
+ const FilePath &destination,
+ std::optional<QByteArray> *result) {
+ const auto onTransfer = [hitCount, loop, destination, result]
+ (const expected_str<void> &transferResult) {
+ QVERIFY(transferResult);
+ const auto onRead = [hitCount, loop, result]
+ (const expected_str<QByteArray> &readResult) {
+ QVERIFY(readResult);
+ *result = *readResult;
+ ++(*hitCount);
+ if (*hitCount == 4)
+ loop->quit();
+ };
+ FileStreamerManager::read(destination, onRead);
+ };
+ FileStreamerManager::copy(source, destination, onTransfer);
+ };
+
+ transferAndRead(localSourcePath, localLocalDestPath, &localLocalData);
+ transferAndRead(remoteSourcePath, localRemoteDestPath, &localRemoteData);
+ transferAndRead(localSourcePath, remoteLocalDestPath, &remoteLocalData);
+ transferAndRead(remoteSourcePath, remoteRemoteDestPath, &remoteRemoteData);
+ loop->exec();
+
+ QVERIFY(localLocalData);
+ QCOMPARE(*localLocalData, data);
+ QVERIFY(localRemoteData);
+ QCOMPARE(*localRemoteData, data);
+ QVERIFY(remoteLocalData);
+ QCOMPARE(*remoteLocalData, data);
+ QVERIFY(remoteRemoteData);
+ QCOMPARE(*remoteRemoteData, data);
+
+ qDebug() << "Elapsed time:" << timer.elapsed() << "ms.";
+}
+
} // Internal
} // RemoteLinux
diff --git a/src/plugins/remotelinux/filesystemaccess_test.h b/src/plugins/remotelinux/filesystemaccess_test.h
index 9684cbc926..087eabbcad 100644
--- a/src/plugins/remotelinux/filesystemaccess_test.h
+++ b/src/plugins/remotelinux/filesystemaccess_test.h
@@ -4,6 +4,7 @@
#pragma once
#include <projectexplorer/devicesupport/idevicefactory.h>
+#include <utils/filepath.h>
namespace RemoteLinux {
namespace Internal {
@@ -23,11 +24,16 @@ private slots:
void testCreateRemoteFile_data();
void testCreateRemoteFile();
+ void testWorkingDirectory();
void testDirStatus();
void testBytesAvailable();
void testFileActions();
void testFileTransfer_data();
void testFileTransfer();
+ void testFileStreamer_data();
+ void testFileStreamer();
+ void testFileStreamerManager_data();
+ void testFileStreamerManager();
void cleanupTestCase();
@@ -35,6 +41,16 @@ private:
TestLinuxDeviceFactory m_testLinuxDeviceFactory;
bool m_skippedAtWhole = false;
ProjectExplorer::IDeviceConstPtr m_device;
+ Utils::FilePath m_localStreamerDir;
+ Utils::FilePath m_remoteStreamerDir;
+ Utils::FilePath m_localSourceDir;
+ Utils::FilePath m_remoteSourceDir;
+ Utils::FilePath m_localDestDir;
+ Utils::FilePath m_remoteDestDir;
+ Utils::FilePath m_localLocalDestDir;
+ Utils::FilePath m_localRemoteDestDir;
+ Utils::FilePath m_remoteLocalDestDir;
+ Utils::FilePath m_remoteRemoteDestDir;
};
} // Internal
diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp
deleted file mode 100644
index 6cd3c79937..0000000000
--- a/src/plugins/remotelinux/genericdirectuploadservice.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "genericdirectuploadservice.h"
-
-#include "remotelinuxtr.h"
-
-#include <projectexplorer/deployablefile.h>
-#include <projectexplorer/devicesupport/filetransfer.h>
-#include <projectexplorer/devicesupport/idevice.h>
-
-#include <utils/hostosinfo.h>
-#include <utils/processinterface.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-
-#include <QDateTime>
-#include <QDir>
-
-using namespace ProjectExplorer;
-using namespace Utils;
-using namespace Utils::Tasking;
-
-namespace RemoteLinux {
-namespace Internal {
-
-const int MaxConcurrentStatCalls = 10;
-
-struct UploadStorage
-{
- QList<DeployableFile> filesToUpload;
-};
-
-class GenericDirectUploadServicePrivate
-{
-public:
- GenericDirectUploadServicePrivate(GenericDirectUploadService *service) : q(service) {}
-
- QDateTime timestampFromStat(const DeployableFile &file, QtcProcess *statProc);
-
- using FilesToStat = std::function<QList<DeployableFile>(UploadStorage *)>;
- using StatEndHandler
- = std::function<void(UploadStorage *, const DeployableFile &, const QDateTime &)>;
- TaskItem statTask(UploadStorage *storage, const DeployableFile &file,
- StatEndHandler statEndHandler);
- TaskItem statTree(const TreeStorage<UploadStorage> &storage, FilesToStat filesToStat,
- StatEndHandler statEndHandler);
- TaskItem uploadTask(const TreeStorage<UploadStorage> &storage);
- TaskItem chmodTask(const DeployableFile &file);
- TaskItem chmodTree(const TreeStorage<UploadStorage> &storage);
-
- GenericDirectUploadService *q = nullptr;
- IncrementalDeployment incremental = IncrementalDeployment::NotSupported;
- bool ignoreMissingFiles = false;
- QList<DeployableFile> deployableFiles;
-};
-
-QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable)
-{
- QList<DeployableFile> collected;
- FilePath localFile = deployable.localFilePath();
- if (localFile.isDir()) {
- const FilePaths files = localFile.dirEntries(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
- const QString remoteDir = deployable.remoteDirectory() + '/' + localFile.fileName();
- for (const FilePath &localFilePath : files)
- collected.append(collectFilesToUpload(DeployableFile(localFilePath, remoteDir)));
- } else {
- collected << deployable;
- }
- return collected;
-}
-
-} // namespace Internal
-
-using namespace Internal;
-
-GenericDirectUploadService::GenericDirectUploadService(QObject *parent)
- : AbstractRemoteLinuxDeployService(parent), d(new GenericDirectUploadServicePrivate(this))
-{
-}
-
-GenericDirectUploadService::~GenericDirectUploadService()
-{
- delete d;
-}
-
-void GenericDirectUploadService::setDeployableFiles(const QList<DeployableFile> &deployableFiles)
-{
- d->deployableFiles = deployableFiles;
-}
-
-void GenericDirectUploadService::setIncrementalDeployment(IncrementalDeployment incremental)
-{
- d->incremental = incremental;
-}
-
-void GenericDirectUploadService::setIgnoreMissingFiles(bool ignoreMissingFiles)
-{
- d->ignoreMissingFiles = ignoreMissingFiles;
-}
-
-bool GenericDirectUploadService::isDeploymentNecessary() const
-{
- QList<DeployableFile> collected;
- for (int i = 0; i < d->deployableFiles.count(); ++i)
- collected.append(collectFilesToUpload(d->deployableFiles.at(i)));
-
- QTC_CHECK(collected.size() >= d->deployableFiles.size());
- d->deployableFiles = collected;
- return !d->deployableFiles.isEmpty();
-}
-
-QDateTime GenericDirectUploadServicePrivate::timestampFromStat(const DeployableFile &file,
- QtcProcess *statProc)
-{
- bool succeeded = false;
- QString error;
- if (statProc->error() == QProcess::FailedToStart) {
- error = Tr::tr("Failed to start \"stat\": %1").arg(statProc->errorString());
- } else if (statProc->exitStatus() == QProcess::CrashExit) {
- error = Tr::tr("\"stat\" crashed.");
- } else if (statProc->exitCode() != 0) {
- error = Tr::tr("\"stat\" failed with exit code %1: %2")
- .arg(statProc->exitCode()).arg(statProc->cleanedStdErr());
- } else {
- succeeded = true;
- }
- if (!succeeded) {
- emit q->warningMessage(Tr::tr("Failed to retrieve remote timestamp for file \"%1\". "
- "Incremental deployment will not work. Error message was: %2")
- .arg(file.remoteFilePath(), error));
- return {};
- }
- const QByteArray output = statProc->readAllRawStandardOutput().trimmed();
- const QString warningString(Tr::tr("Unexpected stat output for remote file \"%1\": %2")
- .arg(file.remoteFilePath()).arg(QString::fromUtf8(output)));
- if (!output.startsWith(file.remoteFilePath().toUtf8())) {
- emit q->warningMessage(warningString);
- return {};
- }
- const QByteArrayList columns = output.mid(file.remoteFilePath().toUtf8().size() + 1).split(' ');
- if (columns.size() < 14) { // Normal Linux stat: 16 columns in total, busybox stat: 15 columns
- emit q->warningMessage(warningString);
- return {};
- }
- bool isNumber;
- const qint64 secsSinceEpoch = columns.at(11).toLongLong(&isNumber);
- if (!isNumber) {
- emit q->warningMessage(warningString);
- return {};
- }
- return QDateTime::fromSecsSinceEpoch(secsSinceEpoch);
-}
-
-TaskItem GenericDirectUploadServicePrivate::statTask(UploadStorage *storage,
- const DeployableFile &file,
- StatEndHandler statEndHandler)
-{
- const auto setupHandler = [=](QtcProcess &process) {
- // We'd like to use --format=%Y, but it's not supported by busybox.
- process.setCommand({q->deviceConfiguration()->filePath("stat"),
- {"-t", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
- };
- const auto endHandler = [=](const QtcProcess &process) {
- QtcProcess *proc = const_cast<QtcProcess *>(&process);
- const QDateTime timestamp = timestampFromStat(file, proc);
- statEndHandler(storage, file, timestamp);
- };
- return Process(setupHandler, endHandler, endHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::statTree(const TreeStorage<UploadStorage> &storage,
- FilesToStat filesToStat, StatEndHandler statEndHandler)
-{
- const auto setupHandler = [=](TaskTree &tree) {
- UploadStorage *storagePtr = storage.activeStorage();
- const QList<DeployableFile> files = filesToStat(storagePtr);
- QList<TaskItem> statList{optional, ParallelLimit(MaxConcurrentStatCalls)};
- for (const DeployableFile &file : std::as_const(files)) {
- QTC_ASSERT(file.isValid(), continue);
- statList.append(statTask(storagePtr, file, statEndHandler));
- }
- tree.setupRoot({statList});
- };
- return Tree(setupHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::uploadTask(const TreeStorage<UploadStorage> &storage)
-{
- const auto setupHandler = [this, storage](FileTransfer &transfer) {
- if (storage->filesToUpload.isEmpty()) {
- emit q->progressMessage(Tr::tr("No files need to be uploaded."));
- return TaskAction::StopWithDone;
- }
- emit q->progressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
- storage->filesToUpload.size()));
- FilesToTransfer files;
- for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
- if (!file.localFilePath().exists()) {
- const QString message = Tr::tr("Local file \"%1\" does not exist.")
- .arg(file.localFilePath().toUserOutput());
- if (ignoreMissingFiles) {
- emit q->warningMessage(message);
- continue;
- }
- emit q->errorMessage(message);
- return TaskAction::StopWithError;
- }
- files.append({file.localFilePath(),
- q->deviceConfiguration()->filePath(file.remoteFilePath())});
- }
- if (files.isEmpty()) {
- emit q->progressMessage(Tr::tr("No files need to be uploaded."));
- return TaskAction::StopWithDone;
- }
- transfer.setFilesToTransfer(files);
- QObject::connect(&transfer, &FileTransfer::progress,
- q, &GenericDirectUploadService::progressMessage);
- return TaskAction::Continue;
- };
- const auto errorHandler = [this](const FileTransfer &transfer) {
- emit q->errorMessage(transfer.resultData().m_errorString);
- };
-
- return Transfer(setupHandler, {}, errorHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::chmodTask(const DeployableFile &file)
-{
- const auto setupHandler = [=](QtcProcess &process) {
- process.setCommand({q->deviceConfiguration()->filePath("chmod"),
- {"a+x", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
- };
- const auto errorHandler = [=](const QtcProcess &process) {
- const QString error = process.errorString();
- if (!error.isEmpty()) {
- emit q->warningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
- .arg(file.remoteFilePath(), error));
- } else if (process.exitCode() != 0) {
- emit q->warningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
- .arg(file.remoteFilePath(), process.cleanedStdErr()));
- }
- };
- return Process(setupHandler, {}, errorHandler);
-}
-
-TaskItem GenericDirectUploadServicePrivate::chmodTree(const TreeStorage<UploadStorage> &storage)
-{
- const auto setupChmodHandler = [=](TaskTree &tree) {
- QList<DeployableFile> filesToChmod;
- for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
- if (file.isExecutable())
- filesToChmod << file;
- }
- QList<TaskItem> chmodList{optional, ParallelLimit(MaxConcurrentStatCalls)};
- for (const DeployableFile &file : std::as_const(filesToChmod)) {
- QTC_ASSERT(file.isValid(), continue);
- chmodList.append(chmodTask(file));
- }
- tree.setupRoot({chmodList});
- };
- return Tree(setupChmodHandler);
-}
-
-Group GenericDirectUploadService::deployRecipe()
-{
- const auto preFilesToStat = [this](UploadStorage *storage) {
- QList<DeployableFile> filesToStat;
- for (const DeployableFile &file : std::as_const(d->deployableFiles)) {
- if (d->incremental != IncrementalDeployment::Enabled || hasLocalFileChanged(file)) {
- storage->filesToUpload.append(file);
- continue;
- }
- if (d->incremental == IncrementalDeployment::NotSupported)
- continue;
- filesToStat << file;
- }
- return filesToStat;
- };
- const auto preStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
- const QDateTime &timestamp) {
- if (!timestamp.isValid() || hasRemoteFileChanged(file, timestamp))
- storage->filesToUpload.append(file);
- };
-
- const auto postFilesToStat = [this](UploadStorage *storage) {
- return d->incremental == IncrementalDeployment::NotSupported
- ? QList<DeployableFile>() : storage->filesToUpload;
- };
- const auto postStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
- const QDateTime &timestamp) {
- Q_UNUSED(storage)
- if (timestamp.isValid())
- saveDeploymentTimeStamp(file, timestamp);
- };
- const auto doneHandler = [this] {
- emit progressMessage(Tr::tr("All files successfully deployed."));
- };
-
- const TreeStorage<UploadStorage> storage;
- const Group root {
- Storage(storage),
- d->statTree(storage, preFilesToStat, preStatEndHandler),
- d->uploadTask(storage),
- Group {
- d->chmodTree(storage),
- d->statTree(storage, postFilesToStat, postStatEndHandler)
- },
- OnGroupDone(doneHandler)
- };
- return root;
-}
-
-} //namespace RemoteLinux
diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h
deleted file mode 100644
index 31799f063a..0000000000
--- a/src/plugins/remotelinux/genericdirectuploadservice.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "remotelinux_export.h"
-
-#include "abstractremotelinuxdeploystep.h"
-
-#include <QList>
-
-namespace ProjectExplorer { class DeployableFile; }
-
-namespace RemoteLinux {
-namespace Internal { class GenericDirectUploadServicePrivate; }
-
-enum class IncrementalDeployment { Enabled, Disabled, NotSupported };
-
-class REMOTELINUX_EXPORT GenericDirectUploadService : public AbstractRemoteLinuxDeployService
-{
- Q_OBJECT
-public:
- GenericDirectUploadService(QObject *parent = nullptr);
- ~GenericDirectUploadService();
-
- void setDeployableFiles(const QList<ProjectExplorer::DeployableFile> &deployableFiles);
- void setIncrementalDeployment(IncrementalDeployment incremental);
- void setIgnoreMissingFiles(bool ignoreMissingFiles);
-
-private:
- bool isDeploymentNecessary() const final;
- Utils::Tasking::Group deployRecipe() final;
-
- friend class Internal::GenericDirectUploadServicePrivate;
- Internal::GenericDirectUploadServicePrivate * const d;
-};
-
-} //namespace RemoteLinux
diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp
index 2b778e2b9c..8a7c1e4c02 100644
--- a/src/plugins/remotelinux/genericdirectuploadstep.cpp
+++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp
@@ -2,69 +2,325 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "genericdirectuploadstep.h"
+#include "abstractremotelinuxdeploystep.h"
-#include "genericdirectuploadservice.h"
#include "remotelinux_constants.h"
#include "remotelinuxtr.h"
+#include <projectexplorer/deployablefile.h>
#include <projectexplorer/deploymentdata.h>
-#include <projectexplorer/target.h>
+#include <projectexplorer/devicesupport/filetransfer.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
+#include <projectexplorer/target.h>
+
+#include <utils/hostosinfo.h>
+#include <utils/process.h>
+#include <utils/processinterface.h>
+#include <utils/qtcassert.h>
+
+#include <QDateTime>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-namespace RemoteLinux {
+namespace RemoteLinux::Internal {
-GenericDirectUploadStep::GenericDirectUploadStep(BuildStepList *bsl, Utils::Id id,
- bool offerIncrementalDeployment)
- : AbstractRemoteLinuxDeployStep(bsl, id)
+const int MaxConcurrentStatCalls = 10;
+
+struct UploadStorage
{
- auto service = new GenericDirectUploadService;
- setDeployService(service);
+ QList<DeployableFile> filesToUpload;
+};
+
+enum class IncrementalDeployment { Enabled, Disabled, NotSupported };
- BoolAspect *incremental = nullptr;
- if (offerIncrementalDeployment) {
- incremental = addAspect<BoolAspect>();
+class GenericDirectUploadStep : public AbstractRemoteLinuxDeployStep
+{
+public:
+ GenericDirectUploadStep(ProjectExplorer::BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ auto incremental = addAspect<BoolAspect>();
incremental->setSettingsKey("RemoteLinux.GenericDirectUploadStep.Incremental");
incremental->setLabel(Tr::tr("Incremental deployment"),
BoolAspect::LabelPlacement::AtCheckBox);
incremental->setValue(true);
incremental->setDefaultValue(true);
+
+ auto ignoreMissingFiles = addAspect<BoolAspect>();
+ ignoreMissingFiles->setSettingsKey("RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles");
+ ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files"),
+ BoolAspect::LabelPlacement::AtCheckBox);
+ ignoreMissingFiles->setValue(false);
+
+ setInternalInitializer([this, incremental, ignoreMissingFiles] {
+ m_incremental = incremental->value()
+ ? IncrementalDeployment::Enabled : IncrementalDeployment::Disabled;
+ m_ignoreMissingFiles = ignoreMissingFiles->value();
+ return isDeploymentPossible();
+ });
+
+ setRunPreparer([this] {
+ m_deployableFiles = target()->deploymentData().allFiles();
+ });
+ }
+
+ bool isDeploymentNecessary() const final;
+ Group deployRecipe() final;
+
+ QDateTime timestampFromStat(const DeployableFile &file, Process *statProc);
+
+ using FilesToStat = std::function<QList<DeployableFile>(UploadStorage *)>;
+ using StatEndHandler
+ = std::function<void(UploadStorage *, const DeployableFile &, const QDateTime &)>;
+ TaskItem statTask(UploadStorage *storage, const DeployableFile &file,
+ StatEndHandler statEndHandler);
+ TaskItem statTree(const TreeStorage<UploadStorage> &storage, FilesToStat filesToStat,
+ StatEndHandler statEndHandler);
+ TaskItem uploadTask(const TreeStorage<UploadStorage> &storage);
+ TaskItem chmodTask(const DeployableFile &file);
+ TaskItem chmodTree(const TreeStorage<UploadStorage> &storage);
+
+ IncrementalDeployment m_incremental = IncrementalDeployment::NotSupported;
+ bool m_ignoreMissingFiles = false;
+ mutable QList<DeployableFile> m_deployableFiles;
+};
+
+static QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable)
+{
+ QList<DeployableFile> collected;
+ FilePath localFile = deployable.localFilePath();
+ if (localFile.isDir()) {
+ const FilePaths files = localFile.dirEntries(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ const QString remoteDir = deployable.remoteDirectory() + '/' + localFile.fileName();
+ for (const FilePath &localFilePath : files)
+ collected.append(collectFilesToUpload(DeployableFile(localFilePath, remoteDir)));
+ } else {
+ collected << deployable;
+ }
+ return collected;
+}
+
+bool GenericDirectUploadStep::isDeploymentNecessary() const
+{
+ QList<DeployableFile> collected;
+ for (int i = 0; i < m_deployableFiles.count(); ++i)
+ collected.append(collectFilesToUpload(m_deployableFiles.at(i)));
+
+ QTC_CHECK(collected.size() >= m_deployableFiles.size());
+ m_deployableFiles = collected;
+ return !m_deployableFiles.isEmpty();
+}
+
+QDateTime GenericDirectUploadStep::timestampFromStat(const DeployableFile &file,
+ Process *statProc)
+{
+ bool succeeded = false;
+ QString error;
+ if (statProc->error() == QProcess::FailedToStart) {
+ error = Tr::tr("Failed to start \"stat\": %1").arg(statProc->errorString());
+ } else if (statProc->exitStatus() == QProcess::CrashExit) {
+ error = Tr::tr("\"stat\" crashed.");
+ } else if (statProc->exitCode() != 0) {
+ error = Tr::tr("\"stat\" failed with exit code %1: %2")
+ .arg(statProc->exitCode()).arg(statProc->cleanedStdErr());
+ } else {
+ succeeded = true;
+ }
+ if (!succeeded) {
+ addWarningMessage(Tr::tr("Failed to retrieve remote timestamp for file \"%1\". "
+ "Incremental deployment will not work. Error message was: %2")
+ .arg(file.remoteFilePath(), error));
+ return {};
}
+ const QByteArray output = statProc->readAllRawStandardOutput().trimmed();
+ const QString warningString(Tr::tr("Unexpected stat output for remote file \"%1\": %2")
+ .arg(file.remoteFilePath()).arg(QString::fromUtf8(output)));
+ if (!output.startsWith(file.remoteFilePath().toUtf8())) {
+ addWarningMessage(warningString);
+ return {};
+ }
+ const QByteArrayList columns = output.mid(file.remoteFilePath().toUtf8().size() + 1).split(' ');
+ if (columns.size() < 14) { // Normal Linux stat: 16 columns in total, busybox stat: 15 columns
+ addWarningMessage(warningString);
+ return {};
+ }
+ bool isNumber;
+ const qint64 secsSinceEpoch = columns.at(11).toLongLong(&isNumber);
+ if (!isNumber) {
+ addWarningMessage(warningString);
+ return {};
+ }
+ return QDateTime::fromSecsSinceEpoch(secsSinceEpoch);
+}
+
+TaskItem GenericDirectUploadStep::statTask(UploadStorage *storage,
+ const DeployableFile &file,
+ StatEndHandler statEndHandler)
+{
+ const auto setupHandler = [=](Process &process) {
+ // We'd like to use --format=%Y, but it's not supported by busybox.
+ process.setCommand({deviceConfiguration()->filePath("stat"),
+ {"-t", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
+ };
+ const auto endHandler = [=](const Process &process) {
+ Process *proc = const_cast<Process *>(&process);
+ const QDateTime timestamp = timestampFromStat(file, proc);
+ statEndHandler(storage, file, timestamp);
+ };
+ return ProcessTask(setupHandler, endHandler, endHandler);
+}
- auto ignoreMissingFiles = addAspect<BoolAspect>();
- ignoreMissingFiles->setSettingsKey("RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles");
- ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files"),
- BoolAspect::LabelPlacement::AtCheckBox);
- ignoreMissingFiles->setValue(false);
-
- setInternalInitializer([incremental, ignoreMissingFiles, service] {
- if (incremental) {
- service->setIncrementalDeployment(incremental->value()
- ? IncrementalDeployment::Enabled : IncrementalDeployment::Disabled);
- } else {
- service->setIncrementalDeployment(IncrementalDeployment::NotSupported);
+TaskItem GenericDirectUploadStep::statTree(const TreeStorage<UploadStorage> &storage,
+ FilesToStat filesToStat, StatEndHandler statEndHandler)
+{
+ const auto setupHandler = [=](TaskTree &tree) {
+ UploadStorage *storagePtr = storage.activeStorage();
+ const QList<DeployableFile> files = filesToStat(storagePtr);
+ QList<TaskItem> statList{optional, ParallelLimit(MaxConcurrentStatCalls)};
+ for (const DeployableFile &file : std::as_const(files)) {
+ QTC_ASSERT(file.isValid(), continue);
+ statList.append(statTask(storagePtr, file, statEndHandler));
+ }
+ tree.setupRoot({statList});
+ };
+ return TaskTreeTask(setupHandler);
+}
+
+TaskItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> &storage)
+{
+ const auto setupHandler = [this, storage](FileTransfer &transfer) {
+ if (storage->filesToUpload.isEmpty()) {
+ addProgressMessage(Tr::tr("No files need to be uploaded."));
+ return TaskAction::StopWithDone;
+ }
+ addProgressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
+ storage->filesToUpload.size()));
+ FilesToTransfer files;
+ for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
+ if (!file.localFilePath().exists()) {
+ const QString message = Tr::tr("Local file \"%1\" does not exist.")
+ .arg(file.localFilePath().toUserOutput());
+ if (m_ignoreMissingFiles) {
+ addWarningMessage(message);
+ continue;
+ }
+ addErrorMessage(message);
+ return TaskAction::StopWithError;
+ }
+ files.append({file.localFilePath(),
+ deviceConfiguration()->filePath(file.remoteFilePath())});
}
- service->setIgnoreMissingFiles(ignoreMissingFiles->value());
- return service->isDeploymentPossible();
- });
+ if (files.isEmpty()) {
+ addProgressMessage(Tr::tr("No files need to be uploaded."));
+ return TaskAction::StopWithDone;
+ }
+ transfer.setFilesToTransfer(files);
+ QObject::connect(&transfer, &FileTransfer::progress,
+ this, &GenericDirectUploadStep::addProgressMessage);
+ return TaskAction::Continue;
+ };
+ const auto errorHandler = [this](const FileTransfer &transfer) {
+ addErrorMessage(transfer.resultData().m_errorString);
+ };
- setRunPreparer([this, service] {
- service->setDeployableFiles(target()->deploymentData().allFiles());
- });
+ return FileTransferTask(setupHandler, {}, errorHandler);
}
-GenericDirectUploadStep::~GenericDirectUploadStep() = default;
+TaskItem GenericDirectUploadStep::chmodTask(const DeployableFile &file)
+{
+ const auto setupHandler = [=](Process &process) {
+ process.setCommand({deviceConfiguration()->filePath("chmod"),
+ {"a+x", Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath())}});
+ };
+ const auto errorHandler = [=](const Process &process) {
+ const QString error = process.errorString();
+ if (!error.isEmpty()) {
+ addWarningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
+ .arg(file.remoteFilePath(), error));
+ } else if (process.exitCode() != 0) {
+ addWarningMessage(Tr::tr("Remote chmod failed for file \"%1\": %2")
+ .arg(file.remoteFilePath(), process.cleanedStdErr()));
+ }
+ };
+ return ProcessTask(setupHandler, {}, errorHandler);
+}
-Utils::Id GenericDirectUploadStep::stepId()
+TaskItem GenericDirectUploadStep::chmodTree(const TreeStorage<UploadStorage> &storage)
{
- return Constants::DirectUploadStepId;
+ const auto setupChmodHandler = [=](TaskTree &tree) {
+ QList<DeployableFile> filesToChmod;
+ for (const DeployableFile &file : std::as_const(storage->filesToUpload)) {
+ if (file.isExecutable())
+ filesToChmod << file;
+ }
+ QList<TaskItem> chmodList{optional, ParallelLimit(MaxConcurrentStatCalls)};
+ for (const DeployableFile &file : std::as_const(filesToChmod)) {
+ QTC_ASSERT(file.isValid(), continue);
+ chmodList.append(chmodTask(file));
+ }
+ tree.setupRoot({chmodList});
+ };
+ return TaskTreeTask(setupChmodHandler);
}
-QString GenericDirectUploadStep::displayName()
+Group GenericDirectUploadStep::deployRecipe()
+{
+ const auto preFilesToStat = [this](UploadStorage *storage) {
+ QList<DeployableFile> filesToStat;
+ for (const DeployableFile &file : std::as_const(m_deployableFiles)) {
+ if (m_incremental != IncrementalDeployment::Enabled || hasLocalFileChanged(file)) {
+ storage->filesToUpload.append(file);
+ continue;
+ }
+ if (m_incremental == IncrementalDeployment::NotSupported)
+ continue;
+ filesToStat << file;
+ }
+ return filesToStat;
+ };
+ const auto preStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
+ const QDateTime &timestamp) {
+ if (!timestamp.isValid() || hasRemoteFileChanged(file, timestamp))
+ storage->filesToUpload.append(file);
+ };
+
+ const auto postFilesToStat = [this](UploadStorage *storage) {
+ return m_incremental == IncrementalDeployment::NotSupported
+ ? QList<DeployableFile>() : storage->filesToUpload;
+ };
+ const auto postStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file,
+ const QDateTime &timestamp) {
+ Q_UNUSED(storage)
+ if (timestamp.isValid())
+ saveDeploymentTimeStamp(file, timestamp);
+ };
+ const auto doneHandler = [this] {
+ addProgressMessage(Tr::tr("All files successfully deployed."));
+ };
+
+ const TreeStorage<UploadStorage> storage;
+ const Group root {
+ Storage(storage),
+ statTree(storage, preFilesToStat, preStatEndHandler),
+ uploadTask(storage),
+ Group {
+ chmodTree(storage),
+ statTree(storage, postFilesToStat, postStatEndHandler)
+ },
+ OnGroupDone(doneHandler)
+ };
+ return root;
+}
+
+// Factory
+
+GenericDirectUploadStepFactory::GenericDirectUploadStepFactory()
{
- return Tr::tr("Upload files via SFTP");
+ registerStep<GenericDirectUploadStep>(Constants::DirectUploadStepId);
+ setDisplayName(Tr::tr("Upload files via SFTP"));
}
-} //namespace RemoteLinux
+} // RemoteLinux::Internal
diff --git a/src/plugins/remotelinux/genericdirectuploadstep.h b/src/plugins/remotelinux/genericdirectuploadstep.h
index 0c2fb1b67d..3e825caf5c 100644
--- a/src/plugins/remotelinux/genericdirectuploadstep.h
+++ b/src/plugins/remotelinux/genericdirectuploadstep.h
@@ -3,23 +3,14 @@
#pragma once
-#include "remotelinux_export.h"
+#include <projectexplorer/buildstep.h>
-#include "abstractremotelinuxdeploystep.h"
+namespace RemoteLinux::Internal {
-namespace RemoteLinux {
-
-class REMOTELINUX_EXPORT GenericDirectUploadStep : public AbstractRemoteLinuxDeployStep
+class GenericDirectUploadStepFactory : public ProjectExplorer::BuildStepFactory
{
- Q_OBJECT
-
public:
- GenericDirectUploadStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id,
- bool offerIncrementalDeployment = true);
- ~GenericDirectUploadStep() override;
-
- static Utils::Id stepId();
- static QString displayName();
+ GenericDirectUploadStepFactory();
};
-} //namespace RemoteLinux
+} // RemoteLinux::Internal
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
index 50c553437f..ae85284ccb 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
@@ -3,9 +3,11 @@
#include "genericlinuxdeviceconfigurationwidget.h"
+#include "remotelinux_constants.h"
#include "remotelinuxtr.h"
#include "sshkeycreationdialog.h"
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/sshparameters.h>
@@ -16,6 +18,7 @@
#include <utils/utilsicons.h>
#include <QCheckBox>
+#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
@@ -32,14 +35,13 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
const IDevice::Ptr &device) :
IDeviceWidget(device)
{
- resize(556, 309);
-
m_defaultAuthButton = new QRadioButton(Tr::tr("Default"), this);
m_keyButton = new QRadioButton(Tr::tr("Specific &key"));
- m_hostLineEdit = new QLineEdit(this);
+ m_hostLineEdit = new FancyLineEdit(this);
m_hostLineEdit->setPlaceholderText(Tr::tr("IP or host name of the device"));
+ m_hostLineEdit->setHistoryCompleter("HostName");
m_sshPortSpinBox = new QSpinBox(this);
m_sshPortSpinBox->setMinimum(0);
@@ -48,8 +50,9 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
m_hostKeyCheckBox = new QCheckBox(Tr::tr("&Check host key"));
- m_portsLineEdit = new QLineEdit(this);
+ m_portsLineEdit = new FancyLineEdit(this);
m_portsLineEdit->setToolTip(Tr::tr("You can enter lists and ranges like this: '1024,1026-1028,1030'."));
+ m_portsLineEdit->setHistoryCompleter("PortRange");
m_portsWarningLabel = new QLabel(this);
@@ -59,7 +62,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
m_timeoutSpinBox->setValue(1000);
m_timeoutSpinBox->setSuffix(Tr::tr("s"));
- m_userLineEdit = new QLineEdit(this);
+ m_userLineEdit = new FancyLineEdit(this);
+ m_userLineEdit->setHistoryCompleter("UserName");
m_keyLabel = new QLabel(Tr::tr("Private key file:"));
@@ -74,11 +78,26 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
m_gdbServerLineEdit->setExpectedKind(PathChooser::ExistingCommand);
m_gdbServerLineEdit->setPlaceholderText(hint);
m_gdbServerLineEdit->setToolTip(hint);
+ m_gdbServerLineEdit->setHistoryCompleter("GdbServer");
m_qmlRuntimeLineEdit = new PathChooser(this);
m_qmlRuntimeLineEdit->setExpectedKind(PathChooser::ExistingCommand);
m_qmlRuntimeLineEdit->setPlaceholderText(hint);
m_qmlRuntimeLineEdit->setToolTip(hint);
+ m_qmlRuntimeLineEdit->setHistoryCompleter("QmlRuntime");
+
+ m_sourceProfileCheckBox =
+ new QCheckBox(Tr::tr("Source %1 and %2").arg("/etc/profile").arg("$HOME/.profile"));
+
+ m_linkDeviceComboBox = new QComboBox;
+ m_linkDeviceComboBox->addItem(Tr::tr("Direct"), QVariant());
+
+ auto dm = DeviceManager::instance();
+ const int dmCount = dm->deviceCount();
+ for (int i = 0; i < dmCount; ++i) {
+ IDevice::ConstPtr dev = dm->deviceAt(i);
+ m_linkDeviceComboBox->addItem(dev->displayName(), dev->id().toSetting());
+ }
auto sshPortLabel = new QLabel(Tr::tr("&SSH port:"));
sshPortLabel->setBuddy(m_sshPortSpinBox);
@@ -93,7 +112,9 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
Tr::tr("&Username:"), m_userLineEdit, st, br,
m_keyLabel, m_keyFileLineEdit, createKeyButton, br,
Tr::tr("GDB server executable:"), m_gdbServerLineEdit, br,
- Tr::tr("QML runtime executable:"), m_qmlRuntimeLineEdit, br
+ Tr::tr("QML runtime executable:"), m_qmlRuntimeLineEdit, br,
+ QString(), m_sourceProfileCheckBox, br,
+ Tr::tr("Access via:"), m_linkDeviceComboBox
}.attachTo(this);
connect(m_hostLineEdit, &QLineEdit::editingFinished,
@@ -124,6 +145,10 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
this, &GenericLinuxDeviceConfigurationWidget::qmlRuntimeEditingFinished);
connect(m_hostKeyCheckBox, &QCheckBox::toggled,
this, &GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged);
+ connect(m_sourceProfileCheckBox, &QCheckBox::toggled,
+ this, &GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged);
+ connect(m_linkDeviceComboBox, &QComboBox::currentIndexChanged,
+ this, &GenericLinuxDeviceConfigurationWidget::linkDeviceChanged);
initGui();
}
@@ -214,6 +239,17 @@ void GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged(bool doCheck)
device()->setSshParameters(sshParams);
}
+void GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged(bool doCheck)
+{
+ device()->setExtraData(Constants::SourceProfile, doCheck);
+}
+
+void GenericLinuxDeviceConfigurationWidget::linkDeviceChanged(int index)
+{
+ const QVariant deviceId = m_linkDeviceComboBox->itemData(index);
+ device()->setExtraData(Constants::LinkDevice, deviceId);
+}
+
void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi()
{
hostNameEditingFinished();
@@ -223,6 +259,10 @@ void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi()
keyFileEditingFinished();
handleFreePortsChanged();
gdbServerEditingFinished();
+ sshPortEditingFinished();
+ timeoutEditingFinished();
+ sourceProfileCheckingChanged(m_sourceProfileCheckBox->isChecked());
+ linkDeviceChanged(m_linkDeviceComboBox->currentIndex());
qmlRuntimeEditingFinished();
}
@@ -261,6 +301,17 @@ void GenericLinuxDeviceConfigurationWidget::initGui()
m_hostLineEdit->setEnabled(!device()->isAutoDetected());
m_sshPortSpinBox->setEnabled(!device()->isAutoDetected());
m_hostKeyCheckBox->setChecked(sshParams.hostKeyCheckingMode != SshHostKeyCheckingNone);
+ m_sourceProfileCheckBox->setChecked(device()->extraData(Constants::SourceProfile).toBool());
+ Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
+ auto dm = DeviceManager::instance();
+ int found = -1;
+ for (int i = 0, n = dm->deviceCount(); i < n; ++i) {
+ if (dm->deviceAt(i)->id() == linkDeviceId) {
+ found = i;
+ break;
+ }
+ }
+ m_linkDeviceComboBox->setCurrentIndex(found + 1); // There's the "Direct" entry first.
m_hostLineEdit->setText(sshParams.host());
m_sshPortSpinBox->setValue(sshParams.port());
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
index 4e00cedcb6..aba251b969 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h
@@ -7,13 +7,14 @@
QT_BEGIN_NAMESPACE
class QCheckBox;
+class QComboBox;
class QLabel;
-class QLineEdit;
class QRadioButton;
class QSpinBox;
QT_END_NAMESPACE
namespace Utils {
+class FancyLineEdit;
class FilePath;
class PathChooser;
} // Utils
@@ -42,6 +43,8 @@ private:
void setPrivateKey(const Utils::FilePath &path);
void createNewKey();
void hostKeyCheckingChanged(bool doCheck);
+ void sourceProfileCheckingChanged(bool doCheck);
+ void linkDeviceChanged(int index);
void updateDeviceFromUi() override;
void updatePortsWarningLabel();
@@ -50,17 +53,19 @@ private:
QRadioButton *m_defaultAuthButton;
QLabel *m_keyLabel;
QRadioButton *m_keyButton;
- QLineEdit *m_hostLineEdit;
+ Utils::FancyLineEdit *m_hostLineEdit;
QSpinBox *m_sshPortSpinBox;
QCheckBox *m_hostKeyCheckBox;
- QLineEdit *m_portsLineEdit;
+ Utils::FancyLineEdit *m_portsLineEdit;
QLabel *m_portsWarningLabel;
- QLineEdit *m_userLineEdit;
+ Utils::FancyLineEdit *m_userLineEdit;
QSpinBox *m_timeoutSpinBox;
Utils::PathChooser *m_keyFileLineEdit;
QLabel *m_machineTypeValueLabel;
Utils::PathChooser *m_gdbServerLineEdit;
Utils::PathChooser *m_qmlRuntimeLineEdit;
+ QCheckBox *m_sourceProfileCheckBox;
+ QComboBox *m_linkDeviceComboBox;
};
} // RemoteLinux::Internal
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
index 4de31d898d..7bd047a590 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp
@@ -11,7 +11,6 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/sshparameters.h>
-#include <utils/portlist.h>
#include <utils/fileutils.h>
using namespace ProjectExplorer;
@@ -45,13 +44,6 @@ GenericLinuxDeviceConfigurationWizard::GenericLinuxDeviceConfigurationWizard(QWi
setPage(Internal::FinalPageId, &d->finalPage);
d->finalPage.setCommitPage(true);
d->device = LinuxDevice::create();
- d->device->setupId(IDevice::ManuallyAdded, Utils::Id());
- d->device->setType(Constants::GenericLinuxOsType);
- d->device->setMachineType(IDevice::Hardware);
- d->device->setFreePorts(Utils::PortList::fromString(QLatin1String("10000-10100")));
- SshParameters sshParams;
- sshParams.timeout = 10;
- d->device->setSshParameters(sshParams);
d->setupPage.setDevice(d->device);
d->keyDeploymentPage.setDevice(d->device);
}
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp
index 89e4c2e483..0a34683bde 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp
@@ -9,14 +9,15 @@
#include <projectexplorer/devicesupport/sshparameters.h>
+#include <utils/filepath.h>
#include <utils/fileutils.h>
+#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/utilsicons.h>
#include <QHBoxLayout>
#include <QLabel>
-#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
@@ -29,10 +30,10 @@ namespace Internal {
class GenericLinuxDeviceConfigurationWizardSetupPagePrivate
{
public:
- QLineEdit *nameLineEdit;
- QLineEdit *hostNameLineEdit;
+ FancyLineEdit *nameLineEdit;
+ FancyLineEdit *hostNameLineEdit;
QSpinBox *sshPortSpinBox;
- QLineEdit *userNameLineEdit;
+ FancyLineEdit *userNameLineEdit;
LinuxDevice::Ptr device;
};
@@ -52,10 +53,16 @@ GenericLinuxDeviceConfigurationWizardSetupPage::GenericLinuxDeviceConfigurationW
setTitle(Tr::tr("Connection"));
setWindowTitle(Tr::tr("WizardPage"));
- d->nameLineEdit = new QLineEdit(this);
- d->hostNameLineEdit = new QLineEdit(this);
+ d->nameLineEdit = new FancyLineEdit(this);
+ d->nameLineEdit->setHistoryCompleter("DeviceName");
+
+ d->hostNameLineEdit = new FancyLineEdit(this);
+ d->hostNameLineEdit->setHistoryCompleter("HostName");
+
d->sshPortSpinBox = new QSpinBox(this);
- d->userNameLineEdit = new QLineEdit(this);
+
+ d->userNameLineEdit = new FancyLineEdit(this);
+ d->userNameLineEdit->setHistoryCompleter("UserName");
using namespace Layouting;
Form {
@@ -97,7 +104,9 @@ bool GenericLinuxDeviceConfigurationWizardSetupPage::validatePage()
{
d->device->setDisplayName(configurationName());
SshParameters sshParams = d->device->sshParameters();
- sshParams.url = url();
+ sshParams.setHost(d->hostNameLineEdit->text().trimmed());
+ sshParams.setUserName(d->userNameLineEdit->text().trimmed());
+ sshParams.setPort(d->sshPortSpinBox->value());
d->device->setSshParameters(sshParams);
return true;
}
@@ -107,15 +116,6 @@ QString GenericLinuxDeviceConfigurationWizardSetupPage::configurationName() cons
return d->nameLineEdit->text().trimmed();
}
-QUrl GenericLinuxDeviceConfigurationWizardSetupPage::url() const
-{
- QUrl url;
- url.setHost(d->hostNameLineEdit->text().trimmed());
- url.setUserName(d->userNameLineEdit->text().trimmed());
- url.setPort(d->sshPortSpinBox->value());
- return url;
-}
-
void GenericLinuxDeviceConfigurationWizardSetupPage::setDevice(const LinuxDevice::Ptr &device)
{
d->device = device;
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h
index 6e5e00119a..2e2d529891 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h
@@ -32,7 +32,6 @@ private:
bool validatePage() override;
QString configurationName() const;
- QUrl url() const;
Internal::GenericLinuxDeviceConfigurationWizardSetupPagePrivate * const d;
};
@@ -64,7 +63,7 @@ class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardFinalPage final :
{
Q_OBJECT
public:
- GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent);
+ GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent = nullptr);
~GenericLinuxDeviceConfigurationWizardFinalPage() override;
protected:
diff --git a/src/plugins/remotelinux/killappstep.cpp b/src/plugins/remotelinux/killappstep.cpp
index 03473d95a2..df8d701dcf 100644
--- a/src/plugins/remotelinux/killappstep.cpp
+++ b/src/plugins/remotelinux/killappstep.cpp
@@ -15,15 +15,26 @@
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux::Internal {
-class KillAppService : public AbstractRemoteLinuxDeployService
+class KillAppStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setRemoteExecutable(const FilePath &filePath) { m_remoteExecutable = filePath; }
+ KillAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+
+ setInternalInitializer([this] {
+ Target * const theTarget = target();
+ QTC_ASSERT(theTarget, return CheckResult::failure());
+ RunConfiguration * const rc = theTarget->activeRunConfiguration();
+ m_remoteExecutable = rc ? rc->runnable().command.executable() : FilePath();
+ return CheckResult::success();
+ });
+ }
private:
bool isDeploymentNecessary() const final { return !m_remoteExecutable.isEmpty(); }
@@ -32,44 +43,23 @@ private:
FilePath m_remoteExecutable;
};
-Group KillAppService::deployRecipe()
+Group KillAppStep::deployRecipe()
{
const auto setupHandler = [this](DeviceProcessKiller &killer) {
killer.setProcessPath(m_remoteExecutable);
- emit progressMessage(Tr::tr("Trying to kill \"%1\" on remote device...")
- .arg(m_remoteExecutable.path()));
+ addProgressMessage(Tr::tr("Trying to kill \"%1\" on remote device...")
+ .arg(m_remoteExecutable.path()));
};
const auto doneHandler = [this](const DeviceProcessKiller &) {
- emit progressMessage(Tr::tr("Remote application killed."));
+ addProgressMessage(Tr::tr("Remote application killed."));
};
const auto errorHandler = [this](const DeviceProcessKiller &) {
- emit progressMessage(Tr::tr("Failed to kill remote application. "
+ addProgressMessage(Tr::tr("Failed to kill remote application. "
"Assuming it was not running."));
};
return Group { Killer(setupHandler, doneHandler, errorHandler) };
}
-class KillAppStep : public AbstractRemoteLinuxDeployStep
-{
-public:
- KillAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new Internal::KillAppService;
- setDeployService(service);
-
- setWidgetExpandedByDefault(false);
-
- setInternalInitializer([this, service] {
- Target * const theTarget = target();
- QTC_ASSERT(theTarget, return CheckResult::failure());
- RunConfiguration * const rc = theTarget->activeRunConfiguration();
- const FilePath remoteExe = rc ? rc->runnable().command.executable() : FilePath();
- service->setRemoteExecutable(remoteExe);
- return CheckResult::success();
- });
- }
-};
-
KillAppStepFactory::KillAppStepFactory()
{
registerStep<KillAppStep>(Constants::KillAppStepId);
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index 5d0d2d6b92..59af192eaa 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -11,14 +11,14 @@
#include "remotelinux_constants.h"
#include "remotelinuxsignaloperation.h"
#include "remotelinuxtr.h"
-#include "sshprocessinterface.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
+#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/devicesupport/filetransfer.h>
#include <projectexplorer/devicesupport/filetransferinterface.h>
-#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
+#include <projectexplorer/devicesupport/processlist.h>
#include <projectexplorer/devicesupport/sshparameters.h>
#include <projectexplorer/devicesupport/sshsettings.h>
@@ -28,9 +28,10 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/port.h>
+#include <utils/portlist.h>
+#include <utils/process.h>
#include <utils/processinfo.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
@@ -51,9 +52,6 @@ namespace RemoteLinux {
const QByteArray s_pidMarker = "__qtc";
-const char Delimiter0[] = "x--";
-const char Delimiter1[] = "---";
-
static Q_LOGGING_CATEGORY(linuxDeviceLog, "qtc.remotelinux.device", QtWarningMsg);
#define DEBUG(x) qCDebug(linuxDeviceLog) << x << '\n'
@@ -94,7 +92,7 @@ private:
QStringList connectionArgs(const FilePath &binary) const;
const SshParameters m_sshParameters;
- std::unique_ptr<QtcProcess> m_masterProcess;
+ std::unique_ptr<Process> m_masterProcess;
std::unique_ptr<QTemporaryDir> m_masterSocketDir;
QTimer m_timer;
int m_ref = 0;
@@ -160,11 +158,11 @@ void SshSharedConnection::connectToHost()
return;
}
- m_masterProcess.reset(new QtcProcess);
+ m_masterProcess.reset(new Process);
SshParameters::setupSshEnvironment(m_masterProcess.get());
m_timer.setSingleShot(true);
connect(&m_timer, &QTimer::timeout, this, &SshSharedConnection::autoDestructRequested);
- connect(m_masterProcess.get(), &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(m_masterProcess.get(), &Process::readyReadStandardOutput, this, [this] {
const QByteArray reply = m_masterProcess->readAllRawStandardOutput();
if (reply == "\n")
emitConnected();
@@ -172,7 +170,7 @@ void SshSharedConnection::connectToHost()
});
// TODO: in case of refused connection we are getting the following on stdErr:
// ssh: connect to host 127.0.0.1 port 22: Connection refused\r\n
- connect(m_masterProcess.get(), &QtcProcess::done, this, [this] {
+ connect(m_masterProcess.get(), &Process::done, this, [this] {
const ProcessResult result = m_masterProcess->result();
const ProcessResultData resultData = m_masterProcess->resultData();
if (result == ProcessResult::StartFailed) {
@@ -276,77 +274,6 @@ private:
IDevice::ConstPtr m_device;
};
-static QString visualizeNull(QString s)
-{
- return s.replace(QLatin1Char('\0'), QLatin1String("<null>"));
-}
-
-class LinuxDeviceProcessList : public SshDeviceProcessList
-{
-public:
- LinuxDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent)
- : SshDeviceProcessList(device, parent)
- {
- }
-
-private:
- QString listProcessesCommandLine() const override
- {
- return QString::fromLatin1(
- "for dir in `ls -d /proc/[0123456789]*`; do "
- "test -d $dir || continue;" // Decrease the likelihood of a race condition.
- "echo $dir;"
- "cat $dir/cmdline;echo;" // cmdline does not end in newline
- "cat $dir/stat;"
- "readlink $dir/exe;"
- "printf '%1''%2';"
- "done").arg(QLatin1String(Delimiter0)).arg(QLatin1String(Delimiter1));
- }
-
- QList<ProcessInfo> buildProcessList(const QString &listProcessesReply) const override
- {
- QList<ProcessInfo> processes;
- const QStringList lines = listProcessesReply.split(QString::fromLatin1(Delimiter0)
- + QString::fromLatin1(Delimiter1), Qt::SkipEmptyParts);
- for (const QString &line : lines) {
- const QStringList elements = line.split(QLatin1Char('\n'));
- if (elements.count() < 4) {
- qDebug("%s: Expected four list elements, got %d. Line was '%s'.", Q_FUNC_INFO,
- int(elements.count()), qPrintable(visualizeNull(line)));
- continue;
- }
- bool ok;
- const int pid = elements.first().mid(6).toInt(&ok);
- if (!ok) {
- qDebug("%s: Expected number in %s. Line was '%s'.", Q_FUNC_INFO,
- qPrintable(elements.first()), qPrintable(visualizeNull(line)));
- continue;
- }
- QString command = elements.at(1);
- command.replace(QLatin1Char('\0'), QLatin1Char(' '));
- if (command.isEmpty()) {
- const QString &statString = elements.at(2);
- const int openParenPos = statString.indexOf(QLatin1Char('('));
- const int closedParenPos = statString.indexOf(QLatin1Char(')'), openParenPos);
- if (openParenPos == -1 || closedParenPos == -1)
- continue;
- command = QLatin1Char('[')
- + statString.mid(openParenPos + 1, closedParenPos - openParenPos - 1)
- + QLatin1Char(']');
- }
-
- ProcessInfo process;
- process.processId = pid;
- process.commandLine = command;
- process.executable = elements.at(3);
- processes.append(process);
- }
-
- return Utils::sorted(std::move(processes));
- }
-};
-
-
// LinuxDevicePrivate
class ShellThreadHandler;
@@ -382,11 +309,13 @@ public:
Environment getEnvironment();
void invalidateEnvironmentCache();
+ void checkOsType();
+ void queryOsType(std::function<RunResult(const CommandLine &)> run);
+
LinuxDevice *q = nullptr;
QThread m_shellThread;
ShellThreadHandler *m_handler = nullptr;
mutable QMutex m_shellMutex;
- QList<QtcProcess *> m_terminals;
LinuxDeviceFileAccess m_fileAccess{this};
QReadWriteLock m_environmentCacheLock;
@@ -410,11 +339,8 @@ Environment LinuxDevicePrivate::getEnvironment()
if (m_environmentCache.has_value())
return m_environmentCache.value();
- QtcProcess getEnvProc;
- getEnvProc.setCommand({FilePath("env").onDevice(q->rootPath()), {}});
- Environment inEnv;
- inEnv.setCombineWithDeviceEnvironment(false);
- getEnvProc.setEnvironment(inEnv);
+ Process getEnvProc;
+ getEnvProc.setCommand({q->filePath("env"), {}});
getEnvProc.runBlocking();
const QString remoteOutput = getEnvProc.cleanedStdOut();
@@ -437,10 +363,8 @@ Environment LinuxDeviceFileAccess::deviceEnvironment() const
class SshProcessInterfacePrivate : public QObject
{
- Q_OBJECT
-
public:
- SshProcessInterfacePrivate(SshProcessInterface *sshInterface, LinuxDevicePrivate *devicePrivate);
+ SshProcessInterfacePrivate(SshProcessInterface *sshInterface, const IDevice::ConstPtr &device);
void start();
@@ -463,47 +387,32 @@ public:
// as this object is alive.
IDevice::ConstPtr m_device;
std::unique_ptr<SshConnectionHandle> m_connectionHandle;
- QtcProcess m_process;
- LinuxDevicePrivate *m_devicePrivate = nullptr;
+ Process m_process;
QString m_socketFilePath;
SshParameters m_sshParameters;
+
bool m_connecting = false;
bool m_killed = false;
ProcessResultData m_result;
+
+ QByteArray m_output;
+ QByteArray m_error;
+ bool m_pidParsed = false;
+ bool m_useConnectionSharing = false;
};
-SshProcessInterface::SshProcessInterface(const LinuxDevice *linuxDevice)
- : d(new SshProcessInterfacePrivate(this, linuxDevice->d))
-{
-}
+SshProcessInterface::SshProcessInterface(const IDevice::ConstPtr &device)
+ : d(new SshProcessInterfacePrivate(this, device))
+{}
SshProcessInterface::~SshProcessInterface()
{
+ killIfRunning();
delete d;
}
-void SshProcessInterface::handleStarted(qint64 processId)
-{
- emitStarted(processId);
-}
-
-void SshProcessInterface::handleDone(const ProcessResultData &resultData)
-{
- emit done(resultData);
-}
-
-void SshProcessInterface::handleReadyReadStandardOutput(const QByteArray &outputData)
-{
- emit readyRead(outputData, {});
-}
-
-void SshProcessInterface::handleReadyReadStandardError(const QByteArray &errorData)
-{
- emit readyRead({}, errorData);
-}
-
void SshProcessInterface::emitStarted(qint64 processId)
{
d->m_processId = processId;
@@ -525,7 +434,7 @@ qint64 SshProcessInterface::processId() const
bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data)
{
- QtcProcess process;
+ Process process;
CommandLine cmd = {d->m_device->filePath("/bin/sh"), {"-c"}};
QString tmp;
ProcessArgs::addArg(&tmp, command.executable().path());
@@ -556,7 +465,7 @@ void SshProcessInterface::sendControlSignal(ControlSignal controlSignal)
d->m_process.closeWriteChannel();
return;
}
- if (d->m_process.usesTerminal()) {
+ if (d->m_process.usesTerminal() || d->m_process.ptyData().has_value()) {
switch (controlSignal) {
case ControlSignal::Terminate: d->m_process.terminate(); break;
case ControlSignal::Kill: d->m_process.kill(); break;
@@ -569,17 +478,7 @@ void SshProcessInterface::sendControlSignal(ControlSignal controlSignal)
handleSendControlSignal(controlSignal);
}
-LinuxProcessInterface::LinuxProcessInterface(const LinuxDevice *linuxDevice)
- : SshProcessInterface(linuxDevice)
-{
-}
-
-LinuxProcessInterface::~LinuxProcessInterface()
-{
- killIfRunning();
-}
-
-void LinuxProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
+void SshProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
{
QTC_ASSERT(controlSignal != ControlSignal::KickOff, return);
QTC_ASSERT(controlSignal != ControlSignal::CloseWriteChannel, return);
@@ -592,75 +491,58 @@ void LinuxProcessInterface::handleSendControlSignal(ControlSignal controlSignal)
runInShell(command);
}
-QString LinuxProcessInterface::fullCommandLine(const CommandLine &commandLine) const
+void SshProcessInterfacePrivate::handleStarted()
{
- CommandLine cmd;
-
- if (!commandLine.isEmpty()) {
- const QStringList rcFilesToSource = {"/etc/profile", "$HOME/.profile"};
- for (const QString &filePath : rcFilesToSource) {
- cmd.addArgs({"test", "-f", filePath});
- cmd.addArgs("&&", CommandLine::Raw);
- cmd.addArgs({".", filePath});
- cmd.addArgs(";", CommandLine::Raw);
- }
- }
-
- if (!m_setup.m_workingDirectory.isEmpty()) {
- cmd.addArgs({"cd", m_setup.m_workingDirectory.path()});
- cmd.addArgs("&&", CommandLine::Raw);
- }
-
- if (m_setup.m_terminalMode == TerminalMode::Off)
- cmd.addArgs(QString("echo ") + s_pidMarker + "$$" + s_pidMarker + " && ", CommandLine::Raw);
-
- const Environment &env = m_setup.m_environment;
- for (auto it = env.constBegin(); it != env.constEnd(); ++it)
- cmd.addArgs(env.key(it) + "='" + env.expandedValueForKey(env.key(it)) + '\'', CommandLine::Raw);
-
- if (m_setup.m_terminalMode == TerminalMode::Off)
- cmd.addArg("exec");
-
- if (!commandLine.isEmpty())
- cmd.addCommandLineAsArgs(commandLine, CommandLine::Raw);
- return cmd.arguments();
-}
+ const qint64 processId = m_process.usesTerminal() ? m_process.processId() : 0;
-void LinuxProcessInterface::handleStarted(qint64 processId)
-{
// Don't emit started() when terminal is off,
// it's being done later inside handleReadyReadStandardOutput().
- if (m_setup.m_terminalMode == TerminalMode::Off)
+ if (q->m_setup.m_terminalMode == TerminalMode::Off && !q->m_setup.m_ptyData)
return;
m_pidParsed = true;
- emitStarted(processId);
+ q->emitStarted(processId);
}
-void LinuxProcessInterface::handleDone(const ProcessResultData &resultData)
+void SshProcessInterfacePrivate::handleDone()
{
- ProcessResultData finalData = resultData;
+ if (m_connectionHandle) // TODO: should it disconnect from signals first?
+ m_connectionHandle.release()->deleteLater();
+
+ ProcessResultData finalData = m_process.resultData();
if (!m_pidParsed) {
finalData.m_error = QProcess::FailedToStart;
finalData.m_errorString = Utils::joinStrings({finalData.m_errorString,
QString::fromLocal8Bit(m_error)}, '\n');
}
- emit done(finalData);
+ emit q->done(finalData);
}
-void LinuxProcessInterface::handleReadyReadStandardOutput(const QByteArray &outputData)
+void SshProcessInterfacePrivate::handleReadyReadStandardOutput()
{
+ // By default this forwards readyRead immediately, but only buffers the
+ // output in case the start signal is not emitted yet.
+ // In case the pid can be parsed now, the delayed started() is
+ // emitted, and any previously buffered output emitted now.
+ const QByteArray outputData = m_process.readAllRawStandardOutput();
+
if (m_pidParsed) {
- emit readyRead(outputData, {});
+ emit q->readyRead(outputData, {});
return;
}
m_output.append(outputData);
static const QByteArray endMarker = s_pidMarker + '\n';
- const int endMarkerOffset = m_output.indexOf(endMarker);
- if (endMarkerOffset == -1)
- return;
+ int endMarkerLength = endMarker.length();
+ int endMarkerOffset = m_output.indexOf(endMarker);
+ if (endMarkerOffset == -1) {
+ static const QByteArray endMarker = s_pidMarker + "\r\n";
+ endMarkerOffset = m_output.indexOf(endMarker);
+ endMarkerLength = endMarker.length();
+ if (endMarkerOffset == -1)
+ return;
+ }
const int startMarkerOffset = m_output.indexOf(s_pidMarker);
if (startMarkerOffset == endMarkerOffset) // Only theoretically possible.
return;
@@ -670,59 +552,109 @@ void LinuxProcessInterface::handleReadyReadStandardOutput(const QByteArray &outp
const qint64 processId = pidString.toLongLong();
// We don't want to show output from e.g. /etc/profile.
- m_output = m_output.mid(endMarkerOffset + endMarker.length());
+ m_output = m_output.mid(endMarkerOffset + endMarkerLength);
- emitStarted(processId);
+ q->emitStarted(processId);
if (!m_output.isEmpty() || !m_error.isEmpty())
- emit readyRead(m_output, m_error);
+ emit q->readyRead(m_output, m_error);
m_output.clear();
m_error.clear();
}
-void LinuxProcessInterface::handleReadyReadStandardError(const QByteArray &errorData)
+void SshProcessInterfacePrivate::handleReadyReadStandardError()
{
+ // By default forwards readyRead immediately, but buffers it in
+ // case the start signal is not emitted yet.
+ const QByteArray errorData = m_process.readAllRawStandardError();
+
if (m_pidParsed) {
- emit readyRead({}, errorData);
+ emit q->readyRead({}, errorData);
return;
}
m_error.append(errorData);
}
SshProcessInterfacePrivate::SshProcessInterfacePrivate(SshProcessInterface *sshInterface,
- LinuxDevicePrivate *devicePrivate)
+ const IDevice::ConstPtr &device)
: QObject(sshInterface)
, q(sshInterface)
- , m_device(devicePrivate->q->sharedFromThis())
+ , m_device(device)
, m_process(this)
- , m_devicePrivate(devicePrivate)
{
- connect(&m_process, &QtcProcess::started, this, &SshProcessInterfacePrivate::handleStarted);
- connect(&m_process, &QtcProcess::done, this, &SshProcessInterfacePrivate::handleDone);
- connect(&m_process, &QtcProcess::readyReadStandardOutput,
+ connect(&m_process, &Process::started, this, &SshProcessInterfacePrivate::handleStarted);
+ connect(&m_process, &Process::done, this, &SshProcessInterfacePrivate::handleDone);
+ connect(&m_process, &Process::readyReadStandardOutput,
this, &SshProcessInterfacePrivate::handleReadyReadStandardOutput);
- connect(&m_process, &QtcProcess::readyReadStandardError,
+ connect(&m_process, &Process::readyReadStandardError,
this, &SshProcessInterfacePrivate::handleReadyReadStandardError);
}
void SshProcessInterfacePrivate::start()
{
clearForStart();
+ m_sshParameters = m_device->sshParameters();
+
+ const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice));
+ if (const IDevice::ConstPtr linkDevice = DeviceManager::instance()->find(linkDeviceId)) {
+ CommandLine cmd{linkDevice->filePath("ssh")};
+ if (!m_sshParameters.userName().isEmpty()) {
+ cmd.addArg("-l");
+ cmd.addArg(m_sshParameters.userName());
+ }
+ cmd.addArg(m_sshParameters.host());
+
+ const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off
+ || q->m_setup.m_ptyData;
+ if (useTerminal)
+ cmd.addArg("-tt");
+
+ const CommandLine full = q->m_setup.m_commandLine;
+ if (!full.isEmpty()) { // Empty is ok in case of opening a terminal.
+ CommandLine inner;
+ const QString wd = q->m_setup.m_workingDirectory.path();
+ if (!wd.isEmpty())
+ inner.addCommandLineWithAnd({"cd", {wd}});
+ if (!useTerminal) {
+ const QString pidArg = QString("%1\\$\\$%1").arg(QString::fromLatin1(s_pidMarker));
+ inner.addCommandLineWithAnd({"echo", pidArg, CommandLine::Raw});
+ }
+ inner.addCommandLineWithAnd(full);
+ cmd.addCommandLineAsSingleArg(inner);
+ }
+
+ m_process.setProcessImpl(q->m_setup.m_processImpl);
+ m_process.setProcessMode(q->m_setup.m_processMode);
+ m_process.setTerminalMode(q->m_setup.m_terminalMode);
+ m_process.setPtyData(q->m_setup.m_ptyData);
+ m_process.setReaperTimeout(q->m_setup.m_reaperTimeout);
+ m_process.setWriteData(q->m_setup.m_writeData);
+ m_process.setCreateConsoleOnWindows(q->m_setup.m_createConsoleOnWindows);
+ m_process.setExtraData(q->m_setup.m_extraData);
+
+ m_process.setCommand(cmd);
+ m_process.start();
+ return;
+ }
+
+ m_useConnectionSharing = SshSettings::connectionSharingEnabled();
- m_sshParameters = m_devicePrivate->q->sshParameters();
// TODO: Do we really need it for master process?
m_sshParameters.x11DisplayName
= q->m_setup.m_extraData.value("Ssh.X11ForwardToDisplay").toString();
- if (SshSettings::connectionSharingEnabled()) {
+ if (m_useConnectionSharing) {
m_connecting = true;
- m_connectionHandle.reset(new SshConnectionHandle(m_devicePrivate->q->sharedFromThis()));
+ m_connectionHandle.reset(new SshConnectionHandle(m_device));
m_connectionHandle->setParent(this);
connect(m_connectionHandle.get(), &SshConnectionHandle::connected,
this, &SshProcessInterfacePrivate::handleConnected);
connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected,
this, &SshProcessInterfacePrivate::handleDisconnected);
- m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
+ auto linuxDevice = m_device.dynamicCast<const LinuxDevice>();
+ QTC_ASSERT(linuxDevice, handleDone(); return);
+ linuxDevice->connectionAccess()
+ ->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
} else {
doStart();
}
@@ -749,34 +681,6 @@ void SshProcessInterfacePrivate::handleDisconnected(const ProcessResultData &res
emit q->done(resultData); // TODO: don't emit done() on process finished afterwards
}
-void SshProcessInterfacePrivate::handleStarted()
-{
- const qint64 processId = m_process.usesTerminal() ? m_process.processId() : 0;
- // By default emits started signal, Linux impl doesn't emit it when terminal is off.
- q->handleStarted(processId);
-}
-
-void SshProcessInterfacePrivate::handleDone()
-{
- if (m_connectionHandle) // TODO: should it disconnect from signals first?
- m_connectionHandle.release()->deleteLater();
- q->handleDone(m_process.resultData());
-}
-
-void SshProcessInterfacePrivate::handleReadyReadStandardOutput()
-{
- // By default emits signal. LinuxProcessImpl does custom parsing for processId
- // and emits delayed start() - only when terminal is off.
- q->handleReadyReadStandardOutput(m_process.readAllRawStandardOutput());
-}
-
-void SshProcessInterfacePrivate::handleReadyReadStandardError()
-{
- // By default emits signal. LinuxProcessImpl buffers the error channel until
- // it emits delayed start() - only when terminal is off.
- q->handleReadyReadStandardError(m_process.readAllRawStandardError());
-}
-
void SshProcessInterfacePrivate::clearForStart()
{
m_result = {};
@@ -787,8 +691,11 @@ void SshProcessInterfacePrivate::doStart()
m_process.setProcessImpl(q->m_setup.m_processImpl);
m_process.setProcessMode(q->m_setup.m_processMode);
m_process.setTerminalMode(q->m_setup.m_terminalMode);
+ m_process.setPtyData(q->m_setup.m_ptyData);
m_process.setReaperTimeout(q->m_setup.m_reaperTimeout);
m_process.setWriteData(q->m_setup.m_writeData);
+ m_process.setCreateConsoleOnWindows(q->m_setup.m_createConsoleOnWindows);
+
// TODO: what about other fields from m_setup?
SshParameters::setupSshEnvironment(&m_process);
if (!m_sshParameters.x11DisplayName.isEmpty()) {
@@ -798,32 +705,72 @@ void SshProcessInterfacePrivate::doStart()
env.set("DISPLAY", m_sshParameters.x11DisplayName);
m_process.setControlEnvironment(env);
}
+ m_process.setExtraData(q->m_setup.m_extraData);
m_process.setCommand(fullLocalCommandLine());
m_process.start();
}
CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const
{
- CommandLine cmd{SshSettings::sshFilePath()};
+ const FilePath sshBinary = SshSettings::sshFilePath();
+ const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off || q->m_setup.m_ptyData;
+ const bool usePidMarker = !useTerminal;
+ const bool sourceProfile = m_device->extraData(Constants::SourceProfile).toBool();
+ const bool useX = !m_sshParameters.x11DisplayName.isEmpty();
+
+ CommandLine cmd{sshBinary};
- if (!m_sshParameters.x11DisplayName.isEmpty())
+ if (useX)
cmd.addArg("-X");
- if (q->m_setup.m_terminalMode != TerminalMode::Off)
+ if (useTerminal)
cmd.addArg("-tt");
cmd.addArg("-q");
- QStringList options = m_sshParameters.connectionOptions(SshSettings::sshFilePath());
+ cmd.addArgs(m_sshParameters.connectionOptions(sshBinary));
if (!m_socketFilePath.isEmpty())
- options << "-o" << ("ControlPath=" + m_socketFilePath);
- options << m_sshParameters.host();
- cmd.addArgs(options);
+ cmd.addArgs({"-o", "ControlPath=" + m_socketFilePath});
+
+ cmd.addArg(m_sshParameters.host());
- CommandLine remoteWithLocalPath = q->m_setup.m_commandLine;
- FilePath executable = FilePath::fromParts({}, {}, remoteWithLocalPath.executable().path());
- remoteWithLocalPath.setExecutable(executable);
+ CommandLine commandLine = q->m_setup.m_commandLine;
+ FilePath executable = FilePath::fromParts({}, {}, commandLine.executable().path());
+ commandLine.setExecutable(executable);
+
+ CommandLine inner;
+
+ if (!commandLine.isEmpty() && sourceProfile) {
+ const QStringList rcFilesToSource = {"/etc/profile", "$HOME/.profile"};
+ for (const QString &filePath : rcFilesToSource) {
+ inner.addArgs({"test", "-f", filePath});
+ inner.addArgs("&&", CommandLine::Raw);
+ inner.addArgs({".", filePath});
+ inner.addArgs(";", CommandLine::Raw);
+ }
+ }
+
+ const FilePath &workingDirectory = q->m_setup.m_workingDirectory;
+ if (!workingDirectory.isEmpty()) {
+ inner.addArgs({"cd", workingDirectory.path()});
+ inner.addArgs("&&", CommandLine::Raw);
+ }
+
+ if (usePidMarker)
+ inner.addArgs(QString("echo ") + s_pidMarker + "$$" + s_pidMarker + " && ", CommandLine::Raw);
+
+ const Environment &env = q->m_setup.m_environment;
+ env.forEachEntry([&](const QString &key, const QString &value, bool) {
+ inner.addArgs(key + "='" + env.expandVariables(value) + '\'', CommandLine::Raw);
+ });
+
+ if (!useTerminal && !commandLine.isEmpty())
+ inner.addArg("exec");
+
+ if (!commandLine.isEmpty())
+ inner.addCommandLineAsArgs(commandLine, CommandLine::Raw);
+
+ cmd.addArg(inner.arguments());
- cmd.addArg(q->fullCommandLine(remoteWithLocalPath));
return cmd;
}
@@ -848,7 +795,7 @@ class ShellThreadHandler : public QObject
}
private:
- void setupShellProcess(QtcProcess *shellProcess) override
+ void setupShellProcess(Process *shellProcess) override
{
SshParameters::setupSshEnvironment(shellProcess);
shellProcess->setCommand(m_cmdLine);
@@ -857,7 +804,7 @@ class ShellThreadHandler : public QObject
CommandLine createFallbackCommand(const CommandLine &cmdLine) override
{
CommandLine result = cmdLine;
- result.setExecutable(cmdLine.executable().onDevice(m_devicePath));
+ result.setExecutable(m_devicePath.withNewMappedPath(cmdLine.executable())); // Needed?
return result;
}
@@ -995,9 +942,16 @@ LinuxDevice::LinuxDevice()
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Remote Linux"));
- setDefaultDisplayName(Tr::tr("Remote Linux Device"));
setOsType(OsTypeLinux);
+ setupId(IDevice::ManuallyAdded, Utils::Id());
+ setType(Constants::GenericLinuxOsType);
+ setMachineType(IDevice::Hardware);
+ setFreePorts(PortList::fromString(QLatin1String("10000-10100")));
+ SshParameters sshParams;
+ sshParams.timeout = 10;
+ setSshParameters(sshParams);
+
addDeviceAction({Tr::tr("Deploy Public Key..."), [](const IDevice::Ptr &device, QWidget *parent) {
if (auto d = PublicKeyDeploymentDialog::createDialog(device, parent)) {
d->exec();
@@ -1006,40 +960,19 @@ LinuxDevice::LinuxDevice()
}});
setOpenTerminal([this](const Environment &env, const FilePath &workingDir) {
- QtcProcess * const proc = new QtcProcess;
- d->m_terminals.append(proc);
- QObject::connect(proc, &QtcProcess::done, proc, [this, proc] {
- if (proc->error() != QProcess::UnknownError) {
- const QString errorString = proc->errorString();
- QString message;
- if (proc->error() == QProcess::FailedToStart)
- message = Tr::tr("Error starting remote shell.");
- else if (errorString.isEmpty())
- message = Tr::tr("Error running remote shell.");
- else
- message = Tr::tr("Error running remote shell: %1").arg(errorString);
- Core::MessageManager::writeDisrupting(message);
- }
- proc->deleteLater();
- d->m_terminals.removeOne(proc);
- });
+ Process proc;
- // We recreate the same way that QtcProcess uses to create the actual environment.
- const Environment finalEnv = (!env.hasChanges() && env.combineWithDeviceEnvironment())
- ? d->getEnvironment()
- : env;
// If we will not set any environment variables, we can leave out the shell executable
// as the "ssh ..." call will automatically launch the default shell if there are
// no arguments. But if we will set environment variables, we need to explicitly
// specify the shell executable.
- const QString shell = finalEnv.hasChanges() ? finalEnv.value_or("SHELL", "/bin/sh")
- : QString();
-
- proc->setCommand({filePath(shell), {}});
- proc->setTerminalMode(TerminalMode::On);
- proc->setEnvironment(env);
- proc->setWorkingDirectory(workingDir);
- proc->start();
+ const QString shell = env.hasChanges() ? env.value_or("SHELL", "/bin/sh") : QString();
+
+ proc.setCommand({filePath(shell), {}});
+ proc.setTerminalMode(TerminalMode::Detached);
+ proc.setEnvironment(env);
+ proc.setWorkingDirectory(workingDir);
+ proc.start();
});
addDeviceAction({Tr::tr("Open Remote Shell"), [](const IDevice::Ptr &device, QWidget *) {
@@ -1047,6 +980,12 @@ LinuxDevice::LinuxDevice()
}});
}
+void LinuxDevice::_setOsType(Utils::OsType osType)
+{
+ qCDebug(linuxDeviceLog) << "Setting OS type to" << osType << "for" << displayName();
+ IDevice::setOsType(osType);
+}
+
LinuxDevice::~LinuxDevice()
{
delete d;
@@ -1057,38 +996,9 @@ IDeviceWidget *LinuxDevice::createWidget()
return new Internal::GenericLinuxDeviceConfigurationWidget(sharedFromThis());
}
-bool LinuxDevice::canAutoDetectPorts() const
-{
- return true;
-}
-
-PortsGatheringMethod LinuxDevice::portsGatheringMethod() const
-{
- return {
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
- return {filePath("sed"),
- "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
- CommandLine::Raw};
- },
-
- &Port::parseFromSedOutput
- };
-}
-
DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const
{
- return new LinuxDeviceProcessList(sharedFromThis(), parent);
+ return new ProcessList(sharedFromThis(), parent);
}
DeviceTester *LinuxDevice::createDeviceTester() const
@@ -1127,7 +1037,7 @@ bool LinuxDevice::handlesFile(const FilePath &filePath) const
ProcessInterface *LinuxDevice::createProcessInterface() const
{
- return new LinuxProcessInterface(this);
+ return new SshProcessInterface(sharedFromThis());
}
LinuxDevicePrivate::LinuxDevicePrivate(LinuxDevice *parent)
@@ -1142,7 +1052,6 @@ LinuxDevicePrivate::LinuxDevicePrivate(LinuxDevice *parent)
LinuxDevicePrivate::~LinuxDevicePrivate()
{
- qDeleteAll(m_terminals);
auto closeShell = [this] {
m_shellThread.quit();
m_shellThread.wait();
@@ -1153,6 +1062,23 @@ LinuxDevicePrivate::~LinuxDevicePrivate()
QMetaObject::invokeMethod(&m_shellThread, closeShell, Qt::BlockingQueuedConnection);
}
+void LinuxDevicePrivate::queryOsType(std::function<RunResult(const CommandLine &)> runInShell)
+{
+ const RunResult result = runInShell({"uname", {"-s"}, OsType::OsTypeLinux});
+ if (result.exitCode != 0)
+ q->_setOsType(OsTypeOtherUnix);
+ const QString osName = QString::fromUtf8(result.stdOut).trimmed();
+ if (osName == "Darwin")
+ q->_setOsType(OsTypeMac);
+ if (osName == "Linux")
+ q->_setOsType(OsTypeLinux);
+}
+
+void LinuxDevicePrivate::checkOsType()
+{
+ queryOsType([this](const CommandLine &cmd) { return runInShell(cmd); });
+}
+
// Call me with shell mutex locked
bool LinuxDevicePrivate::setupShell()
{
@@ -1166,6 +1092,10 @@ bool LinuxDevicePrivate::setupShell()
QMetaObject::invokeMethod(m_handler, [this, sshParameters] {
return m_handler->start(sshParameters);
}, Qt::BlockingQueuedConnection, &ok);
+
+ if (ok) {
+ queryOsType([this](const CommandLine &cmd) { return m_handler->runInShell(cmd); });
+ }
return ok;
}
@@ -1208,13 +1138,9 @@ static FilePaths dirsToCreate(const FilesToTransfer &files)
return sorted(std::move(dirs));
}
-static QByteArray transferCommand(const FileTransferDirection direction, bool link)
+static QByteArray transferCommand(bool link)
{
- if (direction == FileTransferDirection::Upload)
- return link ? "ln -s" : "put";
- if (direction == FileTransferDirection::Download)
- return "get";
- return {};
+ return link ? "ln -s" : "put";
}
class SshTransferInterface : public FileTransferInterface
@@ -1222,21 +1148,20 @@ class SshTransferInterface : public FileTransferInterface
Q_OBJECT
protected:
- SshTransferInterface(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
+ SshTransferInterface(const FileTransferSetupData &setup, const IDevice::ConstPtr &device)
: FileTransferInterface(setup)
- , m_device(devicePrivate->q->sharedFromThis())
- , m_devicePrivate(devicePrivate)
+ , m_device(device)
, m_process(this)
{
- m_direction = m_setup.m_files.isEmpty() ? FileTransferDirection::Invalid
- : m_setup.m_files.first().direction();
SshParameters::setupSshEnvironment(&m_process);
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
emit progress(QString::fromLocal8Bit(m_process.readAllRawStandardOutput()));
});
- connect(&m_process, &QtcProcess::done, this, &SshTransferInterface::doneImpl);
+ connect(&m_process, &Process::done, this, &SshTransferInterface::doneImpl);
}
+ IDevice::ConstPtr device() const { return m_device; }
+
bool handleError()
{
ProcessResultData resultData = m_process.resultData();
@@ -1270,10 +1195,9 @@ protected:
}
QString host() const { return m_sshParameters.host(); }
- QString userAtHost() const { return m_sshParameters.userName() + '@' + m_sshParameters.host(); }
+ QString userAtHost() const { return m_sshParameters.userAtHost(); }
- QtcProcess &process() { return m_process; }
- FileTransferDirection direction() const { return m_direction; }
+ Process &process() { return m_process; }
private:
virtual void startImpl() = 0;
@@ -1282,7 +1206,11 @@ private:
void start() final
{
m_sshParameters = displayless(m_device->sshParameters());
- if (SshSettings::connectionSharingEnabled()) {
+ const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice));
+ const auto linkDevice = DeviceManager::instance()->find(linkDeviceId);
+ const bool useConnectionSharing = !linkDevice && SshSettings::connectionSharingEnabled();
+
+ if (useConnectionSharing) {
m_connecting = true;
m_connectionHandle.reset(new SshConnectionHandle(m_device));
m_connectionHandle->setParent(this);
@@ -1290,7 +1218,10 @@ private:
this, &SshTransferInterface::handleConnected);
connect(m_connectionHandle.get(), &SshConnectionHandle::disconnected,
this, &SshTransferInterface::handleDisconnected);
- m_devicePrivate->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
+ auto linuxDevice = m_device.dynamicCast<const LinuxDevice>();
+ QTC_ASSERT(linuxDevice, startFailed("No Linux device"); return);
+ linuxDevice->connectionAccess()
+ ->attachToSharedConnection(m_connectionHandle.get(), m_sshParameters);
} else {
startImpl();
}
@@ -1318,28 +1249,33 @@ private:
}
IDevice::ConstPtr m_device;
- LinuxDevicePrivate *m_devicePrivate = nullptr;
SshParameters m_sshParameters;
- FileTransferDirection m_direction = FileTransferDirection::Invalid; // helper
// ssh shared connection related
std::unique_ptr<SshConnectionHandle> m_connectionHandle;
QString m_socketFilePath;
bool m_connecting = false;
- QtcProcess m_process;
+ Process m_process;
};
class SftpTransferImpl : public SshTransferInterface
{
public:
- SftpTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
- : SshTransferInterface(setup, devicePrivate) { }
+ SftpTransferImpl(const FileTransferSetupData &setup, const IDevice::ConstPtr &device)
+ : SshTransferInterface(setup, device)
+ {}
private:
void startImpl() final
{
- const FilePath sftpBinary = SshSettings::sftpFilePath();
+ FilePath sftpBinary = SshSettings::sftpFilePath();
+
+ // This is a hack. We only test the last hop here.
+ const Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
+ if (const auto linkDevice = DeviceManager::instance()->find(linkDeviceId))
+ sftpBinary = linkDevice->filePath(sftpBinary.fileName()).searchInPath();
+
if (!sftpBinary.exists()) {
startFailed(Tr::tr("\"sftp\" binary \"%1\" does not exist.")
.arg(sftpBinary.toUserOutput()));
@@ -1349,35 +1285,26 @@ private:
QByteArray batchData;
const FilePaths dirs = dirsToCreate(m_setup.m_files);
- for (const FilePath &dir : dirs) {
- if (direction() == FileTransferDirection::Upload) {
- batchData += "-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() + '\n';
- } else if (direction() == FileTransferDirection::Download) {
- if (!QDir::root().mkpath(dir.path())) {
- startFailed(Tr::tr("Failed to create local directory \"%1\".")
- .arg(QDir::toNativeSeparators(dir.path())));
- return;
- }
- }
- }
+ for (const FilePath &dir : dirs)
+ batchData += "-mkdir " + ProcessArgs::quoteArgUnix(dir.path()).toLocal8Bit() + '\n';
for (const FileToTransfer &file : m_setup.m_files) {
FilePath sourceFileOrLinkTarget = file.m_source;
bool link = false;
- if (direction() == FileTransferDirection::Upload) {
- const QFileInfo fi(file.m_source.toFileInfo());
- if (fi.isSymLink()) {
- link = true;
- batchData += "-rm " + ProcessArgs::quoteArgUnix(
- file.m_target.path()).toLocal8Bit() + '\n';
- // see QTBUG-5817.
- sourceFileOrLinkTarget =
- sourceFileOrLinkTarget.withNewPath(fi.dir().relativeFilePath(fi.symLinkTarget()));
- }
- }
- batchData += transferCommand(direction(), link) + ' '
- + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' '
- + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n';
+
+ const QFileInfo fi(file.m_source.toFileInfo());
+ if (fi.isSymLink()) {
+ link = true;
+ batchData += "-rm " + ProcessArgs::quoteArgUnix(
+ file.m_target.path()).toLocal8Bit() + '\n';
+ // see QTBUG-5817.
+ sourceFileOrLinkTarget =
+ sourceFileOrLinkTarget.withNewPath(fi.dir().relativeFilePath(fi.symLinkTarget()));
+ }
+
+ batchData += transferCommand(link) + ' '
+ + ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget.path()).toLocal8Bit() + ' '
+ + ProcessArgs::quoteArgUnix(file.m_target.path()).toLocal8Bit() + '\n';
}
process().setCommand({sftpBinary, fullConnectionOptions() << "-b" << "-" << host()});
process().setWriteData(batchData);
@@ -1390,8 +1317,8 @@ private:
class RsyncTransferImpl : public SshTransferInterface
{
public:
- RsyncTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *devicePrivate)
- : SshTransferInterface(setup, devicePrivate)
+ RsyncTransferImpl(const FileTransferSetupData &setup, const IDevice::ConstPtr &device)
+ : SshTransferInterface(setup, device)
{ }
private:
@@ -1442,8 +1369,7 @@ private:
if (!HostOsInfo::isWindowsHost())
return file;
- QString localFilePath = direction() == FileTransferDirection::Upload
- ? file.m_source.path() : file.m_target.path();
+ QString localFilePath = file.m_source.path();
localFilePath = '/' + localFilePath.at(0) + localFilePath.mid(2);
if (anyOf(options, [](const QString &opt) {
return opt.contains("cygwin", Qt::CaseInsensitive); })) {
@@ -1451,30 +1377,19 @@ private:
}
FileToTransfer fixedFile = file;
- if (direction() == FileTransferDirection::Upload)
- fixedFile.m_source = fixedFile.m_source.withNewPath(localFilePath);
- else
- fixedFile.m_target = fixedFile.m_target.withNewPath(localFilePath);
+ fixedFile.m_source = fixedFile.m_source.withNewPath(localFilePath);
return fixedFile;
}
QPair<QString, QString> fixPaths(const FileToTransfer &file, const QString &remoteHost) const
{
- FilePath localPath;
- FilePath remotePath;
- if (direction() == FileTransferDirection::Upload) {
- localPath = file.m_source;
- remotePath = file.m_target;
- } else {
- remotePath = file.m_source;
- localPath = file.m_target;
- }
+ FilePath localPath = file.m_source;
+ FilePath remotePath = file.m_target;
const QString local = (localPath.isDir() && localPath.path().back() != '/')
? localPath.path() + '/' : localPath.path();
const QString remote = remoteHost + ':' + remotePath.path();
- return direction() == FileTransferDirection::Upload ? qMakePair(local, remote)
- : qMakePair(remote, local);
+ return qMakePair(local, remote);
}
int m_currentIndex = 0;
@@ -1483,7 +1398,7 @@ private:
class GenericTransferImpl : public FileTransferInterface
{
public:
- GenericTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *)
+ GenericTransferImpl(const FileTransferSetupData &setup)
: FileTransferInterface(setup)
{}
@@ -1525,8 +1440,9 @@ private:
.arg(m_currentIndex)
.arg(m_fileCount)
.arg(source.toUserOutput(), target.toUserOutput()));
- if (!source.copyFile(target)) {
- result.m_errorString = Tr::tr("Failed.");
+ expected_str<void> copyResult = source.copyFile(target);
+ if (!copyResult) {
+ result.m_errorString = Tr::tr("Failed: %1").arg(copyResult.error());
result.m_exitCode = -1; // Random pick
emit done(result);
return;
@@ -1545,14 +1461,24 @@ FileTransferInterface *LinuxDevice::createFileTransferInterface(
const FileTransferSetupData &setup) const
{
switch (setup.m_method) {
- case FileTransferMethod::Sftp: return new SftpTransferImpl(setup, d);
- case FileTransferMethod::Rsync: return new RsyncTransferImpl(setup, d);
- case FileTransferMethod::GenericCopy: return new GenericTransferImpl(setup, d);
+ case FileTransferMethod::Sftp: return new SftpTransferImpl(setup, sharedFromThis());
+ case FileTransferMethod::Rsync: return new RsyncTransferImpl(setup, sharedFromThis());
+ case FileTransferMethod::GenericCopy: return new GenericTransferImpl(setup);
}
QTC_CHECK(false);
return {};
}
+LinuxDevicePrivate *LinuxDevice::connectionAccess() const
+{
+ return d;
+}
+
+void LinuxDevice::checkOsType()
+{
+ d->checkOsType();
+}
+
namespace Internal {
// Factory
@@ -1563,6 +1489,7 @@ LinuxDeviceFactory::LinuxDeviceFactory()
setDisplayName(Tr::tr("Remote Linux Device"));
setIcon(QIcon());
setConstructionFunction(&LinuxDevice::create);
+ setQuickCreationAllowed(true);
setCreator([] {
GenericLinuxDeviceConfigurationWizard wizard(Core::ICore::dialogParent());
if (wizard.exec() != QDialog::Accepted)
diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h
index ac65d6e89c..2094b426b4 100644
--- a/src/plugins/remotelinux/linuxdevice.h
+++ b/src/plugins/remotelinux/linuxdevice.h
@@ -22,8 +22,6 @@ public:
ProjectExplorer::IDeviceWidget *createWidget() override;
- bool canAutoDetectPorts() const override;
- ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
bool canCreateProcessModel() const override { return true; }
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
bool hasDeviceTester() const override { return true; }
@@ -41,12 +39,16 @@ public:
ProjectExplorer::FileTransferInterface *createFileTransferInterface(
const ProjectExplorer::FileTransferSetupData &setup) const override;
+ class LinuxDevicePrivate *connectionAccess() const;
+ void checkOsType() override;
+
protected:
LinuxDevice();
+ void _setOsType(Utils::OsType osType);
+
class LinuxDevicePrivate *d;
- friend class SshProcessInterface;
- friend class SshTransferInterface;
+ friend class LinuxDevicePrivate;
};
namespace Internal {
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index 3c34a63820..c32cabfee1 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -10,20 +10,21 @@
#include <projectexplorer/devicesupport/filetransfer.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/stringutils.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux {
namespace Internal {
struct TransferStorage
{
- bool sftpWorks = false;
+ bool useGenericCopy = false;
};
class GenericLinuxDeviceTesterPrivate
@@ -33,7 +34,7 @@ public:
QStringList commandsToTest() const;
- TaskItem echoTask() const;
+ TaskItem echoTask(const QString &contents) const;
TaskItem unameTask() const;
TaskItem gathererTask() const;
TaskItem transferTask(FileTransferMethod method,
@@ -49,8 +50,6 @@ public:
QList<TaskItem> m_extraTests;
};
-static const char s_echoContents[] = "Hello Remote World!";
-
QStringList GenericLinuxDeviceTesterPrivate::commandsToTest() const
{
static const QStringList s_commandsToTest = {"base64",
@@ -91,48 +90,49 @@ QStringList GenericLinuxDeviceTesterPrivate::commandsToTest() const
return commands;
}
-TaskItem GenericLinuxDeviceTesterPrivate::echoTask() const
+TaskItem GenericLinuxDeviceTesterPrivate::echoTask(const QString &contents) const
{
- const auto setup = [this](QtcProcess &process) {
+ const auto setup = [this, contents](Process &process) {
emit q->progressMessage(Tr::tr("Sending echo to device..."));
- process.setCommand({m_device->filePath("echo"), {s_echoContents}});
+ process.setCommand({m_device->filePath("echo"), {contents}});
};
- const auto done = [this](const QtcProcess &process) {
- const QString reply = process.cleanedStdOut().chopped(1); // Remove trailing '\n'
- if (reply != s_echoContents)
- emit q->errorMessage(Tr::tr("Device replied to echo with unexpected contents.") + '\n');
+ const auto done = [this, contents](const Process &process) {
+ const QString reply = Utils::chopIfEndsWith(process.cleanedStdOut(), '\n');
+ if (reply != contents)
+ emit q->errorMessage(Tr::tr("Device replied to echo with unexpected contents: \"%1\"")
+ .arg(reply) + '\n');
else
emit q->progressMessage(Tr::tr("Device replied to echo with expected contents.") + '\n');
};
- const auto error = [this](const QtcProcess &process) {
+ const auto error = [this](const Process &process) {
const QString stdErrOutput = process.cleanedStdErr();
if (!stdErrOutput.isEmpty())
emit q->errorMessage(Tr::tr("echo failed: %1").arg(stdErrOutput) + '\n');
else
emit q->errorMessage(Tr::tr("echo failed.") + '\n');
};
- return Process(setup, done, error);
+ return ProcessTask(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::unameTask() const
{
- const auto setup = [this](QtcProcess &process) {
+ const auto setup = [this](Process &process) {
emit q->progressMessage(Tr::tr("Checking kernel version..."));
process.setCommand({m_device->filePath("uname"), {"-rsm"}});
};
- const auto done = [this](const QtcProcess &process) {
+ const auto done = [this](const Process &process) {
emit q->progressMessage(process.cleanedStdOut());
};
- const auto error = [this](const QtcProcess &process) {
+ const auto error = [this](const Process &process) {
const QString stdErrOutput = process.cleanedStdErr();
if (!stdErrOutput.isEmpty())
emit q->errorMessage(Tr::tr("uname failed: %1").arg(stdErrOutput) + '\n');
else
emit q->errorMessage(Tr::tr("uname failed.") + '\n');
};
- return Tasking::Group {
+ return Group {
optional,
- Process(setup, done, error)
+ ProcessTask(setup, done, error)
};
}
@@ -154,9 +154,14 @@ TaskItem GenericLinuxDeviceTesterPrivate::gathererTask() const
}
};
const auto error = [this](const DeviceUsedPortsGatherer &gatherer) {
- emit q->errorMessage(Tr::tr("Error gathering ports: %1").arg(gatherer.errorString()) + '\n');
+ emit q->errorMessage(Tr::tr("Error gathering ports: %1").arg(gatherer.errorString()) + '\n'
+ + Tr::tr("Some tools will not work out of the box.\n"));
+ };
+
+ return Group {
+ optional,
+ PortGatherer(setup, done, error)
};
- return PortGatherer(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method,
@@ -173,8 +178,10 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method
emit q->progressMessage(Tr::tr("\"%1\" is functional.\n").arg(methodName));
if (method == FileTransferMethod::Rsync)
m_device->setExtraData(Constants::SupportsRSync, true);
+ else if (method == FileTransferMethod::Sftp)
+ m_device->setExtraData(Constants::SupportsSftp, true);
else
- storage->sftpWorks = true;
+ storage->useGenericCopy = true;
};
const auto error = [this, method, storage](const FileTransfer &transfer) {
const QString methodName = FileTransfer::transferMethodName(method);
@@ -189,25 +196,33 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method
.arg(methodName).arg(resultData.m_exitCode).arg(resultData.m_errorString);
}
emit q->errorMessage(error);
- if (method == FileTransferMethod::Rsync) {
+ if (method == FileTransferMethod::Rsync)
m_device->setExtraData(Constants::SupportsRSync, false);
- if (!storage->sftpWorks)
- return;
+ else if (method == FileTransferMethod::Sftp)
+ m_device->setExtraData(Constants::SupportsSftp, false);
+
+ const QVariant supportsRSync = m_device->extraData(Constants::SupportsRSync);
+ const QVariant supportsSftp = m_device->extraData(Constants::SupportsSftp);
+ if (supportsRSync.isValid() && !supportsRSync.toBool()
+ && supportsSftp.isValid() && !supportsSftp.toBool()) {
+ const QString generic = FileTransfer::transferMethodName(FileTransferMethod::GenericCopy);
const QString sftp = FileTransfer::transferMethodName(FileTransferMethod::Sftp);
- const QString rsync = methodName;
+ const QString rsync = FileTransfer::transferMethodName(FileTransferMethod::Rsync);
emit q->progressMessage(Tr::tr("\"%1\" will be used for deployment, because \"%2\" "
- "is not available.\n").arg(sftp, rsync));
+ "and \"%3\" are not available.\n")
+ .arg(generic, sftp, rsync));
}
};
- return TransferTest(setup, done, error);
+ return FileTransferTestTask(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::transferTasks() const
{
TreeStorage<TransferStorage> storage;
- return Tasking::Group {
+ return Group {
continueOnDone,
Storage(storage),
+ transferTask(FileTransferMethod::GenericCopy, storage),
transferTask(FileTransferMethod::Sftp, storage),
transferTask(FileTransferMethod::Rsync, storage),
OnGroupError([this] { emit q->errorMessage(Tr::tr("Deployment to this device will not "
@@ -218,23 +233,23 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTasks() const
TaskItem GenericLinuxDeviceTesterPrivate::commandTask(const QString &commandName) const
{
- const auto setup = [this, commandName](QtcProcess &process) {
+ const auto setup = [this, commandName](Process &process) {
emit q->progressMessage(Tr::tr("%1...").arg(commandName));
CommandLine command{m_device->filePath("/bin/sh"), {"-c"}};
command.addArgs(QLatin1String("\"command -v %1\"").arg(commandName), CommandLine::Raw);
process.setCommand(command);
};
- const auto done = [this, commandName](const QtcProcess &) {
+ const auto done = [this, commandName](const Process &) {
emit q->progressMessage(Tr::tr("%1 found.").arg(commandName));
};
- const auto error = [this, commandName](const QtcProcess &process) {
+ const auto error = [this, commandName](const Process &process) {
const QString message = process.result() == ProcessResult::StartFailed
? Tr::tr("An error occurred while checking for %1.").arg(commandName)
+ '\n' + process.errorString()
: Tr::tr("%1 not found.").arg(commandName);
emit q->errorMessage(message);
};
- return Process(setup, done, error);
+ return ProcessTask(setup, done, error);
}
TaskItem GenericLinuxDeviceTesterPrivate::commandTasks() const
@@ -245,7 +260,7 @@ TaskItem GenericLinuxDeviceTesterPrivate::commandTasks() const
}));
for (const QString &commandName : commandsToTest())
tasks.append(commandTask(commandName));
- return Tasking::Group {tasks};
+ return Group {tasks};
}
} // namespace Internal
@@ -281,7 +296,8 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguratio
};
QList<TaskItem> taskItems = {
- d->echoTask(),
+ d->echoTask("Hello"), // No quoting necessary
+ d->echoTask("Hello Remote World!"), // Checks quoting, too.
d->unameTask(),
d->gathererTask(),
d->transferTasks()
diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h
index 5c1210f9a1..3fda1ecb3a 100644
--- a/src/plugins/remotelinux/linuxdevicetester.h
+++ b/src/plugins/remotelinux/linuxdevicetester.h
@@ -7,7 +7,7 @@
#include <projectexplorer/devicesupport/idevice.h>
-namespace Utils::Tasking { class TaskItem; }
+namespace Tasking { class TaskItem; }
namespace RemoteLinux {
@@ -22,7 +22,7 @@ public:
~GenericLinuxDeviceTester() override;
void setExtraCommandsToTest(const QStringList &extraCommands);
- void setExtraTests(const QList<Utils::Tasking::TaskItem> &extraTests);
+ void setExtraTests(const QList<Tasking::TaskItem> &extraTests);
void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override;
void stopTest() override;
diff --git a/src/plugins/remotelinux/linuxprocessinterface.h b/src/plugins/remotelinux/linuxprocessinterface.h
index 949c593420..e2a8e82fc8 100644
--- a/src/plugins/remotelinux/linuxprocessinterface.h
+++ b/src/plugins/remotelinux/linuxprocessinterface.h
@@ -5,32 +5,35 @@
#include "remotelinux_export.h"
-#include "sshprocessinterface.h"
+#include "linuxdevice.h"
+
+#include <utils/processinterface.h>
namespace RemoteLinux {
-class LinuxDevice;
class SshProcessInterfacePrivate;
-class REMOTELINUX_EXPORT LinuxProcessInterface : public SshProcessInterface
+class REMOTELINUX_EXPORT SshProcessInterface : public Utils::ProcessInterface
{
public:
- LinuxProcessInterface(const LinuxDevice *linuxDevice);
- ~LinuxProcessInterface();
+ explicit SshProcessInterface(const ProjectExplorer::IDevice::ConstPtr &device);
+ ~SshProcessInterface();
-private:
- void handleSendControlSignal(Utils::ControlSignal controlSignal) override;
+protected:
+ void emitStarted(qint64 processId);
+ void killIfRunning();
+ qint64 processId() const;
+ bool runInShell(const Utils::CommandLine &command, const QByteArray &data = {});
- void handleStarted(qint64 processId) final;
- void handleDone(const Utils::ProcessResultData &resultData) final;
- void handleReadyReadStandardOutput(const QByteArray &outputData) final;
- void handleReadyReadStandardError(const QByteArray &errorData) final;
+private:
+ virtual void handleSendControlSignal(Utils::ControlSignal controlSignal);
- QString fullCommandLine(const Utils::CommandLine &commandLine) const final;
+ void start() final;
+ qint64 write(const QByteArray &data) final;
+ void sendControlSignal(Utils::ControlSignal controlSignal) final;
- QByteArray m_output;
- QByteArray m_error;
- bool m_pidParsed = false;
+ friend class SshProcessInterfacePrivate;
+ SshProcessInterfacePrivate *d = nullptr;
};
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp
index ea7b636458..d1be105145 100644
--- a/src/plugins/remotelinux/makeinstallstep.cpp
+++ b/src/plugins/remotelinux/makeinstallstep.cpp
@@ -20,8 +20,8 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDirIterator>
#include <QFileInfo>
@@ -124,16 +124,6 @@ MakeInstallStep::MakeInstallStep(BuildStepList *parent, Id id) : MakeStep(parent
});
}
-Utils::Id MakeInstallStep::stepId()
-{
- return Constants::MakeInstallStepId;
-}
-
-QString MakeInstallStep::displayName()
-{
- return Tr::tr("Install into temporary host directory");
-}
-
QWidget *MakeInstallStep::createConfigWidget()
{
// Note: this intentionally skips the MakeStep::createConfigWidget() level.
@@ -145,7 +135,7 @@ bool MakeInstallStep::init()
if (!MakeStep::init())
return false;
- const FilePath rootDir = installRoot().onDevice(makeCommand());
+ const FilePath rootDir = makeCommand().withNewPath(installRoot().path()); // FIXME: Needed?
if (rootDir.isEmpty()) {
emit addTask(BuildSystemTask(Task::Error, Tr::tr("You must provide an install root.")));
return false;
@@ -172,12 +162,10 @@ bool MakeInstallStep::init()
const MakeInstallCommand cmd = buildSystem()->makeInstallCommand(rootDir);
if (cmd.environment.hasChanges()) {
Environment env = processParameters()->environment();
- for (auto it = cmd.environment.constBegin(); it != cmd.environment.constEnd(); ++it) {
- if (cmd.environment.isEnabled(it)) {
- const QString key = cmd.environment.key(it);
- env.set(key, cmd.environment.expandedValueForKey(key));
- }
- }
+ cmd.environment.forEachEntry([&](const QString &key, const QString &value, bool enabled) {
+ if (enabled)
+ env.set(key, cmd.environment.expandVariables(value));
+ });
processParameters()->setEnvironment(env);
}
m_noInstallTarget = false;
@@ -193,7 +181,7 @@ bool MakeInstallStep::init()
void MakeInstallStep::finish(ProcessResult result)
{
if (isSuccess(result)) {
- const FilePath rootDir = installRoot().onDevice(makeCommand());
+ const FilePath rootDir = makeCommand().withNewPath(installRoot().path()); // FIXME: Needed?
m_deploymentData = DeploymentData();
m_deploymentData.setLocalInstallRoot(rootDir);
@@ -251,11 +239,8 @@ void MakeInstallStep::updateArgsFromAspect()
void MakeInstallStep::updateFullCommandLine()
{
- // FIXME: Only executable?
- static_cast<StringAspect *>(aspect(FullCommandLineAspectId))->setValue(
- QDir::toNativeSeparators(
- ProcessArgs::quoteArg(makeExecutable().toString()))
- + ' ' + userArguments());
+ CommandLine cmd{makeExecutable(), userArguments(), CommandLine::Raw};
+ static_cast<StringAspect *>(aspect(FullCommandLineAspectId))->setValue(cmd.toUserOutput());
}
void MakeInstallStep::updateFromCustomCommandLineAspect()
@@ -263,7 +248,7 @@ void MakeInstallStep::updateFromCustomCommandLineAspect()
const StringAspect * const aspect = customCommandLineAspect();
if (!aspect->isChecked())
return;
- const QStringList tokens = ProcessArgs::splitArgs(aspect->value());
+ const QStringList tokens = ProcessArgs::splitArgs(aspect->value(), HostOsInfo::hostOs());
setMakeCommand(tokens.isEmpty() ? FilePath() : FilePath::fromString(tokens.first()));
setUserArguments(ProcessArgs::joinArgs(tokens.mid(1)));
}
@@ -283,4 +268,12 @@ bool MakeInstallStep::fromMap(const QVariantMap &map)
return true;
}
-} // namespace RemoteLinux
+// Factory
+
+MakeInstallStepFactory::MakeInstallStepFactory()
+{
+ registerStep<MakeInstallStep>(Constants::MakeInstallStepId);
+ setDisplayName(Tr::tr("Install into temporary host directory"));
+}
+
+} // RemoteLinux
diff --git a/src/plugins/remotelinux/makeinstallstep.h b/src/plugins/remotelinux/makeinstallstep.h
index ec2fc3985a..3be2d5df97 100644
--- a/src/plugins/remotelinux/makeinstallstep.h
+++ b/src/plugins/remotelinux/makeinstallstep.h
@@ -16,9 +16,6 @@ class REMOTELINUX_EXPORT MakeInstallStep : public ProjectExplorer::MakeStep
public:
MakeInstallStep(ProjectExplorer::BuildStepList *parent, Utils::Id id);
- static Utils::Id stepId();
- static QString displayName();
-
private:
bool fromMap(const QVariantMap &map) override;
QWidget *createConfigWidget() override;
@@ -41,4 +38,11 @@ private:
bool m_isCmakeProject = false;
};
+class REMOTELINUX_EXPORT MakeInstallStepFactory
+ : public ProjectExplorer::BuildStepFactory
+{
+public:
+ MakeInstallStepFactory();
+};
+
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.cpp b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
index fcff8efd44..c7b4a54810 100644
--- a/src/plugins/remotelinux/publickeydeploymentdialog.cpp
+++ b/src/plugins/remotelinux/publickeydeploymentdialog.cpp
@@ -10,7 +10,7 @@
#include <projectexplorer/devicesupport/sshsettings.h>
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/stringutils.h>
#include <utils/theme/theme.h>
@@ -23,7 +23,7 @@ namespace Internal {
class PublicKeyDeploymentDialogPrivate
{
public:
- QtcProcess m_process;
+ Process m_process;
bool m_done;
};
} // namespace Internal;
@@ -56,7 +56,7 @@ PublicKeyDeploymentDialog::PublicKeyDeploymentDialog(const IDevice::ConstPtr &de
setValue(0);
connect(this, &PublicKeyDeploymentDialog::canceled, this,
[this] { d->m_done ? accept() : reject(); });
- connect(&d->m_process, &QtcProcess::done, this, [this] {
+ connect(&d->m_process, &Process::done, this, [this] {
const bool succeeded = d->m_process.result() == ProcessResult::FinishedWithSuccess;
QString finalMessage;
if (!succeeded) {
diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs
index 7881cab47d..03a6afad10 100644
--- a/src/plugins/remotelinux/remotelinux.qbs
+++ b/src/plugins/remotelinux/remotelinux.qbs
@@ -19,8 +19,6 @@ Project {
"deploymenttimeinfo.h",
"customcommanddeploystep.cpp",
"customcommanddeploystep.h",
- "genericdirectuploadservice.cpp",
- "genericdirectuploadservice.h",
"genericdirectuploadstep.cpp",
"genericdirectuploadstep.h",
"genericlinuxdeviceconfigurationwidget.cpp",
@@ -62,7 +60,6 @@ Project {
"rsyncdeploystep.h",
"sshkeycreationdialog.cpp",
"sshkeycreationdialog.h",
- "sshprocessinterface.h",
"tarpackagecreationstep.cpp",
"tarpackagecreationstep.h",
"tarpackagedeploystep.cpp",
@@ -70,9 +67,7 @@ Project {
"images/embeddedtarget.png",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"filesystemaccess_test.cpp",
"filesystemaccess_test.h",
diff --git a/src/plugins/remotelinux/remotelinux_constants.h b/src/plugins/remotelinux/remotelinux_constants.h
index 31a9758828..9c428650c7 100644
--- a/src/plugins/remotelinux/remotelinux_constants.h
+++ b/src/plugins/remotelinux/remotelinux_constants.h
@@ -19,7 +19,10 @@ const char RsyncDeployStepId[] = "RemoteLinux.RsyncDeployStep";
const char CustomCommandDeployStepId[] = "RemoteLinux.GenericRemoteLinuxCustomCommandDeploymentStep";
const char KillAppStepId[] = "RemoteLinux.KillAppStep";
-const char SupportsRSync[] = "RemoteLinux.SupportsRSync";
+const char SupportsRSync[] = "RemoteLinux.SupportsRSync";
+const char SupportsSftp[] = "RemoteLinux.SupportsSftp";
+const char SourceProfile[] = "RemoteLinux.SourceProfile";
+const char LinkDevice[] = "RemoteLinux.LinkDevice";
const char RunConfigId[] = "RemoteLinuxRunConfiguration:";
const char CustomRunConfigId[] = "RemoteLinux.CustomRunConfig";
diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
index 8cacd63ff9..94bfbfb981 100644
--- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp
@@ -3,19 +3,38 @@
#include "remotelinuxdeployconfiguration.h"
-#include "makeinstallstep.h"
#include "remotelinux_constants.h"
#include "remotelinuxtr.h"
+#include <projectexplorer/devicesupport/filetransferinterface.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
using namespace ProjectExplorer;
namespace RemoteLinux::Internal {
+FileTransferMethod defaultTransferMethod(Kit *kit)
+{
+ auto runDevice = DeviceKitAspect::device(kit);
+ auto buildDevice = BuildDeviceKitAspect::device(kit);
+
+ if (runDevice != buildDevice) {
+ // FIXME: That's not the full truth, we need support from the build
+ // device, too.
+ if (runDevice && runDevice->extraData(Constants::SupportsRSync).toBool())
+ return FileTransferMethod::Rsync;
+ }
+
+ if (runDevice && runDevice->extraData(Constants::SupportsSftp).toBool())
+ return FileTransferMethod::Sftp;
+
+ return FileTransferMethod::GenericCopy;
+}
+
RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory()
{
setConfigBaseId(RemoteLinux::Constants::DeployToGenericLinux);
@@ -32,31 +51,23 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory()
setPostRestore([needsMakeInstall](DeployConfiguration *dc, const QVariantMap &map) {
// 4.9 -> 4.10. See QTCREATORBUG-22689.
if (map.value("_checkMakeInstall").toBool() && needsMakeInstall(dc->target())) {
- auto step = new MakeInstallStep(dc->stepList(), MakeInstallStep::stepId());
- dc->stepList()->insertStep(0, step);
+ dc->stepList()->insertStep(0, Constants::MakeInstallStepId);
}
});
addInitialStep(Constants::MakeInstallStepId, needsMakeInstall);
addInitialStep(Constants::KillAppStepId);
- // Todo: Check: Instead of having two different steps here, have one
+ // Todo: Check: Instead of having three different steps here, have one
// and shift the logic into the implementation there?
addInitialStep(Constants::RsyncDeployStepId, [](Target *target) {
- auto runDevice = DeviceKitAspect::device(target->kit());
- auto buildDevice = BuildDeviceKitAspect::device(target->kit());
- if (runDevice == buildDevice)
- return false;
- // FIXME: That's not the full truth, we need support from the build
- // device, too.
- return runDevice && runDevice->extraData(Constants::SupportsRSync).toBool();
+ return defaultTransferMethod(target->kit()) == FileTransferMethod::Rsync;
});
addInitialStep(Constants::DirectUploadStepId, [](Target *target) {
- auto runDevice = DeviceKitAspect::device(target->kit());
- auto buildDevice = BuildDeviceKitAspect::device(target->kit());
- if (runDevice == buildDevice)
- return true;
- return runDevice && !runDevice->extraData(Constants::SupportsRSync).toBool();
+ return defaultTransferMethod(target->kit()) == FileTransferMethod::Sftp;
+ });
+ addInitialStep(ProjectExplorer::Constants::COPY_FILE_STEP, [](Target *target) {
+ return defaultTransferMethod(target->kit()) == FileTransferMethod::GenericCopy;
});
}
diff --git a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp
index cc13e51273..1be0624490 100644
--- a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp
+++ b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp
@@ -40,7 +40,7 @@ public:
connect(target, &Target::kitChanged, [aspect] { aspect->setRemoteEnvironment({}); });
- connect(fetchButton, &QPushButton::clicked, this, [this, aspect, target] {
+ connect(fetchButton, &QPushButton::clicked, this, [aspect, target] {
if (IDevice::ConstPtr device = DeviceKitAspect::device(target->kit())) {
DeviceFileAccess *access = device->fileAccess();
QTC_ASSERT(access, return);
diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp
index 73a30b27e3..b3af1fb873 100644
--- a/src/plugins/remotelinux/remotelinuxplugin.cpp
+++ b/src/plugins/remotelinux/remotelinuxplugin.cpp
@@ -34,16 +34,14 @@ using namespace Utils;
namespace RemoteLinux {
namespace Internal {
-template <class Step>
-class GenericDeployStepFactory : public ProjectExplorer::BuildStepFactory
+template <class Factory>
+class RemoteLinuxDeployStepFactory : public Factory
{
public:
- GenericDeployStepFactory()
+ RemoteLinuxDeployStepFactory()
{
- registerStep<Step>(Step::stepId());
- setDisplayName(Step::displayName());
- setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux);
- setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
+ Factory::setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux);
+ Factory::setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY);
}
};
@@ -56,11 +54,11 @@ public:
RemoteLinuxDeployConfigurationFactory deployConfigurationFactory;
TarPackageCreationStepFactory tarPackageCreationStepFactory;
TarPackageDeployStepFactory tarPackageDeployStepFactory;
- GenericDeployStepFactory<GenericDirectUploadStep> genericDirectUploadStepFactory;
- GenericDeployStepFactory<RsyncDeployStep> rsyncDeployStepFactory;
+ RemoteLinuxDeployStepFactory<GenericDirectUploadStepFactory> genericDirectUploadStepFactory;
+ RemoteLinuxDeployStepFactory<RsyncDeployStepFactory> rsyncDeployStepFactory;
CustomCommandDeployStepFactory customCommandDeployStepFactory;
KillAppStepFactory killAppStepFactory;
- GenericDeployStepFactory<MakeInstallStep> makeInstallStepFactory;
+ RemoteLinuxDeployStepFactory<MakeInstallStepFactory> makeInstallStepFactory;
RemoteLinuxRunWorkerFactory runWorkerFactory;
RemoteLinuxDebugWorkerFactory debugWorkerFactory;
RemoteLinuxQmlToolingWorkerFactory qmlToolingWorkerFactory;
diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
index 3e28f24af4..42e80aae41 100644
--- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
+++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp
@@ -10,6 +10,7 @@
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfigurationaspects.h>
@@ -58,18 +59,17 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id)
envAspect, &EnvironmentAspect::environmentChanged);
setUpdater([this, target, exeAspect, symbolsAspect, libAspect] {
- BuildTargetInfo bti = buildTargetInfo();
+ const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(target->kit());
+ const IDeviceConstPtr runDevice = DeviceKitAspect::device(target->kit());
+ QTC_ASSERT(buildDevice, return);
+ QTC_ASSERT(runDevice, return);
+ const BuildTargetInfo bti = buildTargetInfo();
const FilePath localExecutable = bti.targetFilePath;
- DeployableFile depFile = target->deploymentData().deployableForLocalFile(localExecutable);
+ const DeploymentData deploymentData = target->deploymentData();
+ const DeployableFile depFile = deploymentData.deployableForLocalFile(localExecutable);
- if (depFile.localFilePath().needsDevice()) // a full remote build
- exeAspect->setExecutable(depFile.localFilePath());
- else
- exeAspect->setExecutable(FilePath::fromString(depFile.remoteFilePath()));
+ exeAspect->setExecutable(runDevice->filePath(depFile.remoteFilePath()));
symbolsAspect->setFilePath(localExecutable);
-
- const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(target->kit());
- const IDeviceConstPtr runDevice = DeviceKitAspect::device(target->kit());
libAspect->setEnabled(buildDevice == runDevice);
});
diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
index e073acde05..816e0e3869 100644
--- a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
+++ b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp
@@ -7,8 +7,8 @@
#include <utils/commandline.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
@@ -30,8 +30,8 @@ static QString signalProcessGroupByPidCommandLine(qint64 pid, int signal)
void RemoteLinuxSignalOperation::run(const QString &command)
{
QTC_ASSERT(!m_process, return);
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &RemoteLinuxSignalOperation::runnerDone);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &RemoteLinuxSignalOperation::runnerDone);
m_process->setCommand({m_device->filePath("/bin/sh"), {"-c", command}});
m_process->start();
diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.h b/src/plugins/remotelinux/remotelinuxsignaloperation.h
index d3e840e0d3..00967c9414 100644
--- a/src/plugins/remotelinux/remotelinuxsignaloperation.h
+++ b/src/plugins/remotelinux/remotelinuxsignaloperation.h
@@ -32,7 +32,7 @@ private:
void run(const QString &command);
const ProjectExplorer::IDeviceConstPtr m_device;
- std::unique_ptr<Utils::QtcProcess> m_process;
+ std::unique_ptr<Utils::Process> m_process;
friend class LinuxDevice;
};
diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp
index b20eaf9049..84eb47b8cc 100644
--- a/src/plugins/remotelinux/rsyncdeploystep.cpp
+++ b/src/plugins/remotelinux/rsyncdeploystep.cpp
@@ -16,21 +16,21 @@
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux {
-class RsyncDeployService : public AbstractRemoteLinuxDeployService
+// RsyncDeployStep
+
+class RsyncDeployStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setDeployableFiles(const QList<DeployableFile> &files);
- void setIgnoreMissingFiles(bool ignore) { m_ignoreMissingFiles = ignore; }
- void setFlags(const QString &flags) { m_flags = flags; }
+ RsyncDeployStep(BuildStepList *bsl, Id id);
private:
bool isDeploymentNecessary() const final;
@@ -43,23 +43,51 @@ private:
QString m_flags;
};
-void RsyncDeployService::setDeployableFiles(const QList<DeployableFile> &files)
+RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
{
- m_files.clear();
- for (const DeployableFile &f : files)
- m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())});
+ auto flags = addAspect<StringAspect>();
+ flags->setDisplayStyle(StringAspect::LineEditDisplay);
+ flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags");
+ flags->setLabelText(Tr::tr("Flags:"));
+ flags->setValue(FileTransferSetupData::defaultRsyncFlags());
+
+ auto ignoreMissingFiles = addAspect<BoolAspect>();
+ ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");
+ ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"),
+ BoolAspect::LabelPlacement::InExtraLabel);
+ ignoreMissingFiles->setValue(false);
+
+ setInternalInitializer([this, ignoreMissingFiles, flags] {
+ if (BuildDeviceKitAspect::device(kit()) == DeviceKitAspect::device(kit())) {
+ // rsync transfer on the same device currently not implemented
+ // and typically not wanted.
+ return CheckResult::failure(
+ Tr::tr("rsync is only supported for transfers between different devices."));
+ }
+ m_ignoreMissingFiles = ignoreMissingFiles->value();
+ m_flags = flags->value();
+ return isDeploymentPossible();
+ });
+
+ setRunPreparer([this] {
+ const QList<DeployableFile> files = target()->deploymentData().allFiles();
+ m_files.clear();
+ for (const DeployableFile &f : files)
+ m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())});
+ });
}
-bool RsyncDeployService::isDeploymentNecessary() const
+bool RsyncDeployStep::isDeploymentNecessary() const
{
if (m_ignoreMissingFiles)
Utils::erase(m_files, [](const FileToTransfer &file) { return !file.m_source.exists(); });
return !m_files.empty();
}
-TaskItem RsyncDeployService::mkdirTask()
+TaskItem RsyncDeployStep::mkdirTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
QStringList remoteDirs;
for (const FileToTransfer &file : std::as_const(m_files))
remoteDirs << file.m_target.parentDir().path();
@@ -67,11 +95,11 @@ TaskItem RsyncDeployService::mkdirTask()
remoteDirs.removeDuplicates();
process.setCommand({deviceConfiguration()->filePath("mkdir"),
QStringList("-p") + remoteDirs});
- connect(&process, &QtcProcess::readyReadStandardError, this, [this, proc = &process] {
- emit stdErrData(QString::fromLocal8Bit(proc->readAllRawStandardError()));
+ connect(&process, &Process::readyReadStandardError, this, [this, proc = &process] {
+ handleStdErrData(QString::fromLocal8Bit(proc->readAllRawStandardError()));
});
};
- const auto errorHandler = [this](const QtcProcess &process) {
+ const auto errorHandler = [this](const Process &process) {
QString finalMessage = process.errorString();
const QString stdErr = process.cleanedStdErr();
if (!stdErr.isEmpty()) {
@@ -79,87 +107,46 @@ TaskItem RsyncDeployService::mkdirTask()
finalMessage += '\n';
finalMessage += stdErr;
}
- emit errorMessage(Tr::tr("Deploy via rsync: failed to create remote directories:")
- + '\n' + finalMessage);
+ addErrorMessage(Tr::tr("Deploy via rsync: failed to create remote directories:")
+ + '\n' + finalMessage);
};
- return Process(setupHandler, {}, errorHandler);
+ return ProcessTask(setupHandler, {}, errorHandler);
}
-TaskItem RsyncDeployService::transferTask()
+TaskItem RsyncDeployStep::transferTask()
{
const auto setupHandler = [this](FileTransfer &transfer) {
transfer.setTransferMethod(FileTransferMethod::Rsync);
transfer.setRsyncFlags(m_flags);
transfer.setFilesToTransfer(m_files);
connect(&transfer, &FileTransfer::progress,
- this, &AbstractRemoteLinuxDeployService::stdOutData);
+ this, &AbstractRemoteLinuxDeployStep::handleStdOutData);
};
const auto errorHandler = [this](const FileTransfer &transfer) {
const ProcessResultData result = transfer.resultData();
if (result.m_error == QProcess::FailedToStart) {
- emit errorMessage(Tr::tr("rsync failed to start: %1").arg(result.m_errorString));
+ addErrorMessage(Tr::tr("rsync failed to start: %1").arg(result.m_errorString));
} else if (result.m_exitStatus == QProcess::CrashExit) {
- emit errorMessage(Tr::tr("rsync crashed."));
+ addErrorMessage(Tr::tr("rsync crashed."));
} else if (result.m_exitCode != 0) {
- emit errorMessage(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode)
- + "\n" + result.m_errorString);
+ addErrorMessage(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode)
+ + "\n" + result.m_errorString);
}
};
- return Transfer(setupHandler, {}, errorHandler);
+ return FileTransferTask(setupHandler, {}, errorHandler);
}
-Group RsyncDeployService::deployRecipe()
+Group RsyncDeployStep::deployRecipe()
{
return Group { mkdirTask(), transferTask() };
}
-// RsyncDeployStep
-
-RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
-{
- auto service = new RsyncDeployService;
- setDeployService(service);
-
- auto flags = addAspect<StringAspect>();
- flags->setDisplayStyle(StringAspect::LineEditDisplay);
- flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags");
- flags->setLabelText(Tr::tr("Flags:"));
- flags->setValue(FileTransferSetupData::defaultRsyncFlags());
-
- auto ignoreMissingFiles = addAspect<BoolAspect>();
- ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles");
- ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"),
- BoolAspect::LabelPlacement::InExtraLabel);
- ignoreMissingFiles->setValue(false);
-
- setInternalInitializer([this, service, flags, ignoreMissingFiles] {
- if (BuildDeviceKitAspect::device(kit()) == DeviceKitAspect::device(kit())) {
- // rsync transfer on the same device currently not implemented
- // and typically not wanted.
- return CheckResult::failure(
- Tr::tr("rsync is only supported for transfers between different devices."));
- }
- service->setIgnoreMissingFiles(ignoreMissingFiles->value());
- service->setFlags(flags->value());
- return service->isDeploymentPossible();
- });
-
- setRunPreparer([this, service] {
- service->setDeployableFiles(target()->deploymentData().allFiles());
- });
-}
-
-RsyncDeployStep::~RsyncDeployStep() = default;
-
-Utils::Id RsyncDeployStep::stepId()
-{
- return Constants::RsyncDeployStepId;
-}
+// Factory
-QString RsyncDeployStep::displayName()
+RsyncDeployStepFactory::RsyncDeployStepFactory()
{
- return Tr::tr("Deploy files via rsync");
+ registerStep<RsyncDeployStep>(Constants::RsyncDeployStepId);
+ setDisplayName(Tr::tr("Deploy files via rsync"));
}
} // RemoteLinux
diff --git a/src/plugins/remotelinux/rsyncdeploystep.h b/src/plugins/remotelinux/rsyncdeploystep.h
index a0f8790531..7450d84fcb 100644
--- a/src/plugins/remotelinux/rsyncdeploystep.h
+++ b/src/plugins/remotelinux/rsyncdeploystep.h
@@ -5,18 +5,15 @@
#include "remotelinux_export.h"
-#include "abstractremotelinuxdeploystep.h"
+#include <projectexplorer/buildstep.h>
namespace RemoteLinux {
-class REMOTELINUX_EXPORT RsyncDeployStep : public AbstractRemoteLinuxDeployStep
+class REMOTELINUX_EXPORT RsyncDeployStepFactory
+ : public ProjectExplorer::BuildStepFactory
{
public:
- RsyncDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
- ~RsyncDeployStep() override;
-
- static Utils::Id stepId();
- static QString displayName();
+ RsyncDeployStepFactory();
};
} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/sshkeycreationdialog.cpp b/src/plugins/remotelinux/sshkeycreationdialog.cpp
index e0128d2a2a..441edab889 100644
--- a/src/plugins/remotelinux/sshkeycreationdialog.cpp
+++ b/src/plugins/remotelinux/sshkeycreationdialog.cpp
@@ -10,7 +10,7 @@
#include <utils/fileutils.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QApplication>
#include <QComboBox>
@@ -109,7 +109,7 @@ void SshKeyCreationDialog::generateKeys()
}
const QString keyTypeString = QLatin1String(m_rsa->isChecked() ? "rsa": "ecdsa");
QApplication::setOverrideCursor(Qt::BusyCursor);
- QtcProcess keygen;
+ Process keygen;
const QStringList args{"-t", keyTypeString, "-b", m_comboBox->currentText(),
"-N", QString(), "-f", privateKeyFilePath().path()};
QString errorMsg;
diff --git a/src/plugins/remotelinux/sshprocessinterface.h b/src/plugins/remotelinux/sshprocessinterface.h
deleted file mode 100644
index 9d67d6a4e6..0000000000
--- a/src/plugins/remotelinux/sshprocessinterface.h
+++ /dev/null
@@ -1,46 +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 "remotelinux_export.h"
-
-#include <utils/processinterface.h>
-
-namespace RemoteLinux {
-
-class LinuxDevice;
-class SshProcessInterfacePrivate;
-
-class REMOTELINUX_EXPORT SshProcessInterface : public Utils::ProcessInterface
-{
-public:
- SshProcessInterface(const LinuxDevice *linuxDevice);
- ~SshProcessInterface();
-
-protected:
- void emitStarted(qint64 processId);
- // To be called from leaf destructor.
- // Can't call it from SshProcessInterface destructor as it calls virtual method.
- void killIfRunning();
- qint64 processId() const;
- bool runInShell(const Utils::CommandLine &command, const QByteArray &data = {});
-
-private:
- virtual void handleStarted(qint64 processId);
- virtual void handleDone(const Utils::ProcessResultData &resultData);
- virtual void handleReadyReadStandardOutput(const QByteArray &outputData);
- virtual void handleReadyReadStandardError(const QByteArray &errorData);
- virtual void handleSendControlSignal(Utils::ControlSignal controlSignal) = 0;
-
- virtual QString fullCommandLine(const Utils::CommandLine &commandLine) const = 0;
-
- void start() final;
- qint64 write(const QByteArray &data) final;
- void sendControlSignal(Utils::ControlSignal controlSignal) final;
-
- friend class SshProcessInterfacePrivate;
- SshProcessInterfacePrivate *d = nullptr;
-};
-
-} // namespace RemoteLinux
diff --git a/src/plugins/remotelinux/tarpackagecreationstep.cpp b/src/plugins/remotelinux/tarpackagecreationstep.cpp
index 0d53d36ff5..7c577812da 100644
--- a/src/plugins/remotelinux/tarpackagecreationstep.cpp
+++ b/src/plugins/remotelinux/tarpackagecreationstep.cpp
@@ -13,8 +13,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
+#include <utils/async.h>
#include <utils/futuresynchronizer.h>
-#include <utils/runextensions.h>
#include <QDateTime>
#include <QDir>
@@ -74,9 +74,9 @@ private:
bool isPackagingNeeded() const;
void deployFinished(bool success);
void addNeededDeploymentFiles(const DeployableFile &deployable, const Kit *kit);
- void doPackage(QFutureInterface<bool> &fi, const Utils::FilePath &tarFilePath,
+ void doPackage(QPromise<bool> &promise, const Utils::FilePath &tarFilePath,
bool ignoreMissingFiles);
- bool appendFile(QFutureInterface<bool> &fi, QFile &tarFile, const QFileInfo &fileInfo,
+ bool appendFile(QPromise<bool> &promise, QFile &tarFile, const QFileInfo &fileInfo,
const QString &remoteFilePath, const Utils::FilePath &tarFilePath,
bool ignoreMissingFiles);
@@ -94,7 +94,6 @@ private:
TarPackageCreationStep::TarPackageCreationStep(BuildStepList *bsl, Id id)
: BuildStep(bsl, id)
{
- m_synchronizer.setCancelOnWait(true);
connect(target(), &Target::deploymentDataChanged, this, [this] {
m_deploymentDataModified = true;
});
@@ -167,7 +166,7 @@ void TarPackageCreationStep::doRun()
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &TarPackageCreationStep::deployFinished);
});
- auto future = Utils::runAsync(&TarPackageCreationStep::doPackage, this,
+ auto future = Utils::asyncRun(&TarPackageCreationStep::doPackage, this,
m_tarFilePath, m_ignoreMissingFilesAspect->value());
watcher->setFuture(future);
m_synchronizer.addFuture(future);
@@ -271,7 +270,7 @@ void TarPackageCreationStep::addNeededDeploymentFiles(
}
}
-void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePath &tarFilePath,
+void TarPackageCreationStep::doPackage(QPromise<bool> &promise, const FilePath &tarFilePath,
bool ignoreMissingFiles)
{
// TODO: Optimization: Only package changed files
@@ -280,7 +279,7 @@ void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePat
if (!tarFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
raiseError(Tr::tr("Error: tar file %1 cannot be opened (%2).")
.arg(tarFilePath.toUserOutput(), tarFile.errorString()));
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
@@ -291,10 +290,10 @@ void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePat
continue;
}
QFileInfo fileInfo = d.localFilePath().toFileInfo();
- if (!appendFile(fi, tarFile, fileInfo,
+ if (!appendFile(promise, tarFile, fileInfo,
d.remoteDirectory() + QLatin1Char('/') + fileInfo.fileName(),
tarFilePath, ignoreMissingFiles)) {
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
}
@@ -303,10 +302,10 @@ void TarPackageCreationStep::doPackage(QFutureInterface<bool> &fi, const FilePat
if (tarFile.write(eofIndicator) != eofIndicator.length()) {
raiseError(Tr::tr("Error writing tar file \"%1\": %2.")
.arg(QDir::toNativeSeparators(tarFile.fileName()), tarFile.errorString()));
- fi.reportResult(false);
+ promise.addResult(false);
return;
}
- fi.reportResult(true);
+ promise.addResult(true);
}
static bool setFilePath(TarFileHeader &header, const QByteArray &filePath)
@@ -388,7 +387,7 @@ static bool writeHeader(QFile &tarFile, const QFileInfo &fileInfo, const QString
return true;
}
-bool TarPackageCreationStep::appendFile(QFutureInterface<bool> &fi,
+bool TarPackageCreationStep::appendFile(QPromise<bool> &promise,
QFile &tarFile,
const QFileInfo &fileInfo,
const QString &remoteFilePath,
@@ -406,7 +405,7 @@ bool TarPackageCreationStep::appendFile(QFutureInterface<bool> &fi,
for (const QString &fileName : files) {
const QString thisLocalFilePath = dir.path() + QLatin1Char('/') + fileName;
const QString thisRemoteFilePath = remoteFilePath + QLatin1Char('/') + fileName;
- if (!appendFile(fi, tarFile, QFileInfo(thisLocalFilePath), thisRemoteFilePath,
+ if (!appendFile(promise, tarFile, QFileInfo(thisLocalFilePath), thisRemoteFilePath,
tarFilePath, ignoreMissingFiles)) {
return false;
}
@@ -437,7 +436,7 @@ bool TarPackageCreationStep::appendFile(QFutureInterface<bool> &fi,
while (!file.atEnd() && file.error() == QFile::NoError && tarFile.error() == QFile::NoError) {
const QByteArray data = file.read(chunkSize);
tarFile.write(data);
- if (fi.isCanceled())
+ if (promise.isCanceled())
return false;
}
if (file.error() != QFile::NoError) {
diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp
index ee276666ac..6b50ca4dce 100644
--- a/src/plugins/remotelinux/tarpackagedeploystep.cpp
+++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp
@@ -12,19 +12,44 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
+using namespace Tasking;
using namespace Utils;
-using namespace Utils::Tasking;
namespace RemoteLinux::Internal {
-class TarPackageDeployService : public AbstractRemoteLinuxDeployService
+// TarPackageDeployStep
+
+class TarPackageDeployStep : public AbstractRemoteLinuxDeployStep
{
public:
- void setPackageFilePath(const FilePath &filePath);
+ TarPackageDeployStep(BuildStepList *bsl, Id id)
+ : AbstractRemoteLinuxDeployStep(bsl, id)
+ {
+ setWidgetExpandedByDefault(false);
+
+ setInternalInitializer([this] {
+ const BuildStep *tarCreationStep = nullptr;
+
+ for (BuildStep *step : deployConfiguration()->stepList()->steps()) {
+ if (step == this)
+ break;
+ if (step->id() == Constants::TarPackageCreationStepId) {
+ tarCreationStep = step;
+ break;
+ }
+ }
+ if (!tarCreationStep)
+ return CheckResult::failure(Tr::tr("No tarball creation step found."));
+
+ m_packageFilePath =
+ FilePath::fromVariant(tarCreationStep->data(Constants::TarPackageFilePathId));
+ return isDeploymentPossible();
+ });
+ }
private:
QString remoteFilePath() const;
@@ -36,106 +61,65 @@ private:
FilePath m_packageFilePath;
};
-void TarPackageDeployService::setPackageFilePath(const FilePath &filePath)
-{
- m_packageFilePath = filePath;
-}
-
-QString TarPackageDeployService::remoteFilePath() const
+QString TarPackageDeployStep::remoteFilePath() const
{
return QLatin1String("/tmp/") + m_packageFilePath.fileName();
}
-bool TarPackageDeployService::isDeploymentNecessary() const
+bool TarPackageDeployStep::isDeploymentNecessary() const
{
return hasLocalFileChanged(DeployableFile(m_packageFilePath, {}));
}
-TaskItem TarPackageDeployService::uploadTask()
+TaskItem TarPackageDeployStep::uploadTask()
{
const auto setupHandler = [this](FileTransfer &transfer) {
const FilesToTransfer files {{m_packageFilePath,
deviceConfiguration()->filePath(remoteFilePath())}};
transfer.setFilesToTransfer(files);
- connect(&transfer, &FileTransfer::progress,
- this, &TarPackageDeployService::progressMessage);
- emit progressMessage(Tr::tr("Uploading package to device..."));
+ connect(&transfer, &FileTransfer::progress, this, &TarPackageDeployStep::addProgressMessage);
+ addProgressMessage(Tr::tr("Uploading package to device..."));
};
const auto doneHandler = [this](const FileTransfer &) {
- emit progressMessage(Tr::tr("Successfully uploaded package file."));
+ addProgressMessage(Tr::tr("Successfully uploaded package file."));
};
const auto errorHandler = [this](const FileTransfer &transfer) {
const ProcessResultData result = transfer.resultData();
- emit errorMessage(result.m_errorString);
+ addErrorMessage(result.m_errorString);
};
- return Transfer(setupHandler, doneHandler, errorHandler);
+ return FileTransferTask(setupHandler, doneHandler, errorHandler);
}
-TaskItem TarPackageDeployService::installTask()
+TaskItem TarPackageDeployStep::installTask()
{
- const auto setupHandler = [this](QtcProcess &process) {
+ const auto setupHandler = [this](Process &process) {
const QString cmdLine = QLatin1String("cd / && tar xvf ") + remoteFilePath()
+ " && (rm " + remoteFilePath() + " || :)";
process.setCommand({deviceConfiguration()->filePath("/bin/sh"), {"-c", cmdLine}});
- QtcProcess *proc = &process;
- connect(proc, &QtcProcess::readyReadStandardOutput, this, [this, proc] {
- emit stdOutData(proc->readAllStandardOutput());
+ Process *proc = &process;
+ connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
+ handleStdOutData(proc->readAllStandardOutput());
});
- connect(proc, &QtcProcess::readyReadStandardError, this, [this, proc] {
- emit stdErrData(proc->readAllStandardError());
+ connect(proc, &Process::readyReadStandardError, this, [this, proc] {
+ handleStdErrData(proc->readAllStandardError());
});
- emit progressMessage(Tr::tr("Installing package to device..."));
+ addProgressMessage(Tr::tr("Installing package to device..."));
};
- const auto doneHandler = [this](const QtcProcess &) {
+ const auto doneHandler = [this](const Process &) {
saveDeploymentTimeStamp(DeployableFile(m_packageFilePath, {}), {});
- emit progressMessage(Tr::tr("Successfully installed package file."));
+ addProgressMessage(Tr::tr("Successfully installed package file."));
};
- const auto errorHandler = [this](const QtcProcess &process) {
- emit errorMessage(Tr::tr("Installing package failed.") + process.errorString());
+ const auto errorHandler = [this](const Process &process) {
+ addErrorMessage(Tr::tr("Installing package failed.") + process.errorString());
};
- return Process(setupHandler, doneHandler, errorHandler);
+ return ProcessTask(setupHandler, doneHandler, errorHandler);
}
-Group TarPackageDeployService::deployRecipe()
+Group TarPackageDeployStep::deployRecipe()
{
return Group { uploadTask(), installTask() };
}
-// TarPackageDeployStep
-
-class TarPackageDeployStep : public AbstractRemoteLinuxDeployStep
-{
-public:
- TarPackageDeployStep(BuildStepList *bsl, Id id)
- : AbstractRemoteLinuxDeployStep(bsl, id)
- {
- auto service = new TarPackageDeployService;
- setDeployService(service);
-
- setWidgetExpandedByDefault(false);
-
- setInternalInitializer([this, service] {
- const BuildStep *tarCreationStep = nullptr;
-
- for (BuildStep *step : deployConfiguration()->stepList()->steps()) {
- if (step == this)
- break;
- if (step->id() == Constants::TarPackageCreationStepId) {
- tarCreationStep = step;
- break;
- }
- }
- if (!tarCreationStep)
- return CheckResult::failure(Tr::tr("No tarball creation step found."));
-
- const FilePath tarFile =
- FilePath::fromVariant(tarCreationStep->data(Constants::TarPackageFilePathId));
- service->setPackageFilePath(tarFile);
- return service->isDeploymentPossible();
- });
- }
-};
-
// TarPackageDeployStepFactory
diff --git a/src/plugins/resourceeditor/qrceditor/qrceditor.cpp b/src/plugins/resourceeditor/qrceditor/qrceditor.cpp
index 86679c52b7..e08f52340f 100644
--- a/src/plugins/resourceeditor/qrceditor/qrceditor.cpp
+++ b/src/plugins/resourceeditor/qrceditor/qrceditor.cpp
@@ -43,7 +43,7 @@ QrcEditor::QrcEditor(RelativeResourceModel *model, QWidget *parent)
m_languageLabel = new QLabel(Tr::tr("Language:"));
m_languageText = new QLineEdit;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
addPrefixButton,
diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp
index fa0e3b5e4a..d402e3ed89 100644
--- a/src/plugins/resourceeditor/resourcenode.cpp
+++ b/src/plugins/resourceeditor/resourcenode.cpp
@@ -268,9 +268,7 @@ static void compressTree(FolderNode *n)
compressable->compress();
return;
}
- const QList<FolderNode *> childFolders = n->folderNodes();
- for (FolderNode * const c : childFolders)
- compressTree(c);
+ n->forEachFolderNode([](FolderNode *c) { compressTree(c); });
}
void ResourceTopLevelNode::addInternalNodes()
diff --git a/src/plugins/scxmleditor/CMakeLists.txt b/src/plugins/scxmleditor/CMakeLists.txt
index fef49a9082..358c407233 100644
--- a/src/plugins/scxmleditor/CMakeLists.txt
+++ b/src/plugins/scxmleditor/CMakeLists.txt
@@ -42,6 +42,7 @@ add_qtc_plugin(ScxmlEditor
plugin_interface/baseitem.cpp plugin_interface/baseitem.h
plugin_interface/connectableitem.cpp plugin_interface/connectableitem.h
plugin_interface/cornergrabberitem.cpp plugin_interface/cornergrabberitem.h
+ plugin_interface/eventitem.cpp plugin_interface/eventitem.h
plugin_interface/finalstateitem.cpp plugin_interface/finalstateitem.h
plugin_interface/genericscxmlplugin.cpp plugin_interface/genericscxmlplugin.h
plugin_interface/graphicsitemprovider.h
diff --git a/src/plugins/scxmleditor/common/colorpicker.cpp b/src/plugins/scxmleditor/common/colorpicker.cpp
index 04e415224b..88f4d6d22a 100644
--- a/src/plugins/scxmleditor/common/colorpicker.cpp
+++ b/src/plugins/scxmleditor/common/colorpicker.cpp
@@ -34,8 +34,8 @@ ColorPicker::ColorPicker(const QString &key, QWidget *parent)
m_lastUsedColorContainer = new QHBoxLayout(lastUsedColorContainer);
m_lastUsedColorContainer->setContentsMargins(0, 0, 0, 0);
- using namespace Utils::Layouting;
- Grid colorGrid;
+ using namespace Layouting;
+ Grid colorGrid{noMargin};
for (int i = 0; i < colors.count(); ++i) {
QWidget *button = createButton(colors[i]);
colorGrid.addItem(button);
@@ -46,7 +46,7 @@ ColorPicker::ColorPicker(const QString &key, QWidget *parent)
QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred));
}
- colorGrid.attachTo(basicColorContentFrame, WithoutMargins);
+ colorGrid.attachTo(basicColorContentFrame);
Column {
Tr::tr("Basic Colors"),
basicColorContentFrame,
diff --git a/src/plugins/scxmleditor/common/colorsettings.cpp b/src/plugins/scxmleditor/common/colorsettings.cpp
index c2ad9f22ce..1419b481bc 100644
--- a/src/plugins/scxmleditor/common/colorsettings.cpp
+++ b/src/plugins/scxmleditor/common/colorsettings.cpp
@@ -36,7 +36,7 @@ ColorSettings::ColorSettings(QWidget *parent)
s->value(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME).toString());
selectTheme(m_comboColorThemes->currentIndex());
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row {
m_comboColorThemes,
@@ -44,7 +44,8 @@ ColorSettings::ColorSettings(QWidget *parent)
removeTheme,
},
m_colorThemeView,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(m_comboColorThemes, &QComboBox::currentIndexChanged,
this, &ColorSettings::selectTheme);
diff --git a/src/plugins/scxmleditor/common/colorthemedialog.cpp b/src/plugins/scxmleditor/common/colorthemedialog.cpp
index 00f224d1f9..2f2b209b77 100644
--- a/src/plugins/scxmleditor/common/colorthemedialog.cpp
+++ b/src/plugins/scxmleditor/common/colorthemedialog.cpp
@@ -21,7 +21,7 @@ ColorThemeDialog::ColorThemeDialog(QWidget *parent)
QDialogButtonBox::Cancel |
QDialogButtonBox::Apply);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_colorSettings,
buttonBox,
diff --git a/src/plugins/scxmleditor/common/navigatorslider.cpp b/src/plugins/scxmleditor/common/navigatorslider.cpp
index 5f3fc345e1..ab3738bc06 100644
--- a/src/plugins/scxmleditor/common/navigatorslider.cpp
+++ b/src/plugins/scxmleditor/common/navigatorslider.cpp
@@ -29,13 +29,15 @@ NavigatorSlider::NavigatorSlider(QWidget *parent)
btn->setAutoRepeatInterval(10);
}
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
+ spacing(0),
zoomOut,
m_slider,
zoomIn,
Space(20),
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
connect(zoomOut, &QToolButton::clicked, this, &NavigatorSlider::zoomOut);
connect(zoomIn, &QToolButton::clicked, this, &NavigatorSlider::zoomIn);
diff --git a/src/plugins/scxmleditor/common/search.cpp b/src/plugins/scxmleditor/common/search.cpp
index acfc5cda13..f4d7e4bbc3 100644
--- a/src/plugins/scxmleditor/common/search.cpp
+++ b/src/plugins/scxmleditor/common/search.cpp
@@ -45,11 +45,13 @@ Search::Search(QWidget *parent)
m_searchView->setModel(m_proxyModel);
m_searchView->setFrameShape(QFrame::NoFrame);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
+ spacing(0),
m_searchEdit,
m_searchView,
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
connect(m_searchEdit, &Utils::FancyLineEdit::textChanged, this, &Search::setSearchText);
connect(m_searchView, &TableView::pressed, this, &Search::rowActivated);
diff --git a/src/plugins/scxmleditor/common/shapestoolbox.cpp b/src/plugins/scxmleditor/common/shapestoolbox.cpp
index 988c762c34..82def88379 100644
--- a/src/plugins/scxmleditor/common/shapestoolbox.cpp
+++ b/src/plugins/scxmleditor/common/shapestoolbox.cpp
@@ -29,10 +29,12 @@ ShapesToolbox::ShapesToolbox(QWidget *parent)
m_shapeGroupsLayout->setContentsMargins(0, 0, 0, 0);
m_shapeGroupsLayout->setSpacing(0);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
+ spacing(0),
scrollArea,
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
}
void ShapesToolbox::setUIFactory(ScxmlEditor::PluginInterface::ScxmlUiFactory *factory)
diff --git a/src/plugins/scxmleditor/common/stateproperties.cpp b/src/plugins/scxmleditor/common/stateproperties.cpp
index 32d6e43f64..f7a884837e 100644
--- a/src/plugins/scxmleditor/common/stateproperties.cpp
+++ b/src/plugins/scxmleditor/common/stateproperties.cpp
@@ -106,7 +106,6 @@ void StateProperties::createUi()
m_currentTagName = new QLabel;
auto propertiesToolBar = new QToolBar;
- propertiesToolBar->setMinimumHeight(24);
propertiesToolBar->addWidget(titleLabel);
propertiesToolBar->addWidget(m_currentTagName);
diff --git a/src/plugins/scxmleditor/common/stateview.cpp b/src/plugins/scxmleditor/common/stateview.cpp
index 1ee9903441..517f53bd3e 100644
--- a/src/plugins/scxmleditor/common/stateview.cpp
+++ b/src/plugins/scxmleditor/common/stateview.cpp
@@ -31,15 +31,19 @@ StateView::StateView(StateItem *state, QWidget *parent)
m_graphicsView = new GraphicsView;
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
PushButton{ text("Back"), onClicked([this] { closeView(); }, this) },
stateNameLabel,
- }.attachTo(titleBar, WithoutMargins);
+ noMargin
+ }.attachTo(titleBar);
Column {
- titleBar, m_graphicsView
- }.setSpacing(0).attachTo(this, WithoutMargins);
+ spacing(0),
+ titleBar,
+ m_graphicsView,
+ noMargin,
+ }.attachTo(this);
initScene();
}
diff --git a/src/plugins/scxmleditor/common/statistics.cpp b/src/plugins/scxmleditor/common/statistics.cpp
index 06c62f70b2..cff7d337c3 100644
--- a/src/plugins/scxmleditor/common/statistics.cpp
+++ b/src/plugins/scxmleditor/common/statistics.cpp
@@ -135,13 +135,14 @@ Statistics::Statistics(QWidget *parent)
m_statisticsView->setAlternatingRowColors(true);
m_statisticsView->setSortingEnabled(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Grid {
Tr::tr("File"), m_fileNameLabel, br,
Tr::tr("Time"), m_timeLabel, br,
Tr::tr("Max. levels"), m_levels, br,
- Span(2, m_statisticsView), br
- }.attachTo(this, WithoutMargins);
+ Span(2, m_statisticsView), br,
+ noMargin
+ }.attachTo(this);
}
void Statistics::setDocument(ScxmlDocument *doc)
diff --git a/src/plugins/scxmleditor/common/statisticsdialog.cpp b/src/plugins/scxmleditor/common/statisticsdialog.cpp
index 7cfb6cd463..d9a20dd81d 100644
--- a/src/plugins/scxmleditor/common/statisticsdialog.cpp
+++ b/src/plugins/scxmleditor/common/statisticsdialog.cpp
@@ -21,7 +21,7 @@ StatisticsDialog::StatisticsDialog(QWidget *parent)
m_statistics = new Statistics;
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_statistics,
buttonBox,
diff --git a/src/plugins/scxmleditor/plugin_interface/baseitem.cpp b/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
index 861094321a..7d87a68f91 100644
--- a/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/baseitem.cpp
@@ -41,7 +41,8 @@ BaseItem::~BaseItem()
void BaseItem::checkParentBoundingRect()
{
BaseItem *parentBaseItem = this->parentBaseItem();
- if (parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates()) {
+ if ((parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates())
+ || (parentBaseItem && type() == StateWarningType)) {
auto parentStateItem = qgraphicsitem_cast<StateItem*>(parentBaseItem);
if (parentStateItem && (parentStateItem->type() >= StateType))
parentStateItem->updateBoundingRect();
diff --git a/src/plugins/scxmleditor/plugin_interface/baseitem.h b/src/plugins/scxmleditor/plugin_interface/baseitem.h
index 5c992c632b..42f09ca389 100644
--- a/src/plugins/scxmleditor/plugin_interface/baseitem.h
+++ b/src/plugins/scxmleditor/plugin_interface/baseitem.h
@@ -93,6 +93,7 @@ public:
ScxmlUiFactory *uiFactory() const;
virtual void updateUIProperties();
+ virtual void addChild(ScxmlTag */*tag*/) {};
protected:
virtual void updatePolygon();
diff --git a/src/plugins/scxmleditor/plugin_interface/eventitem.cpp b/src/plugins/scxmleditor/plugin_interface/eventitem.cpp
new file mode 100644
index 0000000000..f61517f7cf
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/eventitem.cpp
@@ -0,0 +1,108 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "eventitem.h"
+
+#include <QColor>
+#include <QFont>
+#include <QGraphicsItem>
+#include <QList>
+#include <QString>
+
+namespace ScxmlEditor {
+
+namespace PluginInterface {
+
+EventItem::EventItem(const QPointF &pos, BaseItem *parent)
+ : BaseItem(parent)
+{
+ m_eventNameItem = new TextItem(this);
+ m_eventNameItem->setParentItem(this);
+ QFont serifFont("Times", 13, QFont::Normal);
+ m_eventNameItem->setFont(serifFont);
+
+ QString color = editorInfo("fontColor");
+ m_eventNameItem->setDefaultTextColor(color.isEmpty() ? QColor(Qt::black) : QColor(color));
+
+ setPos(pos);
+ m_eventNameItem->setTextInteractionFlags(Qt::NoTextInteraction);
+ setItemBoundingRect(m_eventNameItem->boundingRect());
+}
+
+void EventItem::updateAttributes()
+{
+ QString text = " " + tag()->tagName();
+ if (tag()->attributeNames().size() > 0) {
+ for (int i = 0; i < tag()->attributeNames().size(); ++i)
+ if (tag()->attributeNames().at(i) == "event") {
+ if (tag()->attributeValues().size() > i)
+ text += " / " + tag()->attributeValues().at(i);
+ break;
+ }
+ }
+ m_eventNameItem->setText(text);
+ setItemBoundingRect(m_eventNameItem->boundingRect());
+}
+
+OnEntryExitItem::OnEntryExitItem(BaseItem *parent)
+ : BaseItem(parent)
+{
+ m_eventNameItem = new TextItem(this);
+ m_eventNameItem->setParentItem(this);
+ QFont serifFont("Times", 13, QFont::Normal);
+ m_eventNameItem->setFont(serifFont);
+ m_eventNameItem->setDefaultTextColor(Qt::black);
+ m_eventNameItem->setTextInteractionFlags(Qt::NoTextInteraction);
+}
+
+void OnEntryExitItem::updateAttributes()
+{
+ QString text = tag()->tagName();
+
+ m_eventNameItem->setText(text);
+ setItemBoundingRect(childBoundingRect());
+ checkParentBoundingRect();
+}
+
+void OnEntryExitItem::finalizeCreation()
+{
+ auto children = tag()->allChildren();
+ auto pos = m_eventNameItem->boundingRect().bottomLeft();
+ for (auto child : children) {
+ EventItem *item = new EventItem(pos, this);
+ item->setTag(child);
+ item->updateAttributes();
+ pos = item->pos() + item->boundingRect().bottomLeft();
+ }
+
+ setItemBoundingRect(childBoundingRect());
+}
+
+void OnEntryExitItem::addChild(ScxmlTag *tag)
+{
+ auto pos = childBoundingRect().bottomLeft();
+ EventItem *item = new EventItem(pos, this);
+ item->setTag(tag);
+ item->updateAttributes();
+
+ setItemBoundingRect(childBoundingRect());
+ checkParentBoundingRect();
+}
+
+QRectF OnEntryExitItem::childBoundingRect() const
+{
+ QRectF r = m_eventNameItem->boundingRect();
+
+ const QList<QGraphicsItem *> children = childItems();
+
+ for (const QGraphicsItem *child : children) {
+ QRectF br = child->boundingRect();
+ QPointF p = child->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+ return r;
+}
+
+} // namespace PluginInterface
+} // namespace ScxmlEditor
diff --git a/src/plugins/scxmleditor/plugin_interface/eventitem.h b/src/plugins/scxmleditor/plugin_interface/eventitem.h
new file mode 100644
index 0000000000..cf8ff3a9e3
--- /dev/null
+++ b/src/plugins/scxmleditor/plugin_interface/eventitem.h
@@ -0,0 +1,41 @@
+// 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 "baseitem.h"
+#include "textitem.h"
+
+namespace ScxmlEditor::PluginInterface {
+
+class EventItem : public BaseItem
+{
+public:
+ explicit EventItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
+
+ void updateAttributes() override;
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
+
+private:
+ TextItem *m_eventNameItem;
+};
+
+class OnEntryExitItem : public BaseItem
+{
+public:
+ explicit OnEntryExitItem(BaseItem *parent = nullptr);
+
+ int type() const override { return StateWarningType; }
+
+ void updateAttributes() override;
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
+ void finalizeCreation() override;
+ void addChild(ScxmlTag *tag) override;
+
+ QRectF childBoundingRect() const;
+
+private:
+ TextItem *m_eventNameItem;
+};
+
+} // namespace ScxmlEditor::PluginInterface
diff --git a/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp b/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
index fb0e8252db..94bd8b41ed 100644
--- a/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/graphicsscene.cpp
@@ -467,8 +467,7 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
break;
}
case ScxmlDocument::TagChangeParent: {
- auto childItem = qobject_cast<ConnectableItem*>(findItem(tag));
-
+ auto childItem = findItem(tag);
if (childItem) {
QTC_ASSERT(tag, break);
BaseItem *newParentItem = findItem(tag->parentTag());
@@ -485,8 +484,11 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
childItem->setParentItem(newParentItem);
childItem->updateUIProperties();
- childItem->updateTransitions(true);
- childItem->updateTransitionAttributes(true);
+ if (auto childConItem = qobject_cast<ConnectableItem*>(findItem(tag))) {
+ childConItem->updateTransitions(true);
+ childConItem->updateTransitionAttributes(true);
+ }
+
childItem->checkWarnings();
childItem->checkInitial();
if (newParentItem) {
@@ -495,6 +497,8 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
newParentItem->checkWarnings();
newParentItem->checkOverlapping();
newParentItem->updateUIProperties();
+ if (auto newConItem = qobject_cast<StateItem*>(newParentItem))
+ newConItem->updateBoundingRect();
}
if (oldParentItem)
@@ -549,6 +553,9 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
}
if (parentItem) {
+ if (childItem == nullptr)
+ parentItem->addChild(childTag);
+
parentItem->updateAttributes();
parentItem->updateUIProperties();
parentItem->checkInitial();
diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
index dd1015f9d4..e53bd8c894 100644
--- a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
+++ b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp
@@ -1,19 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "finalstateitem.h"
+#include "stateitem.h"
+
+#include "eventitem.h"
#include "graphicsitemprovider.h"
#include "graphicsscene.h"
#include "idwarningitem.h"
#include "imageprovider.h"
-#include "initialstateitem.h"
-#include "parallelitem.h"
#include "sceneutils.h"
#include "scxmleditorconstants.h"
#include "scxmleditortr.h"
#include "scxmltagutils.h"
#include "scxmluifactory.h"
-#include "stateitem.h"
#include "statewarningitem.h"
#include "textitem.h"
#include "transitionitem.h"
@@ -28,6 +27,7 @@
#include <QTextOption>
#include <QUndoStack>
#include <QtMath>
+#include <QRubberBand>
using namespace ScxmlEditor::PluginInterface;
@@ -171,6 +171,7 @@ void StateItem::updateBoundingRect()
// Check if we need to increase parent boundingrect
if (!r2.isNull()) {
+ positionOnExitItems();
QRectF r = boundingRect();
QRectF r3 = r.united(r2);
@@ -244,7 +245,6 @@ void StateItem::transitionCountChanged()
QRectF StateItem::childItemsBoundingRect() const
{
QRectF r;
- QRectF rr = boundingRect();
QList<QGraphicsItem*> children = childItems();
for (int i = 0; i < children.count(); ++i) {
@@ -256,15 +256,26 @@ QRectF StateItem::childItemsBoundingRect() const
}
}
+ if (m_onEntryItem) {
+ QRectF br = m_onEntryItem->childBoundingRect();
+ QPointF p = m_onEntryItem->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+
+ if (m_onExitItem) {
+ QRectF br = m_onExitItem->childBoundingRect();
+ QPointF p = m_onExitItem->pos() + br.topLeft();
+ br.moveTopLeft(p);
+ r = r.united(br);
+ }
+
if (m_transitionRect.isValid()) {
r.setLeft(r.left() - m_transitionRect.width());
r.setHeight(qMax(r.height(), m_transitionRect.height()));
r.moveBottom(qMax(r.bottom(), m_transitionRect.bottom()));
}
- if (!r.isNull())
- r.adjust(-20, -(rr.height() * 0.06 + 40), 20, 20);
-
return r;
}
@@ -418,11 +429,18 @@ void StateItem::updatePolygon()
<< m_drawingRect.bottomLeft()
<< m_drawingRect.topLeft();
- m_titleRect = QRectF(m_drawingRect.left(), m_drawingRect.top(), m_drawingRect.width(), TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
+ m_titleRect = QRectF(m_drawingRect.left(),
+ m_drawingRect.top(),
+ m_drawingRect.width(),
+ TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
QFont f = m_stateNameItem->font();
f.setPixelSize(m_titleRect.height() * 0.65);
m_stateNameItem->setFont(f);
+ if (m_onEntryItem)
+ m_onEntryItem->setPos(m_titleRect.x(), m_titleRect.bottom());
+ positionOnExitItems();
+
updateTextPositions();
}
@@ -517,13 +535,41 @@ void StateItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, boo
if (newItem) {
newItem->init(child, this, initChildren, blockUpdates);
newItem->finalizeCreation();
- }
+ } else
+ addChild(child);
}
}
if (blockUpdates)
setBlockUpdates(false);
}
+
+void StateItem::addChild(ScxmlTag *child)
+{
+ if (child->tagName() == "onentry") {
+ OnEntryExitItem *item = new OnEntryExitItem(this);
+ m_onEntryItem = item;
+ item->setTag(child);
+ item->finalizeCreation();
+ item->updateAttributes();
+ m_onEntryItem->setPos(m_titleRect.x(), m_titleRect.bottom());
+ } else if (child->tagName() == "onexit") {
+ OnEntryExitItem *item = new OnEntryExitItem(this);
+ m_onExitItem = item;
+ item->setTag(child);
+ item->finalizeCreation();
+ item->updateAttributes();
+ positionOnExitItems();
+ }
+}
+
+void StateItem::positionOnExitItems()
+{
+ int offset = m_onEntryItem ? m_onEntryItem->boundingRect().height() : 0;
+ if (m_onExitItem)
+ m_onExitItem->setPos(m_titleRect.x(), m_titleRect.bottom() + offset);
+}
+
QString StateItem::itemId() const
{
return m_stateNameItem ? m_stateNameItem->toPlainText() : QString();
diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.h b/src/plugins/scxmleditor/plugin_interface/stateitem.h
index 585992f040..beaebe9ebd 100644
--- a/src/plugins/scxmleditor/plugin_interface/stateitem.h
+++ b/src/plugins/scxmleditor/plugin_interface/stateitem.h
@@ -4,7 +4,9 @@
#pragma once
#include "connectableitem.h"
+#include "textitem.h"
#include <QPen>
+#include <QStyleOptionGraphicsItem>
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
@@ -16,6 +18,7 @@ class TransitionItem;
class TextItem;
class IdWarningItem;
class StateWarningItem;
+class OnEntryExitItem;
/**
* @brief The StateItem class represents the SCXML-State.
@@ -49,6 +52,8 @@ public:
QRectF childItemsBoundingRect() const;
void connectToParent(BaseItem *parentItem) override;
+ void addChild(ScxmlTag *child) override;
+
protected:
void updatePolygon() override;
void transitionsChanged() override;
@@ -69,6 +74,7 @@ private:
void updateTextPositions();
void checkParentBoundingRect();
void checkWarningItems();
+ void positionOnExitItems();
TextItem *m_stateNameItem;
StateWarningItem *m_stateWarningItem = nullptr;
@@ -76,6 +82,8 @@ private:
QPen m_pen;
bool m_initial = false;
bool m_parallelState = false;
+ QPointer<OnEntryExitItem> m_onEntryItem;
+ QPointer<OnEntryExitItem> m_onExitItem;
QImage m_backgroundImage;
};
diff --git a/src/plugins/scxmleditor/scxmleditor.qbs b/src/plugins/scxmleditor/scxmleditor.qbs
index c110b92f28..39340d8764 100644
--- a/src/plugins/scxmleditor/scxmleditor.qbs
+++ b/src/plugins/scxmleditor/scxmleditor.qbs
@@ -93,6 +93,7 @@ QtcPlugin {
"baseitem.cpp", "baseitem.h",
"connectableitem.cpp", "connectableitem.h",
"cornergrabberitem.cpp", "cornergrabberitem.h",
+ "eventitem.cpp", "eventitem.h",
"finalstateitem.cpp", "finalstateitem.h",
"genericscxmlplugin.cpp", "genericscxmlplugin.h",
"graphicsitemprovider.h",
diff --git a/src/plugins/scxmleditor/scxmleditordocument.cpp b/src/plugins/scxmleditor/scxmleditordocument.cpp
index 9c84910b2d..e10c875908 100644
--- a/src/plugins/scxmleditor/scxmleditordocument.cpp
+++ b/src/plugins/scxmleditor/scxmleditordocument.cpp
@@ -6,7 +6,7 @@
#include "scxmleditorconstants.h"
#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp
index b2887cd0a9..52e9f08a95 100644
--- a/src/plugins/silversearcher/findinfilessilversearcher.cpp
+++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp
@@ -4,14 +4,13 @@
#include "findinfilessilversearcher.h"
#include <aggregation/aggregate.h>
-#include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/findinfiles.h>
#include <utils/algorithm.h>
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include "silversearcheroutputparser.h"
#include "silversearchertr.h"
@@ -28,8 +27,6 @@ using namespace Utils;
namespace {
const QLatin1String silverSearcherName("Silver Searcher");
-using FutureInterfaceType = QFutureInterface<FileSearchResultList>;
-
const QString metacharacters = "+()^$.{}[]|\\";
const QString SearchOptionsString = "SearchOptionsString";
@@ -65,7 +62,7 @@ QString convertWildcardToRegex(const QString &wildcard)
bool isSilverSearcherAvailable()
{
- QtcProcess silverSearcherProcess;
+ Process silverSearcherProcess;
silverSearcherProcess.setCommand({"ag", {"--version"}});
silverSearcherProcess.start();
if (silverSearcherProcess.waitForFinished(1000)) {
@@ -76,9 +73,8 @@ bool isSilverSearcherAvailable()
return false;
}
-void runSilverSeacher(FutureInterfaceType &fi, FileFindParameters parameters)
+void runSilverSeacher(QPromise<SearchResultItems> &promise, FileFindParameters parameters)
{
- ProgressTimer progress(fi, 5);
const FilePath directory = FilePath::fromUserInput(parameters.additionalParameters.toString());
QStringList arguments = {"--parallel", "--ackmate"};
@@ -110,25 +106,25 @@ void runSilverSeacher(FutureInterfaceType &fi, FileFindParameters parameters)
arguments << "--" << parameters.text << directory.normalizedPathName().toString();
- QtcProcess process;
+ Process process;
process.setCommand({"ag", arguments});
process.start();
if (process.waitForFinished()) {
- QRegularExpression regexp;
+ std::optional<QRegularExpression> regExp;
if (parameters.flags & FindRegularExpression) {
+ regExp = QRegularExpression();
const QRegularExpression::PatternOptions patternOptions
= (parameters.flags & FindCaseSensitively)
? QRegularExpression::NoPatternOption
: QRegularExpression::CaseInsensitiveOption;
- regexp.setPattern(parameters.text);
- regexp.setPatternOptions(patternOptions);
+ regExp->setPattern(parameters.text);
+ regExp->setPatternOptions(patternOptions);
}
- SilverSearcher::SilverSearcherOutputParser parser(process.cleanedStdOut(), regexp);
- FileSearchResultList items = parser.parse();
+ const SearchResultItems items = SilverSearcher::parse(process.cleanedStdOut(), regExp);
if (!items.isEmpty())
- fi.reportResult(items);
+ promise.addResult(items);
} else {
- fi.reportCanceled();
+ promise.future().cancel();
}
}
@@ -193,10 +189,10 @@ void FindInFilesSilverSearcher::writeSettings(QSettings *settings) const
settings->setValue(SearchOptionsString, m_searchOptionsLineEdit->text());
}
-QFuture<FileSearchResultList> FindInFilesSilverSearcher::executeSearch(
+QFuture<SearchResultItems> FindInFilesSilverSearcher::executeSearch(
const FileFindParameters &parameters, BaseFileFind * /*baseFileFind*/)
{
- return Utils::runAsync(runSilverSeacher, parameters);
+ return Utils::asyncRun(runSilverSeacher, parameters);
}
IEditor *FindInFilesSilverSearcher::openEditor(const SearchResultItem & /*item*/,
diff --git a/src/plugins/silversearcher/findinfilessilversearcher.h b/src/plugins/silversearcher/findinfilessilversearcher.h
index 3a5363d713..85b35192b8 100644
--- a/src/plugins/silversearcher/findinfilessilversearcher.h
+++ b/src/plugins/silversearcher/findinfilessilversearcher.h
@@ -31,9 +31,9 @@ public:
QVariant parameters() const override;
void readSettings(QSettings *settings) override;
void writeSettings(QSettings *settings) const override;
- QFuture<Utils::FileSearchResultList> executeSearch(
+ QFuture<Utils::SearchResultItems> executeSearch(
const TextEditor::FileFindParameters &parameters, TextEditor::BaseFileFind *) override;
- Core::IEditor *openEditor(const Core::SearchResultItem &item,
+ Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;
private:
diff --git a/src/plugins/silversearcher/outputparser_test.cpp b/src/plugins/silversearcher/outputparser_test.cpp
index a9185b3156..4aaacf37e6 100644
--- a/src/plugins/silversearcher/outputparser_test.cpp
+++ b/src/plugins/silversearcher/outputparser_test.cpp
@@ -11,47 +11,56 @@ using namespace Utils;
namespace SilverSearcher {
namespace Internal {
+SearchResultItem searchResult(const FilePath &fileName, const QString &matchingLine,
+ int lineNumber, int matchStart, int matchLength)
+{
+ SearchResultItem result;
+ result.setFilePath(fileName);
+ result.setLineText(matchingLine);
+ result.setMainRange(lineNumber, matchStart, matchLength);
+ return result;
+}
+
void OutputParserTest::test_data()
{
QTest::addColumn<QString>("parserOutput");
- QTest::addColumn<FileSearchResultList>("results");
+ QTest::addColumn<SearchResultItems>("results");
- QTest::addRow("nothing") << QString("\n") << FileSearchResultList();
+ QTest::addRow("nothing") << QString("\n") << SearchResultItems();
QTest::addRow("oneFileOneMatch")
- << QString(":/file/path/to/filename.h\n"
- "1;1 5:match\n")
- << FileSearchResultList({{"/file/path/to/filename.h", 1, "match", 1, 5, {}}});
+ << QString(":/file/path/to/filename.h\n"
+ "1;1 5:match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename.h", "match", 1, 1, 5)};
QTest::addRow("multipleFilesWithOneMatch")
- << QString(":/file/path/to/filename1.h\n"
- "1;1 5:match\n"
- "\n"
- ":/file/path/to/filename2.h\n"
- "2;2 5: match\n")
- << FileSearchResultList({{"/file/path/to/filename1.h", 1, "match", 1, 5, {}},
- {"/file/path/to/filename2.h", 2, " match", 2, 5, {}}});
+ << QString(":/file/path/to/filename1.h\n"
+ "1;1 5:match\n"
+ "\n"
+ ":/file/path/to/filename2.h\n"
+ "2;2 5: match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename1.h", "match", 1, 1, 5),
+ searchResult("/file/path/to/filename2.h", " match", 2, 2, 5)};
QTest::addRow("oneFileMultipleMatches")
- << QString(":/file/path/to/filename.h\n"
- "1;1 5,7 5:match match\n")
- << FileSearchResultList({{"/file/path/to/filename.h", 1, "match match", 1, 5, {}},
- {"/file/path/to/filename.h", 1, "match match", 7, 5, {}}});
+ << QString(":/file/path/to/filename.h\n"
+ "1;1 5,7 5:match match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename.h", "match match", 1, 1, 5),
+ searchResult("/file/path/to/filename.h", "match match", 1, 7, 5)};
QTest::addRow("multipleFilesWithMultipleMatches")
- << QString(":/file/path/to/filename1.h\n"
- "1;1 5,7 5:match match\n"
- "\n"
- ":/file/path/to/filename2.h\n"
- "2;2 5,8 5: match match\n")
- << FileSearchResultList({{"/file/path/to/filename1.h", 1, "match match", 1, 5, {}},
- {"/file/path/to/filename1.h", 1, "match match", 7, 5, {}},
- {"/file/path/to/filename2.h", 2, " match match", 2, 5, {}},
- {"/file/path/to/filename2.h", 2, " match match", 8, 5, {}}});
+ << QString(":/file/path/to/filename1.h\n"
+ "1;1 5,7 5:match match\n"
+ "\n"
+ ":/file/path/to/filename2.h\n"
+ "2;2 5,8 5: match match\n")
+ << SearchResultItems{searchResult("/file/path/to/filename1.h", "match match", 1, 1, 5),
+ searchResult("/file/path/to/filename1.h", "match match", 1, 7, 5),
+ searchResult("/file/path/to/filename2.h", " match match", 2, 2, 5),
+ searchResult("/file/path/to/filename2.h", " match match", 2, 8, 5)};
}
void OutputParserTest::test()
{
QFETCH(QString, parserOutput);
- QFETCH(FileSearchResultList, results);
- SilverSearcher::SilverSearcherOutputParser ssop(parserOutput);
- const FileSearchResultList items = ssop.parse();
+ QFETCH(SearchResultItems, results);
+ const SearchResultItems items = SilverSearcher::parse(parserOutput);
QCOMPARE(items, results);
}
diff --git a/src/plugins/silversearcher/silversearcher.qbs b/src/plugins/silversearcher/silversearcher.qbs
index f099858f6e..8cf2cd1509 100644
--- a/src/plugins/silversearcher/silversearcher.qbs
+++ b/src/plugins/silversearcher/silversearcher.qbs
@@ -13,9 +13,7 @@ QtcPlugin {
"silversearcherplugin.cpp", "silversearcherplugin.h",
]
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"outputparser_test.cpp",
"outputparser_test.h",
diff --git a/src/plugins/silversearcher/silversearcheroutputparser.cpp b/src/plugins/silversearcher/silversearcheroutputparser.cpp
index 9088310c3f..65d773d0c6 100644
--- a/src/plugins/silversearcher/silversearcheroutputparser.cpp
+++ b/src/plugins/silversearcher/silversearcheroutputparser.cpp
@@ -3,117 +3,140 @@
#include "silversearcheroutputparser.h"
-#include <QString>
+using namespace Utils;
namespace SilverSearcher {
-SilverSearcherOutputParser::SilverSearcherOutputParser(
- const QString &output, const QRegularExpression &regexp)
- : output(output)
- , regexp(regexp)
- , outputSize(output.size())
-{
- hasRegexp = !regexp.pattern().isEmpty();
-}
+/*
+// Example output (searching for "ab"):
+
+:/home/jarek/dev/creator-master/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
+149;60 2: static const bool dontUseSharedMemory = qEnvironmentVariableIsSet("DESIGNER_DONT_USE_SHARED_MEMORY");
+198;65 2: qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
-Utils::FileSearchResultList SilverSearcherOutputParser::parse()
+:/home/jarek/dev/creator-master/src/libs/qmlpuppetcommunication/container/propertyabstractcontainer.cpp
+4;18 2:#include "propertyabstractcontainer.h"
+10;8 2,35 2:PropertyAbstractContainer::PropertyAbstractContainer()
+*/
+
+static QStringView nextLine(QStringView *remainingOutput)
{
- while (index < outputSize - 1) {
- if (output[index] == '\n') {
- ++index;
- continue;
- }
- parseFilePath();
- while (index < outputSize && output[index] != '\n') {
- parseLineNumber();
- if (index >= outputSize - 1)
- break;
- int matches = parseMatches();
- if (index >= outputSize - 1)
- break;
- parseText();
- for (int i = 0; i < matches; ++i)
- items[items.size() - i - 1].matchingLine = item.matchingLine;
- }
+ const int newLinePos = remainingOutput->indexOf('\n');
+ if (newLinePos < 0) {
+ QStringView ret = *remainingOutput;
+ *remainingOutput = QStringView();
+ return ret;
}
-
- return items;
+ QStringView ret = remainingOutput->left(newLinePos);
+ *remainingOutput = remainingOutput->mid(newLinePos + 1);
+ return ret;
}
-bool SilverSearcherOutputParser::parseFilePath()
+static bool parseNumber(QStringView numberString, int *number)
{
- int startIndex = ++index;
- while (index < outputSize && output[index] != '\n')
- ++index;
- item.fileName = Utils::FilePath::fromString(QString(output.data() + startIndex, index - startIndex));
- ++index;
- return true;
+ for (const auto &c : numberString) {
+ if (!c.isDigit())
+ return false;
+ }
+ bool ok = false;
+ int parsedNumber = numberString.toInt(&ok);
+ if (ok)
+ *number = parsedNumber;
+ return ok;
}
-bool SilverSearcherOutputParser::parseLineNumber()
+static bool parseLineNumber(QStringView *remainingOutput, int *lineNumber)
{
- int startIndex = index;
- while (index < outputSize && output[++index] != ';') { }
+ const int lineNumberDelimiterPos = remainingOutput->indexOf(';');
+ if (lineNumberDelimiterPos < 0)
+ return false;
- item.lineNumber = QString(output.data() + startIndex, index - startIndex).toInt();
- ++index;
+ if (!parseNumber(remainingOutput->left(lineNumberDelimiterPos), lineNumber))
+ return false;
+
+ *remainingOutput = remainingOutput->mid(lineNumberDelimiterPos + 1);
return true;
}
-bool SilverSearcherOutputParser::parseMatchIndex()
+static bool parseLineHit(QStringView hitString, QPair<int, int> *hit)
{
- int startIndex = index;
- while (index < outputSize && output[++index] != ' ') { }
+ const int hitSpaceDelimiterPos = hitString.indexOf(' ');
+ if (hitSpaceDelimiterPos < 0)
+ return false;
- item.matchStart = QString(output.data() + startIndex, index - startIndex).toInt();
- ++index;
- return true;
-}
+ int hitStart = -1;
+ if (!parseNumber(hitString.left(hitSpaceDelimiterPos), &hitStart))
+ return false;
-bool SilverSearcherOutputParser::parseMatchLength()
-{
- int startIndex = index;
- while (index < outputSize && output[++index] != ':' && output[index] != ',') { }
+ int hitLength = -1;
+ if (!parseNumber(hitString.mid(hitSpaceDelimiterPos + 1), &hitLength))
+ return false;
- item.matchLength = QString(output.data() + startIndex, index - startIndex).toInt();
+ *hit = {hitStart, hitLength};
return true;
}
-int SilverSearcherOutputParser::parseMatches()
+static bool parseLineHits(QStringView *remainingOutput, QList<QPair<int, int>> *hits)
{
- int matches = 1;
- const int colon = output.indexOf(':', index);
- QString text;
- if (colon != -1) {
- const int textStart = colon + 1;
- const int newline = output.indexOf('\n', textStart);
- text = output.mid(textStart, newline >= 0 ? newline - textStart : -1);
+ const int hitsDelimiterPos = remainingOutput->indexOf(':');
+ if (hitsDelimiterPos < 0)
+ return false;
+
+ const QStringView hitsString = remainingOutput->left(hitsDelimiterPos);
+ const QList<QStringView> hitStrings = hitsString.split(',', Qt::SkipEmptyParts);
+ for (const auto hitString : hitStrings) {
+ QPair<int, int> hit;
+ if (!parseLineHit(hitString, &hit))
+ return false;
+ hits->append(hit);
}
- while (index < outputSize && output[index] != ':') {
- if (output[index] == ',') {
- ++matches;
- ++index;
- }
- parseMatchIndex();
- parseMatchLength();
- if (hasRegexp) {
- const QString part = QString(text.mid(item.matchStart, item.matchLength));
- item.regexpCapturedTexts = regexp.match(part).capturedTexts();
- }
- items << item;
- }
-
- ++index;
- return matches;
+ *remainingOutput = remainingOutput->mid(hitsDelimiterPos + 1);
+ return true;
}
-bool SilverSearcherOutputParser::parseText()
+SearchResultItems parse(const QString &output, const std::optional<QRegularExpression> &regExp)
{
- int startIndex = index;
- while (index < outputSize && output[++index] != '\n') { }
- item.matchingLine = QString(output.data() + startIndex, index - startIndex);
- ++index;
- return true;
+ SearchResultItems items;
+
+ QStringView remainingOutput(output);
+ while (true) {
+ if (remainingOutput.isEmpty())
+ break;
+
+ const QStringView filePathLine = nextLine(&remainingOutput);
+ if (filePathLine.isEmpty())
+ continue;
+
+ if (!filePathLine.startsWith(':'))
+ continue;
+
+ const FilePath filePath = FilePath::fromPathPart(filePathLine.mid(1));
+
+ QStringView hitLine = nextLine(&remainingOutput);
+ while (!hitLine.isEmpty()) {
+ int lineNumber = -1;
+ if (!parseLineNumber(&hitLine, &lineNumber))
+ break;
+
+ // List of pairs <hit start pos, hit length>
+ QList<QPair<int, int>> hits;
+ if (!parseLineHits(&hitLine, &hits))
+ break;
+
+ SearchResultItem item;
+ item.setFilePath(filePath);
+ item.setDisplayText(hitLine.toString());
+ for (const QPair<int, int> &hit : hits) {
+ item.setMainRange(lineNumber, hit.first, hit.second);
+ item.setUserData(
+ regExp ? regExp->match(hitLine.mid(hit.first, hit.second)).capturedTexts()
+ : QVariant());
+ items.append(item);
+ }
+ hitLine = nextLine(&remainingOutput);
+ }
+ }
+ return items;
}
} // namespace SilverSearcher
diff --git a/src/plugins/silversearcher/silversearcheroutputparser.h b/src/plugins/silversearcher/silversearcheroutputparser.h
index 187e463938..f9d8502759 100644
--- a/src/plugins/silversearcher/silversearcheroutputparser.h
+++ b/src/plugins/silversearcher/silversearcheroutputparser.h
@@ -3,36 +3,13 @@
#pragma once
-#include <coreplugin/find/searchresultwindow.h>
-#include <utils/filesearch.h>
+#include <utils/searchresultitem.h>
-#include <QList>
#include <QRegularExpression>
namespace SilverSearcher {
-class SilverSearcherOutputParser
-{
-public:
- SilverSearcherOutputParser(const QString &output, const QRegularExpression &regexp = {});
-
- Utils::FileSearchResultList parse();
-
-private:
- int parseMatches();
- bool parseMatchLength();
- bool parseMatchIndex();
- bool parseLineNumber();
- bool parseFilePath();
- bool parseText();
-
- QString output;
- QRegularExpression regexp;
- bool hasRegexp = false;
- int outputSize = 0;
- int index = 0;
- Utils::FileSearchResult item;
- Utils::FileSearchResultList items;
-};
+Utils::SearchResultItems parse(const QString &output,
+ const std::optional<QRegularExpression> &regExp = {});
} // namespace SilverSearcher
diff --git a/src/plugins/squish/deletesymbolicnamedialog.cpp b/src/plugins/squish/deletesymbolicnamedialog.cpp
index 44a3b507d5..dbb6804fb5 100644
--- a/src/plugins/squish/deletesymbolicnamedialog.cpp
+++ b/src/plugins/squish/deletesymbolicnamedialog.cpp
@@ -63,7 +63,7 @@ DeleteSymbolicNameDialog::DeleteSymbolicNameDialog(const QString &symbolicName,
updateDetailsLabel(symbolicName);
populateSymbolicNamesList(names);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_detailsLabel,
diff --git a/src/plugins/squish/images/picker.png b/src/plugins/squish/images/picker.png
new file mode 100644
index 0000000000..1e9a13e8a1
--- /dev/null
+++ b/src/plugins/squish/images/picker.png
Binary files differ
diff --git a/src/plugins/squish/images/picker@2x.png b/src/plugins/squish/images/picker@2x.png
new file mode 100644
index 0000000000..390926b891
--- /dev/null
+++ b/src/plugins/squish/images/picker@2x.png
Binary files differ
diff --git a/src/plugins/squish/objectsmapdocument.cpp b/src/plugins/squish/objectsmapdocument.cpp
index eb7266ddc4..3742ee3466 100644
--- a/src/plugins/squish/objectsmapdocument.cpp
+++ b/src/plugins/squish/objectsmapdocument.cpp
@@ -10,7 +10,7 @@
#include "squishtr.h"
#include <utils/fileutils.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QtCore5Compat/QTextCodec>
@@ -209,7 +209,7 @@ Core::IDocument::OpenResult ObjectsMapDocument::openImpl(QString *error,
return OpenResult::ReadError;
}
- Utils::QtcProcess objectMapReader;
+ Utils::Process objectMapReader;
objectMapReader.setCommand({exe, {"--scriptMap", "--mode", "read",
"--scriptedObjectMapPath", realFileName.toUserOutput()}});
objectMapReader.setCodec(QTextCodec::codecForName("UTF-8"));
@@ -240,7 +240,7 @@ bool ObjectsMapDocument::writeFile(const Utils::FilePath &fileName) const
if (!exe.isExecutableFile())
return false;
- Utils::QtcProcess objectMapWriter;
+ Utils::Process objectMapWriter;
objectMapWriter.setCommand({exe, {"--scriptMap", "--mode", "write",
"--scriptedObjectMapPath", fileName.toUserOutput()}});
objectMapWriter.setWriteData(contents());
diff --git a/src/plugins/squish/objectsmapeditorwidget.cpp b/src/plugins/squish/objectsmapeditorwidget.cpp
index 7e215d0d77..e689a5a296 100644
--- a/src/plugins/squish/objectsmapeditorwidget.cpp
+++ b/src/plugins/squish/objectsmapeditorwidget.cpp
@@ -88,7 +88,7 @@ void ObjectsMapEditorWidget::initUi()
m_stackedLayout->addWidget(validPropertiesWidget);
m_stackedLayout->addWidget(invalidPropertiesWidget);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_propertiesTree,
diff --git a/src/plugins/squish/opensquishsuitesdialog.cpp b/src/plugins/squish/opensquishsuitesdialog.cpp
index f36d402dca..c32e9d0a39 100644
--- a/src/plugins/squish/opensquishsuitesdialog.cpp
+++ b/src/plugins/squish/opensquishsuitesdialog.cpp
@@ -40,7 +40,7 @@ OpenSquishSuitesDialog::OpenSquishSuitesDialog(QWidget *parent)
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Open);
m_buttonBox->button(QDialogButtonBox::Open)->setEnabled(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
new QLabel(Tr::tr("Base directory:")),
diff --git a/src/plugins/squish/squish.qrc b/src/plugins/squish/squish.qrc
index 1a62ee1c1c..e55323c3cc 100644
--- a/src/plugins/squish/squish.qrc
+++ b/src/plugins/squish/squish.qrc
@@ -8,6 +8,8 @@
<file>images/jumpTo@2x.png</file>
<file>images/data.png</file>
<file>images/data@2x.png</file>
+ <file>images/picker.png</file>
+ <file>images/picker@2x.png</file>
<file>wizard/suite/wizard.json</file>
</qresource>
</RCC>
diff --git a/src/plugins/squish/squishfilehandler.cpp b/src/plugins/squish/squishfilehandler.cpp
index 956f948822..6e48c917b8 100644
--- a/src/plugins/squish/squishfilehandler.cpp
+++ b/src/plugins/squish/squishfilehandler.cpp
@@ -15,8 +15,10 @@
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
+
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
+
#include <utils/algorithm.h>
#include <utils/aspects.h>
#include <utils/layoutbuilder.h>
@@ -51,7 +53,7 @@ public:
QWidget *widget = new QWidget(this);
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Form {
label, &aut, br,
arguments,
@@ -93,8 +95,7 @@ SquishFileHandler::SquishFileHandler(QObject *parent)
: QObject(parent)
{
m_instance = this;
- auto sessionManager = ProjectExplorer::SessionManager::instance();
- connect(sessionManager, &ProjectExplorer::SessionManager::sessionLoaded,
+ connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::sessionLoaded,
this, &SquishFileHandler::onSessionLoaded);
}
diff --git a/src/plugins/squish/squishnavigationwidget.cpp b/src/plugins/squish/squishnavigationwidget.cpp
index ce96501398..1b99c338f1 100644
--- a/src/plugins/squish/squishnavigationwidget.cpp
+++ b/src/plugins/squish/squishnavigationwidget.cpp
@@ -185,7 +185,7 @@ void SquishNavigationWidget::contextMenuEvent(QContextMenuEvent *event)
QAction *closeAllSuites = new QAction(Tr::tr("Close All Test Suites"), &menu);
menu.addAction(closeAllSuites);
- connect(closeAllSuites, &QAction::triggered, this, [this] {
+ connect(closeAllSuites, &QAction::triggered, this, [] {
if (SquishMessages::simpleQuestion(Tr::tr("Close All Test Suites"),
Tr::tr("Close all test suites?"
/*"\nThis will close all related files as well."*/))
@@ -296,14 +296,15 @@ void SquishNavigationWidget::onRemoveAllSharedFolderTriggered()
void SquishNavigationWidget::onRecordTestCase(const QString &suiteName, const QString &testCase)
{
- QDialogButtonBox::StandardButton pressed = Utils::CheckableMessageBox::doNotAskAgainQuestion(
- Core::ICore::dialogParent(),
- Tr::tr("Record Test Case"),
- Tr::tr("Do you want to record over the test case \"%1\"? The existing content will "
- "be overwritten by the recorded script.").arg(testCase),
- Core::ICore::settings(),
- "RecordWithoutApproval");
- if (pressed != QDialogButtonBox::Yes)
+ QMessageBox::StandardButton pressed = Utils::CheckableMessageBox::question(
+ Core::ICore::dialogParent(),
+ Tr::tr("Record Test Case"),
+ Tr::tr("Do you want to record over the test case \"%1\"? The existing content will "
+ "be overwritten by the recorded script.")
+ .arg(testCase),
+ Core::ICore::settings(),
+ "RecordWithoutApproval");
+ if (pressed != QMessageBox::Yes)
return;
SquishFileHandler::instance()->recordTestCase(suiteName, testCase);
diff --git a/src/plugins/squish/squishoutputpane.cpp b/src/plugins/squish/squishoutputpane.cpp
index 2b2d90d165..8c8047f47f 100644
--- a/src/plugins/squish/squishoutputpane.cpp
+++ b/src/plugins/squish/squishoutputpane.cpp
@@ -19,6 +19,7 @@
#include <coreplugin/icontext.h>
#include <utils/itemviews.h>
+#include <utils/stylehelper.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
@@ -305,17 +306,20 @@ void SquishOutputPane::clearOldResults()
void SquishOutputPane::createToolButtons()
{
m_expandAll = new QToolButton(m_treeView);
+ Utils::StyleHelper::setPanelWidget(m_expandAll);
m_expandAll->setIcon(Utils::Icons::EXPAND_TOOLBAR.icon());
m_expandAll->setToolTip(Tr::tr("Expand All"));
m_collapseAll = new QToolButton(m_treeView);
+ Utils::StyleHelper::setPanelWidget(m_collapseAll);
m_collapseAll->setIcon(Utils::Icons::COLLAPSE_TOOLBAR.icon());
m_collapseAll->setToolTip(Tr::tr("Collapse All"));
m_filterButton = new QToolButton(m_treeView);
+ Utils::StyleHelper::setPanelWidget(m_filterButton);
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter Test Results"));
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
m_filterButton->setAutoRaise(true);
m_filterButton->setPopupMode(QToolButton::InstantPopup);
m_filterMenu = new QMenu(m_filterButton);
diff --git a/src/plugins/squish/squishperspective.cpp b/src/plugins/squish/squishperspective.cpp
index fca93ce35d..5120ec6ea9 100644
--- a/src/plugins/squish/squishperspective.cpp
+++ b/src/plugins/squish/squishperspective.cpp
@@ -26,10 +26,13 @@
namespace Squish {
namespace Internal {
-enum class IconType { StopRecord, Play, Pause, StepIn, StepOver, StepReturn, Stop };
+enum class IconType { StopRecord, Play, Pause, StepIn, StepOver, StepReturn, Stop, Inspect };
static QIcon iconForType(IconType type)
{
+ static const Utils::Icon inspectIcon({{":/squish/images/picker.png",
+ Utils::Theme::IconsBaseColor}});
+
switch (type) {
case IconType::StopRecord:
return Debugger::Icons::RECORD_ON.icon();
@@ -45,6 +48,8 @@ static QIcon iconForType(IconType type)
return Debugger::Icons::STEP_OUT_TOOLBAR.icon();
case IconType::Stop:
return Utils::Icons::STOP_SMALL.icon();
+ case IconType::Inspect:
+ return inspectIcon.icon();
}
return QIcon();
}
@@ -91,6 +96,79 @@ QVariant LocalsItem::data(int column, int role) const
return TreeItem::data(column, role);
}
+QVariant InspectedObjectItem::data(int column, int role) const
+{
+ if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
+ switch (column) {
+ case 0: return value;
+ case 1: return type;
+ }
+ }
+ return TreeItem::data(column, role);
+}
+
+QVariant InspectedPropertyItem::data(int column, int role) const
+{
+ if (role ==Qt::DisplayRole || role == Qt::ToolTipRole) {
+ switch (column) {
+ case 0: return name;
+ case 1: return value;
+ }
+ }
+ return TreeItem::data(column, role);
+}
+
+void InspectedPropertyItem::parseAndUpdateChildren()
+{
+ const char open = '{';
+ const char close = '}';
+ const char delimiter =',';
+ const char eq = '=';
+
+ if (!value.startsWith(open) || !value.endsWith(close)) // only parse multi-property content
+ return;
+
+ int start = 1;
+ int end = value.size() - 1;
+ do {
+ int endOfName = value.indexOf(eq, start);
+ QTC_ASSERT(endOfName != -1, return);
+ int innerStart = endOfName + 2;
+ QTC_ASSERT(innerStart < end, return);
+ const QString name = value.mid(start, endOfName - start).trimmed();
+ if (value.at(innerStart) != open) {
+ int endOfItemValue = value.indexOf(delimiter, innerStart);
+ if (endOfItemValue == -1)
+ endOfItemValue = end;
+ const QString content = value.mid(innerStart, endOfItemValue - innerStart).trimmed();
+ appendChild(new InspectedPropertyItem(name, content));
+ start = endOfItemValue + 1;
+ } else {
+ int openedBraces = 1;
+ // advance until item's content is complete
+ int pos = innerStart;
+ do {
+ if (++pos > end)
+ break;
+ if (value.at(pos) == open) {
+ ++openedBraces;
+ continue;
+ }
+ if (value.at(pos) == close) {
+ --openedBraces;
+ if (openedBraces == 0)
+ break;
+ }
+ } while (pos < end);
+ ++pos;
+ QTC_ASSERT(pos < end, return);
+ const QString content = value.mid(innerStart, pos - innerStart).trimmed();
+ appendChild(new InspectedPropertyItem(name, content));
+ start = pos + 1;
+ }
+ } while (start < end);
+}
+
class SquishControlBar : public QDialog
{
public:
@@ -209,10 +287,14 @@ void SquishPerspective::initPerspective()
m_stepOutAction->setEnabled(false);
m_stopAction = Debugger::createStopAction();
m_stopAction->setEnabled(false);
+ m_inspectAction = new QAction(this);
+ m_inspectAction->setIcon(iconForType(IconType::Inspect));
+ m_inspectAction->setToolTip(Tr::tr("Inspect"));
+ m_inspectAction->setEnabled(false);
- QVBoxLayout *mainLayout = new QVBoxLayout;
- mainLayout->setContentsMargins(0, 0, 0, 0);
- mainLayout->setSpacing(1);
+ QVBoxLayout *localsMainLayout = new QVBoxLayout;
+ localsMainLayout->setContentsMargins(0, 0, 0, 0);
+ localsMainLayout->setSpacing(1);
m_localsModel.setHeader({Tr::tr("Name"), Tr::tr("Type"), Tr::tr("Value")});
auto localsView = new Utils::TreeView;
@@ -220,11 +302,45 @@ void SquishPerspective::initPerspective()
localsView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
localsView->setModel(&m_localsModel);
localsView->setRootIsDecorated(true);
- mainLayout->addWidget(localsView);
- QWidget *mainWidget = new QWidget;
- mainWidget->setObjectName("SquishLocalsView");
- mainWidget->setWindowTitle(Tr::tr("Squish Locals"));
- mainWidget->setLayout(mainLayout);
+ localsMainLayout->addWidget(localsView);
+ QWidget *localsWidget = new QWidget;
+ localsWidget->setObjectName("SquishLocalsView");
+ localsWidget->setWindowTitle(Tr::tr("Squish Locals"));
+ localsWidget->setLayout(localsMainLayout);
+
+ QVBoxLayout *objectsMainLayout = new QVBoxLayout;
+ objectsMainLayout->setContentsMargins(0, 0, 0, 0);
+ objectsMainLayout->setSpacing(1);
+
+ m_objectsModel.setHeader({Tr::tr("Object"), Tr::tr("Type")});
+ m_objectsView = new Utils::TreeView;
+ m_objectsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_objectsView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_objectsView->setModel(&m_objectsModel);
+ m_objectsView->setRootIsDecorated(true);
+ objectsMainLayout->addWidget(m_objectsView);
+
+ QWidget *objectWidget = new QWidget;
+ objectWidget->setObjectName("SquishObjectsView");
+ objectWidget->setWindowTitle(Tr::tr("Squish Objects"));
+ objectWidget->setLayout(objectsMainLayout);
+
+ QVBoxLayout *propertiesMainLayout = new QVBoxLayout;
+ propertiesMainLayout->setContentsMargins(0, 0, 0, 0);
+ propertiesMainLayout->setSpacing(1);
+
+ m_propertiesModel.setHeader({Tr::tr("Property"), Tr::tr("Value")});
+ auto propertiesView = new Utils::TreeView;
+ propertiesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ propertiesView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ propertiesView->setModel(&m_propertiesModel);
+ propertiesView->setRootIsDecorated(true);
+ propertiesMainLayout->addWidget(propertiesView);
+
+ QWidget *propertiesWidget = new QWidget;
+ propertiesWidget->setObjectName("SquishPropertiesView");
+ propertiesWidget->setWindowTitle(Tr::tr("Squish Object Properties"));
+ propertiesWidget->setLayout(propertiesMainLayout);
addToolBarAction(m_pausePlayAction);
addToolBarAction(m_stepInAction);
@@ -232,10 +348,14 @@ void SquishPerspective::initPerspective()
addToolBarAction(m_stepOutAction);
addToolBarAction(m_stopAction);
addToolbarSeparator();
+ addToolBarAction(m_inspectAction);
+ addToolbarSeparator();
m_status = new QLabel;
addToolBarWidget(m_status);
- addWindow(mainWidget, Perspective::AddToTab, nullptr, true, Qt::RightDockWidgetArea);
+ addWindow(objectWidget, Perspective::SplitVertical, nullptr);
+ addWindow(propertiesWidget, Perspective::SplitHorizontal, objectWidget);
+ addWindow(localsWidget, Perspective::AddToTab, nullptr, true, Qt::RightDockWidgetArea);
connect(m_pausePlayAction, &QAction::triggered, this, &SquishPerspective::onPausePlayTriggered);
connect(m_stepInAction, &QAction::triggered, this, [this] {
@@ -250,6 +370,10 @@ void SquishPerspective::initPerspective()
connect(m_stopAction, &QAction::triggered, this, &SquishPerspective::onStopTriggered);
connect(m_stopRecordAction, &QAction::triggered,
this, &SquishPerspective::onStopRecordTriggered);
+ connect(m_inspectAction, &QAction::triggered, this, [this]{
+ m_inspectAction->setEnabled(false);
+ emit inspectTriggered();
+ });
connect(SquishTools::instance(), &SquishTools::localsUpdated,
this, &SquishPerspective::onLocalsUpdated);
@@ -262,6 +386,33 @@ void SquishPerspective::initPerspective()
SquishTools::instance()->requestExpansion(item->name);
}
});
+
+ connect(SquishTools::instance(), &SquishTools::objectPicked,
+ this, &SquishPerspective::onObjectPicked);
+ connect(SquishTools::instance(), &SquishTools::propertiesFetched,
+ this, &SquishPerspective::onPropertiesFetched);
+ connect(SquishTools::instance(), &SquishTools::autIdRetrieved,
+ this, [this]{
+ m_autIdKnown = true;
+ m_inspectAction->setEnabled(true);
+ });
+ connect(m_objectsView, &QTreeView::expanded, this, [this](const QModelIndex &idx) {
+ InspectedObjectItem *item = m_objectsModel.itemForIndex(idx);
+ if (QTC_GUARD(item)) {
+ if (item->expanded)
+ return;
+ item->expanded = true;
+ SquishTools::instance()->requestExpansionForObject(item->value);
+ }
+ });
+ connect(m_objectsView->selectionModel(), &QItemSelectionModel::currentChanged,
+ this, [this](const QModelIndex &current){
+ m_propertiesModel.clear();
+ InspectedObjectItem *item = m_objectsModel.itemForIndex(current);
+ if (!item)
+ return;
+ SquishTools::instance()->requestPropertiesForObject(item->value);
+ });
}
void SquishPerspective::onStopTriggered()
@@ -269,6 +420,8 @@ void SquishPerspective::onStopTriggered()
m_stopRecordAction->setEnabled(false);
m_pausePlayAction->setEnabled(false);
m_stopAction->setEnabled(false);
+ m_inspectAction->setEnabled(false);
+ m_autIdKnown = false;
emit stopRequested();
}
@@ -277,6 +430,8 @@ void SquishPerspective::onStopRecordTriggered()
m_stopRecordAction->setEnabled(false);
m_pausePlayAction->setEnabled(false);
m_stopAction->setEnabled(false);
+ m_inspectAction->setEnabled(false);
+ m_autIdKnown = false;
emit stopRecordRequested();
}
@@ -326,6 +481,44 @@ void SquishPerspective::onLocalsUpdated(const QString &output)
}
}
+void SquishPerspective::onObjectPicked(const QString &output)
+{
+ // "+{container=':o_QQuickView' text='2' type='Text' unnamed='1' visible='true'}\tQQuickText_QML_1"
+ static const QRegularExpression regex("^(?<exp>[-+])(?<content>\\{.*\\})\t(?<type>.+)$");
+ const QRegularExpressionMatch match = regex.match(output);
+ if (!match.hasMatch())
+ return;
+ InspectedObjectItem *parent = nullptr;
+ const QString content = match.captured("content");
+ parent = m_objectsModel.findNonRootItem([content](InspectedObjectItem *it) {
+ return it->value == content;
+ });
+ if (!parent) {
+ m_objectsModel.clear();
+ parent = m_objectsModel.rootItem();
+ }
+ InspectedObjectItem *obj = new InspectedObjectItem(content, match.captured("type"));
+ if (match.captured("exp") == "+")
+ obj->appendChild(new InspectedObjectItem); // add pseudo child
+ parent->appendChild(obj);
+ m_inspectAction->setEnabled(true);
+ const QModelIndex idx = m_objectsModel.indexForItem(obj);
+ if (idx.isValid())
+ m_objectsView->setCurrentIndex(idx);
+}
+
+void SquishPerspective::onPropertiesFetched(const QStringList &properties)
+{
+ static const QRegularExpression regex("(?<name>.+)=(?<exp>[-+])(?<content>.*)");
+
+ for (const QString &line : properties) {
+ const QRegularExpressionMatch match = regex.match(line);
+ QTC_ASSERT(match.hasMatch(), continue);
+ auto item = new InspectedPropertyItem(match.captured("name"), match.captured("content"));
+ m_propertiesModel.rootItem()->appendChild(item);
+ }
+}
+
void SquishPerspective::updateStatus(const QString &status)
{
m_status->setText(status);
@@ -360,6 +553,12 @@ void SquishPerspective::destroyControlBar()
m_controlBar = nullptr;
}
+void SquishPerspective::resetAutId()
+{
+ m_autIdKnown = false;
+ m_inspectAction->setEnabled(false);
+}
+
void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
{
if (m_mode == mode) // ignore
@@ -376,6 +575,7 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(false);
m_stepOutAction->setEnabled(false);
m_stopAction->setEnabled(true);
+ m_inspectAction->setEnabled(false);
break;
case Recording:
m_stopRecordAction->setEnabled(true);
@@ -386,6 +586,7 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(false);
m_stepOutAction->setEnabled(false);
m_stopAction->setEnabled(true);
+ m_inspectAction->setEnabled(false);
break;
case Interrupted:
m_pausePlayAction->setEnabled(true);
@@ -395,6 +596,7 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(true);
m_stepOutAction->setEnabled(true);
m_stopAction->setEnabled(true);
+ m_inspectAction->setEnabled(m_autIdKnown);
break;
case Configuring:
case Querying:
@@ -407,7 +609,9 @@ void SquishPerspective::setPerspectiveMode(PerspectiveMode mode)
m_stepOverAction->setEnabled(false);
m_stepOutAction->setEnabled(false);
m_stopAction->setEnabled(false);
+ m_inspectAction->setEnabled(false);
m_localsModel.clear();
+ m_objectsModel.clear();
break;
default:
break;
diff --git a/src/plugins/squish/squishperspective.h b/src/plugins/squish/squishperspective.h
index 1e5c4aa515..1ab6b59be2 100644
--- a/src/plugins/squish/squishperspective.h
+++ b/src/plugins/squish/squishperspective.h
@@ -9,6 +9,8 @@
#include <utils/treemodel.h>
+namespace Utils { class TreeView; }
+
namespace Squish {
namespace Internal {
@@ -26,6 +28,31 @@ public:
bool expanded = false;
};
+class InspectedObjectItem : public Utils::TreeItem
+{
+public:
+ InspectedObjectItem() = default;
+ InspectedObjectItem(const QString &v, const QString &t) : value(v), type(t) {}
+ QVariant data(int column, int role) const override;
+ QString value;
+ QString type;
+ bool expanded = false;
+};
+
+class InspectedPropertyItem : public Utils::TreeItem
+{
+public:
+ InspectedPropertyItem() = default;
+ InspectedPropertyItem(const QString &n, const QString &v)
+ : name(n), value(v) { parseAndUpdateChildren(); }
+ QVariant data(int column, int role) const override;
+ QString name;
+ QString value;
+ bool expanded = false;
+private:
+ void parseAndUpdateChildren();
+};
+
class SquishPerspective : public Utils::Perspective
{
Q_OBJECT
@@ -41,18 +68,22 @@ public:
void showControlBar(SquishXmlOutputHandler *xmlOutputHandler);
void destroyControlBar();
+ void resetAutId();
signals:
void stopRequested();
void stopRecordRequested();
void interruptRequested();
void runRequested(StepMode mode);
+ void inspectTriggered();
private:
void onStopTriggered();
void onStopRecordTriggered();
void onPausePlayTriggered();
void onLocalsUpdated(const QString &output);
+ void onObjectPicked(const QString &output);
+ void onPropertiesFetched(const QStringList &properties);
QAction *m_stopRecordAction = nullptr;
QAction *m_pausePlayAction = nullptr;
@@ -60,10 +91,15 @@ private:
QAction *m_stepOverAction = nullptr;
QAction *m_stepOutAction = nullptr;
QAction *m_stopAction = nullptr;
+ QAction *m_inspectAction = nullptr;
QLabel *m_status = nullptr;
class SquishControlBar *m_controlBar = nullptr;
Utils::TreeModel<LocalsItem> m_localsModel;
+ Utils::TreeModel<InspectedObjectItem> m_objectsModel;
+ Utils::TreeModel<InspectedPropertyItem> m_propertiesModel;
+ Utils::TreeView *m_objectsView = nullptr;
PerspectiveMode m_mode = NoMode;
+ bool m_autIdKnown = false;
friend class SquishControlBar;
};
diff --git a/src/plugins/squish/squishprocessbase.cpp b/src/plugins/squish/squishprocessbase.cpp
index 23d3a49aa7..744ea506c1 100644
--- a/src/plugins/squish/squishprocessbase.cpp
+++ b/src/plugins/squish/squishprocessbase.cpp
@@ -8,9 +8,9 @@ namespace Squish::Internal {
SquishProcessBase::SquishProcessBase(QObject *parent)
: QObject(parent)
{
- connect(&m_process, &Utils::QtcProcess::readyReadStandardError,
+ connect(&m_process, &Utils::Process::readyReadStandardError,
this, &SquishProcessBase::onErrorOutput);
- connect(&m_process, &Utils::QtcProcess::done,
+ connect(&m_process, &Utils::Process::done,
this, &SquishProcessBase::onDone);
}
diff --git a/src/plugins/squish/squishprocessbase.h b/src/plugins/squish/squishprocessbase.h
index 088eca8a73..6582c95062 100644
--- a/src/plugins/squish/squishprocessbase.h
+++ b/src/plugins/squish/squishprocessbase.h
@@ -5,7 +5,7 @@
#include "squishconstants.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
@@ -36,7 +36,7 @@ protected:
virtual void onDone() {}
virtual void onErrorOutput() {}
- Utils::QtcProcess m_process;
+ Utils::Process m_process;
private:
SquishProcessState m_state = Idle;
diff --git a/src/plugins/squish/squishrunnerprocess.cpp b/src/plugins/squish/squishrunnerprocess.cpp
index 3fa6474e26..4c6991a207 100644
--- a/src/plugins/squish/squishrunnerprocess.cpp
+++ b/src/plugins/squish/squishrunnerprocess.cpp
@@ -6,6 +6,7 @@
#include "squishtr.h"
#include <debugger/breakhandler.h>
+#include <utils/executeondestruction.h>
#include <QLoggingCategory>
@@ -13,6 +14,14 @@ Q_LOGGING_CATEGORY(runnerLOG, "qtc.squish.squishrunner", QtWarningMsg)
namespace Squish::Internal {
+static QString maskedArgument(const QString &originalArg)
+{
+ QString masked = originalArg;
+ masked.replace('\\', "\\\\");
+ masked.replace(' ', "\\x20");
+ return masked;
+}
+
SquishRunnerProcess::SquishRunnerProcess(QObject *parent)
: SquishProcessBase{parent}
{
@@ -36,6 +45,10 @@ void SquishRunnerProcess::setupProcess(RunnerMode mode)
case Record:
m_process.setProcessMode(Utils::ProcessMode::Writer);
break;
+ case Inspect:
+ m_process.setProcessMode(Utils::ProcessMode::Writer);
+ m_process.setStdOutLineCallback([this](const QString &line) { onInspectorOutput(line); });
+ break;
}
}
@@ -44,6 +57,8 @@ void SquishRunnerProcess::start(const Utils::CommandLine &cmdline, const Utils::
QTC_ASSERT(m_process.state() == QProcess::NotRunning, return);
m_licenseIssues = false;
m_autId = 0;
+ m_outputMode = SingleLine;
+ m_multiLineContent.clear();
SquishProcessBase::start(cmdline, env);
}
@@ -130,11 +145,75 @@ void SquishRunnerProcess::onStdOutput(const QString &lineIn)
isPrompt = true;
m_autId = line.mid(7).toInt();
qCInfo(runnerLOG) << "AUT ID set" << m_autId << "(" << line << ")";
+ emit autIdRetrieved();
}
if (isPrompt)
emit interrupted(fileName, fileLine, fileColumn);
}
+void SquishRunnerProcess::handleMultiLineOutput(OutputMode mode)
+{
+ Utils::ExecuteOnDestruction atExit([this]{
+ m_multiLineContent.clear();
+ m_context.clear();
+ });
+
+ if (mode == MultiLineProperties) {
+ emit propertiesFetched(m_multiLineContent);
+ } else if (mode == MultiLineChildren) {
+ // TODO
+ }
+}
+
+void SquishRunnerProcess::onInspectorOutput(const QString &lineIn)
+{
+ QString line = lineIn;
+ line.chop(1); // line has a newline
+ if (line.startsWith("SSPY:"))
+ line = line.mid(5);
+ if (line.isEmpty()) // we have a prompt, that's fine
+ return;
+
+ if (m_outputMode != SingleLine) {
+ const OutputMode originalMode = m_outputMode;
+ if (line.startsWith("@end")) {
+ m_outputMode = SingleLine;
+ if (!QTC_GUARD(line.mid(6).chopped(1) == m_context)) { // messed up output
+ m_multiLineContent.clear();
+ m_context.clear();
+ return;
+ }
+ } else {
+ m_multiLineContent.append(line);
+ }
+ if (m_outputMode == SingleLine) // we reached the @end
+ handleMultiLineOutput(originalMode);
+ return;
+ }
+ if (line == "@ready")
+ return;
+ if (line.startsWith("@picked: ")) {
+ const QString value = line.mid(9);
+ emit objectPicked(value);
+ return;
+ }
+ if (line.startsWith("@startprop")) {
+ m_outputMode = MultiLineProperties;
+ m_context = line.mid(12).chopped(1);
+ return;
+ }
+ if (line.startsWith("@startobj")) {
+ m_outputMode = MultiLineChildren;
+ m_context = line.mid(11).chopped(1);
+ return;
+ }
+ if (line.contains("license acquisition")) {
+ emit logOutputReceived("Inspect: " + line);
+ return;
+ }
+// qDebug() << "unhandled" << line;
+}
+
static QString cmdToString(SquishRunnerProcess::RunnerCommand cmd)
{
switch (cmd) {
@@ -142,6 +221,7 @@ static QString cmdToString(SquishRunnerProcess::RunnerCommand cmd)
case SquishRunnerProcess::EndRecord: return "endrecord\n";
case SquishRunnerProcess::Exit: return "exit\n";
case SquishRunnerProcess::Next: return "next\n";
+ case SquishRunnerProcess::Pick: return "pick\n";
case SquishRunnerProcess::PrintVariables: return "print variables\n";
case SquishRunnerProcess::Return: return "return\n";
case SquishRunnerProcess::Step: return "step\n";
@@ -161,6 +241,16 @@ void SquishRunnerProcess::requestExpanded(const QString &variableName)
m_process.write("print variables +" + variableName + "\n");
}
+void SquishRunnerProcess::requestListObject(const QString &value)
+{
+ m_process.write("list objects " + maskedArgument(value) + "\n");
+}
+
+void SquishRunnerProcess::requestListProperties(const QString &value)
+{
+ m_process.write("list properties " + maskedArgument(value) + "\n");
+}
+
// FIXME: add/removal of breakpoints while debugging not handled yet
// FIXME: enabled state of breakpoints
Utils::Links SquishRunnerProcess::setBreakpoints(const QString &scriptExtension)
@@ -180,8 +270,7 @@ Utils::Links SquishRunnerProcess::setBreakpoints(const QString &scriptExtension)
continue;
// mask backslashes and spaces
- fileName.replace('\\', "\\\\");
- fileName.replace(' ', "\\x20");
+ fileName = maskedArgument(fileName);
auto line = gb->data(BreakpointLineColumn, Qt::DisplayRole).toInt();
QString cmd = "break ";
cmd.append(fileName);
diff --git a/src/plugins/squish/squishrunnerprocess.h b/src/plugins/squish/squishrunnerprocess.h
index b1c98220c1..624b986c6d 100644
--- a/src/plugins/squish/squishrunnerprocess.h
+++ b/src/plugins/squish/squishrunnerprocess.h
@@ -15,8 +15,8 @@ class SquishRunnerProcess : public SquishProcessBase
{
Q_OBJECT
public:
- enum RunnerCommand { Continue, EndRecord, Exit, Next, PrintVariables, Return, Step };
- enum RunnerMode { Run, StartAut, QueryServer, Record };
+ enum RunnerCommand { Continue, EndRecord, Exit, Next, Pick, PrintVariables, Return, Step };
+ enum RunnerMode { Run, StartAut, QueryServer, Record, Inspect };
enum RunnerError { InvalidSocket, MappedAutMissing };
explicit SquishRunnerProcess(QObject *parent = nullptr);
@@ -33,6 +33,8 @@ public:
void writeCommand(RunnerCommand cmd);
void requestExpanded(const QString &variableName);
+ void requestListObject(const QString &value);
+ void requestListProperties(const QString &value);
Utils::Links setBreakpoints(const QString &scriptExtension);
bool lastRunHadLicenseIssues() const { return m_licenseIssues; }
@@ -43,16 +45,26 @@ signals:
void runnerFinished();
void interrupted(const QString &fileName, int line, int column);
void localsUpdated(const QString &output);
+ void propertiesFetched(const QStringList &properties);
+ void objectPicked(const QString &output);
void runnerError(RunnerError error);
+ void autIdRetrieved();
protected:
void onDone() override;
void onErrorOutput() override;
private:
+ enum OutputMode { SingleLine, MultiLineChildren, MultiLineProperties };
+
void onStdOutput(const QString &line);
+ void handleMultiLineOutput(OutputMode mode);
+ void onInspectorOutput(const QString &line);
Utils::FilePath m_currentTestCasePath;
+ QStringList m_multiLineContent;
+ QString m_context;
+ OutputMode m_outputMode = SingleLine;
int m_autId = 0;
bool m_licenseIssues = false;
std::optional<RunnerMode> m_mode;
diff --git a/src/plugins/squish/squishserverprocess.cpp b/src/plugins/squish/squishserverprocess.cpp
index 4d7bb2ef97..9e4b974143 100644
--- a/src/plugins/squish/squishserverprocess.cpp
+++ b/src/plugins/squish/squishserverprocess.cpp
@@ -10,7 +10,7 @@ namespace Squish::Internal {
SquishServerProcess::SquishServerProcess(QObject *parent)
: SquishProcessBase(parent)
{
- connect(&m_process, &Utils::QtcProcess::readyReadStandardOutput,
+ connect(&m_process, &Utils::Process::readyReadStandardOutput,
this, &SquishServerProcess::onStandardOutput);
}
@@ -25,7 +25,7 @@ void SquishServerProcess::start(const Utils::CommandLine &commandLine,
void SquishServerProcess::stop()
{
if (m_process.state() != QProcess::NotRunning && m_serverPort > 0) {
- Utils::QtcProcess serverKiller;
+ Utils::Process serverKiller;
QStringList args;
args << "--stop" << "--port" << QString::number(m_serverPort);
serverKiller.setCommand({m_process.commandLine().executable(), args});
diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp
index 144e304608..e95ed33f48 100644
--- a/src/plugins/squish/squishsettings.cpp
+++ b/src/plugins/squish/squishsettings.cpp
@@ -127,14 +127,13 @@ SquishSettingsPage::SquishSettingsPage(SquishSettings *settings)
SquishSettings &s = *settings;
using namespace Layouting;
- Grid grid {
+ Form {
s.squishPath, br,
s.licensePath, br,
- Span {2, Row { s.local, s.serverHost, s.serverPort } }, br,
+ s.local, s.serverHost, s.serverPort, br,
s.verbose, br,
s.minimizeIDE, br,
- };
- Column { Row { grid }, st }.attachTo(widget);
+ }.attachTo(widget);
});
}
@@ -386,10 +385,10 @@ SquishServerSettingsWidget::SquishServerSettingsWidget(QWidget *parent)
using namespace Layouting;
Form grid {
&m_applicationsView, br,
- &m_serverSettings.autTimeout,
- &m_serverSettings.responseTimeout,
- &m_serverSettings.postMortemWaitTime,
- &m_serverSettings.animatedCursor,
+ &m_serverSettings.autTimeout, br,
+ &m_serverSettings.responseTimeout, br,
+ &m_serverSettings.postMortemWaitTime, br,
+ &m_serverSettings.animatedCursor, br,
};
Column buttonCol {
add,
diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp
index bc09e2fa6c..64cc98f780 100644
--- a/src/plugins/squish/squishtools.cpp
+++ b/src/plugins/squish/squishtools.cpp
@@ -133,6 +133,8 @@ SquishTools::SquishTools(QObject *parent)
this, &SquishTools::stopRecorder);
connect(&m_perspective, &SquishPerspective::runRequested,
this, &SquishTools::onRunnerRunRequested);
+ connect(&m_perspective, &SquishPerspective::inspectTriggered,
+ this, &SquishTools::onInspectTriggered);
}
SquishTools::~SquishTools()
@@ -440,6 +442,7 @@ void SquishTools::onRunnerStopped()
m_request = ServerStopRequested;
qCInfo(LOG) << "Stopping server from RunnerStopped (query)";
stopSquishServer();
+ return;
} else if (m_request == RecordTestRequested) {
if (m_secondaryRunner && m_secondaryRunner->isRunning()) {
stopRecorder();
@@ -448,7 +451,12 @@ void SquishTools::onRunnerStopped()
qCInfo(LOG) << "Stopping server from RunnerStopped (startaut)";
stopSquishServer();
}
- } else if (m_testCases.isEmpty() || (m_squishRunnerState == RunnerState::Canceled)) {
+ return;
+ }
+ // below only normal run of test case(s)
+ exitAndResetSecondaryRunner();
+
+ if (m_testCases.isEmpty() || (m_squishRunnerState == RunnerState::Canceled)) {
m_request = ServerStopRequested;
qCInfo(LOG) << "Stopping server from RunnerStopped";
stopSquishServer();
@@ -613,6 +621,50 @@ void SquishTools::setupAndStartRecorder()
m_secondaryRunner->start(cmd, squishEnvironment());
}
+void SquishTools::setupAndStartInspector()
+{
+ QTC_ASSERT(m_primaryRunner && m_primaryRunner->autId() != 0, return);
+ QTC_ASSERT(!m_secondaryRunner, return);
+
+ QStringList args;
+ if (!toolsSettings.isLocalServer)
+ args << "--host" << toolsSettings.serverHost;
+ args << "--port" << QString::number(m_serverProcess.port());
+ args << "--debugLog" << "alpw"; // TODO make this configurable?
+ args << "--inspect";
+ args << "--suitedir" << m_suitePath.toUserOutput();
+ args << "--autid" << QString::number(m_primaryRunner->autId());
+
+ m_secondaryRunner = new SquishRunnerProcess(this);
+ m_secondaryRunner->setupProcess(SquishRunnerProcess::Inspect);
+ const CommandLine cmd = {toolsSettings.runnerPath, args};
+ connect(m_secondaryRunner, &SquishRunnerProcess::logOutputReceived,
+ this, &SquishTools::logOutputReceived);
+ connect(m_secondaryRunner, &SquishRunnerProcess::objectPicked,
+ this, &SquishTools::objectPicked);
+ connect(m_secondaryRunner, &SquishRunnerProcess::propertiesFetched,
+ this, &SquishTools::propertiesFetched);
+ qCDebug(LOG) << "Inspector starting:" << cmd.toUserOutput();
+ m_secondaryRunner->start(cmd, squishEnvironment());
+}
+
+void SquishTools::exitAndResetSecondaryRunner()
+{
+ m_perspective.resetAutId();
+ if (m_secondaryRunner) {
+ m_secondaryRunner->writeCommand(SquishRunnerProcess::Exit);
+ m_secondaryRunner->deleteLater();
+ m_secondaryRunner = nullptr;
+ }
+}
+
+void SquishTools::onInspectTriggered()
+{
+ QTC_ASSERT(m_primaryRunner, return);
+ QTC_ASSERT(m_secondaryRunner, return);
+ m_secondaryRunner->writeCommand(SquishRunnerProcess::Pick);
+}
+
void SquishTools::stopRecorder()
{
QTC_ASSERT(m_secondaryRunner && m_secondaryRunner->isRunning(), return);
@@ -865,6 +917,7 @@ void SquishTools::handlePrompt(const QString &fileName, int line, int column)
case RunnerState::CancelRequested:
case RunnerState::CancelRequestedWhileInterrupted:
logAndChangeRunnerState(RunnerState::Canceled);
+ exitAndResetSecondaryRunner();
m_primaryRunner->writeCommand(SquishRunnerProcess::Exit);
clearLocationMarker();
break;
@@ -885,6 +938,9 @@ void SquishTools::handlePrompt(const QString &fileName, int line, int column)
const FilePath filePath = FilePath::fromUserInput(fileName);
Core::EditorManager::openEditorAt({filePath, line, column});
updateLocationMarker(filePath, line);
+ // looks like we need to start inspector while being interrupted?
+ if (!m_secondaryRunner && m_primaryRunner->autId() !=0)
+ setupAndStartInspector();
}
} else { // it's just some output coming from the server
if (m_squishRunnerState == RunnerState::Interrupted && !m_requestVarsTimer) {
@@ -909,6 +965,24 @@ void SquishTools::requestExpansion(const QString &name)
m_primaryRunner->requestExpanded(name);
}
+void SquishTools::requestExpansionForObject(const QString &value)
+{
+ QTC_ASSERT(m_primaryRunner, return);
+ if (m_squishRunnerState != RunnerState::Interrupted)
+ return;
+ QTC_ASSERT(m_secondaryRunner, return);
+ m_secondaryRunner->requestListObject(value);
+}
+
+void SquishTools::requestPropertiesForObject(const QString &value)
+{
+ QTC_ASSERT(m_primaryRunner, return);
+ if (m_squishRunnerState != RunnerState::Interrupted)
+ return;
+ QTC_ASSERT(m_secondaryRunner, return);
+ m_secondaryRunner->requestListProperties(value);
+}
+
bool SquishTools::shutdown()
{
QTC_ASSERT(!m_shutdownInitiated, return true);
@@ -1040,7 +1114,7 @@ void SquishTools::interruptRunner()
QTC_ASSERT(m_primaryRunner, return);
qint64 processId = m_primaryRunner->processId();
const CommandLine cmd(toolsSettings.processComPath, {QString::number(processId), "break"});
- QtcProcess process;
+ Process process;
process.setCommand(cmd);
process.start();
process.waitForFinished();
@@ -1056,7 +1130,7 @@ void SquishTools::terminateRunner()
QTC_ASSERT(m_primaryRunner, return);
qint64 processId = m_primaryRunner->processId();
const CommandLine cmd(toolsSettings.processComPath, {QString::number(processId), "terminate"});
- QtcProcess process;
+ Process process;
process.setCommand(cmd);
process.start();
process.waitForFinished();
@@ -1198,10 +1272,12 @@ bool SquishTools::setupRunnerPath()
void SquishTools::setupAndStartSquishRunnerProcess(const Utils::CommandLine &cmdLine)
{
QTC_ASSERT(m_primaryRunner, return);
- // avoid crashes on fast re-usage of QtcProcess
+ // avoid crashes on fast re-usage of Process
m_primaryRunner->closeProcess();
if (m_request == RunTestRequested) {
+ connect(m_primaryRunner, &SquishRunnerProcess::autIdRetrieved,
+ this, &SquishTools::autIdRetrieved);
// set up the file system watcher for being able to read the results.xml file
m_resultsFileWatcher = new QFileSystemWatcher;
// on 2nd run this directory exists and won't emit changes, so use the current subdirectory
diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h
index 19b57a7cb4..11bb9d3096 100644
--- a/src/plugins/squish/squishtools.h
+++ b/src/plugins/squish/squishtools.h
@@ -9,7 +9,7 @@
#include "suiteconf.h"
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QObject>
#include <QStringList>
@@ -60,10 +60,13 @@ public:
void requestSetSharedFolders(const Utils::FilePaths &sharedFolders);
void writeServerSettingsChanges(const QList<QStringList> &changes);
void requestExpansion(const QString &name);
+ void requestExpansionForObject(const QString &value);
+ void requestPropertiesForObject(const QString &value);
bool shutdown();
signals:
+ void autIdRetrieved();
void logOutputReceived(const QString &output);
void squishTestRunStarted();
void squishTestRunFinished();
@@ -71,6 +74,8 @@ signals:
void configChangesFailed(QProcess::ProcessError error);
void configChangesWritten();
void localsUpdated(const QString &output);
+ void objectPicked(const QString &output);
+ void propertiesFetched(const QStringList &properties);
void shutdownFinished();
private:
@@ -103,6 +108,9 @@ private:
void stopSquishServer();
void startSquishRunner();
void setupAndStartRecorder();
+ void setupAndStartInspector();
+ void exitAndResetSecondaryRunner();
+ void onInspectTriggered();
void stopRecorder();
void queryServer(RunnerQuery query);
void executeRunnerQuery();
diff --git a/src/plugins/squish/squishwizardpages.cpp b/src/plugins/squish/squishwizardpages.cpp
index 118c3217f2..e55a6ee8b2 100644
--- a/src/plugins/squish/squishwizardpages.cpp
+++ b/src/plugins/squish/squishwizardpages.cpp
@@ -48,7 +48,6 @@ bool SquishToolkitsPageFactory::validateData(Utils::Id typeId, const QVariant &,
SquishToolkitsPage::SquishToolkitsPage()
{
- resize(400, 300);
setTitle(Tr::tr("Create New Squish Test Suite"));
auto layout = new QVBoxLayout(this);
@@ -173,7 +172,6 @@ bool SquishScriptLanguagePageFactory::validateData(Utils::Id typeId, const QVari
SquishScriptLanguagePage::SquishScriptLanguagePage()
{
- resize(400, 300);
setTitle(Tr::tr("Create New Squish Test Suite"));
auto layout = new QHBoxLayout(this);
@@ -229,7 +227,6 @@ bool SquishAUTPageFactory::validateData(Utils::Id typeId, const QVariant &, QStr
SquishAUTPage::SquishAUTPage()
{
- resize(400, 300);
auto layout = new QVBoxLayout(this);
m_autCombo = new QComboBox(this);
layout->addWidget(m_autCombo);
diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp
index ad9aab9a9c..a0e5d07bab 100644
--- a/src/plugins/studiowelcome/examplecheckout.cpp
+++ b/src/plugins/studiowelcome/examplecheckout.cpp
@@ -7,6 +7,8 @@
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
+#include <qmldesignerbase/qmldesignerbaseplugin.h>
+
#include <utils/archive.h>
#include <utils/algorithm.h>
#include <utils/networkaccessmanager.h>
@@ -20,6 +22,7 @@
#include <projectexplorer/projectexplorer.h>
#include <qmldesigner/qmldesignerplugin.h>
+#include <qmldesigner/utils/fileextractor.h>
#include <QDialog>
#include <QFileDialog>
@@ -34,218 +37,57 @@
using namespace Utils;
-static bool enableDownload()
-{
- const QString lastQDSVersionEntry = "QML/Designer/EnableWelcomePageDownload";
- return Core::ICore::settings()->value(lastQDSVersionEntry, false).toBool();
-}
-
void ExampleCheckout::registerTypes()
{
static bool once = []() {
- FileDownloader::registerQmlType();
- FileExtractor::registerQmlType();
+ qmlRegisterType<QmlDesigner::FileDownloader>("ExampleCheckout", 1, 0, "FileDownloader");
+ qmlRegisterType<QmlDesigner::FileExtractor>("ExampleCheckout", 1, 0, "FileExtractor");
return true;
}();
QTC_ASSERT(once, ;);
}
-void FileDownloader::registerQmlType()
-{
- qmlRegisterType<FileDownloader>("ExampleCheckout", 1, 0, "FileDownloader");
-}
-
-FileDownloader::FileDownloader(QObject *parent)
- : QObject(parent)
-{}
-
-FileDownloader::~FileDownloader()
-{
- if (m_tempFile.exists())
- m_tempFile.remove();
-}
-
-void FileDownloader::start()
-{
- QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleDownload:" + name());
- m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip");
- m_tempFile.open(QIODevice::WriteOnly);
-
- auto request = QNetworkRequest(m_url);
- request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
- QNetworkRequest::UserVerifiedRedirectPolicy);
- m_reply = Utils::NetworkAccessManager::instance()->get(request);
-
- QNetworkReply::connect(m_reply, &QNetworkReply::readyRead, this, [this]() {
- m_tempFile.write(m_reply->readAll());
- });
-
- QNetworkReply::connect(m_reply,
- &QNetworkReply::downloadProgress,
- this,
- [this](qint64 current, qint64 max) {
- if (max == 0)
- return;
-
- m_progress = current * 100 / max;
- emit progressChanged();
- });
-
- QNetworkReply::connect(m_reply, &QNetworkReply::redirected, [this](const QUrl &) {
- emit m_reply->redirectAllowed();
- });
-
- QNetworkReply::connect(m_reply, &QNetworkReply::finished, this, [this]() {
- if (m_reply->error()) {
- if (m_tempFile.exists())
- m_tempFile.remove();
-
- if (m_reply->error() != QNetworkReply::OperationCanceledError) {
- qWarning() << Q_FUNC_INFO << m_url << m_reply->errorString();
- emit downloadFailed();
- } else {
- emit downloadCanceled();
- }
- } else {
- m_tempFile.flush();
- m_tempFile.close();
- m_finished = true;
- emit tempFileChanged();
- emit finishedChanged();
- }
-
- m_reply = nullptr;
- });
-}
-
-void FileDownloader::cancel()
-{
- if (m_reply)
- m_reply->abort();
-}
-
-void FileDownloader::setUrl(const QUrl &url)
-{
- m_url = url;
- emit nameChanged();
-
- probeUrl();
-}
-
-QUrl FileDownloader::url() const
-{
- return m_url;
-}
-
-bool FileDownloader::finished() const
-{
- return m_finished;
-}
-
-bool FileDownloader::error() const
-{
- return m_error;
-}
-
-QString FileDownloader::name() const
-{
- const QFileInfo fileInfo(m_url.path());
- return fileInfo.baseName();
-}
-
-QString FileDownloader::completeBaseName() const
+void DataModelDownloader::usageStatisticsDownloadExample(const QString &name)
{
- const QFileInfo fileInfo(m_url.path());
- return fileInfo.completeBaseName();
+ QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleDownload:" + name);
}
-int FileDownloader::progress() const
+bool DataModelDownloader::downloadEnabled() const
{
- return m_progress;
-}
-
-QString FileDownloader::tempFile() const
-{
- return QFileInfo(m_tempFile).canonicalFilePath();
-}
-
-QDateTime FileDownloader::lastModified() const
-{
- return m_lastModified;
+ const QString lastQDSVersionEntry = "QML/Designer/EnableWelcomePageDownload";
+ return Core::ICore::settings()->value(lastQDSVersionEntry, false).toBool();
}
-bool FileDownloader::available() const
+QString DataModelDownloader::targetPath() const
{
- return m_available;
+ return QmlDesigner::QmlDesignerBasePlugin::examplesPathSetting();
}
-void FileDownloader::probeUrl()
+static Utils::FilePath tempFilePath()
{
- if (!enableDownload()) {
- m_available = false;
- emit availableChanged();
- return;
- }
-
- auto request = QNetworkRequest(m_url);
- request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
- QNetworkRequest::UserVerifiedRedirectPolicy);
- QNetworkReply *reply = Utils::NetworkAccessManager::instance()->head(request);
-
- QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &) {
- emit reply->redirectAllowed();
- });
-
- QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
- if (reply->error())
- return;
-
- m_lastModified = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
- emit lastModifiedChanged();
-
- m_available = true;
- emit availableChanged();
- });
+ QStandardPaths::StandardLocation location = QStandardPaths::CacheLocation;
- QNetworkReply::connect(reply,
- &QNetworkReply::errorOccurred,
- this,
- [this](QNetworkReply::NetworkError) {
- QQmlData *data = QQmlData::get(this, false);
- if (!data) {
- qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr.";
- return;
- }
-
- if (QQmlData::wasDeleted(this)) {
- qDebug() << Q_FUNC_INFO << "FileDownloader was deleted.";
- return;
- }
-
- m_available = false;
- emit availableChanged();
- });
+ return Utils::FilePath::fromString(QStandardPaths::writableLocation(location))
+ .pathAppended("QtDesignStudio");
}
-
-FileExtractor::FileExtractor(QObject *parent)
- : QObject(parent)
+DataModelDownloader::DataModelDownloader(QObject * /* parent */)
{
- m_targetPath = Utils::FilePath::fromString(
- StudioWelcome::Internal::StudioWelcomePlugin::examplesPathSetting());
-
- m_timer.setInterval(100);
- m_timer.setSingleShot(false);
+ auto fileInfo = targetFolder().toFileInfo();
+ m_birthTime = fileInfo.lastModified();
+ m_exists = fileInfo.exists();
+ m_fileDownloader.setProbeUrl(true);
- QObject::connect(this, &FileExtractor::targetFolderExistsChanged, this, [this]() {
- if (targetFolderExists()) {
- m_birthTime = QFileInfo(m_targetPath.toString() + "/" + m_archiveName).birthTime();
- } else
- m_birthTime = QDateTime();
+ connect(&m_fileDownloader,
+ &QmlDesigner::FileDownloader::progressChanged,
+ this,
+ &DataModelDownloader::progressChanged);
- emit birthTimeChanged();
- });
+ connect(&m_fileDownloader,
+ &QmlDesigner::FileDownloader::downloadFailed,
+ this,
+ &DataModelDownloader::downloadFailed);
const ExtensionSystem::PluginSpec *pluginSpec
= Utils::findOrDefault(ExtensionSystem::PluginManager::plugins(),
@@ -263,244 +105,66 @@ FileExtractor::FileExtractor(QObject *parent)
auto studioWelcomePlugin = qobject_cast<StudioWelcome::Internal::StudioWelcomePlugin *>(plugin);
if (studioWelcomePlugin) {
- QObject::connect(studioWelcomePlugin,
- &StudioWelcome::Internal::StudioWelcomePlugin::examplesDownloadPathChanged,
+ QObject::connect(QmlDesigner::QmlDesignerBasePlugin::instance(),
+ &QmlDesigner::QmlDesignerBasePlugin::examplesDownloadPathChanged,
this,
- [this](const QString &path) {
- m_targetPath = Utils::FilePath::fromString(path);
- emit targetPathChanged();
- emit targetFolderExistsChanged();
- });
+ &DataModelDownloader::targetPathMustChange);
}
-}
-
-FileExtractor::~FileExtractor() {}
-
-void FileExtractor::registerQmlType()
-{
- qmlRegisterType<FileExtractor>("ExampleCheckout", 1, 0, "FileExtractor");
-}
-
-QString FileExtractor::targetPath() const
-{
- return m_targetPath.toUserOutput();
-}
-
-void FileExtractor::browse()
-{
- const FilePath path =
- FileUtils::getExistingDirectory(nullptr, tr("Choose Directory"), m_targetPath);
-
- if (!path.isEmpty())
- m_targetPath = path;
-
- emit targetPathChanged();
- emit targetFolderExistsChanged();
-}
-
-void FileExtractor::setSourceFile(QString &sourceFilePath)
-{
- m_sourceFile = Utils::FilePath::fromString(sourceFilePath);
- emit targetFolderExistsChanged();
-}
-
-void FileExtractor::setArchiveName(QString &filePath)
-{
- m_archiveName = filePath;
- emit targetFolderExistsChanged();
-}
-
-const QString FileExtractor::detailedText()
-{
- return m_detailedText;
-}
-
-bool FileExtractor::finished() const
-{
- return m_finished;
-}
-QString FileExtractor::currentFile() const
-{
- return m_currentFile;
-}
-
-QString FileExtractor::size() const
-{
- return m_size;
-}
-
-QString FileExtractor::count() const
-{
- return m_count;
-}
-
-bool FileExtractor::targetFolderExists() const
-{
- return QFileInfo::exists(m_targetPath.toString() + "/" + m_archiveName);
-}
-
-int FileExtractor::progress() const
-{
- return m_progress;
-}
-
-QDateTime FileExtractor::birthTime() const
-{
- return m_birthTime;
-}
-
-QString FileExtractor::archiveName() const
-{
- return m_archiveName;
-}
-
-QString FileExtractor::sourceFile() const
-{
- return m_sourceFile.toString();
-}
-
-void FileExtractor::extract()
-{
- const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName;
-
- // If the target directory already exists, remove it and its content
- QDir targetDir(targetFolder);
- if (targetDir.exists())
- targetDir.removeRecursively();
-
- // Create a new directory to generate a proper creation date
- targetDir.mkdir(targetFolder);
-
- Utils::Archive *archive = new Utils::Archive(m_sourceFile, m_targetPath);
- QTC_ASSERT(archive->isValid(), delete archive; return);
-
- m_timer.start();
- qint64 bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable();
- qint64 compressedSize = QFileInfo(m_sourceFile.toString()).size();
-
- QTimer::connect(
- &m_timer, &QTimer::timeout, this, [this, bytesBefore, targetFolder, compressedSize]() {
- static QHash<QString, int> hash;
- QDirIterator it(targetFolder, {"*.*"}, QDir::Files, QDirIterator::Subdirectories);
-
- int count = 0;
- while (it.hasNext()) {
- if (!hash.contains(it.fileName())) {
- m_currentFile = it.fileName();
- hash.insert(m_currentFile, 0);
- emit currentFileChanged();
- }
- it.next();
- count++;
- }
-
- qint64 currentSize = bytesBefore
- - QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable();
-
- // We can not get the uncompressed size of the archive yet, that is why we use an
- // approximation. We assume a 50% compression rate.
- m_progress = std::min(100ll, currentSize * 100 / compressedSize * 2);
- emit progressChanged();
-
- m_size = QString::number(currentSize);
- m_count = QString::number(count);
- emit sizeChanged();
- });
-
- QObject::connect(archive, &Utils::Archive::outputReceived, this, [this](const QString &output) {
- m_detailedText += output;
- emit detailedTextChanged();
- });
-
- QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) {
- archive->deleteLater();
- m_finished = ret;
- m_timer.stop();
-
- m_progress = 100;
- emit progressChanged();
-
- emit targetFolderExistsChanged();
- emit finishedChanged();
- QTC_CHECK(ret);
+ connect(&m_fileDownloader, &QmlDesigner::FileDownloader::finishedChanged, this, [this]() {
+ m_started = false;
+
+ if (m_fileDownloader.finished()) {
+ const Utils::FilePath archiveFile = Utils::FilePath::fromString(
+ m_fileDownloader.outputFile());
+ QTC_ASSERT(Utils::Archive::supportsFile(archiveFile), return );
+ auto archive = new Utils::Archive(archiveFile, tempFilePath());
+ QTC_ASSERT(archive->isValid(), delete archive; return );
+ QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) {
+ QTC_CHECK(ret);
+ archive->deleteLater();
+ emit finished();
+ });
+ archive->unarchive();
+ }
});
- archive->unarchive();
}
-static Utils::FilePath tempFilePath()
+void DataModelDownloader::onAvailableChanged()
{
- QStandardPaths::StandardLocation location = QStandardPaths::CacheLocation;
+ m_available = m_fileDownloader.available();
- return Utils::FilePath::fromString(QStandardPaths::writableLocation(location))
- .pathAppended("QtDesignStudio");
-}
+ emit availableChanged();
-DataModelDownloader::DataModelDownloader(QObject * /* parent */)
-{
- auto fileInfo = targetFolder().toFileInfo();
- m_birthTime = fileInfo.lastModified();
- m_exists = fileInfo.exists();
+ if (!m_available) {
+ qWarning() << m_fileDownloader.url() << "failed to download";
+ return;
+ }
- connect(&m_fileDownloader,
- &FileDownloader::progressChanged,
- this,
- &DataModelDownloader::progressChanged);
+ if (!m_forceDownload && (m_fileDownloader.lastModified() <= m_birthTime))
+ return;
- connect(&m_fileDownloader,
- &FileDownloader::downloadFailed,
- this,
- &DataModelDownloader::downloadFailed);
+ m_started = true;
+
+ m_fileDownloader.start();
}
bool DataModelDownloader::start()
{
-
- if (!enableDownload()) {
+ if (!downloadEnabled()) {
m_available = false;
emit availableChanged();
return false;
}
+ m_fileDownloader.setDownloadEnabled(true);
m_fileDownloader.setUrl(QUrl::fromUserInput(
"https://download.qt.io/learning/examples/qtdesignstudio/dataImports.zip"));
- bool started = false;
-
- connect(&m_fileDownloader, &FileDownloader::availableChanged, this, [this, &started]() {
-
- m_available = m_fileDownloader.available();
+ m_started = false;
- emit availableChanged();
-
- if (!m_available) {
- qWarning() << m_fileDownloader.url() << "failed to download";
- return;
- }
-
- if (!m_forceDownload && (m_fileDownloader.lastModified() <= m_birthTime))
- return;
-
- started = true;
-
- m_fileDownloader.start();
- connect(&m_fileDownloader, &FileDownloader::finishedChanged, this, [this]() {
- if (m_fileDownloader.finished()) {
- const Utils::FilePath archiveFile = Utils::FilePath::fromString(
- m_fileDownloader.tempFile());
- QTC_ASSERT(Utils::Archive::supportsFile(archiveFile), return );
- auto archive = new Utils::Archive(archiveFile, tempFilePath());
- QTC_ASSERT(archive->isValid(), delete archive; return );
- QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) {
- QTC_CHECK(ret);
- archive->deleteLater();
- emit finished();
- });
- archive->unarchive();
- }
- });
- });
- return started;
+ connect(&m_fileDownloader, &QmlDesigner::FileDownloader::availableChanged, this, &DataModelDownloader::onAvailableChanged);
+ return m_started;
}
bool DataModelDownloader::exists() const
diff --git a/src/plugins/studiowelcome/examplecheckout.h b/src/plugins/studiowelcome/examplecheckout.h
index 7e22daf002..4726796bfe 100644
--- a/src/plugins/studiowelcome/examplecheckout.h
+++ b/src/plugins/studiowelcome/examplecheckout.h
@@ -4,6 +4,7 @@
#pragma once
#include <utils/fileutils.h>
+#include <qmldesigner/utils/filedownloader.h>
#include <QObject>
#include <QTimer>
@@ -16,135 +17,15 @@ struct ExampleCheckout
static void registerTypes();
};
-class FileExtractor : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QString targetPath READ targetPath NOTIFY targetPathChanged)
- Q_PROPERTY(QString archiveName READ archiveName WRITE setArchiveName)
- Q_PROPERTY(QString detailedText READ detailedText NOTIFY detailedTextChanged)
- Q_PROPERTY(QString currentFile READ currentFile NOTIFY currentFileChanged)
- Q_PROPERTY(QString size READ size NOTIFY sizeChanged)
- Q_PROPERTY(QString count READ count NOTIFY sizeChanged)
- Q_PROPERTY(QString sourceFile READ sourceFile WRITE setSourceFile)
- Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
- Q_PROPERTY(bool targetFolderExists READ targetFolderExists NOTIFY targetFolderExistsChanged)
- Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
- Q_PROPERTY(QDateTime birthTime READ birthTime NOTIFY birthTimeChanged)
-
-public:
- explicit FileExtractor(QObject *parent = nullptr);
- ~FileExtractor();
-
- static void registerQmlType();
-
- QString targetPath() const;
- void setSourceFile(QString &sourceFilePath);
- void setArchiveName(QString &filePath);
- const QString detailedText();
- bool finished() const;
- QString currentFile() const;
- QString size() const;
- QString count() const;
- bool targetFolderExists() const;
- int progress() const;
- QDateTime birthTime() const;
-
- QString sourceFile() const;
- QString archiveName() const;
-
- Q_INVOKABLE void browse();
- Q_INVOKABLE void extract();
-
-signals:
- void targetPathChanged();
- void detailedTextChanged();
- void finishedChanged();
- void currentFileChanged();
- void sizeChanged();
- void targetFolderExistsChanged();
- void progressChanged();
- void birthTimeChanged();
-
-private:
- Utils::FilePath m_targetPath;
- Utils::FilePath m_sourceFile;
- QString m_detailedText;
- bool m_finished = false;
- QTimer m_timer;
- QString m_currentFile;
- QString m_size;
- QString m_count;
- QString m_archiveName;
- int m_progress = 0;
- QDateTime m_birthTime;
-};
-
-class FileDownloader : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QUrl url READ url WRITE setUrl)
- Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
- Q_PROPERTY(bool error READ error NOTIFY errorChanged)
- Q_PROPERTY(QString name READ name NOTIFY nameChanged)
- Q_PROPERTY(QString completeBaseName READ completeBaseName NOTIFY nameChanged)
- Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
- Q_PROPERTY(QString tempFile READ tempFile NOTIFY tempFileChanged)
- Q_PROPERTY(QDateTime lastModified READ lastModified NOTIFY lastModifiedChanged)
- Q_PROPERTY(bool available READ available NOTIFY availableChanged)
-
-public:
- explicit FileDownloader(QObject *parent = nullptr);
-
- ~FileDownloader();
-
- void setUrl(const QUrl &url);
- QUrl url() const;
- bool finished() const;
- bool error() const;
- static void registerQmlType();
- QString name() const;
- QString completeBaseName() const;
- int progress() const;
- QString tempFile() const;
- QDateTime lastModified() const;
- bool available() const;
-
- Q_INVOKABLE void start();
- Q_INVOKABLE void cancel();
-
-signals:
- void finishedChanged();
- void errorChanged();
- void nameChanged();
- void progressChanged();
- void tempFileChanged();
- void downloadFailed();
- void lastModifiedChanged();
- void availableChanged();
-
- void downloadCanceled();
-
-private:
- void probeUrl();
-
- QUrl m_url;
- bool m_finished = false;
- bool m_error = false;
- int m_progress = 0;
- QFile m_tempFile;
- QDateTime m_lastModified;
- bool m_available = false;
-
- QNetworkReply *m_reply = nullptr;
-};
-
class DataModelDownloader : public QObject
{
Q_OBJECT
public:
+ Q_INVOKABLE void usageStatisticsDownloadExample(const QString &name);
+ Q_INVOKABLE bool downloadEnabled() const;
+ Q_INVOKABLE QString targetPath() const;
+
explicit DataModelDownloader(QObject *parent = nullptr);
bool start();
bool exists() const;
@@ -158,11 +39,14 @@ signals:
void availableChanged();
void progressChanged();
void downloadFailed();
+ void targetPathMustChange(const QString &newPath);
private:
- FileDownloader m_fileDownloader;
+ void onAvailableChanged();
+ QmlDesigner::FileDownloader m_fileDownloader;
QDateTime m_birthTime;
bool m_exists = false;
bool m_available = false;
bool m_forceDownload = false;
+ bool m_started = false;
};
diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp
index d53a86aaa6..964ae4db10 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.cpp
+++ b/src/plugins/studiowelcome/qdsnewdialog.cpp
@@ -11,6 +11,7 @@
#include <utils/qtcassert.h>
#include <qmldesigner/components/componentcore/theme.h>
+#include <qmldesigner/qmldesignerconstants.h>
#include "createproject.h"
#include "newprojectdialogimageprovider.h"
@@ -61,6 +62,7 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
m_recentsStore.setReverseOrder();
m_recentsStore.setMaximum(10);
+ m_dialog->setObjectName(QmlDesigner::Constants::OBJECT_NAME_NEW_DIALOG);
m_dialog->setResizeMode(QQuickWidget::SizeRootObjectToView); // SizeViewToRootObject
m_dialog->engine()->addImageProvider(QStringLiteral("newprojectdialog_library"),
new Internal::NewProjectDialogImageProvider());
@@ -86,10 +88,10 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
m_dialog->installEventFilter(this);
- QObject::connect(&m_wizard, &WizardHandler::wizardCreationFailed, this, [this]() {
+ QObject::connect(&m_wizard, &WizardHandler::wizardCreationFailed, this, [this] {
QMessageBox::critical(m_dialog, tr("New Project"), tr("Failed to initialize data."));
reject();
- delete this;
+ deleteLater();
});
QObject::connect(m_styleModel.data(), &StyleModel::modelAboutToBeReset, this, [this]() {
@@ -390,7 +392,7 @@ void QdsNewDialog::accept()
create.withName(m_qmlProjectName)
.atLocation(m_qmlProjectLocation)
.withScreenSizes(m_qmlScreenSizeIndex, m_qmlCustomWidth, m_qmlCustomHeight)
- .withStyle(m_qmlStyleIndex)
+ .withStyle(getStyleIndex())
.useQtVirtualKeyboard(m_qmlUseVirtualKeyboard)
.saveAsDefaultLocation(m_qmlSaveAsDefaultLocation)
.withTargetQtVersion(m_qmlTargetQtVersionIndex)
@@ -450,7 +452,7 @@ UserPresetData QdsNewDialog::currentUserPresetData(const QString &displayName) c
targetQtVersion = m_wizard.targetQtVersionName(m_qmlTargetQtVersionIndex);
if (m_wizard.haveStyleModel())
- styleName = m_wizard.styleName(m_qmlStyleIndex);
+ styleName = m_wizard.styleName(getStyleIndex());
if (m_wizard.haveVirtualKeyboard())
useVirtualKeyboard = m_qmlUseVirtualKeyboard;
diff --git a/src/plugins/studiowelcome/qml/downloaddialog/main.qml b/src/plugins/studiowelcome/qml/downloaddialog/main.qml
index 7f41fee148..5ed8e36f60 100644
--- a/src/plugins/studiowelcome/qml/downloaddialog/main.qml
+++ b/src/plugins/studiowelcome/qml/downloaddialog/main.qml
@@ -40,7 +40,7 @@ Rectangle {
FileExtractor {
id: fileExtractor
archiveName: root.completeBaseName.length === 0 ? downloader.completeBaseName : root.completeBaseName
- sourceFile: root.tempFile.length === 0 ? downloader.tempFile : root.tempFile
+ sourceFile: root.tempFile.length === 0 ? downloader.outputFile : root.tempFile
}
FileDownloader {
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
index 17acc9b651..ba58c0bd74 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
@@ -10,14 +10,18 @@
#include <coreplugin/coreconstants.h>
#include <coreplugin/dialogs/restartdialog.h>
-#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/documentmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/helpmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/imode.h>
#include <coreplugin/modemanager.h>
+#include "projectexplorer/target.h"
+#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
@@ -25,10 +29,16 @@
#include <qmlprojectmanager/projectfilecontenttools.h>
#include <qmlprojectmanager/qmlproject.h>
+#include <qtsupport/baseqtversion.h>
+#include <qtsupport/qtkitinformation.h>
+
#include <qmldesigner/components/componentcore/theme.h>
#include <qmldesigner/dynamiclicensecheck.h>
+#include <qmldesigner/qmldesignerconstants.h>
#include <qmldesigner/qmldesignerplugin.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
+
#include <utils/checkablemessagebox.h>
#include <utils/hostosinfo.h>
#include <utils/icon.h>
@@ -38,14 +48,11 @@
#include <QAbstractListModel>
#include <QApplication>
-#include <QCheckBox>
#include <QDesktopServices>
#include <QFileInfo>
#include <QFontDatabase>
-#include <QGroupBox>
#include <QMainWindow>
#include <QPointer>
-#include <QPushButton>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItem>
@@ -90,12 +97,35 @@ const char STATISTICS_COLLECTION_MODE[] = "StatisticsCollectionMode";
const char NO_TELEMETRY[] = "NoTelemetry";
const char CRASH_REPORTER_SETTING[] = "CrashReportingEnabled";
-const char EXAMPLES_DOWNLOAD_PATH[] = "StudioWelcome/ExamplesDownloadPath";
-
QPointer<QQuickView> s_viewWindow = nullptr;
QPointer<QQuickWidget> s_viewWidget = nullptr;
static StudioWelcomePlugin *s_pluginInstance = nullptr;
+static Utils::FilePath getMainUiFileWithFallback()
+{
+ auto project = ProjectExplorer::ProjectManager::startupProject();
+ if (!project)
+ return {};
+
+ if (!project->activeTarget())
+ return {};
+
+ auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
+ project->activeTarget()->buildSystem());
+
+ auto mainUiFile = qmlBuildSystem->mainUiFilePath();
+ if (mainUiFile.exists())
+ return mainUiFile;
+
+ const Utils::FilePaths uiFiles = project->files([&](const ProjectExplorer::Node *node) {
+ return node->filePath().completeSuffix() == "ui.qml";
+ });
+ if (!uiFiles.isEmpty())
+ return uiFiles.first();
+
+ return {};
+}
+
std::unique_ptr<QSettings> makeUserFeedbackSettings()
{
QStringList domain = QCoreApplication::organizationDomain().split(QLatin1Char('.'));
@@ -223,8 +253,15 @@ public:
m_blockOpenRecent = true;
const FilePath projectFile = FilePath::fromVariant(data(index(row, 0), ProjectModel::FilePathRole));
- if (projectFile.exists())
- ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile);
+ if (projectFile.exists()) {
+ const ProjectExplorerPlugin::OpenProjectResult result
+ = ProjectExplorer::ProjectExplorerPlugin::openProject(projectFile);
+ if (!result && !result.alreadyOpen().isEmpty()) {
+ const auto mainUiFile = getMainUiFileWithFallback();
+ if (mainUiFile.exists())
+ Core::EditorManager::openEditor(mainUiFile, Utils::Id());
+ };
+ }
resetProjects();
}
@@ -241,6 +278,7 @@ public:
const QString &formFile,
const QString &explicitQmlproject)
{
+ QTC_ASSERT(!exampleName.isEmpty(), return );
QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleOpened:"
+ exampleName);
@@ -525,6 +563,7 @@ void StudioWelcomePlugin::extensionsInitialized()
if (showSplashScreen()) {
connect(Core::ICore::instance(), &Core::ICore::coreOpened, this, [this] {
+ Core::ModeManager::setModeStyle(Core::ModeManager::Style::Hidden);
if (Utils::HostOsInfo::isMacHost()) {
s_viewWindow = new QQuickView(Core::ICore::mainWindow()->windowHandle());
@@ -563,11 +602,13 @@ void StudioWelcomePlugin::extensionsInitialized()
s_viewWindow->show();
s_viewWindow->requestActivate();
+ s_viewWindow->setObjectName(QmlDesigner::Constants::OBJECT_NAME_SPLASH_SCREEN);
} else {
s_viewWidget = new QQuickWidget(Core::ICore::dialogParent());
s_viewWidget->setWindowFlag(Qt::SplashScreen, true);
+ s_viewWidget->setObjectName(QmlDesigner::Constants::OBJECT_NAME_SPLASH_SCREEN);
s_viewWidget->setWindowModality(Qt::ApplicationModal);
s_viewWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
s_viewWidget->engine()->addImportPath("qrc:/studiofonts");
@@ -601,24 +642,36 @@ void StudioWelcomePlugin::extensionsInitialized()
bool StudioWelcomePlugin::delayedInitialize()
{
- return true;
-}
+ QTimer::singleShot(2000, this, []() {
+ auto modelManager = QmlJS::ModelManagerInterface::instance();
+ if (!modelManager)
+ return;
-Utils::FilePath StudioWelcomePlugin::defaultExamplesPath()
-{
- QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost()
- ? QStandardPaths::HomeLocation
- : QStandardPaths::DocumentsLocation;
+ QmlJS::PathsAndLanguages importPaths;
- return Utils::FilePath::fromString(QStandardPaths::writableLocation(location))
- .pathAppended("QtDesignStudio");
-}
+ const QList<Kit *> kits = Utils::filtered(KitManager::kits(), [](const Kit *k) {
+ const QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
+ const bool isQt6 = version && version->qtVersion().majorVersion() == 6;
-QString StudioWelcomePlugin::examplesPathSetting()
-{
- return Core::ICore::settings()
- ->value(EXAMPLES_DOWNLOAD_PATH, defaultExamplesPath().toString())
- .toString();
+ return isQt6
+ && ProjectExplorer::DeviceTypeKitAspect::deviceTypeId(k)
+ == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
+ });
+
+ for (const Kit *kit : kits) {
+ const QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit);
+
+ const Utils::FilePath qmlPath = version->qmlPath();
+ importPaths.maybeInsert(qmlPath, QmlJS::Dialect::QmlQtQuick2);
+
+ QmlJS::ModelManagerInterface::importScan(QmlJS::ModelManagerInterface::workingCopy(),
+ importPaths,
+ modelManager,
+ false);
+ }
+ });
+
+ return true;
}
WelcomeMode::WelcomeMode()
@@ -706,36 +759,6 @@ WelcomeMode::WelcomeMode()
[](const QString &path) { return QFileInfo::exists(path); }));
}
-static bool hideBuildMenuSetting()
-{
- return Core::ICore::settings()
- ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD, false)
- .toBool();
-}
-
-static bool hideDebugMenuSetting()
-{
- return Core::ICore::settings()
- ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, false)
- .toBool();
-}
-
-static bool hideAnalyzeMenuSetting()
-{
- return Core::ICore::settings()
- ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE, false)
- .toBool();
-}
-
-void setSettingIfDifferent(const QString &key, bool value, bool &dirty)
-{
- QSettings *s = Core::ICore::settings();
- if (s->value(key, false).toBool() != value) {
- dirty = true;
- s->setValue(key, value);
- }
-}
-
WelcomeMode::~WelcomeMode()
{
delete m_modeWidget;
@@ -751,10 +774,12 @@ void WelcomeMode::setupQuickWidget(const QString &welcomePagePath)
m_quickWidget->setSource(
QUrl::fromLocalFile(QLatin1String(STUDIO_QML_PATH) + "welcomepage/main.qml"));
#else
+ m_quickWidget->rootContext()->setContextProperty("$dataModel", m_dataModelDownloader);
m_quickWidget->engine()->addImportPath("qrc:/qml/welcomepage/imports");
m_quickWidget->setSource(QUrl("qrc:/qml/welcomepage/main.qml"));
#endif
} else {
+ m_quickWidget->rootContext()->setContextProperty("$dataModel", m_dataModelDownloader);
m_quickWidget->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources/imports").toString());
@@ -778,6 +803,7 @@ void WelcomeMode::createQuickWidget()
m_quickWidget = new QQuickWidget;
m_quickWidget->setMinimumSize(640, 480);
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_quickWidget->setObjectName(QmlDesigner::Constants::OBJECT_NAME_WELCOME_PAGE);
QmlDesigner::Theme::setupTheme(m_quickWidget->engine());
m_quickWidget->engine()->addImportPath("qrc:/studiofonts");
@@ -786,104 +812,6 @@ void WelcomeMode::createQuickWidget()
m_quickWidget->engine()->setOutputWarningsToStandardError(false);
}
-StudioSettingsPage::StudioSettingsPage()
- : m_buildCheckBox(new QCheckBox(tr("Build")))
- , m_debugCheckBox(new QCheckBox(tr("Debug")))
- , m_analyzeCheckBox(new QCheckBox(tr("Analyze")))
- , m_pathChooser(new Utils::PathChooser())
-{
- const QString toolTip = tr(
- "Hide top-level menus with advanced functionality to simplify the UI. <b>Build</b> is "
- "generally not required in the context of Qt Design Studio. <b>Debug</b> and <b>Analyze</b> "
- "are only required for debugging and profiling.");
-
- QVBoxLayout *boxLayout = new QVBoxLayout();
- setLayout(boxLayout);
- auto groupBox = new QGroupBox(tr("Hide Menu"));
- groupBox->setToolTip(toolTip);
- boxLayout->addWidget(groupBox);
-
- auto verticalLayout = new QVBoxLayout();
- groupBox->setLayout(verticalLayout);
-
- m_buildCheckBox->setToolTip(toolTip);
- m_debugCheckBox->setToolTip(toolTip);
- m_analyzeCheckBox->setToolTip(toolTip);
-
- verticalLayout->addWidget(m_buildCheckBox);
- verticalLayout->addWidget(m_debugCheckBox);
- verticalLayout->addWidget(m_analyzeCheckBox);
- verticalLayout->addSpacerItem(
- new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum));
-
- m_buildCheckBox->setChecked(hideBuildMenuSetting());
- m_debugCheckBox->setChecked(hideDebugMenuSetting());
- m_analyzeCheckBox->setChecked(hideAnalyzeMenuSetting());
-
- auto examplesGroupBox = new QGroupBox(tr("Examples"));
- boxLayout->addWidget(examplesGroupBox);
-
- auto horizontalLayout = new QHBoxLayout();
- examplesGroupBox->setLayout(horizontalLayout);
-
- auto label = new QLabel(tr("Examples path:"));
- m_pathChooser->setFilePath(
- Utils::FilePath::fromString(StudioWelcomePlugin::examplesPathSetting()));
- auto resetButton = new QPushButton(tr("Reset Path"));
-
- connect(resetButton, &QPushButton::clicked, this, [this]() {
- m_pathChooser->setFilePath(StudioWelcomePlugin::defaultExamplesPath());
- });
-
- horizontalLayout->addWidget(label);
- horizontalLayout->addWidget(m_pathChooser);
- horizontalLayout->addWidget(resetButton);
-
-
- boxLayout->addSpacerItem(
- new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding));
-}
-
-void StudioSettingsPage::apply()
-{
- bool dirty = false;
-
- setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_BUILD,
- m_buildCheckBox->isChecked(),
- dirty);
-
- setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG,
- m_debugCheckBox->isChecked(),
- dirty);
-
- setSettingIfDifferent(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_ANALYZE,
- m_analyzeCheckBox->isChecked(),
- dirty);
-
-
- if (dirty) {
- const QString restartText = tr("The menu visibility change will take effect after restart.");
- Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
- restartDialog.exec();
- }
-
- QSettings *s = Core::ICore::settings();
- const QString value = m_pathChooser->filePath().toString();
-
- if (s->value(EXAMPLES_DOWNLOAD_PATH, false).toString() != value) {
- s->setValue(EXAMPLES_DOWNLOAD_PATH, value);
- emit s_pluginInstance->examplesDownloadPathChanged(value);
- }
-}
-
-StudioWelcomeSettingsPage::StudioWelcomeSettingsPage()
-{
- setId("Z.StudioWelcome.Settings");
- setDisplayName(tr("Qt Design Studio Configuration"));
- setCategory(Core::Constants::SETTINGS_CATEGORY_CORE);
- setWidgetCreator([] { return new StudioSettingsPage; });
-}
-
} // namespace Internal
} // namespace StudioWelcome
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.h b/src/plugins/studiowelcome/studiowelcomeplugin.h
index 5f0a2a4dc6..51ec59bafe 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.h
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.h
@@ -5,39 +5,12 @@
#include <extensionsystem/iplugin.h>
#include <coreplugin/dialogs/ioptionspage.h>
-#include <utils/pathchooser.h>
#include <QTimer>
-QT_FORWARD_DECLARE_CLASS(QCheckBox)
-
namespace StudioWelcome {
namespace Internal {
-class StudioSettingsPage : public Core::IOptionsPageWidget
-{
- Q_OBJECT
-
-public:
- void apply() final;
-
- StudioSettingsPage();
-
-private:
- QCheckBox *m_buildCheckBox;
- QCheckBox *m_debugCheckBox;
- QCheckBox *m_analyzeCheckBox;
- Utils::PathChooser *m_pathChooser;
-};
-
-class StudioWelcomeSettingsPage : public Core::IOptionsPage
-{
- Q_OBJECT
-
-public:
- StudioWelcomeSettingsPage();
-};
-
class StudioWelcomePlugin final : public ExtensionSystem::IPlugin
{
Q_OBJECT
@@ -54,15 +27,8 @@ public:
void extensionsInitialized() override;
bool delayedInitialize() override;
- static Utils::FilePath defaultExamplesPath();
- static QString examplesPathSetting();
-
-signals:
- void examplesDownloadPathChanged(const QString &path);
-
private:
class WelcomeMode *m_welcomeMode = nullptr;
- StudioWelcomeSettingsPage m_settingsPage;
};
} // namespace Internal
diff --git a/src/plugins/studiowelcome/stylemodel.cpp b/src/plugins/studiowelcome/stylemodel.cpp
index d93fc5fded..1198052994 100644
--- a/src/plugins/studiowelcome/stylemodel.cpp
+++ b/src/plugins/studiowelcome/stylemodel.cpp
@@ -3,8 +3,8 @@
#include "stylemodel.h"
-#include "utils/algorithm.h"
-#include "utils/qtcassert.h"
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
#include <QRegularExpression>
diff --git a/src/plugins/studiowelcome/wizardhandler.cpp b/src/plugins/studiowelcome/wizardhandler.cpp
index ee20518e60..d6d750d39e 100644
--- a/src/plugins/studiowelcome/wizardhandler.cpp
+++ b/src/plugins/studiowelcome/wizardhandler.cpp
@@ -11,8 +11,8 @@
#include <projectexplorer/jsonwizard/jsonprojectpage.h>
-#include "utils/wizard.h"
#include <utils/qtcassert.h>
+#include <utils/wizard.h>
using namespace StudioWelcome;
diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp
index f496054da6..3146f066d6 100644
--- a/src/plugins/subversion/subversionclient.cpp
+++ b/src/plugins/subversion/subversionclient.cpp
@@ -12,8 +12,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <vcsbase/vcsbaseconstants.h>
#include <vcsbase/vcsbasediffeditorcontroller.h>
@@ -35,27 +34,22 @@ using namespace VcsBase;
namespace Subversion {
namespace Internal {
-static SubversionSettings *s_settings = nullptr;
-
class SubversionLogConfig : public VcsBaseEditorConfig
{
Q_OBJECT
public:
- SubversionLogConfig(SubversionSettings &settings, QToolBar *toolBar) :
- VcsBaseEditorConfig(toolBar)
+ explicit SubversionLogConfig(QToolBar *toolBar)
+ : VcsBaseEditorConfig(toolBar)
{
mapSetting(addToggleButton("--verbose", Tr::tr("Verbose"),
Tr::tr("Show files changed in each revision")),
- &settings.logVerbose);
+ &settings().logVerbose);
}
};
-SubversionClient::SubversionClient(SubversionSettings *settings) : VcsBaseClient(settings)
+SubversionClient::SubversionClient() : VcsBaseClient(&Internal::settings())
{
- s_settings = settings;
- setLogConfigCreator([settings](QToolBar *toolBar) {
- return new SubversionLogConfig(*settings, toolBar);
- });
+ setLogConfigCreator([](QToolBar *toolBar) { return new SubversionLogConfig(toolBar); });
}
bool SubversionClient::doCommit(const FilePath &repositoryRoot,
@@ -102,11 +96,11 @@ Id SubversionClient::vcsEditorKind(VcsCommandTag cmd) const
// Add authorization options to the command line arguments.
CommandLine &operator<<(Utils::CommandLine &command, SubversionClient::AddAuthOptions)
{
- if (!s_settings->hasAuthentication())
+ if (!settings().hasAuthentication())
return command;
- const QString userName = s_settings->userName.value();
- const QString password = s_settings->password.value();
+ const QString userName = settings().userName();
+ const QString password = settings().password();
if (userName.isEmpty())
return command;
@@ -171,7 +165,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
const TreeStorage<QString> diffInputStorage = inputStorage();
- const auto setupDescription = [this](QtcProcess &process) {
+ const auto setupDescription = [this](Process &process) {
if (m_changeNumber == 0)
return TaskAction::StopWithDone;
setupCommand(process, {"log", "-r", QString::number(m_changeNumber)});
@@ -181,14 +175,14 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
setDescription(Tr::tr("Waiting for data..."));
return TaskAction::Continue;
};
- const auto onDescriptionDone = [this](const QtcProcess &process) {
+ const auto onDescriptionDone = [this](const Process &process) {
setDescription(process.cleanedStdOut());
};
- const auto onDescriptionError = [this](const QtcProcess &) {
+ const auto onDescriptionError = [this](const Process &) {
setDescription({});
};
- const auto setupDiff = [this](QtcProcess &process) {
+ const auto setupDiff = [this](Process &process) {
QStringList args = QStringList{"diff"} << "--internal-diff";
if (ignoreWhitespace())
args << "-x" << "-uw";
@@ -202,8 +196,8 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
command << SubversionClient::AddAuthOptions();
process.setCommand(command);
};
- const auto onDiffDone = [diffInputStorage](const QtcProcess &process) {
- *diffInputStorage.activeStorage() = process.cleanedStdOut();
+ const auto onDiffDone = [diffInputStorage](const Process &process) {
+ *diffInputStorage = process.cleanedStdOut();
};
const Group root {
@@ -211,10 +205,10 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
parallel,
Group {
optional,
- Process(setupDescription, onDescriptionDone, onDescriptionError)
+ ProcessTask(setupDescription, onDescriptionDone, onDescriptionError)
},
Group {
- Process(setupDiff, onDiffDone),
+ ProcessTask(setupDiff, onDiffDone),
postProcessTask()
}
};
@@ -240,13 +234,13 @@ void SubversionDiffEditorController::setChangeNumber(int changeNumber)
SubversionDiffEditorController *SubversionClient::findOrCreateDiffEditor(const QString &documentId,
const FilePath &source, const QString &title, const FilePath &workingDirectory)
{
- auto &settings = static_cast<SubversionSettings &>(this->settings());
+ SubversionSettings &settings = Internal::settings();
IDocument *document = DiffEditorController::findOrCreateDocument(documentId, title);
auto controller = qobject_cast<SubversionDiffEditorController *>(
DiffEditorController::controller(document));
if (!controller) {
controller = new SubversionDiffEditorController(document);
- controller->setVcsBinary(settings.binaryPath.filePath());
+ controller->setVcsBinary(settings.binaryPath());
controller->setProcessEnvironment(processEnvironment());
controller->setWorkingDirectory(workingDirectory);
}
@@ -278,7 +272,7 @@ void SubversionClient::log(const FilePath &workingDir,
const std::function<void(Utils::CommandLine &)> &addAuthOptions)
{
auto &settings = static_cast<SubversionSettings &>(this->settings());
- const int logCount = settings.logCount.value();
+ const int logCount = settings.logCount();
QStringList svnExtraOptions = extraOptions;
if (logCount > 0)
svnExtraOptions << QLatin1String("-l") << QString::number(logCount);
diff --git a/src/plugins/subversion/subversionclient.h b/src/plugins/subversion/subversionclient.h
index d3beec2b4c..b2ce0f9061 100644
--- a/src/plugins/subversion/subversionclient.h
+++ b/src/plugins/subversion/subversionclient.h
@@ -18,7 +18,7 @@ class SubversionClient : public VcsBase::VcsBaseClient
Q_OBJECT
public:
- SubversionClient(SubversionSettings *settings);
+ SubversionClient();
bool doCommit(const Utils::FilePath &repositoryRoot,
const QStringList &files,
diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp
index 1eedd61606..5cb7475edb 100644
--- a/src/plugins/subversion/subversionplugin.cpp
+++ b/src/plugins/subversion/subversionplugin.cpp
@@ -289,8 +289,6 @@ private:
QAction *m_menuAction = nullptr;
- SubversionSettingsPage m_settingsPage{&m_settings};
-
public:
VcsSubmitEditorFactory submitEditorFactory {
submitParameters,
@@ -358,7 +356,7 @@ SubversionPluginPrivate::SubversionPluginPrivate()
{
dd = this;
- m_client = new SubversionClient(&m_settings);
+ m_client = new SubversionClient();
setTopicCache(new SubversionTopicCache(this));
@@ -524,7 +522,7 @@ SubversionPluginPrivate::SubversionPluginPrivate()
subversionMenu->addAction(command);
m_commandLocator->appendCommand(command);
- connect(&m_settings, &AspectContainer::applied, this, &IVersionControl::configurationChanged);
+ connect(&settings(), &AspectContainer::applied, this, &IVersionControl::configurationChanged);
}
bool SubversionPluginPrivate::isVcsDirectory(const FilePath &fileName) const
@@ -640,7 +638,7 @@ void SubversionPluginPrivate::revertAll()
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
return;
// NoteL: Svn "revert ." doesn not work.
- CommandLine args{m_settings.binaryPath.filePath(), {"revert"}};
+ CommandLine args{settings().binaryPath(), {"revert"}};
args << SubversionClient::AddAuthOptions();
args << QLatin1String("--recursive") << state.topLevel().toString();
const auto revertResponse = runSvn(state.topLevel(), args, RunFlags::ShowStdOut);
@@ -657,7 +655,7 @@ void SubversionPluginPrivate::revertCurrentFile()
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasFile(), return);
- CommandLine diffArgs{m_settings.binaryPath.filePath(), {"diff"}};
+ CommandLine diffArgs{settings().binaryPath(), {"diff"}};
diffArgs << SubversionClient::AddAuthOptions();
diffArgs << SubversionClient::escapeFile(state.relativeCurrentFile());
@@ -675,7 +673,7 @@ void SubversionPluginPrivate::revertCurrentFile()
FileChangeBlocker fcb(state.currentFile());
// revert
- CommandLine args{m_settings.binaryPath.filePath(), {"revert"}};
+ CommandLine args{settings().binaryPath(), {"revert"}};
args << SubversionClient::AddAuthOptions();
args << SubversionClient::escapeFile(state.relativeCurrentFile());
@@ -736,7 +734,7 @@ void SubversionPluginPrivate::startCommit(const FilePath &workingDir, const QStr
return;
}
- CommandLine args{m_settings.binaryPath.filePath(), {"status"}};
+ CommandLine args{settings().binaryPath(), {"status"}};
args << SubversionClient::AddAuthOptions();
args << SubversionClient::escapeFiles(files);
@@ -815,7 +813,7 @@ void SubversionPluginPrivate::svnStatus(const FilePath &workingDir, const QStrin
{
const VcsBasePluginState state = currentState();
QTC_ASSERT(state.hasTopLevel(), return);
- CommandLine args{m_settings.binaryPath.filePath(), {"status"}};
+ CommandLine args{settings().binaryPath(), {"status"}};
args << SubversionClient::AddAuthOptions();
if (!relativePath.isEmpty())
args << SubversionClient::escapeFile(relativePath);
@@ -841,7 +839,7 @@ void SubversionPluginPrivate::updateProject()
void SubversionPluginPrivate::svnUpdate(const FilePath &workingDir, const QString &relativePath)
{
- CommandLine args{m_settings.binaryPath.filePath(), {"update"}};
+ CommandLine args{settings().binaryPath(), {"update"}};
args << SubversionClient::AddAuthOptions();
args << Constants::NON_INTERACTIVE_OPTION;
if (!relativePath.isEmpty())
@@ -865,9 +863,9 @@ void SubversionPluginPrivate::vcsAnnotateHelper(const FilePath &workingDir, cons
const FilePath source = VcsBaseEditor::getSource(workingDir, file);
QTextCodec *codec = VcsBaseEditor::getCodec(source);
- CommandLine args{m_settings.binaryPath.filePath(), {"annotate"}};
+ CommandLine args{settings().binaryPath.filePath(), {"annotate"}};
args << SubversionClient::AddAuthOptions();
- if (m_settings.spaceIgnorantAnnotation.value())
+ if (settings().spaceIgnorantAnnotation.value())
args << "-x" << "-uw";
if (!revision.isEmpty())
args << "-r" << revision;
@@ -949,10 +947,10 @@ CommandResult SubversionPluginPrivate::runSvn(const FilePath &workingDir,
const CommandLine &command, RunFlags flags,
QTextCodec *outputCodec, int timeoutMutiplier) const
{
- if (m_settings.binaryPath.value().isEmpty())
+ if (settings().binaryPath().isEmpty())
return CommandResult(ProcessResult::StartFailed, Tr::tr("No subversion executable specified."));
- const int timeoutS = m_settings.timeout.value() * timeoutMutiplier;
+ const int timeoutS = settings().timeout() * timeoutMutiplier;
return m_client->vcsSynchronousExec(workingDir, command, flags, timeoutS, outputCodec);
}
@@ -1008,7 +1006,7 @@ QString SubversionPluginPrivate::synchronousTopic(const FilePath &repository) co
bool SubversionPluginPrivate::vcsAdd(const FilePath &workingDir, const QString &rawFileName)
{
const QString file = QDir::toNativeSeparators(SubversionClient::escapeFile(rawFileName));
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath.filePath()};
args << "add" << SubversionClient::AddAuthOptions() << "--parents" << file;
return runSvn(workingDir, args, RunFlags::ShowStdOut).result()
== ProcessResult::FinishedWithSuccess;
@@ -1018,7 +1016,7 @@ bool SubversionPluginPrivate::vcsDelete(const FilePath &workingDir, const QStrin
{
const QString file = QDir::toNativeSeparators(SubversionClient::escapeFile(rawFileName));
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath.filePath()};
args << "delete" << SubversionClient::AddAuthOptions() << "--force" << file;
return runSvn(workingDir, args, RunFlags::ShowStdOut).result()
@@ -1027,7 +1025,7 @@ bool SubversionPluginPrivate::vcsDelete(const FilePath &workingDir, const QStrin
bool SubversionPluginPrivate::vcsMove(const FilePath &workingDir, const QString &from, const QString &to)
{
- CommandLine args{m_settings.binaryPath.filePath(), {"move"}};
+ CommandLine args{settings().binaryPath.filePath(), {"move"}};
args << SubversionClient::AddAuthOptions()
<< QDir::toNativeSeparators(SubversionClient::escapeFile(from))
<< QDir::toNativeSeparators(SubversionClient::escapeFile(to));
@@ -1040,7 +1038,7 @@ bool SubversionPluginPrivate::vcsCheckout(const FilePath &directory, const QByte
QUrl tempUrl = QUrl::fromEncoded(url);
const QString username = tempUrl.userName();
const QString password = tempUrl.password();
- CommandLine args{m_settings.binaryPath.filePath(), {"checkout"}};
+ CommandLine args{settings().binaryPath.filePath(), {"checkout"}};
args << Constants::NON_INTERACTIVE_OPTION;
if (!username.isEmpty()) {
@@ -1087,7 +1085,7 @@ bool SubversionPluginPrivate::managesDirectory(const FilePath &directory, FilePa
bool SubversionPluginPrivate::managesFile(const FilePath &workingDirectory, const QString &fileName) const
{
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath.filePath()};
args << "status" << SubversionClient::AddAuthOptions()
<< QDir::toNativeSeparators(SubversionClient::escapeFile(fileName));
const QString output = runSvn(workingDirectory, args).cleanedStdOut();
@@ -1126,7 +1124,7 @@ bool SubversionPluginPrivate::isVcsFileOrDirectory(const FilePath &filePath) con
bool SubversionPluginPrivate::isConfigured() const
{
- const FilePath binary = m_settings.binaryPath.filePath();
+ const FilePath binary = settings().binaryPath.filePath();
if (binary.isEmpty())
return false;
QFileInfo fi = binary.toFileInfo();
@@ -1189,7 +1187,7 @@ VcsCommand *SubversionPluginPrivate::createInitialCheckoutCommand(const QString
const QString &localName,
const QStringList &extraArgs)
{
- CommandLine args{m_settings.binaryPath.filePath()};
+ CommandLine args{settings().binaryPath.filePath()};
args << "checkout";
args << SubversionClient::AddAuthOptions();
args << Subversion::Constants::NON_INTERACTIVE_OPTION << extraArgs << url << localName;
diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp
index 5a27512912..9bdd07fbf8 100644
--- a/src/plugins/subversion/subversionsettings.cpp
+++ b/src/plugins/subversion/subversionsettings.cpp
@@ -3,37 +3,36 @@
#include "subversionsettings.h"
-#include "subversionclient.h"
-#include "subversionplugin.h"
#include "subversiontr.h"
-#include <coreplugin/icore.h>
-#include <coreplugin/dialogs/ioptionspage.h>
-
-#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <vcsbase/vcsbaseconstants.h>
-#include <QSettings>
-
using namespace Utils;
using namespace VcsBase;
-namespace Subversion {
-namespace Internal {
+namespace Subversion::Internal {
+
+static SubversionSettings *theSettings;
-// SubversionSettings
+SubversionSettings &settings()
+{
+ return *theSettings;
+}
SubversionSettings::SubversionSettings()
{
- setAutoApply(false);
+ theSettings = this;
+
+ setId(VcsBase::Constants::VCS_ID_SUBVERSION);
+ setDisplayName(Tr::tr("Subversion"));
+ setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
setSettingsGroup("Subversion");
registerAspect(&binaryPath);
- binaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
binaryPath.setExpectedKind(PathChooser::ExistingCommand);
binaryPath.setHistoryCompleter("Subversion.Command.History");
binaryPath.setDefaultValue("svn" QTC_HOST_EXE_SUFFIX);
@@ -74,46 +73,33 @@ SubversionSettings::SubversionSettings()
timeout.setSuffix(Tr::tr("s"));
QObject::connect(&useAuthentication, &BaseAspect::changed, this, [this] {
- userName.setEnabled(useAuthentication.value());
- password.setEnabled(useAuthentication.value());
+ userName.setEnabled(useAuthentication());
+ password.setEnabled(useAuthentication());
});
-}
-bool SubversionSettings::hasAuthentication() const
-{
- return useAuthentication.value() && !userName.value().isEmpty();
-}
-
-SubversionSettingsPage::SubversionSettingsPage(SubversionSettings *settings)
-{
- setId(VcsBase::Constants::VCS_ID_SUBVERSION);
- setDisplayName(Tr::tr("Subversion"));
- setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY);
- setSettings(settings);
-
- setLayouter([settings](QWidget *widget) {
- SubversionSettings &s = *settings;
+ setLayouter([this](QWidget *widget) {
using namespace Layouting;
Column {
Group {
title(Tr::tr("Configuration")),
- Column { s.binaryPath }
+ Column { binaryPath }
},
Group {
- title(Tr::tr("Authentication"), &s.useAuthentication),
+ title(Tr::tr("Authentication")),
+ useAuthentication.groupChecker(),
Form {
- s.userName,
- s.password,
+ userName, br,
+ password,
}
},
Group {
title(Tr::tr("Miscellaneous")),
Column {
- Row { s.logCount, s.timeout, st },
- s.spaceIgnorantAnnotation,
+ Row { logCount, timeout, st },
+ spaceIgnorantAnnotation,
}
},
@@ -122,5 +108,9 @@ SubversionSettingsPage::SubversionSettingsPage(SubversionSettings *settings)
});
}
-} // Internal
-} // Subversion
+bool SubversionSettings::hasAuthentication() const
+{
+ return useAuthentication() && !userName().isEmpty();
+}
+
+} // Subversion::Internal
diff --git a/src/plugins/subversion/subversionsettings.h b/src/plugins/subversion/subversionsettings.h
index 01e3091695..49aaec171b 100644
--- a/src/plugins/subversion/subversionsettings.h
+++ b/src/plugins/subversion/subversionsettings.h
@@ -3,11 +3,9 @@
#pragma once
-#include <coreplugin/dialogs/ioptionspage.h>
#include <vcsbase/vcsbaseclientsettings.h>
-namespace Subversion {
-namespace Internal {
+namespace Subversion::Internal {
class SubversionSettings : public VcsBase::VcsBaseSettings
{
@@ -23,11 +21,6 @@ public:
Utils::BoolAspect logVerbose;
};
-class SubversionSettingsPage final : public Core::IOptionsPage
-{
-public:
- explicit SubversionSettingsPage(SubversionSettings *settings);
-};
+SubversionSettings &settings();
-} // namespace Internal
-} // namespace Subversion
+} // Subversion::Internal
diff --git a/src/plugins/terminal/CMakeLists.txt b/src/plugins/terminal/CMakeLists.txt
new file mode 100644
index 0000000000..005a3aaca5
--- /dev/null
+++ b/src/plugins/terminal/CMakeLists.txt
@@ -0,0 +1,23 @@
+
+add_qtc_plugin(Terminal
+ PLUGIN_DEPENDS Core ProjectExplorer
+ DEPENDS libvterm
+ SOURCES
+ celliterator.cpp celliterator.h
+ glyphcache.cpp glyphcache.h
+ keys.cpp keys.h
+ scrollback.cpp scrollback.h
+ shellintegration.cpp shellintegration.h
+ shellmodel.cpp shellmodel.h
+ terminal.qrc
+ terminalcommands.cpp terminalcommands.h
+ terminalpane.cpp terminalpane.h
+ terminalplugin.cpp terminalplugin.h
+ terminalprocessimpl.cpp terminalprocessimpl.h
+ terminalsearch.cpp terminalsearch.h
+ terminalsettings.cpp terminalsettings.h
+ terminalsettingspage.cpp terminalsettingspage.h
+ terminalsurface.cpp terminalsurface.h
+ terminaltr.h
+ terminalwidget.cpp terminalwidget.h
+)
diff --git a/src/plugins/terminal/Terminal.json.in b/src/plugins/terminal/Terminal.json.in
new file mode 100644
index 0000000000..5640012c08
--- /dev/null
+++ b/src/plugins/terminal/Terminal.json.in
@@ -0,0 +1,18 @@
+{
+ \"Name\" : \"Terminal\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
+ ],
+ \"Description\" : \"Terminal window.\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/terminal/celliterator.cpp b/src/plugins/terminal/celliterator.cpp
new file mode 100644
index 0000000000..91a70f76ea
--- /dev/null
+++ b/src/plugins/terminal/celliterator.cpp
@@ -0,0 +1,94 @@
+// 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 "celliterator.h"
+
+#include "terminalsurface.h"
+
+#include <stdexcept>
+
+namespace Terminal::Internal {
+
+CellIterator::CellIterator(const TerminalSurface *surface, QPoint pos)
+ : CellIterator(surface, pos.x() + (pos.y() * surface->liveSize().width()))
+{}
+
+CellIterator::CellIterator(const TerminalSurface *surface, int pos)
+ : m_state(State::INSIDE)
+ , m_surface(surface)
+{
+ m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
+ m_pos = qMax(0, qMin(m_maxpos + 1, pos));
+
+ if (m_pos == 0) {
+ m_state = State::BEGIN;
+ } else if (m_pos == m_maxpos + 1) {
+ m_state = State::END;
+ }
+
+ if (m_state != State::END)
+ updateChar();
+}
+
+CellIterator::CellIterator(const TerminalSurface *surface)
+ : m_state(State::END)
+ , m_surface(surface)
+{
+ m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
+ m_pos = m_maxpos + 1;
+}
+
+QPoint CellIterator::gridPos() const
+{
+ return m_surface->posToGrid(m_pos);
+}
+
+bool CellIterator::updateChar()
+{
+ QPoint cell = m_surface->posToGrid(m_pos);
+ m_char = m_surface->fetchCharAt(cell.x(), cell.y());
+ return m_char != 0;
+}
+
+CellIterator &CellIterator::operator-=(int n)
+{
+ if (n == 0)
+ return *this;
+
+ if (m_pos - n < 0)
+ throw new std::runtime_error("-= n too big!");
+
+ m_pos -= n;
+
+ while (!updateChar() && m_pos > 0 && m_skipZeros)
+ m_pos--;
+
+ m_state = State::INSIDE;
+
+ if (m_pos == 0) {
+ m_state = State::BEGIN;
+ }
+
+ return *this;
+}
+
+CellIterator &CellIterator::operator+=(int n)
+{
+ if (n == 0)
+ return *this;
+
+ if (m_pos + n < m_maxpos + 1) {
+ m_state = State::INSIDE;
+ m_pos += n;
+ while (!updateChar() && m_pos < (m_maxpos + 1) && m_skipZeros)
+ m_pos++;
+
+ if (m_pos == m_maxpos + 1)
+ m_state = State::END;
+ } else {
+ *this = m_surface->end();
+ }
+ return *this;
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/celliterator.h b/src/plugins/terminal/celliterator.h
new file mode 100644
index 0000000000..c246aaa311
--- /dev/null
+++ b/src/plugins/terminal/celliterator.h
@@ -0,0 +1,97 @@
+// 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
+
+#pragma once
+
+#include <string>
+
+#include <QPoint>
+
+namespace Terminal::Internal {
+
+class TerminalSurface;
+
+class CellIterator
+{
+public:
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::u32string::value_type;
+ using pointer = std::u32string::value_type *;
+ // We need to return copies for std::reverse_iterator to work
+ using reference = std::u32string::value_type;
+
+ enum class State { BEGIN, INSIDE, END } m_state{};
+
+public:
+ CellIterator(const TerminalSurface *surface, QPoint pos);
+ CellIterator(const TerminalSurface *surface, int pos);
+ CellIterator(const TerminalSurface *surface);
+
+public:
+ QPoint gridPos() const;
+
+public:
+ CellIterator &operator-=(int n);
+ CellIterator &operator+=(int n);
+
+ reference operator*() const { return m_char; }
+ pointer operator->() { return &m_char; }
+
+ CellIterator &operator++() { return *this += 1; }
+ CellIterator operator++(int)
+ {
+ CellIterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ CellIterator &operator--() { return *this -= 1; }
+ CellIterator operator--(int)
+ {
+ CellIterator tmp = *this;
+ --(*this);
+ return tmp;
+ }
+
+ bool operator!=(const CellIterator &other) const
+ {
+ if (other.m_state != m_state)
+ return true;
+
+ if (other.m_pos != m_pos)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const CellIterator &other) const { return !operator!=(other); }
+
+ CellIterator operator-(int n) const
+ {
+ CellIterator result = *this;
+ result -= n;
+ return result;
+ }
+
+ CellIterator operator+(int n) const
+ {
+ CellIterator result = *this;
+ result += n;
+ return result;
+ }
+
+ int position() const { return m_pos; }
+ void setSkipZeros(bool skipZeros) { m_skipZeros = skipZeros; }
+
+private:
+ bool updateChar();
+
+ const TerminalSurface *m_surface{nullptr};
+ int m_pos{-1};
+ int m_maxpos{-1};
+ bool m_skipZeros{false};
+ mutable std::u32string::value_type m_char;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/glyphcache.cpp b/src/plugins/terminal/glyphcache.cpp
new file mode 100644
index 0000000000..72a0fd7b9d
--- /dev/null
+++ b/src/plugins/terminal/glyphcache.cpp
@@ -0,0 +1,48 @@
+// 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 "glyphcache.h"
+
+#include <QTextLayout>
+
+namespace Terminal::Internal {
+
+size_t qHash(const GlyphCacheKey &key, size_t seed = 0)
+{
+ return qHash(key.font, seed) ^ qHash(key.text, seed);
+}
+
+GlyphCache &GlyphCache::instance()
+{
+ static GlyphCache cache(5000);
+ return cache;
+}
+
+const QGlyphRun *GlyphCache::get(const QFont &font, const QString &text)
+{
+ GlyphCacheKey key{font, text};
+ if (auto *run = object(key))
+ return run;
+
+ QTextLayout layout;
+
+ layout.setText(text);
+ layout.setFont(font);
+
+ layout.beginLayout();
+ layout.createLine().setNumColumns(std::numeric_limits<int>::max());
+ layout.endLayout();
+
+ if (layout.lineCount() > 0) {
+ const auto &line = layout.lineAt(0);
+ const auto runs = line.glyphRuns();
+ if (!runs.isEmpty()) {
+ QGlyphRun *run = new QGlyphRun(layout.lineAt(0).glyphRuns().first());
+ insert(key, run);
+ return run;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/glyphcache.h b/src/plugins/terminal/glyphcache.h
new file mode 100644
index 0000000000..60701098f5
--- /dev/null
+++ b/src/plugins/terminal/glyphcache.h
@@ -0,0 +1,34 @@
+// 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
+
+#pragma once
+
+#include <QCache>
+#include <QFont>
+#include <QGlyphRun>
+#include <QString>
+
+namespace Terminal::Internal {
+
+struct GlyphCacheKey
+{
+ QFont font;
+ QString text;
+
+ bool operator==(const GlyphCacheKey &other) const
+ {
+ return font == other.font && text == other.text;
+ }
+};
+
+class GlyphCache : public QCache<GlyphCacheKey, QGlyphRun>
+{
+public:
+ using QCache::QCache;
+
+ static GlyphCache &instance();
+
+ const QGlyphRun *get(const QFont &font, const QString &text);
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/images/settingscategory_terminal.png b/src/plugins/terminal/images/settingscategory_terminal.png
new file mode 100644
index 0000000000..6e8c716778
--- /dev/null
+++ b/src/plugins/terminal/images/settingscategory_terminal.png
Binary files differ
diff --git a/src/plugins/terminal/images/settingscategory_terminal@2x.png b/src/plugins/terminal/images/settingscategory_terminal@2x.png
new file mode 100644
index 0000000000..71e292d8bc
--- /dev/null
+++ b/src/plugins/terminal/images/settingscategory_terminal@2x.png
Binary files differ
diff --git a/src/plugins/terminal/images/terminal.png b/src/plugins/terminal/images/terminal.png
new file mode 100644
index 0000000000..0a1dd311ec
--- /dev/null
+++ b/src/plugins/terminal/images/terminal.png
Binary files differ
diff --git a/src/plugins/terminal/images/terminal@2x.png b/src/plugins/terminal/images/terminal@2x.png
new file mode 100644
index 0000000000..da36b36721
--- /dev/null
+++ b/src/plugins/terminal/images/terminal@2x.png
Binary files differ
diff --git a/src/plugins/terminal/keys.cpp b/src/plugins/terminal/keys.cpp
new file mode 100644
index 0000000000..f6a7a91b13
--- /dev/null
+++ b/src/plugins/terminal/keys.cpp
@@ -0,0 +1,83 @@
+// 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 "keys.h"
+
+namespace Terminal::Internal {
+
+VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod)
+{
+ int ret = VTERM_MOD_NONE;
+
+ if (mod & Qt::ShiftModifier)
+ ret |= VTERM_MOD_SHIFT;
+
+ if (mod & Qt::AltModifier)
+ ret |= VTERM_MOD_ALT;
+
+#ifdef Q_OS_DARWIN
+ if (mod & Qt::MetaModifier)
+ ret |= VTERM_MOD_CTRL;
+#else
+ if (mod & Qt::ControlModifier)
+ ret |= VTERM_MOD_CTRL;
+#endif
+
+ return static_cast<VTermModifier>(ret);
+}
+
+VTermKey qtKeyToVTerm(Qt::Key key, bool keypad)
+{
+ if (key >= Qt::Key_F1 && key <= Qt::Key_F35)
+ return static_cast<VTermKey>(VTERM_KEY_FUNCTION_0 + key - Qt::Key_F1 + 1);
+
+ switch (key) {
+ case Qt::Key_Return:
+ return VTERM_KEY_ENTER;
+ case Qt::Key_Tab:
+ return VTERM_KEY_TAB;
+ case Qt::Key_Backspace:
+ return VTERM_KEY_BACKSPACE;
+ case Qt::Key_Escape:
+ return VTERM_KEY_ESCAPE;
+ case Qt::Key_Up:
+ return VTERM_KEY_UP;
+ case Qt::Key_Down:
+ return VTERM_KEY_DOWN;
+ case Qt::Key_Left:
+ return VTERM_KEY_LEFT;
+ case Qt::Key_Right:
+ return VTERM_KEY_RIGHT;
+ case Qt::Key_Insert:
+ return VTERM_KEY_INS;
+ case Qt::Key_Delete:
+ return VTERM_KEY_DEL;
+ case Qt::Key_Home:
+ return VTERM_KEY_HOME;
+ case Qt::Key_End:
+ return VTERM_KEY_END;
+ case Qt::Key_PageUp:
+ return VTERM_KEY_PAGEUP;
+ case Qt::Key_PageDown:
+ return VTERM_KEY_PAGEDOWN;
+ case Qt::Key_multiply:
+ return keypad ? VTERM_KEY_KP_MULT : VTERM_KEY_NONE;
+ case Qt::Key_Plus:
+ return keypad ? VTERM_KEY_KP_PLUS : VTERM_KEY_NONE;
+ case Qt::Key_Comma:
+ return keypad ? VTERM_KEY_KP_COMMA : VTERM_KEY_NONE;
+ case Qt::Key_Minus:
+ return keypad ? VTERM_KEY_KP_MINUS : VTERM_KEY_NONE;
+ case Qt::Key_Period:
+ return keypad ? VTERM_KEY_KP_PERIOD : VTERM_KEY_NONE;
+ case Qt::Key_Slash:
+ return keypad ? VTERM_KEY_KP_DIVIDE : VTERM_KEY_NONE;
+ case Qt::Key_Enter:
+ return keypad ? VTERM_KEY_KP_ENTER : VTERM_KEY_NONE;
+ case Qt::Key_Equal:
+ return keypad ? VTERM_KEY_KP_EQUAL : VTERM_KEY_NONE;
+ default:
+ return VTERM_KEY_NONE;
+ }
+}
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/keys.h b/src/plugins/terminal/keys.h
new file mode 100644
index 0000000000..f3df933001
--- /dev/null
+++ b/src/plugins/terminal/keys.h
@@ -0,0 +1,15 @@
+// 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
+
+#pragma once
+
+#include <vterm_keycodes.h>
+
+#include <QKeyEvent>
+
+namespace Terminal::Internal {
+
+VTermKey qtKeyToVTerm(Qt::Key key, bool keypad);
+VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod);
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/scrollback.cpp b/src/plugins/terminal/scrollback.cpp
new file mode 100644
index 0000000000..e22d5fa243
--- /dev/null
+++ b/src/plugins/terminal/scrollback.cpp
@@ -0,0 +1,61 @@
+// Copyright (c) 2020, Justin Bronder
+// Copied and modified from: https://github.com/jsbronder/sff
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include "scrollback.h"
+
+#include <cassert>
+#include <cstring>
+#include <future>
+
+namespace Terminal::Internal {
+
+Scrollback::Line::Line(int cols, const VTermScreenCell *cells)
+ : m_cols(cols)
+ , m_cells(std::make_unique<VTermScreenCell[]>(cols))
+{
+ memcpy(m_cells.get(), cells, cols * sizeof(cells[0]));
+}
+
+const VTermScreenCell *Scrollback::Line::cell(int i) const
+{
+ assert(i >= 0 && i < m_cols);
+ return &m_cells[i];
+}
+
+Scrollback::Scrollback(size_t capacity)
+ : m_capacity(capacity)
+{}
+
+void Scrollback::emplace(int cols, const VTermScreenCell *cells)
+{
+ m_deque.emplace_front(cols, cells);
+ while (m_deque.size() > m_capacity) {
+ m_deque.pop_back();
+ }
+}
+
+void Scrollback::popto(int cols, VTermScreenCell *cells)
+{
+ const Line &sbl = m_deque.front();
+
+ int ncells = cols;
+ if (ncells > sbl.cols())
+ ncells = sbl.cols();
+
+ memcpy(cells, sbl.cells(), sizeof(cells[0]) * ncells);
+ for (size_t i = ncells; i < static_cast<size_t>(cols); ++i) {
+ cells[i].chars[0] = '\0';
+ cells[i].width = 1;
+ cells[i].bg = cells[ncells - 1].bg;
+ }
+
+ m_deque.pop_front();
+}
+
+void Scrollback::clear()
+{
+ m_deque.clear();
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/scrollback.h b/src/plugins/terminal/scrollback.h
new file mode 100644
index 0000000000..9ca71eec61
--- /dev/null
+++ b/src/plugins/terminal/scrollback.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020, Justin Bronder
+// Copied and modified from: https://github.com/jsbronder/sff
+// SPDX-License-Identifier: BSD-3-Clause
+
+#pragma once
+
+#include <vterm.h>
+
+#include <deque>
+#include <future>
+#include <memory>
+
+#include <QFont>
+#include <QTextLayout>
+
+namespace Terminal::Internal {
+
+class Scrollback
+{
+public:
+ class Line
+ {
+ public:
+ Line(int cols, const VTermScreenCell *cells);
+ Line(Line &&other) = default;
+ Line() = delete;
+
+ int cols() const { return m_cols; };
+ const VTermScreenCell *cell(int i) const;
+ const VTermScreenCell *cells() const { return &m_cells[0]; };
+
+ private:
+ int m_cols;
+ std::unique_ptr<VTermScreenCell[]> m_cells;
+ };
+
+public:
+ Scrollback(size_t capacity);
+ Scrollback() = delete;
+
+ int capacity() const { return static_cast<int>(m_capacity); };
+ int size() const { return static_cast<int>(m_deque.size()); };
+
+ const Line &line(size_t index) const { return m_deque.at(index); };
+ const std::deque<Line> &lines() const { return m_deque; };
+
+ void emplace(int cols, const VTermScreenCell *cells);
+ void popto(int cols, VTermScreenCell *cells);
+
+ void clear();
+
+private:
+ size_t m_capacity;
+ std::deque<Line> m_deque;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/shellintegration.cpp b/src/plugins/terminal/shellintegration.cpp
new file mode 100644
index 0000000000..d8e26f94ce
--- /dev/null
+++ b/src/plugins/terminal/shellintegration.cpp
@@ -0,0 +1,164 @@
+// 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 "shellintegration.h"
+
+#include <utils/environment.h>
+#include <utils/filepath.h>
+#include <utils/stringutils.h>
+
+#include <QLoggingCategory>
+
+Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg)
+
+using namespace Utils;
+
+namespace Terminal {
+
+struct FileToCopy
+{
+ FilePath source;
+ QString destName;
+};
+
+// clang-format off
+struct
+{
+ struct
+ {
+ FilePath rcFile{":/terminal/shellintegrations/shellintegration-bash.sh"};
+ } bash;
+ struct
+ {
+ QList<FileToCopy> files{
+ {":/terminal/shellintegrations/shellintegration-env.zsh", ".zshenv"},
+ {":/terminal/shellintegrations/shellintegration-login.zsh", ".zlogin"},
+ {":/terminal/shellintegrations/shellintegration-profile.zsh", ".zprofile"},
+ {":/terminal/shellintegrations/shellintegration-rc.zsh", ".zshrc"}
+ };
+ } zsh;
+ struct
+ {
+ FilePath script{":/terminal/shellintegrations/shellintegration.ps1"};
+ } pwsh;
+ struct
+ {
+ FilePath script{":/terminal/shellintegrations/shellintegration-clink.lua"};
+ } clink;
+
+} filesToCopy;
+// clang-format on
+
+bool ShellIntegration::canIntegrate(const Utils::CommandLine &cmdLine)
+{
+ if (cmdLine.executable().needsDevice())
+ return false; // TODO: Allow integration for remote shells
+
+ if (cmdLine.executable().baseName() == "zsh")
+ return true;
+
+ if (!cmdLine.arguments().isEmpty() && cmdLine.arguments() != "-l")
+ return false;
+
+ if (cmdLine.executable().baseName() == "bash")
+ return true;
+
+ if (cmdLine.executable().baseName() == "pwsh"
+ || cmdLine.executable().baseName() == "powershell") {
+ return true;
+ }
+
+ if (cmdLine.executable().baseName() == "cmd")
+ return true;
+
+ return false;
+}
+
+void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment)
+{
+ QString d = QString::fromLocal8Bit(fragment.str, fragment.len);
+ const auto [command, data] = Utils::splitAtFirst(d, ';');
+
+ if (cmd == 1337) {
+ const auto [key, value] = Utils::splitAtFirst(command, '=');
+ if (key == QStringView(u"CurrentDir"))
+ emit currentDirChanged(FilePath::fromUserInput(value.toString()).path());
+
+ } else if (cmd == 7) {
+ emit currentDirChanged(FilePath::fromUserInput(d).path());
+ } else if (cmd == 133) {
+ qCDebug(integrationLog) << "OSC 133:" << data;
+ } else if (cmd == 633 && command.length() == 1) {
+ if (command[0] == 'E') {
+ CommandLine cmdLine = CommandLine::fromUserInput(data.toString());
+ emit commandChanged(cmdLine);
+ } else if (command[0] == 'D') {
+ emit commandChanged({});
+ } else if (command[0] == 'P') {
+ const auto [key, value] = Utils::splitAtFirst(data, '=');
+ if (key == QStringView(u"Cwd"))
+ emit currentDirChanged(value.toString());
+ }
+ }
+}
+
+void ShellIntegration::prepareProcess(Utils::Process &process)
+{
+ Environment env = process.environment().hasChanges() ? process.environment()
+ : Environment::systemEnvironment();
+ CommandLine cmd = process.commandLine();
+
+ if (!canIntegrate(cmd))
+ return;
+
+ env.set("VSCODE_INJECTION", "1");
+ env.set("TERM_PROGRAM", "vscode");
+
+ if (cmd.executable().baseName() == "bash") {
+ const FilePath rcPath = filesToCopy.bash.rcFile;
+ const FilePath tmpRc = FilePath::fromUserInput(
+ m_tempDir.filePath(filesToCopy.bash.rcFile.fileName()));
+ rcPath.copyFile(tmpRc);
+
+ CommandLine newCmd = {cmd.executable(), {"--init-file", tmpRc.nativePath()}};
+
+ if (cmd.arguments() == "-l")
+ newCmd.addArg("-l");
+
+ cmd = newCmd;
+ } else if (cmd.executable().baseName() == "zsh") {
+ for (const FileToCopy &file : filesToCopy.zsh.files) {
+ const auto copyResult = file.source.copyFile(
+ FilePath::fromUserInput(m_tempDir.filePath(file.destName)));
+ QTC_ASSERT_EXPECTED(copyResult, return);
+ }
+
+ const Utils::FilePath originalZdotDir = FilePath::fromUserInput(
+ env.value_or("ZDOTDIR", QDir::homePath()));
+
+ env.set("ZDOTDIR", m_tempDir.path());
+ env.set("USER_ZDOTDIR", originalZdotDir.nativePath());
+ } else if (cmd.executable().baseName() == "pwsh"
+ || cmd.executable().baseName() == "powershell") {
+ const FilePath rcPath = filesToCopy.pwsh.script;
+ const FilePath tmpRc = FilePath::fromUserInput(
+ m_tempDir.filePath(filesToCopy.pwsh.script.fileName()));
+ rcPath.copyFile(tmpRc);
+
+ cmd.addArgs(QString("-noexit -command try { . \"%1\" } catch {}{1}").arg(tmpRc.nativePath()),
+ CommandLine::Raw);
+ } else if (cmd.executable().baseName() == "cmd") {
+ const FilePath rcPath = filesToCopy.clink.script;
+ const FilePath tmpRc = FilePath::fromUserInput(
+ m_tempDir.filePath(filesToCopy.clink.script.fileName()));
+ rcPath.copyFile(tmpRc);
+
+ env.set("CLINK_HISTORY_LABEL", "QtCreator");
+ env.appendOrSet("CLINK_PATH", tmpRc.parentDir().nativePath(), ";");
+ }
+
+ process.setCommand(cmd);
+ process.setEnvironment(env);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/shellintegration.h b/src/plugins/terminal/shellintegration.h
new file mode 100644
index 0000000000..a4a813c8a6
--- /dev/null
+++ b/src/plugins/terminal/shellintegration.h
@@ -0,0 +1,34 @@
+// 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
+
+#pragma once
+
+#include <utils/commandline.h>
+#include <utils/process.h>
+
+#include <vterm.h>
+
+#include <QTemporaryDir>
+
+namespace Terminal {
+
+class ShellIntegration : public QObject
+{
+ Q_OBJECT
+public:
+ static bool canIntegrate(const Utils::CommandLine &cmdLine);
+
+ void onOsc(int cmd, const VTermStringFragment &fragment);
+
+ void prepareProcess(Utils::Process &process);
+
+signals:
+ void commandChanged(const Utils::CommandLine &command);
+ void currentDirChanged(const QString &dir);
+
+private:
+ QTemporaryDir m_tempDir;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-bash.sh b/src/plugins/terminal/shellintegrations/shellintegration-bash.sh
new file mode 100755
index 0000000000..7db188be08
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-bash.sh
@@ -0,0 +1,252 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+
+# Prevent the script recursing when setting up
+if [[ -n "$VSCODE_SHELL_INTEGRATION" ]]; then
+ builtin return
+fi
+
+VSCODE_SHELL_INTEGRATION=1
+
+# Run relevant rc/profile only if shell integration has been injected, not when run manually
+if [ "$VSCODE_INJECTION" == "1" ]; then
+ if [ -z "$VSCODE_SHELL_LOGIN" ]; then
+ if [ -r ~/.bashrc ]; then
+ . ~/.bashrc
+ fi
+ else
+ # Imitate -l because --init-file doesn't support it:
+ # run the first of these files that exists
+ if [ -r /etc/profile ]; then
+ . /etc/profile
+ fi
+ # exceute the first that exists
+ if [ -r ~/.bash_profile ]; then
+ . ~/.bash_profile
+ elif [ -r ~/.bash_login ]; then
+ . ~/.bash_login
+ elif [ -r ~/.profile ]; then
+ . ~/.profile
+ fi
+ builtin unset VSCODE_SHELL_LOGIN
+
+ # Apply any explicit path prefix (see #99878)
+ if [ -n "$VSCODE_PATH_PREFIX" ]; then
+ export PATH=$VSCODE_PATH_PREFIX$PATH
+ builtin unset VSCODE_PATH_PREFIX
+ fi
+ fi
+ builtin unset VSCODE_INJECTION
+fi
+
+if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
+ builtin return
+fi
+
+__vsc_get_trap() {
+ # 'trap -p DEBUG' outputs a shell command like `trap -- '…shellcode…' DEBUG`.
+ # The terms are quoted literals, but are not guaranteed to be on a single line.
+ # (Consider a trap like $'echo foo\necho \'bar\'').
+ # To parse, we splice those terms into an expression capturing them into an array.
+ # This preserves the quoting of those terms: when we `eval` that expression, they are preserved exactly.
+ # This is different than simply exploding the string, which would split everything on IFS, oblivious to quoting.
+ builtin local -a terms
+ builtin eval "terms=( $(trap -p "${1:-DEBUG}") )"
+ # |________________________|
+ # |
+ # \-------------------*--------------------/
+ # terms=( trap -- '…arbitrary shellcode…' DEBUG )
+ # |____||__| |_____________________| |_____|
+ # | | | |
+ # 0 1 2 3
+ # |
+ # \--------*----/
+ builtin printf '%s' "${terms[2]:-}"
+}
+
+# The property (P) and command (E) codes embed values which require escaping.
+# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
+__vsc_escape_value() {
+ # Process text byte by byte, not by codepoint.
+ builtin local LC_ALL=C str="${1}" i byte token out=''
+
+ for (( i=0; i < "${#str}"; ++i )); do
+ byte="${str:$i:1}"
+
+ # Escape backslashes and semi-colons
+ if [ "$byte" = "\\" ]; then
+ token="\\\\"
+ elif [ "$byte" = ";" ]; then
+ token="\\x3b"
+ else
+ token="$byte"
+ fi
+
+ out+="$token"
+ done
+
+ builtin printf '%s\n' "${out}"
+}
+
+# Send the IsWindows property if the environment looks like Windows
+if [[ "$(uname -s)" =~ ^CYGWIN*|MINGW*|MSYS* ]]; then
+ builtin printf '\e]633;P;IsWindows=True\a'
+fi
+
+# Allow verifying $BASH_COMMAND doesn't have aliases resolved via history when the right HISTCONTROL
+# configuration is used
+if [[ "$HISTCONTROL" =~ .*(erasedups|ignoreboth|ignoredups).* ]]; then
+ __vsc_history_verify=0
+else
+ __vsc_history_verify=1
+fi
+
+__vsc_initialized=0
+__vsc_original_PS1="$PS1"
+__vsc_original_PS2="$PS2"
+__vsc_custom_PS1=""
+__vsc_custom_PS2=""
+__vsc_in_command_execution="1"
+__vsc_current_command=""
+
+__vsc_prompt_start() {
+ builtin printf '\e]633;A\a'
+}
+
+__vsc_prompt_end() {
+ builtin printf '\e]633;B\a'
+}
+
+__vsc_update_cwd() {
+ builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "$PWD")"
+}
+
+__vsc_command_output_start() {
+ builtin printf '\e]633;C\a'
+ builtin printf '\e]633;E;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")"
+}
+
+__vsc_continuation_start() {
+ builtin printf '\e]633;F\a'
+}
+
+__vsc_continuation_end() {
+ builtin printf '\e]633;G\a'
+}
+
+__vsc_command_complete() {
+ if [ "$__vsc_current_command" = "" ]; then
+ builtin printf '\e]633;D\a'
+ else
+ builtin printf '\e]633;D;%s\a' "$__vsc_status"
+ fi
+ __vsc_update_cwd
+}
+__vsc_update_prompt() {
+ # in command execution
+ if [ "$__vsc_in_command_execution" = "1" ]; then
+ # Wrap the prompt if it is not yet wrapped, if the PS1 changed this this was last set it
+ # means the user re-exported the PS1 so we should re-wrap it
+ if [[ "$__vsc_custom_PS1" == "" || "$__vsc_custom_PS1" != "$PS1" ]]; then
+ __vsc_original_PS1=$PS1
+ __vsc_custom_PS1="\[$(__vsc_prompt_start)\]$__vsc_original_PS1\[$(__vsc_prompt_end)\]"
+ PS1="$__vsc_custom_PS1"
+ fi
+ if [[ "$__vsc_custom_PS2" == "" || "$__vsc_custom_PS2" != "$PS2" ]]; then
+ __vsc_original_PS2=$PS2
+ __vsc_custom_PS2="\[$(__vsc_continuation_start)\]$__vsc_original_PS2\[$(__vsc_continuation_end)\]"
+ PS2="$__vsc_custom_PS2"
+ fi
+ __vsc_in_command_execution="0"
+ fi
+}
+
+__vsc_precmd() {
+ __vsc_command_complete "$__vsc_status"
+ __vsc_current_command=""
+ __vsc_update_prompt
+}
+
+__vsc_preexec() {
+ __vsc_initialized=1
+ if [[ ! "$BASH_COMMAND" =~ ^__vsc_prompt* ]]; then
+ # Use history if it's available to verify the command as BASH_COMMAND comes in with aliases
+ # resolved
+ if [ "$__vsc_history_verify" = "1" ]; then
+ __vsc_current_command="$(builtin history 1 | sed 's/ *[0-9]* *//')"
+ else
+ __vsc_current_command=$BASH_COMMAND
+ fi
+ else
+ __vsc_current_command=""
+ fi
+ __vsc_command_output_start
+}
+
+# Debug trapping/preexec inspired by starship (ISC)
+if [[ -n "${bash_preexec_imported:-}" ]]; then
+ __vsc_preexec_only() {
+ if [ "$__vsc_in_command_execution" = "0" ]; then
+ __vsc_in_command_execution="1"
+ __vsc_preexec
+ fi
+ }
+ precmd_functions+=(__vsc_prompt_cmd)
+ preexec_functions+=(__vsc_preexec_only)
+else
+ __vsc_dbg_trap="$(__vsc_get_trap DEBUG)"
+
+ if [[ -z "$__vsc_dbg_trap" ]]; then
+ __vsc_preexec_only() {
+ if [ "$__vsc_in_command_execution" = "0" ]; then
+ __vsc_in_command_execution="1"
+ __vsc_preexec
+ fi
+ }
+ trap '__vsc_preexec_only "$_"' DEBUG
+ elif [[ "$__vsc_dbg_trap" != '__vsc_preexec "$_"' && "$__vsc_dbg_trap" != '__vsc_preexec_all "$_"' ]]; then
+ __vsc_preexec_all() {
+ if [ "$__vsc_in_command_execution" = "0" ]; then
+ __vsc_in_command_execution="1"
+ builtin eval "${__vsc_dbg_trap}"
+ __vsc_preexec
+ fi
+ }
+ trap '__vsc_preexec_all "$_"' DEBUG
+ fi
+fi
+
+__vsc_update_prompt
+
+__vsc_restore_exit_code() {
+ return "$1"
+}
+
+__vsc_prompt_cmd_original() {
+ __vsc_status="$?"
+ __vsc_restore_exit_code "${__vsc_status}"
+ # Evaluate the original PROMPT_COMMAND similarly to how bash would normally
+ # See https://unix.stackexchange.com/a/672843 for technique
+ for cmd in "${__vsc_original_prompt_command[@]}"; do
+ eval "${cmd:-}"
+ done
+ __vsc_precmd
+}
+
+__vsc_prompt_cmd() {
+ __vsc_status="$?"
+ __vsc_precmd
+}
+
+# PROMPT_COMMAND arrays and strings seem to be handled the same (handling only the first entry of
+# the array?)
+__vsc_original_prompt_command=$PROMPT_COMMAND
+
+if [[ -z "${bash_preexec_imported:-}" ]]; then
+ if [[ -n "$__vsc_original_prompt_command" && "$__vsc_original_prompt_command" != "__vsc_prompt_cmd" ]]; then
+ PROMPT_COMMAND=__vsc_prompt_cmd_original
+ else
+ PROMPT_COMMAND=__vsc_prompt_cmd
+ fi
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-clink.lua b/src/plugins/terminal/shellintegrations/shellintegration-clink.lua
new file mode 100644
index 0000000000..ff9d5f3fac
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-clink.lua
@@ -0,0 +1,52 @@
+-- Copyright (c) 2023 Chris Antos
+-- SPDX-License-Identifier: MIT
+
+-- luacheck: globals vscode_shell_integration NONL
+if vscode_shell_integration == nil then
+ vscode_shell_integration = true
+end
+
+if not vscode_shell_integration then
+ return
+end
+
+local function is_vscode()
+ local term_program = os.getenv("term_program") or ""
+ if term_program:lower() == "vscode" then
+ return true
+ end
+end
+
+local function send_context()
+ if is_vscode() then
+ local codes = ""
+ codes = codes .. "\027]633;D;" .. os.geterrorlevel() .. "\a" -- send command exit code
+ codes = codes .. "\027]633;P;Cwd=" .. os.getcwd() .. "\a" -- send cwd as title
+ clink.print(codes, NONL)
+ end
+end
+
+local p = clink.promptfilter(-999)
+
+function p:filter() -- luacheck: no unused
+ -- Nothing to do here, but the filter function must be defined.
+end
+
+function p:surround() -- luacheck: no unused
+ if is_vscode() then
+ local pre, suf
+ local rpre, rsuf
+
+ -- ESC codes surrounding prompt string
+ pre = "\027]633;A\a" -- copied from shellIntegration-rc.zsh
+ suf = "\027]633;B\a" -- copied from shellIntegration-rc.zsh
+
+ -- ESC codes surrounding right side prompt string
+ rpre = "\027]633;H\a" -- copied from shellIntegration-rc.zsh
+ rsuf = "\027]633;I\a" -- copied from shellIntegration-rc.zsh
+
+ return pre, suf, rpre, rsuf
+ end
+end
+
+clink.onbeginedit(send_context)
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-env.zsh b/src/plugins/terminal/shellintegrations/shellintegration-env.zsh
new file mode 100644
index 0000000000..3c890539ae
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-env.zsh
@@ -0,0 +1,15 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+if [[ -f $USER_ZDOTDIR/.zshenv ]]; then
+ VSCODE_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$USER_ZDOTDIR
+
+ # prevent recursion
+ if [[ $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
+ . $USER_ZDOTDIR/.zshenv
+ fi
+
+ USER_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$VSCODE_ZDOTDIR
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-login.zsh b/src/plugins/terminal/shellintegrations/shellintegration-login.zsh
new file mode 100644
index 0000000000..37ff543979
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-login.zsh
@@ -0,0 +1,7 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+ZDOTDIR=$USER_ZDOTDIR
+if [[ $options[norcs] = off && -o "login" && -f $ZDOTDIR/.zlogin ]]; then
+ . $ZDOTDIR/.zlogin
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-profile.zsh b/src/plugins/terminal/shellintegrations/shellintegration-profile.zsh
new file mode 100644
index 0000000000..724e1f2879
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-profile.zsh
@@ -0,0 +1,15 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+if [[ $options[norcs] = off && -o "login" && -f $USER_ZDOTDIR/.zprofile ]]; then
+ VSCODE_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$USER_ZDOTDIR
+ . $USER_ZDOTDIR/.zprofile
+ ZDOTDIR=$VSCODE_ZDOTDIR
+
+ # Apply any explicit path prefix (see #99878)
+ if (( ${+VSCODE_PATH_PREFIX} )); then
+ export PATH=$VSCODE_PATH_PREFIX$PATH
+ fi
+ builtin unset VSCODE_PATH_PREFIX
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh b/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh
new file mode 100644
index 0000000000..df4109131a
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh
@@ -0,0 +1,160 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+builtin autoload -Uz add-zsh-hook
+
+# Prevent the script recursing when setting up
+if [ -n "$VSCODE_SHELL_INTEGRATION" ]; then
+ ZDOTDIR=$USER_ZDOTDIR
+ builtin return
+fi
+
+# This variable allows the shell to both detect that VS Code's shell integration is enabled as well
+# as disable it by unsetting the variable.
+VSCODE_SHELL_INTEGRATION=1
+
+# By default, zsh will set the $HISTFILE to the $ZDOTDIR location automatically. In the case of the
+# shell integration being injected, this means that the terminal will use a different history file
+# to other terminals. To fix this issue, set $HISTFILE back to the default location before ~/.zshrc
+# is called as that may depend upon the value.
+if [[ "$VSCODE_INJECTION" == "1" ]]; then
+ HISTFILE=$USER_ZDOTDIR/.zsh_history
+fi
+
+# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
+if [[ "$VSCODE_INJECTION" == "1" ]]; then
+ if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
+ VSCODE_ZDOTDIR=$ZDOTDIR
+ ZDOTDIR=$USER_ZDOTDIR
+ # A user's custom HISTFILE location might be set when their .zshrc file is sourced below
+ . $USER_ZDOTDIR/.zshrc
+ fi
+fi
+
+# Shell integration was disabled by the shell, exit without warning assuming either the shell has
+# explicitly disabled shell integration as it's incompatible or it implements the protocol.
+if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
+ builtin return
+fi
+
+# The property (P) and command (E) codes embed values which require escaping.
+# Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex.
+__vsc_escape_value() {
+ builtin emulate -L zsh
+
+ # Process text byte by byte, not by codepoint.
+ builtin local LC_ALL=C str="$1" i byte token out=''
+
+ for (( i = 0; i < ${#str}; ++i )); do
+ byte="${str:$i:1}"
+
+ # Escape backslashes and semi-colons
+ if [ "$byte" = "\\" ]; then
+ token="\\\\"
+ elif [ "$byte" = ";" ]; then
+ token="\\x3b"
+ else
+ token="$byte"
+ fi
+
+ out+="$token"
+ done
+
+ builtin print -r "$out"
+}
+
+__vsc_in_command_execution="1"
+__vsc_current_command=""
+
+__vsc_prompt_start() {
+ builtin printf '\e]633;A\a'
+}
+
+__vsc_prompt_end() {
+ builtin printf '\e]633;B\a'
+}
+
+__vsc_update_cwd() {
+ builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "${PWD}")"
+}
+
+__vsc_command_output_start() {
+ builtin printf '\e]633;C\a'
+ builtin printf '\e]633;E;%s\a' "${__vsc_current_command}"
+}
+
+__vsc_continuation_start() {
+ builtin printf '\e]633;F\a'
+}
+
+__vsc_continuation_end() {
+ builtin printf '\e]633;G\a'
+}
+
+__vsc_right_prompt_start() {
+ builtin printf '\e]633;H\a'
+}
+
+__vsc_right_prompt_end() {
+ builtin printf '\e]633;I\a'
+}
+
+__vsc_command_complete() {
+ if [[ "$__vsc_current_command" == "" ]]; then
+ builtin printf '\e]633;D\a'
+ else
+ builtin printf '\e]633;D;%s\a' "$__vsc_status"
+ fi
+ __vsc_update_cwd
+}
+
+if [[ -o NOUNSET ]]; then
+ if [ -z "${RPROMPT-}" ]; then
+ RPROMPT=""
+ fi
+fi
+__vsc_update_prompt() {
+ __vsc_prior_prompt="$PS1"
+ __vsc_prior_prompt2="$PS2"
+ __vsc_in_command_execution=""
+ PS1="%{$(__vsc_prompt_start)%}$PS1%{$(__vsc_prompt_end)%}"
+ PS2="%{$(__vsc_continuation_start)%}$PS2%{$(__vsc_continuation_end)%}"
+ if [ -n "$RPROMPT" ]; then
+ __vsc_prior_rprompt="$RPROMPT"
+ RPROMPT="%{$(__vsc_right_prompt_start)%}$RPROMPT%{$(__vsc_right_prompt_end)%}"
+ fi
+}
+
+__vsc_precmd() {
+ local __vsc_status="$?"
+ if [ -z "${__vsc_in_command_execution-}" ]; then
+ # not in command execution
+ __vsc_command_output_start
+ fi
+
+ __vsc_command_complete "$__vsc_status"
+ __vsc_current_command=""
+
+ # in command execution
+ if [ -n "$__vsc_in_command_execution" ]; then
+ # non null
+ __vsc_update_prompt
+ fi
+}
+
+__vsc_preexec() {
+ PS1="$__vsc_prior_prompt"
+ PS2="$__vsc_prior_prompt2"
+ if [ -n "$RPROMPT" ]; then
+ RPROMPT="$__vsc_prior_rprompt"
+ fi
+ __vsc_in_command_execution="1"
+ __vsc_current_command=$2
+ __vsc_command_output_start
+}
+add-zsh-hook precmd __vsc_precmd
+add-zsh-hook preexec __vsc_preexec
+
+if [[ $options[login] = off && $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
+ ZDOTDIR=$USER_ZDOTDIR
+fi
diff --git a/src/plugins/terminal/shellintegrations/shellintegration.fish b/src/plugins/terminal/shellintegrations/shellintegration.fish
new file mode 100644
index 0000000000..7495bab3f4
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration.fish
@@ -0,0 +1,122 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+# Visual Studio Code terminal integration for fish
+#
+# Manual installation:
+#
+# (1) Add the following to the end of `$__fish_config_dir/config.fish`:
+#
+# string match -q "$TERM_PROGRAM" "vscode"
+# and . (code --locate-shell-integration-path fish)
+#
+# (2) Restart fish.
+
+# Don't run in scripts, other terminals, or more than once per session.
+status is-interactive
+and string match --quiet "$TERM_PROGRAM" "vscode"
+and ! set --query VSCODE_SHELL_INTEGRATION
+or exit
+
+set --global VSCODE_SHELL_INTEGRATION 1
+
+# Apply any explicit path prefix (see #99878)
+if status --is-login; and set -q VSCODE_PATH_PREFIX
+ fish_add_path -p $VSCODE_PATH_PREFIX
+end
+set -e VSCODE_PATH_PREFIX
+
+# Helper function
+function __vsc_esc -d "Emit escape sequences for VS Code shell integration"
+ builtin printf "\e]633;%s\a" (string join ";" $argv)
+end
+
+# Sent right before executing an interactive command.
+# Marks the beginning of command output.
+function __vsc_cmd_executed --on-event fish_preexec
+ __vsc_esc C
+ __vsc_esc E (__vsc_escape_value "$argv")
+
+ # Creates a marker to indicate a command was run.
+ set --global _vsc_has_cmd
+end
+
+
+# Escape a value for use in the 'P' ("Property") or 'E' ("Command Line") sequences.
+# Backslashes are doubled and non-alphanumeric characters are hex encoded.
+function __vsc_escape_value
+ # Escape backslashes and semi-colons
+ echo $argv \
+ | string replace --all '\\' '\\\\' \
+ | string replace --all ';' '\\x3b' \
+ ;
+end
+
+# Sent right after an interactive command has finished executing.
+# Marks the end of command output.
+function __vsc_cmd_finished --on-event fish_postexec
+ __vsc_esc D $status
+end
+
+# Sent when a command line is cleared or reset, but no command was run.
+# Marks the cleared line with neither success nor failure.
+function __vsc_cmd_clear --on-event fish_cancel
+ __vsc_esc D
+end
+
+# Sent whenever a new fish prompt is about to be displayed.
+# Updates the current working directory.
+function __vsc_update_cwd --on-event fish_prompt
+ __vsc_esc P Cwd=(__vsc_escape_value "$PWD")
+
+ # If a command marker exists, remove it.
+ # Otherwise, the commandline is empty and no command was run.
+ if set --query _vsc_has_cmd
+ set --erase _vsc_has_cmd
+ else
+ __vsc_cmd_clear
+ end
+end
+
+# Sent at the start of the prompt.
+# Marks the beginning of the prompt (and, implicitly, a new line).
+function __vsc_fish_prompt_start
+ __vsc_esc A
+end
+
+# Sent at the end of the prompt.
+# Marks the beginning of the user's command input.
+function __vsc_fish_cmd_start
+ __vsc_esc B
+end
+
+function __vsc_fish_has_mode_prompt -d "Returns true if fish_mode_prompt is defined and not empty"
+ functions fish_mode_prompt | string match -rvq '^ *(#|function |end$|$)'
+end
+
+# Preserve the user's existing prompt, to wrap in our escape sequences.
+functions --copy fish_prompt __vsc_fish_prompt
+
+# Preserve and wrap fish_mode_prompt (which appears to the left of the regular
+# prompt), but only if it's not defined as an empty function (which is the
+# officially documented way to disable that feature).
+if __vsc_fish_has_mode_prompt
+ functions --copy fish_mode_prompt __vsc_fish_mode_prompt
+
+ function fish_mode_prompt
+ __vsc_fish_prompt_start
+ __vsc_fish_mode_prompt
+ end
+
+ function fish_prompt
+ __vsc_fish_prompt
+ __vsc_fish_cmd_start
+ end
+else
+ # No fish_mode_prompt, so put everything in fish_prompt.
+ function fish_prompt
+ __vsc_fish_prompt_start
+ __vsc_fish_prompt
+ __vsc_fish_cmd_start
+ end
+end
diff --git a/src/plugins/terminal/shellintegrations/shellintegration.ps1 b/src/plugins/terminal/shellintegrations/shellintegration.ps1
new file mode 100644
index 0000000000..4fd978a884
--- /dev/null
+++ b/src/plugins/terminal/shellintegrations/shellintegration.ps1
@@ -0,0 +1,158 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# SPDX-License-Identifier: MIT
+
+# Prevent installing more than once per session
+if (Test-Path variable:global:__VSCodeOriginalPrompt) {
+ return;
+}
+
+# Disable shell integration when the language mode is restricted
+if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") {
+ return;
+}
+
+$Global:__VSCodeOriginalPrompt = $function:Prompt
+
+$Global:__LastHistoryId = -1
+
+function Global:__VSCode-Escape-Value([string]$value) {
+ # NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`.
+ # Replace any non-alphanumeric characters.
+ [regex]::Replace($value, '[\\\n;]', { param($match)
+ # Encode the (ascii) matches as `\x<hex>`
+ -Join (
+ [System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
+ )
+ })
+}
+
+function Global:Prompt() {
+ $FakeCode = [int]!$global:?
+ # NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an
+ # error when $LastHistoryEntry is null, and is not otherwise useful.
+ Set-StrictMode -Off
+ $LastHistoryEntry = Get-History -Count 1
+ # Skip finishing the command if the first command has not yet started
+ if ($Global:__LastHistoryId -ne -1) {
+ if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
+ # Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
+ $Result = "$([char]0x1b)]633;E`a"
+ $Result += "$([char]0x1b)]633;D`a"
+ } else {
+ # Command finished command line
+ # OSC 633 ; A ; <CommandLine?> ST
+ $Result = "$([char]0x1b)]633;E;"
+ # Sanitize the command line to ensure it can get transferred to the terminal and can be parsed
+ # correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter
+ # to only be composed of _printable_ characters as per the spec.
+ if ($LastHistoryEntry.CommandLine) {
+ $CommandLine = $LastHistoryEntry.CommandLine
+ } else {
+ $CommandLine = ""
+ }
+ $Result += $(__VSCode-Escape-Value $CommandLine)
+ $Result += "`a"
+ # Command finished exit code
+ # OSC 633 ; D [; <ExitCode>] ST
+ $Result += "$([char]0x1b)]633;D;$FakeCode`a"
+ }
+ }
+ # Prompt started
+ # OSC 633 ; A ST
+ $Result += "$([char]0x1b)]633;A`a"
+ # Current working directory
+ # OSC 633 ; <Property>=<Value> ST
+ $Result += if($pwd.Provider.Name -eq 'FileSystem'){"$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a"}
+ # Before running the original prompt, put $? back to what it was:
+ if ($FakeCode -ne 0) {
+ Write-Error "failure" -ea ignore
+ }
+ # Run the original prompt
+ $Result += $Global:__VSCodeOriginalPrompt.Invoke()
+ # Write command started
+ $Result += "$([char]0x1b)]633;B`a"
+ $Global:__LastHistoryId = $LastHistoryEntry.Id
+ return $Result
+}
+
+# Only send the command executed sequence when PSReadLine is loaded, if not shell integration should
+# still work thanks to the command line sequence
+if (Get-Module -Name PSReadLine) {
+ $__VSCodeOriginalPSConsoleHostReadLine = $function:PSConsoleHostReadLine
+ function Global:PSConsoleHostReadLine {
+ $tmp = $__VSCodeOriginalPSConsoleHostReadLine.Invoke()
+ # Write command executed sequence directly to Console to avoid the new line from Write-Host
+ [Console]::Write("$([char]0x1b)]633;C`a")
+ $tmp
+ }
+}
+
+# Set IsWindows property
+[Console]::Write("$([char]0x1b)]633;P;IsWindows=$($IsWindows)`a")
+
+# Set always on key handlers which map to default VS Code keybindings
+function Set-MappedKeyHandler {
+ param ([string[]] $Chord, [string[]]$Sequence)
+ try {
+ $Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
+ } catch [System.Management.Automation.ParameterBindingException] {
+ # PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord,
+ # so we check what's bound and filter it.
+ $Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1
+ }
+ if ($Handler) {
+ Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function
+ }
+}
+
+function Set-MappedKeyHandlers {
+ Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
+ Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
+ Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
+ Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'
+
+ # Conditionally enable suggestions
+ if ($env:VSCODE_SUGGEST -eq '1') {
+ Remove-Item Env:VSCODE_SUGGEST
+
+ # VS Code send completions request (may override Ctrl+Spacebar)
+ Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock {
+ Send-Completions
+ }
+
+ # Suggest trigger characters
+ Set-PSReadLineKeyHandler -Chord "-" -ScriptBlock {
+ [Microsoft.PowerShell.PSConsoleReadLine]::Insert("-")
+ Send-Completions
+ }
+ }
+}
+
+function Send-Completions {
+ $commandLine = ""
+ $cursorIndex = 0
+ # TODO: Since fuzzy matching exists, should completions be provided only for character after the
+ # last space and then filter on the client side? That would let you trigger ctrl+space
+ # anywhere on a word and have full completions available
+ [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex)
+ $completionPrefix = $commandLine
+
+ # Get completions
+ $result = "`e]633;Completions"
+ if ($completionPrefix.Length -gt 0) {
+ # Get and send completions
+ $completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex
+ if ($null -ne $completions.CompletionMatches) {
+ $result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);"
+ $result += $completions.CompletionMatches | ConvertTo-Json -Compress
+ }
+ }
+ $result += "`a"
+
+ Write-Host -NoNewLine $result
+}
+
+# Register key handlers if PSReadLine is available
+if (Get-Module -Name PSReadLine) {
+ Set-MappedKeyHandlers
+}
diff --git a/src/plugins/terminal/shellmodel.cpp b/src/plugins/terminal/shellmodel.cpp
new file mode 100644
index 0000000000..b4cf53a1e9
--- /dev/null
+++ b/src/plugins/terminal/shellmodel.cpp
@@ -0,0 +1,110 @@
+// 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 "shellmodel.h"
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/filepath.h>
+
+#include <QFileIconProvider>
+#include <QStandardPaths>
+
+namespace Terminal::Internal {
+
+using namespace Utils;
+
+FilePaths availableShells()
+{
+ if (Utils::HostOsInfo::isWindowsHost()) {
+ FilePaths shells;
+
+ FilePath comspec = FilePath::fromUserInput(qtcEnvironmentVariable("COMSPEC"));
+ shells << comspec;
+
+ if (comspec.fileName() != "cmd.exe") {
+ FilePath cmd = FilePath::fromUserInput(QStandardPaths::findExecutable("cmd.exe"));
+ shells << cmd;
+ }
+
+ FilePath powershell = FilePath::fromUserInput(
+ QStandardPaths::findExecutable("powershell.exe"));
+ if (powershell.exists())
+ shells << powershell;
+
+ FilePath bash = FilePath::fromUserInput(QStandardPaths::findExecutable("bash.exe"));
+ if (bash.exists())
+ shells << bash;
+
+ FilePath git_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("git.exe"));
+ if (git_bash.exists())
+ shells << git_bash.parentDir().parentDir().pathAppended("usr/bin/bash.exe");
+
+ FilePath msys2_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("msys2.exe"));
+ if (msys2_bash.exists())
+ shells << msys2_bash.parentDir().pathAppended("usr/bin/bash.exe");
+
+ return shells;
+ } else {
+ FilePath shellsFile = FilePath::fromString("/etc/shells");
+ const auto shellFileContent = shellsFile.fileContents();
+ QTC_ASSERT_EXPECTED(shellFileContent, return {});
+
+ QString shellFileContentString = QString::fromUtf8(*shellFileContent);
+
+ // Filter out comments ...
+ const QStringList lines
+ = Utils::filtered(shellFileContentString.split('\n', Qt::SkipEmptyParts),
+ [](const QString &line) { return !line.trimmed().startsWith('#'); });
+
+ // Convert lines to file paths ...
+ const FilePaths shells = Utils::transform(lines, [](const QString &line) {
+ return FilePath::fromUserInput(line.trimmed());
+ });
+
+ // ... and filter out non-existing shells.
+ return Utils::filtered(shells, [](const FilePath &shell) { return shell.exists(); });
+ }
+}
+
+struct ShellModelPrivate
+{
+ QList<ShellModelItem> localShells;
+};
+
+ShellModel::ShellModel(QObject *parent)
+ : QObject(parent)
+ , d(new ShellModelPrivate())
+{
+ QFileIconProvider iconProvider;
+
+ const FilePaths shells = availableShells();
+ for (const FilePath &shell : shells) {
+ ShellModelItem item;
+ item.icon = iconProvider.icon(shell.toFileInfo());
+ item.name = shell.toUserOutput();
+ item.openParameters.shellCommand = {shell, {}};
+ d->localShells << item;
+ }
+}
+
+ShellModel::~ShellModel() = default;
+
+QList<ShellModelItem> ShellModel::local() const
+{
+ return d->localShells;
+}
+
+QList<ShellModelItem> ShellModel::remote() const
+{
+ const auto deviceCmds = Utils::Terminal::Hooks::instance().getTerminalCommandsForDevicesHook()();
+
+ const QList<ShellModelItem> deviceItems = Utils::transform(
+ deviceCmds, [](const Utils::Terminal::NameAndCommandLine &item) -> ShellModelItem {
+ return ShellModelItem{item.name, {}, {item.commandLine, std::nullopt, std::nullopt}};
+ });
+
+ return deviceItems;
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/shellmodel.h b/src/plugins/terminal/shellmodel.h
new file mode 100644
index 0000000000..272f3fcd39
--- /dev/null
+++ b/src/plugins/terminal/shellmodel.h
@@ -0,0 +1,37 @@
+// 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
+
+#pragma once
+
+#include <utils/terminalhooks.h>
+
+#include <QIcon>
+#include <QObject>
+#include <QString>
+
+#include <memory>
+
+namespace Terminal::Internal {
+struct ShellModelPrivate;
+
+struct ShellModelItem
+{
+ QString name;
+ QIcon icon;
+ Utils::Terminal::OpenTerminalParameters openParameters;
+};
+
+class ShellModel : public QObject
+{
+public:
+ ShellModel(QObject *parent = nullptr);
+ ~ShellModel();
+
+ QList<ShellModelItem> local() const;
+ QList<ShellModelItem> remote() const;
+
+private:
+ std::unique_ptr<ShellModelPrivate> d;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/terminal.qbs b/src/plugins/terminal/terminal.qbs
new file mode 100644
index 0000000000..231d3630b7
--- /dev/null
+++ b/src/plugins/terminal/terminal.qbs
@@ -0,0 +1,46 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "Terminal"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "vterm" }
+ Depends { name: "ptyqt" }
+
+ files: [
+ "celliterator.cpp",
+ "celliterator.h",
+ "glyphcache.cpp",
+ "glyphcache.h",
+ "keys.cpp",
+ "keys.h",
+ "scrollback.cpp",
+ "scrollback.h",
+ "shellmodel.cpp",
+ "shellmodel.h",
+ "shellintegration.cpp",
+ "shellintegration.h",
+ "terminal.qrc",
+ "terminalcommands.cpp",
+ "terminalcommands.h",
+ "terminalpane.cpp",
+ "terminalpane.h",
+ "terminalplugin.cpp",
+ "terminalplugin.h",
+ "terminalprocessimpl.cpp",
+ "terminalprocessimpl.h",
+ "terminalsearch.cpp",
+ "terminalsearch.h",
+ "terminalsettings.cpp",
+ "terminalsettings.h",
+ "terminalsettingspage.cpp",
+ "terminalsettingspage.h",
+ "terminalsurface.cpp",
+ "terminalsurface.h",
+ "terminaltr.h",
+ "terminalwidget.cpp",
+ "terminalwidget.h",
+ ]
+}
+
diff --git a/src/plugins/terminal/terminal.qrc b/src/plugins/terminal/terminal.qrc
new file mode 100644
index 0000000000..63c28169df
--- /dev/null
+++ b/src/plugins/terminal/terminal.qrc
@@ -0,0 +1,16 @@
+<RCC>
+ <qresource prefix="/terminal">
+ <file>images/settingscategory_terminal.png</file>
+ <file>images/settingscategory_terminal@2x.png</file>
+ <file>images/terminal.png</file>
+ <file>images/terminal@2x.png</file>
+ <file>shellintegrations/shellintegration-bash.sh</file>
+ <file>shellintegrations/shellintegration-env.zsh</file>
+ <file>shellintegrations/shellintegration-login.zsh</file>
+ <file>shellintegrations/shellintegration-profile.zsh</file>
+ <file>shellintegrations/shellintegration-rc.zsh</file>
+ <file>shellintegrations/shellintegration.fish</file>
+ <file>shellintegrations/shellintegration.ps1</file>
+ <file>shellintegrations/shellintegration-clink.lua</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/terminal/terminalcommands.cpp b/src/plugins/terminal/terminalcommands.cpp
new file mode 100644
index 0000000000..0e7cba8194
--- /dev/null
+++ b/src/plugins/terminal/terminalcommands.cpp
@@ -0,0 +1,186 @@
+// 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 "terminalcommands.h"
+#include "terminaltr.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/find/textfindconstants.h>
+#include <coreplugin/locator/locatorconstants.h>
+
+#include <utils/hostosinfo.h>
+
+//#include <coreplugin/context.h>
+
+using namespace Core;
+using namespace Utils;
+
+namespace Terminal {
+
+constexpr char COPY[] = "Terminal.Copy";
+constexpr char PASTE[] = "Terminal.Paste";
+constexpr char COPY_LINK[] = "Terminal.CopyLink";
+constexpr char CLEARSELECTION[] = "Terminal.ClearSelection";
+constexpr char MOVECURSORWORDLEFT[] = "Terminal.MoveCursorWordLeft";
+constexpr char MOVECURSORWORDRIGHT[] = "Terminal.MoveCursorWordRight";
+
+constexpr char NEWTERMINAL[] = "Terminal.NewTerminal";
+constexpr char CLOSETERMINAL[] = "Terminal.CloseTerminal";
+constexpr char NEXTTERMINAL[] = "Terminal.NextTerminal";
+constexpr char PREVTERMINAL[] = "Terminal.PrevTerminal";
+constexpr char MINMAX[] = "Terminal.MinMax";
+
+TerminalCommands &TerminalCommands::instance()
+{
+ static TerminalCommands instance;
+ return instance;
+}
+
+TerminalCommands::TerminalCommands() {}
+
+void TerminalCommands::init(const Core::Context &context)
+{
+ m_context = context;
+ initWidgetActions();
+ initPaneActions();
+ initGlobalCommands();
+}
+
+void TerminalCommands::registerAction(QAction &action,
+ const Utils::Id &id,
+ QList<QKeySequence> shortCuts)
+{
+ Command *cmd = ActionManager::instance()->registerAction(&action, id, m_context);
+ cmd->setKeySequences(shortCuts);
+ m_commands.push_back(cmd);
+}
+
+void TerminalCommands::initWidgetActions()
+{
+ m_widgetActions.copy.setText(Tr::tr("Copy"));
+ m_widgetActions.paste.setText(Tr::tr("Paste"));
+ m_widgetActions.copyLink.setText(Tr::tr("Copy Link"));
+ m_widgetActions.clearSelection.setText(Tr::tr("Clear Selection"));
+ m_widgetActions.clearTerminal.setText(Tr::tr("Clear Terminal"));
+ m_widgetActions.moveCursorWordLeft.setText(Tr::tr("Move Cursor Word Left"));
+ m_widgetActions.moveCursorWordRight.setText(Tr::tr("Move Cursor Word Right"));
+ m_widgetActions.findNext.setText(Tr::tr("Find Next"));
+ m_widgetActions.findPrevious.setText(Tr::tr("Find Previous"));
+
+ registerAction(m_widgetActions.copy,
+ COPY,
+ {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C")
+ : QLatin1String("Ctrl+Shift+C"))});
+
+ registerAction(m_widgetActions.paste,
+ PASTE,
+ {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V")
+ : QLatin1String("Ctrl+Shift+V"))});
+
+ registerAction(m_widgetActions.copyLink, COPY_LINK);
+
+ registerAction(m_widgetActions.clearSelection, CLEARSELECTION);
+
+ registerAction(m_widgetActions.moveCursorWordLeft,
+ MOVECURSORWORDLEFT,
+ {QKeySequence("Alt+Left")});
+
+ registerAction(m_widgetActions.moveCursorWordRight,
+ MOVECURSORWORDRIGHT,
+ {QKeySequence("Alt+Right")});
+}
+
+void TerminalCommands::initPaneActions()
+{
+ m_paneActions.newTerminal.setText(Tr::tr("New Terminal"));
+ m_paneActions.closeTerminal.setText(Tr::tr("Close Terminal"));
+ m_paneActions.nextTerminal.setText(Tr::tr("Next Terminal"));
+ m_paneActions.prevTerminal.setText(Tr::tr("Previous Terminal"));
+ m_paneActions.minMax.setText(Tr::tr("Minimize/Maximize Terminal"));
+
+ registerAction(m_paneActions.newTerminal,
+ NEWTERMINAL,
+ {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T")
+ : QLatin1String("Ctrl+Shift+T"))});
+
+ registerAction(m_paneActions.closeTerminal,
+ CLOSETERMINAL,
+ {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+W")
+ : QLatin1String("Ctrl+Shift+W"))});
+
+ registerAction(m_paneActions.nextTerminal,
+ NEXTTERMINAL,
+ {QKeySequence("ALT+TAB"),
+ QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[")
+ : QLatin1String("Ctrl+PgUp"))});
+
+ registerAction(m_paneActions.prevTerminal,
+ PREVTERMINAL,
+ {QKeySequence("ALT+SHIFT+TAB"),
+ QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]")
+ : QLatin1String("Ctrl+PgDown"))});
+
+ registerAction(m_paneActions.minMax,
+ MINMAX,
+ {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Return")
+ : QLatin1String("Alt+Return"))});
+}
+
+void TerminalCommands::initGlobalCommands()
+{
+ // Global commands we still want to allow
+ m_commands.push_back(ActionManager::command(Constants::ZOOM_IN));
+ m_commands.push_back(ActionManager::command(Constants::ZOOM_OUT));
+ m_commands.push_back(ActionManager::command(Constants::EXIT));
+ m_commands.push_back(ActionManager::command(Constants::OPTIONS));
+}
+
+bool TerminalCommands::triggerAction(QKeyEvent *event)
+{
+ QKeyCombination combination = event->keyCombination();
+
+ // On macOS, the arrow keys include the KeypadModifier, which we don't want.
+ if (HostOsInfo::isMacHost() && combination.keyboardModifiers() & Qt::KeypadModifier)
+ combination = QKeyCombination(combination.keyboardModifiers() & ~Qt::KeypadModifier,
+ combination.key());
+
+ for (const auto &command : TerminalCommands::instance().m_commands) {
+ if (!command->action()->isEnabled())
+ continue;
+
+ for (const auto &shortcut : command->keySequences()) {
+ const auto result = shortcut.matches(QKeySequence(combination));
+ if (result == QKeySequence::ExactMatch) {
+ command->action()->trigger();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+QAction *TerminalCommands::openSettingsAction()
+{
+ return ActionManager::command("Preferences.Terminal.General")->action();
+}
+
+void TerminalCommands::lazyInitCommand(const Utils::Id &id)
+{
+ Command *cmd = ActionManager::command(id);
+ QTC_ASSERT(cmd, return);
+ m_commands.append(cmd);
+}
+
+void TerminalCommands::lazyInitCommands()
+{
+ static const Utils::Id terminalPaneCmd("QtCreator.Pane.Terminal");
+ lazyInitCommand(terminalPaneCmd);
+ lazyInitCommand(Core::Constants::FIND_IN_DOCUMENT);
+ lazyInitCommand(Core::Constants::FIND_NEXT);
+ lazyInitCommand(Core::Constants::FIND_PREVIOUS);
+ lazyInitCommand(Core::Constants::LOCATE);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalcommands.h b/src/plugins/terminal/terminalcommands.h
new file mode 100644
index 0000000000..71bc7ffe0f
--- /dev/null
+++ b/src/plugins/terminal/terminalcommands.h
@@ -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
+
+#pragma once
+
+#include <utils/id.h>
+
+#include <coreplugin/icontext.h>
+
+#include <QAction>
+#include <QCoreApplication>
+#include <QKeyEvent>
+
+namespace Core {
+class Command;
+class Context;
+} // namespace Core
+
+namespace Terminal {
+
+struct WidgetActions
+{
+ QAction copy;
+ QAction paste;
+ QAction copyLink;
+ QAction clearSelection;
+ QAction clearTerminal;
+ QAction moveCursorWordLeft;
+ QAction moveCursorWordRight;
+ QAction findNext;
+ QAction findPrevious;
+};
+
+struct PaneActions
+{
+ QAction newTerminal;
+ QAction closeTerminal;
+ QAction nextTerminal;
+ QAction prevTerminal;
+ QAction minMax;
+};
+
+class TerminalCommands
+{
+public:
+ TerminalCommands();
+
+ void init(const Core::Context &context);
+ static TerminalCommands &instance();
+ static WidgetActions &widgetActions() { return instance().m_widgetActions; }
+ static PaneActions &paneActions() { return instance().m_paneActions; }
+
+ static QList<QKeySequence> shortcutsFor(QAction *action);
+
+ static bool triggerAction(QKeyEvent *event);
+
+ static QAction *openSettingsAction();
+
+ void lazyInitCommands();
+
+protected:
+ void initWidgetActions();
+ void initPaneActions();
+ void initGlobalCommands();
+
+ void lazyInitCommand(const Utils::Id &id);
+ void registerAction(QAction &action, const Utils::Id &id, QList<QKeySequence> shortcuts = {});
+
+private:
+ WidgetActions m_widgetActions;
+ PaneActions m_paneActions;
+ QList<Core::Command *> m_commands;
+ Core::Context m_context;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp
new file mode 100644
index 0000000000..92ad595191
--- /dev/null
+++ b/src/plugins/terminal/terminalpane.cpp
@@ -0,0 +1,397 @@
+// 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 "terminalpane.h"
+
+#include "shellmodel.h"
+#include "terminalcommands.h"
+#include "terminalsettings.h"
+#include "terminaltr.h"
+#include "terminalwidget.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/terminalhooks.h>
+#include <utils/utilsicons.h>
+
+#include <QMenu>
+#include <QStandardPaths>
+#include <QToolButton>
+
+namespace Terminal {
+
+using namespace Utils;
+using namespace Utils::Terminal;
+
+TerminalPane::TerminalPane(QObject *parent)
+ : Core::IOutputPane(parent)
+ , m_tabWidget(new QTabWidget)
+{
+ setupContext("Terminal.Pane", m_tabWidget);
+ setZoomButtonsEnabled(true);
+
+ TerminalCommands::instance().init(Core::Context("Terminal.Pane"));
+
+ connect(this, &IOutputPane::zoomInRequested, this, [this] {
+ if (currentTerminal())
+ currentTerminal()->zoomIn();
+ });
+ connect(this, &IOutputPane::zoomOutRequested, this, [this] {
+ if (currentTerminal())
+ currentTerminal()->zoomOut();
+ });
+
+ QAction &newTerminal = TerminalCommands::instance().paneActions().newTerminal;
+ QAction &closeTerminal = TerminalCommands::instance().paneActions().closeTerminal;
+
+ newTerminal.setIcon(
+ Icon({{":/terminal/images/terminal.png", Theme::IconsBaseColor},
+ {":/utils/images/iconoverlay_add_small.png", Theme::IconsRunToolBarColor}})
+ .icon());
+ newTerminal.setToolTip(Tr::tr("Create a new Terminal."));
+
+ connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); });
+
+ closeTerminal.setIcon(
+ Icon({{":/terminal/images/terminal.png", Theme::IconsBaseColor},
+ {":/utils/images/iconoverlay_close_small.png", Theme::IconsStopToolBarColor}})
+ .icon());
+ closeTerminal.setToolTip(Tr::tr("Close the current Terminal."));
+
+ connect(&closeTerminal, &QAction::triggered, this, [this] {
+ removeTab(m_tabWidget->currentIndex());
+ });
+
+ m_newTerminalButton = new QToolButton();
+
+ QMenu *shellMenu = new QMenu(m_newTerminalButton);
+ Internal::ShellModel *shellModel = new Internal::ShellModel(shellMenu);
+ connect(shellMenu, &QMenu::aboutToShow, shellMenu, [shellMenu, shellModel, pane = this] {
+ shellMenu->clear();
+
+ const auto addItems = [shellMenu, pane](const QList<Internal::ShellModelItem> &items) {
+ for (const Internal::ShellModelItem &item : items) {
+ QAction *action = new QAction(item.icon, item.name, shellMenu);
+
+ connect(action, &QAction::triggered, action, [item, pane]() {
+ pane->openTerminal(item.openParameters);
+ });
+
+ shellMenu->addAction(action);
+ }
+ };
+
+ addItems(shellModel->local());
+ shellMenu->addSection(Tr::tr("Devices"));
+ addItems(shellModel->remote());
+ });
+
+ newTerminal.setMenu(shellMenu);
+
+ m_newTerminalButton->setDefaultAction(&newTerminal);
+
+ m_closeTerminalButton = new QToolButton();
+ m_closeTerminalButton->setDefaultAction(&closeTerminal);
+
+ connect(&TerminalCommands::instance().paneActions().nextTerminal,
+ &QAction::triggered,
+ this,
+ [this] {
+ if (canNavigate())
+ goToNext();
+ });
+ connect(&TerminalCommands::instance().paneActions().prevTerminal,
+ &QAction::triggered,
+ this,
+ [this] {
+ if (canPrevious())
+ goToPrev();
+ });
+
+ connect(&TerminalCommands::instance().paneActions().minMax, &QAction::triggered, this, []() {
+ Core::Command *minMaxCommand = Core::ActionManager::command("Coreplugin.OutputPane.minmax");
+ if (minMaxCommand)
+ emit minMaxCommand->action()->triggered();
+ });
+
+ m_openSettingsButton = new QToolButton();
+ m_openSettingsButton->setToolTip(Tr::tr("Open Terminal Settings"));
+ m_openSettingsButton->setIcon(Icons::SETTINGS_TOOLBAR.icon());
+
+ connect(m_openSettingsButton, &QToolButton::clicked, m_openSettingsButton, []() {
+ TerminalCommands::openSettingsAction()->trigger();
+ });
+
+ auto updateEscButton = [this] {
+ m_escSettingButton->setChecked(TerminalSettings::instance().sendEscapeToTerminal.value());
+ static QString escKey = QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText);
+ static QString shiftEsc = QKeySequence(QKeyCombination(Qt::ShiftModifier, Qt::Key_Escape))
+ .toString(QKeySequence::NativeText);
+ if (TerminalSettings::instance().sendEscapeToTerminal.value()) {
+ m_escSettingButton->setText(escKey);
+ m_escSettingButton->setToolTip(Tr::tr("Sending ESC to terminal instead of Qt Creator"));
+ } else {
+ m_escSettingButton->setText(shiftEsc);
+ m_escSettingButton->setToolTip(Tr::tr("Press %1 to send ESC to terminal").arg(shiftEsc));
+ }
+ };
+
+ m_escSettingButton = new QToolButton();
+ m_escSettingButton->setCheckable(true);
+
+ updateEscButton();
+
+ connect(m_escSettingButton, &QToolButton::toggled, this, [this] {
+ TerminalSettings::instance().sendEscapeToTerminal.setValue(m_escSettingButton->isChecked());
+ TerminalSettings::instance().apply();
+ TerminalSettings::instance().writeSettings(Core::ICore::settings());
+ });
+
+ connect(&TerminalSettings::instance(), &TerminalSettings::applied, this, updateEscButton);
+}
+
+TerminalPane::~TerminalPane()
+{
+ delete m_tabWidget;
+}
+
+static std::optional<FilePath> startupProjectDirectory()
+{
+ ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
+ if (!project)
+ return std::nullopt;
+
+ return project->projectDirectory();
+}
+
+void TerminalPane::openTerminal(const OpenTerminalParameters &parameters)
+{
+ OpenTerminalParameters parametersCopy{parameters};
+ if (!m_isVisible)
+ emit showPage(IOutputPane::ModeSwitch);
+
+ if (!parametersCopy.workingDirectory) {
+ const std::optional<FilePath> projectDir = startupProjectDirectory();
+ if (projectDir) {
+ if (!parametersCopy.shellCommand
+ || parametersCopy.shellCommand->executable().ensureReachable(*projectDir)) {
+ parametersCopy.workingDirectory = *projectDir;
+ }
+ }
+ }
+
+ auto terminalWidget = new TerminalWidget(m_tabWidget, parametersCopy);
+ m_tabWidget->setCurrentIndex(m_tabWidget->addTab(terminalWidget, Tr::tr("Terminal")));
+ setupTerminalWidget(terminalWidget);
+
+ m_tabWidget->currentWidget()->setFocus();
+
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::addTerminal(TerminalWidget *terminal, const QString &title)
+{
+ if (!m_isVisible)
+ emit showPage(IOutputPane::ModeSwitch);
+ m_tabWidget->setCurrentIndex(m_tabWidget->addTab(terminal, title));
+ setupTerminalWidget(terminal);
+
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::ensureVisible(TerminalWidget *terminal)
+{
+ if (!m_isVisible)
+ emit showPage(IOutputPane::ModeSwitch);
+ m_tabWidget->setCurrentWidget(terminal);
+ terminal->setFocus();
+}
+
+TerminalWidget *TerminalPane::stoppedTerminalWithId(const Id &identifier) const
+{
+ QTC_ASSERT(m_tabWidget, return nullptr);
+
+ for (int i = 0; i < m_tabWidget->count(); ++i) {
+ auto terminal = qobject_cast<TerminalWidget *>(m_tabWidget->widget(i));
+ if (terminal->processState() == QProcess::NotRunning && terminal->identifier() == identifier)
+ return terminal;
+ }
+
+ return nullptr;
+}
+
+QWidget *TerminalPane::outputWidget(QWidget *parent)
+{
+ if (!m_widgetInitialized) {
+ m_widgetInitialized = true;
+ m_tabWidget->setTabBarAutoHide(false);
+ m_tabWidget->setDocumentMode(true);
+ m_tabWidget->setTabsClosable(true);
+ m_tabWidget->setMovable(true);
+
+ connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int index) {
+ removeTab(index);
+ });
+
+ connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int index) {
+ if (auto widget = m_tabWidget->widget(index))
+ widget->setFocus();
+ else
+ emit hidePage();
+ });
+
+ auto terminalWidget = new TerminalWidget(parent);
+ m_tabWidget->addTab(terminalWidget, Tr::tr("Terminal"));
+ setupTerminalWidget(terminalWidget);
+ }
+
+ return m_tabWidget;
+}
+
+TerminalWidget *TerminalPane::currentTerminal() const
+{
+ QWidget *activeWidget = m_tabWidget->currentWidget();
+ return static_cast<TerminalWidget *>(activeWidget);
+}
+
+void TerminalPane::removeTab(int index)
+{
+ delete m_tabWidget->widget(index);
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::setupTerminalWidget(TerminalWidget *terminal)
+{
+ if (!terminal)
+ return;
+
+ auto setTabText = [this](TerminalWidget *terminal) {
+ auto index = m_tabWidget->indexOf(terminal);
+ const FilePath cwd = terminal->cwd();
+
+ const QString exe = terminal->currentCommand().isEmpty()
+ ? terminal->shellName()
+ : terminal->currentCommand().executable().fileName();
+
+ if (cwd.isEmpty())
+ m_tabWidget->setTabText(index, exe);
+ else
+ m_tabWidget->setTabText(index, exe + " - " + cwd.fileName());
+ };
+
+ connect(terminal, &TerminalWidget::started, this, [setTabText, terminal](qint64 /*pid*/) {
+ setTabText(terminal);
+ });
+
+ connect(terminal, &TerminalWidget::cwdChanged, this, [setTabText, terminal]() {
+ setTabText(terminal);
+ });
+
+ connect(terminal, &TerminalWidget::commandChanged, this, [setTabText, terminal]() {
+ setTabText(terminal);
+ });
+
+ if (!terminal->shellName().isEmpty())
+ setTabText(terminal);
+}
+
+QList<QWidget *> TerminalPane::toolBarWidgets() const
+{
+ QList<QWidget *> widgets = IOutputPane::toolBarWidgets();
+ widgets.prepend(m_newTerminalButton);
+ widgets.prepend(m_closeTerminalButton);
+
+ return widgets << m_openSettingsButton << m_escSettingButton;
+}
+
+QString TerminalPane::displayName() const
+{
+ return Tr::tr("Terminal");
+}
+
+int TerminalPane::priorityInStatusBar() const
+{
+ return 50;
+}
+
+void TerminalPane::clearContents()
+{
+ if (const auto t = currentTerminal())
+ t->clearContents();
+}
+
+void TerminalPane::visibilityChanged(bool visible)
+{
+ if (m_isVisible == visible)
+ return;
+
+ m_isVisible = visible;
+
+ if (visible && m_tabWidget && m_tabWidget->count() == 0)
+ openTerminal({});
+
+ IOutputPane::visibilityChanged(visible);
+}
+
+void TerminalPane::setFocus()
+{
+ if (const auto t = currentTerminal())
+ t->setFocus();
+}
+
+bool TerminalPane::hasFocus() const
+{
+ if (const auto t = currentTerminal())
+ return t->hasFocus();
+
+ return false;
+}
+
+bool TerminalPane::canFocus() const
+{
+ return true;
+}
+
+bool TerminalPane::canNavigate() const
+{
+ return true;
+}
+
+bool TerminalPane::canNext() const
+{
+ return m_tabWidget->count() > 1;
+}
+
+bool TerminalPane::canPrevious() const
+{
+ return m_tabWidget->count() > 1;
+}
+
+void TerminalPane::goToNext()
+{
+ int nextIndex = m_tabWidget->currentIndex() + 1;
+ if (nextIndex >= m_tabWidget->count())
+ nextIndex = 0;
+
+ m_tabWidget->setCurrentIndex(nextIndex);
+ emit navigateStateUpdate();
+}
+
+void TerminalPane::goToPrev()
+{
+ int prevIndex = m_tabWidget->currentIndex() - 1;
+ if (prevIndex < 0)
+ prevIndex = m_tabWidget->count() - 1;
+
+ m_tabWidget->setCurrentIndex(prevIndex);
+ emit navigateStateUpdate();
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalpane.h b/src/plugins/terminal/terminalpane.h
new file mode 100644
index 0000000000..28c59b523d
--- /dev/null
+++ b/src/plugins/terminal/terminalpane.h
@@ -0,0 +1,65 @@
+// 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
+
+#pragma once
+
+#include <coreplugin/ioutputpane.h>
+
+#include <utils/terminalhooks.h>
+
+#include <QAction>
+#include <QTabWidget>
+#include <QToolButton>
+
+namespace Terminal {
+
+class TerminalWidget;
+
+class TerminalPane : public Core::IOutputPane
+{
+ Q_OBJECT
+public:
+ TerminalPane(QObject *parent = nullptr);
+ ~TerminalPane() override;
+
+ QWidget *outputWidget(QWidget *parent) override;
+ QList<QWidget *> toolBarWidgets() const override;
+ QString displayName() const override;
+ int priorityInStatusBar() const override;
+ void clearContents() override;
+ void visibilityChanged(bool visible) override;
+ void setFocus() override;
+ bool hasFocus() const override;
+ bool canFocus() const override;
+ bool canNavigate() const override;
+ bool canNext() const override;
+ bool canPrevious() const override;
+ void goToNext() override;
+ void goToPrev() override;
+
+ void openTerminal(const Utils::Terminal::OpenTerminalParameters &parameters);
+ void addTerminal(TerminalWidget *terminal, const QString &title);
+
+ TerminalWidget *stoppedTerminalWithId(const Utils::Id &identifier) const;
+
+ void ensureVisible(TerminalWidget *terminal);
+
+private:
+ TerminalWidget *currentTerminal() const;
+
+ void removeTab(int index);
+ void setupTerminalWidget(TerminalWidget *terminal);
+
+private:
+ QTabWidget *m_tabWidget{nullptr};
+
+ QToolButton *m_newTerminalButton{nullptr};
+ QToolButton *m_closeTerminalButton{nullptr};
+ QToolButton *m_openSettingsButton{nullptr};
+ QToolButton *m_escSettingButton{nullptr};
+
+ bool m_widgetInitialized{false};
+ bool m_isVisible{false};
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalplugin.cpp b/src/plugins/terminal/terminalplugin.cpp
new file mode 100644
index 0000000000..831981e823
--- /dev/null
+++ b/src/plugins/terminal/terminalplugin.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "terminalplugin.h"
+
+#include "terminalpane.h"
+#include "terminalprocessimpl.h"
+#include "terminalsettings.h"
+#include "terminalsettingspage.h"
+#include "terminalcommands.h"
+
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/modemanager.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <QAction>
+#include <QDebug>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPushButton>
+
+namespace Terminal {
+namespace Internal {
+
+TerminalPlugin::TerminalPlugin() {}
+
+TerminalPlugin::~TerminalPlugin()
+{
+ ExtensionSystem::PluginManager::instance()->removeObject(m_terminalPane);
+ delete m_terminalPane;
+ m_terminalPane = nullptr;
+}
+
+bool TerminalPlugin::delayedInitialize()
+{
+ TerminalCommands::instance().lazyInitCommands();
+ return true;
+}
+
+void TerminalPlugin::extensionsInitialized()
+{
+ (void) TerminalSettingsPage::instance();
+ TerminalSettings::instance().readSettings(Core::ICore::settings());
+
+ m_terminalPane = new TerminalPane();
+ ExtensionSystem::PluginManager::instance()->addObject(m_terminalPane);
+
+ auto enable = [this] {
+ Utils::Terminal::Hooks::instance()
+ .addCallbackSet("Internal",
+ {[this](const Utils::Terminal::OpenTerminalParameters &p) {
+ m_terminalPane->openTerminal(p);
+ },
+ [this] { return new TerminalProcessImpl(m_terminalPane); }});
+ };
+
+ auto disable = [] { Utils::Terminal::Hooks::instance().removeCallbackSet("Internal"); };
+
+ static bool isEnabled = false;
+ auto settingsChanged = [enable, disable] {
+ if (isEnabled != TerminalSettings::instance().enableTerminal.value()) {
+ isEnabled = TerminalSettings::instance().enableTerminal.value();
+ if (isEnabled)
+ enable();
+ else
+ disable();
+ }
+ };
+
+ QObject::connect(&TerminalSettings::instance(),
+ &Utils::AspectContainer::applied,
+ this,
+ settingsChanged);
+
+ settingsChanged();
+}
+
+} // namespace Internal
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalplugin.h b/src/plugins/terminal/terminalplugin.h
new file mode 100644
index 0000000000..2bfbd9f22e
--- /dev/null
+++ b/src/plugins/terminal/terminalplugin.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <extensionsystem/iplugin.h>
+
+namespace Terminal {
+
+class TerminalPane;
+namespace Internal {
+
+class TerminalPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Terminal.json")
+
+public:
+ TerminalPlugin();
+ ~TerminalPlugin() override;
+
+ void extensionsInitialized() override;
+ bool delayedInitialize() override;
+
+private:
+ TerminalPane *m_terminalPane{nullptr};
+};
+
+} // namespace Internal
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalprocessimpl.cpp b/src/plugins/terminal/terminalprocessimpl.cpp
new file mode 100644
index 0000000000..d2b76e51a6
--- /dev/null
+++ b/src/plugins/terminal/terminalprocessimpl.cpp
@@ -0,0 +1,72 @@
+// 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 "terminalprocessimpl.h"
+#include "terminalwidget.h"
+
+#include <QCoreApplication>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QLoggingCategory>
+#include <QTemporaryFile>
+#include <QTimer>
+
+Q_LOGGING_CATEGORY(terminalProcessLog, "qtc.terminal.stubprocess", QtDebugMsg)
+
+using namespace Utils;
+using namespace Utils::Terminal;
+
+namespace Terminal {
+
+class ProcessStubCreator : public StubCreator
+{
+public:
+ ProcessStubCreator(TerminalProcessImpl *interface, TerminalPane *terminalPane)
+ : m_terminalPane(terminalPane)
+ , m_process(interface)
+ {}
+
+ expected_str<qint64> startStubProcess(const ProcessSetupData &setup) override
+ {
+ const Id id = Id::fromString(setup.m_commandLine.executable().toUserOutput());
+
+ TerminalWidget *terminal = m_terminalPane->stoppedTerminalWithId(id);
+
+ const OpenTerminalParameters openParameters{setup.m_commandLine,
+ std::nullopt,
+ std::nullopt,
+ ExitBehavior::Keep,
+ id};
+
+ if (!terminal) {
+ terminal = new TerminalWidget(nullptr, openParameters);
+
+ terminal->setShellName(setup.m_commandLine.executable().fileName());
+ m_terminalPane->addTerminal(terminal, "App");
+ } else {
+ terminal->restart(openParameters);
+ }
+
+ m_terminalPane->ensureVisible(terminal);
+
+ connect(terminal, &TerminalWidget::destroyed, m_process, [process = m_process] {
+ if (process->inferiorProcessId())
+ process->emitFinished(-1, QProcess::CrashExit);
+ });
+
+ return 0;
+ }
+
+ TerminalPane *m_terminalPane;
+ TerminalProcessImpl *m_process;
+};
+
+TerminalProcessImpl::TerminalProcessImpl(TerminalPane *terminalPane)
+ : TerminalInterface(false)
+{
+ auto creator = new ProcessStubCreator(this, terminalPane);
+ creator->moveToThread(qApp->thread());
+ setStubCreator(creator);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalprocessimpl.h b/src/plugins/terminal/terminalprocessimpl.h
new file mode 100644
index 0000000000..f77f418281
--- /dev/null
+++ b/src/plugins/terminal/terminalprocessimpl.h
@@ -0,0 +1,18 @@
+// 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
+
+#pragma once
+
+#include "terminalpane.h"
+
+#include <utils/terminalinterface.h>
+
+namespace Terminal {
+
+class TerminalProcessImpl : public Utils::TerminalInterface
+{
+public:
+ TerminalProcessImpl(TerminalPane *terminalPane);
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsearch.cpp b/src/plugins/terminal/terminalsearch.cpp
new file mode 100644
index 0000000000..b69587298b
--- /dev/null
+++ b/src/plugins/terminal/terminalsearch.cpp
@@ -0,0 +1,283 @@
+// 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 "terminalsearch.h"
+#include "terminalcommands.h"
+
+#include <QElapsedTimer>
+#include <QLoggingCategory>
+#include <QRegularExpression>
+
+#include <chrono>
+
+Q_LOGGING_CATEGORY(terminalSearchLog, "qtc.terminal.search", QtWarningMsg)
+
+using namespace std::chrono_literals;
+
+namespace Terminal {
+
+using namespace Terminal::Internal;
+
+constexpr std::chrono::milliseconds debounceInterval = 100ms;
+
+TerminalSearch::TerminalSearch(TerminalSurface *surface)
+ : m_surface(surface)
+{
+ m_debounceTimer.setInterval(debounceInterval);
+ m_debounceTimer.setSingleShot(true);
+
+ connect(surface, &TerminalSurface::invalidated, this, &TerminalSearch::updateHits);
+ connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits);
+
+ connect(&TerminalCommands::widgetActions().findNext,
+ &QAction::triggered,
+ this,
+ &TerminalSearch::nextHit);
+ connect(&TerminalCommands::widgetActions().findPrevious,
+ &QAction::triggered,
+ this,
+ &TerminalSearch::previousHit);
+}
+
+void TerminalSearch::setCurrentSelection(std::optional<SearchHitWithText> selection)
+{
+ m_currentSelection = selection;
+}
+
+void TerminalSearch::setSearchString(const QString &searchString, Core::FindFlags findFlags)
+{
+ if (m_currentSearchString != searchString || m_findFlags != findFlags) {
+ m_currentSearchString = searchString;
+ m_findFlags = findFlags;
+ updateHits();
+ }
+}
+
+void TerminalSearch::nextHit()
+{
+ if (m_hits.isEmpty())
+ return;
+
+ m_currentHit = (m_currentHit + 1) % m_hits.size();
+ emit currentHitChanged();
+}
+
+void TerminalSearch::previousHit()
+{
+ if (m_hits.isEmpty())
+ return;
+
+ m_currentHit = (m_currentHit - 1 + m_hits.size()) % m_hits.size();
+ emit currentHitChanged();
+}
+
+void TerminalSearch::updateHits()
+{
+ if (!m_hits.isEmpty()) {
+ m_hits.clear();
+ m_currentHit = -1;
+ emit hitsChanged();
+ emit currentHitChanged();
+ }
+
+ m_debounceTimer.start();
+}
+
+bool isSpace(char32_t a, char32_t b)
+{
+ if (a == std::numeric_limits<char32_t>::max())
+ return std::isspace(b);
+ else if (b == std::numeric_limits<char32_t>::max())
+ return std::isspace(a);
+
+ return false;
+}
+
+QList<SearchHit> TerminalSearch::search()
+{
+ QList<SearchHit> hits;
+
+ std::function<bool(char32_t, char32_t)> compare;
+
+ if (m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively))
+ compare = [](char32_t a, char32_t b) {
+ return std::tolower(a) == std::tolower(b) || isSpace(a, b);
+ };
+ else
+ compare = [](char32_t a, char32_t b) { return a == b || isSpace(a, b); };
+
+ if (!m_currentSearchString.isEmpty()) {
+ const QList<uint> asUcs4 = m_currentSearchString.toUcs4();
+ std::u32string searchString(asUcs4.begin(), asUcs4.end());
+
+ if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) {
+ searchString.push_back(std::numeric_limits<char32_t>::max());
+ searchString.insert(searchString.begin(), std::numeric_limits<char32_t>::max());
+ }
+
+ Internal::CellIterator it = m_surface->begin();
+ while (it != m_surface->end()) {
+ it = std::search(it, m_surface->end(), searchString.begin(), searchString.end(), compare);
+
+ if (it != m_surface->end()) {
+ auto hit = SearchHit{it.position(),
+ static_cast<int>(it.position() + searchString.size())};
+ if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) {
+ hit.start++;
+ hit.end--;
+ }
+ hits << hit;
+ it += m_currentSearchString.size();
+ }
+ }
+ }
+ return hits;
+}
+
+QList<SearchHit> TerminalSearch::searchRegex()
+{
+ QList<SearchHit> hits;
+
+ QString allText;
+ allText.reserve(1000);
+
+ // Contains offsets at which there are characters > 2 bytes
+ QList<int> adjustTable;
+
+ for (auto it = m_surface->begin(); it != m_surface->end(); ++it) {
+ auto chs = QChar::fromUcs4(*it);
+ if (chs.size() > 1)
+ adjustTable << (allText.size());
+ allText += chs;
+ }
+
+ QRegularExpression re(m_currentSearchString,
+ m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively)
+ ? QRegularExpression::NoPatternOption
+ : QRegularExpression::CaseInsensitiveOption);
+
+ QRegularExpressionMatchIterator it = re.globalMatch(allText);
+ int adjust = 0;
+ auto itAdjust = adjustTable.begin();
+ while (it.hasNext()) {
+ QRegularExpressionMatch match = it.next();
+ int s = match.capturedStart();
+ int e = match.capturedEnd();
+
+ // Update 'adjust' to account for characters > 2 bytes
+ if (itAdjust != adjustTable.end()) {
+ while (s > *itAdjust && itAdjust != adjustTable.end()) {
+ adjust++;
+ itAdjust++;
+ }
+ s -= adjust;
+ while (e > *itAdjust && itAdjust != adjustTable.end()) {
+ adjust++;
+ itAdjust++;
+ }
+ e -= adjust;
+ }
+ hits << SearchHit{s, e};
+ }
+
+ return hits;
+}
+
+void TerminalSearch::debouncedUpdateHits()
+{
+ QElapsedTimer t;
+ t.start();
+
+ m_currentHit = -1;
+
+ const bool regex = m_findFlags.testFlag(Core::FindFlag::FindRegularExpression);
+
+ QList<SearchHit> hits = regex ? searchRegex() : search();
+
+ if (hits != m_hits) {
+ m_currentHit = -1;
+ if (m_currentSelection)
+ m_currentHit = hits.indexOf(*m_currentSelection);
+
+ if (m_currentHit == -1 && !hits.isEmpty())
+ m_currentHit = 0;
+
+ m_hits = hits;
+ emit hitsChanged();
+ emit currentHitChanged();
+ emit changed();
+ }
+ if (!m_currentSearchString.isEmpty())
+ qCDebug(terminalSearchLog) << "Search took" << t.elapsed() << "ms";
+}
+
+Core::FindFlags TerminalSearch::supportedFindFlags() const
+{
+ return Core::FindFlag::FindCaseSensitively | Core::FindFlag::FindBackward
+ | Core::FindFlag::FindRegularExpression | Core::FindFlag::FindWholeWords;
+}
+
+void TerminalSearch::resetIncrementalSearch()
+{
+ m_currentSelection.reset();
+}
+
+void TerminalSearch::clearHighlights()
+{
+ setSearchString("", {});
+}
+
+QString TerminalSearch::currentFindString() const
+{
+ if (m_currentSelection)
+ return m_currentSelection->text;
+ else
+ return m_currentSearchString;
+}
+
+QString TerminalSearch::completedFindString() const
+{
+ return {};
+}
+
+Core::IFindSupport::Result TerminalSearch::findIncremental(const QString &txt,
+ Core::FindFlags findFlags)
+{
+ if (txt == m_currentSearchString) {
+ if (m_debounceTimer.isActive())
+ return Result::NotYetFound;
+ else if (m_hits.isEmpty())
+ return Result::NotFound;
+ else
+ return Result::Found;
+ }
+
+ setSearchString(txt, findFlags);
+ return Result::NotYetFound;
+}
+
+Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, Core::FindFlags findFlags)
+{
+ if (txt == m_currentSearchString) {
+ if (m_debounceTimer.isActive())
+ return Result::NotYetFound;
+ else if (m_hits.isEmpty())
+ return Result::NotFound;
+
+ if (findFlags.testFlag(Core::FindFlag::FindBackward))
+ previousHit();
+ else
+ nextHit();
+
+ return Result::Found;
+ }
+
+ return findIncremental(txt, findFlags);
+}
+
+void TerminalSearch::highlightAll(const QString &txt, Core::FindFlags findFlags)
+{
+ setSearchString(txt, findFlags);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsearch.h b/src/plugins/terminal/terminalsearch.h
new file mode 100644
index 0000000000..3daa05c04d
--- /dev/null
+++ b/src/plugins/terminal/terminalsearch.h
@@ -0,0 +1,82 @@
+// 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 "terminalsurface.h"
+
+#include <coreplugin/find/ifindsupport.h>
+#include <coreplugin/find/textfindconstants.h>
+
+#include <QTimer>
+
+namespace Terminal {
+
+struct SearchHit
+{
+ int start{-1};
+ int end{-1};
+
+ bool operator!=(const SearchHit &other) const
+ {
+ return start != other.start || end != other.end;
+ }
+ bool operator==(const SearchHit &other) const { return !operator!=(other); }
+};
+
+struct SearchHitWithText : SearchHit
+{
+ QString text;
+};
+
+class TerminalSearch : public Core::IFindSupport
+{
+ Q_OBJECT
+public:
+ TerminalSearch(Internal::TerminalSurface *surface);
+
+ void setCurrentSelection(std::optional<SearchHitWithText> selection);
+ void setSearchString(const QString &searchString, Core::FindFlags findFlags);
+ void nextHit();
+ void previousHit();
+
+ const QList<SearchHit> &hits() const { return m_hits; }
+ SearchHit currentHit() const
+ {
+ return m_currentHit >= 0 ? m_hits.at(m_currentHit) : SearchHit{};
+ }
+
+public:
+ bool supportsReplace() const override { return false; }
+ Core::FindFlags supportedFindFlags() const override;
+ void resetIncrementalSearch() override;
+ void clearHighlights() override;
+ QString currentFindString() const override;
+ QString completedFindString() const override;
+ Result findIncremental(const QString &txt, Core::FindFlags findFlags) override;
+ Result findStep(const QString &txt, Core::FindFlags findFlags) override;
+
+ void highlightAll(const QString &, Core::FindFlags) override;
+
+signals:
+ void hitsChanged();
+ void currentHitChanged();
+
+protected:
+ void updateHits();
+ void debouncedUpdateHits();
+ QList<SearchHit> search();
+ QList<SearchHit> searchRegex();
+
+private:
+ std::optional<SearchHitWithText> m_currentSelection;
+ QString m_currentSearchString;
+ Core::FindFlags m_findFlags;
+ Internal::TerminalSurface *m_surface;
+
+ int m_currentHit{-1};
+ QList<SearchHit> m_hits;
+ QTimer m_debounceTimer;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp
new file mode 100644
index 0000000000..e2d2150cd7
--- /dev/null
+++ b/src/plugins/terminal/terminalsettings.cpp
@@ -0,0 +1,177 @@
+// 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 "terminalsettings.h"
+
+#include "terminaltr.h"
+
+#include <utils/environment.h>
+#include <utils/hostosinfo.h>
+#include <utils/theme/theme.h>
+
+using namespace Utils;
+
+namespace Terminal {
+
+static QString defaultFontFamily()
+{
+ if (HostOsInfo::isMacHost())
+ return QLatin1String("Menlo");
+
+ if (Utils::HostOsInfo::isAnyUnixHost())
+ return QLatin1String("Monospace");
+
+ return QLatin1String("Consolas");
+}
+
+static int defaultFontSize()
+{
+ if (Utils::HostOsInfo::isMacHost())
+ return 12;
+ if (Utils::HostOsInfo::isAnyUnixHost())
+ return 9;
+ return 10;
+}
+
+static QString defaultShell()
+{
+ if (HostOsInfo::isWindowsHost())
+ return qtcEnvironmentVariable("COMSPEC");
+
+ QString defaultShell = qtcEnvironmentVariable("SHELL");
+ if (FilePath::fromUserInput(defaultShell).isExecutableFile())
+ return defaultShell;
+
+ Utils::FilePath shPath = Utils::Environment::systemEnvironment().searchInPath("sh");
+ return shPath.nativePath();
+}
+
+TerminalSettings &TerminalSettings::instance()
+{
+ static TerminalSettings settings;
+ return settings;
+}
+
+void setupColor(TerminalSettings *settings,
+ ColorAspect &color,
+ const QString &label,
+ const QColor &defaultColor)
+{
+ color.setSettingsKey(label);
+ color.setDefaultValue(defaultColor);
+ color.setToolTip(Tr::tr("The color used for %1.").arg(label));
+
+ settings->registerAspect(&color);
+}
+
+TerminalSettings::TerminalSettings()
+{
+ setAutoApply(false);
+ setSettingsGroup("Terminal");
+
+ enableTerminal.setSettingsKey("EnableTerminal");
+ enableTerminal.setLabelText(Tr::tr("Use internal terminal"));
+ enableTerminal.setToolTip(
+ Tr::tr("If enabled, use the internal terminal when \"Run In Terminal\" is "
+ "enabled and for \"Open Terminal here\"."));
+ enableTerminal.setDefaultValue(true);
+
+ font.setSettingsKey("FontFamily");
+ font.setLabelText(Tr::tr("Family:"));
+ font.setHistoryCompleter("Terminal.Fonts.History");
+ font.setToolTip(Tr::tr("The font family used in the terminal."));
+ font.setDefaultValue(defaultFontFamily());
+
+ fontSize.setSettingsKey("FontSize");
+ fontSize.setLabelText(Tr::tr("Size:"));
+ fontSize.setToolTip(Tr::tr("The font size used in the terminal. (in points)"));
+ fontSize.setDefaultValue(defaultFontSize());
+ fontSize.setRange(1, 100);
+
+ allowBlinkingCursor.setSettingsKey("AllowBlinkingCursor");
+ allowBlinkingCursor.setLabelText(Tr::tr("Allow blinking cursor"));
+ allowBlinkingCursor.setToolTip(Tr::tr("Allow the cursor to blink."));
+ allowBlinkingCursor.setDefaultValue(false);
+
+ shell.setSettingsKey("ShellPath");
+ shell.setLabelText(Tr::tr("Shell path:"));
+ shell.setExpectedKind(PathChooser::ExistingCommand);
+ shell.setDisplayStyle(StringAspect::PathChooserDisplay);
+ shell.setHistoryCompleter("Terminal.Shell.History");
+ shell.setToolTip(Tr::tr("The shell executable to be started."));
+ shell.setDefaultValue(defaultShell());
+
+ shellArguments.setSettingsKey("ShellArguments");
+ shellArguments.setLabelText(Tr::tr("Shell arguments:"));
+ shellArguments.setDisplayStyle(StringAspect::LineEditDisplay);
+ shellArguments.setHistoryCompleter("Terminal.Shell.History");
+ shellArguments.setToolTip(Tr::tr("The arguments to be passed to the shell."));
+ if (!HostOsInfo::isWindowsHost())
+ shellArguments.setDefaultValue(QString("-l"));
+
+ sendEscapeToTerminal.setSettingsKey("SendEscapeToTerminal");
+ sendEscapeToTerminal.setLabelText(Tr::tr("Send escape key to terminal"));
+ sendEscapeToTerminal.setToolTip(
+ Tr::tr("If enabled, pressing the escape key will send it to the terminal "
+ "instead of closing the terminal."));
+ sendEscapeToTerminal.setDefaultValue(false);
+
+ audibleBell.setSettingsKey("AudibleBell");
+ audibleBell.setLabelText(Tr::tr("Audible bell"));
+ audibleBell.setToolTip(Tr::tr("If enabled, the terminal will beep when a bell "
+ "character is received."));
+ audibleBell.setDefaultValue(true);
+
+ registerAspect(&font);
+ registerAspect(&fontSize);
+ registerAspect(&shell);
+ registerAspect(&allowBlinkingCursor);
+ registerAspect(&enableTerminal);
+ registerAspect(&sendEscapeToTerminal);
+ registerAspect(&audibleBell);
+ registerAspect(&shellArguments);
+
+ setupColor(this,
+ foregroundColor,
+ "Foreground",
+ Utils::creatorTheme()->color(Theme::TerminalForeground));
+ setupColor(this,
+ backgroundColor,
+ "Background",
+ Utils::creatorTheme()->color(Theme::TerminalBackground));
+ setupColor(this,
+ selectionColor,
+ "Selection",
+ Utils::creatorTheme()->color(Theme::TerminalSelection));
+
+ setupColor(this,
+ findMatchColor,
+ "Find matches",
+ Utils::creatorTheme()->color(Theme::TerminalFindMatch));
+
+ setupColor(this, colors[0], "0", Utils::creatorTheme()->color(Theme::TerminalAnsi0));
+ setupColor(this, colors[8], "8", Utils::creatorTheme()->color(Theme::TerminalAnsi8));
+
+ setupColor(this, colors[1], "1", Utils::creatorTheme()->color(Theme::TerminalAnsi1));
+ setupColor(this, colors[9], "9", Utils::creatorTheme()->color(Theme::TerminalAnsi9));
+
+ setupColor(this, colors[2], "2", Utils::creatorTheme()->color(Theme::TerminalAnsi2));
+ setupColor(this, colors[10], "10", Utils::creatorTheme()->color(Theme::TerminalAnsi10));
+
+ setupColor(this, colors[3], "3", Utils::creatorTheme()->color(Theme::TerminalAnsi3));
+ setupColor(this, colors[11], "11", Utils::creatorTheme()->color(Theme::TerminalAnsi11));
+
+ setupColor(this, colors[4], "4", Utils::creatorTheme()->color(Theme::TerminalAnsi4));
+ setupColor(this, colors[12], "12", Utils::creatorTheme()->color(Theme::TerminalAnsi12));
+
+ setupColor(this, colors[5], "5", Utils::creatorTheme()->color(Theme::TerminalAnsi5));
+ setupColor(this, colors[13], "13", Utils::creatorTheme()->color(Theme::TerminalAnsi13));
+
+ setupColor(this, colors[6], "6", Utils::creatorTheme()->color(Theme::TerminalAnsi6));
+ setupColor(this, colors[14], "14", Utils::creatorTheme()->color(Theme::TerminalAnsi14));
+
+ setupColor(this, colors[7], "7", Utils::creatorTheme()->color(Theme::TerminalAnsi7));
+ setupColor(this, colors[15], "15", Utils::creatorTheme()->color(Theme::TerminalAnsi15));
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h
new file mode 100644
index 0000000000..137f3abe3a
--- /dev/null
+++ b/src/plugins/terminal/terminalsettings.h
@@ -0,0 +1,36 @@
+// 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
+
+#pragma once
+
+#include <utils/aspects.h>
+
+namespace Terminal {
+class TerminalSettings : public Utils::AspectContainer
+{
+public:
+ TerminalSettings();
+
+ static TerminalSettings &instance();
+
+ Utils::BoolAspect enableTerminal;
+
+ Utils::StringAspect font;
+ Utils::IntegerAspect fontSize;
+ Utils::StringAspect shell;
+ Utils::StringAspect shellArguments;
+
+ Utils::ColorAspect foregroundColor;
+ Utils::ColorAspect backgroundColor;
+ Utils::ColorAspect selectionColor;
+ Utils::ColorAspect findMatchColor;
+
+ Utils::ColorAspect colors[16];
+
+ Utils::BoolAspect allowBlinkingCursor;
+
+ Utils::BoolAspect sendEscapeToTerminal;
+ Utils::BoolAspect audibleBell;
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsettingspage.cpp b/src/plugins/terminal/terminalsettingspage.cpp
new file mode 100644
index 0000000000..25a7250a1e
--- /dev/null
+++ b/src/plugins/terminal/terminalsettingspage.cpp
@@ -0,0 +1,464 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "terminalsettingspage.h"
+
+#include "terminalsettings.h"
+#include "terminaltr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/aspects.h>
+#include <utils/dropsupport.h>
+#include <utils/expected.h>
+#include <utils/fileutils.h>
+#include <utils/layoutbuilder.h>
+#include <utils/pathchooser.h>
+
+#include <QFontComboBox>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QRegularExpression>
+#include <QTemporaryFile>
+#include <QXmlStreamReader>
+
+using namespace Utils;
+
+namespace Terminal {
+
+static expected_str<void> loadXdefaults(const FilePath &path)
+{
+ const expected_str<QByteArray> readResult = path.fileContents();
+ if (!readResult)
+ return make_unexpected(readResult.error());
+
+ QRegularExpression re(R"(.*\*(color[0-9]{1,2}|foreground|background):\s*(#[0-9a-f]{6}))");
+
+ for (const QByteArray &line : readResult->split('\n')) {
+ if (line.trimmed().startsWith('!'))
+ continue;
+
+ const auto match = re.match(QString::fromUtf8(line));
+ if (match.hasMatch()) {
+ const QString colorName = match.captured(1);
+ const QColor color(match.captured(2));
+ if (colorName == "foreground") {
+ TerminalSettings::instance().foregroundColor.setVolatileValue(color);
+ } else if (colorName == "background") {
+ TerminalSettings::instance().backgroundColor.setVolatileValue(color);
+ } else {
+ const int colorIndex = colorName.mid(5).toInt();
+ if (colorIndex >= 0 && colorIndex < 16)
+ TerminalSettings::instance().colors[colorIndex].setVolatileValue(color);
+ }
+ }
+ }
+
+ return {};
+}
+
+static expected_str<void> loadItermColors(const FilePath &path)
+{
+ QFile f(path.toFSPathString());
+ const bool opened = f.open(QIODevice::ReadOnly);
+ if (!opened)
+ return make_unexpected(Tr::tr("Failed to open file"));
+
+ QXmlStreamReader reader(&f);
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"plist") {
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"dict") {
+ QString colorName;
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"key") {
+ colorName = reader.readElementText();
+ } else if (reader.name() == u"dict") {
+ QColor color;
+ int component = 0;
+ while (!reader.atEnd() && reader.readNextStartElement()) {
+ if (reader.name() == u"key") {
+ const auto &text = reader.readElementText();
+ if (text == u"Red Component")
+ component = 0;
+ else if (text == u"Green Component")
+ component = 1;
+ else if (text == u"Blue Component")
+ component = 2;
+ else if (text == u"Alpha Component")
+ component = 3;
+ } else if (reader.name() == u"real") {
+ // clang-format off
+ switch (component) {
+ case 0: color.setRedF(reader.readElementText().toDouble()); break;
+ case 1: color.setGreenF(reader.readElementText().toDouble()); break;
+ case 2: color.setBlueF(reader.readElementText().toDouble()); break;
+ case 3: color.setAlphaF(reader.readElementText().toDouble()); break;
+ }
+ // clang-format on
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+
+ if (colorName.startsWith("Ansi")) {
+ const auto c = colorName.mid(5, 2);
+ const int colorIndex = c.toInt();
+ if (colorIndex >= 0 && colorIndex < 16)
+ TerminalSettings::instance().colors[colorIndex].setVolatileValue(
+ color);
+ } else if (colorName == "Foreground Color") {
+ TerminalSettings::instance().foregroundColor.setVolatileValue(color);
+ } else if (colorName == "Background Color") {
+ TerminalSettings::instance().backgroundColor.setVolatileValue(color);
+ } else if (colorName == "Selection Color") {
+ TerminalSettings::instance().selectionColor.setVolatileValue(color);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (reader.hasError())
+ return make_unexpected(reader.errorString());
+
+ return {};
+}
+
+static expected_str<void> loadVsCodeColors(const FilePath &path)
+{
+ const expected_str<QByteArray> readResult = path.fileContents();
+ if (!readResult)
+ return make_unexpected(readResult.error());
+
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(*readResult, &error);
+ if (error.error != QJsonParseError::NoError)
+ return make_unexpected(Tr::tr("JSON parsing error: \"%1\", at offset: %2")
+ .arg(error.errorString())
+ .arg(error.offset));
+
+ const QJsonObject root = doc.object();
+ const auto itColors = root.find("colors");
+ if (itColors == root.end())
+ return make_unexpected(Tr::tr("No colors found"));
+
+ const QJsonObject colors = itColors->toObject();
+
+ // clang-format off
+ const QList<QPair<QStringView, ColorAspect *>> colorKeys = {
+ qMakePair(u"editor.background", &TerminalSettings::instance().backgroundColor),
+ qMakePair(u"terminal.foreground", &TerminalSettings::instance().foregroundColor),
+ qMakePair(u"terminal.selectionBackground", &TerminalSettings::instance().selectionColor),
+
+ qMakePair(u"terminal.ansiBlack", &TerminalSettings::instance().colors[0]),
+ qMakePair(u"terminal.ansiBrightBlack", &TerminalSettings::instance().colors[8]),
+
+ qMakePair(u"terminal.ansiRed", &TerminalSettings::instance().colors[1]),
+ qMakePair(u"terminal.ansiBrightRed", &TerminalSettings::instance().colors[9]),
+
+ qMakePair(u"terminal.ansiGreen", &TerminalSettings::instance().colors[2]),
+ qMakePair(u"terminal.ansiBrightGreen", &TerminalSettings::instance().colors[10]),
+
+ qMakePair(u"terminal.ansiYellow", &TerminalSettings::instance().colors[3]),
+ qMakePair(u"terminal.ansiBrightYellow", &TerminalSettings::instance().colors[11]),
+
+ qMakePair(u"terminal.ansiBlue", &TerminalSettings::instance().colors[4]),
+ qMakePair(u"terminal.ansiBrightBlue", &TerminalSettings::instance().colors[12]),
+
+ qMakePair(u"terminal.ansiMagenta", &TerminalSettings::instance().colors[5]),
+ qMakePair(u"terminal.ansiBrightMagenta", &TerminalSettings::instance().colors[13]),
+
+ qMakePair(u"terminal.ansiCyan", &TerminalSettings::instance().colors[6]),
+ qMakePair(u"terminal.ansiBrightCyan", &TerminalSettings::instance().colors[14]),
+
+ qMakePair(u"terminal.ansiWhite", &TerminalSettings::instance().colors[7]),
+ qMakePair(u"terminal.ansiBrightWhite", &TerminalSettings::instance().colors[15])
+ };
+ // clang-format on
+
+ for (const auto &pair : colorKeys) {
+ const auto it = colors.find(pair.first);
+ if (it != colors.end()) {
+ const QString colorString = it->toString();
+ if (colorString.startsWith("#")) {
+ QColor color(colorString.mid(0, 7));
+ if (colorString.size() > 7) {
+ int alpha = colorString.mid(7).toInt(nullptr, 16);
+ color.setAlpha(alpha);
+ }
+ if (color.isValid())
+ pair.second->setVolatileValue(color);
+ }
+ }
+ }
+
+ return {};
+}
+
+static expected_str<void> loadKonsoleColorScheme(const FilePath &path)
+{
+ QSettings settings(path.toFSPathString(), QSettings::IniFormat);
+
+ auto parseColor = [](const QStringList &parts) -> expected_str<QColor> {
+ if (parts.size() != 3 && parts.size() != 4)
+ return make_unexpected(Tr::tr("Invalid color format"));
+ int alpha = parts.size() == 4 ? parts[3].toInt() : 255;
+ return QColor(parts[0].toInt(), parts[1].toInt(), parts[2].toInt(), alpha);
+ };
+
+ // clang-format off
+ const QList<QPair<QString, ColorAspect *>> colorKeys = {
+ qMakePair(QLatin1String("Background/Color"), &TerminalSettings::instance().backgroundColor),
+ qMakePair(QLatin1String("Foreground/Color"), &TerminalSettings::instance().foregroundColor),
+
+ qMakePair(QLatin1String("Color0/Color"), &TerminalSettings::instance().colors[0]),
+ qMakePair(QLatin1String("Color0Intense/Color"), &TerminalSettings::instance().colors[8]),
+
+ qMakePair(QLatin1String("Color1/Color"), &TerminalSettings::instance().colors[1]),
+ qMakePair(QLatin1String("Color1Intense/Color"), &TerminalSettings::instance().colors[9]),
+
+ qMakePair(QLatin1String("Color2/Color"), &TerminalSettings::instance().colors[2]),
+ qMakePair(QLatin1String("Color2Intense/Color"), &TerminalSettings::instance().colors[10]),
+
+ qMakePair(QLatin1String("Color3/Color"), &TerminalSettings::instance().colors[3]),
+ qMakePair(QLatin1String("Color3Intense/Color"), &TerminalSettings::instance().colors[11]),
+
+ qMakePair(QLatin1String("Color4/Color"), &TerminalSettings::instance().colors[4]),
+ qMakePair(QLatin1String("Color4Intense/Color"), &TerminalSettings::instance().colors[12]),
+
+ qMakePair(QLatin1String("Color5/Color"), &TerminalSettings::instance().colors[5]),
+ qMakePair(QLatin1String("Color5Intense/Color"), &TerminalSettings::instance().colors[13]),
+
+ qMakePair(QLatin1String("Color6/Color"), &TerminalSettings::instance().colors[6]),
+ qMakePair(QLatin1String("Color6Intense/Color"), &TerminalSettings::instance().colors[14]),
+
+ qMakePair(QLatin1String("Color7/Color"), &TerminalSettings::instance().colors[7]),
+ qMakePair(QLatin1String("Color7Intense/Color"), &TerminalSettings::instance().colors[15])
+ };
+ // clang-format on
+
+ for (const auto &colorKey : colorKeys) {
+ if (settings.contains(colorKey.first)) {
+ const auto color = parseColor(settings.value(colorKey.first).toStringList());
+ if (!color)
+ return make_unexpected(color.error());
+
+ colorKey.second->setVolatileValue(*color);
+ }
+ }
+
+ return {};
+}
+
+static expected_str<void> loadXFCE4ColorScheme(const FilePath &path)
+{
+ expected_str<QByteArray> arr = path.fileContents();
+ if (!arr)
+ return make_unexpected(arr.error());
+
+ arr->replace(';', ',');
+
+ QTemporaryFile f;
+ f.open();
+ f.write(*arr);
+ f.close();
+
+ QSettings settings(f.fileName(), QSettings::IniFormat);
+
+ // clang-format off
+ const QList<QPair<QString, ColorAspect *>> colorKeys = {
+ qMakePair(QLatin1String("Scheme/ColorBackground"), &TerminalSettings::instance().backgroundColor),
+ qMakePair(QLatin1String("Scheme/ColorForeground"), &TerminalSettings::instance().foregroundColor),
+ };
+ // clang-format on
+
+ for (const auto &colorKey : colorKeys) {
+ if (settings.contains(colorKey.first)) {
+ colorKey.second->setVolatileValue(QColor(settings.value(colorKey.first).toString()));
+ }
+ }
+
+ QStringList colors = settings.value(QLatin1String("Scheme/ColorPalette")).toStringList();
+ int i = 0;
+ for (const auto &color : colors) {
+ TerminalSettings::instance().colors[i++].setVolatileValue(QColor(color));
+ }
+
+ return {};
+}
+
+static expected_str<void> loadColorScheme(const FilePath &path)
+{
+ if (path.endsWith("Xdefaults"))
+ return loadXdefaults(path);
+ else if (path.suffix() == "itermcolors")
+ return loadItermColors(path);
+ else if (path.suffix() == "json")
+ return loadVsCodeColors(path);
+ else if (path.suffix() == "colorscheme")
+ return loadKonsoleColorScheme(path);
+ else if (path.suffix() == "theme" || path.completeSuffix() == "theme.txt")
+ return loadXFCE4ColorScheme(path);
+
+ return make_unexpected(Tr::tr("Unknown color scheme format"));
+}
+
+class TerminalSettingsPageWidget : public Core::IOptionsPageWidget
+{
+public:
+ TerminalSettingsPageWidget()
+ {
+ using namespace Layouting;
+
+ QFontComboBox *fontComboBox = new QFontComboBox(this);
+ fontComboBox->setFontFilters(QFontComboBox::MonospacedFonts);
+ fontComboBox->setCurrentFont(TerminalSettings::instance().font.value());
+
+ connect(fontComboBox, &QFontComboBox::currentFontChanged, this, [](const QFont &f) {
+ TerminalSettings::instance().font.setValue(f.family());
+ });
+
+ TerminalSettings &settings = TerminalSettings::instance();
+
+ QPushButton *loadThemeButton = new QPushButton(Tr::tr("Load Theme..."));
+ QPushButton *resetTheme = new QPushButton(Tr::tr("Reset Theme"));
+
+ connect(loadThemeButton, &QPushButton::clicked, this, [this] {
+ const FilePath path = FileUtils::getOpenFilePath(
+ this,
+ "Open Theme",
+ {},
+ "All Scheme formats (*.itermcolors *.json *.colorscheme *.theme *.theme.txt);;"
+ "Xdefaults (.Xdefaults Xdefaults);;"
+ "iTerm Color Schemes(*.itermcolors);;"
+ "VS Code Color Schemes(*.json);;"
+ "Konsole Color Schemes(*.colorscheme);;"
+ "XFCE4 Terminal Color Schemes(*.theme *.theme.txt);;"
+ "All files (*)",
+ nullptr,
+ {},
+ true,
+ false);
+
+ if (path.isEmpty())
+ return;
+
+ const expected_str<void> result = loadColorScheme(path);
+ if (!result)
+ QMessageBox::warning(this, Tr::tr("Error"), result.error());
+ });
+
+ connect(resetTheme, &QPushButton::clicked, this, [] {
+ TerminalSettings &settings = TerminalSettings::instance();
+ settings.foregroundColor.setVolatileValue(settings.foregroundColor.defaultValue());
+ settings.backgroundColor.setVolatileValue(settings.backgroundColor.defaultValue());
+ settings.selectionColor.setVolatileValue(settings.selectionColor.defaultValue());
+
+ for (auto &color : settings.colors)
+ color.setVolatileValue(color.defaultValue());
+ });
+
+ // clang-format off
+ Column {
+ Group {
+ title(Tr::tr("General")),
+ Column {
+ settings.enableTerminal, st,
+ settings.sendEscapeToTerminal, st,
+ settings.audibleBell, st,
+ settings.allowBlinkingCursor, st,
+ },
+ },
+ Group {
+ title(Tr::tr("Font")),
+ Row {
+ settings.font.labelText(), fontComboBox, Space(20),
+ settings.fontSize, st,
+ },
+ },
+ Group {
+ title(Tr::tr("Colors")),
+ Column {
+ Row {
+ Tr::tr("Foreground"), settings.foregroundColor, st,
+ Tr::tr("Background"), settings.backgroundColor, st,
+ Tr::tr("Selection"), settings.selectionColor, st,
+ Tr::tr("Find match"), settings.findMatchColor, st,
+ },
+ Row {
+ settings.colors[0], settings.colors[1],
+ settings.colors[2], settings.colors[3],
+ settings.colors[4], settings.colors[5],
+ settings.colors[6], settings.colors[7]
+ },
+ Row {
+ settings.colors[8], settings.colors[9],
+ settings.colors[10], settings.colors[11],
+ settings.colors[12], settings.colors[13],
+ settings.colors[14], settings.colors[15]
+ },
+ Row {
+ loadThemeButton, resetTheme, st,
+ }
+ },
+ },
+ Group {
+ title(Tr::tr("Default Shell")),
+ Column {
+ settings.shell,
+ settings.shellArguments,
+ },
+ },
+ st,
+ }.attachTo(this);
+ // clang-format on
+
+ DropSupport *dropSupport = new DropSupport(this);
+ connect(dropSupport,
+ &DropSupport::filesDropped,
+ this,
+ [this](const QList<DropSupport::FileSpec> &files) {
+ if (files.size() != 1)
+ return;
+
+ const expected_str<void> result = loadColorScheme(files.at(0).filePath);
+ if (!result)
+ QMessageBox::warning(this, Tr::tr("Error"), result.error());
+ });
+ }
+
+ void apply() final
+ {
+ TerminalSettings &settings = TerminalSettings::instance();
+ if (settings.isDirty()) {
+ settings.apply();
+ settings.writeSettings(Core::ICore::settings());
+ }
+ }
+};
+
+// TerminalSettingsPage
+
+TerminalSettingsPage::TerminalSettingsPage()
+{
+ setId("Terminal.General");
+ setDisplayName("Terminal");
+ setCategory("ZY.Terminal");
+ setDisplayCategory("Terminal");
+ setSettings(&TerminalSettings::instance());
+ setCategoryIconPath(":/terminal/images/settingscategory_terminal.png");
+ setWidgetCreator([] { return new TerminalSettingsPageWidget; });
+}
+
+TerminalSettingsPage &TerminalSettingsPage::instance()
+{
+ static TerminalSettingsPage settingsPage;
+ return settingsPage;
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsettingspage.h b/src/plugins/terminal/terminalsettingspage.h
new file mode 100644
index 0000000000..12d3762844
--- /dev/null
+++ b/src/plugins/terminal/terminalsettingspage.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Terminal {
+
+class TerminalSettingsPage : public Core::IOptionsPage
+{
+public:
+ TerminalSettingsPage();
+
+ static TerminalSettingsPage &instance();
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalsurface.cpp b/src/plugins/terminal/terminalsurface.cpp
new file mode 100644
index 0000000000..ee8e571adb
--- /dev/null
+++ b/src/plugins/terminal/terminalsurface.cpp
@@ -0,0 +1,531 @@
+// 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 "terminalsurface.h"
+
+#include "keys.h"
+#include "scrollback.h"
+
+#include <utils/qtcassert.h>
+
+#include <vterm.h>
+
+#include <QLoggingCategory>
+
+Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg);
+
+namespace Terminal::Internal {
+
+QColor toQColor(const VTermColor &c)
+{
+ return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue));
+};
+
+struct TerminalSurfacePrivate
+{
+ TerminalSurfacePrivate(TerminalSurface *surface,
+ const QSize &initialGridSize,
+ ShellIntegration *shellIntegration)
+ : m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free)
+ , m_vtermScreen(vterm_obtain_screen(m_vterm.get()))
+ , m_scrollback(std::make_unique<Internal::Scrollback>(5000))
+ , m_shellIntegration(shellIntegration)
+ , q(surface)
+ {}
+
+ void init()
+ {
+ vterm_set_utf8(m_vterm.get(), true);
+
+ static auto writeToPty = [](const char *s, size_t len, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ emit p->q->writeToPty(QByteArray(s, static_cast<int>(len)));
+ };
+
+ vterm_output_set_callback(m_vterm.get(), writeToPty, this);
+
+ memset(&m_vtermScreenCallbacks, 0, sizeof(m_vtermScreenCallbacks));
+
+ m_vtermScreenCallbacks.damage = [](VTermRect rect, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ p->invalidate(rect);
+ return 1;
+ };
+ m_vtermScreenCallbacks.sb_pushline = [](int cols, const VTermScreenCell *cells, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->sb_pushline(cols, cells);
+ };
+ m_vtermScreenCallbacks.sb_popline = [](int cols, VTermScreenCell *cells, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->sb_popline(cols, cells);
+ };
+ m_vtermScreenCallbacks.settermprop = [](VTermProp prop, VTermValue *val, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->setTerminalProperties(prop, val);
+ };
+ m_vtermScreenCallbacks.movecursor =
+ [](VTermPos pos, VTermPos oldpos, int visible, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->movecursor(pos, oldpos, visible);
+ };
+ m_vtermScreenCallbacks.sb_clear = [](void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->sb_clear();
+ };
+ m_vtermScreenCallbacks.bell = [](void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ emit p->q->bell();
+ return 1;
+ };
+
+ vterm_screen_set_callbacks(m_vtermScreen, &m_vtermScreenCallbacks, this);
+ vterm_screen_set_damage_merge(m_vtermScreen, VTERM_DAMAGE_SCROLL);
+ vterm_screen_enable_altscreen(m_vtermScreen, true);
+
+ memset(&m_vtermStateFallbacks, 0, sizeof(m_vtermStateFallbacks));
+
+ m_vtermStateFallbacks.osc = [](int cmd, VTermStringFragment fragment, void *user) {
+ auto p = static_cast<TerminalSurfacePrivate *>(user);
+ return p->osc(cmd, fragment);
+ };
+
+ VTermState *vts = vterm_obtain_state(m_vterm.get());
+ vterm_state_set_unrecognised_fallbacks(vts, &m_vtermStateFallbacks, this);
+ vterm_state_set_bold_highbright(vts, true);
+
+ VTermColor fg;
+ VTermColor bg;
+ vterm_color_indexed(&fg, ColorIndex::Foreground);
+ vterm_color_indexed(&bg, ColorIndex::Background);
+ vterm_state_set_default_colors(vts, &fg, &bg);
+
+ for (int i = 0; i < 16; ++i) {
+ VTermColor col;
+ vterm_color_indexed(&col, i);
+ vterm_state_set_palette_color(vts, i, &col);
+ }
+
+ vterm_screen_reset(m_vtermScreen, 1);
+ }
+
+ QSize liveSize() const
+ {
+ int rows;
+ int cols;
+ vterm_get_size(m_vterm.get(), &rows, &cols);
+
+ return QSize(cols, rows);
+ }
+
+ std::variant<int, QColor> toVariantColor(const VTermColor &color)
+ {
+ if (color.type & VTERM_COLOR_DEFAULT_BG)
+ return ColorIndex::Background;
+ else if (color.type & VTERM_COLOR_DEFAULT_FG)
+ return ColorIndex::Foreground;
+ else if (color.type & VTERM_COLOR_INDEXED) {
+ if (color.indexed.idx >= 16) {
+ VTermColor c = color;
+ vterm_state_convert_color_to_rgb(vterm_obtain_state(m_vterm.get()), &c);
+ return toQColor(c);
+ }
+ return color.indexed.idx;
+ } else if (color.type == VTERM_COLOR_RGB)
+ return toQColor(color);
+ else
+ return -1;
+ }
+
+ TerminalCell toCell(const VTermScreenCell &cell)
+ {
+ TerminalCell result;
+ result.width = cell.width;
+ result.text = QString::fromUcs4(cell.chars);
+
+ const VTermColor *bg = &cell.bg;
+ const VTermColor *fg = &cell.fg;
+
+ if (static_cast<bool>(cell.attrs.reverse))
+ std::swap(fg, bg);
+
+ result.backgroundColor = toVariantColor(*bg);
+ result.foregroundColor = toVariantColor(*fg);
+
+ result.bold = cell.attrs.bold;
+ result.strikeOut = cell.attrs.strike;
+
+ if (cell.attrs.underline > 0) {
+ result.underlineStyle = QTextCharFormat::NoUnderline;
+ switch (cell.attrs.underline) {
+ case VTERM_UNDERLINE_SINGLE:
+ result.underlineStyle = QTextCharFormat::SingleUnderline;
+ break;
+ case VTERM_UNDERLINE_DOUBLE:
+ // TODO: Double underline
+ result.underlineStyle = QTextCharFormat::SingleUnderline;
+ break;
+ case VTERM_UNDERLINE_CURLY:
+ result.underlineStyle = QTextCharFormat::WaveUnderline;
+ break;
+ case VTERM_UNDERLINE_DASHED:
+ result.underlineStyle = QTextCharFormat::DashUnderline;
+ break;
+ case VTERM_UNDERLINE_DOTTED:
+ result.underlineStyle = QTextCharFormat::DotLine;
+ break;
+ }
+ }
+
+ result.strikeOut = cell.attrs.strike;
+
+ return result;
+ }
+
+ // Callbacks from vterm
+ void invalidate(VTermRect rect)
+ {
+ if (!m_altscreen) {
+ rect.start_row += m_scrollback->size();
+ rect.end_row += m_scrollback->size();
+ }
+
+ emit q->invalidated(
+ QRect{QPoint{rect.start_col, rect.start_row}, QPoint{rect.end_col, rect.end_row - 1}});
+ }
+
+ int sb_pushline(int cols, const VTermScreenCell *cells)
+ {
+ m_scrollback->emplace(cols, cells);
+ emit q->fullSizeChanged(q->fullSize());
+ return 1;
+ }
+
+ int sb_popline(int cols, VTermScreenCell *cells)
+ {
+ if (m_scrollback->size() == 0)
+ return 0;
+
+ m_scrollback->popto(cols, cells);
+ emit q->fullSizeChanged(q->fullSize());
+ return 1;
+ }
+
+ int sb_clear()
+ {
+ m_scrollback->clear();
+ emit q->fullSizeChanged(q->fullSize());
+ return 1;
+ }
+
+ int osc(int cmd, const VTermStringFragment &fragment)
+ {
+ if (m_shellIntegration)
+ m_shellIntegration->onOsc(cmd, fragment);
+
+ return 1;
+ }
+
+ int setTerminalProperties(VTermProp prop, VTermValue *val)
+ {
+ switch (prop) {
+ case VTERM_PROP_CURSORVISIBLE: {
+ Cursor old = q->cursor();
+ m_cursor.visible = val->boolean;
+ q->cursorChanged(old, q->cursor());
+ break;
+ }
+ case VTERM_PROP_CURSORBLINK: {
+ Cursor old = q->cursor();
+ m_cursor.blink = val->boolean;
+ emit q->cursorChanged(old, q->cursor());
+ break;
+ }
+ case VTERM_PROP_CURSORSHAPE: {
+ Cursor old = q->cursor();
+ m_cursor.shape = (Cursor::Shape) val->number;
+ emit q->cursorChanged(old, q->cursor());
+ break;
+ }
+ case VTERM_PROP_ICONNAME:
+ break;
+ case VTERM_PROP_TITLE:
+ break;
+ case VTERM_PROP_ALTSCREEN:
+ m_altscreen = val->boolean;
+ emit q->altscreenChanged(m_altscreen);
+ break;
+ case VTERM_PROP_MOUSE:
+ qCDebug(log) << "Ignoring VTERM_PROP_MOUSE" << val->number;
+ break;
+ case VTERM_PROP_REVERSE:
+ qCDebug(log) << "Ignoring VTERM_PROP_REVERSE" << val->boolean;
+ break;
+ case VTERM_N_PROPS:
+ break;
+ }
+ return 1;
+ }
+ int movecursor(VTermPos pos, VTermPos oldpos, int visible)
+ {
+ Q_UNUSED(oldpos);
+ Cursor oldCursor = q->cursor();
+ m_cursor.position = {pos.col, pos.row};
+ m_cursor.visible = visible > 0;
+ q->cursorChanged(oldCursor, q->cursor());
+ return 1;
+ }
+
+ const VTermScreenCell *cellAt(int x, int y)
+ {
+ QTC_ASSERT(y >= 0 && x >= 0, return nullptr);
+ QTC_ASSERT(y < q->fullSize().height() && x < liveSize().width(), return nullptr);
+
+ if (!m_altscreen && y < m_scrollback->size()) {
+ const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y);
+ if (x < sbl.cols()) {
+ return sbl.cell(x);
+ }
+ return nullptr;
+ }
+
+ if (!m_altscreen)
+ y -= m_scrollback->size();
+
+ static VTermScreenCell refCell{};
+ VTermPos vtp{y, x};
+ vterm_screen_get_cell(m_vtermScreen, vtp, &refCell);
+
+ return &refCell;
+ }
+
+ std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
+ VTermScreen *m_vtermScreen;
+ VTermScreenCallbacks m_vtermScreenCallbacks;
+ VTermStateFallbacks m_vtermStateFallbacks;
+
+ Cursor m_cursor;
+ QString m_currentCommand;
+
+ bool m_altscreen{false};
+
+ std::unique_ptr<Internal::Scrollback> m_scrollback;
+
+ ShellIntegration *m_shellIntegration{nullptr};
+
+ TerminalSurface *q;
+};
+
+TerminalSurface::TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration)
+ : d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize, shellIntegration))
+{
+ d->init();
+}
+
+TerminalSurface::~TerminalSurface() = default;
+
+int TerminalSurface::cellWidthAt(int x, int y) const
+{
+ const VTermScreenCell *cell = d->cellAt(x, y);
+ if (!cell)
+ return 0;
+ return cell->width;
+}
+
+QSize TerminalSurface::liveSize() const
+{
+ return d->liveSize();
+}
+
+QSize TerminalSurface::fullSize() const
+{
+ if (d->m_altscreen)
+ return liveSize();
+ return QSize{d->liveSize().width(), d->liveSize().height() + d->m_scrollback->size()};
+}
+
+std::u32string::value_type TerminalSurface::fetchCharAt(int x, int y) const
+{
+ const VTermScreenCell *cell = d->cellAt(x, y);
+ if (!cell)
+ return 0;
+
+ if (cell->width == 0)
+ return 0;
+
+ QString s = QString::fromUcs4(cell->chars, 6).normalized(QString::NormalizationForm_C);
+ const QList<uint> ucs4 = s.toUcs4();
+ return std::u32string(ucs4.begin(), ucs4.end()).front();
+}
+
+TerminalCell TerminalSurface::fetchCell(int x, int y) const
+{
+ static TerminalCell emptyCell{1,
+ {},
+ {},
+ false,
+ ColorIndex::Foreground,
+ ColorIndex::Background,
+ QTextCharFormat::NoUnderline,
+ false};
+
+ QTC_ASSERT(y >= 0, return emptyCell);
+ QTC_ASSERT(y < fullSize().height() && x < fullSize().width(), return emptyCell);
+
+ const VTermScreenCell *refCell = d->cellAt(x, y);
+ if (!refCell)
+ return emptyCell;
+
+ return d->toCell(*refCell);
+}
+
+void TerminalSurface::clearAll()
+{
+ // Fake a scrollback clearing
+ QByteArray data{"\x1b[3J"};
+ vterm_input_write(d->m_vterm.get(), data.constData(), data.size());
+
+ // Send Ctrl+L which will clear the screen
+ emit writeToPty(QByteArray("\f"));
+}
+
+void TerminalSurface::resize(QSize newSize)
+{
+ vterm_set_size(d->m_vterm.get(), newSize.height(), newSize.width());
+}
+
+QPoint TerminalSurface::posToGrid(int pos) const
+{
+ return {pos % d->liveSize().width(), pos / d->liveSize().width()};
+}
+int TerminalSurface::gridToPos(QPoint gridPos) const
+{
+ return gridPos.y() * d->liveSize().width() + gridPos.x();
+}
+
+void TerminalSurface::dataFromPty(const QByteArray &data)
+{
+ vterm_input_write(d->m_vterm.get(), data.constData(), data.size());
+ vterm_screen_flush_damage(d->m_vtermScreen);
+}
+
+void TerminalSurface::flush()
+{
+ vterm_screen_flush_damage(d->m_vtermScreen);
+}
+
+void TerminalSurface::pasteFromClipboard(const QString &clipboardText)
+{
+ if (clipboardText.isEmpty())
+ return;
+
+ vterm_keyboard_start_paste(d->m_vterm.get());
+ for (unsigned int ch : clipboardText.toUcs4())
+ vterm_keyboard_unichar(d->m_vterm.get(), ch, VTERM_MOD_NONE);
+ vterm_keyboard_end_paste(d->m_vterm.get());
+
+ if (!d->m_altscreen) {
+ emit unscroll();
+ }
+}
+
+void TerminalSurface::sendKey(Qt::Key key)
+{
+ if (key == Qt::Key_Escape)
+ vterm_keyboard_key(d->m_vterm.get(), VTERM_KEY_ESCAPE, VTERM_MOD_NONE);
+}
+
+void TerminalSurface::sendKey(const QString &text)
+{
+ for (const unsigned int ch : text.toUcs4())
+ vterm_keyboard_unichar(d->m_vterm.get(), ch, VTERM_MOD_NONE);
+}
+
+void TerminalSurface::sendKey(QKeyEvent *event)
+{
+ bool keypad = event->modifiers() & Qt::KeypadModifier;
+ VTermModifier mod = Internal::qtModifierToVTerm(event->modifiers());
+ VTermKey key = Internal::qtKeyToVTerm(Qt::Key(event->key()), keypad);
+
+ if (key != VTERM_KEY_NONE) {
+ if (mod == VTERM_MOD_SHIFT && (key == VTERM_KEY_ESCAPE || key == VTERM_KEY_BACKSPACE))
+ mod = VTERM_MOD_NONE;
+
+ vterm_keyboard_key(d->m_vterm.get(), key, mod);
+ } else if (event->text().length() == 1) {
+ // This maps to delete word and is way to easy to mistakenly type
+ // if (event->key() == Qt::Key_Space && mod == VTERM_MOD_SHIFT)
+ // mod = VTERM_MOD_NONE;
+
+ // Per https://github.com/justinmk/neovim/commit/317d5ca7b0f92ef42de989b3556ca9503f0a3bf6
+ // libvterm prefers we send the full keycode rather than sending the
+ // ctrl modifier. This helps with ncurses applications which otherwise
+ // do not recognize ctrl+<key> and in the shell for getting common control characters
+ // like ctrl+i for tab or ctrl+j for newline.
+
+ // Workaround for "ALT+SHIFT+/" (\ on german mac keyboards)
+ if (mod == (VTERM_MOD_SHIFT | VTERM_MOD_ALT) && event->key() == Qt::Key_Slash) {
+ mod = VTERM_MOD_NONE;
+ }
+
+ vterm_keyboard_unichar(d->m_vterm.get(), event->text().toUcs4()[0], VTERM_MOD_NONE);
+ } else if (mod == VTERM_MOD_CTRL && event->key() >= Qt::Key_A && event->key() < Qt::Key_Z) {
+ vterm_keyboard_unichar(d->m_vterm.get(), 'a' + (event->key() - Qt::Key_A), mod);
+ }
+}
+
+Cursor TerminalSurface::cursor() const
+{
+ Cursor cursor = d->m_cursor;
+ if (!d->m_altscreen)
+ cursor.position.setY(cursor.position.y() + d->m_scrollback->size());
+
+ return cursor;
+}
+
+ShellIntegration *TerminalSurface::shellIntegration() const
+{
+ return d->m_shellIntegration;
+}
+
+CellIterator TerminalSurface::begin() const
+{
+ auto res = CellIterator(this, {0, 0});
+ res.m_state = CellIterator::State::BEGIN;
+ return res;
+}
+
+CellIterator TerminalSurface::end() const
+{
+ return CellIterator(this);
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rbegin() const
+{
+ return std::make_reverse_iterator(end());
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rend() const
+{
+ return std::make_reverse_iterator(begin());
+}
+
+CellIterator TerminalSurface::iteratorAt(QPoint pos) const
+{
+ return CellIterator(this, pos);
+}
+CellIterator TerminalSurface::iteratorAt(int pos) const
+{
+ return CellIterator(this, pos);
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(QPoint pos) const
+{
+ return std::make_reverse_iterator(iteratorAt(pos));
+}
+
+std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(int pos) const
+{
+ return std::make_reverse_iterator(iteratorAt(pos));
+}
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/terminalsurface.h b/src/plugins/terminal/terminalsurface.h
new file mode 100644
index 0000000000..a6fc7425d4
--- /dev/null
+++ b/src/plugins/terminal/terminalsurface.h
@@ -0,0 +1,111 @@
+// 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
+
+#pragma once
+
+#include "celliterator.h"
+#include "shellintegration.h"
+
+#include <QKeyEvent>
+#include <QSize>
+#include <QTextCharFormat>
+
+#include <memory>
+
+namespace Terminal::Internal {
+
+class Scrollback;
+
+struct TerminalSurfacePrivate;
+
+enum ColorIndex { Foreground = 16, Background = 17 };
+
+struct TerminalCell
+{
+ int width;
+ QString text;
+ bool bold{false};
+ bool italic{false};
+ std::variant<int, QColor> foregroundColor;
+ std::variant<int, QColor> backgroundColor;
+ QTextCharFormat::UnderlineStyle underlineStyle{QTextCharFormat::NoUnderline};
+ bool strikeOut{false};
+};
+
+struct Cursor
+{
+ enum class Shape {
+ Block = 1,
+ Underline,
+ LeftBar,
+ };
+ QPoint position;
+ bool visible;
+ Shape shape;
+ bool blink{false};
+};
+
+class TerminalSurface : public QObject
+{
+ Q_OBJECT;
+
+public:
+ TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration);
+ ~TerminalSurface();
+
+public:
+ CellIterator begin() const;
+ CellIterator end() const;
+ std::reverse_iterator<CellIterator> rbegin() const;
+ std::reverse_iterator<CellIterator> rend() const;
+
+ CellIterator iteratorAt(QPoint pos) const;
+ CellIterator iteratorAt(int pos) const;
+
+ std::reverse_iterator<CellIterator> rIteratorAt(QPoint pos) const;
+ std::reverse_iterator<CellIterator> rIteratorAt(int pos) const;
+
+public:
+ void clearAll();
+
+ void resize(QSize newSize);
+
+ TerminalCell fetchCell(int x, int y) const;
+ std::u32string::value_type fetchCharAt(int x, int y) const;
+ int cellWidthAt(int x, int y) const;
+
+ QSize liveSize() const;
+ QSize fullSize() const;
+
+ QPoint posToGrid(int pos) const;
+ int gridToPos(QPoint gridPos) const;
+
+ void dataFromPty(const QByteArray &data);
+ void flush();
+
+ void pasteFromClipboard(const QString &text);
+
+ void sendKey(Qt::Key key);
+ void sendKey(QKeyEvent *event);
+ void sendKey(const QString &text);
+
+ int invertedScrollOffset() const;
+
+ Cursor cursor() const;
+
+ ShellIntegration *shellIntegration() const;
+
+signals:
+ void writeToPty(const QByteArray &data);
+ void invalidated(QRect grid);
+ void fullSizeChanged(QSize newSize);
+ void cursorChanged(Cursor oldCursor, Cursor newCursor);
+ void altscreenChanged(bool altScreen);
+ void unscroll();
+ void bell();
+
+private:
+ std::unique_ptr<TerminalSurfacePrivate> d;
+};
+
+} // namespace Terminal::Internal
diff --git a/src/plugins/terminal/terminaltr.h b/src/plugins/terminal/terminaltr.h
new file mode 100644
index 0000000000..b645284833
--- /dev/null
+++ b/src/plugins/terminal/terminaltr.h
@@ -0,0 +1,15 @@
+// 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 <QCoreApplication>
+
+namespace Terminal {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::Terminal)
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp
new file mode 100644
index 0000000000..b63e66b78d
--- /dev/null
+++ b/src/plugins/terminal/terminalwidget.cpp
@@ -0,0 +1,1502 @@
+// 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 "terminalwidget.h"
+#include "glyphcache.h"
+#include "terminalcommands.h"
+#include "terminalsettings.h"
+#include "terminalsurface.h"
+
+#include <aggregation/aggregate.h>
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/fileutils.h>
+#include <coreplugin/icore.h>
+
+#include <utils/algorithm.h>
+#include <utils/environment.h>
+#include <utils/fileutils.h>
+#include <utils/hostosinfo.h>
+#include <utils/processinterface.h>
+#include <utils/stringutils.h>
+
+#include <vterm.h>
+
+#include <QApplication>
+#include <QCache>
+#include <QClipboard>
+#include <QDesktopServices>
+#include <QElapsedTimer>
+#include <QGlyphRun>
+#include <QLoggingCategory>
+#include <QMenu>
+#include <QMimeData>
+#include <QPaintEvent>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPixmapCache>
+#include <QRawFont>
+#include <QRegularExpression>
+#include <QScrollBar>
+#include <QTextItem>
+#include <QTextLayout>
+#include <QToolTip>
+
+Q_LOGGING_CATEGORY(terminalLog, "qtc.terminal", QtWarningMsg)
+Q_LOGGING_CATEGORY(selectionLog, "qtc.terminal.selection", QtWarningMsg)
+Q_LOGGING_CATEGORY(paintLog, "qtc.terminal.paint", QtWarningMsg)
+
+using namespace Utils;
+using namespace Utils::Terminal;
+
+namespace Terminal {
+
+namespace ColorIndex {
+enum Indices {
+ Foreground = Internal::ColorIndex::Foreground,
+ Background = Internal::ColorIndex::Background,
+ Selection,
+ FindMatch,
+};
+}
+
+using namespace std::chrono_literals;
+
+// Minimum time between two refreshes. (30fps)
+static constexpr std::chrono::milliseconds minRefreshInterval = 1s / 30;
+
+TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters)
+ : QAbstractScrollArea(parent)
+ , m_openParameters(openParameters)
+ , m_lastFlush(std::chrono::system_clock::now())
+ , m_lastDoubleClick(std::chrono::system_clock::now())
+{
+ setupSurface();
+ setupFont();
+ setupColors();
+ setupActions();
+
+ m_cursorBlinkTimer.setInterval(750);
+ m_cursorBlinkTimer.setSingleShot(false);
+
+ connect(&m_cursorBlinkTimer, &QTimer::timeout, this, [this]() {
+ if (hasFocus())
+ m_cursorBlinkState = !m_cursorBlinkState;
+ else
+ m_cursorBlinkState = true;
+ updateViewportRect(gridToViewport(QRect{m_cursor.position, m_cursor.position}));
+ });
+
+ setAttribute(Qt::WA_InputMethodEnabled);
+ setAttribute(Qt::WA_MouseTracking);
+ setAcceptDrops(true);
+
+ setCursor(Qt::IBeamCursor);
+
+ setViewportMargins(1, 1, 1, 1);
+
+ setFocus();
+ setFocusPolicy(Qt::StrongFocus);
+
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+
+ m_flushDelayTimer.setSingleShot(true);
+ m_flushDelayTimer.setInterval(minRefreshInterval);
+
+ connect(&m_flushDelayTimer, &QTimer::timeout, this, [this]() { flushVTerm(true); });
+
+ m_scrollTimer.setSingleShot(false);
+ m_scrollTimer.setInterval(1s / 2);
+ connect(&m_scrollTimer, &QTimer::timeout, this, [this] {
+ if (m_scrollDirection < 0)
+ verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
+ else if (m_scrollDirection > 0)
+ verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
+ });
+
+ connect(&TerminalSettings::instance(), &AspectContainer::applied, this, [this] {
+ // Setup colors first, as setupFont will redraw the screen.
+ setupColors();
+ setupFont();
+ configBlinkTimer();
+ });
+
+ m_aggregate = new Aggregation::Aggregate(this);
+ m_aggregate->add(this);
+ m_aggregate->add(m_search.get());
+}
+
+void TerminalWidget::setupPty()
+{
+ m_process = std::make_unique<Process>();
+
+ CommandLine shellCommand = m_openParameters.shellCommand.value_or(
+ CommandLine{TerminalSettings::instance().shell.filePath(),
+ TerminalSettings::instance().shellArguments.value(),
+ CommandLine::Raw});
+
+ Environment env = m_openParameters.environment.value_or(
+ shellCommand.executable().deviceEnvironment());
+
+ // For git bash on Windows
+ env.prependOrSetPath(shellCommand.executable().parentDir());
+ if (env.hasKey("CLINK_NOAUTORUN"))
+ env.unset("CLINK_NOAUTORUN");
+
+ m_process->setProcessMode(ProcessMode::Writer);
+ m_process->setPtyData(Utils::Pty::Data());
+ m_process->setCommand(shellCommand);
+ if (m_openParameters.workingDirectory.has_value())
+ m_process->setWorkingDirectory(*m_openParameters.workingDirectory);
+ m_process->setEnvironment(env);
+
+ if (m_surface->shellIntegration()) {
+ m_surface->shellIntegration()->prepareProcess(*m_process.get());
+ }
+
+ connect(m_process.get(), &Process::readyReadStandardOutput, this, [this]() {
+ onReadyRead(false);
+ });
+
+ connect(m_process.get(), &Process::done, this, [this] {
+ if (m_process) {
+ if (m_process->exitCode() != 0) {
+ QByteArray msg = QString("\r\n\033[31mProcess exited with code: %1")
+ .arg(m_process->exitCode())
+ .toUtf8();
+
+ if (!m_process->errorString().isEmpty())
+ msg += QString(" (%1)").arg(m_process->errorString()).toUtf8();
+
+ m_surface->dataFromPty(msg);
+
+ return;
+ }
+ }
+
+ if (m_openParameters.m_exitBehavior == ExitBehavior::Restart) {
+ QMetaObject::invokeMethod(
+ this,
+ [this] {
+ m_process.reset();
+ setupSurface();
+ setupPty();
+ },
+ Qt::QueuedConnection);
+ }
+
+ if (m_openParameters.m_exitBehavior == ExitBehavior::Close)
+ deleteLater();
+
+ if (m_openParameters.m_exitBehavior == ExitBehavior::Keep) {
+ QByteArray msg = QString("\r\nProcess exited with code: %1")
+ .arg(m_process ? m_process->exitCode() : -1)
+ .toUtf8();
+
+ m_surface->dataFromPty(msg);
+ }
+ });
+
+ connect(m_process.get(), &Process::started, this, [this] {
+ if (m_shellName.isEmpty())
+ m_shellName = m_process->commandLine().executable().fileName();
+ if (HostOsInfo::isWindowsHost() && m_shellName.endsWith(QTC_WIN_EXE_SUFFIX))
+ m_shellName.chop(QStringLiteral(QTC_WIN_EXE_SUFFIX).size());
+
+ applySizeChange();
+ emit started(m_process->processId());
+ });
+
+ m_process->start();
+}
+
+void TerminalWidget::setupFont()
+{
+ QFont f;
+ f.setFixedPitch(true);
+ f.setFamily(TerminalSettings::instance().font.value());
+ f.setPointSize(TerminalSettings::instance().fontSize.value());
+
+ setFont(f);
+}
+
+void TerminalWidget::setupColors()
+{
+ // Check if the colors have changed.
+ std::array<QColor, 20> newColors;
+ for (int i = 0; i < 16; ++i) {
+ newColors[i] = TerminalSettings::instance().colors[i].value();
+ }
+ newColors[ColorIndex::Background] = TerminalSettings::instance().backgroundColor.value();
+ newColors[ColorIndex::Foreground] = TerminalSettings::instance().foregroundColor.value();
+ newColors[ColorIndex::Selection] = TerminalSettings::instance().selectionColor.value();
+ newColors[ColorIndex::FindMatch] = TerminalSettings::instance().findMatchColor.value();
+
+ if (m_currentColors == newColors)
+ return;
+
+ m_currentColors = newColors;
+
+ updateViewport();
+ update();
+}
+
+void TerminalWidget::setupActions()
+{
+ WidgetActions &a = TerminalCommands::widgetActions();
+
+ auto ifHasFocus = [this](void (TerminalWidget::*f)()) {
+ return [this, f] {
+ if (hasFocus())
+ (this->*f)();
+ };
+ };
+
+ // clang-format off
+ connect(&a.copy, &QAction::triggered, this, ifHasFocus(&TerminalWidget::copyToClipboard));
+ connect(&a.paste, &QAction::triggered, this, ifHasFocus(&TerminalWidget::pasteFromClipboard));
+ connect(&a.copyLink, &QAction::triggered, this, ifHasFocus(&TerminalWidget::copyLinkToClipboard));
+ connect(&a.clearSelection, &QAction::triggered, this, ifHasFocus(&TerminalWidget::clearSelection));
+ connect(&a.clearTerminal, &QAction::triggered, this, ifHasFocus(&TerminalWidget::clearContents));
+ connect(&a.moveCursorWordLeft, &QAction::triggered, this, ifHasFocus(&TerminalWidget::moveCursorWordLeft));
+ connect(&a.moveCursorWordRight, &QAction::triggered, this, ifHasFocus(&TerminalWidget::moveCursorWordRight));
+ // clang-format on
+}
+
+void TerminalWidget::writeToPty(const QByteArray &data)
+{
+ if (m_process && m_process->isRunning())
+ m_process->writeRaw(data);
+}
+
+void TerminalWidget::setupSurface()
+{
+ m_shellIntegration.reset(new ShellIntegration());
+ m_surface = std::make_unique<Internal::TerminalSurface>(QSize{80, 60}, m_shellIntegration.get());
+ m_search = TerminalSearchPtr(new TerminalSearch(m_surface.get()), [this](TerminalSearch *p) {
+ m_aggregate->remove(p);
+ delete p;
+ });
+
+ connect(m_search.get(), &TerminalSearch::hitsChanged, this, &TerminalWidget::updateViewport);
+ connect(m_search.get(), &TerminalSearch::currentHitChanged, this, [this] {
+ SearchHit hit = m_search->currentHit();
+ if (hit.start >= 0) {
+ setSelection(Selection{hit.start, hit.end, true}, hit != m_lastSelectedHit);
+ m_lastSelectedHit = hit;
+ }
+ });
+
+ connect(m_surface.get(),
+ &Internal::TerminalSurface::writeToPty,
+ this,
+ &TerminalWidget::writeToPty);
+ connect(m_surface.get(), &Internal::TerminalSurface::fullSizeChanged, this, [this] {
+ updateScrollBars();
+ });
+ connect(m_surface.get(),
+ &Internal::TerminalSurface::invalidated,
+ this,
+ [this](const QRect &rect) {
+ setSelection(std::nullopt);
+ updateViewportRect(gridToViewport(rect));
+ verticalScrollBar()->setValue(m_surface->fullSize().height());
+ });
+ connect(m_surface.get(),
+ &Internal::TerminalSurface::cursorChanged,
+ this,
+ [this](const Internal::Cursor &oldCursor, const Internal::Cursor &newCursor) {
+ int startX = oldCursor.position.x();
+ int endX = newCursor.position.x();
+
+ if (startX > endX)
+ std::swap(startX, endX);
+
+ int startY = oldCursor.position.y();
+ int endY = newCursor.position.y();
+ if (startY > endY)
+ std::swap(startY, endY);
+
+ m_cursor = newCursor;
+
+ updateViewportRect(
+ gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}}));
+ configBlinkTimer();
+ });
+ connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] {
+ updateScrollBars();
+ if (!setSelection(std::nullopt))
+ updateViewport();
+ });
+ connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] {
+ verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+ });
+ connect(m_surface.get(), &Internal::TerminalSurface::bell, this, [] {
+ if (TerminalSettings::instance().audibleBell.value())
+ QApplication::beep();
+ });
+
+ if (m_shellIntegration) {
+ connect(m_shellIntegration.get(),
+ &ShellIntegration::commandChanged,
+ this,
+ [this](const CommandLine &command) {
+ m_currentCommand = command;
+ emit commandChanged(m_currentCommand);
+ });
+ connect(m_shellIntegration.get(),
+ &ShellIntegration::currentDirChanged,
+ this,
+ [this](const QString &currentDir) {
+ m_cwd = FilePath::fromUserInput(currentDir);
+ emit cwdChanged(m_cwd);
+ });
+ }
+}
+
+void TerminalWidget::configBlinkTimer()
+{
+ bool shouldRun = m_cursor.visible && m_cursor.blink && hasFocus()
+ && TerminalSettings::instance().allowBlinkingCursor.value();
+ if (shouldRun != m_cursorBlinkTimer.isActive()) {
+ if (shouldRun)
+ m_cursorBlinkTimer.start();
+ else
+ m_cursorBlinkTimer.stop();
+ }
+}
+
+QColor TerminalWidget::toQColor(std::variant<int, QColor> color) const
+{
+ if (std::holds_alternative<int>(color)) {
+ int idx = std::get<int>(color);
+ if (idx >= 0 && idx < 18)
+ return m_currentColors[idx];
+
+ return m_currentColors[ColorIndex::Background];
+ }
+ return std::get<QColor>(color);
+}
+
+void TerminalWidget::updateCopyState()
+{
+ if (!hasFocus())
+ return;
+
+ TerminalCommands::widgetActions().copy.setEnabled(m_selection.has_value());
+ TerminalCommands::widgetActions().copyLink.setEnabled(m_linkSelection.has_value());
+}
+
+void TerminalWidget::setFont(const QFont &font)
+{
+ m_font = font;
+
+ QFontMetricsF qfm{m_font};
+ const qreal w = [qfm]() -> qreal {
+ if (HostOsInfo::isMacHost())
+ return qfm.maxWidth();
+ return qfm.averageCharWidth();
+ }();
+
+ qCInfo(terminalLog) << font.family() << font.pointSize() << w << viewport()->size();
+
+ m_cellSize = {w, (double) qCeil(qfm.height())};
+
+ QAbstractScrollArea::setFont(m_font);
+
+ if (m_process) {
+ applySizeChange();
+ }
+}
+
+void TerminalWidget::copyToClipboard()
+{
+ QTC_ASSERT(m_selection.has_value(), return);
+
+ QString text = textFromSelection();
+
+ qCDebug(selectionLog) << "Copied to clipboard: " << text;
+
+ setClipboardAndSelection(text);
+}
+
+void TerminalWidget::pasteFromClipboard()
+{
+ QClipboard *clipboard = QApplication::clipboard();
+ const QString clipboardText = clipboard->text(QClipboard::Clipboard);
+
+ if (clipboardText.isEmpty())
+ return;
+
+ m_surface->pasteFromClipboard(clipboardText);
+}
+
+void TerminalWidget::copyLinkToClipboard()
+{
+ if (m_linkSelection)
+ setClipboardAndSelection(m_linkSelection->link.targetFilePath.toUserOutput());
+}
+
+void TerminalWidget::clearSelection()
+{
+ setSelection(std::nullopt);
+ m_surface->sendKey(Qt::Key_Escape);
+}
+
+void TerminalWidget::zoomIn()
+{
+ m_font.setPointSize(m_font.pointSize() + 1);
+ setFont(m_font);
+}
+
+void TerminalWidget::zoomOut()
+{
+ m_font.setPointSize(qMax(m_font.pointSize() - 1, 1));
+ setFont(m_font);
+}
+
+void TerminalWidget::moveCursorWordLeft()
+{
+ writeToPty("\x1b\x62");
+}
+
+void TerminalWidget::moveCursorWordRight()
+{
+ writeToPty("\x1b\x66");
+}
+
+void TerminalWidget::clearContents()
+{
+ m_surface->clearAll();
+}
+
+void TerminalWidget::onReadyRead(bool forceFlush)
+{
+ QByteArray data = m_process->readAllRawStandardOutput();
+
+ m_surface->dataFromPty(data);
+
+ flushVTerm(forceFlush);
+}
+
+void TerminalWidget::flushVTerm(bool force)
+{
+ const std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ const std::chrono::milliseconds timeSinceLastFlush
+ = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastFlush);
+
+ const bool shouldFlushImmediately = timeSinceLastFlush > minRefreshInterval;
+ if (force || shouldFlushImmediately) {
+ if (m_flushDelayTimer.isActive())
+ m_flushDelayTimer.stop();
+
+ m_lastFlush = now;
+ m_surface->flush();
+ return;
+ }
+
+ if (!m_flushDelayTimer.isActive()) {
+ const std::chrono::milliseconds timeToNextFlush = (minRefreshInterval - timeSinceLastFlush);
+ m_flushDelayTimer.start(timeToNextFlush.count());
+ }
+}
+
+QString TerminalWidget::textFromSelection() const
+{
+ if (!m_selection)
+ return {};
+
+ Internal::CellIterator it = m_surface->iteratorAt(m_selection->start);
+ Internal::CellIterator end = m_surface->iteratorAt(m_selection->end);
+
+ std::u32string s;
+ bool previousWasZero = false;
+ for (; it != end; ++it) {
+ if (it.gridPos().x() == 0 && !s.empty() && previousWasZero)
+ s += U'\n';
+
+ if (*it != 0) {
+ previousWasZero = false;
+ s += *it;
+ } else {
+ previousWasZero = true;
+ }
+ }
+
+ return QString::fromUcs4(s.data(), static_cast<int>(s.size()));
+}
+
+bool TerminalWidget::setSelection(const std::optional<Selection> &selection, bool scroll)
+{
+ qCDebug(selectionLog) << "setSelection" << selection.has_value();
+ if (selection.has_value())
+ qCDebug(selectionLog) << "start:" << selection->start << "end:" << selection->end
+ << "final:" << selection->final;
+
+ if (selectionLog().isDebugEnabled())
+ updateViewport();
+
+ if (selection == m_selection)
+ return false;
+
+ m_selection = selection;
+
+ updateCopyState();
+
+ if (m_selection && m_selection->final) {
+ qCDebug(selectionLog) << "Copy enabled:" << selection.has_value();
+ QString text = textFromSelection();
+
+ QClipboard *clipboard = QApplication::clipboard();
+ if (clipboard->supportsSelection()) {
+ qCDebug(selectionLog) << "Selection set to clipboard: " << text;
+ clipboard->setText(text, QClipboard::Selection);
+ }
+
+ if (scroll) {
+ QPoint start = m_surface->posToGrid(m_selection->start);
+ QPoint end = m_surface->posToGrid(m_selection->end);
+ QRect viewRect = gridToViewport(QRect{start, end});
+ if (viewRect.y() >= viewport()->height() || viewRect.y() < 0) {
+ // Selection is outside of the viewport, scroll to it.
+ verticalScrollBar()->setValue(start.y());
+ }
+ }
+
+ m_search->setCurrentSelection(SearchHitWithText{{selection->start, selection->end}, text});
+ }
+
+ if (!selectionLog().isDebugEnabled())
+ updateViewport();
+
+ return true;
+}
+
+void TerminalWidget::setShellName(const QString &shellName)
+{
+ m_shellName = shellName;
+}
+
+QString TerminalWidget::shellName() const
+{
+ return m_shellName;
+}
+
+FilePath TerminalWidget::cwd() const
+{
+ return m_cwd;
+}
+
+CommandLine TerminalWidget::currentCommand() const
+{
+ return m_currentCommand;
+}
+
+std::optional<Id> TerminalWidget::identifier() const
+{
+ return m_openParameters.identifier;
+}
+
+QProcess::ProcessState TerminalWidget::processState() const
+{
+ if (m_process)
+ return m_process->state();
+
+ return QProcess::NotRunning;
+}
+
+void TerminalWidget::restart(const OpenTerminalParameters &openParameters)
+{
+ QTC_ASSERT(!m_process || !m_process->isRunning(), return);
+ m_openParameters = openParameters;
+
+ m_process.reset();
+ setupSurface();
+ setupPty();
+}
+
+QPoint TerminalWidget::viewportToGlobal(QPoint p) const
+{
+ int y = p.y() - topMargin();
+ const double offset = verticalScrollBar()->value() * m_cellSize.height();
+ y += offset;
+
+ return {p.x(), y};
+}
+
+QPoint TerminalWidget::globalToViewport(QPoint p) const
+{
+ int y = p.y() + topMargin();
+ const double offset = verticalScrollBar()->value() * m_cellSize.height();
+ y -= offset;
+
+ return {p.x(), y};
+}
+
+QPoint TerminalWidget::globalToGrid(QPointF p) const
+{
+ return QPoint(p.x() / m_cellSize.width(), p.y() / m_cellSize.height());
+}
+
+QPointF TerminalWidget::gridToGlobal(QPoint p, bool bottom, bool right) const
+{
+ QPointF result = QPointF(p.x() * m_cellSize.width(), p.y() * m_cellSize.height());
+ if (bottom || right)
+ result += {right ? m_cellSize.width() : 0, bottom ? m_cellSize.height() : 0};
+ return result;
+}
+
+qreal TerminalWidget::topMargin() const
+{
+ return viewport()->size().height() - (m_surface->liveSize().height() * m_cellSize.height());
+}
+
+static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen)
+{
+ const qreal radiusBase = qMax(qreal(1), maxRadius);
+ const qreal pWidth = pen.widthF();
+
+ const QString key = QLatin1String("WaveUnderline-") % pen.color().name()
+ % QString::number(int(radiusBase), 16)
+ % QString::number(int(pWidth), 16);
+
+ QPixmap pixmap;
+ if (QPixmapCache::find(key, &pixmap))
+ return pixmap;
+
+ const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio
+ const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod);
+ const qreal radius = qFloor(radiusBase * 2) / 2.;
+
+ QPainterPath path;
+
+ qreal xs = 0;
+ qreal ys = radius;
+
+ while (xs < width) {
+ xs += halfPeriod;
+ ys = -ys;
+ path.quadTo(xs - halfPeriod / 2, ys, xs, 0);
+ }
+
+ pixmap = QPixmap(width, radius * 2);
+ pixmap.fill(Qt::transparent);
+ {
+ QPen wavePen = pen;
+ wavePen.setCapStyle(Qt::SquareCap);
+
+ // This is to protect against making the line too fat, as happens on macOS
+ // due to it having a rather thick width for the regular underline.
+ const qreal maxPenWidth = .8 * radius;
+ if (wavePen.widthF() > maxPenWidth)
+ wavePen.setWidthF(maxPenWidth);
+
+ QPainter imgPainter(&pixmap);
+ imgPainter.setPen(wavePen);
+ imgPainter.setRenderHint(QPainter::Antialiasing);
+ imgPainter.translate(0, radius);
+ imgPainter.drawPath(path);
+ }
+
+ QPixmapCache::insert(key, pixmap);
+
+ return pixmap;
+}
+
+// Copied from qpainter.cpp
+static void drawTextItemDecoration(QPainter &painter,
+ const QPointF &pos,
+ QTextCharFormat::UnderlineStyle underlineStyle,
+ QTextItem::RenderFlags flags,
+ qreal width,
+ const QColor &underlineColor,
+ const QRawFont &font)
+{
+ if (underlineStyle == QTextCharFormat::NoUnderline
+ && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
+ return;
+
+ const QPen oldPen = painter.pen();
+ const QBrush oldBrush = painter.brush();
+ painter.setBrush(Qt::NoBrush);
+ QPen pen = oldPen;
+ pen.setStyle(Qt::SolidLine);
+ pen.setWidthF(font.lineThickness());
+ pen.setCapStyle(Qt::FlatCap);
+
+ QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y());
+
+ const qreal underlineOffset = font.underlinePosition();
+
+ /*if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
+ QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
+ if (theme)
+ underlineStyle = QTextCharFormat::UnderlineStyle(
+ theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt());
+ if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved
+ underlineStyle = QTextCharFormat::WaveUnderline;
+ }*/
+
+ if (underlineStyle == QTextCharFormat::WaveUnderline) {
+ painter.save();
+ painter.translate(0, pos.y() + 1);
+ qreal maxHeight = font.descent() - qreal(1);
+
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms
+ const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()),
+ maxHeight / qreal(2.)),
+ pen);
+ const int descent = qFloor(maxHeight);
+
+ painter.setBrushOrigin(painter.brushOrigin().x(), 0);
+ painter.fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
+ painter.restore();
+ } else if (underlineStyle != QTextCharFormat::NoUnderline) {
+ // Deliberately ceil the offset to avoid the underline coming too close to
+ // the text above it, but limit it to stay within descent.
+ qreal adjustedUnderlineOffset = std::ceil(underlineOffset) + 0.5;
+ if (underlineOffset <= font.descent())
+ adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, font.descent() - qreal(0.5));
+ const qreal underlinePos = pos.y() + adjustedUnderlineOffset;
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ pen.setStyle((Qt::PenStyle)(underlineStyle));
+ painter.setPen(pen);
+ QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos);
+ painter.drawLine(underline);
+ }
+
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(oldPen.color());
+
+ if (flags & QTextItem::StrikeOut) {
+ QLineF strikeOutLine = line;
+ strikeOutLine.translate(0., -font.ascent() / 3.);
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+ painter.setPen(pen);
+ painter.drawLine(strikeOutLine);
+ }
+
+ if (flags & QTextItem::Overline) {
+ QLineF overline = line;
+ overline.translate(0., -font.ascent());
+ QColor uc = underlineColor;
+ if (uc.isValid())
+ pen.setColor(uc);
+ painter.setPen(pen);
+ painter.drawLine(overline);
+ }
+
+ painter.setPen(oldPen);
+ painter.setBrush(oldBrush);
+}
+
+bool TerminalWidget::paintFindMatches(QPainter &p,
+ QList<SearchHit>::const_iterator &it,
+ const QRectF &cellRect,
+ const QPoint gridPos) const
+{
+ if (it == m_search->hits().constEnd())
+ return false;
+
+ const int pos = m_surface->gridToPos(gridPos);
+ while (it != m_search->hits().constEnd()) {
+ if (pos < it->start)
+ return false;
+
+ if (pos >= it->end) {
+ ++it;
+ continue;
+ }
+ break;
+ }
+
+ if (it == m_search->hits().constEnd())
+ return false;
+
+ p.fillRect(cellRect, m_currentColors[ColorIndex::FindMatch]);
+
+ return true;
+}
+
+bool TerminalWidget::paintSelection(QPainter &p, const QRectF &cellRect, const QPoint gridPos) const
+{
+ bool isInSelection = false;
+ const int pos = m_surface->gridToPos(gridPos);
+
+ if (m_selection)
+ isInSelection = pos >= m_selection->start && pos < m_selection->end;
+
+ if (isInSelection)
+ p.fillRect(cellRect, m_currentColors[ColorIndex::Selection]);
+
+ return isInSelection;
+}
+
+int TerminalWidget::paintCell(QPainter &p,
+ const QRectF &cellRect,
+ QPoint gridPos,
+ const Internal::TerminalCell &cell,
+ QFont &f,
+ QList<SearchHit>::const_iterator &searchIt) const
+{
+ bool paintBackground = !paintSelection(p, cellRect, gridPos)
+ && !paintFindMatches(p, searchIt, cellRect, gridPos);
+
+ bool isDefaultBg = std::holds_alternative<int>(cell.backgroundColor)
+ && std::get<int>(cell.backgroundColor) == 17;
+
+ if (paintBackground && !isDefaultBg)
+ p.fillRect(cellRect, toQColor(cell.backgroundColor));
+
+ p.setPen(toQColor(cell.foregroundColor));
+
+ f.setBold(cell.bold);
+ f.setItalic(cell.italic);
+
+ if (!cell.text.isEmpty()) {
+ const auto r = Internal::GlyphCache::instance().get(f, cell.text);
+
+ if (r) {
+ const auto brSize = r->boundingRect().size();
+ QPointF brOffset;
+ if (brSize.width() > cellRect.size().width())
+ brOffset.setX(-(brSize.width() - cellRect.size().width()) / 2.0);
+ if (brSize.height() > cellRect.size().height())
+ brOffset.setY(-(brSize.height() - cellRect.size().height()) / 2.0);
+
+ QPointF finalPos = cellRect.topLeft() + brOffset;
+
+ p.drawGlyphRun(finalPos, *r);
+
+ bool tempLink = false;
+ if (m_linkSelection) {
+ int chPos = m_surface->gridToPos(gridPos);
+ tempLink = chPos >= m_linkSelection->start && chPos < m_linkSelection->end;
+ }
+ if (cell.underlineStyle != QTextCharFormat::NoUnderline || cell.strikeOut || tempLink) {
+ QTextItem::RenderFlags flags;
+ //flags.setFlag(QTextItem::RenderFlag::Underline, cell.format.fontUnderline());
+ flags.setFlag(QTextItem::StrikeOut, cell.strikeOut);
+ finalPos.setY(finalPos.y() + r->rawFont().ascent());
+ drawTextItemDecoration(p,
+ finalPos,
+ tempLink ? QTextCharFormat::DashUnderline
+ : cell.underlineStyle,
+ flags,
+ cellRect.size().width(),
+ {},
+ r->rawFont());
+ }
+ }
+ }
+
+ return cell.width;
+}
+
+void TerminalWidget::paintCursor(QPainter &p) const
+{
+ if (!m_process || !m_process->isRunning())
+ return;
+
+ auto cursor = m_surface->cursor();
+
+ const bool blinkState = !cursor.blink || m_cursorBlinkState
+ || !TerminalSettings::instance().allowBlinkingCursor.value();
+
+ if (cursor.visible && blinkState) {
+ const int cursorCellWidth = m_surface->cellWidthAt(cursor.position.x(), cursor.position.y());
+
+ QRectF cursorRect = QRectF(gridToGlobal(cursor.position),
+ gridToGlobal({cursor.position.x() + cursorCellWidth,
+ cursor.position.y()},
+ true))
+ .toAlignedRect();
+
+ cursorRect.adjust(1, 1, -1, -1);
+
+ QPen pen(Qt::white, 0, Qt::SolidLine);
+ p.setPen(pen);
+
+ if (hasFocus()) {
+ QPainter::CompositionMode oldMode = p.compositionMode();
+ p.setCompositionMode(QPainter::RasterOp_NotDestination);
+ switch (cursor.shape) {
+ case Internal::Cursor::Shape::Block:
+ p.fillRect(cursorRect, p.pen().brush());
+ break;
+ case Internal::Cursor::Shape::Underline:
+ p.drawLine(cursorRect.bottomLeft(), cursorRect.bottomRight());
+ break;
+ case Internal::Cursor::Shape::LeftBar:
+ p.drawLine(cursorRect.topLeft(), cursorRect.bottomLeft());
+ break;
+ }
+ p.setCompositionMode(oldMode);
+ } else {
+ p.drawRect(cursorRect);
+ }
+ }
+}
+
+void TerminalWidget::paintPreedit(QPainter &p) const
+{
+ auto cursor = m_surface->cursor();
+ if (!m_preEditString.isEmpty()) {
+ QRectF rect = QRectF(gridToGlobal(cursor.position),
+ gridToGlobal({cursor.position.x(), cursor.position.y()}, true, true));
+
+ p.fillRect(rect, QColor::fromRgb(0, 0, 0));
+ p.setPen(Qt::white);
+ p.drawText(rect, m_preEditString);
+ }
+}
+
+void TerminalWidget::paintCells(QPainter &p, QPaintEvent *event) const
+{
+ QFont f = m_font;
+
+ const int scrollOffset = verticalScrollBar()->value();
+
+ const int maxRow = m_surface->fullSize().height();
+ const int startRow = qFloor((qreal) event->rect().y() / m_cellSize.height()) + scrollOffset;
+ const int endRow = qMin(maxRow,
+ qCeil((event->rect().y() + event->rect().height()) / m_cellSize.height())
+ + scrollOffset);
+
+ QList<SearchHit>::const_iterator searchIt
+ = std::lower_bound(m_search->hits().constBegin(),
+ m_search->hits().constEnd(),
+ startRow,
+ [this](const SearchHit &hit, int value) {
+ return m_surface->posToGrid(hit.start).y() < value;
+ });
+
+ for (int cellY = startRow; cellY < endRow; ++cellY) {
+ for (int cellX = 0; cellX < m_surface->liveSize().width();) {
+ const auto cell = m_surface->fetchCell(cellX, cellY);
+
+ QRectF cellRect(gridToGlobal({cellX, cellY}),
+ QSizeF{m_cellSize.width() * cell.width, m_cellSize.height()});
+
+ int numCells = paintCell(p, cellRect, {cellX, cellY}, cell, f, searchIt);
+
+ cellX += numCells;
+ }
+ }
+}
+
+void TerminalWidget::paintDebugSelection(QPainter &p, const Selection &selection) const
+{
+ auto s = globalToViewport(gridToGlobal(m_surface->posToGrid(selection.start)).toPoint());
+ const auto e = globalToViewport(
+ gridToGlobal(m_surface->posToGrid(selection.end), true).toPoint());
+
+ p.setPen(QPen(Qt::green, 1, Qt::DashLine));
+ p.drawLine(s.x(), 0, s.x(), height());
+ p.drawLine(0, s.y(), width(), s.y());
+
+ p.setPen(QPen(Qt::red, 1, Qt::DashLine));
+
+ p.drawLine(e.x(), 0, e.x(), height());
+ p.drawLine(0, e.y(), width(), e.y());
+}
+
+void TerminalWidget::paintEvent(QPaintEvent *event)
+{
+ QElapsedTimer t;
+ t.start();
+ event->accept();
+ QPainter p(viewport());
+
+ p.save();
+
+ if (paintLog().isDebugEnabled())
+ p.fillRect(event->rect(), QColor::fromRgb(rand() % 60, rand() % 60, rand() % 60));
+ else
+ p.fillRect(event->rect(), m_currentColors[ColorIndex::Background]);
+
+ int scrollOffset = verticalScrollBar()->value();
+ int offset = -(scrollOffset * m_cellSize.height());
+
+ qreal margin = topMargin();
+
+ p.translate(QPointF{0.0, offset + margin});
+
+ paintCells(p, event);
+ paintCursor(p);
+ paintPreedit(p);
+
+ p.restore();
+
+ p.fillRect(QRectF{{0, 0}, QSizeF{(qreal) width(), topMargin()}},
+ m_currentColors[ColorIndex::Background]);
+
+ if (selectionLog().isDebugEnabled()) {
+ if (m_selection)
+ paintDebugSelection(p, *m_selection);
+ if (m_linkSelection)
+ paintDebugSelection(p, *m_linkSelection);
+ }
+
+ if (paintLog().isDebugEnabled()) {
+ QToolTip::showText(this->mapToGlobal(QPoint(width() - 200, 0)),
+ QString("Paint: %1ms").arg(t.elapsed()));
+ }
+}
+
+void TerminalWidget::keyPressEvent(QKeyEvent *event)
+{
+ // Don't blink during typing
+ if (m_cursorBlinkTimer.isActive()) {
+ m_cursorBlinkTimer.start();
+ m_cursorBlinkState = true;
+ }
+
+ if (event->key() == Qt::Key_Escape) {
+ bool sendToTerminal = TerminalSettings::instance().sendEscapeToTerminal.value();
+ bool send = false;
+ if (sendToTerminal && event->modifiers() == Qt::NoModifier)
+ send = true;
+ else if (!sendToTerminal && event->modifiers() == Qt::ShiftModifier)
+ send = true;
+
+ if (send) {
+ event->setModifiers(Qt::NoModifier);
+ m_surface->sendKey(event);
+ return;
+ }
+
+ if (m_selection)
+ TerminalCommands::widgetActions().clearSelection.trigger();
+ else {
+ QTC_ASSERT(Core::ActionManager::command(Core::Constants::S_RETURNTOEDITOR), return);
+ Core::ActionManager::command(Core::Constants::S_RETURNTOEDITOR)->action()->trigger();
+ }
+ return;
+ }
+
+ auto oldSelection = m_selection;
+ if (TerminalCommands::triggerAction(event)) {
+ if (oldSelection && oldSelection == m_selection)
+ setSelection(std::nullopt);
+ return;
+ }
+
+ if (event->key() == Qt::Key_Control) {
+ if (!m_linkSelection.has_value() && checkLinkAt(mapFromGlobal(QCursor::pos()))) {
+ setCursor(Qt::PointingHandCursor);
+ }
+ }
+
+ event->accept();
+
+ m_surface->sendKey(event);
+}
+
+void TerminalWidget::keyReleaseEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Control && m_linkSelection.has_value()) {
+ m_linkSelection.reset();
+ updateCopyState();
+ setCursor(Qt::IBeamCursor);
+ updateViewport();
+ }
+}
+
+void TerminalWidget::applySizeChange()
+{
+ QSize newLiveSize = {
+ qFloor((qreal) (viewport()->size().width()) / (qreal) m_cellSize.width()),
+ qFloor((qreal) (viewport()->size().height()) / m_cellSize.height()),
+ };
+
+ if (newLiveSize.height() <= 0)
+ newLiveSize.setHeight(1);
+
+ if (newLiveSize.width() <= 0)
+ newLiveSize.setWidth(1);
+
+ if (m_process && m_process->ptyData())
+ m_process->ptyData()->resize(newLiveSize);
+
+ m_surface->resize(newLiveSize);
+ flushVTerm(true);
+}
+
+void TerminalWidget::updateScrollBars()
+{
+ int scrollSize = m_surface->fullSize().height() - m_surface->liveSize().height();
+ verticalScrollBar()->setRange(0, scrollSize);
+ verticalScrollBar()->setValue(verticalScrollBar()->maximum());
+ updateViewport();
+}
+
+void TerminalWidget::resizeEvent(QResizeEvent *event)
+{
+ event->accept();
+
+ // If increasing in size, we'll trigger libvterm to call sb_popline in
+ // order to pull lines out of the history. This will cause the scrollback
+ // to decrease in size which reduces the size of the verticalScrollBar.
+ // That will trigger a scroll offset increase which we want to ignore.
+ m_ignoreScroll = true;
+
+ applySizeChange();
+
+ setSelection(std::nullopt);
+ m_ignoreScroll = false;
+}
+
+QRect TerminalWidget::gridToViewport(QRect rect) const
+{
+ int offset = verticalScrollBar()->value();
+
+ int startRow = rect.y() - offset;
+ int numRows = rect.height();
+ int numCols = rect.width();
+
+ QRectF r{rect.x() * m_cellSize.width(),
+ startRow * m_cellSize.height(),
+ numCols * m_cellSize.width(),
+ numRows * m_cellSize.height()};
+
+ r.translate(0, topMargin());
+
+ return r.toAlignedRect();
+}
+
+void TerminalWidget::updateViewport()
+{
+ viewport()->update();
+}
+
+void TerminalWidget::updateViewportRect(const QRect &rect)
+{
+ viewport()->update(rect);
+}
+
+void TerminalWidget::wheelEvent(QWheelEvent *event)
+{
+ verticalScrollBar()->event(event);
+}
+
+void TerminalWidget::focusInEvent(QFocusEvent *)
+{
+ updateViewport();
+ configBlinkTimer();
+ updateCopyState();
+}
+void TerminalWidget::focusOutEvent(QFocusEvent *)
+{
+ updateViewport();
+ configBlinkTimer();
+}
+
+void TerminalWidget::inputMethodEvent(QInputMethodEvent *event)
+{
+ m_preEditString = event->preeditString();
+
+ if (event->commitString().isEmpty()) {
+ updateViewport();
+ return;
+ }
+
+ m_surface->sendKey(event->commitString());
+}
+
+void TerminalWidget::mousePressEvent(QMouseEvent *event)
+{
+ m_scrollDirection = 0;
+
+ m_activeMouseSelect.start = viewportToGlobal(event->pos());
+
+ if (event->button() == Qt::LeftButton && event->modifiers() & Qt::ControlModifier) {
+ if (m_linkSelection) {
+ if (event->modifiers() & Qt::ShiftModifier) {
+ copyLinkToClipboard();
+ return;
+ }
+
+ if (m_linkSelection->link.targetFilePath.scheme().toString().startsWith("http")) {
+ QDesktopServices::openUrl(m_linkSelection->link.targetFilePath.toUrl());
+ return;
+ }
+
+ if (m_linkSelection->link.targetFilePath.isDir())
+ Core::FileUtils::showInFileSystemView(m_linkSelection->link.targetFilePath);
+ else
+ Core::EditorManager::openEditorAt(m_linkSelection->link);
+ }
+ return;
+ }
+
+ if (event->button() == Qt::LeftButton) {
+ if (std::chrono::system_clock::now() - m_lastDoubleClick < 500ms) {
+ m_selectLineMode = true;
+ const Selection newSelection{m_surface->gridToPos(
+ {0, m_surface->posToGrid(m_selection->start).y()}),
+ m_surface->gridToPos(
+ {m_surface->liveSize().width(),
+ m_surface->posToGrid(m_selection->end).y()}),
+ false};
+ setSelection(newSelection);
+ } else {
+ m_selectLineMode = false;
+ int pos = m_surface->gridToPos(globalToGrid(viewportToGlobal(event->pos())));
+ setSelection(Selection{pos, pos, false});
+ }
+ event->accept();
+ updateViewport();
+ } else if (event->button() == Qt::RightButton) {
+ if (event->modifiers() & Qt::ShiftModifier) {
+ QMenu *contextMenu = new QMenu(this);
+ contextMenu->addAction(&TerminalCommands::widgetActions().copy);
+ contextMenu->addAction(&TerminalCommands::widgetActions().paste);
+ contextMenu->addAction(&TerminalCommands::widgetActions().copyLink);
+ contextMenu->addSeparator();
+ contextMenu->addAction(&TerminalCommands::widgetActions().clearTerminal);
+ contextMenu->addSeparator();
+ contextMenu->addAction(TerminalCommands::openSettingsAction());
+
+ contextMenu->popup(event->globalPos());
+ } else if (m_selection) {
+ copyToClipboard();
+ setSelection(std::nullopt);
+ } else {
+ pasteFromClipboard();
+ }
+ } else if (event->button() == Qt::MiddleButton) {
+ QClipboard *clipboard = QApplication::clipboard();
+ if (clipboard->supportsSelection()) {
+ const QString selectionText = clipboard->text(QClipboard::Selection);
+ if (!selectionText.isEmpty())
+ m_surface->pasteFromClipboard(selectionText);
+ } else {
+ m_surface->pasteFromClipboard(textFromSelection());
+ }
+ }
+}
+void TerminalWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_selection && event->buttons() & Qt::LeftButton) {
+ Selection newSelection = *m_selection;
+ int scrollVelocity = 0;
+ if (event->pos().y() < 0) {
+ scrollVelocity = (event->pos().y());
+ } else if (event->pos().y() > viewport()->height()) {
+ scrollVelocity = (event->pos().y() - viewport()->height());
+ }
+
+ if ((scrollVelocity != 0) != m_scrollTimer.isActive()) {
+ if (scrollVelocity != 0)
+ m_scrollTimer.start();
+ else
+ m_scrollTimer.stop();
+ }
+
+ m_scrollDirection = scrollVelocity;
+
+ if (m_scrollTimer.isActive() && scrollVelocity != 0) {
+ const std::chrono::milliseconds scrollInterval = 1000ms / qAbs(scrollVelocity);
+ if (m_scrollTimer.intervalAsDuration() != scrollInterval)
+ m_scrollTimer.setInterval(scrollInterval);
+ }
+
+ QPoint posBoundedToViewport = event->pos();
+ posBoundedToViewport.setX(qBound(0, posBoundedToViewport.x(), viewport()->width()));
+
+ int start = m_surface->gridToPos(globalToGrid(m_activeMouseSelect.start));
+ int newEnd = m_surface->gridToPos(globalToGrid(viewportToGlobal(posBoundedToViewport)));
+
+ if (start > newEnd) {
+ std::swap(start, newEnd);
+ }
+ if (start < 0)
+ start = 0;
+
+ if (m_selectLineMode) {
+ newSelection.start = m_surface->gridToPos({0, m_surface->posToGrid(start).y()});
+ newSelection.end = m_surface->gridToPos(
+ {m_surface->liveSize().width(), m_surface->posToGrid(newEnd).y()});
+ } else {
+ newSelection.start = start;
+ newSelection.end = newEnd;
+ }
+
+ setSelection(newSelection);
+ } else if (event->modifiers() & Qt::ControlModifier) {
+ checkLinkAt(event->pos());
+ } else if (m_linkSelection) {
+ m_linkSelection.reset();
+ updateCopyState();
+ updateViewport();
+ }
+
+ if (m_linkSelection) {
+ setCursor(Qt::PointingHandCursor);
+ } else {
+ setCursor(Qt::IBeamCursor);
+ }
+}
+
+bool TerminalWidget::checkLinkAt(const QPoint &pos)
+{
+ const TextAndOffsets hit = textAt(pos);
+
+ if (hit.text.size() > 0) {
+ QString t = QString::fromUcs4(hit.text.c_str(), hit.text.size()).trimmed();
+ t = chopIfEndsWith(t, ':');
+
+ if (!t.isEmpty()) {
+ if (t.startsWith("~/"))
+ t = QDir::homePath() + t.mid(1);
+
+ Link link = Link::fromString(t, true);
+
+ if (!link.targetFilePath.isEmpty() && !link.targetFilePath.isAbsolutePath())
+ link.targetFilePath = m_cwd.pathAppended(link.targetFilePath.path());
+
+ if (link.hasValidTarget()
+ && (link.targetFilePath.scheme().toString().startsWith("http")
+ || link.targetFilePath.exists())) {
+ const LinkSelection newSelection = LinkSelection{{hit.start, hit.end}, link};
+ if (!m_linkSelection || *m_linkSelection != newSelection) {
+ m_linkSelection = newSelection;
+ updateViewport();
+ updateCopyState();
+ }
+ return true;
+ }
+ }
+ }
+
+ if (m_linkSelection) {
+ m_linkSelection.reset();
+ updateCopyState();
+ updateViewport();
+ }
+ return false;
+}
+
+void TerminalWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ m_scrollTimer.stop();
+
+ if (m_selection && event->button() == Qt::LeftButton) {
+ if (m_selection->end - m_selection->start == 0)
+ setSelection(std::nullopt);
+ else
+ setSelection(Selection{m_selection->start, m_selection->end, true});
+ }
+}
+
+TerminalWidget::TextAndOffsets TerminalWidget::textAt(const QPoint &pos) const
+{
+ auto it = m_surface->iteratorAt(globalToGrid(viewportToGlobal(pos)));
+ auto itRev = m_surface->rIteratorAt(globalToGrid(viewportToGlobal(pos)));
+
+ std::u32string whiteSpaces = U" \t\x00a0";
+
+ const bool inverted = whiteSpaces.find(*it) != std::u32string::npos || *it == 0;
+
+ auto predicate = [inverted, whiteSpaces](const std::u32string::value_type &ch) {
+ if (inverted)
+ return ch != 0 && whiteSpaces.find(ch) == std::u32string::npos;
+ else
+ return ch == 0 || whiteSpaces.find(ch) != std::u32string::npos;
+ };
+
+ auto itRight = std::find_if(it, m_surface->end(), predicate);
+ auto itLeft = std::find_if(itRev, m_surface->rend(), predicate);
+
+ std::u32string text;
+ std::copy(itLeft.base(), it, std::back_inserter(text));
+ std::copy(it, itRight, std::back_inserter(text));
+ std::transform(text.begin(), text.end(), text.begin(), [](const char32_t &ch) {
+ return ch == 0 ? U' ' : ch;
+ });
+
+ return {(itLeft.base()).position(), itRight.position(), text};
+}
+
+void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ const auto hit = textAt(event->pos());
+
+ setSelection(Selection{hit.start, hit.end, true});
+
+ m_lastDoubleClick = std::chrono::system_clock::now();
+
+ event->accept();
+}
+
+void TerminalWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->hasUrls()) {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ }
+}
+
+void TerminalWidget::dropEvent(QDropEvent *event)
+{
+ QString urls = Utils::transform(event->mimeData()->urls(), [](const QUrl &url) {
+ return QString("\"%1\"").arg(url.toDisplayString(QUrl::PreferLocalFile));
+ }).join(" ");
+
+ writeToPty(urls.toUtf8());
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+}
+
+void TerminalWidget::showEvent(QShowEvent *event)
+{
+ Q_UNUSED(event);
+
+ if (!m_process)
+ setupPty();
+
+ QAbstractScrollArea::showEvent(event);
+}
+
+bool TerminalWidget::event(QEvent *event)
+{
+ if (event->type() == QEvent::ShortcutOverride) {
+ if (hasFocus()) {
+ event->accept();
+ return true;
+ }
+ }
+
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *k = (QKeyEvent *) event;
+ keyPressEvent(k);
+ return true;
+ }
+ if (event->type() == QEvent::KeyRelease) {
+ QKeyEvent *k = (QKeyEvent *) event;
+ keyReleaseEvent(k);
+ return true;
+ }
+
+ if (event->type() == QEvent::Paint) {
+ QPainter p(this);
+ p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]);
+ return true;
+ }
+
+ return QAbstractScrollArea::event(event);
+}
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h
new file mode 100644
index 0000000000..f44a721bd3
--- /dev/null
+++ b/src/plugins/terminal/terminalwidget.h
@@ -0,0 +1,231 @@
+// 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
+
+#pragma once
+
+#include "terminalsearch.h"
+#include "terminalsurface.h"
+
+#include <aggregation/aggregate.h>
+
+#include <utils/link.h>
+#include <utils/process.h>
+#include <utils/terminalhooks.h>
+
+#include <QAbstractScrollArea>
+#include <QAction>
+#include <QTextLayout>
+#include <QTimer>
+
+#include <chrono>
+#include <memory>
+
+namespace Terminal {
+
+class TerminalWidget : public QAbstractScrollArea
+{
+ friend class CellIterator;
+ Q_OBJECT
+public:
+ TerminalWidget(QWidget *parent = nullptr,
+ const Utils::Terminal::OpenTerminalParameters &openParameters = {});
+
+ void setFont(const QFont &font);
+
+ void copyToClipboard();
+ void pasteFromClipboard();
+ void copyLinkToClipboard();
+
+ void clearSelection();
+
+ void zoomIn();
+ void zoomOut();
+
+ void moveCursorWordLeft();
+ void moveCursorWordRight();
+
+ void clearContents();
+
+ TerminalSearch *search() { return m_search.get(); }
+
+ struct Selection
+ {
+ int start;
+ int end;
+ bool final{false};
+
+ bool operator!=(const Selection &other) const
+ {
+ return start != other.start || end != other.end || final != other.final;
+ }
+
+ bool operator==(const Selection &other) const { return !operator!=(other); }
+ };
+
+ struct LinkSelection : public Selection
+ {
+ Utils::Link link;
+
+ bool operator!=(const LinkSelection &other) const
+ {
+ return link != other.link || Selection::operator!=(other);
+ }
+ };
+
+ void setShellName(const QString &shellName);
+ QString shellName() const;
+
+ Utils::FilePath cwd() const;
+ Utils::CommandLine currentCommand() const;
+ std::optional<Utils::Id> identifier() const;
+ QProcess::ProcessState processState() const;
+
+ void restart(const Utils::Terminal::OpenTerminalParameters &openParameters);
+
+signals:
+ void started(qint64 pid);
+ void cwdChanged(const Utils::FilePath &cwd);
+ void commandChanged(const Utils::CommandLine &cmd);
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+ void inputMethodEvent(QInputMethodEvent *event) override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+
+ void showEvent(QShowEvent *event) override;
+
+ bool event(QEvent *event) override;
+
+protected:
+ void onReadyRead(bool forceFlush);
+ void setupSurface();
+ void setupFont();
+ void setupPty();
+ void setupColors();
+ void setupActions();
+
+ void writeToPty(const QByteArray &data);
+
+ int paintCell(QPainter &p,
+ const QRectF &cellRect,
+ QPoint gridPos,
+ const Internal::TerminalCell &cell,
+ QFont &f,
+ QList<SearchHit>::const_iterator &searchIt) const;
+ void paintCells(QPainter &painter, QPaintEvent *event) const;
+ void paintCursor(QPainter &painter) const;
+ void paintPreedit(QPainter &painter) const;
+ bool paintFindMatches(QPainter &painter,
+ QList<SearchHit>::const_iterator &searchIt,
+ const QRectF &cellRect,
+ const QPoint gridPos) const;
+ bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const;
+ void paintDebugSelection(QPainter &painter, const Selection &selection) const;
+
+ qreal topMargin() const;
+
+ QPoint viewportToGlobal(QPoint p) const;
+ QPoint globalToViewport(QPoint p) const;
+ QPoint globalToGrid(QPointF p) const;
+ QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const;
+ QRect gridToViewport(QRect rect) const;
+
+ void updateViewport();
+ void updateViewportRect(const QRect &rect);
+
+ int textLineFromPixel(int y) const;
+ std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const;
+
+ std::optional<QTextLayout::FormatRange> selectionToFormatRange(
+ TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const;
+
+ bool checkLinkAt(const QPoint &pos);
+
+ struct TextAndOffsets
+ {
+ int start;
+ int end;
+ std::u32string text;
+ };
+
+ TextAndOffsets textAt(const QPoint &pos) const;
+
+ void applySizeChange();
+ void updateScrollBars();
+
+ void flushVTerm(bool force);
+
+ bool setSelection(const std::optional<Selection> &selection, bool scroll = true);
+ QString textFromSelection() const;
+
+ void configBlinkTimer();
+
+ QColor toQColor(std::variant<int, QColor> color) const;
+
+ void updateCopyState();
+
+private:
+ std::unique_ptr<Utils::Process> m_process;
+ std::unique_ptr<Internal::TerminalSurface> m_surface;
+ std::unique_ptr<ShellIntegration> m_shellIntegration;
+
+ QString m_shellName;
+ Utils::Id m_identifier;
+
+ QFont m_font;
+ QSizeF m_cellSize;
+
+ bool m_ignoreScroll{false};
+
+ QString m_preEditString;
+
+ std::optional<Selection> m_selection;
+ std::optional<LinkSelection> m_linkSelection;
+
+ struct
+ {
+ QPoint start;
+ QPoint end;
+ } m_activeMouseSelect;
+
+ QTimer m_flushDelayTimer;
+
+ QTimer m_scrollTimer;
+ int m_scrollDirection{0};
+
+ std::array<QColor, 20> m_currentColors;
+
+ Utils::Terminal::OpenTerminalParameters m_openParameters;
+
+ std::chrono::system_clock::time_point m_lastFlush;
+ std::chrono::system_clock::time_point m_lastDoubleClick;
+ bool m_selectLineMode{false};
+
+ Internal::Cursor m_cursor;
+ QTimer m_cursorBlinkTimer;
+ bool m_cursorBlinkState{true};
+
+ Utils::FilePath m_cwd;
+ Utils::CommandLine m_currentCommand;
+
+ using TerminalSearchPtr = std::unique_ptr<TerminalSearch, std::function<void(TerminalSearch *)>>;
+ TerminalSearchPtr m_search;
+
+ Aggregation::Aggregate *m_aggregate{nullptr};
+ SearchHit m_lastSelectedHit{};
+};
+
+} // namespace Terminal
diff --git a/src/plugins/terminal/tests/colors b/src/plugins/terminal/tests/colors
new file mode 100755
index 0000000000..a1910d45cc
--- /dev/null
+++ b/src/plugins/terminal/tests/colors
@@ -0,0 +1,112 @@
+#!/usr/bin/python3
+# Source: https://gist.github.com/lilydjwg/fdeaf79e921c2f413f44b6f613f6ad53
+
+from functools import partial
+
+
+def colors16():
+ for bold in [0, 1]:
+ for i in range(30, 38):
+ for j in range(40, 48):
+ print(f'\x1b[{bold};{i};{j}m {bold};{i};{j} |\x1b[0m', end='')
+ print()
+ print()
+
+ for bold in [0, 1]:
+ for i in range(90, 98):
+ for j in range(100, 108):
+ print(f'\x1b[{bold};{i};{j}m {bold};{i};{j} |\x1b[0m', end='')
+ print()
+ print()
+
+
+def color1(c, n=0):
+ print(f'\x1b[{n};38;5;{c}m{c:4}\x1b[0m', end='')
+
+
+def color1_sep(c):
+ if (c - 15) % 18 == 0:
+ print()
+
+
+def color2(c):
+ print(f'\x1b[48;5;{c}m \x1b[0m', end='')
+
+
+def color2_sep(c):
+ if (c - 15) % 36 == 0:
+ print()
+ elif (c - 15) % 6 == 0:
+ print(' ', end='')
+
+
+def colors256(color, sepfunc):
+ for i in range(0, 8):
+ color(i)
+ print()
+ for i in range(8, 16):
+ color(i)
+ print('\n')
+
+ for i in range(16, 232):
+ color(i)
+ sepfunc(i)
+ print()
+
+ for i in range(232, 256):
+ color(i)
+ print('\n')
+
+
+def colors_gradient():
+ s = '/\\' * 40
+ for col in range(0, 77):
+ r = 255 - col * 255 // 76
+ g = col * 510 // 76
+ b = col * 255 // 76
+ if g > 255:
+ g = 510 - g
+ print(
+ f'\x1b[48;2;{r};{g};{b}m\x1b[38;2;{255-r};{255-g};{255-b}m{s[col]}\x1b[0m', end='')
+ print()
+
+
+def other_attributes():
+ for i in range(0, 10):
+ print(f' \x1b[{i}mSGR {i:2}\x1b[m', end=' ')
+ print(' \x1b[53mSGR 53\x1b[m', end=' ') # overline
+ print('\n')
+ # https://askubuntu.com/a/985386/235132
+ for i in range(1, 6):
+ print(f' \x1b[4:{i}mSGR 4:{i}\x1b[m', end=' ')
+ print(' \x1b[21mSGR 21\x1b[m', end=' ')
+
+ print(
+ ' \x1b[4:3m\x1b[58;2;135;0;255mtruecolor underline\x1b[59m\x1b[4:0m', end=' ')
+ print(' \x1b]8;;https://askubuntu.com/a/985386/235132\x1b\\hyperlink\x1b]8;;\x1b\\')
+
+
+if __name__ == '__main__':
+ print('basic 16 colors, foreground & background:\n')
+ colors16()
+
+ print('256 colors:\n')
+ colors256(color1, color1_sep)
+
+ print('256 colors, bold:\n')
+ colors256(partial(color1, n=1), color1_sep)
+
+ print('256 colors, dim:\n')
+ colors256(partial(color1, n=2), color1_sep)
+
+ print('256 colors, bold dim:\n')
+ colors256(partial(color1, n='1;2'), color1_sep)
+
+ print('256 colors, solid background:\n')
+ colors256(color2, color2_sep)
+
+ print('true colors gradient:\n')
+ colors_gradient()
+
+ print('other attributes:\n')
+ other_attributes()
diff --git a/src/plugins/terminal/tests/cursor/bar b/src/plugins/terminal/tests/cursor/bar
new file mode 100755
index 0000000000..a7bd99b55d
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/bar
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x36 q" # Steady bar
diff --git a/src/plugins/terminal/tests/cursor/blinkbar b/src/plugins/terminal/tests/cursor/blinkbar
new file mode 100755
index 0000000000..0acf6179d9
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/blinkbar
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x35 q" # Blinking bar
diff --git a/src/plugins/terminal/tests/cursor/blinkblock b/src/plugins/terminal/tests/cursor/blinkblock
new file mode 100755
index 0000000000..c536c83b8b
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/blinkblock
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x31 q" # Blinking block (default)
diff --git a/src/plugins/terminal/tests/cursor/blinkunderline b/src/plugins/terminal/tests/cursor/blinkunderline
new file mode 100755
index 0000000000..745d9e2a29
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/blinkunderline
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x33 q" # Blinking underline
diff --git a/src/plugins/terminal/tests/cursor/block b/src/plugins/terminal/tests/cursor/block
new file mode 100755
index 0000000000..421df3b8c5
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/block
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x32 q" # Steady block
diff --git a/src/plugins/terminal/tests/cursor/underline b/src/plugins/terminal/tests/cursor/underline
new file mode 100755
index 0000000000..7638b5358d
--- /dev/null
+++ b/src/plugins/terminal/tests/cursor/underline
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo -e -n "\x1b[\x34 q" # Steady underline
diff --git a/src/plugins/terminal/tests/decoration b/src/plugins/terminal/tests/decoration
new file mode 100755
index 0000000000..a584d46092
--- /dev/null
+++ b/src/plugins/terminal/tests/decoration
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+printf "\e[1mbold\e[0m\n"
+printf "\e[3mitalic\e[0m\n"
+printf "\e[3m\e[1mbold italic\e[0m\n"
+printf "\e[4munderline (abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ)\e[0m\n"
+printf "\e[9mstrikethrough\e[0m\n"
+printf "\e[31mHello World\e[0m\n"
+printf "\x1B[31mHello World\e[0m\n"
diff --git a/src/plugins/terminal/tests/filenames b/src/plugins/terminal/tests/filenames
new file mode 100755
index 0000000000..6a4e33e3ed
--- /dev/null
+++ b/src/plugins/terminal/tests/filenames
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+echo "Home:"
+echo "~/"
+
+FULLPATH=$(readlink -f "$0")
+
+echo "This file:"
+echo "$FULLPATH"
+
+echo "This file, this line:"
+echo "$FULLPATH:11"
+
+echo "This file, this line, this word:"
+echo "$FULLPATH:14:34"
+
+echo "This file, with an error message:"
+echo "$FULLPATH:18:23: error: C++ requires a type specifier for all declarations"
+
+echo "A link: http://google.com"
+echo "Another link: https://www.qt.io"
+echo "Another one: https://codereview.qt-project.org/c/qt-creator/qt-creator/+/464740"
diff --git a/src/plugins/terminal/tests/integration b/src/plugins/terminal/tests/integration
new file mode 100755
index 0000000000..ac17432cb6
--- /dev/null
+++ b/src/plugins/terminal/tests/integration
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+echo "Testing integration response, best start this from a terminal that has no builtin integration"
+echo "e.g. 'sh'"
+echo
+
+echo -e "\033[1m ⎆ Current dir should have changed to '/Some/Dir/Here'\033[0m"
+printf "\033]7;file:///Some/Dir/Here\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ Current dir should have changed to '/Some/Other/Dir/Here'\033[0m"
+printf "\033]1337;CurrentDir=/Some/Other/Dir/Here\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+
+echo -e "\033[1m ⎆ Current dir should have changed to '/VSCode/dir/with space'\033[0m"
+printf "\033]633P;Cwd=/VSCode/dir/with space\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test'\033[0m"
+printf "\033]633E;test with arguments\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test with space'\033[0m"
+printf "\033]633E;'test with space'\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test with space v2'\033[0m"
+printf "\033]633E;\"test with space v2\"\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'test with space v3'\033[0m"
+printf "\033]633E;\"./test/test with space v3\" -argument\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'cat'\033[0m"
+printf "\033]633E;cat /dev/random | base64 -argument\033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
+echo -e "\033[1m ⎆ The current process should have changed to 'cat me'\033[0m"
+printf "\033]633E;cat\\ me args \033\\"
+
+read -p " ⎆ Press enter to continue " -n1 -s
+echo
+echo
+
diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt
index 01636fe754..eebe8bf0be 100644
--- a/src/plugins/texteditor/CMakeLists.txt
+++ b/src/plugins/texteditor/CMakeLists.txt
@@ -70,6 +70,7 @@ add_qtc_plugin(TextEditor
ioutlinewidget.h
linenumberfilter.cpp linenumberfilter.h
marginsettings.cpp marginsettings.h
+ markdowneditor.cpp markdowneditor.h
outlinefactory.cpp outlinefactory.h
plaintexteditorfactory.cpp plaintexteditorfactory.h
quickfix.cpp quickfix.h
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index 29cccc6c10..974f147291 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -51,9 +51,8 @@ public:
QVariant parameters() const override { return {}; }
void readSettings(QSettings * /*settings*/) override {}
void writeSettings(QSettings * /*settings*/) const override {}
- QFuture<Utils::FileSearchResultList> executeSearch(
- const TextEditor::FileFindParameters &parameters,
- BaseFileFind *baseFileFind) override
+ QFuture<SearchResultItems> executeSearch(const TextEditor::FileFindParameters &parameters,
+ BaseFileFind *baseFileFind) override
{
const auto func = parameters.flags & FindRegularExpression ? Utils::findInFilesRegExp
: Utils::findInFiles;
@@ -65,7 +64,7 @@ public:
TextDocument::openedTextDocumentContents());
}
- Core::IEditor *openEditor(const Core::SearchResultItem &/*item*/,
+ Core::IEditor *openEditor(const SearchResultItem &/*item*/,
const TextEditor::FileFindParameters &/*parameters*/) override
{
return nullptr;
@@ -91,8 +90,6 @@ public:
class BaseFileFindPrivate
{
public:
- BaseFileFindPrivate() { m_futureSynchronizer.setCancelOnWait(true); }
-
QPointer<IFindSupport> m_currentFindSupport;
Utils::FutureSynchronizer m_futureSynchronizer;
@@ -210,34 +207,6 @@ void BaseFileFind::setCurrentSearchEngine(int index)
emit currentSearchEngineChanged();
}
-static QString displayText(const QString &line)
-{
- QString result = line;
- auto end = result.end();
- for (auto it = result.begin(); it != end; ++it) {
- if (!it->isSpace() && !it->isPrint())
- *it = QChar('?');
- }
- return result;
-}
-
-static void displayResult(QFutureWatcher<FileSearchResultList> *watcher,
- SearchResult *search, int index)
-{
- const FileSearchResultList results = watcher->resultAt(index);
- QList<SearchResultItem> items;
- for (const FileSearchResult &result : results) {
- SearchResultItem item;
- item.setFilePath(result.fileName);
- item.setMainRange(result.lineNumber, result.matchStart, result.matchLength);
- item.setLineText(displayText(result.matchingLine));
- item.setUseTextEditorFont(true);
- item.setUserData(result.regexpCapturedTexts);
- items << item;
- }
- search->addResults(items, SearchResult::AddOrdered);
-}
-
void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
SearchResultWindow::SearchMode searchMode)
{
@@ -285,7 +254,7 @@ void BaseFileFind::runSearch(SearchResult *search)
{
const FileFindParameters parameters = search->userData().value<FileFindParameters>();
SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch|IOutputPane::WithFocus));
- auto watcher = new QFutureWatcher<FileSearchResultList>();
+ auto watcher = new QFutureWatcher<SearchResultItems>;
watcher->setPendingResultsLimit(1);
// search is deleted if it is removed from search panel
connect(search, &QObject::destroyed, watcher, &QFutureWatcherBase::cancel);
@@ -295,14 +264,14 @@ void BaseFileFind::runSearch(SearchResult *search)
watcher->setPaused(paused);
});
connect(watcher, &QFutureWatcherBase::resultReadyAt, search, [watcher, search](int index) {
- displayResult(watcher, search, index);
+ search->addResults(watcher->resultAt(index), SearchResult::AddOrdered);
});
// auto-delete:
connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
connect(watcher, &QFutureWatcherBase::finished, search, [watcher, search]() {
search->finishSearch(watcher->isCanceled());
});
- QFuture<FileSearchResultList> future = executeSearch(parameters);
+ QFuture<SearchResultItems> future = executeSearch(parameters);
watcher->setFuture(future);
d->m_futureSynchronizer.addFuture(future);
FutureProgress *progress = ProgressManager::addTask(future,
@@ -332,8 +301,7 @@ void BaseFileFind::addSearchEngine(SearchEngine *searchEngine)
setCurrentSearchEngine(0);
}
-void BaseFileFind::doReplace(const QString &text,
- const QList<SearchResultItem> &items,
+void BaseFileFind::doReplace(const QString &text, const SearchResultItems &items,
bool preserveCase)
{
const FilePaths files = replaceAll(text, items, preserveCase);
@@ -474,8 +442,7 @@ void BaseFileFind::recheckEnabled(SearchResult *search)
search->setSearchAgainEnabled(isEnabled());
}
-FilePaths BaseFileFind::replaceAll(const QString &text,
- const QList<SearchResultItem> &items,
+FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems &items,
bool preserveCase)
{
if (items.isEmpty())
@@ -483,7 +450,7 @@ FilePaths BaseFileFind::replaceAll(const QString &text,
RefactoringChanges refactoring;
- QHash<FilePath, QList<SearchResultItem> > changes;
+ QHash<FilePath, SearchResultItems> changes;
for (const SearchResultItem &item : items)
changes[FilePath::fromUserInput(item.path().constFirst())].append(item);
@@ -504,7 +471,7 @@ FilePaths BaseFileFind::replaceAll(const QString &text,
for (auto it = changes.cbegin(), end = changes.cend(); it != end; ++it) {
const FilePath filePath = it.key();
- const QList<SearchResultItem> changeItems = it.value();
+ const SearchResultItems changeItems = it.value();
ChangeSet changeSet;
RefactoringFilePtr file = refactoring.file(filePath);
@@ -519,9 +486,13 @@ FilePaths BaseFileFind::replaceAll(const QString &text,
if (item.userData().canConvert<QStringList>() && !item.userData().toStringList().isEmpty()) {
replacement = Utils::expandRegExpReplacement(text, item.userData().toStringList());
} else if (preserveCase) {
- const QString originalText = (item.mainRange().length(item.lineText()) == 0)
- ? item.lineText()
- : item.mainRange().mid(item.lineText());
+ Text::Range range = item.mainRange();
+ range.end.line -= range.begin.line - 1;
+ range.begin.line = 1;
+ QString originalText = item.lineText();
+ const int rangeLength = range.length(item.lineText());
+ if (rangeLength > 0)
+ originalText = originalText.mid(range.begin.column, rangeLength);
replacement = Utils::matchCaseReplacement(originalText, text);
} else {
replacement = text;
@@ -545,7 +516,7 @@ QVariant BaseFileFind::getAdditionalParameters(SearchResult *search)
return search->userData().value<FileFindParameters>().additionalParameters;
}
-QFuture<FileSearchResultList> BaseFileFind::executeSearch(const FileFindParameters &parameters)
+QFuture<SearchResultItems> BaseFileFind::executeSearch(const FileFindParameters &parameters)
{
return d->m_searchEngines[parameters.searchEngineIndex]->executeSearch(parameters, this);
}
diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h
index 7181197814..b564b35c0c 100644
--- a/src/plugins/texteditor/basefilefind.h
+++ b/src/plugins/texteditor/basefilefind.h
@@ -4,20 +4,24 @@
#pragma once
#include "texteditor_global.h"
-#include <utils/filesearch.h>
#include <coreplugin/find/ifindfilter.h>
#include <coreplugin/find/searchresultwindow.h>
+#include <utils/filesearch.h>
+#include <utils/searchresultitem.h>
+
#include <QFuture>
-namespace Utils { class FileIterator; }
namespace Core {
class IEditor;
class SearchResult;
-class SearchResultItem;
} // namespace Core
+namespace Utils {
+class FileIterator;
+}
+
namespace TextEditor {
namespace Internal {
@@ -52,9 +56,9 @@ public:
virtual QVariant parameters() const = 0;
virtual void readSettings(QSettings *settings) = 0;
virtual void writeSettings(QSettings *settings) const = 0;
- virtual QFuture<Utils::FileSearchResultList> executeSearch(
+ virtual QFuture<Utils::SearchResultItems> executeSearch(
const FileFindParameters &parameters, BaseFileFind *baseFileFind) = 0;
- virtual Core::IEditor *openEditor(const Core::SearchResultItem &item,
+ virtual Core::IEditor *openEditor(const Utils::SearchResultItem &item,
const FileFindParameters &parameters) = 0;
bool isEnabled() const;
void setEnabled(bool enabled);
@@ -81,8 +85,7 @@ public:
void addSearchEngine(SearchEngine *searchEngine);
/* returns the list of unique files that were passed in items */
- static Utils::FilePaths replaceAll(const QString &txt,
- const QList<Core::SearchResultItem> &items,
+ static Utils::FilePaths replaceAll(const QString &txt, const Utils::SearchResultItems &items,
bool preserveCase = false);
virtual Utils::FileIterator *files(const QStringList &nameFilters,
const QStringList &exclusionFilters,
@@ -94,7 +97,7 @@ protected:
virtual QString label() const = 0; // see Core::SearchResultWindow::startNewSearch
virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch,
// add %1 placeholder where the find flags should be put
- QFuture<Utils::FileSearchResultList> executeSearch(const FileFindParameters &parameters);
+ QFuture<Utils::SearchResultItems> executeSearch(const FileFindParameters &parameters);
void writeCommonSettings(QSettings *settings);
void readCommonSettings(QSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter);
@@ -111,10 +114,8 @@ signals:
void currentSearchEngineChanged();
private:
- void openEditor(Core::SearchResult *result, const Core::SearchResultItem &item);
- void doReplace(const QString &txt,
- const QList<Core::SearchResultItem> &items,
- bool preserveCase);
+ void openEditor(Core::SearchResult *result, const Utils::SearchResultItem &item);
+ void doReplace(const QString &txt, const Utils::SearchResultItems &items, bool preserveCase);
void hideHighlightAll(bool visible);
void searchAgain(Core::SearchResult *search);
virtual void recheckEnabled(Core::SearchResult *search);
diff --git a/src/plugins/texteditor/basehoverhandler.h b/src/plugins/texteditor/basehoverhandler.h
index 9b6d90fd89..c24ae4a1df 100644
--- a/src/plugins/texteditor/basehoverhandler.h
+++ b/src/plugins/texteditor/basehoverhandler.h
@@ -39,7 +39,8 @@ protected:
Priority_None = 0,
Priority_Tooltip = 5,
Priority_Help = 10,
- Priority_Diagnostic = 20
+ Priority_Diagnostic = 20,
+ Priority_Suggestion = 40
};
void setPriority(int priority);
int priority() const;
diff --git a/src/plugins/texteditor/behaviorsettingspage.cpp b/src/plugins/texteditor/behaviorsettingspage.cpp
index 5d39745807..73675e2cdb 100644
--- a/src/plugins/texteditor/behaviorsettingspage.cpp
+++ b/src/plugins/texteditor/behaviorsettingspage.cpp
@@ -10,6 +10,7 @@
#include "simplecodestylepreferences.h"
#include "storagesettings.h"
#include "tabsettings.h"
+#include "tabsettingswidget.h"
#include "texteditorconstants.h"
#include "texteditorsettings.h"
#include "texteditortr.h"
@@ -35,12 +36,12 @@
namespace TextEditor {
-struct BehaviorSettingsPage::BehaviorSettingsPagePrivate : public QObject
+class BehaviorSettingsPagePrivate : public QObject
{
+public:
BehaviorSettingsPagePrivate();
const QString m_settingsPrefix{"text"};
- QPointer<QWidget> m_widget;
TextEditor::BehaviorSettingsWidget *m_behaviorWidget = nullptr;
CodeStylePool *m_defaultCodeStylePool = nullptr;
@@ -52,7 +53,7 @@ struct BehaviorSettingsPage::BehaviorSettingsPagePrivate : public QObject
ExtraEncodingSettings m_extraEncodingSettings;
};
-BehaviorSettingsPage::BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate()
+BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate()
{
// global tab preferences for all other languages
m_codeStyle = new SimpleCodeStylePreferences(this);
@@ -71,38 +72,22 @@ BehaviorSettingsPage::BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate()
m_extraEncodingSettings.fromSettings(m_settingsPrefix, s);
}
-BehaviorSettingsPage::BehaviorSettingsPage()
- : d(new BehaviorSettingsPagePrivate)
+class BehaviorSettingsWidgetImpl : public Core::IOptionsPageWidget
{
- // Add the GUI used to configure the tab, storage and interaction settings
- setId(Constants::TEXT_EDITOR_BEHAVIOR_SETTINGS);
- setDisplayName(Tr::tr("Behavior"));
-
- setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
- setDisplayCategory(Tr::tr("Text Editor"));
- setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
-}
-
-BehaviorSettingsPage::~BehaviorSettingsPage()
-{
- delete d;
-}
-
-QWidget *BehaviorSettingsPage::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new QWidget;
- d->m_behaviorWidget = new BehaviorSettingsWidget(d->m_widget);
+public:
+ BehaviorSettingsWidgetImpl(BehaviorSettingsPagePrivate *d) : d(d)
+ {
+ d->m_behaviorWidget = new BehaviorSettingsWidget(this);
auto verticalSpacer = new QSpacerItem(20, 13, QSizePolicy::Minimum, QSizePolicy::Expanding);
- auto gridLayout = new QGridLayout(d->m_widget);
+ auto gridLayout = new QGridLayout(this);
if (Utils::HostOsInfo::isMacHost())
gridLayout->setContentsMargins(-1, 0, -1, 0); // don't ask.
gridLayout->addWidget(d->m_behaviorWidget, 0, 0, 1, 1);
gridLayout->addItem(verticalSpacer, 1, 0, 1, 1);
- d->m_pageCodeStyle = new SimpleCodeStylePreferences(d->m_widget);
+ d->m_pageCodeStyle = new SimpleCodeStylePreferences(this);
d->m_pageCodeStyle->setDelegatingPool(d->m_codeStyle->delegatingPool());
d->m_pageCodeStyle->setTabSettings(d->m_codeStyle->tabSettings());
d->m_pageCodeStyle->setCurrentDelegate(d->m_codeStyle->currentDelegate());
@@ -111,14 +96,49 @@ QWidget *BehaviorSettingsPage::widget()
TabSettingsWidget *tabSettingsWidget = d->m_behaviorWidget->tabSettingsWidget();
tabSettingsWidget->setCodingStyleWarningVisible(true);
connect(tabSettingsWidget, &TabSettingsWidget::codingStyleLinkClicked,
- this, &BehaviorSettingsPage::openCodingStylePreferences);
-
- settingsToUI();
+ this, [] (TabSettingsWidget::CodingStyleLink link) {
+ switch (link) {
+ case TabSettingsWidget::CppLink:
+ Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CODE_STYLE_SETTINGS_ID);
+ break;
+ case TabSettingsWidget::QtQuickLink:
+ Core::ICore::showOptionsDialog(QmlJSTools::Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
+ break;
+ }
+ });
+
+ d->m_behaviorWidget->setAssignedTypingSettings(d->m_typingSettings);
+ d->m_behaviorWidget->setAssignedStorageSettings(d->m_storageSettings);
+ d->m_behaviorWidget->setAssignedBehaviorSettings(d->m_behaviorSettings);
+ d->m_behaviorWidget->setAssignedExtraEncodingSettings(d->m_extraEncodingSettings);
+ d->m_behaviorWidget->setAssignedCodec(Core::EditorManager::defaultTextCodec());
+ d->m_behaviorWidget->setAssignedLineEnding(Core::EditorManager::defaultLineEnding());
}
- return d->m_widget;
+
+ void apply() final;
+
+ BehaviorSettingsPagePrivate *d;
+};
+
+BehaviorSettingsPage::BehaviorSettingsPage()
+ : d(new BehaviorSettingsPagePrivate)
+{
+ // Add the GUI used to configure the tab, storage and interaction settings
+ setId(Constants::TEXT_EDITOR_BEHAVIOR_SETTINGS);
+ setDisplayName(Tr::tr("Behavior"));
+
+ setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
+ setDisplayCategory(Tr::tr("Text Editor"));
+ setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
+ setWidgetCreator([this] { return new BehaviorSettingsWidgetImpl(d); });
}
-void BehaviorSettingsPage::apply()
+BehaviorSettingsPage::~BehaviorSettingsPage()
+{
+ delete d;
+}
+
+void BehaviorSettingsWidgetImpl::apply()
{
if (!d->m_behaviorWidget) // page was never shown
return;
@@ -128,8 +148,10 @@ void BehaviorSettingsPage::apply()
BehaviorSettings newBehaviorSettings;
ExtraEncodingSettings newExtraEncodingSettings;
- settingsFromUI(&newTypingSettings, &newStorageSettings,
- &newBehaviorSettings, &newExtraEncodingSettings);
+ d->m_behaviorWidget->assignedTypingSettings(&newTypingSettings);
+ d->m_behaviorWidget->assignedStorageSettings(&newStorageSettings);
+ d->m_behaviorWidget->assignedBehaviorSettings(&newBehaviorSettings);
+ d->m_behaviorWidget->assignedExtraEncodingSettings(&newExtraEncodingSettings);
QSettings *s = Core::ICore::settings();
QTC_ASSERT(s, return);
@@ -178,32 +200,6 @@ void BehaviorSettingsPage::apply()
d->m_behaviorWidget->assignedLineEnding());
}
-void BehaviorSettingsPage::settingsFromUI(TypingSettings *typingSettings,
- StorageSettings *storageSettings,
- BehaviorSettings *behaviorSettings,
- ExtraEncodingSettings *extraEncodingSettings) const
-{
- d->m_behaviorWidget->assignedTypingSettings(typingSettings);
- d->m_behaviorWidget->assignedStorageSettings(storageSettings);
- d->m_behaviorWidget->assignedBehaviorSettings(behaviorSettings);
- d->m_behaviorWidget->assignedExtraEncodingSettings(extraEncodingSettings);
-}
-
-void BehaviorSettingsPage::settingsToUI()
-{
- d->m_behaviorWidget->setAssignedTypingSettings(d->m_typingSettings);
- d->m_behaviorWidget->setAssignedStorageSettings(d->m_storageSettings);
- d->m_behaviorWidget->setAssignedBehaviorSettings(d->m_behaviorSettings);
- d->m_behaviorWidget->setAssignedExtraEncodingSettings(d->m_extraEncodingSettings);
- d->m_behaviorWidget->setAssignedCodec(Core::EditorManager::defaultTextCodec());
- d->m_behaviorWidget->setAssignedLineEnding(Core::EditorManager::defaultLineEnding());
-}
-
-void BehaviorSettingsPage::finish()
-{
- delete d->m_widget;
-}
-
ICodeStylePreferences *BehaviorSettingsPage::codeStyle() const
{
return d->m_codeStyle;
@@ -235,16 +231,4 @@ const ExtraEncodingSettings &BehaviorSettingsPage::extraEncodingSettings() const
}
-void BehaviorSettingsPage::openCodingStylePreferences(TabSettingsWidget::CodingStyleLink link)
-{
- switch (link) {
- case TabSettingsWidget::CppLink:
- Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CODE_STYLE_SETTINGS_ID);
- break;
- case TabSettingsWidget::QtQuickLink:
- Core::ICore::showOptionsDialog(QmlJSTools::Constants::QML_JS_CODE_STYLE_SETTINGS_ID);
- break;
- }
-}
-
} // namespace TextEditor
diff --git a/src/plugins/texteditor/behaviorsettingspage.h b/src/plugins/texteditor/behaviorsettingspage.h
index 495a800d86..2ecb48ea29 100644
--- a/src/plugins/texteditor/behaviorsettingspage.h
+++ b/src/plugins/texteditor/behaviorsettingspage.h
@@ -3,10 +3,6 @@
#pragma once
-#include "texteditor_global.h"
-
-#include "tabsettingswidget.h"
-
#include <coreplugin/dialogs/ioptionspage.h>
QT_BEGIN_NAMESPACE
@@ -25,17 +21,10 @@ class CodeStylePool;
class BehaviorSettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
BehaviorSettingsPage();
~BehaviorSettingsPage() override;
- // IOptionsPage
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
ICodeStylePreferences *codeStyle() const;
CodeStylePool *codeStylePool() const;
const TypingSettings &typingSettings() const;
@@ -44,17 +33,8 @@ public:
const ExtraEncodingSettings &extraEncodingSettings() const;
private:
- void openCodingStylePreferences(TextEditor::TabSettingsWidget::CodingStyleLink link);
-
- void settingsFromUI(TypingSettings *typingSettings,
- StorageSettings *storageSettings,
- BehaviorSettings *behaviorSettings,
- ExtraEncodingSettings *extraEncodingSettings) const;
- void settingsToUI();
-
QList<QTextCodec *> m_codecs;
- struct BehaviorSettingsPagePrivate;
- BehaviorSettingsPagePrivate *d;
+ class BehaviorSettingsPagePrivate *d;
};
} // namespace TextEditor
diff --git a/src/plugins/texteditor/behaviorsettingswidget.cpp b/src/plugins/texteditor/behaviorsettingswidget.cpp
index 518f09c8a5..144afa6eb2 100644
--- a/src/plugins/texteditor/behaviorsettingswidget.cpp
+++ b/src/plugins/texteditor/behaviorsettingswidget.cpp
@@ -65,8 +65,6 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
: QWidget(parent)
, d(new BehaviorSettingsWidgetPrivate)
{
- resize(801, 693);
-
d->tabPreferencesWidget = new SimpleCodeStylePreferencesWidget(this);
d->tabPreferencesWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // FIXME: Desirable?
@@ -165,7 +163,7 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
d->groupBoxMouse = new QGroupBox(Tr::tr("Mouse and Keyboard"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
const auto indent = [](QWidget *inner) { return Row { Space(30), inner }; };
@@ -207,8 +205,9 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
Row {
Column { d->tabPreferencesWidget, d->groupBoxTyping, st },
- Column { d->groupBoxStorageSettings, d->groupBoxEncodings, d->groupBoxMouse, st }
- }.attachTo(this, WithoutMargins);
+ Column { d->groupBoxStorageSettings, d->groupBoxEncodings, d->groupBoxMouse, st },
+ noMargin,
+ }.attachTo(this);
connect(d->cleanWhitespace, &QCheckBox::toggled,
d->inEntireDocument, &QCheckBox::setEnabled);
diff --git a/src/plugins/texteditor/codeassist/asyncprocessor.cpp b/src/plugins/texteditor/codeassist/asyncprocessor.cpp
index 90f993a39c..3f440bfa70 100644
--- a/src/plugins/texteditor/codeassist/asyncprocessor.cpp
+++ b/src/plugins/texteditor/codeassist/asyncprocessor.cpp
@@ -6,7 +6,7 @@
#include "assistinterface.h"
#include "iassistproposal.h"
-#include <utils/runextensions.h>
+#include <utils/async.h>
namespace TextEditor {
@@ -21,7 +21,7 @@ IAssistProposal *AsyncProcessor::perform()
{
IAssistProposal *result = immediateProposal();
interface()->prepareForAsyncUse();
- m_watcher.setFuture(Utils::runAsync([this] {
+ m_watcher.setFuture(Utils::asyncRun([this] {
interface()->recreateTextDocument();
return performAsync();
}));
diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp
index b3d9d477e2..fce8d08385 100644
--- a/src/plugins/texteditor/codeassist/codeassistant.cpp
+++ b/src/plugins/texteditor/codeassist/codeassistant.cpp
@@ -81,6 +81,7 @@ private:
IAssistProcessor *m_processor = nullptr;
AssistKind m_assistKind = TextEditor::Completion;
IAssistProposalWidget *m_proposalWidget = nullptr;
+ TextEditorWidget::SuggestionBlocker m_suggestionBlocker;
bool m_receivedContentWhileWaiting = false;
QTimer m_automaticProposalTimer;
CompletionSettings m_settings;
@@ -251,6 +252,14 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
return;
}
+ if (m_editorWidget->suggestionVisible()) {
+ if (reason != ExplicitlyInvoked) {
+ destroyContext();
+ return;
+ }
+ m_editorWidget->clearSuggestion();
+ }
+
const QString prefix = m_editorWidget->textAt(basePosition,
m_editorWidget->position() - basePosition);
if (!newProposal->hasItemsToPropose(prefix, reason)) {
@@ -287,6 +296,7 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition));
m_proposalWidget->setIsSynchronized(!m_receivedContentWhileWaiting);
m_proposalWidget->showProposal(prefix);
+ m_suggestionBlocker = m_editorWidget->blockSuggestions();
}
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
@@ -329,6 +339,7 @@ void CodeAssistantPrivate::handlePrefixExpansion(const QString &newPrefix)
void CodeAssistantPrivate::finalizeProposal()
{
stopAutomaticProposalTimer();
+ m_suggestionBlocker.reset();
m_proposalWidget = nullptr;
if (m_receivedContentWhileWaiting)
m_receivedContentWhileWaiting = false;
@@ -445,6 +456,7 @@ void CodeAssistantPrivate::automaticProposalTimeout()
{
if (isWaitingForProposal()
|| m_editorWidget->multiTextCursor().hasMultipleCursors()
+ || m_editorWidget->suggestionVisible()
|| (isDisplayingProposal() && !m_proposalWidget->isFragile())) {
return;
}
diff --git a/src/plugins/texteditor/codestyleeditor.cpp b/src/plugins/texteditor/codestyleeditor.cpp
index ac75f1ad6d..070a230e38 100644
--- a/src/plugins/texteditor/codestyleeditor.cpp
+++ b/src/plugins/texteditor/codestyleeditor.cpp
@@ -29,6 +29,7 @@ CodeStyleEditor::CodeStyleEditor(ICodeStylePreferencesFactory *factory,
, m_codeStyle(codeStyle)
{
m_layout = new QVBoxLayout(this);
+ m_layout->setContentsMargins(0, 0, 0, 0);
auto selector = new CodeStyleSelectorWidget(factory, project, this);
selector->setCodeStyle(codeStyle);
m_additionalGlobalSettingsWidget = factory->createAdditionalGlobalSettings(codeStyle,
diff --git a/src/plugins/texteditor/codestyleselectorwidget.cpp b/src/plugins/texteditor/codestyleselectorwidget.cpp
index 56b8ca4c05..2b21fd12c3 100644
--- a/src/plugins/texteditor/codestyleselectorwidget.cpp
+++ b/src/plugins/texteditor/codestyleselectorwidget.cpp
@@ -33,8 +33,6 @@ CodeStyleSelectorWidget::CodeStyleSelectorWidget(ICodeStylePreferencesFactory *f
, m_factory(factory)
, m_project(project)
{
- resize(536, 59);
-
m_delegateComboBox = new QComboBox(this);
m_delegateComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -48,7 +46,7 @@ CodeStyleSelectorWidget::CodeStyleSelectorWidget(ICodeStylePreferencesFactory *f
m_importButton = new QPushButton(Tr::tr("Import..."));
m_importButton->setEnabled(false);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Grid {
@@ -59,8 +57,8 @@ CodeStyleSelectorWidget::CodeStyleSelectorWidget(ICodeStylePreferencesFactory *f
m_exportButton,
m_importButton
},
-
- }.attachTo(this, WithoutMargins);
+ noMargin,
+ }.attachTo(this);
connect(m_delegateComboBox, &QComboBox::activated,
this, &CodeStyleSelectorWidget::slotComboBoxActivated);
diff --git a/src/plugins/texteditor/colorschemeedit.cpp b/src/plugins/texteditor/colorschemeedit.cpp
index 0bd0c54015..c6f661df42 100644
--- a/src/plugins/texteditor/colorschemeedit.cpp
+++ b/src/plugins/texteditor/colorschemeedit.cpp
@@ -6,13 +6,13 @@
#include "texteditortr.h"
#include <utils/layoutbuilder.h>
+#include <utils/qtcolorbutton.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <QAbstractListModel>
#include <QApplication>
#include <QCheckBox>
-#include <QColorDialog>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QLabel>
@@ -25,14 +25,6 @@ namespace TextEditor::Internal {
const int layoutSpacing = 6;
-static QString colorButtonStyleSheet(const QColor &bgColor)
-{
- QString rc("border-width: 2px; border-radius: 2px; border-color: black; ");
- rc += bgColor.isValid() ? "border-style: solid; background:" + bgColor.name() + ";"
- : QString("border-style: dotted;");
- return rc;
-}
-
class FormatsModel : public QAbstractListModel
{
public:
@@ -129,10 +121,9 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
m_formatsModel(new FormatsModel(this))
{
setContentsMargins(0, layoutSpacing, 0, 0);
- resize(513, 416);
auto colorButton = [] () {
- auto tb = new QToolButton;
+ auto tb = new Utils::QtColorButton;
tb->setMinimumWidth(56);
return tb;
};
@@ -210,13 +201,14 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
auto bottomSpacer = new QWidget;
bottomSpacer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Row {
m_itemList,
m_builtinSchemeLabel,
m_fontProperties,
- }.attachTo(this, WithoutMargins);
+ noMargin
+ }.attachTo(this);
Grid {
m_foregroundLabel, m_foregroundToolButton, m_eraseForegroundToolButton, br,
@@ -245,9 +237,9 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
connect(m_itemList->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &ColorSchemeEdit::currentItemChanged);
- connect(m_foregroundToolButton, &QAbstractButton::clicked,
+ connect(m_foregroundToolButton, &Utils::QtColorButton::colorChanged,
this, &ColorSchemeEdit::changeForeColor);
- connect(m_backgroundToolButton, &QAbstractButton::clicked,
+ connect(m_backgroundToolButton, &Utils::QtColorButton::colorChanged,
this, &ColorSchemeEdit::changeBackColor);
connect(m_eraseBackgroundToolButton, &QAbstractButton::clicked,
this, &ColorSchemeEdit::eraseBackColor);
@@ -265,7 +257,7 @@ ColorSchemeEdit::ColorSchemeEdit(QWidget *parent) :
this, &ColorSchemeEdit::checkCheckBoxes);
connect(m_italicCheckBox, &QAbstractButton::toggled,
this, &ColorSchemeEdit::checkCheckBoxes);
- connect(m_underlineColorToolButton, &QToolButton::clicked,
+ connect(m_underlineColorToolButton, &Utils::QtColorButton::colorChanged,
this, &ColorSchemeEdit::changeUnderlineColor);
connect(m_eraseUnderlineColorToolButton, &QToolButton::clicked,
this, &ColorSchemeEdit::eraseUnderlineColor);
@@ -347,7 +339,7 @@ void ColorSchemeEdit::updateForegroundControls()
m_foregroundToolButton->setVisible(isVisible);
m_eraseForegroundToolButton->setVisible(isVisible);
- m_foregroundToolButton->setStyleSheet(colorButtonStyleSheet(format.foreground()));
+ m_foregroundToolButton->setColor(format.foreground());
m_eraseForegroundToolButton->setEnabled(!m_readOnly
&& m_curItem > 0
&& format.foreground().isValid());
@@ -366,7 +358,7 @@ void ColorSchemeEdit::updateBackgroundControls()
m_backgroundToolButton->setVisible(isVisible);
m_eraseBackgroundToolButton->setVisible(isVisible);
- m_backgroundToolButton->setStyleSheet(colorButtonStyleSheet(format.background()));
+ m_backgroundToolButton->setColor(format.background());
m_eraseBackgroundToolButton->setEnabled(!m_readOnly
&& m_curItem > 0
&& format.background().isValid());
@@ -466,7 +458,7 @@ void ColorSchemeEdit::updateUnderlineControls()
m_eraseUnderlineColorToolButton->setVisible(isVisible);
m_underlineComboBox->setVisible(isVisible);
- m_underlineColorToolButton->setStyleSheet(colorButtonStyleSheet(format.underlineColor()));
+ m_underlineColorToolButton->setColor(format.underlineColor());
m_eraseUnderlineColorToolButton->setEnabled(!m_readOnly
&& m_curItem > 0
&& format.underlineColor().isValid());
@@ -478,11 +470,8 @@ void ColorSchemeEdit::changeForeColor()
{
if (m_curItem == -1)
return;
- QColor color = m_scheme.formatFor(m_descriptions[m_curItem].id()).foreground();
- const QColor newColor = QColorDialog::getColor(color, m_boldCheckBox->window());
- if (!newColor.isValid())
- return;
- m_foregroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+
+ const QColor newColor = m_foregroundToolButton->color();
m_eraseForegroundToolButton->setEnabled(true);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
@@ -498,11 +487,8 @@ void ColorSchemeEdit::changeBackColor()
{
if (m_curItem == -1)
return;
- QColor color = m_scheme.formatFor(m_descriptions[m_curItem].id()).background();
- const QColor newColor = QColorDialog::getColor(color, m_boldCheckBox->window());
- if (!newColor.isValid())
- return;
- m_backgroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+
+ const QColor newColor = m_backgroundToolButton->color();
m_eraseBackgroundToolButton->setEnabled(true);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
@@ -521,14 +507,13 @@ void ColorSchemeEdit::eraseBackColor()
{
if (m_curItem == -1)
return;
- QColor newColor;
- m_backgroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+ m_backgroundToolButton->setColor({});
m_eraseBackgroundToolButton->setEnabled(false);
const QList<QModelIndex> indexes = m_itemList->selectionModel()->selectedRows();
for (const QModelIndex &index : indexes) {
const TextStyle category = m_descriptions[index.row()].id();
- m_scheme.formatFor(category).setBackground(newColor);
+ m_scheme.formatFor(category).setBackground({});
m_formatsModel->emitDataChanged(index);
}
@@ -539,14 +524,13 @@ void ColorSchemeEdit::eraseForeColor()
{
if (m_curItem == -1)
return;
- QColor newColor;
- m_foregroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+ m_foregroundToolButton->setColor({});
m_eraseForegroundToolButton->setEnabled(false);
const QList<QModelIndex> indexes = m_itemList->selectionModel()->selectedRows();
for (const QModelIndex &index : indexes) {
const TextStyle category = m_descriptions[index.row()].id();
- m_scheme.formatFor(category).setForeground(newColor);
+ m_scheme.formatFor(category).setForeground({});
m_formatsModel->emitDataChanged(index);
}
@@ -635,11 +619,8 @@ void ColorSchemeEdit::changeUnderlineColor()
{
if (m_curItem == -1)
return;
- QColor color = m_scheme.formatFor(m_descriptions[m_curItem].id()).underlineColor();
- const QColor newColor = QColorDialog::getColor(color, m_boldCheckBox->window());
- if (!newColor.isValid())
- return;
- m_underlineColorToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+
+ const QColor newColor = m_underlineColorToolButton->color();
m_eraseUnderlineColorToolButton->setEnabled(true);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
@@ -653,13 +634,12 @@ void ColorSchemeEdit::eraseUnderlineColor()
{
if (m_curItem == -1)
return;
- QColor newColor;
- m_underlineColorToolButton->setStyleSheet(colorButtonStyleSheet(newColor));
+ m_underlineColorToolButton->setColor({});
m_eraseUnderlineColorToolButton->setEnabled(false);
for (const QModelIndex &index : m_itemList->selectionModel()->selectedRows()) {
const TextStyle category = m_descriptions[index.row()].id();
- m_scheme.formatFor(category).setUnderlineColor(newColor);
+ m_scheme.formatFor(category).setUnderlineColor({});
m_formatsModel->emitDataChanged(index);
}
}
diff --git a/src/plugins/texteditor/colorschemeedit.h b/src/plugins/texteditor/colorschemeedit.h
index 645ece9efe..a2d5797079 100644
--- a/src/plugins/texteditor/colorschemeedit.h
+++ b/src/plugins/texteditor/colorschemeedit.h
@@ -20,6 +20,8 @@ class QScrollArea;
class QToolButton;
QT_END_NAMESPACE
+namespace Utils { class QtColorButton; }
+
namespace TextEditor::Internal {
class FormatsModel;
@@ -80,10 +82,10 @@ private:
QLabel *m_builtinSchemeLabel;
QWidget *m_fontProperties;
QLabel *m_foregroundLabel;
- QToolButton *m_foregroundToolButton;
+ Utils::QtColorButton *m_foregroundToolButton;
QAbstractButton *m_eraseForegroundToolButton;
QLabel *m_backgroundLabel;
- QToolButton *m_backgroundToolButton;
+ Utils::QtColorButton *m_backgroundToolButton;
QAbstractButton *m_eraseBackgroundToolButton;
QLabel *m_relativeForegroundHeadline;
QLabel *m_foregroundLightnessLabel;
@@ -100,7 +102,7 @@ private:
QCheckBox *m_italicCheckBox;
QLabel *m_underlineHeadline;
QLabel *m_underlineLabel;
- QToolButton *m_underlineColorToolButton;
+ Utils::QtColorButton *m_underlineColorToolButton;
QAbstractButton *m_eraseUnderlineColorToolButton;
QComboBox *m_underlineComboBox;
diff --git a/src/plugins/texteditor/completionsettingspage.cpp b/src/plugins/texteditor/completionsettingspage.cpp
index 6187688e4e..167cde25a7 100644
--- a/src/plugins/texteditor/completionsettingspage.cpp
+++ b/src/plugins/texteditor/completionsettingspage.cpp
@@ -66,8 +66,6 @@ private:
CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPage *owner)
: m_owner(owner)
{
- resize(823, 756);
-
m_caseSensitivity = new QComboBox;
m_caseSensitivity->addItem(Tr::tr("Full"));
m_caseSensitivity->addItem(Tr::tr("None"));
diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp
index 58f9e04068..2449618bf0 100644
--- a/src/plugins/texteditor/displaysettingspage.cpp
+++ b/src/plugins/texteditor/displaysettingspage.cpp
@@ -4,6 +4,7 @@
#include "displaysettingspage.h"
#include "displaysettings.h"
+#include "fontsettings.h"
#include "marginsettings.h"
#include "texteditorconstants.h"
#include "texteditorsettings.h"
@@ -43,30 +44,23 @@ public:
DisplaySettingsWidget(DisplaySettingsPagePrivate *data)
: m_data(data)
{
- resize(452, 458);
-
enableTextWrapping = new QCheckBox(Tr::tr("Enable text &wrapping"));
enableTextWrappingHintLabel = new QLabel(Tr::tr("<i>Set <a href=\"font zoom\">font line spacing</a> "
"to 100% to enable text wrapping option.</i>"));
- fontSettingsPageLineSpacing = fontSettingsPageLineSpacingLink();
-
- if (fontSettingsPageLineSpacing) {
- connect(fontSettingsPageLineSpacing, &QSpinBox::valueChanged,
- this, [this](const int &value) {
- if (value != 100)
- enableTextWrapping->setChecked(false);
- enableTextWrapping->setEnabled(value == 100);
- enableTextWrappingHintLabel->setVisible(value != 100);
- });
-
- if (fontSettingsPageLineSpacing->value() != 100)
+ auto updateWrapping = [this] {
+ const bool normalLineSpacing = TextEditorSettings::fontSettings().relativeLineSpacing() == 100;
+ if (!normalLineSpacing)
enableTextWrapping->setChecked(false);
+ enableTextWrapping->setEnabled(normalLineSpacing);
+ enableTextWrappingHintLabel->setVisible(!normalLineSpacing);
+ };
- enableTextWrapping->setEnabled(fontSettingsPageLineSpacing->value() == 100);
- enableTextWrappingHintLabel->setVisible(fontSettingsPageLineSpacing->value() != 100);
- }
+ updateWrapping();
+
+ connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
+ this, updateWrapping);
connect(enableTextWrappingHintLabel, &QLabel::linkActivated, [] {
Core::ICore::showOptionsDialog(Constants::TEXT_EDITOR_FONT_SETTINGS); } );
@@ -113,7 +107,7 @@ public:
displayAnnotations = new QGroupBox(Tr::tr("Line annotations")),
displayAnnotations->setCheckable(true);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
leftAligned,
@@ -177,8 +171,6 @@ public:
void settingsToUI();
void setDisplaySettings(const DisplaySettings &, const MarginSettings &newMarginSettings);
- QSpinBox *fontSettingsPageLineSpacingLink();
-
DisplaySettingsPagePrivate *m_data = nullptr;
QCheckBox *enableTextWrapping;
@@ -208,8 +200,6 @@ public:
QRadioButton *atMargin;
QRadioButton *rightAligned;
QRadioButton *betweenLines;
-
- QSpinBox *fontSettingsPageLineSpacing = nullptr;
};
void DisplaySettingsWidget::apply()
@@ -226,10 +216,8 @@ void DisplaySettingsWidget::settingsFromUI(DisplaySettings &displaySettings,
{
displaySettings.m_displayLineNumbers = displayLineNumbers->isChecked();
displaySettings.m_textWrapping = enableTextWrapping->isChecked();
- if (fontSettingsPageLineSpacing) {
- if (fontSettingsPageLineSpacing->value() != 100)
- displaySettings.m_textWrapping = false;
- }
+ if (TextEditorSettings::fontSettings().relativeLineSpacing() != 100)
+ displaySettings.m_textWrapping = false;
marginSettings.m_showMargin = showWrapColumn->isChecked();
marginSettings.m_tintMarginArea = tintMarginArea->isChecked();
marginSettings.m_useIndenter = useIndenter->isChecked();
@@ -268,8 +256,10 @@ void DisplaySettingsWidget::settingsToUI()
enableTextWrapping->setChecked(displaySettings.m_textWrapping);
showWrapColumn->setChecked(marginSettings.m_showMargin);
tintMarginArea->setChecked(marginSettings.m_tintMarginArea);
+ tintMarginArea->setEnabled(marginSettings.m_showMargin);
useIndenter->setChecked(marginSettings.m_useIndenter);
wrapColumn->setValue(marginSettings.m_marginColumn);
+ wrapColumn->setEnabled(marginSettings.m_showMargin);
visualizeWhitespace->setChecked(displaySettings.m_visualizeWhitespace);
visualizeIndent->setChecked(displaySettings.m_visualizeIndent);
displayFoldingMarkers->setChecked(displaySettings.m_displayFoldingMarkers);
@@ -322,23 +312,6 @@ void DisplaySettingsWidget::setDisplaySettings(const DisplaySettings &newDisplay
}
}
- QSpinBox *DisplaySettingsWidget::fontSettingsPageLineSpacingLink()
- {
- for (const auto &page : Core::IOptionsPage::allOptionsPages()) {
- QWidget *widget = page->widget();
-
- if (!widget)
- continue;
-
- for (QSpinBox *spinBox : widget->findChildren<QSpinBox *>()) {
- if (spinBox->objectName() == QLatin1String("FontSettingsPage.LineSpacingSpinBox"))
- return spinBox;
- }
- }
-
- return nullptr;
- }
-
DisplaySettingsPage::DisplaySettingsPage()
: d(new DisplaySettingsPagePrivate)
{
diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp
index 1e9ec7fafb..b57e23f35b 100644
--- a/src/plugins/texteditor/fontsettingspage.cpp
+++ b/src/plugins/texteditor/fontsettingspage.cpp
@@ -107,8 +107,6 @@ public:
{
m_lastValue = m_value;
- resize(639, 306);
-
m_antialias = new QCheckBox(Tr::tr("Antialias"));
m_antialias->setChecked(m_value.antialias());
@@ -119,7 +117,6 @@ public:
m_zoomSpinBox->setValue(m_value.fontZoom());
m_lineSpacingSpinBox = new QSpinBox;
- m_lineSpacingSpinBox->setObjectName(QLatin1String("FontSettingsPage.LineSpacingSpinBox"));
m_lineSpacingSpinBox->setSuffix(Tr::tr("%"));
m_lineSpacingSpinBox->setRange(50, 3000);
m_lineSpacingSpinBox->setValue(m_value.relativeLineSpacing());
diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp
index 6f1139e057..3e31006d61 100644
--- a/src/plugins/texteditor/formattexteditor.cpp
+++ b/src/plugins/texteditor/formattexteditor.cpp
@@ -10,10 +10,10 @@
#include <coreplugin/messagemanager.h>
+#include <utils/async.h>
#include <utils/differ.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
#include <utils/temporarydirectory.h>
#include <utils/textutils.h>
@@ -64,7 +64,7 @@ static FormatTask format(FormatTask task)
// Format temporary file
QStringList options = task.command.options();
options.replaceInStrings(QLatin1String("%file"), sourceFile.filePath().toString());
- QtcProcess process;
+ Process process;
process.setTimeoutS(5);
process.setCommand({executable, options});
process.runBlocking();
@@ -89,7 +89,7 @@ static FormatTask format(FormatTask task)
return task;
case Command::PipeProcessing: {
- QtcProcess process;
+ Process process;
QStringList options = task.command.options();
options.replaceInStrings("%filename", task.filePath.fileName());
options.replaceInStrings("%file", task.filePath.toString());
@@ -324,7 +324,7 @@ void formatEditorAsync(TextEditorWidget *editor, const Command &command, int sta
checkAndApplyTask(watcher->result());
watcher->deleteLater();
});
- watcher->setFuture(Utils::runAsync(&format, FormatTask(editor, doc->filePath(), sd,
+ watcher->setFuture(Utils::asyncRun(&format, FormatTask(editor, doc->filePath(), sd,
command, startPos, endPos)));
}
diff --git a/src/plugins/texteditor/highlightersettingspage.cpp b/src/plugins/texteditor/highlightersettingspage.cpp
index ceb8f7d48f..4cc16a488a 100644
--- a/src/plugins/texteditor/highlightersettingspage.cpp
+++ b/src/plugins/texteditor/highlightersettingspage.cpp
@@ -13,7 +13,6 @@
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
-#include <QApplication>
#include <QDir>
#include <QLabel>
#include <QLineEdit>
@@ -24,24 +23,50 @@
using namespace Utils;
namespace TextEditor {
-namespace Internal {
-class HighlighterSettingsPageWidget : public QWidget
+class HighlighterSettingsPageWidget;
+
+class HighlighterSettingsPagePrivate
+{
+public:
+ void ensureInitialized()
+ {
+ if (m_initialized)
+ return;
+ m_initialized = true;
+ m_settings.fromSettings(m_settingsPrefix, Core::ICore::settings());
+ migrateGenericHighlighterFiles();
+ }
+
+ void migrateGenericHighlighterFiles()
+ {
+ QDir userDefinitionPath(m_settings.definitionFilesPath().toString());
+ if (userDefinitionPath.mkdir("syntax")) {
+ const auto link = Utils::HostOsInfo::isAnyUnixHost()
+ ? static_cast<bool(*)(const QString &, const QString &)>(&QFile::link)
+ : static_cast<bool(*)(const QString &, const QString &)>(&QFile::copy);
+
+ for (const QFileInfo &file : userDefinitionPath.entryInfoList({"*.xml"}, QDir::Files))
+ link(file.filePath(), file.absolutePath() + "/syntax/" + file.fileName());
+ }
+ }
+
+ bool m_initialized = false;
+ const QString m_settingsPrefix{"Text"};
+
+ HighlighterSettings m_settings;
+
+ QPointer<HighlighterSettingsPageWidget> m_widget;
+};
+
+class HighlighterSettingsPageWidget : public Core::IOptionsPageWidget
{
public:
- QLabel *definitionsInfolabel;
- QPushButton *downloadDefinitions;
- QLabel *updateStatus;
- PathChooser *definitionFilesPath;
- QPushButton *reloadDefinitions;
- QPushButton *resetCache;
- QLineEdit *ignoreEdit;
-
- HighlighterSettingsPageWidget()
+ HighlighterSettingsPageWidget(HighlighterSettingsPagePrivate *d) : d(d)
{
- resize(521, 332);
+ d->ensureInitialized();
- definitionsInfolabel = new QLabel(this);
+ auto definitionsInfolabel = new QLabel(this);
definitionsInfolabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
definitionsInfolabel->setTextFormat(Qt::RichText);
definitionsInfolabel->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
@@ -51,24 +76,25 @@ public:
"<a href=\"https://api.kde.org/frameworks/syntax-highlighting/html/index.html\">"
"KSyntaxHighlighting</a> engine.</p></body></html>"));
- downloadDefinitions = new QPushButton(Tr::tr("Download Definitions"));
+ auto downloadDefinitions = new QPushButton(Tr::tr("Download Definitions"));
downloadDefinitions->setToolTip(Tr::tr("Download missing and update existing syntax definition files."));
- updateStatus = new QLabel;
+ auto updateStatus = new QLabel;
updateStatus->setObjectName("updateStatus");
- definitionFilesPath = new PathChooser;
- definitionFilesPath->setExpectedKind(PathChooser::ExistingDirectory);
- definitionFilesPath->setHistoryCompleter("TextEditor.Highlighter.History");
+ m_definitionFilesPath = new PathChooser;
+ m_definitionFilesPath->setFilePath(d->m_settings.definitionFilesPath());
+ m_definitionFilesPath->setExpectedKind(PathChooser::ExistingDirectory);
+ m_definitionFilesPath->setHistoryCompleter("TextEditor.Highlighter.History");
- reloadDefinitions = new QPushButton(Tr::tr("Reload Definitions"));
+ auto reloadDefinitions = new QPushButton(Tr::tr("Reload Definitions"));
reloadDefinitions->setToolTip(Tr::tr("Reload externally modified definition files."));
- resetCache = new QPushButton(Tr::tr("Reset Remembered Definitions"));
+ auto resetCache = new QPushButton(Tr::tr("Reset Remembered Definitions"));
resetCache->setToolTip(Tr::tr("Reset definitions remembered for files that can be "
"associated with more than one highlighter definition."));
- ignoreEdit = new QLineEdit;
+ m_ignoreEdit = new QLineEdit(d->m_settings.ignoredFilesPatterns());
using namespace Layouting;
Column {
@@ -79,11 +105,11 @@ public:
Column {
Row { downloadDefinitions, updateStatus, st },
Row { Tr::tr("User Highlight Definition Files"),
- definitionFilesPath, reloadDefinitions },
+ m_definitionFilesPath, reloadDefinitions },
Row { st, resetCache }
}
},
- Row { Tr::tr("Ignored file patterns:"), ignoreEdit },
+ Row { Tr::tr("Ignored file patterns:"), m_ignoreEdit },
st
}.attachTo(this);
@@ -102,53 +128,25 @@ public:
Highlighter::clearDefinitionForDocumentCache();
});
}
-};
-
-} // Internal
-
-using namespace Internal;
-
-class HighlighterSettingsPagePrivate
-{
-public:
- HighlighterSettingsPagePrivate() = default;
-
- void ensureInitialized();
- void migrateGenericHighlighterFiles();
-
- void settingsFromUI();
- void settingsToUI();
- bool settingsChanged();
-
- bool m_initialized = false;
- const QString m_settingsPrefix{"Text"};
- HighlighterSettings m_settings;
+ void apply() final
+ {
+ bool changed = d->m_settings.definitionFilesPath() != m_definitionFilesPath->filePath()
+ || d->m_settings.ignoredFilesPatterns() != m_ignoreEdit->text();
+
+ if (changed) {
+ d->m_settings.setDefinitionFilesPath(m_definitionFilesPath->filePath());
+ d->m_settings.setIgnoredFilesPatterns(m_ignoreEdit->text());
+ d->m_settings.toSettings(d->m_settingsPrefix, Core::ICore::settings());
+ }
+ }
- QPointer<HighlighterSettingsPageWidget> m_widget;
+ PathChooser *m_definitionFilesPath;
+ QLineEdit *m_ignoreEdit;
+ HighlighterSettingsPagePrivate *d;
};
-void HighlighterSettingsPagePrivate::migrateGenericHighlighterFiles()
-{
- QDir userDefinitionPath(m_settings.definitionFilesPath().toString());
- if (userDefinitionPath.mkdir("syntax")) {
- const auto link = Utils::HostOsInfo::isAnyUnixHost()
- ? static_cast<bool(*)(const QString &, const QString &)>(&QFile::link)
- : static_cast<bool(*)(const QString &, const QString &)>(&QFile::copy);
-
- for (const QFileInfo &file : userDefinitionPath.entryInfoList({"*.xml"}, QDir::Files))
- link(file.filePath(), file.absolutePath() + "/syntax/" + file.fileName());
- }
-}
-
-void HighlighterSettingsPagePrivate::ensureInitialized()
-{
- if (m_initialized)
- return;
- m_initialized = true;
- m_settings.fromSettings(m_settingsPrefix, Core::ICore::settings());
- migrateGenericHighlighterFiles();
-}
+// HighlighterSettingsPage
HighlighterSettingsPage::HighlighterSettingsPage()
: d(new HighlighterSettingsPagePrivate)
@@ -158,6 +156,7 @@ HighlighterSettingsPage::HighlighterSettingsPage()
setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Text Editor"));
setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
+ setWidgetCreator([this] { return new HighlighterSettingsPageWidget(d); });
}
HighlighterSettingsPage::~HighlighterSettingsPage()
@@ -165,55 +164,10 @@ HighlighterSettingsPage::~HighlighterSettingsPage()
delete d;
}
-QWidget *HighlighterSettingsPage::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new HighlighterSettingsPageWidget;
- d->settingsToUI();
- }
- return d->m_widget;
-}
-
-void HighlighterSettingsPage::apply()
-{
- if (!d->m_widget) // page was not shown
- return;
- if (d->settingsChanged())
- d->settingsFromUI();
-}
-
-void HighlighterSettingsPage::finish()
-{
- delete d->m_widget;
- d->m_widget = nullptr;
-}
-
const HighlighterSettings &HighlighterSettingsPage::highlighterSettings() const
{
d->ensureInitialized();
return d->m_settings;
}
-void HighlighterSettingsPagePrivate::settingsFromUI()
-{
- ensureInitialized();
- m_settings.setDefinitionFilesPath(m_widget->definitionFilesPath->filePath());
- m_settings.setIgnoredFilesPatterns(m_widget->ignoreEdit->text());
- m_settings.toSettings(m_settingsPrefix, Core::ICore::settings());
-}
-
-void HighlighterSettingsPagePrivate::settingsToUI()
-{
- ensureInitialized();
- m_widget->definitionFilesPath->setFilePath(m_settings.definitionFilesPath());
- m_widget->ignoreEdit->setText(m_settings.ignoredFilesPatterns());
-}
-
-bool HighlighterSettingsPagePrivate::settingsChanged()
-{
- ensureInitialized();
- return m_settings.definitionFilesPath() != m_widget->definitionFilesPath->filePath()
- || m_settings.ignoredFilesPatterns() != m_widget->ignoreEdit->text();
-}
-
} // TextEditor
diff --git a/src/plugins/texteditor/highlightersettingspage.h b/src/plugins/texteditor/highlightersettingspage.h
index c56e6d2238..94407c9020 100644
--- a/src/plugins/texteditor/highlightersettingspage.h
+++ b/src/plugins/texteditor/highlightersettingspage.h
@@ -15,10 +15,6 @@ public:
HighlighterSettingsPage();
~HighlighterSettingsPage() override;
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
const HighlighterSettings &highlighterSettings() const;
private:
diff --git a/src/plugins/texteditor/linenumberfilter.cpp b/src/plugins/texteditor/linenumberfilter.cpp
index 78a65c84d2..b718e5b433 100644
--- a/src/plugins/texteditor/linenumberfilter.cpp
+++ b/src/plugins/texteditor/linenumberfilter.cpp
@@ -5,24 +5,14 @@
#include "texteditortr.h"
-#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/icore.h>
-#include <coreplugin/modemanager.h>
-
-#include <QMetaType>
-#include <QPair>
-#include <QVariant>
-
-using LineColumn = QPair<int, int>;
-Q_DECLARE_METATYPE(LineColumn)
using namespace Core;
+using namespace Utils;
namespace TextEditor::Internal {
-LineNumberFilter::LineNumberFilter(QObject *parent)
- : ILocatorFilter(parent)
+LineNumberFilter::LineNumberFilter()
{
setId("Line in current document");
setDisplayName(Tr::tr("Line in Current Document"));
@@ -33,57 +23,47 @@ LineNumberFilter::LineNumberFilter(QObject *parent)
setDefaultIncludedByDefault(true);
}
-void LineNumberFilter::prepareSearch(const QString &entry)
+LocatorMatcherTasks LineNumberFilter::matchers()
{
- Q_UNUSED(entry)
- m_hasCurrentEditor = EditorManager::currentEditor() != nullptr;
-}
+ using namespace Tasking;
-QList<LocatorFilterEntry> LineNumberFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &, const QString &entry)
-{
- QList<LocatorFilterEntry> value;
- const QStringList lineAndColumn = entry.split(':');
- int sectionCount = lineAndColumn.size();
- int line = 0;
- int column = 0;
- bool ok = false;
- if (sectionCount > 0)
- line = lineAndColumn.at(0).toInt(&ok);
- if (ok && sectionCount > 1)
- column = lineAndColumn.at(1).toInt(&ok);
- if (!ok)
- return value;
- if (m_hasCurrentEditor && (line > 0 || column > 0)) {
- LineColumn data;
- data.first = line;
- data.second = column - 1; // column API is 0-based
- QString text;
- if (line > 0 && column > 0)
- text = Tr::tr("Line %1, Column %2").arg(line).arg(column);
- else if (line > 0)
- text = Tr::tr("Line %1").arg(line);
- else
- text = Tr::tr("Column %1").arg(column);
- value.append(LocatorFilterEntry(this, text, QVariant::fromValue(data)));
- }
- return value;
-}
+ TreeStorage<LocatorStorage> storage;
-void LineNumberFilter::accept(const LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const
-{
- Q_UNUSED(newText)
- Q_UNUSED(selectionStart)
- Q_UNUSED(selectionLength)
- IEditor *editor = EditorManager::currentEditor();
- if (editor) {
- EditorManager::addCurrentPositionToNavigationHistory();
- LineColumn data = selection.internalData.value<LineColumn>();
- if (data.first < 1) // jump to column in same line
- data.first = editor->currentLine();
- editor->gotoLine(data.first, data.second);
- EditorManager::activateEditor(editor);
- }
+ const auto onSetup = [storage] {
+ const QStringList lineAndColumn = storage->input().split(':');
+ int sectionCount = lineAndColumn.size();
+ int line = 0;
+ int column = 0;
+ bool ok = false;
+ if (sectionCount > 0)
+ line = lineAndColumn.at(0).toInt(&ok);
+ if (ok && sectionCount > 1)
+ column = lineAndColumn.at(1).toInt(&ok);
+ if (!ok)
+ return;
+ if (EditorManager::currentEditor() && (line > 0 || column > 0)) {
+ QString text;
+ if (line > 0 && column > 0)
+ text = Tr::tr("Line %1, Column %2").arg(line).arg(column);
+ else if (line > 0)
+ text = Tr::tr("Line %1").arg(line);
+ else
+ text = Tr::tr("Column %1").arg(column);
+ LocatorFilterEntry entry;
+ entry.displayName = text;
+ entry.acceptor = [line, targetColumn = column - 1] {
+ IEditor *editor = EditorManager::currentEditor();
+ if (!editor)
+ return AcceptResult();
+ EditorManager::addCurrentPositionToNavigationHistory();
+ editor->gotoLine(line < 1 ? editor->currentLine() : line, targetColumn);
+ EditorManager::activateEditor(editor);
+ return AcceptResult();
+ };
+ storage->reportOutput({entry});
+ }
+ };
+ return {{Sync(onSetup), storage}};
}
-} // TextEditor::Internal
+} // namespace TextEditor::Internal
diff --git a/src/plugins/texteditor/linenumberfilter.h b/src/plugins/texteditor/linenumberfilter.h
index 03b52f270b..ec72c1bb02 100644
--- a/src/plugins/texteditor/linenumberfilter.h
+++ b/src/plugins/texteditor/linenumberfilter.h
@@ -5,31 +5,15 @@
#include <coreplugin/locator/ilocatorfilter.h>
-#include <QString>
-#include <QList>
-#include <QFutureInterface>
-
-namespace Core { class IEditor; }
-
-namespace TextEditor {
-namespace Internal {
+namespace TextEditor::Internal {
class LineNumberFilter : public Core::ILocatorFilter
{
- Q_OBJECT
-
public:
- explicit LineNumberFilter(QObject *parent = nullptr);
-
- void prepareSearch(const QString &entry) override;
- QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
- const QString &entry) override;
- void accept(const Core::LocatorFilterEntry &selection,
- QString *newText, int *selectionStart, int *selectionLength) const override;
+ LineNumberFilter();
private:
- bool m_hasCurrentEditor = false;
+ Core::LocatorMatcherTasks matchers() final;
};
-} // namespace Internal
-} // namespace TextEditor
+} // namespace TextEditor::Internal
diff --git a/src/plugins/texteditor/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp
new file mode 100644
index 0000000000..12e60f027a
--- /dev/null
+++ b/src/plugins/texteditor/markdowneditor.cpp
@@ -0,0 +1,328 @@
+// Copyright (C) 2023 Tasuku Suzuki
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "markdowneditor.h"
+
+#include "textdocument.h"
+#include "texteditor.h"
+#include "texteditortr.h"
+
+#include <aggregation/aggregate.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/coreplugintr.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/minisplitter.h>
+#include <utils/stringutils.h>
+
+#include <QHBoxLayout>
+#include <QScrollBar>
+#include <QTextBrowser>
+#include <QTimer>
+#include <QToolButton>
+
+#include <optional>
+
+namespace TextEditor::Internal {
+
+const char MARKDOWNVIEWER_ID[] = "Editors.MarkdownViewer";
+const char MARKDOWNVIEWER_TEXT_CONTEXT[] = "Editors.MarkdownViewer.Text";
+const char MARKDOWNVIEWER_MIME_TYPE[] = "text/markdown";
+const char MARKDOWNVIEWER_TEXTEDITOR_RIGHT[] = "Markdown.TextEditorRight";
+const char MARKDOWNVIEWER_SHOW_EDITOR[] = "Markdown.ShowEditor";
+const char MARKDOWNVIEWER_SHOW_PREVIEW[] = "Markdown.ShowPreview";
+const bool kTextEditorRightDefault = true;
+const bool kShowEditorDefault = true;
+const bool kShowPreviewDefault = true;
+
+class MarkdownEditor : public Core::IEditor
+{
+public:
+ MarkdownEditor()
+ : m_document(new TextDocument(MARKDOWNVIEWER_ID))
+ {
+ m_document->setMimeType(MARKDOWNVIEWER_MIME_TYPE);
+
+ QSettings *s = Core::ICore::settings();
+ const bool textEditorRight
+ = s->value(MARKDOWNVIEWER_TEXTEDITOR_RIGHT, kTextEditorRightDefault).toBool();
+ const bool showPreview = s->value(MARKDOWNVIEWER_SHOW_PREVIEW, kShowPreviewDefault).toBool();
+ const bool showEditor = s->value(MARKDOWNVIEWER_SHOW_EDITOR, kShowEditorDefault).toBool()
+ || !showPreview; // ensure at least one is visible
+
+ m_splitter = new Core::MiniSplitter;
+
+ // preview
+ m_previewWidget = new QTextBrowser();
+ m_previewWidget->setOpenExternalLinks(true);
+ m_previewWidget->setFrameShape(QFrame::NoFrame);
+ new Utils::MarkdownHighlighter(m_previewWidget->document());
+
+ // editor
+ m_textEditorWidget = new TextEditorWidget;
+ m_textEditorWidget->setTextDocument(m_document);
+ m_textEditorWidget->setupGenericHighlighter();
+ m_textEditorWidget->setMarksVisible(false);
+ auto context = new Core::IContext(this);
+ context->setWidget(m_textEditorWidget);
+ context->setContext(Core::Context(MARKDOWNVIEWER_TEXT_CONTEXT));
+ Core::ICore::addContextObject(context);
+
+ m_splitter->addWidget(m_previewWidget);
+ m_splitter->addWidget(m_textEditorWidget);
+
+ setContext(Core::Context(MARKDOWNVIEWER_ID));
+
+ auto widget = new QWidget;
+ auto layout = new QVBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ widget->setLayout(layout);
+ layout->addWidget(m_splitter);
+ setWidget(widget);
+ m_widget->installEventFilter(this);
+ using namespace Aggregation;
+ Aggregate *agg = Aggregate::parentAggregate(m_textEditorWidget);
+ if (!agg) {
+ agg = new Aggregate;
+ agg->add(m_textEditorWidget);
+ }
+ agg->add(m_widget.get());
+
+ m_togglePreviewVisible = new QToolButton;
+ m_togglePreviewVisible->setText(Tr::tr("Show Preview"));
+ m_togglePreviewVisible->setCheckable(true);
+ m_togglePreviewVisible->setChecked(showPreview);
+ m_previewWidget->setVisible(showPreview);
+
+ m_toggleEditorVisible = new QToolButton;
+ m_toggleEditorVisible->setText(Tr::tr("Show Editor"));
+ m_toggleEditorVisible->setCheckable(true);
+ m_toggleEditorVisible->setChecked(showEditor);
+ m_textEditorWidget->setVisible(showEditor);
+
+ auto swapViews = new QToolButton;
+ swapViews->setText(Tr::tr("Swap Views"));
+ swapViews->setEnabled(showEditor && showPreview);
+
+ m_toolbarLayout = new QHBoxLayout(&m_toolbar);
+ m_toolbarLayout->setSpacing(0);
+ m_toolbarLayout->setContentsMargins(0, 0, 0, 0);
+ m_toolbarLayout->addStretch();
+ m_toolbarLayout->addWidget(m_togglePreviewVisible);
+ m_toolbarLayout->addWidget(m_toggleEditorVisible);
+ m_toolbarLayout->addWidget(swapViews);
+
+ setWidgetOrder(textEditorRight);
+
+ connect(m_document.data(),
+ &TextDocument::mimeTypeChanged,
+ m_document.data(),
+ &TextDocument::changed);
+
+ const auto updatePreview = [this] {
+ // save scroll positions
+ const QPoint positions = m_previewRestoreScrollPosition
+ ? *m_previewRestoreScrollPosition
+ : QPoint(m_previewWidget->horizontalScrollBar()->value(),
+ m_previewWidget->verticalScrollBar()->value());
+ m_previewRestoreScrollPosition.reset();
+
+ m_previewWidget->setMarkdown(m_document->plainText());
+
+ m_previewWidget->horizontalScrollBar()->setValue(positions.x());
+ m_previewWidget->verticalScrollBar()->setValue(positions.y());
+ };
+
+ const auto viewToggled =
+ [swapViews](QWidget *view, bool visible, QWidget *otherView, QToolButton *otherButton) {
+ if (view->isVisible() == visible)
+ return;
+ view->setVisible(visible);
+ if (visible) {
+ view->setFocus();
+ } else if (otherView->isVisible()) {
+ otherView->setFocus();
+ } else {
+ // make sure at least one view is visible
+ otherButton->toggle();
+ }
+ swapViews->setEnabled(view->isVisible() && otherView->isVisible());
+ };
+ const auto saveViewSettings = [this] {
+ Utils::QtcSettings *s = Core::ICore::settings();
+ s->setValueWithDefault(MARKDOWNVIEWER_SHOW_PREVIEW,
+ m_togglePreviewVisible->isChecked(),
+ kShowPreviewDefault);
+ s->setValueWithDefault(MARKDOWNVIEWER_SHOW_EDITOR,
+ m_toggleEditorVisible->isChecked(),
+ kShowEditorDefault);
+ };
+
+ connect(m_toggleEditorVisible,
+ &QToolButton::toggled,
+ this,
+ [this, viewToggled, saveViewSettings](bool visible) {
+ viewToggled(m_textEditorWidget,
+ visible,
+ m_previewWidget,
+ m_togglePreviewVisible);
+ saveViewSettings();
+ });
+ connect(m_togglePreviewVisible,
+ &QToolButton::toggled,
+ this,
+ [this, viewToggled, updatePreview, saveViewSettings](bool visible) {
+ viewToggled(m_previewWidget, visible, m_textEditorWidget, m_toggleEditorVisible);
+ if (visible && m_performDelayedUpdate) {
+ m_performDelayedUpdate = false;
+ updatePreview();
+ }
+ saveViewSettings();
+ });
+
+ connect(swapViews, &QToolButton::clicked, m_textEditorWidget, [this] {
+ const bool textEditorRight = isTextEditorRight();
+ setWidgetOrder(!textEditorRight);
+ // save settings
+ Utils::QtcSettings *s = Core::ICore::settings();
+ s->setValueWithDefault(MARKDOWNVIEWER_TEXTEDITOR_RIGHT,
+ !textEditorRight,
+ kTextEditorRightDefault);
+ });
+
+ // TODO directly update when we build with Qt 6.5.2
+ m_previewTimer.setInterval(500);
+ m_previewTimer.setSingleShot(true);
+ connect(&m_previewTimer, &QTimer::timeout, this, [this, updatePreview] {
+ if (m_togglePreviewVisible->isChecked())
+ updatePreview();
+ else
+ m_performDelayedUpdate = true;
+ });
+
+ connect(m_document->document(), &QTextDocument::contentsChanged, &m_previewTimer, [this] {
+ m_previewTimer.start();
+ });
+ }
+
+ bool isTextEditorRight() const { return m_splitter->widget(0) == m_previewWidget; }
+
+ void setWidgetOrder(bool textEditorRight)
+ {
+ QTC_ASSERT(m_splitter->count() > 1, return);
+ QWidget *left = textEditorRight ? static_cast<QWidget *>(m_previewWidget)
+ : m_textEditorWidget;
+ QWidget *right = textEditorRight ? static_cast<QWidget *>(m_textEditorWidget)
+ : m_previewWidget;
+ m_splitter->insertWidget(0, left);
+ m_splitter->insertWidget(1, right);
+ // buttons
+ QWidget *leftButton = textEditorRight ? m_togglePreviewVisible : m_toggleEditorVisible;
+ QWidget *rightButton = textEditorRight ? m_toggleEditorVisible : m_togglePreviewVisible;
+ const int rightIndex = m_toolbarLayout->count() - 2;
+ m_toolbarLayout->insertWidget(rightIndex, leftButton);
+ m_toolbarLayout->insertWidget(rightIndex, rightButton);
+ }
+
+ QWidget *toolBar() override { return &m_toolbar; }
+
+ Core::IDocument *document() const override { return m_document.data(); }
+ TextEditorWidget *textEditorWidget() const { return m_textEditorWidget; }
+ int currentLine() const override { return textEditorWidget()->textCursor().blockNumber() + 1; };
+ int currentColumn() const override
+ {
+ QTextCursor cursor = textEditorWidget()->textCursor();
+ return cursor.position() - cursor.block().position() + 1;
+ }
+ void gotoLine(int line, int column, bool centerLine) override
+ {
+ if (!m_toggleEditorVisible->isChecked())
+ m_toggleEditorVisible->toggle();
+ textEditorWidget()->gotoLine(line, column, centerLine);
+ }
+
+ bool eventFilter(QObject *obj, QEvent *ev) override
+ {
+ if (obj == m_widget && ev->type() == QEvent::FocusIn) {
+ if (m_splitter->focusWidget())
+ m_splitter->focusWidget()->setFocus();
+ else
+ m_splitter->widget(0)->setFocus();
+ return true;
+ }
+ return Core::IEditor::eventFilter(obj, ev);
+ }
+
+ QByteArray saveState() const override
+ {
+ QByteArray state;
+ QDataStream stream(&state, QIODevice::WriteOnly);
+ stream << 1; // version number
+ stream << m_textEditorWidget->saveState();
+ stream << m_previewWidget->horizontalScrollBar()->value();
+ stream << m_previewWidget->verticalScrollBar()->value();
+ stream << isTextEditorRight();
+ stream << m_togglePreviewVisible->isChecked();
+ stream << m_toggleEditorVisible->isChecked();
+ stream << m_splitter->saveState();
+ return state;
+ }
+
+ void restoreState(const QByteArray &state) override
+ {
+ if (state.isEmpty())
+ return;
+ int version;
+ QByteArray editorState;
+ int previewHV;
+ int previewVV;
+ bool textEditorRight;
+ bool previewShown;
+ bool textEditorShown;
+ QByteArray splitterState;
+ QDataStream stream(state);
+ stream >> version;
+ stream >> editorState;
+ stream >> previewHV;
+ stream >> previewVV;
+ stream >> textEditorRight;
+ stream >> previewShown;
+ stream >> textEditorShown;
+ stream >> splitterState;
+ m_textEditorWidget->restoreState(editorState);
+ m_previewRestoreScrollPosition.emplace(previewHV, previewVV);
+ setWidgetOrder(textEditorRight);
+ m_splitter->restoreState(splitterState);
+ m_togglePreviewVisible->setChecked(previewShown);
+ // ensure at least one is shown
+ m_toggleEditorVisible->setChecked(textEditorShown || !previewShown);
+ }
+
+private:
+ QTimer m_previewTimer;
+ bool m_performDelayedUpdate = false;
+ Core::MiniSplitter *m_splitter;
+ QTextBrowser *m_previewWidget;
+ TextEditorWidget *m_textEditorWidget;
+ TextDocumentPtr m_document;
+ QWidget m_toolbar;
+ QHBoxLayout *m_toolbarLayout;
+ QToolButton *m_toggleEditorVisible;
+ QToolButton *m_togglePreviewVisible;
+ std::optional<QPoint> m_previewRestoreScrollPosition;
+};
+
+MarkdownEditorFactory::MarkdownEditorFactory()
+ : m_actionHandler(MARKDOWNVIEWER_ID,
+ MARKDOWNVIEWER_TEXT_CONTEXT,
+ TextEditor::TextEditorActionHandler::None,
+ [](Core::IEditor *editor) {
+ return static_cast<MarkdownEditor *>(editor)->textEditorWidget();
+ })
+{
+ setId(MARKDOWNVIEWER_ID);
+ setDisplayName(::Core::Tr::tr("Markdown Viewer"));
+ addMimeType(MARKDOWNVIEWER_MIME_TYPE);
+ setEditorCreator([] { return new MarkdownEditor; });
+}
+
+} // namespace TextEditor::Internal
diff --git a/src/plugins/texteditor/markdowneditor.h b/src/plugins/texteditor/markdowneditor.h
new file mode 100644
index 0000000000..de87c558e1
--- /dev/null
+++ b/src/plugins/texteditor/markdowneditor.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 Tasuku Suzuki
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <coreplugin/editormanager/ieditorfactory.h>
+
+#include <texteditor/texteditoractionhandler.h>
+
+namespace TextEditor::Internal {
+
+class MarkdownEditorFactory final : public Core::IEditorFactory
+{
+public:
+ MarkdownEditorFactory();
+
+private:
+ TextEditor::TextEditorActionHandler m_actionHandler;
+};
+
+} // TextEditor::Internal
diff --git a/src/plugins/texteditor/outlinefactory.cpp b/src/plugins/texteditor/outlinefactory.cpp
index 76222642c1..bc9aa03c8b 100644
--- a/src/plugins/texteditor/outlinefactory.cpp
+++ b/src/plugins/texteditor/outlinefactory.cpp
@@ -12,6 +12,7 @@
#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
+#include <utils/stylehelper.h>
#include <QDebug>
#include <QLabel>
@@ -63,6 +64,7 @@ OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) :
this, &OutlineWidgetStack::toggleCursorSynchronization);
m_filterButton = new QToolButton(this);
+ Utils::StyleHelper::setPanelWidget(m_filterButton);
// The ToolButton needs a parent because updateFilterMenu() sets
// it visible. That would open a top-level window if the button
// did not have a parent in that moment.
@@ -70,11 +72,12 @@ OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) :
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
m_filterButton->setToolTip(Tr::tr("Filter tree"));
m_filterButton->setPopupMode(QToolButton::InstantPopup);
- m_filterButton->setProperty("noArrow", true);
+ m_filterButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true);
m_filterMenu = new QMenu(m_filterButton);
m_filterButton->setMenu(m_filterMenu);
m_toggleSort = new QToolButton(this);
+ Utils::StyleHelper::setPanelWidget(m_toggleSort);
m_toggleSort->setIcon(Utils::Icons::SORT_ALPHABETICALLY_TOOLBAR.icon());
m_toggleSort->setCheckable(true);
m_toggleSort->setChecked(false);
diff --git a/src/plugins/texteditor/quickfix.h b/src/plugins/texteditor/quickfix.h
index 6469bd7d8b..e456230ac2 100644
--- a/src/plugins/texteditor/quickfix.h
+++ b/src/plugins/texteditor/quickfix.h
@@ -35,8 +35,7 @@ public:
virtual ~QuickFixOperation();
/*!
- \returns The priority for this quick-fix. See the QuickFixCollector for more
- information.
+ Returns The priority for this quick-fix. See the QuickFixCollector for more information.
*/
virtual int priority() const;
@@ -44,8 +43,7 @@ public:
void setPriority(int priority);
/*!
- \returns The description for this quick-fix. This description is shown to the
- user.
+ Returns The description for this quick-fix. This description is shown to the user.
*/
virtual QString description() const;
diff --git a/src/plugins/texteditor/snippets/snippet.cpp b/src/plugins/texteditor/snippets/snippet.cpp
index f41156c4ce..74a4dc543c 100644
--- a/src/plugins/texteditor/snippets/snippet.cpp
+++ b/src/plugins/texteditor/snippets/snippet.cpp
@@ -330,7 +330,7 @@ void Internal::TextEditorPlugin::testSnippetParsing_data()
<< QString::fromLatin1("\\\\$test\\\\\\\\$\\\\") << false << Parts();
QTest::newRow("Q_PROPERTY") << QString(
- "Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed)")
+ "Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed FINAL)")
<< true
<< Parts{SnippetPart("Q_PROPERTY("),
SnippetPart("type", 0),
@@ -342,7 +342,7 @@ void Internal::TextEditorPlugin::testSnippetParsing_data()
SnippetPart("name", 1, TCMANGLER_ID),
SnippetPart(" NOTIFY "),
SnippetPart("name", 1),
- SnippetPart("Changed)")};
+ SnippetPart("Changed FINAL)")};
QTest::newRow("open identifier") << QString("$test") << false << Parts();
QTest::newRow("wrong mangler") << QString("$test:X$") << false << Parts();
diff --git a/src/plugins/texteditor/snippets/snippetscollection.cpp b/src/plugins/texteditor/snippets/snippetscollection.cpp
index be658c1477..f799b3fe42 100644
--- a/src/plugins/texteditor/snippets/snippetscollection.cpp
+++ b/src/plugins/texteditor/snippets/snippetscollection.cpp
@@ -78,9 +78,7 @@ SnippetsCollection::SnippetsCollection()
m_builtInSnippetsFiles(Core::ICore::resourcePath("snippets")
.dirEntries(FileFilter({"*.xml"})))
{
-
- connect(Core::ICore::instance(), &Core::ICore::coreOpened,
- this, &SnippetsCollection::identifyGroups);
+ identifyGroups();
}
SnippetsCollection::~SnippetsCollection() = default;
diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.cpp b/src/plugins/texteditor/snippets/snippetssettingspage.cpp
index b64910a0f1..e4bfdf7df8 100644
--- a/src/plugins/texteditor/snippets/snippetssettingspage.cpp
+++ b/src/plugins/texteditor/snippets/snippetssettingspage.cpp
@@ -35,15 +35,14 @@
#include <QStackedWidget>
#include <QTextStream>
-namespace TextEditor {
-namespace Internal {
+namespace TextEditor::Internal {
// SnippetsTableModel
+
class SnippetsTableModel : public QAbstractTableModel
{
- Q_OBJECT
public:
- SnippetsTableModel(QObject *parent);
+ SnippetsTableModel();
~SnippetsTableModel() override = default;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -74,9 +73,8 @@ private:
QString m_activeGroupId;
};
-SnippetsTableModel::SnippetsTableModel(QObject *parent) :
- QAbstractTableModel(parent),
- m_collection(SnippetsCollection::instance())
+SnippetsTableModel::SnippetsTableModel()
+ : m_collection(SnippetsCollection::instance())
{}
int SnippetsTableModel::rowCount(const QModelIndex &) const
@@ -244,19 +242,15 @@ void SnippetsTableModel::replaceSnippet(const Snippet &snippet, const QModelInde
}
}
-// SnippetsSettingsPagePrivate
-class SnippetsSettingsPagePrivate : public QObject
+// SnippetsSettingsWidget
+
+class SnippetsSettingsWidget : public Core::IOptionsPageWidget
{
public:
- SnippetsSettingsPagePrivate();
- ~SnippetsSettingsPagePrivate() override { delete m_model; }
-
- void configureUi(QWidget *parent);
+ SnippetsSettingsWidget();
- void apply();
- void finish();
-
- QPointer<QWidget> m_widget;
+ void apply() final;
+ void finish() final;
private:
void loadSnippetGroup(int index);
@@ -279,9 +273,9 @@ private:
bool settingsChanged() const;
void writeSettings();
- const QString m_settingsPrefix;
- SnippetsTableModel *m_model;
- bool m_snippetsCollectionChanged;
+ const QString m_settingsPrefix{QLatin1String("Text")};
+ SnippetsTableModel m_model;
+ bool m_snippetsCollectionChanged = false;
SnippetsSettings m_settings;
QStackedWidget *m_snippetsEditorStack;
@@ -290,38 +284,22 @@ private:
QPushButton *m_revertButton;
};
-SnippetsSettingsPagePrivate::SnippetsSettingsPagePrivate() :
- m_settingsPrefix(QLatin1String("Text")),
- m_model(new SnippetsTableModel(nullptr)),
- m_snippetsCollectionChanged(false)
-{}
-
-SnippetEditorWidget *SnippetsSettingsPagePrivate::currentEditor() const
-{
- return editorAt(m_snippetsEditorStack->currentIndex());
-}
-
-SnippetEditorWidget *SnippetsSettingsPagePrivate::editorAt(int i) const
-{
- return static_cast<SnippetEditorWidget *>(m_snippetsEditorStack->widget(i));
-}
-
-void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
+SnippetsSettingsWidget::SnippetsSettingsWidget()
{
m_groupCombo = new QComboBox;
m_snippetsEditorStack = new QStackedWidget;
for (const SnippetProvider &provider : SnippetProvider::snippetProviders()) {
m_groupCombo->addItem(provider.displayName(), provider.groupId());
- auto snippetEditor = new SnippetEditorWidget(w);
+ auto snippetEditor = new SnippetEditorWidget(this);
SnippetProvider::decorateEditor(snippetEditor, provider.groupId());
m_snippetsEditorStack->insertWidget(m_groupCombo->count() - 1, snippetEditor);
connect(snippetEditor, &SnippetEditorWidget::snippetContentChanged,
- this, &SnippetsSettingsPagePrivate::setSnippetContent);
+ this, &SnippetsSettingsWidget::setSnippetContent);
}
m_snippetsTable = new Utils::TreeView;
m_snippetsTable->setRootIsDecorated(false);
- m_snippetsTable->setModel(m_model);
+ m_snippetsTable->setModel(&m_model);
m_revertButton = new QPushButton(Tr::tr("Revert Built-in"));
m_revertButton->setEnabled(false);
@@ -332,7 +310,7 @@ void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
snippetSplitter->addWidget(m_snippetsTable);
snippetSplitter->addWidget(m_snippetsEditorStack);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { Tr::tr("Group:"), m_groupCombo, st },
Row {
@@ -350,40 +328,50 @@ void SnippetsSettingsPagePrivate::configureUi(QWidget *w)
st,
}
}
- }.attachTo(w);
+ }.attachTo(this);
loadSettings();
loadSnippetGroup(m_groupCombo->currentIndex());
- connect(m_model, &QAbstractItemModel::rowsInserted,
- this, &SnippetsSettingsPagePrivate::selectSnippet);
- connect(m_model, &QAbstractItemModel::rowsInserted,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::rowsRemoved,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::rowsMoved,
- this, &SnippetsSettingsPagePrivate::selectMovedSnippet);
- connect(m_model, &QAbstractItemModel::rowsMoved,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::dataChanged,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
- connect(m_model, &QAbstractItemModel::modelReset,
+ connect(&m_model, &QAbstractItemModel::rowsInserted,
+ this, &SnippetsSettingsWidget::selectSnippet);
+ connect(&m_model, &QAbstractItemModel::rowsInserted,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::rowsRemoved,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::rowsMoved,
+ this, &SnippetsSettingsWidget::selectMovedSnippet);
+ connect(&m_model, &QAbstractItemModel::rowsMoved,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::dataChanged,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::modelReset,
this, [this] { this->updateCurrentSnippetDependent(); });
- connect(m_model, &QAbstractItemModel::modelReset,
- this, &SnippetsSettingsPagePrivate::markSnippetsCollection);
+ connect(&m_model, &QAbstractItemModel::modelReset,
+ this, &SnippetsSettingsWidget::markSnippetsCollection);
connect(m_groupCombo, &QComboBox::currentIndexChanged,
- this, &SnippetsSettingsPagePrivate::loadSnippetGroup);
+ this, &SnippetsSettingsWidget::loadSnippetGroup);
connect(m_revertButton, &QAbstractButton::clicked,
- this, &SnippetsSettingsPagePrivate::revertBuiltInSnippet);
+ this, &SnippetsSettingsWidget::revertBuiltInSnippet);
connect(m_snippetsTable->selectionModel(), &QItemSelectionModel::currentChanged,
- this, &SnippetsSettingsPagePrivate::updateCurrentSnippetDependent);
+ this, &SnippetsSettingsWidget::updateCurrentSnippetDependent);
connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
- this, &SnippetsSettingsPagePrivate::decorateEditors);
+ this, &SnippetsSettingsWidget::decorateEditors);
+}
+
+SnippetEditorWidget *SnippetsSettingsWidget::currentEditor() const
+{
+ return editorAt(m_snippetsEditorStack->currentIndex());
}
-void SnippetsSettingsPagePrivate::apply()
+SnippetEditorWidget *SnippetsSettingsWidget::editorAt(int i) const
+{
+ return static_cast<SnippetEditorWidget *>(m_snippetsEditorStack->widget(i));
+}
+
+void SnippetsSettingsWidget::apply()
{
if (settingsChanged())
writeSettings();
@@ -402,7 +390,7 @@ void SnippetsSettingsPagePrivate::apply()
}
}
-void SnippetsSettingsPagePrivate::finish()
+void SnippetsSettingsWidget::finish()
{
if (m_snippetsCollectionChanged) {
SnippetsCollection::instance()->reload();
@@ -412,7 +400,7 @@ void SnippetsSettingsPagePrivate::finish()
disconnect(TextEditorSettings::instance(), nullptr, this, nullptr);
}
-void SnippetsSettingsPagePrivate::loadSettings()
+void SnippetsSettingsWidget::loadSettings()
{
if (m_groupCombo->count() == 0)
return;
@@ -426,7 +414,7 @@ void SnippetsSettingsPagePrivate::loadSettings()
m_groupCombo->setCurrentIndex(0);
}
-void SnippetsSettingsPagePrivate::writeSettings()
+void SnippetsSettingsWidget::writeSettings()
{
if (m_groupCombo->count() == 0)
return;
@@ -435,72 +423,72 @@ void SnippetsSettingsPagePrivate::writeSettings()
m_settings.toSettings(m_settingsPrefix, Core::ICore::settings());
}
-bool SnippetsSettingsPagePrivate::settingsChanged() const
+bool SnippetsSettingsWidget::settingsChanged() const
{
if (m_settings.lastUsedSnippetGroup() != m_groupCombo->currentText())
return true;
return false;
}
-void SnippetsSettingsPagePrivate::loadSnippetGroup(int index)
+void SnippetsSettingsWidget::loadSnippetGroup(int index)
{
if (index == -1)
return;
m_snippetsEditorStack->setCurrentIndex(index);
currentEditor()->clear();
- m_model->load(m_groupCombo->itemData(index).toString());
+ m_model.load(m_groupCombo->itemData(index).toString());
}
-void SnippetsSettingsPagePrivate::markSnippetsCollection()
+void SnippetsSettingsWidget::markSnippetsCollection()
{
if (!m_snippetsCollectionChanged)
m_snippetsCollectionChanged = true;
}
-void SnippetsSettingsPagePrivate::addSnippet()
+void SnippetsSettingsWidget::addSnippet()
{
- const QModelIndex &modelIndex = m_model->createSnippet();
+ const QModelIndex &modelIndex = m_model.createSnippet();
selectSnippet(QModelIndex(), modelIndex.row());
m_snippetsTable->edit(modelIndex);
}
-void SnippetsSettingsPagePrivate::removeSnippet()
+void SnippetsSettingsWidget::removeSnippet()
{
const QModelIndex &modelIndex = m_snippetsTable->selectionModel()->currentIndex();
if (!modelIndex.isValid()) {
QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Error"), Tr::tr("No snippet selected."));
return;
}
- m_model->removeSnippet(modelIndex);
+ m_model.removeSnippet(modelIndex);
}
-void SnippetsSettingsPagePrivate::restoreRemovedBuiltInSnippets()
+void SnippetsSettingsWidget::restoreRemovedBuiltInSnippets()
{
- m_model->restoreRemovedBuiltInSnippets();
+ m_model.restoreRemovedBuiltInSnippets();
}
-void SnippetsSettingsPagePrivate::revertBuiltInSnippet()
+void SnippetsSettingsWidget::revertBuiltInSnippet()
{
- m_model->revertBuitInSnippet(m_snippetsTable->selectionModel()->currentIndex());
+ m_model.revertBuitInSnippet(m_snippetsTable->selectionModel()->currentIndex());
}
-void SnippetsSettingsPagePrivate::resetAllSnippets()
+void SnippetsSettingsWidget::resetAllSnippets()
{
- m_model->resetSnippets();
+ m_model.resetSnippets();
}
-void SnippetsSettingsPagePrivate::selectSnippet(const QModelIndex &parent, int row)
+void SnippetsSettingsWidget::selectSnippet(const QModelIndex &parent, int row)
{
- QModelIndex topLeft = m_model->index(row, 0, parent);
- QModelIndex bottomRight = m_model->index(row, 1, parent);
+ QModelIndex topLeft = m_model.index(row, 0, parent);
+ QModelIndex bottomRight = m_model.index(row, 1, parent);
QItemSelection selection(topLeft, bottomRight);
m_snippetsTable->selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
m_snippetsTable->setCurrentIndex(topLeft);
m_snippetsTable->scrollTo(topLeft);
}
-void SnippetsSettingsPagePrivate::selectMovedSnippet(const QModelIndex &,
+void SnippetsSettingsWidget::selectMovedSnippet(const QModelIndex &,
int sourceRow,
int,
const QModelIndex &destinationParent,
@@ -508,17 +496,17 @@ void SnippetsSettingsPagePrivate::selectMovedSnippet(const QModelIndex &,
{
QModelIndex modelIndex;
if (sourceRow < destinationRow)
- modelIndex = m_model->index(destinationRow - 1, 0, destinationParent);
+ modelIndex = m_model.index(destinationRow - 1, 0, destinationParent);
else
- modelIndex = m_model->index(destinationRow, 0, destinationParent);
+ modelIndex = m_model.index(destinationRow, 0, destinationParent);
m_snippetsTable->scrollTo(modelIndex);
- currentEditor()->setPlainText(m_model->snippetAt(modelIndex).content());
+ currentEditor()->setPlainText(m_model.snippetAt(modelIndex).content());
}
-void SnippetsSettingsPagePrivate::updateCurrentSnippetDependent(const QModelIndex &modelIndex)
+void SnippetsSettingsWidget::updateCurrentSnippetDependent(const QModelIndex &modelIndex)
{
if (modelIndex.isValid()) {
- const Snippet &snippet = m_model->snippetAt(modelIndex);
+ const Snippet &snippet = m_model.snippetAt(modelIndex);
currentEditor()->setPlainText(snippet.content());
m_revertButton->setEnabled(snippet.isBuiltIn());
} else {
@@ -527,16 +515,16 @@ void SnippetsSettingsPagePrivate::updateCurrentSnippetDependent(const QModelInde
}
}
-void SnippetsSettingsPagePrivate::setSnippetContent()
+void SnippetsSettingsWidget::setSnippetContent()
{
const QModelIndex &modelIndex = m_snippetsTable->selectionModel()->currentIndex();
if (modelIndex.isValid()) {
- m_model->setSnippetContent(modelIndex, currentEditor()->toPlainText());
+ m_model.setSnippetContent(modelIndex, currentEditor()->toPlainText());
markSnippetsCollection();
}
}
-void SnippetsSettingsPagePrivate::decorateEditors(const TextEditor::FontSettings &fontSettings)
+void SnippetsSettingsWidget::decorateEditors(const TextEditor::FontSettings &fontSettings)
{
for (int i = 0; i < m_groupCombo->count(); ++i) {
SnippetEditorWidget *snippetEditor = editorAt(i);
@@ -550,41 +538,13 @@ void SnippetsSettingsPagePrivate::decorateEditors(const TextEditor::FontSettings
// SnippetsSettingsPage
SnippetsSettingsPage::SnippetsSettingsPage()
- : d(new SnippetsSettingsPagePrivate)
{
setId(Constants::TEXT_EDITOR_SNIPPETS_SETTINGS);
setDisplayName(Tr::tr("Snippets"));
setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("Text Editor"));
setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH);
+ setWidgetCreator([] { return new SnippetsSettingsWidget; });
}
-SnippetsSettingsPage::~SnippetsSettingsPage()
-{
- delete d;
-}
-
-QWidget *SnippetsSettingsPage::widget()
-{
- if (!d->m_widget) {
- d->m_widget = new QWidget;
- d->configureUi(d->m_widget);
- }
- return d->m_widget;
-}
-
-void SnippetsSettingsPage::apply()
-{
- d->apply();
-}
-
-void SnippetsSettingsPage::finish()
-{
- d->finish();
- delete d->m_widget;
-}
-
-} // Internal
-} // TextEditor
-
-#include "snippetssettingspage.moc"
+} // TextEditor::Internal
diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.h b/src/plugins/texteditor/snippets/snippetssettingspage.h
index f7c4b8c3eb..31216d3d30 100644
--- a/src/plugins/texteditor/snippets/snippetssettingspage.h
+++ b/src/plugins/texteditor/snippets/snippetssettingspage.h
@@ -5,24 +5,12 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace TextEditor {
-namespace Internal {
-
-class SnippetsSettingsPagePrivate;
+namespace TextEditor::Internal {
class SnippetsSettingsPage final : public Core::IOptionsPage
{
public:
SnippetsSettingsPage();
- ~SnippetsSettingsPage() override;
-
- QWidget *widget() override;
- void apply() override;
- void finish() override;
-
-private:
- SnippetsSettingsPagePrivate *d;
};
-} // Internal
-} // TextEditor
+} // TextEditor::Internal
diff --git a/src/plugins/texteditor/tabsettingswidget.cpp b/src/plugins/texteditor/tabsettingswidget.cpp
index f55d8d6f44..f3476ec5f4 100644
--- a/src/plugins/texteditor/tabsettingswidget.cpp
+++ b/src/plugins/texteditor/tabsettingswidget.cpp
@@ -50,7 +50,6 @@ QString continuationTooltip()
TabSettingsWidget::TabSettingsWidget(QWidget *parent) :
QGroupBox(parent)
{
- resize(254, 189);
setTitle(Tr::tr("Tabs And Indentation"));
m_codingStyleWarning = new QLabel(
@@ -87,7 +86,7 @@ TabSettingsWidget::TabSettingsWidget(QWidget *parent) :
tabSizeLabel->setBuddy(m_tabSize);
indentSizeLabel->setBuddy(m_indentSize);
- using namespace Utils::Layouting;
+ using namespace Layouting;
const auto indent = [](QWidget *inner) { return Row { Space(30), inner }; };
Column {
diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp
index 61c3b319b3..6276d731d0 100644
--- a/src/plugins/texteditor/textdocument.cpp
+++ b/src/plugins/texteditor/textdocument.cpp
@@ -373,6 +373,16 @@ QAction *TextDocument::createDiffAgainstCurrentFileAction(
return diffAction;
}
+void TextDocument::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ QTextCursor cursor(&d->m_document);
+ cursor.setPosition(suggestion->position());
+ const QTextBlock block = cursor.block();
+ TextDocumentLayout::userData(block)->insertSuggestion(std::move(suggestion));
+ TextDocumentLayout::updateSuggestionFormats(block, fontSettings());
+ updateLayout();
+}
+
#ifdef WITH_TESTS
void TextDocument::setSilentReload()
{
@@ -419,6 +429,12 @@ IAssistProvider *TextDocument::quickFixAssistProvider() const
void TextDocument::applyFontSettings()
{
d->m_fontSettingsNeedsApply = false;
+ QTextBlock block = document()->firstBlock();
+ while (block.isValid()) {
+ TextDocumentLayout::updateSuggestionFormats(block, fontSettings());
+ block = block.next();
+ }
+ updateLayout();
if (d->m_highlighter) {
d->m_highlighter->setFontSettings(d->m_fontSettings);
d->m_highlighter->rehighlight();
@@ -820,15 +836,14 @@ bool TextDocument::reload(QString *errorString, const FilePath &realFilePath)
emit aboutToReload();
auto documentLayout =
qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
- TextMarks marks;
if (documentLayout)
- marks = documentLayout->documentClosing(); // removes text marks non-permanently
+ documentLayout->documentAboutToReload(); // removes text marks non-permanently
bool success = openImpl(errorString, filePath(), realFilePath, /*reload =*/true)
== OpenResult::Success;
if (documentLayout)
- documentLayout->documentReloaded(marks, this); // re-adds text marks
+ documentLayout->documentReloaded(this); // re-adds text marks
emit reloadFinished(success);
return success;
}
diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h
index dc52e1e9a8..d1f686f6a0 100644
--- a/src/plugins/texteditor/textdocument.h
+++ b/src/plugins/texteditor/textdocument.h
@@ -37,6 +37,7 @@ class SyntaxHighlighter;
class TabSettings;
class TextDocumentPrivate;
class TextMark;
+class TextSuggestion;
class TypingSettings;
using TextMarks = QList<TextMark *>;
@@ -144,6 +145,9 @@ public:
static QAction *createDiffAgainstCurrentFileAction(QObject *parent,
const std::function<Utils::FilePath()> &filePath);
+ void insertSuggestion(const QString &text, const QTextCursor &cursor);
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+
#ifdef WITH_TESTS
void setSilentReload();
#endif
diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp
index 1f26530dbb..703f9563bf 100644
--- a/src/plugins/texteditor/textdocumentlayout.cpp
+++ b/src/plugins/texteditor/textdocumentlayout.cpp
@@ -345,6 +345,21 @@ void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
m_codeFormatterData = data;
}
+void TextBlockUserData::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ m_suggestion = std::move(suggestion);
+}
+
+TextSuggestion *TextBlockUserData::suggestion() const
+{
+ return m_suggestion.get();
+}
+
+void TextBlockUserData::clearSuggestion()
+{
+ m_suggestion.reset();
+}
+
void TextBlockUserData::addMark(TextMark *mark)
{
int i = 0;
@@ -355,7 +370,6 @@ void TextBlockUserData::addMark(TextMark *mark)
m_marks.insert(i, mark);
}
-
TextDocumentLayout::TextDocumentLayout(QTextDocument *doc)
: QPlainTextDocumentLayout(doc)
{}
@@ -519,6 +533,81 @@ QByteArray TextDocumentLayout::expectedRawStringSuffix(const QTextBlock &block)
return {};
}
+TextSuggestion *TextDocumentLayout::suggestion(const QTextBlock &block)
+{
+ if (TextBlockUserData *userData = textUserData(block))
+ return userData->suggestion();
+ return nullptr;
+}
+
+void TextDocumentLayout::updateSuggestionFormats(const QTextBlock &block,
+ const FontSettings &fontSettings)
+{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ QTextDocument *suggestionDoc = suggestion->document();
+ const QTextCharFormat replacementFormat = fontSettings.toTextCharFormat(
+ TextStyles{C_TEXT, {C_DISABLED_CODE}});
+ QList<QTextLayout::FormatRange> formats = block.layout()->formats();
+ QTextCursor cursor(suggestionDoc);
+ cursor.select(QTextCursor::Document);
+ cursor.setCharFormat(fontSettings.toTextCharFormat(C_TEXT));
+ const int position = suggestion->currentPosition() - block.position();
+ cursor.setPosition(position);
+ const QString trailingText = block.text().mid(position);
+ if (!trailingText.isEmpty()) {
+ const int trailingIndex = suggestionDoc->firstBlock().text().indexOf(trailingText,
+ position);
+ if (trailingIndex >= 0) {
+ cursor.setPosition(trailingIndex, QTextCursor::KeepAnchor);
+ cursor.setCharFormat(replacementFormat);
+ cursor.setPosition(trailingIndex + trailingText.size());
+ const int length = std::max(trailingIndex - position, 0);
+ if (length) {
+ // we have a replacement in the middle of the line adjust all formats that are
+ // behind the replacement
+ QTextLayout::FormatRange rest;
+ rest.start = -1;
+ for (QTextLayout::FormatRange &range : formats) {
+ if (range.start >= position) {
+ range.start += length;
+ } else if (range.start + range.length > position) {
+ // the format range starts before and ends after the position so we need to
+ // split the format into before and after the suggestion format ranges
+ rest.start = trailingIndex;
+ rest.length = range.length - (position - range.start);
+ rest.format = range.format;
+ range.length = position - range.start;
+ }
+ }
+ if (rest.start >= 0)
+ formats += rest;
+ }
+ }
+ }
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ cursor.setCharFormat(replacementFormat);
+ suggestionDoc->firstBlock().layout()->setFormats(formats);
+ }
+}
+
+bool TextDocumentLayout::updateSuggestion(const QTextBlock &block,
+ int position,
+ const FontSettings &fontSettings)
+{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ auto positionInBlock = position - block.position();
+ const QString start = block.text().left(positionInBlock);
+ const QString end = block.text().mid(positionInBlock);
+ const QString replacement = suggestion->document()->firstBlock().text();
+ if (replacement.startsWith(start) && replacement.indexOf(end, start.size()) >= 0) {
+ suggestion->setCurrentPosition(position);
+ TextDocumentLayout::updateSuggestionFormats(block, fontSettings);
+ return true;
+ }
+ }
+ return false;
+}
+
void TextDocumentLayout::requestExtraAreaUpdate()
{
emit updateExtraArea();
@@ -567,6 +656,7 @@ QSizeF TextDocumentLayout::documentSize() const
TextMarks TextDocumentLayout::documentClosing()
{
+ QTC_ASSERT(m_reloadMarks.isEmpty(), resetReloadMarks());
TextMarks marks;
for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) {
if (auto data = static_cast<TextBlockUserData *>(block.userData()))
@@ -575,9 +665,18 @@ TextMarks TextDocumentLayout::documentClosing()
return marks;
}
-void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTextDocument)
+void TextDocumentLayout::documentAboutToReload()
+{
+ m_reloadMarks = documentClosing();
+ for (TextMark *mark : std::as_const(m_reloadMarks))
+ mark->setDeleteCallback([this, mark] { m_reloadMarks.removeOne(mark); });
+}
+
+void TextDocumentLayout::documentReloaded(TextDocument *baseTextDocument)
{
- for (TextMark *mark : std::as_const(marks)) {
+ const TextMarks marks = m_reloadMarks;
+ resetReloadMarks();
+ for (TextMark *mark : marks) {
int blockNumber = mark->lineNumber() - 1;
QTextBlock block = document()->findBlockByNumber(blockNumber);
if (block.isValid()) {
@@ -632,8 +731,37 @@ void TextDocumentLayout::requestUpdateNow()
requestUpdate();
}
+void TextDocumentLayout::resetReloadMarks()
+{
+ for (TextMark *mark : std::as_const(m_reloadMarks))
+ mark->setDeleteCallback({});
+ m_reloadMarks.clear();
+}
+
+static QRectF replacementBoundingRect(const QTextDocument *replacement)
+{
+ QTC_ASSERT(replacement, return {});
+ auto *layout = static_cast<QPlainTextDocumentLayout *>(replacement->documentLayout());
+ QRectF boundingRect;
+ QTextBlock block = replacement->firstBlock();
+ while (block.isValid()) {
+ const QRectF blockBoundingRect = layout->blockBoundingRect(block);
+ boundingRect.setWidth(std::max(boundingRect.width(), blockBoundingRect.width()));
+ boundingRect.setHeight(boundingRect.height() + blockBoundingRect.height());
+ block = block.next();
+ }
+ return boundingRect;
+}
+
QRectF TextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ // since multiple code paths expects that we have a valid block layout after requesting the
+ // block bounding rect explicitly create that layout here
+ ensureBlockLayout(block);
+ return replacementBoundingRect(suggestion->document());
+ }
+
QRectF boundingRect = QPlainTextDocumentLayout::blockBoundingRect(block);
if (TextEditorSettings::fontSettings().relativeLineSpacing() != 100) {
@@ -722,4 +850,12 @@ void insertSorted(Parentheses &list, const Parenthesis &elem)
list.insert(it, elem);
}
+TextSuggestion::TextSuggestion()
+{
+ m_replacementDocument.setDocumentLayout(new TextDocumentLayout(&m_replacementDocument));
+ m_replacementDocument.setDocumentMargin(0);
+}
+
+TextSuggestion::~TextSuggestion() = default;
+
} // namespace TextEditor
diff --git a/src/plugins/texteditor/textdocumentlayout.h b/src/plugins/texteditor/textdocumentlayout.h
index ba31398db7..d6f847e481 100644
--- a/src/plugins/texteditor/textdocumentlayout.h
+++ b/src/plugins/texteditor/textdocumentlayout.h
@@ -42,6 +42,28 @@ public:
virtual ~CodeFormatterData();
};
+class TEXTEDITOR_EXPORT TextSuggestion
+{
+public:
+ TextSuggestion();
+ virtual ~TextSuggestion();
+ // Returns true if the suggestion was applied completely, false if it was only partially applied.
+ virtual bool apply() = 0;
+ // Returns true if the suggestion was applied completely, false if it was only partially applied.
+ virtual bool applyWord(TextEditorWidget *widget) = 0;
+ virtual void reset() = 0;
+ virtual int position() = 0;
+
+ int currentPosition() const { return m_currentPosition; }
+ void setCurrentPosition(int position) { m_currentPosition = position; }
+
+ QTextDocument *document() { return &m_replacementDocument; }
+
+private:
+ QTextDocument m_replacementDocument;
+ int m_currentPosition = -1;
+};
+
class TEXTEDITOR_EXPORT TextBlockUserData : public QTextBlockUserData
{
public:
@@ -126,6 +148,10 @@ public:
QByteArray expectedRawStringSuffix() { return m_expectedRawStringSuffix; }
void setExpectedRawStringSuffix(const QByteArray &suffix) { m_expectedRawStringSuffix = suffix; }
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+ TextSuggestion *suggestion() const;
+ void clearSuggestion();
+
private:
TextMarks m_marks;
int m_foldingIndent : 16;
@@ -139,9 +165,10 @@ private:
CodeFormatterData *m_codeFormatterData;
KSyntaxHighlighting::State m_syntaxState;
QByteArray m_expectedRawStringSuffix; // A bit C++-specific, but let's be pragmatic.
+ std::unique_ptr<QTextDocument> m_replacement;
+ std::unique_ptr<TextSuggestion> m_suggestion;
};
-
class TEXTEDITOR_EXPORT TextDocumentLayout : public QPlainTextDocumentLayout
{
Q_OBJECT
@@ -172,6 +199,12 @@ public:
static void setFolded(const QTextBlock &block, bool folded);
static void setExpectedRawStringSuffix(const QTextBlock &block, const QByteArray &suffix);
static QByteArray expectedRawStringSuffix(const QTextBlock &block);
+ static TextSuggestion *suggestion(const QTextBlock &block);
+ static void updateSuggestionFormats(const QTextBlock &block,
+ const FontSettings &fontSettings);
+ static bool updateSuggestion(const QTextBlock &block,
+ int position,
+ const FontSettings &fontSettings);
class TEXTEDITOR_EXPORT FoldValidator
{
@@ -212,7 +245,8 @@ public:
QRectF blockBoundingRect(const QTextBlock &block) const override;
TextMarks documentClosing();
- void documentReloaded(TextMarks marks, TextDocument *baseextDocument);
+ void documentAboutToReload();
+ void documentReloaded(TextDocument *baseextDocument);
void updateMarksLineNumber();
void updateMarksBlock(const QTextBlock &block);
void scheduleUpdate();
@@ -220,6 +254,8 @@ public:
private:
bool m_updateScheduled = false;
+ TextMarks m_reloadMarks;
+ void resetReloadMarks();
signals:
void updateExtraArea();
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 2dc0531753..bd50833304 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -61,6 +61,7 @@
#include <utils/minimizableinfobars.h>
#include <utils/multitextcursor.h>
#include <utils/qtcassert.h>
+#include <utils/searchresultitem.h>
#include <utils/styledbar.h>
#include <utils/stylehelper.h>
#include <utils/textutils.h>
@@ -657,6 +658,7 @@ public:
uint m_optionalActionMask = TextEditorActionHandler::None;
bool m_contentsChanged = false;
bool m_lastCursorChangeWasInteresting = false;
+ std::shared_ptr<void> m_suggestionBlocker;
QSharedPointer<TextDocument> m_document;
QList<QMetaObject::Connection> m_documentConnections;
@@ -789,7 +791,7 @@ public:
QScopedPointer<AutoCompleter> m_autoCompleter;
CommentDefinition m_commentDefinition;
- QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr;
+ QFutureWatcher<SearchResultItems> *m_searchWatcher = nullptr;
QVector<SearchResult> m_searchResults;
QTimer m_scrollBarUpdateTimer;
HighlightScrollBarController *m_highlightScrollBarController = nullptr;
@@ -819,6 +821,11 @@ public:
QStack<UndoMultiCursor> m_undoCursorStack;
QList<int> m_visualIndentCache;
int m_visualIndentOffset = 0;
+
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+ void updateSuggestion();
+ void clearCurrentSuggestion();
+ QTextBlock m_suggestionBlock;
};
class TextEditorWidgetFind : public BaseTextFind
@@ -840,10 +847,27 @@ public:
private:
TextEditorWidget * const m_editor;
- static QFutureWatcher<FileSearchResultList> *m_selectWatcher;
+ static QFutureWatcher<SearchResultItems> *m_selectWatcher;
};
-QFutureWatcher<FileSearchResultList> *TextEditorWidgetFind::m_selectWatcher = nullptr;
+static QTextCursor selectRange(QTextDocument *textDocument, const Text::Range &range,
+ TextEditorWidgetPrivate::SearchResult *searchResult = nullptr)
+{
+ const int startLine = qMax(range.begin.line - 1, 0);
+ const int startColumn = qMax(range.begin.column, 0);
+ const int endLine = qMax(range.end.line - 1, 0);
+ const int endColumn = qMax(range.end.column, 0);
+ const int startPosition = textDocument->findBlockByNumber(startLine).position() + startColumn;
+ const int endPosition = textDocument->findBlockByNumber(endLine).position() + endColumn;
+ QTextCursor textCursor(textDocument);
+ textCursor.setPosition(startPosition);
+ textCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
+ if (searchResult)
+ *searchResult = {startPosition + 1, endPosition + 1};
+ return textCursor;
+}
+
+QFutureWatcher<SearchResultItems> *TextEditorWidgetFind::m_selectWatcher = nullptr;
void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags)
{
@@ -852,26 +876,24 @@ void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags)
cancelCurrentSelectAll();
- m_selectWatcher = new QFutureWatcher<FileSearchResultList>();
- connect(m_selectWatcher, &QFutureWatcher<Utils::FileSearchResultList>::finished,
- this, [this] {
- const QFuture<FileSearchResultList> future = m_selectWatcher->future();
- m_selectWatcher->deleteLater();
- m_selectWatcher = nullptr;
- if (future.resultCount() <= 0)
- return;
- const FileSearchResultList &results = future.result();
- const QTextCursor c(m_editor->document());
- auto cursorForResult = [c](const FileSearchResult &r) {
- return Utils::Text::selectAt(c, r.lineNumber, r.matchStart + 1, r.matchLength);
- };
- QList<QTextCursor> cursors = Utils::transform(results, cursorForResult);
- cursors = Utils::filtered(cursors, [this](const QTextCursor &c) {
- return m_editor->inFindScope(c);
- });
- m_editor->setMultiTextCursor(MultiTextCursor(cursors));
- m_editor->setFocus();
- });
+ m_selectWatcher = new QFutureWatcher<SearchResultItems>();
+ connect(m_selectWatcher, &QFutureWatcher<SearchResultItems>::finished, this, [this] {
+ const QFuture<SearchResultItems> future = m_selectWatcher->future();
+ m_selectWatcher->deleteLater();
+ m_selectWatcher = nullptr;
+ if (future.resultCount() <= 0)
+ return;
+ const SearchResultItems &results = future.result();
+ const auto cursorForResult = [this](const SearchResultItem &item) {
+ return selectRange(m_editor->document(), item.mainRange());
+ };
+ QList<QTextCursor> cursors = Utils::transform(results, cursorForResult);
+ cursors = Utils::filtered(cursors, [this](const QTextCursor &c) {
+ return m_editor->inFindScope(c);
+ });
+ m_editor->setMultiTextCursor(MultiTextCursor(cursors));
+ m_editor->setFocus();
+ });
const FilePath &fileName = m_editor->textDocument()->filePath();
QMap<FilePath, QString> fileToContentsMap;
@@ -900,6 +922,7 @@ void TextEditorWidgetFind::cancelCurrentSelectAll()
TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent)
: q(parent)
+ , m_suggestionBlocker((void *) this, [](void *) {})
, m_overlay(new TextEditorOverlay(q))
, m_snippetOverlay(new SnippetOverlay(q))
, m_searchResultOverlay(new TextEditorOverlay(q))
@@ -1646,6 +1669,43 @@ void TextEditorWidgetPrivate::handleMoveBlockSelection(QTextCursor::MoveOperatio
q->setMultiTextCursor(MultiTextCursor(cursors));
}
+void TextEditorWidgetPrivate::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ clearCurrentSuggestion();
+
+ if (m_suggestionBlocker.use_count() > 1)
+ return;
+
+ auto cursor = q->textCursor();
+ cursor.setPosition(suggestion->position());
+ m_suggestionBlock = cursor.block();
+ m_document->insertSuggestion(std::move(suggestion));
+}
+
+void TextEditorWidgetPrivate::updateSuggestion()
+{
+ if (!m_suggestionBlock.isValid())
+ return;
+ if (m_cursors.mainCursor().block() != m_suggestionBlock) {
+ clearCurrentSuggestion();
+ } else {
+ if (!TextDocumentLayout::updateSuggestion(m_suggestionBlock,
+ m_cursors.mainCursor().position(),
+ m_document->fontSettings())) {
+ clearCurrentSuggestion();
+ }
+ }
+}
+
+void TextEditorWidgetPrivate::clearCurrentSuggestion()
+{
+ if (TextBlockUserData *userData = TextDocumentLayout::textUserData(m_suggestionBlock)) {
+ userData->clearSuggestion();
+ m_document->updateLayout();
+ }
+ m_suggestionBlock = QTextBlock();
+}
+
void TextEditorWidget::selectEncoding()
{
TextDocument *doc = d->m_document.data();
@@ -1828,6 +1888,8 @@ TextEditorWidget *TextEditorWidget::fromEditor(const IEditor *editor)
void TextEditorWidgetPrivate::editorContentsChange(int position, int charsRemoved, int charsAdded)
{
+ updateSuggestion();
+
if (m_bracketsAnimator)
m_bracketsAnimator->finish();
@@ -2308,6 +2370,11 @@ void TextEditorWidget::renameSymbolUnderCursor()
emit requestRename(textCursor());
}
+void TextEditorWidget::openCallHierarchy()
+{
+ emit requestCallHierarchy(textCursor());
+}
+
void TextEditorWidget::abortAssist()
{
d->m_codeAssistant.destroyContext();
@@ -2488,7 +2555,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
{
ICore::restartTrimmer();
- ExecuteOnDestruction eod([&]() { d->clearBlockSelection(); });
+ auto clearBlockSelectionGuard = qScopeGuard([&]() { d->clearBlockSelection(); });
if (!isModifier(e) && mouseHidingEnabled())
viewport()->setCursor(Qt::BlankCursor);
@@ -2506,6 +2573,11 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
d->m_maybeFakeTooltipEvent = false;
if (e->key() == Qt::Key_Escape ) {
TextEditorWidgetFind::cancelCurrentSelectAll();
+ if (d->m_suggestionBlock.isValid()) {
+ d->clearCurrentSuggestion();
+ e->accept();
+ return;
+ }
if (d->m_snippetOverlay->isVisible()) {
e->accept();
d->m_snippetOverlay->accept();
@@ -2527,6 +2599,21 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
const bool inOverwriteMode = overwriteMode();
const bool hasMultipleCursors = cursor.hasMultipleCursors();
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(d->m_suggestionBlock)) {
+ if (e->matches(QKeySequence::MoveToNextWord)) {
+ e->accept();
+ if (suggestion->applyWord(this))
+ d->clearCurrentSuggestion();
+ return;
+ } else if (e->modifiers() == Qt::NoModifier
+ && (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab)) {
+ e->accept();
+ if (suggestion->apply())
+ d->clearCurrentSuggestion();
+ return;
+ }
+ }
+
if (!ro
&& (e == QKeySequence::InsertParagraphSeparator
|| (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator))) {
@@ -2684,6 +2771,8 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
| Qt::AltModifier
| Qt::MetaModifier)) == Qt::NoModifier) {
e->accept();
+ if (d->m_suggestionBlock.isValid())
+ d->clearCurrentSuggestion();
if (cursor.hasSelection()) {
cursor.removeSelectedText();
setMultiTextCursor(cursor);
@@ -2745,8 +2834,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
}
if (blockSelectionOperation != QTextCursor::NoMove) {
- auto doNothing = [](){};
- eod.reset(doNothing);
+ clearBlockSelectionGuard.dismiss();
d->handleMoveBlockSelection(blockSelectionOperation);
} else if (!d->cursorMoveKeyEvent(e)) {
QTextCursor cursor = textCursor();
@@ -2834,13 +2922,13 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
if (!autoText.isEmpty())
cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor());
+ setTextCursor(cursor);
+
if (doEditBlock) {
cursor.endEditBlock();
if (cursorWithinSnippet)
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
}
-
- setTextCursor(cursor);
}
if (!ro && e->key() == Qt::Key_Delete && d->m_parenthesesMatchingEnabled)
@@ -3105,7 +3193,9 @@ bool TextEditorWidget::event(QEvent *e)
case QEvent::ShortcutOverride: {
auto ke = static_cast<QKeyEvent *>(e);
if (ke->key() == Qt::Key_Escape
- && (d->m_snippetOverlay->isVisible() || multiTextCursor().hasMultipleCursors())) {
+ && (d->m_snippetOverlay->isVisible()
+ || multiTextCursor().hasMultipleCursors()
+ || d->m_suggestionBlock.isValid())) {
e->accept();
} else {
// hack copied from QInputControl::isCommonTextEditShortcut
@@ -3676,7 +3766,10 @@ bool TextEditorWidget::viewportEvent(QEvent *event)
// Only handle tool tip for text cursor if mouse is within the block for the text cursor,
// and not if the mouse is e.g. in the empty space behind a short line.
if (line.isValid()) {
- if (pos.x() <= blockBoundingGeometry(block).left() + line.naturalTextRect().right()) {
+ const QRectF blockGeometry = blockBoundingGeometry(block);
+ const int width = block == d->m_suggestionBlock ? blockGeometry.width()
+ : line.naturalTextRect().right();
+ if (pos.x() <= blockGeometry.left() + width) {
d->processTooltipRequest(tc);
return true;
} else if (d->processAnnotaionTooltipRequest(block, pos)) {
@@ -3999,7 +4092,13 @@ static TextMarks availableMarks(const TextMarks &marks,
QRectF TextEditorWidgetPrivate::getLastLineLineRect(const QTextBlock &block)
{
- const QTextLayout *layout = block.layout();
+ QTextLayout *layout = nullptr;
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block))
+ layout = suggestion->document()->firstBlock().layout();
+ else
+ layout = block.layout();
+
+ QTC_ASSERT(layout, layout = block.layout());
const int lineCount = layout->lineCount();
if (lineCount < 1)
return {};
@@ -4394,15 +4493,28 @@ void TextEditorWidgetPrivate::paintAdditionalVisualWhitespaces(PaintEventData &d
visualArrow);
}
if (!nextBlockIsValid) { // paint EOF symbol
- QTextLine line = layout->lineAt(lineCount-1);
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(data.block)) {
+ const QTextBlock lastReplacementBlock = suggestion->document()->lastBlock();
+ for (QTextBlock block = suggestion->document()->firstBlock();
+ block != lastReplacementBlock && block.isValid();
+ block = block.next()) {
+ top += suggestion->document()
+ ->documentLayout()
+ ->blockBoundingRect(block)
+ .height();
+ }
+ layout = lastReplacementBlock.layout();
+ lineCount = layout->lineCount();
+ }
+ QTextLine line = layout->lineAt(lineCount - 1);
QRectF lineRect = line.naturalTextRect().translated(data.offset.x(), top);
int h = 4;
lineRect.adjust(0, 0, -1, -1);
QPainterPath path;
- QPointF pos(lineRect.topRight() + QPointF(h+4, line.ascent()));
+ QPointF pos(lineRect.topRight() + QPointF(h + 4, line.ascent()));
path.moveTo(pos);
path.lineTo(pos + QPointF(-h, -h));
- path.lineTo(pos + QPointF(0, -2*h));
+ path.lineTo(pos + QPointF(0, -2 * h));
path.lineTo(pos + QPointF(h, -h));
path.closeSubpath();
painter.setBrush(painter.pen().color());
@@ -4649,6 +4761,21 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
PaintEventBlockData &blockData) const
{
QVector<QTextLayout::FormatRange> prioritySelections;
+
+ int deltaPos = -1;
+ int delta = 0;
+
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(data.block)) {
+ deltaPos = suggestion->currentPosition() - data.block.position();
+ const QString trailingText = data.block.text().mid(deltaPos);
+ if (!trailingText.isEmpty()) {
+ const int trailingIndex
+ = suggestion->document()->firstBlock().text().indexOf(trailingText, deltaPos);
+ if (trailingIndex >= 0)
+ delta = std::max(trailingIndex - deltaPos, 0);
+ }
+ }
+
for (int i = 0; i < data.context.selections.size(); ++i) {
const QAbstractTextDocumentLayout::Selection &range = data.context.selections.at(i);
const int selStart = range.cursor.selectionStart() - blockData.position;
@@ -4659,6 +4786,22 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
o.start = selStart;
o.length = selEnd - selStart;
o.format = range.format;
+ QTextLayout::FormatRange rest;
+ rest.start = -1;
+ if (deltaPos >= 0 && delta != 0) {
+ if (o.start >= deltaPos) {
+ o.start += delta;
+ } else if (o.start + o.length > deltaPos) {
+ // the format range starts before and ends after the position so we need to
+ // split the format into before and after the suggestion format ranges
+ rest.start = deltaPos + delta;
+ rest.length = o.length - (deltaPos - o.start);
+ rest.format = o.format;
+ o.length = deltaPos - o.start;
+ }
+ }
+
+ o.format = range.format;
if (data.textCursor.hasSelection() && data.textCursor == range.cursor
&& data.textCursor.anchor() == range.cursor.anchor()) {
const QTextCharFormat selectionFormat = data.fontSettings.toTextCharFormat(C_SELECTION);
@@ -4670,10 +4813,15 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
|| (o.format.foreground().style() == Qt::NoBrush
&& o.format.underlineStyle() != QTextCharFormat::NoUnderline
&& o.format.background() == Qt::NoBrush)) {
- if (q->selectionVisible(data.block.blockNumber()))
+ if (q->selectionVisible(data.block.blockNumber())) {
prioritySelections.append(o);
+ if (rest.start >= 0)
+ prioritySelections.append(rest);
+ }
} else {
blockData.selections.append(o);
+ if (rest.start >= 0)
+ blockData.selections.append(rest);
}
}
}
@@ -4784,6 +4932,7 @@ void TextEditorWidget::paintEvent(QPaintEvent *e)
if (blockData.boundingRect.bottom() >= data.eventRect.top()
&& blockData.boundingRect.top() <= data.eventRect.bottom()) {
+ data.documentLayout->ensureBlockLayout(data.block);
d->setupBlockLayout(data, painter, blockData);
blockData.position = data.block.position();
blockData.length = data.block.length();
@@ -4868,6 +5017,27 @@ void TextEditorWidget::paintBlock(QPainter *painter,
const QVector<QTextLayout::FormatRange> &selections,
const QRect &clipRect) const
{
+ if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
+ QTextBlock suggestionBlock = suggestion->document()->firstBlock();
+ QPointF suggestionOffset = offset;
+ suggestionOffset.rx() += document()->documentMargin();
+ while (suggestionBlock.isValid()) {
+ const QVector<QTextLayout::FormatRange> blockSelections
+ = suggestionBlock.blockNumber() == 0 ? selections
+ : QVector<QTextLayout::FormatRange>{};
+ suggestionBlock.layout()->draw(painter,
+ suggestionOffset,
+ blockSelections,
+ clipRect);
+ suggestionOffset.ry() += suggestion->document()
+ ->documentLayout()
+ ->blockBoundingRect(suggestionBlock)
+ .height();
+ suggestionBlock = suggestionBlock.next();
+ }
+ return;
+ }
+
block.layout()->draw(painter, offset, selections, clipRect);
}
@@ -5407,6 +5577,7 @@ void TextEditorWidget::slotCursorPositionChanged()
setMultiTextCursor(cursor);
d->updateCursorSelections();
d->updateHighlights();
+ d->updateSuggestion();
}
void TextEditorWidgetPrivate::updateHighlights()
@@ -5842,8 +6013,42 @@ void TextEditorWidget::addHoverHandler(BaseHoverHandler *handler)
void TextEditorWidget::removeHoverHandler(BaseHoverHandler *handler)
{
- d->m_hoverHandlers.removeAll(handler);
- d->m_hoverHandlerRunner.handlerRemoved(handler);
+ if (d->m_hoverHandlers.removeAll(handler) > 0)
+ d->m_hoverHandlerRunner.handlerRemoved(handler);
+}
+
+void TextEditorWidget::insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion)
+{
+ d->insertSuggestion(std::move(suggestion));
+}
+
+void TextEditorWidget::clearSuggestion()
+{
+ d->clearCurrentSuggestion();
+}
+
+TextSuggestion *TextEditorWidget::currentSuggestion() const
+{
+ if (d->m_suggestionBlock.isValid())
+ return TextDocumentLayout::suggestion(d->m_suggestionBlock);
+ return nullptr;
+}
+
+bool TextEditorWidget::suggestionVisible() const
+{
+ return currentSuggestion();
+}
+
+bool TextEditorWidget::suggestionsBlocked() const
+{
+ return d->m_suggestionBlocker.use_count() > 1;
+}
+
+TextEditorWidget::SuggestionBlocker TextEditorWidget::blockSuggestions()
+{
+ if (!suggestionsBlocked())
+ clearSuggestion();
+ return d->m_suggestionBlocker;
}
#ifdef WITH_TESTS
@@ -6482,16 +6687,11 @@ void TextEditorWidgetPrivate::searchResultsReady(int beginIndex, int endIndex)
{
QVector<SearchResult> results;
for (int index = beginIndex; index < endIndex; ++index) {
- const FileSearchResultList resultList = m_searchWatcher->resultAt(index);
- for (FileSearchResult result : resultList) {
- const QTextBlock &block = q->document()->findBlockByNumber(result.lineNumber - 1);
- const int matchStart = block.position() + result.matchStart;
- QTextCursor cursor(block);
- cursor.setPosition(matchStart);
- cursor.setPosition(matchStart + result.matchLength, QTextCursor::KeepAnchor);
- if (!q->inFindScope(cursor))
- continue;
- results << SearchResult{matchStart, result.matchLength};
+ const SearchResultItems resultList = m_searchWatcher->resultAt(index);
+ for (const SearchResultItem &result : resultList) {
+ SearchResult searchResult;
+ if (q->inFindScope(selectRange(q->document(), result.mainRange(), &searchResult)))
+ results << searchResult;
}
}
m_searchResults << results;
@@ -6537,10 +6737,10 @@ void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar()
adjustScrollBarRanges();
- m_searchWatcher = new QFutureWatcher<FileSearchResultList>();
- connect(m_searchWatcher, &QFutureWatcher<FileSearchResultList>::resultsReadyAt,
+ m_searchWatcher = new QFutureWatcher<SearchResultItems>;
+ connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::resultsReadyAt,
this, &TextEditorWidgetPrivate::searchResultsReady);
- connect(m_searchWatcher, &QFutureWatcher<FileSearchResultList>::finished,
+ connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::finished,
this, &TextEditorWidgetPrivate::searchFinished);
m_searchWatcher->setPendingResultsLimit(10);
@@ -6586,7 +6786,7 @@ void TextEditorWidgetPrivate::addSearchResultsToScrollBar(const QVector<SearchRe
{
if (!m_highlightScrollBarController)
return;
- for (SearchResult result : results) {
+ for (const SearchResult &result : results) {
const QTextBlock &block = q->document()->findBlock(result.start);
if (block.isValid() && block.isVisible()) {
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();
@@ -6639,10 +6839,11 @@ MultiTextCursor TextEditorWidget::multiTextCursor() const
void TextEditorWidget::setMultiTextCursor(const Utils::MultiTextCursor &cursor)
{
+ if (cursor == d->m_cursors)
+ return;
+
const MultiTextCursor oldCursor = d->m_cursors;
const_cast<MultiTextCursor &>(d->m_cursors) = cursor;
- if (oldCursor == d->m_cursors)
- return;
doSetTextCursor(d->m_cursors.mainCursor(), /*keepMultiSelection*/ true);
QRect updateRect = d->cursorUpdateRect(oldCursor);
if (d->m_highlightCurrentLine)
@@ -8068,6 +8269,11 @@ void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
if (!menu->actions().contains(findUsage))
menu->addAction(findUsage);
}
+ if (optionalActions() & TextEditorActionHandler::CallHierarchy) {
+ const auto callHierarchy = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action();
+ if (!menu->actions().contains(callHierarchy))
+ menu->addAction(callHierarchy);
+ }
menu->addSeparator();
appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU);
diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h
index dc492eca21..2f2a9f8105 100644
--- a/src/plugins/texteditor/texteditor.h
+++ b/src/plugins/texteditor/texteditor.h
@@ -42,15 +42,16 @@ class HighlightScrollBarController;
}
namespace TextEditor {
-class TextDocument;
-class TextMark;
-class BaseHoverHandler;
-class RefactorOverlay;
-class SyntaxHighlighter;
class AssistInterface;
+class BaseHoverHandler;
+class CompletionAssistProvider;
class IAssistProvider;
class ICodeStylePreferences;
-class CompletionAssistProvider;
+class RefactorOverlay;
+class SyntaxHighlighter;
+class TextDocument;
+class TextMark;
+class TextSuggestion;
using RefactorMarkers = QList<RefactorMarker>;
using TextMarks = QList<TextMark *>;
@@ -437,6 +438,7 @@ public:
virtual void findUsages();
virtual void renameSymbolUnderCursor();
+ virtual void openCallHierarchy();
/// Abort code assistant if it is running.
void abortAssist();
@@ -469,6 +471,16 @@ public:
void addHoverHandler(BaseHoverHandler *handler);
void removeHoverHandler(BaseHoverHandler *handler);
+ void insertSuggestion(std::unique_ptr<TextSuggestion> &&suggestion);
+ void clearSuggestion();
+ TextSuggestion *currentSuggestion() const;
+ bool suggestionVisible() const;
+ bool suggestionsBlocked() const;
+
+ using SuggestionBlocker = std::shared_ptr<void>;
+ // Returns an object that blocks suggestions until it is destroyed.
+ SuggestionBlocker blockSuggestions();
+
#ifdef WITH_TESTS
void processTooltipRequest(const QTextCursor &c);
#endif
@@ -483,6 +495,7 @@ signals:
bool resolveTarget, bool inNextSplit);
void requestUsages(const QTextCursor &cursor);
void requestRename(const QTextCursor &cursor);
+ void requestCallHierarchy(const QTextCursor &cursor);
void optionalActionMaskChanged();
void toolbarOutlineChanged(QWidget *newOutline);
diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs
index 971ef31fae..1b94811a56 100644
--- a/src/plugins/texteditor/texteditor.qbs
+++ b/src/plugins/texteditor/texteditor.qbs
@@ -94,6 +94,8 @@ Project {
"linenumberfilter.h",
"marginsettings.cpp",
"marginsettings.h",
+ "markdowneditor.cpp",
+ "markdowneditor.h",
"outlinefactory.cpp",
"outlinefactory.h",
"plaintexteditorfactory.cpp",
@@ -220,9 +222,7 @@ Project {
]
}
- Group {
- name: "Tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"texteditor_test.cpp",
]
diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp
index 25116729c9..00ed94a5ee 100644
--- a/src/plugins/texteditor/texteditoractionhandler.cpp
+++ b/src/plugins/texteditor/texteditoractionhandler.cpp
@@ -116,6 +116,7 @@ public:
QAction *m_followSymbolAction = nullptr;
QAction *m_followSymbolInNextSplitAction = nullptr;
QAction *m_findUsageAction = nullptr;
+ QAction *m_openCallHierarchyAction = nullptr;
QAction *m_renameSymbolAction = nullptr;
QAction *m_jumpToFileAction = nullptr;
QAction *m_jumpToFileInNextSplitAction = nullptr;
@@ -228,6 +229,8 @@ void TextEditorActionHandlerPrivate::createActions()
m_jumpToFileInNextSplitAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT,
[] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Jump to File Under Cursor in Next Split"),
QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2")).toString());
+ m_openCallHierarchyAction = registerAction(OPEN_CALL_HIERARCHY,
+ [] (TextEditorWidget *w) { w->openCallHierarchy(); }, true, Tr::tr("Open Call Hierarchy"));
registerAction(VIEW_PAGE_UP,
[] (TextEditorWidget *w) { w->viewPageUp(); }, true, Tr::tr("Move the View a Page Up and Keep the Cursor Position"),
@@ -484,6 +487,8 @@ void TextEditorActionHandlerPrivate::updateOptionalActions()
optionalActions & TextEditorActionHandler::UnCollapseAll);
m_renameSymbolAction->setEnabled(
optionalActions & TextEditorActionHandler::RenameSymbol);
+ m_openCallHierarchyAction->setEnabled(
+ optionalActions & TextEditorActionHandler::CallHierarchy);
bool formatEnabled = (optionalActions & TextEditorActionHandler::Format)
&& m_currentEditorWidget && !m_currentEditorWidget->isReadOnly();
diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h
index 277c2cf66f..2bdb8efff3 100644
--- a/src/plugins/texteditor/texteditoractionhandler.h
+++ b/src/plugins/texteditor/texteditoractionhandler.h
@@ -36,7 +36,8 @@ public:
FollowSymbolUnderCursor = 8,
JumpToFileUnderCursor = 16,
RenameSymbol = 32,
- FindUsage = 64
+ FindUsage = 64,
+ CallHierarchy = 128
};
using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>;
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index 0560dae9d8..f422630f0d 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -208,6 +208,7 @@ const char FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbol
const char FIND_USAGES[] = "TextEditor.FindUsages";
// moved from CppEditor to TextEditor avoid breaking the setting by using the old key
const char RENAME_SYMBOL[] = "CppEditor.RenameSymbolUnderCursor";
+const char OPEN_CALL_HIERARCHY[] = "TextEditor.OpenCallHierarchy";
const char JUMP_TO_FILE_UNDER_CURSOR[] = "TextEditor.JumpToFileUnderCursor";
const char JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnderCursorInNextSplit";
diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp
index 52ab8709de..4257adbbdb 100644
--- a/src/plugins/texteditor/texteditoroverlay.cpp
+++ b/src/plugins/texteditor/texteditoroverlay.cpp
@@ -129,6 +129,7 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
QTextLayout *blockLayout = block.layout();
int pos = begin.position() - begin.block().position();
QTextLine line = blockLayout->lineForTextPosition(pos);
+ QTC_ASSERT(line.isValid(), return {});
QRectF lineRect = line.naturalTextRect();
lineRect = lineRect.translated(blockGeometry.topLeft());
int x = line.cursorToX(pos);
@@ -154,12 +155,12 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
QTextLayout *blockLayout = block.layout();
int firstLine = 0;
- QTextLine line = blockLayout->lineAt(firstLine);
int beginChar = 0;
if (block == begin.block()) {
beginChar = begin.positionInBlock();
- line = blockLayout->lineForTextPosition(beginChar);
+ QTextLine line = blockLayout->lineForTextPosition(beginChar);
+ QTC_ASSERT(line.isValid(), return {});
firstLine = line.lineNumber();
const int lineEnd = line.textStart() + line.textLength();
if (beginChar == lineEnd)
@@ -170,7 +171,9 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
int endChar = -1;
if (block == end.block()) {
endChar = end.positionInBlock();
- lastLine = blockLayout->lineForTextPosition(endChar).lineNumber();
+ QTextLine line = blockLayout->lineForTextPosition(endChar);
+ QTC_ASSERT(line.isValid(), return {});
+ lastLine = line.lineNumber();
if (endChar == beginChar)
break; // Do not expand overlay to empty selection at end
} else {
@@ -181,7 +184,8 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
}
for (int i = firstLine; i <= lastLine; ++i) {
- line = blockLayout->lineAt(i);
+ QTextLine line = blockLayout->lineAt(i);
+ QTC_ASSERT(line.isValid(), return {});
QRectF lineRect = line.naturalTextRect();
if (i == firstLine && beginChar > 0)
lineRect.setLeft(line.cursorToX(beginChar));
diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp
index e97453adda..a66ddf23c3 100644
--- a/src/plugins/texteditor/texteditorplugin.cpp
+++ b/src/plugins/texteditor/texteditorplugin.cpp
@@ -10,6 +10,7 @@
#include "highlighter.h"
#include "icodestylepreferences.h"
#include "linenumberfilter.h"
+#include "markdowneditor.h"
#include "outlinefactory.h"
#include "plaintexteditorfactory.h"
#include "snippets/snippetprovider.h"
@@ -66,6 +67,7 @@ public:
FindInOpenFiles findInOpenFilesFilter;
PlainTextEditorFactory plainTextEditorFactory;
+ MarkdownEditorFactory markdownEditorFactory;
};
static TextEditorPlugin *m_instance = nullptr;
diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp
index 37be854d9b..4f86bc5f94 100644
--- a/src/plugins/texteditor/textmark.cpp
+++ b/src/plugins/texteditor/textmark.cpp
@@ -12,11 +12,13 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
+#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
#include <QAction>
+#include <QDesktopServices>
#include <QGridLayout>
#include <QPainter>
#include <QToolButton>
@@ -79,6 +81,8 @@ TextMark::~TextMark()
TextMarkRegistry::remove(this);
if (m_baseTextDocument)
m_baseTextDocument->removeMark(this);
+ if (m_deleteCallback)
+ m_deleteCallback();
m_baseTextDocument = nullptr;
}
@@ -282,7 +286,7 @@ void TextMark::addToToolTipLayout(QGridLayout *target) const
if (m_category.id.isValid() && !m_lineAnnotation.isEmpty()) {
auto visibilityAction = new QAction;
const bool isHidden = TextDocument::marksAnnotationHidden(m_category.id);
- visibilityAction->setIcon(Utils::Icons::EYE_OPEN_TOOLBAR.icon());
+ visibilityAction->setIcon(Utils::Icons::EYE_OPEN.icon());
const QString tooltip = (isHidden ? Tr::tr("Show inline annotations for %1")
: Tr::tr("Temporarily hide inline annotations for %1"))
.arg(m_category.displayName);
@@ -298,7 +302,7 @@ void TextMark::addToToolTipLayout(QGridLayout *target) const
}
if (m_settingsPage.isValid()) {
auto settingsAction = new QAction;
- settingsAction->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon());
+ settingsAction->setIcon(Utils::Icons::SETTINGS.icon());
settingsAction->setToolTip(Tr::tr("Show Diagnostic Settings"));
QObject::connect(settingsAction, &QAction::triggered, Core::ICore::instance(),
[id = m_settingsPage] { Core::ICore::showOptionsDialog(id); },
@@ -338,11 +342,18 @@ bool TextMark::addToolTipContent(QLayout *target) const
}
auto textLabel = new QLabel;
- textLabel->setOpenExternalLinks(true);
textLabel->setText(text);
// Differentiate between tool tips that where explicitly set and default tool tips.
textLabel->setDisabled(useDefaultToolTip);
target->addWidget(textLabel);
+ QObject::connect(textLabel, &QLabel::linkActivated, [](const QString &link) {
+ if (OutputLineParser::isLinkTarget(link)) {
+ Core::EditorManager::openEditorAt(OutputLineParser::parseLinkTarget(link), {},
+ Core::EditorManager::SwitchSplitIfAlreadyVisible);
+ } else {
+ QDesktopServices::openUrl(link);
+ }
+ });
return true;
}
diff --git a/src/plugins/texteditor/textmark.h b/src/plugins/texteditor/textmark.h
index fb000da1a4..b6fb4731cb 100644
--- a/src/plugins/texteditor/textmark.h
+++ b/src/plugins/texteditor/textmark.h
@@ -118,12 +118,15 @@ public:
bool isLocationMarker() const;
void setIsLocationMarker(bool newIsLocationMarker);
+
protected:
void setSettingsPage(Utils::Id settingsPage);
private:
Q_DISABLE_COPY(TextMark)
+ void setDeleteCallback(const std::function<void()> &callback) { m_deleteCallback = callback; };
+
TextDocument *m_baseTextDocument = nullptr;
Utils::FilePath m_fileName;
int m_lineNumber = 0;
@@ -141,6 +144,9 @@ private:
QVector<QAction *> m_actions; // FIXME Remove in master
std::function<QList<QAction *>()> m_actionsProvider;
Utils::Id m_settingsPage;
+ std::function<void()> m_deleteCallback;
+
+ friend class TextDocumentLayout;
};
} // namespace TextEditor
diff --git a/src/plugins/todo/keyworddialog.cpp b/src/plugins/todo/keyworddialog.cpp
index 0f92091a1f..cebeb7b692 100644
--- a/src/plugins/todo/keyworddialog.cpp
+++ b/src/plugins/todo/keyworddialog.cpp
@@ -47,7 +47,7 @@ KeywordDialog::KeywordDialog(const Keyword &keyword, const QSet<QString> &alread
m_buttonBox->setOrientation(Qt::Horizontal);
m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
new QLabel(Tr::tr("Icon")),
diff --git a/src/plugins/todo/optionsdialog.cpp b/src/plugins/todo/optionsdialog.cpp
index f536ca51ed..3b300a7d10 100644
--- a/src/plugins/todo/optionsdialog.cpp
+++ b/src/plugins/todo/optionsdialog.cpp
@@ -73,7 +73,7 @@ OptionsDialog::OptionsDialog(Settings *settings, const std::function<void ()> &o
m_scanInSubprojectRadioButton = new QRadioButton(Tr::tr("Scan the current subproject"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
diff --git a/src/plugins/todo/todoitemsprovider.cpp b/src/plugins/todo/todoitemsprovider.cpp
index 02790c64df..02ba71e2fc 100644
--- a/src/plugins/todo/todoitemsprovider.cpp
+++ b/src/plugins/todo/todoitemsprovider.cpp
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "todoitemsprovider.h"
+
#include "constants.h"
#include "cpptodoitemsscanner.h"
#include "qmljstodoitemsscanner.h"
@@ -13,9 +14,9 @@
#include <coreplugin/idocument.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <utils/algorithm.h>
@@ -182,8 +183,8 @@ void TodoItemsProvider::updateListTimeoutElapsed()
void TodoItemsProvider::setupStartupProjectBinding()
{
- m_startupProject = SessionManager::startupProject();
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ m_startupProject = ProjectManager::startupProject();
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &TodoItemsProvider::startupProjectChanged);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
this, &TodoItemsProvider::projectsFilesChanged);
diff --git a/src/plugins/todo/todoprojectsettingswidget.cpp b/src/plugins/todo/todoprojectsettingswidget.cpp
index 0d0e0d732a..6f68ccc37b 100644
--- a/src/plugins/todo/todoprojectsettingswidget.cpp
+++ b/src/plugins/todo/todoprojectsettingswidget.cpp
@@ -32,7 +32,7 @@ TodoProjectSettingsWidget::TodoProjectSettingsWidget(ProjectExplorer::Project *p
auto addExcludedPatternButton = new QPushButton(Tr::tr("Add"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Group {
diff --git a/src/plugins/updateinfo/settingspage.cpp b/src/plugins/updateinfo/settingspage.cpp
index 587102bce7..18d070092e 100644
--- a/src/plugins/updateinfo/settingspage.cpp
+++ b/src/plugins/updateinfo/settingspage.cpp
@@ -45,7 +45,7 @@ public:
m_nextCheckDateLabel = new QLabel;
m_checkForNewQtVersions = new QCheckBox(Tr::tr("Check for new Qt versions"));
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
m_infoLabel,
diff --git a/src/plugins/updateinfo/settingspage.h b/src/plugins/updateinfo/settingspage.h
index 3ed45a71c6..6da738a9a0 100644
--- a/src/plugins/updateinfo/settingspage.h
+++ b/src/plugins/updateinfo/settingspage.h
@@ -5,18 +5,14 @@
#include <coreplugin/dialogs/ioptionspage.h>
-namespace UpdateInfo {
-namespace Internal {
+namespace UpdateInfo::Internal {
class UpdateInfoPlugin;
class SettingsPage : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
explicit SettingsPage(UpdateInfoPlugin *plugin);
};
-} // namespace Internal
-} // namespace UpdateInfo
+} // UpdateInfo::Internal
diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp
index decce3a47a..749cbbbec6 100644
--- a/src/plugins/updateinfo/updateinfoplugin.cpp
+++ b/src/plugins/updateinfo/updateinfoplugin.cpp
@@ -13,8 +13,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/taskprogress.h>
#include <utils/infobar.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QDate>
#include <QLabel>
@@ -43,6 +43,7 @@ const char InstallQtUpdates[] = "UpdateInfo.InstallQtUpdates";
const char M_MAINTENANCE_TOOL[] = "QtCreator.Menu.Tools.MaintenanceTool";
using namespace Core;
+using namespace Tasking;
using namespace Utils;
namespace UpdateInfo {
@@ -119,7 +120,7 @@ void UpdateInfoPlugin::startCheckForUpdates()
using namespace Tasking;
- const auto doSetup = [this](QtcProcess &process, const QStringList &args) {
+ const auto doSetup = [this](Process &process, const QStringList &args) {
process.setCommand({d->m_maintenanceTool, args});
};
const auto doCleanup = [this] {
@@ -127,22 +128,22 @@ void UpdateInfoPlugin::startCheckForUpdates()
checkForUpdatesStopped();
};
- const auto setupUpdate = [doSetup](QtcProcess &process) {
+ const auto setupUpdate = [doSetup](Process &process) {
doSetup(process, {"ch", "-g", "*=false,ifw.package.*=true"});
};
- const auto updateDone = [this](const QtcProcess &process) {
+ const auto updateDone = [this](const Process &process) {
d->m_updateOutput = process.cleanedStdOut();
};
- QList<TaskItem> tasks { Process(setupUpdate, updateDone) };
+ QList<TaskItem> tasks { ProcessTask(setupUpdate, updateDone) };
if (d->m_settings.checkForQtVersions) {
- const auto setupPackages = [doSetup](QtcProcess &process) {
+ const auto setupPackages = [doSetup](Process &process) {
doSetup(process, {"se", "qt[.]qt[0-9][.][0-9]+$", "-g", "*=false,ifw.package.*=true"});
};
- const auto packagesDone = [this](const QtcProcess &process) {
+ const auto packagesDone = [this](const Process &process) {
d->m_packagesOutput = process.cleanedStdOut();
};
- tasks << Process(setupPackages, packagesDone);
+ tasks << ProcessTask(setupPackages, packagesDone);
}
d->m_taskTree.reset(new TaskTree(Group{tasks}));
@@ -461,7 +462,7 @@ QDate UpdateInfoPlugin::nextCheckDate(CheckUpdateInterval interval) const
void UpdateInfoPlugin::startMaintenanceTool(const QStringList &args) const
{
- QtcProcess::startDetached(CommandLine{d->m_maintenanceTool, args});
+ Process::startDetached(CommandLine{d->m_maintenanceTool, args});
}
void UpdateInfoPlugin::startUpdater() const
diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp
index d70c667df6..e8ddb08fac 100644
--- a/src/plugins/valgrind/callgrindengine.cpp
+++ b/src/plugins/valgrind/callgrindengine.cpp
@@ -12,8 +12,9 @@
#include <debugger/analyzer/analyzermanager.h>
#include <utils/filepath.h>
+#include <utils/filestreamermanager.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
@@ -85,7 +86,7 @@ QStringList CallgrindToolRunner::toolArguments() const
arguments << "--callgrind-out-file=" + m_valgrindOutputFile.path();
- arguments << ProcessArgs::splitArgs(m_settings.callgrindArguments.value());
+ arguments << ProcessArgs::splitArgs(m_settings.callgrindArguments.value(), HostOsInfo::hostOs());
return arguments;
}
@@ -174,7 +175,7 @@ void CallgrindToolRunner::run(Option option)
// save back current running operation
m_lastOption = option;
- m_controllerProcess.reset(new QtcProcess);
+ m_controllerProcess.reset(new Process);
switch (option) {
case CallgrindToolRunner::Dump:
@@ -196,11 +197,11 @@ void CallgrindToolRunner::run(Option option)
#if CALLGRIND_CONTROL_DEBUG
m_controllerProcess->setProcessChannelMode(QProcess::ForwardedChannels);
#endif
- connect(m_controllerProcess.get(), &QtcProcess::done,
+ connect(m_controllerProcess.get(), &Process::done,
this, &CallgrindToolRunner::controllerProcessDone);
const FilePath control =
- FilePath(CALLGRIND_CONTROL_BINARY).onDevice(m_valgrindRunnable.command.executable());
+ m_valgrindRunnable.command.executable().withNewPath(CALLGRIND_CONTROL_BINARY);
m_controllerProcess->setCommand({control, {toOptionString(option), QString::number(m_pid)}});
m_controllerProcess->setWorkingDirectory(m_valgrindRunnable.workingDirectory);
m_controllerProcess->setEnvironment(m_valgrindRunnable.environment);
@@ -257,11 +258,14 @@ void CallgrindToolRunner::triggerParse()
}
const auto afterCopy = [this](expected_str<void> res) {
- QTC_ASSERT_EXPECTED(res, return);
+ if (!res) // failed to run callgrind
+ return;
showStatusMessage(Tr::tr("Parsing Profile Data..."));
m_parser.parse(m_hostOutputFile);
};
- m_valgrindOutputFile.asyncCopyFile(afterCopy, m_hostOutputFile);
+ // TODO: Store the handle and cancel on CallgrindToolRunner destructor?
+ // TODO: Should d'tor of context object cancel the running task?
+ FileStreamerManager::copy(m_valgrindOutputFile, m_hostOutputFile, this, afterCopy);
}
void CallgrindToolRunner::cleanupTempFile()
diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h
index 2f22280ffa..4ec6a16d7f 100644
--- a/src/plugins/valgrind/callgrindengine.h
+++ b/src/plugins/valgrind/callgrindengine.h
@@ -8,7 +8,7 @@
#include "callgrind/callgrindparsedata.h"
#include "callgrind/callgrindparser.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
namespace Valgrind {
namespace Internal {
@@ -73,7 +73,7 @@ private:
bool m_markAsPaused = false;
- std::unique_ptr<Utils::QtcProcess> m_controllerProcess;
+ std::unique_ptr<Utils::Process> m_controllerProcess;
ProjectExplorer::Runnable m_valgrindRunnable;
qint64 m_pid = 0;
diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp
index fc50b2ecf0..ab5b0e5560 100644
--- a/src/plugins/valgrind/callgrindtool.cpp
+++ b/src/plugins/valgrind/callgrindtool.cpp
@@ -47,13 +47,13 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/taskhub.h>
#include <utils/fancymainwindow.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/styledbar.h>
#include <utils/utilsicons.h>
@@ -256,7 +256,7 @@ CallgrindToolPrivate::CallgrindToolPrivate()
menu->addAction(ActionManager::registerAction(action, CallgrindRemoteActionId),
Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
QObject::connect(action, &QAction::triggered, this, [this, action] {
- auto runConfig = SessionManager::startupRunConfiguration();
+ auto runConfig = ProjectManager::startupRunConfiguration();
if (!runConfig) {
showCannotStartDialog(action->text());
return;
@@ -361,7 +361,7 @@ CallgrindToolPrivate::CallgrindToolPrivate()
action->setIcon(kCachegrindIcon.icon());
action->setToolTip(Tr::tr("Open results in KCachegrind."));
connect(action, &QAction::triggered, this, [this, settings] {
- QtcProcess::startDetached({FilePath::fromString(settings->kcachegrindExecutable.value()), { m_lastFileName }});
+ Process::startDetached({FilePath::fromString(settings->kcachegrindExecutable.value()), { m_lastFileName }});
});
// dump action
diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp
index 3bb8a66a02..48e9afd801 100644
--- a/src/plugins/valgrind/memcheckerrorview.cpp
+++ b/src/plugins/valgrind/memcheckerrorview.cpp
@@ -14,7 +14,7 @@
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <utils/qtcassert.h>
#include <utils/icon.h>
diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp
index d4a5e4a649..552d5a2823 100644
--- a/src/plugins/valgrind/memchecktool.cpp
+++ b/src/plugins/valgrind/memchecktool.cpp
@@ -7,7 +7,6 @@
#include "valgrindengine.h"
#include "valgrindrunner.h"
#include "valgrindsettings.h"
-#include "valgrindsettings.h"
#include "valgrindtr.h"
#include "xmlprotocol/error.h"
@@ -30,8 +29,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runconfiguration.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
@@ -50,8 +49,9 @@
#include <utils/checkablemessagebox.h>
#include <utils/fancymainwindow.h>
#include <utils/pathchooser.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
+#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QAction>
@@ -129,9 +129,9 @@ public:
void start() override
{
QTC_ASSERT(!m_process, return);
- m_process.reset(new QtcProcess);
+ m_process.reset(new Process);
m_process->setCommand({device()->filePath("echo"), "-n $SSH_CLIENT", CommandLine::Raw});
- connect(m_process.get(), &QtcProcess::done, this, [this] {
+ connect(m_process.get(), &Process::done, this, [this] {
if (m_process->error() != QProcess::UnknownError) {
reportFailure();
return;
@@ -159,7 +159,7 @@ public:
}
private:
- std::unique_ptr<QtcProcess> m_process = nullptr;
+ std::unique_ptr<Process> m_process = nullptr;
QHostAddress *m_localServerAddress = nullptr;
};
@@ -214,7 +214,7 @@ QStringList MemcheckToolRunner::toolArguments() const
if (m_withGdb)
arguments << "--vgdb=yes" << "--vgdb-error=0";
- arguments << Utils::ProcessArgs::splitArgs(m_settings.memcheckArguments.value());
+ arguments << ProcessArgs::splitArgs(m_settings.memcheckArguments.value(), HostOsInfo::hostOs());
return arguments;
}
@@ -337,7 +337,7 @@ bool MemcheckErrorFilterProxyModel::filterAcceptsRow(int sourceRow, const QModel
// ALGORITHM: look at last five stack frames, if none of these is inside any open projects,
// assume this error was created by an external library
QSet<QString> validFolders;
- for (Project *project : SessionManager::projects()) {
+ for (Project *project : ProjectManager::projects()) {
validFolders << project->projectDirectory().toString();
const QList<Target *> targets = project->targets();
for (const Target *target : targets) {
@@ -609,7 +609,7 @@ MemcheckToolPrivate::MemcheckToolPrivate()
filterButton->setIcon(Icons::FILTER.icon());
filterButton->setText(Tr::tr("Error Filter"));
filterButton->setPopupMode(QToolButton::InstantPopup);
- filterButton->setProperty("noArrow", true);
+ filterButton->setProperty(StyleHelper::C_NO_ARROW, true);
m_filterMenu = new QMenu(filterButton);
for (QAction *filterAction : std::as_const(m_errorFilterActions))
@@ -676,7 +676,7 @@ MemcheckToolPrivate::MemcheckToolPrivate()
menu->addAction(ActionManager::registerAction(action, "Memcheck.Remote"),
Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
QObject::connect(action, &QAction::triggered, this, [this, action] {
- RunConfiguration *runConfig = SessionManager::startupRunConfiguration();
+ RunConfiguration *runConfig = ProjectManager::startupRunConfiguration();
if (!runConfig) {
showCannotStartDialog(action->text());
return;
@@ -718,7 +718,7 @@ void MemcheckToolPrivate::heobAction()
Abi abi;
bool hasLocalRc = false;
Kit *kit = nullptr;
- if (Target *target = SessionManager::startupTarget()) {
+ if (Target *target = ProjectManager::startupTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration()) {
kit = target->kit();
if (kit) {
@@ -800,19 +800,19 @@ void MemcheckToolPrivate::heobAction()
const QString dwarfstack = QString("dwarfstack%1.dll").arg(abi.wordWidth());
const QString dwarfstackPath = dialog.path() + '/' + dwarfstack;
if (!QFile::exists(dwarfstackPath)
- && CheckableMessageBox::doNotShowAgainInformation(
+ && CheckableMessageBox::information(
Core::ICore::dialogParent(),
Tr::tr("Heob"),
Tr::tr("Heob used with MinGW projects needs the %1 DLLs for proper "
- "stacktrace resolution.")
+ "stacktrace resolution.")
.arg(
"<a "
"href=\"https://github.com/ssbssa/dwarfstack/releases\">Dwarfstack</a>"),
ICore::settings(),
"HeobDwarfstackInfo",
- QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
- QDialogButtonBox::Ok)
- != QDialogButtonBox::Ok)
+ QMessageBox::Ok | QMessageBox::Cancel,
+ QMessageBox::Ok)
+ != QMessageBox::Ok)
return;
}
@@ -940,7 +940,7 @@ void MemcheckToolPrivate::maybeActiveRunConfigurationChanged()
updateRunActions();
ValgrindBaseSettings *settings = nullptr;
- if (Project *project = SessionManager::startupProject())
+ if (Project *project = ProjectManager::startupProject())
if (Target *target = project->activeTarget())
if (RunConfiguration *rc = target->activeRunConfiguration())
settings = rc->currentSettings<ValgrindBaseSettings>(ANALYZER_VALGRIND_SETTINGS);
diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp
index 08ed8ae5ff..6273580e82 100644
--- a/src/plugins/valgrind/suppressiondialog.cpp
+++ b/src/plugins/valgrind/suppressiondialog.cpp
@@ -12,9 +12,9 @@
#include "xmlprotocol/stack.h"
#include "xmlprotocol/frame.h"
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <utils/algorithm.h>
@@ -182,8 +182,8 @@ void SuppressionDialog::accept()
return;
// Add file to project if there is a project containing this file on the file system.
- if (!ProjectExplorer::SessionManager::projectForFile(path)) {
- for (ProjectExplorer::Project *p : ProjectExplorer::SessionManager::projects()) {
+ if (!ProjectExplorer::ProjectManager::projectForFile(path)) {
+ for (ProjectExplorer::Project *p : ProjectExplorer::ProjectManager::projects()) {
if (path.startsWith(p->projectDirectory().toString())) {
p->rootProjectNode()->addFiles({path});
break;
diff --git a/src/plugins/valgrind/valgrind.qbs b/src/plugins/valgrind/valgrind.qbs
index f0e117ce98..973b1d7b85 100644
--- a/src/plugins/valgrind/valgrind.qbs
+++ b/src/plugins/valgrind/valgrind.qbs
@@ -76,9 +76,7 @@ QtcPlugin {
]
}
- Group {
- name: "Test sources"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"valgrindmemcheckparsertest.cpp",
"valgrindmemcheckparsertest.h",
diff --git a/src/plugins/valgrind/valgrindengine.cpp b/src/plugins/valgrind/valgrindengine.cpp
index 58919333f1..462d1b1a73 100644
--- a/src/plugins/valgrind/valgrindengine.cpp
+++ b/src/plugins/valgrind/valgrindengine.cpp
@@ -22,8 +22,6 @@
#include <QApplication>
-#define VALGRIND_DEBUG_OUTPUT 0
-
using namespace Debugger;
using namespace Core;
using namespace Utils;
@@ -39,8 +37,10 @@ ValgrindToolRunner::ValgrindToolRunner(RunControl *runControl)
m_settings.fromMap(runControl->settingsData(ANALYZER_VALGRIND_SETTINGS));
- connect(&m_runner, &ValgrindRunner::appendMessage,
- this, &ValgrindToolRunner::appendMessage);
+ connect(&m_runner,
+ &ValgrindRunner::appendMessage,
+ this,
+ [this](const QString &msg, Utils::OutputFormat format) { appendMessage(msg, format); });
connect(&m_runner, &ValgrindRunner::valgrindExecuted,
this, [this](const QString &commandLine) {
appendMessage(commandLine, NormalMessageFormat);
@@ -53,6 +53,16 @@ ValgrindToolRunner::ValgrindToolRunner(RunControl *runControl)
void ValgrindToolRunner::start()
{
+ FilePath valgrindExecutable = m_settings.valgrindExecutable.filePath();
+ if (IDevice::ConstPtr dev = DeviceKitAspect::device(runControl()->kit()))
+ valgrindExecutable = dev->filePath(valgrindExecutable.path());
+ if (!valgrindExecutable.isExecutableFile()) {
+ reportFailure(Tr::tr("Valgrind executable \"%1\" not found or not executable.\n"
+ "Check settings or ensure valgrind is installed and available in PATH.")
+ .arg(valgrindExecutable.toUserOutput()));
+ return;
+ }
+
FutureProgress *fp = ProgressManager::addTimedTask(m_progress, progressTitle(), "valgrind", 100);
connect(fp, &FutureProgress::canceled,
this, &ValgrindToolRunner::handleProgressCanceled);
@@ -60,17 +70,6 @@ void ValgrindToolRunner::start()
this, &ValgrindToolRunner::handleProgressFinished);
m_progress.reportStarted();
-#if VALGRIND_DEBUG_OUTPUT
- emit outputReceived(Tr::tr("Valgrind options: %1").arg(toolArguments().join(' ')), LogMessageFormat);
- emit outputReceived(Tr::tr("Working directory: %1").arg(runnable().workingDirectory), LogMessageFormat);
- emit outputReceived(Tr::tr("Command line arguments: %1").arg(runnable().debuggeeArgs), LogMessageFormat);
-#endif
-
-
- FilePath valgrindExecutable = m_settings.valgrindExecutable.filePath();
- if (IDevice::ConstPtr dev = DeviceKitAspect::device(runControl()->kit()))
- valgrindExecutable = dev->filePath(valgrindExecutable.path());
-
CommandLine valgrind{valgrindExecutable};
valgrind.addArgs(m_settings.valgrindArguments.value(), CommandLine::Raw);
valgrind.addArgs(genericToolArguments());
diff --git a/src/plugins/valgrind/valgrindrunner.cpp b/src/plugins/valgrind/valgrindrunner.cpp
index ef84fba675..fd26544422 100644
--- a/src/plugins/valgrind/valgrindrunner.cpp
+++ b/src/plugins/valgrind/valgrindrunner.cpp
@@ -9,8 +9,8 @@
#include <projectexplorer/runcontrol.h>
#include <utils/hostosinfo.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QEventLoop>
#include <QTcpServer>
@@ -25,18 +25,18 @@ class ValgrindRunner::Private : public QObject
{
public:
Private(ValgrindRunner *owner) : q(owner) {
- connect(&m_process, &QtcProcess::started, this, [this] {
+ connect(&m_process, &Process::started, this, [this] {
emit q->valgrindStarted(m_process.processId());
});
- connect(&m_process, &QtcProcess::done, this, [this] {
+ connect(&m_process, &Process::done, this, [this] {
if (m_process.result() != ProcessResult::FinishedWithSuccess)
emit q->processErrorReceived(m_process.errorString(), m_process.error());
emit q->finished();
});
- connect(&m_process, &QtcProcess::readyReadStandardOutput, this, [this] {
+ connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
emit q->appendMessage(m_process.readAllStandardOutput(), StdOutFormat);
});
- connect(&m_process, &QtcProcess::readyReadStandardError, this, [this] {
+ connect(&m_process, &Process::readyReadStandardError, this, [this] {
emit q->appendMessage(m_process.readAllStandardError(), StdErrFormat);
});
@@ -53,7 +53,7 @@ public:
Runnable m_debuggee;
CommandLine m_command;
- QtcProcess m_process;
+ Process m_process;
QHostAddress m_localServerAddress;
@@ -197,7 +197,7 @@ void ValgrindRunner::setLocalServerAddress(const QHostAddress &localServerAddres
void ValgrindRunner::setUseTerminal(bool on)
{
- d->m_process.setTerminalMode(on ? TerminalMode::On : TerminalMode::Off);
+ d->m_process.setTerminalMode(on ? TerminalMode::Run : TerminalMode::Off);
}
void ValgrindRunner::waitForFinished() const
diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp
index fe546ea0cc..bdcf9971e6 100644
--- a/src/plugins/valgrind/valgrindsettings.cpp
+++ b/src/plugins/valgrind/valgrindsettings.cpp
@@ -128,7 +128,7 @@ void SuppressionAspect::setValue(const FilePaths &val)
BaseAspect::setValue(Utils::transform<QStringList>(val, &FilePath::toString));
}
-void SuppressionAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void SuppressionAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!d->addEntry);
QTC_CHECK(!d->removeEntry);
@@ -150,12 +150,12 @@ void SuppressionAspect::addToLayout(Layouting::LayoutBuilder &builder)
connect(d->entryList->selectionModel(), &QItemSelectionModel::selectionChanged,
d, &SuppressionAspectPrivate::slotSuppressionSelectionChanged);
- builder.addItem(Column { Tr::tr("Suppression files:"), st });
+ parent.addItem(Column { Tr::tr("Suppression files:"), st });
Row group {
d->entryList.data(),
Column { d->addEntry.data(), d->removeEntry.data(), st }
};
- builder.addItem(Span { 2, group });
+ parent.addItem(Span { 2, group });
setVolatileValue(BaseAspect::value());
}
diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h
index 22952d0978..a8359804dd 100644
--- a/src/plugins/valgrind/valgrindsettings.h
+++ b/src/plugins/valgrind/valgrindsettings.h
@@ -23,7 +23,7 @@ public:
Utils::FilePaths value() const;
void setValue(const Utils::FilePaths &val);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) final;
+ void addToLayout(Layouting::LayoutItem &parent) final;
void fromMap(const QVariantMap &map) final;
void toMap(QVariantMap &map) const final;
diff --git a/src/plugins/vcpkg/CMakeLists.txt b/src/plugins/vcpkg/CMakeLists.txt
new file mode 100644
index 0000000000..d3fb8d00ed
--- /dev/null
+++ b/src/plugins/vcpkg/CMakeLists.txt
@@ -0,0 +1,17 @@
+add_qtc_plugin(Vcpkg
+ PLUGIN_DEPENDS Core ProjectExplorer
+ SOURCES
+ vcpkg.qrc
+ vcpkgconstants.h
+ vcpkgmanifesteditor.cpp vcpkgmanifesteditor.h
+ vcpkgplugin.cpp vcpkgplugin.h
+ vcpkgsearch.cpp vcpkgsearch.h
+ vcpkgsettings.cpp vcpkgsettings.h
+)
+
+extend_qtc_plugin(Vcpkg
+ CONDITION WITH_TESTS
+ SOURCES
+ vcpkg_test.cpp vcpkg_test.h
+ EXPLICIT_MOC vcpkg_test.h
+)
diff --git a/src/plugins/vcpkg/Vcpkg.json.in b/src/plugins/vcpkg/Vcpkg.json.in
new file mode 100644
index 0000000000..7357c9e845
--- /dev/null
+++ b/src/plugins/vcpkg/Vcpkg.json.in
@@ -0,0 +1,30 @@
+{
+ \"Name\" : \"Vcpkg\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
+ ],
+ \"Experimental\" : true,
+ \"Description\" : \"vcpkg integration.\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList,
+
+ \"Mimetypes\" : [
+ \"<?xml version=\'1.0\' encoding=\'UTF-8\'?>\",
+ \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\",
+ \" <mime-type type=\'application/vcpkg.manifest+json\'>\",
+ \" <sub-class-of type=\'application/json\'/>\",
+ \" <comment>Vcpkg Manifest File</comment>\",
+ \" <glob pattern=\'vcpkg.json\' weight=\'71\'/>\",
+ \" </mime-type>\",
+ \"</mime-info>\"
+ ]
+}
diff --git a/src/plugins/vcpkg/vcpkg.qbs b/src/plugins/vcpkg/vcpkg.qbs
new file mode 100644
index 0000000000..dff796ab91
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg.qbs
@@ -0,0 +1,32 @@
+import qbs 1.0
+
+QtcPlugin {
+ name: "Vcpkg"
+
+ Depends { name: "Qt.widgets" }
+ Depends { name: "Utils" }
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "TextEditor" }
+
+ files: [
+ "vcpkg.qrc",
+ "vcpkgconstants.h",
+ "vcpkgmanifesteditor.cpp",
+ "vcpkgmanifesteditor.h",
+ "vcpkgplugin.cpp",
+ "vcpkgplugin.h",
+ "vcpkgsearch.cpp",
+ "vcpkgsearch.h",
+ "vcpkgsettings.cpp",
+ "vcpkgsettings.h",
+ ]
+
+ QtcTestFiles {
+ files: [
+ "vcpkg_test.h",
+ "vcpkg_test.cpp",
+ ]
+ }
+}
diff --git a/src/plugins/vcpkg/vcpkg.qrc b/src/plugins/vcpkg/vcpkg.qrc
new file mode 100644
index 0000000000..a377b736db
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/vcpkg">
+ <file>wizards/manifest/vcpkg.json.tpl</file>
+ <file>wizards/manifest/wizard.json</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/vcpkg/vcpkg_test.cpp b/src/plugins/vcpkg/vcpkg_test.cpp
new file mode 100644
index 0000000000..65079b96a4
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg_test.cpp
@@ -0,0 +1,110 @@
+// 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 "vcpkg_test.h"
+
+#include "vcpkgsearch.h"
+
+#include <QTest>
+
+namespace Vcpkg::Internal {
+
+VcpkgSearchTest::VcpkgSearchTest(QObject *parent)
+ : QObject(parent)
+{ }
+
+VcpkgSearchTest::~VcpkgSearchTest() = default;
+
+void VcpkgSearchTest::testVcpkgJsonParser_data()
+{
+ QTest::addColumn<QString>("vcpkgManifestJsonData");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("version");
+ QTest::addColumn<QString>("license");
+ QTest::addColumn<QString>("shortDescription");
+ QTest::addColumn<QStringList>("description");
+ QTest::addColumn<QUrl>("homepage");
+ QTest::addColumn<bool>("success");
+
+ QTest::newRow("cimg, version, short description")
+ << R"({
+ "name": "cimg",
+ "version": "2.9.9",
+ "description": "The CImg Library is a small, open-source, and modern C++ toolkit for image processing",
+ "homepage": "https://github.com/dtschump/CImg",
+ "dependencies": [
+ {
+ "name": "vcpkg-cmake",
+ "host": true
+ }
+ ]
+ })"
+ << "cimg"
+ << "2.9.9"
+ << ""
+ << "The CImg Library is a small, open-source, and modern C++ toolkit for image processing"
+ << QStringList()
+ << QUrl::fromUserInput("https://github.com/dtschump/CImg")
+ << true;
+
+ QTest::newRow("catch-classic, version-string, complete description")
+ << R"({
+ "name": "catch-classic",
+ "version-string": "1.12.2",
+ "port-version": 1,
+ "description": [
+ "A modern, header-only test framework for unit tests",
+ "This is specifically the legacy 1.x branch provided for compatibility",
+ "with older compilers."
+ ],
+ "homepage": "https://github.com/catchorg/Catch2"
+ })"
+ << "catch-classic"
+ << "1.12.2"
+ << ""
+ << "A modern, header-only test framework for unit tests"
+ << QStringList({"This is specifically the legacy 1.x branch provided for compatibility",
+ "with older compilers."})
+ << QUrl::fromUserInput("https://github.com/catchorg/Catch2")
+ << true;
+
+ QTest::newRow("Incomplete")
+ << R"({
+ "version-semver": "1.0",
+ "description": "foo",
+ "license": "WTFPL"
+ })"
+ << ""
+ << "1.0"
+ << "WTFPL"
+ << "foo"
+ << QStringList()
+ << QUrl()
+ << false;
+}
+
+void VcpkgSearchTest::testVcpkgJsonParser()
+{
+ QFETCH(QString, vcpkgManifestJsonData);
+ QFETCH(QString, name);
+ QFETCH(QString, version);
+ QFETCH(QString, license);
+ QFETCH(QString, shortDescription);
+ QFETCH(QStringList, description);
+ QFETCH(QUrl, homepage);
+ QFETCH(bool, success);
+
+ bool ok = false;
+ const Search::VcpkgManifest mf =
+ Search::parseVcpkgManifest(vcpkgManifestJsonData.toUtf8(), &ok);
+
+ QCOMPARE(mf.name, name);
+ QCOMPARE(mf.version, version);
+ QCOMPARE(mf.license, license);
+ QCOMPARE(mf.shortDescription, shortDescription);
+ QCOMPARE(mf.description, description);
+ QCOMPARE(mf.homepage, homepage);
+ QCOMPARE(ok, success);
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkg_test.h b/src/plugins/vcpkg/vcpkg_test.h
new file mode 100644
index 0000000000..8175e28d59
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkg_test.h
@@ -0,0 +1,24 @@
+// 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 <coreplugin/dialogs/ioptionspage.h>
+#include <utils/aspects.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgSearchTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ VcpkgSearchTest(QObject *parent = nullptr);
+ ~VcpkgSearchTest();
+
+private slots:
+ void testVcpkgJsonParser_data();
+ void testVcpkgJsonParser();
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgconstants.h b/src/plugins/vcpkg/vcpkgconstants.h
new file mode 100644
index 0000000000..644dfab95f
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgconstants.h
@@ -0,0 +1,14 @@
+// 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 Vcpkg::Constants {
+
+const char TOOLSSETTINGSPAGE_ID[] = "Vcpkg.VcpkgSettings";
+const char WEBSITE_URL[] = "https://vcpkg.io/";
+const char ENVVAR_VCPKG_ROOT[] = "VCPKG_ROOT";
+const char VCPKGMANIFEST_EDITOR_ID[] = "Vcpkg.VcpkgManifestEditor";
+const char VCPKGMANIFEST_MIMETYPE[] = "application/vcpkg.manifest+json";
+
+} // namespace Vcpkg::Constants
diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.cpp b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp
new file mode 100644
index 0000000000..fa7c6ebfaf
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp
@@ -0,0 +1,71 @@
+// 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 "vcpkgmanifesteditor.h"
+
+#include "vcpkgconstants.h"
+#include "vcpkgsearch.h"
+#include "vcpkgsettings.h"
+#include "vcpkgtr.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/utilsicons.h>
+
+#include <texteditor/textdocument.h>
+
+#include <QToolBar>
+
+namespace Vcpkg::Internal {
+
+class VcpkgManifestEditorWidget : public TextEditor::TextEditorWidget
+{
+public:
+ VcpkgManifestEditorWidget()
+ {
+ m_searchPkgAction = toolBar()->addAction(Utils::Icons::ZOOM_TOOLBAR.icon(),
+ Tr::tr("Search package..."));
+ connect(m_searchPkgAction, &QAction::triggered, this, [this] {
+ const Search::VcpkgManifest package = Search::showVcpkgPackageSearchDialog();
+ if (!package.name.isEmpty())
+ textCursor().insertText(package.name);
+ });
+ updateToolBar();
+
+ QAction *optionsAction = toolBar()->addAction(Utils::Icons::SETTINGS_TOOLBAR.icon(),
+ Core::ICore::msgShowOptionsDialog());
+ connect(optionsAction, &QAction::triggered, [] {
+ Core::ICore::showOptionsDialog(Constants::TOOLSSETTINGSPAGE_ID);
+ });
+
+ connect(&VcpkgSettings::instance()->vcpkgRoot, &Utils::BaseAspect::changed,
+ this, &VcpkgManifestEditorWidget::updateToolBar);
+ }
+
+ void updateToolBar()
+ {
+ m_searchPkgAction->setEnabled(VcpkgSettings::instance()->vcpkgRootValid());
+ }
+
+private:
+ QAction *m_searchPkgAction;
+};
+
+static TextEditor::TextDocument *createVcpkgManifestDocument()
+{
+ auto doc = new TextEditor::TextDocument;
+ doc->setId(Constants::VCPKGMANIFEST_EDITOR_ID);
+ return doc;
+}
+
+VcpkgManifestEditorFactory::VcpkgManifestEditorFactory()
+{
+ setId(Constants::VCPKGMANIFEST_EDITOR_ID);
+ setDisplayName(Tr::tr("Vcpkg Manifest Editor"));
+ addMimeType(Constants::VCPKGMANIFEST_MIMETYPE);
+ setDocumentCreator(createVcpkgManifestDocument);
+ setEditorWidgetCreator([] { return new VcpkgManifestEditorWidget; });
+ setUseGenericHighlighter(true);
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.h b/src/plugins/vcpkg/vcpkgmanifesteditor.h
new file mode 100644
index 0000000000..c7762d69df
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgmanifesteditor.h
@@ -0,0 +1,16 @@
+// 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 <texteditor/texteditor.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgManifestEditorFactory : public TextEditor::TextEditorFactory
+{
+public:
+ VcpkgManifestEditorFactory();
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgplugin.cpp b/src/plugins/vcpkg/vcpkgplugin.cpp
new file mode 100644
index 0000000000..4ef89499a7
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgplugin.cpp
@@ -0,0 +1,38 @@
+// 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 "vcpkgplugin.h"
+
+#ifdef WITH_TESTS
+#include "vcpkg_test.h"
+#endif // WITH_TESTS
+#include "vcpkgmanifesteditor.h"
+#include "vcpkgsettings.h"
+
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgPluginPrivate
+{
+public:
+ VcpkgManifestEditorFactory manifestEditorFactory;
+ VcpkgSettings settings;
+};
+
+VcpkgPlugin::~VcpkgPlugin()
+{
+ delete d;
+}
+
+void VcpkgPlugin::initialize()
+{
+ d = new VcpkgPluginPrivate;
+ ProjectExplorer::JsonWizardFactory::addWizardPath(":/vcpkg/wizards/");
+
+#ifdef WITH_TESTS
+ addTest<VcpkgSearchTest>();
+#endif
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgplugin.h b/src/plugins/vcpkg/vcpkgplugin.h
new file mode 100644
index 0000000000..797083ea95
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgplugin.h
@@ -0,0 +1,26 @@
+// 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 <extensionsystem/iplugin.h>
+
+namespace ProjectExplorer { class Project; }
+
+namespace Vcpkg::Internal {
+
+class VcpkgPlugin final : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Vcpkg.json")
+
+public:
+ ~VcpkgPlugin();
+
+ void initialize() final;
+
+private:
+ class VcpkgPluginPrivate *d = nullptr;
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgsearch.cpp b/src/plugins/vcpkg/vcpkgsearch.cpp
new file mode 100644
index 0000000000..a2ec2c66c2
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsearch.cpp
@@ -0,0 +1,214 @@
+// 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 "vcpkgsearch.h"
+
+#include "qpushbutton.h"
+#include "vcpkgsettings.h"
+#include "vcpkgtr.h"
+
+#include <utils/algorithm.h>
+#include <utils/fancylineedit.h>
+#include <utils/fileutils.h>
+#include <utils/itemviews.h>
+#include <utils/layoutbuilder.h>
+
+#include <coreplugin/icore.h>
+
+#include <QDialogButtonBox>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QTextBrowser>
+
+using namespace Utils;
+
+namespace Vcpkg::Internal::Search {
+
+class VcpkgPackageSearchDialog : public QDialog
+{
+public:
+ explicit VcpkgPackageSearchDialog(QWidget *parent);
+
+ VcpkgManifest selectedPackage() const;
+
+private:
+ void listPackages(const QString &filter);
+ void showPackageDetails(const QString &packageName);
+
+ VcpkgManifests m_allPackages;
+ VcpkgManifest m_selectedPackage;
+
+ FancyLineEdit *m_packagesFilter;
+ ListWidget *m_packagesList;
+ QLineEdit *m_vcpkgName;
+ QLabel *m_vcpkgVersion;
+ QLabel *m_vcpkgLicense;
+ QTextBrowser *m_vcpkgDescription;
+ QLabel *m_vcpkgHomepage;
+ QDialogButtonBox *m_buttonBox;
+};
+
+VcpkgPackageSearchDialog::VcpkgPackageSearchDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ resize(920, 400);
+
+ m_packagesFilter = new FancyLineEdit;
+ m_packagesFilter->setFiltering(true);
+ m_packagesFilter->setFocus();
+ m_packagesFilter->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+
+ m_packagesList = new ListWidget;
+ m_packagesList->setMaximumWidth(300);
+
+ m_vcpkgName = new QLineEdit;
+ m_vcpkgName->setReadOnly(true);
+
+ m_vcpkgVersion = new QLabel;
+ m_vcpkgLicense = new QLabel;
+ m_vcpkgDescription = new QTextBrowser;
+
+ m_vcpkgHomepage = new QLabel;
+ m_vcpkgHomepage->setOpenExternalLinks(true);
+ m_vcpkgHomepage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
+ m_vcpkgHomepage->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close);
+
+ using namespace Layouting;
+ Column {
+ Row {
+ Column {
+ m_packagesFilter,
+ m_packagesList,
+ },
+ Form {
+ Tr::tr("Name:"), m_vcpkgName, br,
+ Tr::tr("Version:"), m_vcpkgVersion, br,
+ Tr::tr("License:"), m_vcpkgLicense, br,
+ Tr::tr("Description:"), m_vcpkgDescription, br,
+ Tr::tr("Homepage:"), m_vcpkgHomepage, br,
+ },
+ },
+ m_buttonBox,
+ }.attachTo(this);
+
+ m_allPackages = vcpkgManifests(VcpkgSettings::instance()->vcpkgRoot.filePath());
+
+ listPackages({});
+
+ connect(m_packagesFilter, &FancyLineEdit::filterChanged,
+ this, &VcpkgPackageSearchDialog::listPackages);
+ connect(m_packagesList, &ListWidget::currentTextChanged,
+ this, &VcpkgPackageSearchDialog::showPackageDetails);
+ connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
+
+VcpkgManifest VcpkgPackageSearchDialog::selectedPackage() const
+{
+ return m_selectedPackage;
+}
+
+void VcpkgPackageSearchDialog::listPackages(const QString &filter)
+{
+ const VcpkgManifests filteredPackages = filtered(m_allPackages,
+ [&filter] (const VcpkgManifest &package) {
+ return filter.isEmpty()
+ || package.name.contains(filter, Qt::CaseInsensitive)
+ || package.shortDescription.contains(filter, Qt::CaseInsensitive)
+ || package.description.contains(filter, Qt::CaseInsensitive);
+ });
+ QStringList names = transform(filteredPackages, [] (const VcpkgManifest &package) {
+ return package.name;
+ });
+ names.sort();
+ m_packagesList->clear();
+ m_packagesList->addItems(names);
+}
+
+void VcpkgPackageSearchDialog::showPackageDetails(const QString &packageName)
+{
+ const VcpkgManifest manifest = findOrDefault(m_allPackages,
+ [&packageName] (const VcpkgManifest &m) {
+ return m.name == packageName;
+ });
+
+ m_vcpkgName->setText(manifest.name);
+ m_vcpkgVersion->setText(manifest.version);
+ m_vcpkgLicense->setText(manifest.license);
+ QString description = manifest.shortDescription;
+ if (!manifest.description.isEmpty())
+ description.append("<p>" + manifest.description.join("</p><p>") + "</p>");
+ m_vcpkgDescription->setText(description);
+ m_vcpkgHomepage->setText(QString::fromLatin1("<a href=\"%1\">%1</a>")
+ .arg(manifest.homepage.toDisplayString()));
+
+ m_selectedPackage = manifest;
+ m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!manifest.name.isEmpty());
+}
+
+VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok)
+{
+ // https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json
+ VcpkgManifest result;
+ const QJsonObject jsonObject = QJsonDocument::fromJson(vcpkgManifestJsonData).object();
+ if (const QJsonValue name = jsonObject.value("name"); !name.isUndefined())
+ result.name = name.toString();
+ for (const char *key : {"version", "version-semver", "version-date", "version-string"} ) {
+ if (const QJsonValue ver = jsonObject.value(QLatin1String(key)); !ver.isUndefined()) {
+ result.version = ver.toString();
+ break;
+ }
+ }
+ if (const QJsonValue license = jsonObject.value("license"); !license.isUndefined())
+ result.license = license.toString();
+ if (const QJsonValue description = jsonObject.value("description"); !description.isUndefined()) {
+ if (description.isArray()) {
+ const QJsonArray descriptionLines = description.toArray();
+ for (const QJsonValue &val : descriptionLines) {
+ const QString line = val.toString();
+ if (result.shortDescription.isEmpty()) {
+ result.shortDescription = line;
+ continue;
+ }
+ result.description.append(line);
+ }
+ } else {
+ result.shortDescription = description.toString();
+ }
+ }
+ if (const QJsonValue homepage = jsonObject.value("homepage"); !homepage.isUndefined())
+ result.homepage = QUrl::fromUserInput(homepage.toString());
+
+ if (ok)
+ *ok = !(result.name.isEmpty() || result.version.isEmpty());
+
+ return result;
+}
+
+VcpkgManifests vcpkgManifests(const FilePath &vcpkgRoot)
+{
+ const FilePath portsDir = vcpkgRoot / "ports";
+ VcpkgManifests result;
+ const FilePaths manifestFiles =
+ portsDir.dirEntries({{"vcpkg.json"}, QDir::Files, QDirIterator::Subdirectories});
+ for (const FilePath &manifestFile : manifestFiles) {
+ FileReader reader;
+ if (reader.fetch(manifestFile)) {
+ const QByteArray &manifestData = reader.data();
+ const VcpkgManifest manifest = parseVcpkgManifest(manifestData);
+ result.append(manifest);
+ }
+ }
+ return result;
+}
+
+VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent)
+{
+ VcpkgPackageSearchDialog dlg(parent ? parent : Core::ICore::dialogParent());
+ return (dlg.exec() == QDialog::Accepted) ? dlg.selectedPackage() : VcpkgManifest();
+}
+
+} // namespace Vcpkg::Internal::Search
diff --git a/src/plugins/vcpkg/vcpkgsearch.h b/src/plugins/vcpkg/vcpkgsearch.h
new file mode 100644
index 0000000000..bb2d568a00
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsearch.h
@@ -0,0 +1,29 @@
+// 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 <coreplugin/dialogs/ioptionspage.h>
+#include <utils/aspects.h>
+
+#include <QUrl>
+
+namespace Vcpkg::Internal::Search {
+
+struct VcpkgManifest
+{
+ QString name;
+ QString version;
+ QString license;
+ QString shortDescription;
+ QStringList description;
+ QUrl homepage;
+};
+
+using VcpkgManifests = QList<VcpkgManifest>;
+
+VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok = nullptr);
+VcpkgManifests vcpkgManifests(const Utils::FilePath &vcpkgRoot);
+VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent = nullptr);
+
+} // namespace Vcpkg::Internal::Search
diff --git a/src/plugins/vcpkg/vcpkgsettings.cpp b/src/plugins/vcpkg/vcpkgsettings.cpp
new file mode 100644
index 0000000000..85b4b6f4fa
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsettings.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 "vcpkgsettings.h"
+
+#include "vcpkgconstants.h"
+
+#include <cmakeprojectmanager/cmakeprojectconstants.h>
+
+#include <utils/aspects.h>
+#include <utils/environment.h>
+#include <utils/layoutbuilder.h>
+#include <utils/utilsicons.h>
+
+#include <QDesktopServices>
+#include <QToolButton>
+
+namespace Vcpkg::Internal {
+
+static VcpkgSettings *theSettings = nullptr;
+
+VcpkgSettings *VcpkgSettings::instance()
+{
+ return theSettings;
+}
+
+VcpkgSettings::VcpkgSettings()
+{
+ theSettings = this;
+
+ setSettingsGroup("Vcpkg");
+
+ setId(Constants::TOOLSSETTINGSPAGE_ID);
+ setDisplayName("Vcpkg");
+ setCategory(CMakeProjectManager::Constants::Settings::CATEGORY);
+
+ setLayouter([this](QWidget *widget) {
+ using namespace Layouting;
+ auto websiteButton = new QToolButton;
+ websiteButton->setIcon(Utils::Icons::ONLINE.icon());
+ websiteButton->setToolTip(Constants::WEBSITE_URL);
+
+ // clang-format off
+ using namespace Layouting;
+ Column {
+ Group {
+ title(tr("Vcpkg installation")),
+ Form {
+ Utils::PathChooser::label(),
+ Span{ 2, Row{ vcpkgRoot, websiteButton} },
+ },
+ },
+ st,
+ }.attachTo(widget);
+ // clang-format on
+
+ connect(websiteButton, &QAbstractButton::clicked, [] {
+ QDesktopServices::openUrl(QUrl::fromUserInput(Constants::WEBSITE_URL));
+ });
+ });
+
+ registerAspect(&vcpkgRoot);
+ vcpkgRoot.setSettingsKey("VcpkgRoot");
+ vcpkgRoot.setDisplayStyle(Utils::StringAspect::PathChooserDisplay);
+ vcpkgRoot.setExpectedKind(Utils::PathChooser::ExistingDirectory);
+ vcpkgRoot.setDefaultValue(Utils::qtcEnvironmentVariable(Constants::ENVVAR_VCPKG_ROOT));
+
+ readSettings();
+}
+
+bool VcpkgSettings::vcpkgRootValid() const
+{
+ return (vcpkgRoot.filePath() / "vcpkg").withExecutableSuffix().isExecutableFile();
+}
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgsettings.h b/src/plugins/vcpkg/vcpkgsettings.h
new file mode 100644
index 0000000000..fb1c478fc4
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgsettings.h
@@ -0,0 +1,21 @@
+// 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 <coreplugin/dialogs/ioptionspage.h>
+
+namespace Vcpkg::Internal {
+
+class VcpkgSettings : public Core::PagedSettings
+{
+public:
+ VcpkgSettings();
+
+ static VcpkgSettings *instance();
+ bool vcpkgRootValid() const;
+
+ Utils::StringAspect vcpkgRoot;
+};
+
+} // namespace Vcpkg::Internal
diff --git a/src/plugins/vcpkg/vcpkgtr.h b/src/plugins/vcpkg/vcpkgtr.h
new file mode 100644
index 0000000000..0914ce6444
--- /dev/null
+++ b/src/plugins/vcpkg/vcpkgtr.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCoreApplication>
+
+namespace Vcpkg {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(Vcpkg)
+};
+
+} // namespace Vcpkg
diff --git a/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl b/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl
new file mode 100644
index 0000000000..1519949d2e
--- /dev/null
+++ b/src/plugins/vcpkg/wizards/manifest/vcpkg.json.tpl
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
+ "name": "%{Name}",
+ "version-string": "%{VersionString}",
+%{Dependencies}
+}
diff --git a/src/plugins/vcpkg/wizards/manifest/wizard.json b/src/plugins/vcpkg/wizards/manifest/wizard.json
new file mode 100644
index 0000000000..80e8b0fcc5
--- /dev/null
+++ b/src/plugins/vcpkg/wizards/manifest/wizard.json
@@ -0,0 +1,80 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ ],
+ "id": "VcpkgManifest.Json",
+ "category": "U.VcpkgManifest",
+ "trDescription": "Creates a vcpkg.json manifest file.",
+ "trDisplayName": "vcpkg.json Manifest File",
+ "trDisplayCategory": "vcpkg",
+ "iconText": "json",
+
+ "options": [
+ { "key": "InitialFileName", "value": "vcpkg.json" },
+ { "key": "TargetPath", "value": "%{Path}" }
+ ],
+
+ "pages":
+ [
+ {
+ "trDisplayName": "Location",
+ "trShortTitle": "Location",
+ "typeId": "File"
+ },
+ {
+ "trDisplayName": "vcpkg.json Manifest File",
+ "trShortTitle": "Manifest fields",
+ "typeId": "Fields",
+ "data":
+ [
+ {
+ "name": "Name",
+ "trDisplayName": "Name:",
+ "mandatory": true,
+ "type": "LineEdit",
+ "data":
+ {
+ "trText": "mypackage",
+ "validator": "^[a-z_0-9]+$"
+ }
+ },
+ {
+ "name": "VersionString",
+ "trDisplayName": "Version string:",
+ "mandatory": true,
+ "type": "LineEdit",
+ "data":
+ {
+ "trText": "0.0.1"
+ }
+ },
+ {
+ "name": "Dependencies",
+ "trDisplayName": "Dependencies:",
+ "mandatory": false,
+ "type": "TextEdit",
+ "data":
+ {
+ "trText": " \"dependencies\": [\n \"fmt\"\n ]"
+ }
+ }
+ ]
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators":
+ [
+ {
+ "typeId": "File",
+ "data":
+ {
+ "source": "vcpkg.json.tpl",
+ "target": "%{Path}/vcpkg.json",
+ "openInEditor": true
+ }
+ }
+ ]
+}
diff --git a/src/plugins/vcsbase/cleandialog.cpp b/src/plugins/vcsbase/cleandialog.cpp
index bc5ddafe66..ae0b14a7ba 100644
--- a/src/plugins/vcsbase/cleandialog.cpp
+++ b/src/plugins/vcsbase/cleandialog.cpp
@@ -9,8 +9,8 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
+#include <utils/async.h>
#include <utils/layoutbuilder.h>
-#include <utils/runextensions.h>
#include <QApplication>
#include <QCheckBox>
@@ -38,10 +38,10 @@ enum { nameColumn, columnCount };
enum { fileNameRole = Qt::UserRole, isDirectoryRole = Qt::UserRole + 1 };
// Helper for recursively removing files.
-static void removeFileRecursion(QFutureInterface<void> &futureInterface,
- const QFileInfo &f, QString *errorMessage)
+static void removeFileRecursion(QPromise<void> &promise, const QFileInfo &f,
+ QString *errorMessage)
{
- if (futureInterface.isCanceled())
+ if (promise.isCanceled())
return;
// The version control system might list files/directory in arbitrary
// order, causing files to be removed from parent directories.
@@ -51,7 +51,7 @@ static void removeFileRecursion(QFutureInterface<void> &futureInterface,
const QDir dir(f.absoluteFilePath());
const QList<QFileInfo> infos = dir.entryInfoList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Hidden);
for (const QFileInfo &fi : infos)
- removeFileRecursion(futureInterface, fi, errorMessage);
+ removeFileRecursion(promise, fi, errorMessage);
QDir parent = f.absoluteDir();
if (!parent.rmdir(f.fileName()))
errorMessage->append(Tr::tr("The directory %1 could not be deleted.")
@@ -67,18 +67,19 @@ static void removeFileRecursion(QFutureInterface<void> &futureInterface,
}
// Cleaning files in the background
-static void runCleanFiles(QFutureInterface<void> &futureInterface,
- const FilePath &repository, const QStringList &files,
+static void runCleanFiles(QPromise<void> &promise, const FilePath &repository,
+ const QStringList &files,
const std::function<void(const QString&)> &errorHandler)
{
QString errorMessage;
- futureInterface.setProgressRange(0, files.size());
- futureInterface.setProgressValue(0);
+ promise.setProgressRange(0, files.size());
+ promise.setProgressValue(0);
+ int fileIndex = 0;
for (const QString &name : files) {
- removeFileRecursion(futureInterface, QFileInfo(name), &errorMessage);
- if (futureInterface.isCanceled())
+ removeFileRecursion(promise, QFileInfo(name), &errorMessage);
+ if (promise.isCanceled())
break;
- futureInterface.setProgressValue(futureInterface.progressValue() + 1);
+ promise.setProgressValue(++fileIndex);
}
if (!errorMessage.isEmpty()) {
// Format and emit error.
@@ -258,8 +259,8 @@ bool CleanDialog::promptToDelete()
return false;
// Remove in background
- QFuture<void> task = runAsync(Internal::runCleanFiles, d->m_workingDirectory,
- selectedFiles, Internal::handleError);
+ QFuture<void> task = Utils::asyncRun(Internal::runCleanFiles, d->m_workingDirectory,
+ selectedFiles, Internal::handleError);
const QString taskName = Tr::tr("Cleaning \"%1\"").arg(d->m_workingDirectory.toUserOutput());
Core::ProgressManager::addTask(task, taskName, "VcsBase.cleanRepository");
diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp
index 160095d38d..46c412011e 100644
--- a/src/plugins/vcsbase/commonvcssettings.cpp
+++ b/src/plugins/vcsbase/commonvcssettings.cpp
@@ -94,61 +94,48 @@ CommonVcsSettings::CommonVcsSettings()
class CommonSettingsWidget final : public Core::IOptionsPageWidget
{
public:
- CommonSettingsWidget(CommonOptionsPage *page);
-
- void apply() final;
-
-private:
- void updatePath();
- CommonOptionsPage *m_page;
-};
-
-
-CommonSettingsWidget::CommonSettingsWidget(CommonOptionsPage *page)
- : m_page(page)
-{
- CommonVcsSettings &s = m_page->settings();
-
- auto cacheResetButton = new QPushButton(Tr::tr("Reset VCS Cache"));
- cacheResetButton->setToolTip(Tr::tr("Reset information about which "
- "version control system handles which directory."));
-
- updatePath();
-
- using namespace Layouting;
- Column {
- Row { s.lineWrap, s.lineWrapWidth, st },
- Form {
- s.submitMessageCheckScript,
- s.nickNameMailMap,
- s.nickNameFieldListFile,
- s.sshPasswordPrompt,
- {}, cacheResetButton
- }
- }.attachTo(this);
-
- connect(Core::VcsManager::instance(), &Core::VcsManager::configurationChanged,
- this, &CommonSettingsWidget::updatePath);
- connect(cacheResetButton, &QPushButton::clicked,
- Core::VcsManager::instance(), &Core::VcsManager::clearVersionControlCache);
-}
-
-void CommonSettingsWidget::updatePath()
-{
- EnvironmentChange change;
- change.addAppendToPath(Core::VcsManager::additionalToolsPath());
- m_page->settings().sshPasswordPrompt.setEnvironmentChange(change);
-}
-
-void CommonSettingsWidget::apply()
-{
- CommonVcsSettings &s = m_page->settings();
- if (s.isDirty()) {
- s.apply();
- s.writeSettings(Core::ICore::settings());
- emit m_page->settingsChanged();
+ CommonSettingsWidget(CommonOptionsPage *page)
+ {
+ CommonVcsSettings &s = page->settings();
+
+ auto cacheResetButton = new QPushButton(Tr::tr("Reset VCS Cache"));
+ cacheResetButton->setToolTip(Tr::tr("Reset information about which "
+ "version control system handles which directory."));
+
+ auto updatePath = [&s] {
+ Environment env;
+ env.appendToPath(Core::VcsManager::additionalToolsPath());
+ s.sshPasswordPrompt.setEnvironment(env);
+ };
+
+ using namespace Layouting;
+ Column {
+ Row { s.lineWrap, s.lineWrapWidth, st },
+ Form {
+ s.submitMessageCheckScript, br,
+ s.nickNameMailMap, br,
+ s.nickNameFieldListFile, br,
+ s.sshPasswordPrompt, br,
+ {}, cacheResetButton
+ }
+ }.attachTo(this);
+
+ updatePath();
+
+ connect(Core::VcsManager::instance(), &Core::VcsManager::configurationChanged,
+ this, updatePath);
+ connect(cacheResetButton, &QPushButton::clicked,
+ Core::VcsManager::instance(), &Core::VcsManager::clearVersionControlCache);
+
+ setOnApply([&s] {
+ if (s.isDirty()) {
+ s.apply();
+ s.writeSettings(Core::ICore::settings());
+ emit s.settingsChanged();
+ }
+ });
}
-}
+};
// CommonOptionsPage
diff --git a/src/plugins/vcsbase/commonvcssettings.h b/src/plugins/vcsbase/commonvcssettings.h
index 8d781a45d7..b1cf09ac14 100644
--- a/src/plugins/vcsbase/commonvcssettings.h
+++ b/src/plugins/vcsbase/commonvcssettings.h
@@ -12,11 +12,11 @@ namespace Internal {
class CommonVcsSettings : public Utils::AspectContainer
{
+ Q_OBJECT
+
public:
CommonVcsSettings();
- friend QDebug operator<<(QDebug, const CommonVcsSettings &);
-
Utils::StringAspect nickNameMailMap;
Utils::StringAspect nickNameFieldListFile;
@@ -27,20 +27,18 @@ public:
Utils::BoolAspect lineWrap;
Utils::IntegerAspect lineWrapWidth;
+
+signals:
+ void settingsChanged();
};
class CommonOptionsPage final : public Core::IOptionsPage
{
- Q_OBJECT
-
public:
- explicit CommonOptionsPage();
+ CommonOptionsPage();
CommonVcsSettings &settings() { return m_settings; }
-signals:
- void settingsChanged();
-
private:
CommonVcsSettings m_settings;
};
diff --git a/src/plugins/vcsbase/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp
index eaac1d61d2..63a75e7ac0 100644
--- a/src/plugins/vcsbase/submiteditorwidget.cpp
+++ b/src/plugins/vcsbase/submiteditorwidget.cpp
@@ -127,8 +127,6 @@ struct SubmitEditorWidgetPrivate
SubmitEditorWidget::SubmitEditorWidget() :
d(new SubmitEditorWidgetPrivate)
{
- resize(507, 419);
- setMinimumSize(QSize(0, 0));
setWindowTitle(Tr::tr("Subversion Submit"));
auto scrollAreaWidgetContents = new QWidget();
@@ -208,8 +206,9 @@ SubmitEditorWidget::SubmitEditorWidget() :
using namespace Layouting;
Column {
- scrollArea
- }.attachTo(this, WithoutMargins);
+ scrollArea,
+ noMargin
+ }.attachTo(this);
connect(d->description, &QWidget::customContextMenuRequested,
this, &SubmitEditorWidget::editorCustomContextMenuRequested);
diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp
index c51b1292f9..ffa19b24f7 100644
--- a/src/plugins/vcsbase/vcsbaseclient.cpp
+++ b/src/plugins/vcsbase/vcsbaseclient.cpp
@@ -21,6 +21,7 @@
#include <utils/commandline.h>
#include <utils/environment.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -56,7 +57,7 @@ namespace VcsBase {
VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseSettings *baseSettings)
: m_baseSettings(baseSettings)
{
- m_baseSettings->readSettings(ICore::settings());
+ m_baseSettings->readSettings();
connect(ICore::instance(), &ICore::saveSettingsRequested,
this, &VcsBaseClientImpl::saveSettings);
}
@@ -68,7 +69,7 @@ VcsBaseSettings &VcsBaseClientImpl::settings() const
FilePath VcsBaseClientImpl::vcsBinary() const
{
- return m_baseSettings->binaryPath.filePath();
+ return m_baseSettings->binaryPath();
}
VcsCommand *VcsBaseClientImpl::createCommand(const FilePath &workingDirectory,
@@ -89,6 +90,16 @@ VcsCommand *VcsBaseClientImpl::createCommand(const FilePath &workingDirectory,
return cmd;
}
+void VcsBaseClientImpl::setupCommand(Utils::Process &process,
+ const FilePath &workingDirectory,
+ const QStringList &args) const
+{
+ process.setEnvironment(processEnvironment());
+ process.setWorkingDirectory(workingDirectory);
+ process.setCommand({vcsBinary(), args});
+ process.setUseCtrlCStub(true);
+}
+
void VcsBaseClientImpl::enqueueJob(VcsCommand *cmd, const QStringList &args,
const ExitCodeInterpreter &interpreter) const
{
@@ -193,7 +204,7 @@ void VcsBaseClientImpl::vcsExecWithEditor(const Utils::FilePath &workingDirector
int VcsBaseClientImpl::vcsTimeoutS() const
{
- return m_baseSettings->timeout.value();
+ return m_baseSettings->timeout();
}
VcsCommand *VcsBaseClientImpl::createVcsCommand(const FilePath &defaultWorkingDir,
@@ -224,6 +235,7 @@ VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Id kind, QString title,
connect(baseEditor, &VcsBaseEditorWidget::annotateRevisionRequested,
this, &VcsBaseClientImpl::annotateRevisionRequested);
baseEditor->setSource(source);
+ baseEditor->setDefaultLineNumber(1);
if (codec)
baseEditor->setCodec(codec);
}
diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h
index 6703495c37..035e2f0042 100644
--- a/src/plugins/vcsbase/vcsbaseclient.h
+++ b/src/plugins/vcsbase/vcsbaseclient.h
@@ -23,6 +23,10 @@ class QTextCodec;
class QToolBar;
QT_END_NAMESPACE
+namespace Utils {
+class Process;
+}
+
namespace VcsBase {
class CommandResult;
@@ -56,6 +60,10 @@ public:
VcsCommand *createCommand(const Utils::FilePath &workingDirectory,
VcsBaseEditorWidget *editor = nullptr) const;
+ void setupCommand(Utils::Process &process,
+ const Utils::FilePath &workingDirectory,
+ const QStringList &args) const;
+
void enqueueJob(VcsCommand *cmd, const QStringList &args,
const Utils::ExitCodeInterpreter &interpreter = {}) const;
diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.h b/src/plugins/vcsbase/vcsbaseclientsettings.h
index 87835755ca..ec5ad3aab0 100644
--- a/src/plugins/vcsbase/vcsbaseclientsettings.h
+++ b/src/plugins/vcsbase/vcsbaseclientsettings.h
@@ -5,17 +5,17 @@
#include "vcsbase_global.h"
-#include <utils/aspects.h>
+#include <coreplugin/dialogs/ioptionspage.h>
namespace VcsBase {
-class VCSBASE_EXPORT VcsBaseSettings : public Utils::AspectContainer
+class VCSBASE_EXPORT VcsBaseSettings : public Core::PagedSettings
{
public:
VcsBaseSettings();
~VcsBaseSettings();
- Utils::StringAspect binaryPath;
+ Utils::FilePathAspect binaryPath;
Utils::StringAspect userName;
Utils::StringAspect userEmail;
Utils::IntegerAspect logCount;
@@ -23,9 +23,6 @@ public:
Utils::StringAspect path;
Utils::FilePaths searchPathList() const;
-
-private:
- QString m_settingsGroup;
};
} // namespace VcsBase
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
index c0d31775af..696e96b291 100644
--- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
+++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
@@ -2,28 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "vcsbasediffeditorcontroller.h"
-#include "vcsplugin.h"
-#include <utils/asynctask.h>
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/async.h>
#include <utils/environment.h>
#include <utils/futuresynchronizer.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
using namespace DiffEditor;
+using namespace Tasking;
using namespace Utils;
namespace VcsBase {
-static void readPatch(QFutureInterface<QList<FileData>> &futureInterface, const QString &patch)
-{
- bool ok;
- const QList<FileData> &fileDataList = DiffUtils::readPatch(patch, &ok, &futureInterface);
- futureInterface.reportResult(fileDataList);
-}
-
-/////////////////////
-
class VcsBaseDiffEditorControllerPrivate
{
public:
@@ -32,7 +25,7 @@ public:
VcsBaseDiffEditorController *q;
Environment m_processEnvironment;
FilePath m_vcsBinary;
- const Tasking::TreeStorage<QString> m_inputStorage;
+ const TreeStorage<QString> m_inputStorage;
};
/////////////////////
@@ -47,33 +40,32 @@ VcsBaseDiffEditorController::~VcsBaseDiffEditorController()
delete d;
}
-Tasking::TreeStorage<QString> VcsBaseDiffEditorController::inputStorage() const
+TreeStorage<QString> VcsBaseDiffEditorController::inputStorage() const
{
return d->m_inputStorage;
}
-Tasking::TaskItem VcsBaseDiffEditorController::postProcessTask()
+TaskItem VcsBaseDiffEditorController::postProcessTask()
{
- using namespace Tasking;
-
- const auto setupDiffProcessor = [this](AsyncTask<QList<FileData>> &async) {
+ const auto setupDiffProcessor = [this](Async<QList<FileData>> &async) {
const QString *storage = inputStorage().activeStorage();
QTC_ASSERT(storage, qWarning("Using postProcessTask() requires putting inputStorage() "
"into task tree's root group."));
const QString inputData = storage ? *storage : QString();
- async.setAsyncCallData(readPatch, inputData);
- async.setFutureSynchronizer(Internal::VcsPlugin::futureSynchronizer());
+ async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
+ async.setConcurrentCallData(&DiffUtils::readPatchWithPromise, inputData);
};
- const auto onDiffProcessorDone = [this](const AsyncTask<QList<FileData>> &async) {
- setDiffFiles(async.result());
+ const auto onDiffProcessorDone = [this](const Async<QList<FileData>> &async) {
+ setDiffFiles(async.isResultAvailable() ? async.result() : QList<FileData>());
+ // TODO: We should set the right starting line here
};
- const auto onDiffProcessorError = [this](const AsyncTask<QList<FileData>> &) {
+ const auto onDiffProcessorError = [this](const Async<QList<FileData>> &) {
setDiffFiles({});
};
- return Async<QList<FileData>>(setupDiffProcessor, onDiffProcessorDone, onDiffProcessorError);
+ return AsyncTask<QList<FileData>>(setupDiffProcessor, onDiffProcessorDone, onDiffProcessorError);
}
-void VcsBaseDiffEditorController::setupCommand(QtcProcess &process, const QStringList &args) const
+void VcsBaseDiffEditorController::setupCommand(Process &process, const QStringList &args) const
{
process.setEnvironment(d->m_processEnvironment);
process.setWorkingDirectory(workingDirectory());
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h
index 75672edb1f..63976bc26c 100644
--- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h
+++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h
@@ -9,7 +9,7 @@
namespace Utils {
class Environment;
-class QtcProcess;
+class Process;
} // Utils
namespace VcsBase {
@@ -28,10 +28,10 @@ public:
void setVcsBinary(const Utils::FilePath &path);
protected:
- Utils::Tasking::TreeStorage<QString> inputStorage() const;
- Utils::Tasking::TaskItem postProcessTask();
+ Tasking::TreeStorage<QString> inputStorage() const;
+ Tasking::TaskItem postProcessTask();
- void setupCommand(Utils::QtcProcess &process, const QStringList &args) const;
+ void setupCommand(Utils::Process &process, const QStringList &args) const;
private:
friend class VcsBaseDiffEditorControllerPrivate;
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index c73c14f9f6..26c19c7f91 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -24,7 +24,7 @@
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
@@ -1234,7 +1234,7 @@ static QTextCodec *findProjectCodec(const FilePath &dirPath)
{
typedef QList<ProjectExplorer::Project*> ProjectList;
// Try to find a project under which file tree the file is.
- const ProjectList projects = ProjectExplorer::SessionManager::projects();
+ const ProjectList projects = ProjectExplorer::ProjectManager::projects();
const ProjectExplorer::Project *p
= findOrDefault(projects, equal(&ProjectExplorer::Project::projectDirectory, dirPath));
return p ? p->editorConfiguration()->textCodec() : nullptr;
diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp
index b277a2d068..517ffbdf48 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.cpp
+++ b/src/plugins/vcsbase/vcsbaseplugin.cpp
@@ -9,14 +9,16 @@
#include "vcsplugin.h"
#include <coreplugin/documentmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
-#include <coreplugin/editormanager/editormanager.h>
-#include <projectexplorer/projecttree.h>
+
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/projecttree.h>
+
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QAction>
#include <QDebug>
@@ -195,7 +197,7 @@ StateListener::StateListener(QObject *parent) : QObject(parent)
connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
this, &StateListener::slotStateChanged);
- connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
+ connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged,
this, &StateListener::slotStateChanged);
EditorManager::setWindowTitleVcsTopicHandler(&StateListener::windowTitleVcsTopic);
@@ -213,7 +215,7 @@ QString StateListener::windowTitleVcsTopic(const FilePath &filePath)
searchPath = filePath.absolutePath();
} else {
// use single project's information if there is only one loaded.
- const QList<Project *> projects = SessionManager::projects();
+ const QList<Project *> projects = ProjectManager::projects();
if (projects.size() == 1)
searchPath = projects.first()->projectDirectory();
}
@@ -282,7 +284,7 @@ void StateListener::slotStateChanged()
IVersionControl *projectControl = nullptr;
Project *currentProject = ProjectTree::currentProject();
if (!currentProject)
- currentProject = SessionManager::startupProject();
+ currentProject = ProjectManager::startupProject();
if (currentProject) {
state.currentProjectPath = currentProject->projectDirectory();
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
index eaa825adb1..1895a64f4d 100644
--- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
+++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp
@@ -28,8 +28,8 @@
#include <utils/completingtextedit.h>
#include <utils/fileutils.h>
#include <utils/icon.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <utils/theme/theme.h>
@@ -37,7 +37,7 @@
#include <texteditor/texteditorsettings.h>
#include <projectexplorer/project.h>
-#include <projectexplorer/session.h>
+#include <projectexplorer/projectmanager.h>
#include <QDir>
#include <QFileInfo>
@@ -230,8 +230,8 @@ VcsBaseSubmitEditor::~VcsBaseSubmitEditor()
void VcsBaseSubmitEditor::slotUpdateEditorSettings()
{
const CommonVcsSettings &s = VcsPlugin::instance()->settings();
- setLineWrapWidth(s.lineWrapWidth.value());
- setLineWrap(s.lineWrap.value());
+ setLineWrapWidth(s.lineWrapWidth());
+ setLineWrap(s.lineWrap());
}
// Return a trimmed list of non-empty field texts
@@ -561,7 +561,7 @@ bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript
// Run check process
VcsOutputWindow::appendShellCommandLine(msgCheckScript(d->m_checkScriptWorkingDirectory,
checkScript));
- QtcProcess checkProcess;
+ Process checkProcess;
if (!d->m_checkScriptWorkingDirectory.isEmpty())
checkProcess.setWorkingDirectory(d->m_checkScriptWorkingDirectory);
checkProcess.setCommand({FilePath::fromString(checkScript), {saver.filePath().toString()}});
@@ -606,7 +606,7 @@ void VcsBaseSubmitEditor::filterUntrackedFilesOfProject(const FilePath &reposito
{
for (QStringList::iterator it = untrackedFiles->begin(); it != untrackedFiles->end(); ) {
const FilePath path = repositoryDirectory.resolvePath(*it).absoluteFilePath();
- if (ProjectExplorer::SessionManager::projectForFile(path))
+ if (ProjectExplorer::ProjectManager::projectForFile(path))
++it;
else
it = untrackedFiles->erase(it);
diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp
index 5a4f43d28c..885ff004da 100644
--- a/src/plugins/vcsbase/vcscommand.cpp
+++ b/src/plugins/vcsbase/vcscommand.cpp
@@ -10,8 +10,8 @@
#include <utils/environment.h>
#include <utils/globalfilechangeblocker.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/threadutils.h>
#include <QTextCodec>
@@ -55,10 +55,10 @@ public:
void setup();
void cleanup();
- void setupProcess(QtcProcess *process, const Job &job);
- void installStdCallbacks(QtcProcess *process);
+ void setupProcess(Process *process, const Job &job);
+ void installStdCallbacks(Process *process);
EventLoopMode eventLoopMode() const;
- void handleDone(QtcProcess *process);
+ void handleDone(Process *process);
void startAll();
void startNextJob();
void processDone();
@@ -73,7 +73,7 @@ public:
QList<Job> m_jobs;
int m_currentJob = 0;
- std::unique_ptr<QtcProcess> m_process;
+ std::unique_ptr<Process> m_process;
QString m_stdOut;
QString m_stdErr;
ProcessResult m_result = ProcessResult::StartFailed;
@@ -99,7 +99,7 @@ void VcsCommandPrivate::cleanup()
GlobalFileChangeBlocker::instance()->forceBlocked(false);
}
-void VcsCommandPrivate::setupProcess(QtcProcess *process, const Job &job)
+void VcsCommandPrivate::setupProcess(Process *process, const Job &job)
{
process->setExitCodeInterpreter(job.exitCodeInterpreter);
process->setTimeoutS(job.timeoutS);
@@ -127,12 +127,12 @@ void VcsCommandPrivate::setupProcess(QtcProcess *process, const Job &job)
progress->setProgressParser(m_progressParser);
}
-void VcsCommandPrivate::installStdCallbacks(QtcProcess *process)
+void VcsCommandPrivate::installStdCallbacks(Process *process)
{
if (!(m_flags & RunFlags::MergeOutputChannels) && (m_flags & RunFlags::ProgressiveOutput
|| m_progressParser || !(m_flags & RunFlags::SuppressStdErr))) {
process->setTextChannelMode(Channel::Error, TextChannelMode::MultiLine);
- connect(process, &QtcProcess::textOnStandardError, this, [this](const QString &text) {
+ connect(process, &Process::textOnStandardError, this, [this](const QString &text) {
if (!(m_flags & RunFlags::SuppressStdErr))
VcsOutputWindow::appendError(text);
if (m_flags & RunFlags::ProgressiveOutput)
@@ -142,7 +142,7 @@ void VcsCommandPrivate::installStdCallbacks(QtcProcess *process)
if (m_progressParser || m_flags & RunFlags::ProgressiveOutput
|| m_flags & RunFlags::ShowStdOut) {
process->setTextChannelMode(Channel::Output, TextChannelMode::MultiLine);
- connect(process, &QtcProcess::textOnStandardOutput, this, [this](const QString &text) {
+ connect(process, &Process::textOnStandardOutput, this, [this](const QString &text) {
if (m_flags & RunFlags::ShowStdOut)
VcsOutputWindow::append(text);
if (m_flags & RunFlags::ProgressiveOutput)
@@ -158,7 +158,7 @@ EventLoopMode VcsCommandPrivate::eventLoopMode() const
return EventLoopMode::Off;
}
-void VcsCommandPrivate::handleDone(QtcProcess *process)
+void VcsCommandPrivate::handleDone(Process *process)
{
// Success/Fail message in appropriate window?
if (process->result() == ProcessResult::FinishedWithSuccess) {
@@ -187,8 +187,8 @@ void VcsCommandPrivate::startAll()
void VcsCommandPrivate::startNextJob()
{
QTC_ASSERT(m_currentJob < m_jobs.count(), return);
- m_process.reset(new QtcProcess);
- connect(m_process.get(), &QtcProcess::done, this, &VcsCommandPrivate::processDone);
+ m_process.reset(new Process);
+ connect(m_process.get(), &Process::done, this, &VcsCommandPrivate::processDone);
setupProcess(m_process.get(), m_jobs.at(m_currentJob));
m_process->start();
}
@@ -297,7 +297,7 @@ CommandResult VcsCommand::runBlocking(const Utils::FilePath &workingDirectory,
CommandResult VcsCommand::runBlockingHelper(const CommandLine &command, int timeoutS)
{
- QtcProcess process;
+ Process process;
if (command.executable().isEmpty())
return {};
@@ -321,7 +321,7 @@ void VcsCommand::setProgressParser(const ProgressParser &parser)
d->m_progressParser = parser;
}
-CommandResult::CommandResult(const QtcProcess &process)
+CommandResult::CommandResult(const Process &process)
: m_result(process.result())
, m_exitCode(process.exitCode())
, m_exitMessage(process.exitMessage())
diff --git a/src/plugins/vcsbase/vcscommand.h b/src/plugins/vcsbase/vcscommand.h
index 568d109580..1cc4d5db54 100644
--- a/src/plugins/vcsbase/vcscommand.h
+++ b/src/plugins/vcsbase/vcscommand.h
@@ -20,7 +20,7 @@ QT_END_NAMESPACE
namespace Utils {
class CommandLine;
class Environment;
-class QtcProcess;
+class Process;
}
namespace VcsBase {
@@ -33,7 +33,7 @@ class VCSBASE_EXPORT CommandResult
{
public:
CommandResult() = default;
- CommandResult(const Utils::QtcProcess &process);
+ CommandResult(const Utils::Process &process);
CommandResult(const VcsCommand &command);
CommandResult(Utils::ProcessResult result, const QString &exitMessage)
: m_result(result), m_exitMessage(exitMessage) {}
diff --git a/src/plugins/vcsbase/vcsenums.h b/src/plugins/vcsbase/vcsenums.h
index 69f85f2e5d..5a5b2c42a7 100644
--- a/src/plugins/vcsbase/vcsenums.h
+++ b/src/plugins/vcsbase/vcsenums.h
@@ -9,7 +9,7 @@ namespace VcsBase {
enum class RunFlags {
None = 0, // Empty.
- // QtcProcess related
+ // Process related
MergeOutputChannels = (1 << 0), // See QProcess::ProcessChannelMode::MergedChannels.
ForceCLocale = (1 << 1), // Force C-locale, sets LANG and LANGUAGE env vars to "C".
UseEventLoop = (1 << 2), // Use event loop when executed in UI thread with
diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp
index 245de85134..2b5536809f 100644
--- a/src/plugins/vcsbase/vcsoutputwindow.cpp
+++ b/src/plugins/vcsbase/vcsoutputwindow.cpp
@@ -14,7 +14,7 @@
#include <texteditor/texteditorsettings.h>
#include <utils/filepath.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/theme/theme.h>
#include <QAction>
diff --git a/src/plugins/vcsbase/vcsplugin.cpp b/src/plugins/vcsbase/vcsplugin.cpp
index e5108942f5..0596f406b8 100644
--- a/src/plugins/vcsbase/vcsplugin.cpp
+++ b/src/plugins/vcsbase/vcsplugin.cpp
@@ -23,7 +23,6 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projecttree.h>
-#include <utils/futuresynchronizer.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
@@ -33,15 +32,47 @@ using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
-namespace VcsBase {
-namespace Internal {
+namespace VcsBase::Internal {
class VcsPluginPrivate
{
public:
+ explicit VcsPluginPrivate(VcsPlugin *plugin)
+ : q(plugin)
+ {
+ QObject::connect(&m_settingsPage.settings(), &CommonVcsSettings::settingsChanged,
+ [this] { slotSettingsChanged(); });
+ slotSettingsChanged();
+ }
+
+ QStandardItemModel *nickNameModel()
+ {
+ if (!m_nickNameModel) {
+ m_nickNameModel = NickNameDialog::createModel(q);
+ populateNickNameModel();
+ }
+ return m_nickNameModel;
+ }
+
+ void populateNickNameModel()
+ {
+ QString errorMessage;
+ if (!NickNameDialog::populateModelFromMailCapFile(m_settingsPage.settings().nickNameMailMap.filePath(),
+ m_nickNameModel,
+ &errorMessage)) {
+ qWarning("%s", qPrintable(errorMessage));
+ }
+ }
+
+ void slotSettingsChanged()
+ {
+ if (m_nickNameModel)
+ populateNickNameModel();
+ }
+
+ VcsPlugin *q;
CommonOptionsPage m_settingsPage;
QStandardItemModel *m_nickNameModel = nullptr;
- FutureSynchronizer m_futureSynchronizer;
};
static VcsPlugin *m_instance = nullptr;
@@ -61,7 +92,7 @@ VcsPlugin::~VcsPlugin()
void VcsPlugin::initialize()
{
- d = new VcsPluginPrivate;
+ d = new VcsPluginPrivate(this);
EditorManager::addCloseEditorListener([this](IEditor *editor) -> bool {
bool result = true;
@@ -70,11 +101,8 @@ void VcsPlugin::initialize()
return result;
});
- connect(&d->m_settingsPage, &CommonOptionsPage::settingsChanged,
+ connect(&d->m_settingsPage.settings(), &CommonVcsSettings::settingsChanged,
this, &VcsPlugin::settingsChanged);
- connect(&d->m_settingsPage, &CommonOptionsPage::settingsChanged,
- this, &VcsPlugin::slotSettingsChanged);
- slotSettingsChanged();
JsonWizardFactory::registerPageFactory(new Internal::VcsConfigurationPageFactory);
JsonWizardFactory::registerPageFactory(new Internal::VcsCommandPageFactory);
@@ -123,37 +151,11 @@ CommonVcsSettings &VcsPlugin::settings() const
return d->m_settingsPage.settings();
}
-FutureSynchronizer *VcsPlugin::futureSynchronizer()
-{
- QTC_ASSERT(m_instance, return nullptr);
- return &m_instance->d->m_futureSynchronizer;
-}
-
/* Delayed creation/update of the nick name model. */
QStandardItemModel *VcsPlugin::nickNameModel()
{
- if (!d->m_nickNameModel) {
- d->m_nickNameModel = NickNameDialog::createModel(this);
- populateNickNameModel();
- }
- return d->m_nickNameModel;
-}
-
-void VcsPlugin::populateNickNameModel()
-{
- QString errorMessage;
- if (!NickNameDialog::populateModelFromMailCapFile(settings().nickNameMailMap.filePath(),
- d->m_nickNameModel,
- &errorMessage)) {
- qWarning("%s", qPrintable(errorMessage));
- }
-}
-
-void VcsPlugin::slotSettingsChanged()
-{
- if (d->m_nickNameModel)
- populateNickNameModel();
+ QTC_ASSERT(d, return nullptr);
+ return d->nickNameModel();
}
-} // namespace Internal
-} // namespace VcsBase
+} // VcsBase::Internal
diff --git a/src/plugins/vcsbase/vcsplugin.h b/src/plugins/vcsbase/vcsplugin.h
index 83157fc140..6d4b35c11f 100644
--- a/src/plugins/vcsbase/vcsplugin.h
+++ b/src/plugins/vcsbase/vcsplugin.h
@@ -5,14 +5,10 @@
#include <extensionsystem/iplugin.h>
-#include <QFuture>
-
QT_BEGIN_NAMESPACE
class QStandardItemModel;
QT_END_NAMESPACE
-namespace Utils { class FutureSynchronizer; }
-
namespace VcsBase {
class VcsBaseSubmitEditor;
@@ -36,8 +32,6 @@ public:
CommonVcsSettings &settings() const;
- static Utils::FutureSynchronizer *futureSynchronizer();
-
// Model of user nick names used for the submit
// editor. Stored centrally here to achieve delayed
// initialization and updating on settings change.
@@ -48,9 +42,6 @@ signals:
void submitEditorAboutToClose(VcsBase::VcsBaseSubmitEditor *e, bool *result);
private:
- void slotSettingsChanged();
- void populateNickNameModel();
-
class VcsPluginPrivate *d = nullptr;
};
diff --git a/src/plugins/vcsbase/wizard/vcscommandpage.cpp b/src/plugins/vcsbase/wizard/vcscommandpage.cpp
index 1fef5540f4..0877e3ffbe 100644
--- a/src/plugins/vcsbase/wizard/vcscommandpage.cpp
+++ b/src/plugins/vcsbase/wizard/vcscommandpage.cpp
@@ -212,7 +212,6 @@ bool VcsCommandPageFactory::validateData(Id typeId, const QVariant &data, QStrin
VcsCommandPage::VcsCommandPage()
: m_startedStatus(Tr::tr("Command started..."))
{
- resize(264, 200);
auto verticalLayout = new QVBoxLayout(this);
m_logPlainTextEdit = new QPlainTextEdit;
m_formatter = new OutputFormatter;
diff --git a/src/plugins/webassembly/webassembly.qbs b/src/plugins/webassembly/webassembly.qbs
index 9c6e95f99f..b859ea538a 100644
--- a/src/plugins/webassembly/webassembly.qbs
+++ b/src/plugins/webassembly/webassembly.qbs
@@ -33,9 +33,7 @@ QtcPlugin {
"webassemblytoolchain.h",
]
- Group {
- name: "Unit tests"
- condition: qtc.testsEnabled
+ QtcTestFiles {
files: [
"webassembly_test.cpp",
"webassembly_test.h",
diff --git a/src/plugins/webassembly/webassemblydevice.h b/src/plugins/webassembly/webassemblydevice.h
index 6ff4a2481e..520d0fb535 100644
--- a/src/plugins/webassembly/webassemblydevice.h
+++ b/src/plugins/webassembly/webassemblydevice.h
@@ -4,6 +4,7 @@
#pragma once
#include <projectexplorer/devicesupport/desktopdevice.h>
+#include <projectexplorer/devicesupport/idevicefactory.h>
namespace WebAssembly {
namespace Internal {
diff --git a/src/plugins/webassembly/webassemblyemsdk.cpp b/src/plugins/webassembly/webassemblyemsdk.cpp
index 4acf6589c4..42f8738a8e 100644
--- a/src/plugins/webassembly/webassemblyemsdk.cpp
+++ b/src/plugins/webassembly/webassemblyemsdk.cpp
@@ -7,7 +7,7 @@
#include <coreplugin/icore.h>
#include <utils/environment.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/hostosinfo.h>
#include <QCache>
@@ -29,7 +29,7 @@ static QString emSdkEnvOutput(const FilePath &sdkRoot)
if (!emSdkEnvCache()->contains(cacheKey)) {
const FilePath scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") +
(isWindows ? ".bat" : ".sh"));
- QtcProcess emSdkEnv;
+ Process emSdkEnv;
if (isWindows) {
emSdkEnv.setCommand(CommandLine(scriptFile));
} else {
@@ -88,7 +88,7 @@ QVersionNumber version(const FilePath &sdkRoot)
QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"};
FilePath script = sdkRoot.withNewPath(scriptFile).searchInDirectories(env.path());
const CommandLine command(script, {"-dumpversion"});
- QtcProcess emcc;
+ Process emcc;
emcc.setCommand(command);
emcc.setEnvironment(env);
emcc.runBlocking();
diff --git a/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp b/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp
index c1eb243e49..05b7e89e82 100644
--- a/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp
+++ b/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp
@@ -8,8 +8,8 @@
#include <projectexplorer/target.h>
#include <utils/layoutbuilder.h>
+#include <utils/process.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QTextStream>
@@ -29,7 +29,7 @@ static WebBrowserEntries emrunBrowsers(ProjectExplorer::Target *target)
const Utils::Environment environment = bc->environment();
const Utils::FilePath emrunPath = environment.searchInPath("emrun");
- QtcProcess browserLister;
+ Process browserLister;
browserLister.setEnvironment(environment);
browserLister.setCommand({emrunPath, {"--list_browsers"}});
browserLister.start();
@@ -55,7 +55,7 @@ WebBrowserSelectionAspect::WebBrowserSelectionAspect(ProjectExplorer::Target *ta
addDataExtractor(this, &WebBrowserSelectionAspect::currentBrowser, &Data::currentBrowser);
}
-void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
+void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_CHECK(!m_webBrowserComboBox);
m_webBrowserComboBox = new QComboBox;
@@ -66,7 +66,7 @@ void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutBuilder &builder)
m_currentBrowser = m_webBrowserComboBox->currentData().toString();
emit changed();
});
- builder.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox});
+ parent.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox});
}
void WebBrowserSelectionAspect::fromMap(const QVariantMap &map)
diff --git a/src/plugins/webassembly/webassemblyrunconfigurationaspects.h b/src/plugins/webassembly/webassemblyrunconfigurationaspects.h
index 1d713274ed..8a3cc24bdb 100644
--- a/src/plugins/webassembly/webassemblyrunconfigurationaspects.h
+++ b/src/plugins/webassembly/webassemblyrunconfigurationaspects.h
@@ -20,7 +20,7 @@ class WebBrowserSelectionAspect : public Utils::BaseAspect
public:
WebBrowserSelectionAspect(ProjectExplorer::Target *target);
- void addToLayout(Utils::Layouting::LayoutBuilder &builder) override;
+ void addToLayout(Layouting::LayoutItem &parent) override;
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
diff --git a/src/plugins/webassembly/webassemblytoolchain.cpp b/src/plugins/webassembly/webassemblytoolchain.cpp
index e38b00dac6..4ee3eff591 100644
--- a/src/plugins/webassembly/webassemblytoolchain.cpp
+++ b/src/plugins/webassembly/webassemblytoolchain.cpp
@@ -96,7 +96,7 @@ static Toolchains doAutoDetect(const ToolchainDetector &detector)
if (!WebAssemblyEmSdk::isValid(sdk))
return {};
- if (detector.device) {
+ if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
// Only detect toolchains from the emsdk installation device
const FilePath deviceRoot = detector.device->rootPath();
if (deviceRoot.host() != sdk.host())
diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp
index a18287b0f6..4ee3e7868b 100644
--- a/src/plugins/welcome/introductionwidget.cpp
+++ b/src/plugins/welcome/introductionwidget.cpp
@@ -28,8 +28,9 @@ namespace Internal {
void IntroductionWidget::askUserAboutIntroduction(QWidget *parent, QSettings *settings)
{
+ auto decider = CheckableMessageBox::make_decider(settings, kTakeTourSetting);
// CheckableMessageBox for compatibility with Qt Creator < 4.11
- if (!CheckableMessageBox::shouldAskAgain(settings, kTakeTourSetting)
+ if (!CheckableMessageBox::shouldAskAgain(decider)
|| !Core::ICore::infoBar()->canInfoBeAdded(kTakeTourSetting))
return;
diff --git a/src/share/qtcreator/externaltools/lrelease.xml b/src/share/qtcreator/externaltools/lrelease.xml
index deb9aa0132..05ab83e115 100644
--- a/src/share/qtcreator/externaltools/lrelease.xml
+++ b/src/share/qtcreator/externaltools/lrelease.xml
@@ -11,6 +11,7 @@
<order>2</order>
<executable>
<path>%{CurrentDocument:Project:QT_INSTALL_BINS}/lrelease</path>
+ <path>%{ActiveProject:QT_INSTALL_BINS}/lrelease</path>
<path>lrelease</path>
<arguments>%{CurrentDocument:Project:FilePath}</arguments>
<workingdirectory>%{CurrentDocument:Project:Path}</workingdirectory>
diff --git a/src/share/qtcreator/externaltools/lupdate.xml b/src/share/qtcreator/externaltools/lupdate.xml
index 2e885ef3ed..f0edc35a83 100644
--- a/src/share/qtcreator/externaltools/lupdate.xml
+++ b/src/share/qtcreator/externaltools/lupdate.xml
@@ -11,6 +11,7 @@
<order>1</order>
<executable>
<path>%{CurrentDocument:Project:QT_INSTALL_BINS}/lupdate</path>
+ <path>%{ActiveProject:QT_INSTALL_BINS}/lupdate</path>
<path>lupdate</path>
<arguments>%{CurrentDocument:Project:FilePath}</arguments>
<workingdirectory>%{CurrentDocument:Project:Path}</workingdirectory>
diff --git a/src/share/qtcreator/externaltools/qml.xml b/src/share/qtcreator/externaltools/qml.xml
index ec2cc5a9a7..ee7d5479a7 100644
--- a/src/share/qtcreator/externaltools/qml.xml
+++ b/src/share/qtcreator/externaltools/qml.xml
@@ -11,6 +11,7 @@
<order>1</order>
<executable>
<path>%{CurrentDocument:Project:QT_INSTALL_BINS}/qml</path>
+ <path>%{ActiveProject:QT_INSTALL_BINS}/qml</path>
<path>qml</path>
<arguments>%{CurrentDocument:FilePath}</arguments>
<workingdirectory>%{CurrentDocument:Path}</workingdirectory>
diff --git a/src/share/qtcreator/externaltools/qmlscene.xml b/src/share/qtcreator/externaltools/qmlscene.xml
index ff8751a7ed..09b780e521 100644
--- a/src/share/qtcreator/externaltools/qmlscene.xml
+++ b/src/share/qtcreator/externaltools/qmlscene.xml
@@ -11,6 +11,7 @@
<order>1</order>
<executable>
<path>%{CurrentDocument:Project:QT_INSTALL_BINS}/qmlscene</path>
+ <path>%{ActiveProject:QT_INSTALL_BINS}/qmlscene</path>
<path>qmlscene</path>
<arguments>%{CurrentDocument:FilePath}</arguments>
<workingdirectory>%{CurrentDocument:Path}</workingdirectory>
diff --git a/src/shared/help/bookmarkmanager.cpp b/src/shared/help/bookmarkmanager.cpp
index 6a3ed6e14e..72bd5db321 100644
--- a/src/shared/help/bookmarkmanager.cpp
+++ b/src/shared/help/bookmarkmanager.cpp
@@ -75,7 +75,7 @@ BookmarkDialog::BookmarkDialog(BookmarkManager *manager, const QString &title,
m_newFolderButton = new QPushButton(::Help::Tr::tr("New Folder"));
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Form {
::Help::Tr::tr("Bookmark:"), m_bookmarkEdit, br,
diff --git a/src/shared/help/topicchooser.cpp b/src/shared/help/topicchooser.cpp
index efa3705b14..93a4bf47be 100644
--- a/src/shared/help/topicchooser.cpp
+++ b/src/shared/help/topicchooser.cpp
@@ -52,7 +52,7 @@ TopicChooser::TopicChooser(QWidget *parent, const QString &keyword,
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
::Help::Tr::tr("Choose a topic for <b>%1</b>:").arg(keyword),
m_lineEdit,
diff --git a/src/shared/qtsingleapplication/qtlocalpeer.h b/src/shared/qtsingleapplication/qtlocalpeer.h
index bd7f6e9dce..67a0d42e2c 100644
--- a/src/shared/qtsingleapplication/qtlocalpeer.h
+++ b/src/shared/qtsingleapplication/qtlocalpeer.h
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
#include <qtlockedfile.h>
#include <QLocalServer>
diff --git a/src/shared/qtsingleapplication/qtsingleapplication.cpp b/src/shared/qtsingleapplication/qtsingleapplication.cpp
index 4a79f4eee6..0f8fa8b6d1 100644
--- a/src/shared/qtsingleapplication/qtsingleapplication.cpp
+++ b/src/shared/qtsingleapplication/qtsingleapplication.cpp
@@ -148,13 +148,11 @@ void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessag
disconnect(pidPeer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow);
}
-
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
-
void QtSingleApplication::activateWindow()
{
if (actWin) {
@@ -164,4 +162,70 @@ void QtSingleApplication::activateWindow()
}
}
+static const char s_freezeDetector[] = "QTC_FREEZE_DETECTOR";
+
+static std::optional<int> isUsingFreezeDetector()
+{
+ if (!qEnvironmentVariableIsSet(s_freezeDetector))
+ return {};
+
+ bool ok = false;
+ const int threshold = qEnvironmentVariableIntValue(s_freezeDetector, &ok);
+ return ok ? threshold : 100; // default value 100ms
+}
+
+class ApplicationWithFreezerDetector : public SharedTools::QtSingleApplication
+{
+public:
+ ApplicationWithFreezerDetector(const QString &id, int &argc, char **argv)
+ : QtSingleApplication(id, argc, argv)
+ , m_align(21, QChar::Space)
+ {}
+ void setFreezeTreshold(std::chrono::milliseconds freezeAbove) { m_threshold = freezeAbove; }
+
+ bool notify(QObject *receiver, QEvent *event) override {
+ using namespace std::chrono;
+ const auto start = system_clock::now();
+ const QPointer<QObject> p(receiver);
+ const QString className = QLatin1String(receiver->metaObject()->className());
+ const QString name = receiver->objectName();
+
+ const bool ret = QtSingleApplication::notify(receiver, event);
+
+ const auto end = system_clock::now();
+ const auto freeze = duration_cast<milliseconds>(end - start);
+ if (freeze > m_threshold) {
+ const QString time = QTime::currentTime().toString(Qt::ISODateWithMs);
+ qDebug().noquote() << QString("FREEZE [%1]").arg(time)
+ << "of" << freeze.count() << "ms, on:" << event;
+ const QString receiverMessage = name.isEmpty()
+ ? QString("receiver class: %1").arg(className)
+ : QString("receiver class: %1, object name: %2").arg(className, name);
+ qDebug().noquote() << m_align << receiverMessage;
+ if (!p)
+ qDebug().noquote() << m_align << "THE RECEIVER GOT DELETED inside the event filter!";
+ }
+ return ret;
+ }
+
+private:
+ const QString m_align;
+ std::chrono::milliseconds m_threshold = std::chrono::milliseconds(100);
+};
+
+QtSingleApplication *createApplication(const QString &id, int &argc, char **argv)
+{
+ const std::optional<int> freezeDetector = isUsingFreezeDetector();
+ if (!freezeDetector)
+ return new SharedTools::QtSingleApplication(id, argc, argv);
+
+ qDebug() << s_freezeDetector << "evn var is set. The freezes of main thread, above"
+ << *freezeDetector << "ms, will be reported.";
+ qDebug() << "Change the freeze detection threshold by setting the" << s_freezeDetector
+ << "env var to a different numeric value (in ms).";
+ ApplicationWithFreezerDetector *app = new ApplicationWithFreezerDetector(id, argc, argv);
+ app->setFreezeTreshold(std::chrono::milliseconds(*freezeDetector));
+ return app;
+}
+
} // namespace SharedTools
diff --git a/src/shared/qtsingleapplication/qtsingleapplication.h b/src/shared/qtsingleapplication/qtsingleapplication.h
index 3d8e140bb4..fdc485bd98 100644
--- a/src/shared/qtsingleapplication/qtsingleapplication.h
+++ b/src/shared/qtsingleapplication/qtsingleapplication.h
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
#include <QApplication>
QT_FORWARD_DECLARE_CLASS(QSharedMemory)
@@ -44,4 +46,7 @@ private:
bool block;
};
+// Instantiates Freeze Detector when QTC_FREEZE_DETECTOR env var is set.
+QtSingleApplication *createApplication(const QString &id, int &argc, char **argv);
+
} // namespace SharedTools
diff --git a/src/shared/registryaccess/CMakeLists.txt b/src/shared/registryaccess/CMakeLists.txt
index 135df2c501..634c8c19e6 100644
--- a/src/shared/registryaccess/CMakeLists.txt
+++ b/src/shared/registryaccess/CMakeLists.txt
@@ -3,10 +3,10 @@ if (WIN32)
target_link_libraries(registryaccess PUBLIC advapi32 ole32 shell32 Qt::Widgets)
target_compile_definitions(registryaccess PRIVATE _UNICODE UNICODE)
target_include_directories(registryaccess PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+ if (WITH_SANITIZE)
+ qtc_enable_sanitize(registryaccess ${SANITIZE_FLAGS})
+ endif()
else()
add_library(registryaccess INTERFACE)
endif()
-if (WITH_SANITIZE)
- qtc_enable_sanitize(registryaccess ${SANITIZE_FLAGS})
-endif()
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 1bee7cccfb..2204ac3c5f 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -1,10 +1,9 @@
add_subdirectory(3rdparty)
add_subdirectory(buildoutputparser)
-option(BUILD_CPLUSPLUS_TOOLS "Build CPlusPlus tools" OFF)
-
function(add_qtc_cpp_tool name)
add_qtc_executable(${name}
+ SKIP_INSTALL
DEFINES
PATH_PREPROCESSOR_CONFIG=\"${CMAKE_CURRENT_SOURCE_DIR}/cplusplus-shared/pp-configuration.inc\"
${ARGN}
@@ -17,12 +16,10 @@ function(add_qtc_cpp_tool name)
)
endfunction()
-if (BUILD_CPLUSPLUS_TOOLS)
- add_qtc_cpp_tool(cplusplus-ast2png "")
- add_qtc_cpp_tool(cplusplus-frontend "")
- add_qtc_cpp_tool(cplusplus-mkvisitor PATH_AST_H=\"${CMAKE_CURRENT_SOURCE_DIR}/../libs/3rdparty/cplusplus/AST.h\")
- add_qtc_cpp_tool(cplusplus-update-frontend PATH_CPP_FRONTEND=\"${CMAKE_CURRENT_SOURCE_DIR}/../libs/3rdparty/cplusplus\" PATH_DUMPERS_FILE=\"${CMAKE_CURRENT_SOURCE_DIR}/cplusplus-ast2png/dumpers.inc\")
-endif()
+add_qtc_cpp_tool(cplusplus-ast2png "")
+add_qtc_cpp_tool(cplusplus-frontend "")
+add_qtc_cpp_tool(cplusplus-mkvisitor PATH_AST_H=\"${CMAKE_CURRENT_SOURCE_DIR}/../libs/3rdparty/cplusplus/AST.h\")
+add_qtc_cpp_tool(cplusplus-update-frontend PATH_CPP_FRONTEND=\"${CMAKE_CURRENT_SOURCE_DIR}/../libs/3rdparty/cplusplus\" PATH_DUMPERS_FILE=\"${CMAKE_CURRENT_SOURCE_DIR}/cplusplus-ast2png/dumpers.inc\")
if (APPLE)
add_subdirectory(disclaim)
@@ -44,3 +41,5 @@ add_subdirectory(wininterrupt) ## windows only
if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/perfparser/CMakeLists.txt)
add_subdirectory(perfparser)
endif()
+
+add_subdirectory(process_stub)
diff --git a/src/tools/cplusplus-shared/CPlusPlusTool.qbs b/src/tools/cplusplus-shared/CPlusPlusTool.qbs
index 55c4aff940..63313e2462 100644
--- a/src/tools/cplusplus-shared/CPlusPlusTool.qbs
+++ b/src/tools/cplusplus-shared/CPlusPlusTool.qbs
@@ -1,6 +1,7 @@
import qbs 1.0
QtcTool {
+ install: false
Depends { name: "Qt"; submodules: ["core", "widgets"]; }
Depends { name: "CPlusPlus" }
Depends { name: "Utils" }
diff --git a/src/tools/cplusplustools.qbs b/src/tools/cplusplustools.qbs
index d67d558330..ab3aa51707 100644
--- a/src/tools/cplusplustools.qbs
+++ b/src/tools/cplusplustools.qbs
@@ -3,7 +3,6 @@ import qbs.Environment
Project {
name: "CPlusPlus Tools"
- condition: Environment.getEnv("BUILD_CPLUSPLUS_TOOLS")
references: [
"3rdparty/cplusplus-keywordgen/cplusplus-keywordgen.qbs",
"cplusplus-ast2png/cplusplus-ast2png.qbs",
diff --git a/src/tools/icons/GenericDocumentIcon.iconset/readme.txt b/src/tools/icons/GenericDocumentIcon.iconset/readme.txt
deleted file mode 100644
index a00748fafd..0000000000
--- a/src/tools/icons/GenericDocumentIcon.iconset/readme.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This directory needs to contain the files
-
- icon_128x128.png
- icon_128x128@2x.png
- icon_16x16.png
- icon_16x16@2x.png
- icon_256x256.png
- icon_256x256@2x.png
- icon_32x32.png
- icon_32x32@2x.png
- icon_512x512.png
- icon_512x512@2x.png
-
-On OSX, they are obtainable by executing the following command:
-iconutil -c iconset /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericDocumentIcon.icns
diff --git a/src/tools/icons/applicationicons.svg b/src/tools/icons/applicationicons.svg
deleted file mode 100644
index 018fb75ea5..0000000000
--- a/src/tools/icons/applicationicons.svg
+++ /dev/null
@@ -1,2206 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="2120"
- height="8985"
- id="svg3397"
- version="1.1"
- inkscape:version="0.91 r13725"
- sodipodi:docname="applicationicons.svg"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <defs
- id="defs3399">
- <linearGradient
- id="linearGradient6827">
- <stop
- style="stop-color:#dadada;stop-opacity:1;"
- offset="0"
- id="stop6829" />
- <stop
- style="stop-color:#f9f9f9;stop-opacity:1;"
- offset="1"
- id="stop6831" />
- </linearGradient>
- <linearGradient
- id="linearGradient6819">
- <stop
- style="stop-color:#d5d5d5;stop-opacity:1;"
- offset="0"
- id="stop6821" />
- <stop
- style="stop-color:#f5f5f5;stop-opacity:1;"
- offset="1"
- id="stop6823" />
- </linearGradient>
- <linearGradient
- id="linearGradient6811">
- <stop
- style="stop-color:#e0e0e0;stop-opacity:1;"
- offset="0"
- id="stop6813" />
- <stop
- style="stop-color:#f6f6f6;stop-opacity:1;"
- offset="1"
- id="stop6815" />
- </linearGradient>
- <linearGradient
- id="linearGradient6801">
- <stop
- style="stop-color:#c1c1c1;stop-opacity:1;"
- offset="0"
- id="stop6803" />
- <stop
- style="stop-color:#f9f9f9;stop-opacity:1;"
- offset="1"
- id="stop6805" />
- </linearGradient>
- <linearGradient
- id="linearGradient4153-5-4">
- <stop
- style="stop-color:#d5d5d5;stop-opacity:1;"
- offset="0"
- id="stop4155-5-8" />
- <stop
- style="stop-color:#f1f1f1;stop-opacity:1;"
- offset="1"
- id="stop4157-1-8" />
- </linearGradient>
- <linearGradient
- id="linearGradient4145-1-4">
- <stop
- style="stop-color:#eeeeee;stop-opacity:1;"
- offset="0"
- id="stop4147-1-5" />
- <stop
- style="stop-color:#bfbfbf;stop-opacity:1;"
- offset="1"
- id="stop4149-5-5" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient6827"
- id="linearGradient5129"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(0,-7905)"
- x1="20"
- y1="8643"
- x2="165"
- y2="8542" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient6819"
- id="linearGradient5587"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(0,-7905)"
- x1="71"
- y1="8648"
- x2="168"
- y2="8539" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient6811"
- id="linearGradient6035"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(0,-7905)"
- x1="115"
- y1="8655"
- x2="167"
- y2="8543" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient6801"
- id="linearGradient3451"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(0,-7905)"
- x1="169"
- y1="8684"
- x2="164"
- y2="8547" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4153-5-4"
- id="linearGradient4541"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.0625,0,0,0.0625,-13.666671,67.80674)"
- x1="600"
- y1="8473.999"
- x2="676"
- y2="8427.999" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient4145-1-4"
- id="linearGradient4543"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.0625,0,0,0.0625,-13.666671,67.80674)"
- x1="675"
- y1="8412.999"
- x2="668"
- y2="8513.999" />
- <filter
- id="filter5469"
- inkscape:label="bubble_shadow"
- inkscape:menu="Shadows and Glows"
- inkscape:menu-tooltip="Adds a colorizable drop shadow inside"
- color-interpolation-filters="sRGB">
- <feGaussianBlur
- id="feGaussianBlur5471"
- stdDeviation="8"
- result="result8" />
- <feOffset
- id="feOffset5473"
- dx="0"
- dy="0"
- result="result11" />
- <feComposite
- id="feComposite5475"
- in2="result11"
- result="result6"
- in="SourceGraphic"
- operator="in" />
- <feFlood
- id="feFlood5477"
- result="result10"
- in="result6"
- flood-opacity="1"
- flood-color="rgb(12,75,102)" />
- <feBlend
- id="feBlend5479"
- in2="result10"
- mode="normal"
- in="result6"
- result="result12" />
- <feComposite
- id="feComposite5481"
- in2="SourceGraphic"
- result="result2"
- operator="in" />
- </filter>
- <filter
- id="filter4131"
- inkscape:label="Colorize"
- inkscape:menu="Color"
- inkscape:menu-tooltip="Blend image or object with a flood color and set lightness and contrast"
- x="0"
- y="0"
- width="1"
- height="1"
- color-interpolation-filters="sRGB">
- <feColorMatrix
- id="feColorMatrix4133"
- values="1"
- in="SourceGraphic"
- type="saturate"
- result="result2" />
- <feFlood
- id="feFlood4135"
- flood-color="rgb(62,106,190)"
- flood-opacity="1"
- result="result1" />
- <feBlend
- id="feBlend4137"
- in2="result2"
- mode="multiply"
- in="result1"
- result="result3" />
- <feComposite
- id="feComposite4139"
- in2="SourceGraphic"
- operator="in"
- result="result4" />
- </filter>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath4086">
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use4088" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath4101">
- <rect
- id="use4103"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath4156">
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use4158" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath4448">
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use4450" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath5822">
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="path5824"
- d="m 26,-2221.8674 c 1,2 1,5 1,8 3,0 6,0 8,1 l 0,20 -22,0 0,-29 z"
- style="fill:none;stroke:none" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6024">
- <rect
- style="fill:none;stroke:none"
- id="rect6026"
- width="87"
- height="100"
- x="225"
- y="-5429" />
- </clipPath>
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath5186">
- <rect
- id="rect5188"
- width="32"
- height="32"
- x="56"
- y="-152"
- style="fill:none" />
- </clipPath>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.1767767"
- inkscape:cx="2704.5851"
- inkscape:cy="6460.1659"
- inkscape:document-units="px"
- inkscape:current-layer="layer3"
- showgrid="true"
- inkscape:window-width="2880"
- inkscape:window-height="1503"
- inkscape:window-x="-13"
- inkscape:window-y="527"
- inkscape:window-maximized="1"
- inkscape:snap-grids="true"
- showguides="true"
- inkscape:guide-bbox="true"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0">
- <inkscape:grid
- type="xygrid"
- id="grid3405"
- empspacing="1"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="false"
- dotted="true"
- originx="0px"
- originy="1.7e-005px" />
- <inkscape:grid
- type="xygrid"
- id="grid3407"
- empspacing="1"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="false"
- spacingx="4px"
- spacingy="4px"
- originx="0px"
- originy="1.7e-005px" />
- </sodipodi:namedview>
- <metadata
- id="metadata3402">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="qt-logos"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(0,7932.6378)">
- <g
- transform="translate(0,452.36218)"
- style="display:inline"
- id="qt-logo-original">
- <rect
- id="rect3865"
- width="16"
- height="16"
- x="0"
- y="584"
- style="fill:none" />
- <path
- transform="matrix(0.05453306,0,0,0.05453306,23.432856,559.98862)"
- style="fill:#41cd52"
- d="m -429.7,695.2 252.7,0 40.7,-40.7 0,-175.4 -252.7,0 -40.7,40.7 z"
- id="logoBackground"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc" />
- <path
- style="fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 5.9550103,595.3424 c -1.0524881,0 -1.7886844,-0.28357 -2.2031357,-0.85071 -0.4144512,-0.56715 -0.6216768,-1.46694 -0.6216768,-2.68848 0,-1.227 0.2126789,-2.1377 0.6325835,-2.73211 0.4199045,-0.59441 1.1561008,-0.89434 2.192229,-0.89434 1.0415814,0 1.7723244,0.29448 2.1867757,0.88889 0.4199045,0.58895 0.6271302,1.49966 0.6271302,2.7321 0,0.81255 -0.087253,1.46694 -0.2563054,1.96319 -0.1745058,0.50171 -0.4526244,0.88344 -0.8452624,1.1452 l 0.8507157,1.36878 -1.0415815,0.48534 -0.8997954,-1.47784 c -0.1308794,0.0436 -0.338105,0.06 -0.6216769,0.06 z m -1.3142468,-1.576 c 0.2290389,0.39264 0.6707567,0.59441 1.3197001,0.59441 0.6489434,0 1.0852079,-0.19632 1.3142467,-0.58351 0.2235856,-0.38718 0.338105,-1.04703 0.338105,-1.96864 0,-0.92706 -0.1145194,-1.59782 -0.3490116,-2.01772 -0.2344921,-0.41991 -0.6653033,-0.62713 -1.3033401,-0.62713 -0.6325835,0 -1.068848,0.20722 -1.3087935,0.62713 -0.2344921,0.4199 -0.3544649,1.08521 -0.3544649,2.00681 0,0.91616 0.1145195,1.57601 0.3435583,1.96865 z"
- id="path4480" />
- <path
- style="fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 11.277437,590.97976 0,2.2304 c 0,0.41445 0.03272,0.68712 0.09271,0.82345 0.05999,0.13633 0.218132,0.20177 0.463531,0.20177 l 0.828903,-0.0327 0.04908,0.88344 c -0.452624,0.0873 -0.796182,0.13088 -1.036128,0.13088 -0.572597,0 -0.965235,-0.13088 -1.177914,-0.39264 -0.212679,-0.26176 -0.321745,-0.75801 -0.321745,-1.48875 l 0,-2.35038 -0.768916,0 0,-0.94342 0.774369,0 0,-1.46694 1.096115,0 0,1.46694 1.396046,0 -3e-6,0.93796 z"
- id="path4482"
- sodipodi:nodetypes="csscccssscccccccccc" />
- </g>
- <g
- transform="translate(16,452.36218)"
- style="display:inline"
- id="qt-logo-16"
- inkscape:label="#qtlogo-1"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <rect
- id="rect3865-9"
- width="16"
- height="16"
- x="0"
- y="584"
- style="fill:none" />
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="use7459"
- d="m 0,598 14,0 2,-2 0,-10 -14,0 -2,2 z"
- style="fill:#41cd52" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 5.9563948,595.31326 c -1.1384025,0 -1.9346941,-0.28944 -2.3829768,-0.86832 -0.4482826,-0.57877 -0.6724245,-1.4971 -0.6724245,-2.74376 0,-1.25226 0.2300399,-2.18172 0.6842207,-2.78836 C 4.039396,588.30608 4.8356878,588 5.9563948,588 c 1.1266064,0 1.9169999,0.30057 2.3652825,0.90721 C 8.7758593,589.50834 9,590.43779 9,591.69557 c 0,0.82933 -0.094372,1.49711 -0.2772272,2.00363 -0.1887509,0.51203 -0.4895716,0.90159 -0.9142607,1.16878 l 1.2737123,1.86103 -1.1266053,0.49529 -1.3267997,-1.97227 c -0.1415627,0.0449 -0.3657045,0.0612 -0.6724246,0.0612 z M 4.534867,593.70481 c 0.2477354,0.40068 0.7255098,0.60664 1.427427,0.60664 0.7019163,0 1.1737936,-0.20044 1.4215291,-0.59552 0.2418362,-0.39517 0.3657034,-1.06866 0.3657034,-2.00924 0,-0.94609 -0.1238672,-1.6307 -0.3775008,-2.05925 -0.2536335,-0.42854 -0.7196117,-0.64001 -1.4097317,-0.64001 -0.6842217,0 -1.156098,0.21147 -1.4156307,0.64001 -0.2536336,0.42855 -0.3834,1.10755 -0.3834,2.04813 0,0.93507 0.1238683,1.60845 0.3716037,2.00924 z"
- id="path4480-8"
- sodipodi:nodetypes="ccscscsccccccccscssscsc" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 13.858491,590.97976 -1.749599,0 0,2.2304 c 0,0.4145 0.03272,0.6871 0.09271,0.8235 0.05999,0.1363 0.218132,0.2017 0.463531,0.2017 l 0.828903,-0.033 0.04908,0.8835 c -0.452624,0.087 -0.796182,0.1309 -1.036128,0.1309 -0.572597,0 -0.965235,-0.1309 -1.177914,-0.3927 -0.212679,-0.2617 -0.321745,-0.758 -0.321745,-1.4887 l 0,-2.3504 -0.768916,0 0,-0.9434 0.774369,0 0,-1.99733 1.096115,0 0,1.99733 1.749599,0 0,0.9379 z"
- id="path4482-8"
- sodipodi:nodetypes="ccsccccscscccccccccc" />
- </g>
- <g
- id="qt-logo-24"
- style="display:inline"
- transform="matrix(1.5,0,0,1.5,32,152.36218)"
- inkscape:label="#g3454"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <rect
- style="fill:none"
- y="584"
- x="0"
- height="16"
- width="16"
- id="rect3462" />
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="use7459-1"
- d="m 0,598 14,0 2,-2 0,-10 -14,0 -2,2 z"
- style="fill:#41cd52" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 6.0783695,595.23512 c -1.048244,0 -1.7814705,-0.28624 -2.1942507,-0.85889 -0.4127801,-0.57266 -0.6191694,-1.48123 -0.6191694,-2.71468 0,-1.23888 0.2118213,-2.15836 0.6300319,-2.75858 C 4.313192,588.30291 5.0464201,588 6.0783695,588 c 1.0373817,0 1.7651777,0.29731 2.1779563,0.89752 0.4182121,0.59461 0.6246015,1.51424 0.6246015,2.75859 0,0.82042 -0.086903,1.48122 -0.2552714,1.98223 -0.1738017,0.50661 -0.4507997,0.89207 -0.8418532,1.15635 l 0.8472852,1.38202 -1.0373816,0.48996 -0.8961673,-1.49213 c -0.1303471,0.0439 -0.3367409,0.0605 -0.6191695,0.0605 z m -1.3089474,-1.59133 c 0.2281158,0.39651 0.668053,0.60022 1.3143795,0.60022 0.646325,0 1.0808302,-0.19826 1.308946,-0.58917 0.2226837,-0.39089 0.3367409,-1.05714 0.3367409,-1.98767 0,-0.93615 -0.1140631,-1.61329 -0.3476034,-2.03737 -0.2335464,-0.42392 -0.6626211,-0.63323 -1.2980835,-0.63323 -0.6300335,0 -1.0645387,0.20931 -1.3035154,0.63323 -0.2335479,0.42408 -0.3530356,1.09578 -0.3530356,2.02631 0,0.92508 0.1140631,1.59133 0.3421715,1.98768 z"
- id="path4480-9" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 12.666628,590.90957 -1.287296,0 0,2.22136 c 0,0.41278 0.03258,0.68438 0.09234,0.82018 0.05974,0.13596 0.217252,0.20094 0.461661,0.20094 l 0.825562,-0.0328 0.04888,0.87993 c -0.4508,0.0866 -0.792971,0.12997 -1.03195,0.12997 -0.570288,0 -0.961342,-0.12997 -1.173163,-0.39111 -0.211822,-0.2607 -0.320447,-0.75489 -0.320447,-1.48275 l 0,-2.34088 -0.7658166,0 0,-0.93954 0.7712456,0 0,-1.46108 1.091696,0 0,1.46108 1.287296,0 0,0.93417 z"
- id="path4482-7"
- sodipodi:nodetypes="ccsccccscscccccccccc" />
- </g>
- <g
- id="qt-logo-32"
- style="display:inline"
- transform="matrix(2,0,0,2,56,-147.63782)"
- inkscape:label="#g3464"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <rect
- style="fill:none"
- y="584"
- x="0"
- height="16"
- width="16"
- id="rect3472" />
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="use7459-1-7"
- d="m 0,598 14,0 2,-2 0,-10 -14,0 -2,2 z"
- style="fill:#41cd52" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 5.5058025,595.49953 c -1.1199227,0 -1.903288,-0.30009 -2.3442946,-0.90007 C 2.7205033,593.99928 2.5,593.04715 2.5,591.7545 c 0,-1.29836 0.2263046,-2.26192 0.6731147,-2.89088 0.446808,-0.62897 1.2301733,-0.94642 2.3326878,-0.94642 1.1083178,0 1.8858797,0.31152 2.3268863,0.9407 C 8.2794968,589.48095 8.5,590.44471 8.5,591.74879 c 0,0.85986 -0.092851,1.55232 -0.2727256,2.07738 -0.1856808,0.53097 -0.4816267,0.93477 -0.8994217,1.2118 l 0.905223,1.4484 L 7.1247577,597 6.1673123,595.43604 c -0.1392656,0.0465 -0.3597689,0.0635 -0.6615098,0.0635 z m -1.398452,-1.66766 c 0.2437129,0.41544 0.7137324,0.62898 1.4042554,0.62898 0.6905229,0 1.1547392,-0.2074 1.3984519,-0.61733 0.2379115,-0.40972 0.359769,-1.10789 0.359769,-2.08309 0,-0.98113 -0.1218575,-1.69094 -0.3713737,-2.13515 -0.2495163,-0.44442 -0.7079313,-0.66368 -1.3868472,-0.66368 -0.6731148,0 -1.1373309,0.21926 -1.3926507,0.66368 -0.2495163,0.44421 -0.3771751,1.14831 -0.3771751,2.12351 0,0.96948 0.1218575,1.66766 0.3655704,2.08308 z"
- id="path4480-88" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 11.49998,590.99431 0,2.39279 c 0,0.45035 -0.02304,0.7468 0.04216,0.89481 0.06518,0.1478 0.237049,0.21931 0.503729,0.21931 L 12.94666,594.46648 13,595.42649 c -0.491876,0.0957 -0.865229,0.14127 -1.125983,0.14127 -0.622255,0 -1.048944,-0.14127 -1.280066,-0.42664 -0.231124,-0.28451 -0.349648,-0.82374 -0.349648,-1.61792 l 0,-2.5232 -0.8355983,0 0,-1 0.8415253,0 0,-2 1.24977,0 0,2 1.5,0 -2e-6,0.99412 z"
- id="path4482-0"
- sodipodi:nodetypes="csscccssscccccccccc" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-24"
- id="qt-logo-48"
- transform="matrix(2,0,0,2,24,-1052.3622)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-128"
- id="qt-logo-64"
- transform="matrix(0.5,0,0,0.5,-54,526.18085)"
- width="1920"
- height="1080"
- inkscape:label="#use3476"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-original"
- id="qt-logo-80"
- transform="matrix(5,0,0,5,200,-4209.4487)"
- width="1920"
- height="1080"
- inkscape:label="#use3478"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- id="qt-logo-100"
- style="display:inline"
- transform="matrix(6.25,0,0,6.25,280.5,-2697.6378)"
- inkscape:label="#use3480"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- transform="matrix(1,0,0,1.0000012,-0.08,-7.6129842e-4)"
- style="display:inline"
- x="0"
- y="0"
- xlink:href="#logoBackground"
- id="use7459-1-0"
- width="1"
- height="1" />
- <path
- inkscape:connector-curvature="0"
- id="path3497"
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
- d="m 7.0072488,594.79983 c -0.015766,10e-4 -0.031029,0.002 -0.046394,0.002 -0.347749,0 -0.6126026,-0.24831 -0.7894394,-0.74949 -0.1875816,-0.52617 -0.2807197,-1.42237 -0.2807197,-2.68548 0,-1.30713 0.09585,-2.17056 0.2850879,-2.58716 0.1748787,-0.38227 0.425774,-0.5726 0.7520335,-0.5726 0.025807,0 0.052469,10e-4 0.079431,0.004 0.3521674,0.0309 0.6148619,0.27546 0.7904435,0.72805 0.1755314,0.45044 0.2618912,1.27125 0.2618912,2.46489 0,1.32962 -0.087213,2.22095 -0.2639498,2.6753 -0.1779414,0.45871 -0.4401841,0.69934 -0.7883849,0.72132 z m 0,-7.7315 c -0.1301423,-0.0152 -0.2565691,-0.0228 -0.3787281,-0.0228 -0.7635315,0 -1.3702595,0.298 -1.8052219,0.90418 -0.5152971,0.71826 -0.7773389,1.85092 -0.7773389,3.38573 0,1.42323 0.1914477,2.51162 0.5675147,3.25548 0.3705942,0.73232 0.9058243,1.1686 1.5922846,1.31491 0.1494226,0.72409 0.3825439,1.21362 0.6985607,1.46735 0.2387448,0.19213 0.5562176,0.28735 0.9485021,0.28735 0.1184937,0 0.2444185,-0.009 0.3764686,-0.0258 l 0.4110189,-0.0587 0,-0.93845 c -0.1355377,0.002 -0.2022967,0.009 -0.3199896,-0.002 -0.1176929,-0.0108 -0.232569,-0.0586 -0.3058243,-0.12423 -0.1309455,-0.11861 -0.2296066,-0.39037 -0.2946778,-0.72461 0.6456402,-0.22085 1.1174561,-0.69793 1.4266445,-1.42709 0.3038158,-0.71897 0.4542929,-1.69282 0.4542929,-2.93113 0,-1.3518 -0.2043012,-2.38107 -0.620385,-3.09928 -0.4237657,-0.73407 -1.0749289,-1.15775 -1.9731214,-1.2612"
- sodipodi:nodetypes="cscscsccscccscscccsccczcccsccc" />
- <path
- inkscape:connector-curvature="0"
- id="path3499"
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
- d="m 11.991386,594.35405 c -0.125222,0 -0.204452,-0.0395 -0.264803,-0.11961 -0.06708,-0.0875 -0.04658,-0.28229 -0.04658,-0.58505 l 0,-3.48029 0.799997,0.0647 0,-0.95217 -0.799997,-0.089 0,-1.51794 -0.95995,-0.1532 -0.317684,1.58527 -0.642369,-0.0584 0,1.02043 0.480003,0.0387 0,3.8578 c 0,0.50494 0.122299,0.87301 0.327253,1.10214 0.175833,0.19776 0.446059,0.29543 0.782762,0.29543 0.05031,0 0.103129,-0.002 0.157054,-0.007 0.371799,-0.0316 0.735615,-0.13236 1.137289,-0.30176 l 0,-0.87502 c -0.200837,0.1033 -0.429791,0.16217 -0.62144,0.17332 -0.01255,8e-4 -0.01953,10e-4 -0.03153,10e-4"
- sodipodi:nodetypes="ccsccccccccccscsccccc" />
- <rect
- style="fill:none"
- y="584"
- x="0"
- height="16"
- width="16"
- id="rect3501" />
- </g>
- <g
- id="qt-logo-128"
- style="display:inline"
- transform="matrix(8,0,0,8,380,-3747.6379)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <rect
- style="fill:none"
- y="584"
- x="0"
- height="16"
- width="16"
- id="rect4817" />
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="use7459-1-0-9"
- d="m 0,598.00001 13.75,0 2.25,-2.25 0,-9.75 -13.75,0 -2.25,2.25 z"
- style="fill:#41cd52" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 5.9550223,595.34259 c -1.0524887,0 -1.7886812,-0.2832 -2.2031375,-0.85072 -0.4144801,-0.56721 -0.6216803,-1.46688 -0.6216803,-2.68848 0,-1.22698 0.2126402,-2.13768 0.6325603,-2.73209 0.4199203,-0.5944 1.1560969,-0.89432 2.1922255,-0.89432 1.0415847,0 1.7723291,0.2944 2.1867774,0.88889 0.4199203,0.5888 0.6271205,1.49966 0.6271205,2.73207 0,0.81256 -0.08728,1.46697 -0.2563202,1.96321 -0.1744808,0.5016 -0.4526403,0.88344 -0.8452566,1.1452 l 0.8507127,1.3688 -1.0415848,0.4856 -0.8997926,-1.47784 c -0.1308803,0.04 -0.3380801,0.064 -0.6216804,0.064 z m -1.3142488,-1.57601 c 0.2290401,0.3928 0.6707205,0.5944 1.3197049,0.5944 0.6489603,0 1.0852087,-0.19599 1.3142488,-0.58319 0.2236002,-0.3872 0.3380803,-1.04697 0.3380803,-1.96864 0,-0.92705 -0.1145602,-1.59777 -0.3490404,-2.01769 -0.2344802,-0.42 -0.6652803,-0.6272 -1.3033368,-0.6272 -0.6325603,0 -1.0688486,0.2072 -1.3087929,0.6272 -0.2344802,0.42001 -0.3544802,1.08521 -0.3544802,2.00681 0,0.91615 0.1144797,1.57599 0.3435203,1.96864 z"
- id="path4480-1" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 11.277458,590.97994 0,2.23041 c 0,0.41439 0.0328,0.6872 0.09272,0.82344 0.06,0.13605 0.21816,0.2016 0.46352,0.2016 l 0.828904,-0.032 0.0488,0.88344 c -0.452641,0.0872 -0.796161,0.13129 -1.036129,0.13129 -0.572641,0 -0.965241,-0.13129 -1.177921,-0.39281 -0.21264,-0.2616 -0.32176,-0.7576 -0.32176,-1.48879 l 0,-2.35032 -0.7688806,0 0,-0.94344 0.7744006,0 0,-1.46697 1.096113,0 0,1.46697 1.396048,0 0,0.93792 z"
- id="path4482-2"
- sodipodi:nodetypes="csscccssscccccccccc" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-128"
- id="qt-logo-256"
- transform="matrix(2,0,0,2,-252,-1052.3636)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- inkscape:label="#use3503" />
- <use
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- x="0"
- y="0"
- xlink:href="#qt-logo-128"
- id="qt-logo-512"
- transform="matrix(4,0,0,4,-756,-3157.0892)"
- width="1920"
- height="1080"
- inkscape:label="#use4651"
- style="fill:#d45500" />
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer3"
- inkscape:label="qt-appicons"
- transform="translate(0,7905)">
- <g
- id="qt-logo-appicon-16"
- transform="translate(-2,0)">
- <rect
- style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.2;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- id="rect4465-3"
- width="16"
- height="15.999983"
- x="14"
- y="384" />
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="use7459-1-0-9-4"
- d="m 15,396.99998 11.5,0 2.5,-2.5 0,-7.5 -11.5,0 -2.5,2.5 z"
- style="fill:#41cd52" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 20.003869,394.8418 c -0.746615,0 -1.26886,-0.2316 -1.562863,-0.69479 C 18.147002,393.68372 18,392.94869 18,391.95097 c 0,-1.00219 0.15087,-1.74609 0.448742,-2.23159 0.297873,-0.48551 0.820116,-0.73046 1.555127,-0.73046 0.738877,0 1.257253,0.24047 1.551257,0.72597 0.297872,0.48112 0.444874,1.22502 0.444874,2.2317 0,0.66361 -0.06189,1.19813 -0.181818,1.60345 -0.123792,0.4099 -0.321084,0.72168 -0.599613,0.93545 l 0.691869,1.26167 -0.738877,0.39645 -0.726687,-1.35082 c -0.09284,0.0364 -0.239846,0.0495 -0.441005,0.0495 z m -0.932301,-1.28728 c 0.162474,0.32066 0.475821,0.48551 0.93617,0.48551 0.460348,0 0.769825,-0.16028 0.9323,-0.47655 0.158608,-0.31626 0.239845,-0.85526 0.239845,-1.60803 0,-0.75724 -0.08123,-1.30511 -0.247582,-1.64807 -0.166343,-0.34306 -0.471954,-0.5123 -0.924563,-0.5123 -0.448744,0 -0.758222,0.16924 -0.928435,0.5123 -0.166344,0.34296 -0.251451,0.88644 -0.251451,1.63921 0,0.74828 0.08123,1.28728 0.243716,1.60793 z"
- id="path4480-11"
- sodipodi:nodetypes="ccssscsccccccccscscscsc" />
- <path
- style="display:inline;fill:#ffffff"
- inkscape:connector-curvature="0"
- class="st1"
- d="m 25.000343,390.99795 0,2.06092 c 0,0.3626 0.02863,0.6013 0.08112,0.7205 0.0525,0.1193 0.14667,0.1766 0.361395,0.1766 l 0.725289,-0.029 0.04294,0.773 c -0.396046,0.076 -0.696659,0.1145 -0.906612,0.1145 -0.501021,0 -0.844579,-0.1145 -1.030674,-0.3435 -0.186094,-0.2291 -0.281527,-0.6633 -0.281527,-1.3027 l 0,-2.16592 -0.992274,0 0,-1.00235 0.997046,0 0.003,-1.00002 1,0 3.4e-4,1.00002 1.177347,0 -3e-6,0.99765 z"
- id="path4482-08"
- sodipodi:nodetypes="csscccssscccccccccc" />
- </g>
- <g
- id="qt-logo-appicon-24"
- transform="matrix(1.3636364,0,0,1.3636364,9.318182,-152.54382)"
- inkscape:label="#g3372">
- <rect
- style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.2;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- id="rect4465-3-6"
- width="17.599998"
- height="17.599993"
- x="13.7"
- y="387.59879" />
- <g
- id="g4518"
- transform="translate(4.6325679e-8,4.3999968)">
- <path
- style="fill:#41cd52"
- d="m 15.9,397.13212 10.999999,-10e-6 2.2,-2.2 0,-8.06666 -11,0 -2.2,2.2 z"
- id="use7459-1-0-9-4-8"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc" />
- <path
- id="path4480-7"
- d="m 21.039754,394.97215 c -0.868303,0 -1.475665,-0.23603 -1.817587,-0.7082 -0.341922,-0.47217 -0.512884,-1.22135 -0.512884,-2.23836 0,-1.02153 0.17546,-1.77969 0.521882,-2.27456 0.346421,-0.49487 0.953783,-0.74464 1.808589,-0.74464 0.859304,0 1.462167,0.24522 1.80409,0.7401 0.346421,0.49033 0.517383,1.24849 0.517383,2.27456 0,0.67653 -0.07199,1.22136 -0.211453,1.6345 -0.143968,0.41767 -0.373415,0.73555 -0.697342,0.95342 l 0.701841,1.13961 -0.859305,0.40405 -0.742331,-1.23032 c -0.107977,0.0363 -0.278937,0.05 -0.512883,0.05 z M 19.9555,393.66009 c 0.188957,0.32684 0.553375,0.49487 1.088753,0.49487 0.535377,0 0.895296,-0.16348 1.084254,-0.48579 0.184457,-0.32231 0.278936,-0.87168 0.278936,-1.63903 0,-0.77177 -0.09448,-1.33023 -0.287935,-1.67979 -0.193455,-0.34967 -0.548875,-0.52212 -1.075255,-0.52212 -0.521882,0 -0.8818,0.17245 -1.079755,0.52212 -0.193456,0.34956 -0.292433,0.90347 -0.292433,1.6707 0,0.76281 0.09448,1.31219 0.283435,1.63904 z"
- class="st1"
- inkscape:connector-curvature="0"
- style="display:inline;fill:#ffffff" />
- <path
- sodipodi:nodetypes="csscccssscccccccccc"
- id="path4482-5"
- d="m 25.433333,391.26545 -7e-6,1.62421 c -1e-6,0.34189 0.08924,1.08949 0.138739,1.20199 0.04949,0.1125 0.179958,0.16638 0.382412,0.16638 l 0.945522,-0.0593 0,0.73333 c -0.373416,0.072 -0.878036,0.0555 -1.075993,0.0555 -0.472392,0 -0.687606,-0.26393 -0.863066,-0.47982 -0.175461,-0.216 -0.265439,-0.91185 -0.265439,-1.51473 l 0.0045,-1.7276 -0.733333,0 0,-0.73334 0.733333,0 0,-1.46666 0.733334,0 0,1.46666 1.466666,0 0,0.73334 z"
- class="st1"
- inkscape:connector-curvature="0"
- style="display:inline;fill:#ffffff" />
- </g>
- </g>
- <g
- id="qt-logo-appicon-32"
- transform="matrix(1.7142857,0,0,1.7142857,27.571429,-290.57143)"
- inkscape:label="#g3384">
- <g
- id="g4525"
- transform="translate(0.58333302,1.7500024)">
- <path
- style="fill:#41cd52"
- d="m 16,396.99999 11.666667,0 L 30,394.66666 l 0,-8.16667 -11.666667,0 L 16,388.83333 Z"
- id="use7459-1-0-9-4-8-8"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccc" />
- <path
- sodipodi:nodetypes="ccscscsccccccccscscscsc"
- id="path4480-13"
- d="m 21.300737,394.7506 c -0.896868,0 -1.52421,-0.25065 -1.877382,-0.75196 -0.35317,-0.50146 -0.529755,-1.10777 -0.529755,-2.18767 0,-1.08461 0.181232,-1.70168 0.539051,-2.22708 0.357817,-0.52556 0.98516,-0.79061 1.868086,-0.79061 0.887573,0 1.51027,0.26035 1.863439,0.78576 0.357817,0.52055 0.534403,1.13762 0.534403,2.22708 0,0.71832 -0.07435,0.95952 -0.218407,1.39826 -0.148704,0.44357 -0.3857,0.92929 -0.720282,1.16054 l 0.724929,1.21009 -0.887576,0.42902 -0.766751,-1.30647 c -0.111532,0.0394 -0.288112,0.053 -0.529755,0.053 z m -1.119923,-1.54138 c 0.195174,0.34704 0.571579,0.67364 1.124569,0.67364 0.552991,0 0.924749,-0.32175 1.119922,-0.66394 0.190528,-0.34234 0.288114,-0.5884 0.288114,-1.40311 0,-0.81955 -0.09758,-1.22445 -0.297406,-1.59559 -0.199819,-0.37128 -0.566933,-0.55435 -1.11063,-0.55435 -0.539048,0 -0.910808,0.18307 -1.115275,0.55435 -0.199821,0.37114 -0.302055,0.77119 -0.302055,1.5859 0,0.80985 0.09758,1.05606 0.292761,1.4031 z"
- class="st1"
- inkscape:connector-curvature="0"
- style="display:inline;fill:#ffffff" />
- <path
- sodipodi:nodetypes="csscccssscccccccccc"
- id="path4482-4"
- d="m 25.867755,391.52938 0,1.22006 c 0,0.36271 0.02864,0.6721 0.08112,0.7915 0.05248,0.12 0.190865,0.17655 0.40559,0.17655 l 0.725289,-0.0285 0.04294,0.84587 c -0.396045,0.0765 -0.696659,0.114 -0.906612,0.114 -0.501021,0 -0.972184,-0.18692 -1.158278,-0.41657 -0.186095,-0.22905 -0.281528,-0.73404 -0.281528,-1.3735 l 0,-1.32521 -0.609609,-0.002 0,-0.94793 0.583333,0 0,-1.75 1.166667,0 0,1.75 1.163666,0 0.006,0.9456 z"
- class="st1"
- inkscape:connector-curvature="0"
- style="display:inline;fill:#ffffff" />
- </g>
- <rect
- style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.2;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
- id="rect4523"
- width="18.666666"
- height="18.666672"
- x="14.25"
- y="384.16666" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-24"
- id="qt-logo-appicon-48"
- transform="matrix(2,0,0,2,27.999998,-399.99999)"
- width="1920"
- height="1080" />
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-128"
- id="qt-logo-appicon-64"
- transform="matrix(0.5,0,0,0.5,34,200)"
- width="1920"
- height="1080" />
- <g
- id="qt-logo-appicon-128"
- transform="matrix(7.1111111,0,0,7.1111111,95.77778,-2454.6667)"
- inkscape:label="#g3420">
- <rect
- style="display:inline;fill:none"
- y="385.68747"
- x="16.34375"
- height="13.5"
- width="13.5"
- id="rect3865-7" />
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="use7459-1-0-9-4-8-8-5"
- d="m 16.34375,397.5 11.53125,0 1.96875,-1.96875 0,-8.15625 -11.53125,0 -1.96875,1.96875 z"
- style="fill:#41cd52" />
- <path
- id="path4480-74"
- d="m 21.368288,395.25771 c -0.888036,0 -1.509204,-0.2394 -1.858896,-0.71778 -0.34968,-0.4782 -0.52452,-1.23774 -0.52452,-2.26842 0,-1.03524 0.17946,-1.80366 0.53376,-2.3052 0.3543,-0.5016 0.975456,-0.75462 1.849692,-0.75462 0.878832,0 1.495398,0.2484 1.84509,0.75 0.3543,0.4968 0.52914,1.26534 0.52914,2.30526 0,0.68556 -0.07362,1.23768 -0.21624,1.65642 -0.14724,0.423 -0.3819,0.74538 -0.71319,0.96624 l 0.717786,1.15494 -0.878832,0.4092 -0.759204,-1.24692 c -0.11046,0.036 -0.2853,0.048 -0.52452,0.048 z m -1.108896,-1.32972 c 0.19326,0.3312 0.56592,0.5016 1.113498,0.5016 0.54756,0 0.915642,-0.1656 1.108896,-0.4926 0.18864,-0.327 0.2853,-0.88344 0.2853,-1.66104 0,-0.78222 -0.0966,-1.34814 -0.29448,-1.70244 -0.19782,-0.354 -0.56136,-0.5292 -1.099692,-0.5292 -0.53376,0 -0.901842,0.1746 -1.104294,0.5292 -0.19788,0.3546 -0.2991,0.91566 -0.2991,1.69326 0,0.77298 0.0966,1.32972 0.28986,1.66104 z"
- class="st1"
- inkscape:connector-curvature="0"
- style="display:inline;fill:#ffffff" />
- <path
- sodipodi:nodetypes="csscccssscccccccccc"
- id="path4482-05"
- d="m 25.859084,391.57677 0,1.8819 c 0,0.3498 0.0276,0.5796 0.07824,0.69474 0.0504,0.1152 0.18402,0.1704 0.39108,0.1704 l 0.699384,-0.03 0.0414,0.74538 c -0.3819,0.0738 -0.671778,0.1104 -0.874236,0.1104 -0.48312,0 -0.814416,-0.1104 -0.993864,-0.3312 -0.17946,-0.2208 -0.27144,-0.63954 -0.27144,-1.2561 l 0,-1.98318 -0.648774,0 0,-0.79596 0.653376,0 0,-1.23774 0.924846,0 0,1.23774 1.177914,0 -6e-6,0.7914 z"
- class="st1"
- inkscape:connector-curvature="0"
- style="display:inline;fill:#ffffff" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-128"
- id="qt-logo-appicon-256"
- transform="matrix(2,0,0,2,-68,-400)"
- width="1920"
- height="1080"
- qt-logo-appicon-256="#qt-logo-appicon-128" />
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-128"
- id="qt-logo-appicon-512"
- transform="matrix(4,0,0,4,-204,-1200)"
- width="1920"
- height="1080" />
- <g
- id="assistant_icon_16x16"
- clip-path="url(#clipPath4101)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3142"
- xlink:href="#qt-logo-appicon-16"
- y="0"
- x="0" />
- <rect
- id="use3144"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- transform="matrix(0.07059978,0,0,0.07059978,4.0327637,-164.38411)"
- height="1"
- width="1"
- id="use4094"
- xlink:href="#assistant_questionmark"
- y="0"
- x="0"
- style="filter:url(#filter4131)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- </g>
- <g
- id="assistant_icon_24x24"
- clip-path="url(#clipPath4156)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use3148" />
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3146"
- xlink:href="#qt-logo-appicon-24"
- y="0"
- x="0" />
- <use
- x="0"
- y="0"
- xlink:href="#assistant_questionmark"
- id="use4149"
- width="1"
- height="1"
- transform="matrix(0.09499217,0,0,0.09499217,18.094296,-180.71335)" />
- </g>
- <g
- id="assistant_icon_32x32"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <rect
- style="fill:none;stroke:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use3152" />
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3150"
- xlink:href="#qt-logo-appicon-32"
- y="0"
- x="0" />
- <use
- transform="matrix(0.12226277,0,0,0.12226277,38.583484,-198.42154)"
- height="1"
- width="1"
- id="use4145"
- xlink:href="#assistant_questionmark"
- y="0"
- x="0" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#assistant_icon_24x24"
- id="assistant_icon_48x48"
- transform="matrix(2,0,0,2,24,120)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#assistant_icon_128x128"
- id="assistant_icon_64x64"
- width="1920"
- height="1080"
- transform="matrix(0.5,0,0,0.5,36,-60)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- id="assistant_icon_128x128"
- clip-path="url(#clipPath4086)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3162"
- xlink:href="#qt-logo-appicon-128"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use3164" />
- <use
- x="0"
- y="0"
- xlink:href="#assistant_questionmark"
- id="use4030"
- width="1"
- height="1"
- transform="matrix(0.37894144,0,0,0.37894144,173.89568,-370.20262)" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#assistant_icon_128x128"
- id="assistant_icon_256x256"
- transform="matrix(2,0,0,2,-72,120)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#assistant_icon_128x128"
- id="assistant_icon_512x512"
- width="1920"
- height="1080"
- transform="matrix(4,0,0,4,-216,360)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#assistant_icon_128x128"
- id="assistant_icon_1024x1024"
- transform="matrix(8,0,0,8,-504,840)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4101)"
- id="linguist_icon_16x16"
- transform="translate(0,-1040)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-16"
- id="use4164"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- id="use4166"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- x="0"
- y="0"
- xlink:href="#linguist_bubble"
- id="use4240-2"
- width="1"
- height="1"
- transform="matrix(0.08658171,0,0,0.08658171,6.2471299,-179.29014)" />
- </g>
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4156)"
- id="linguist_icon_24x24"
- transform="translate(0,-1040)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-24"
- id="use4172"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use4174" />
- <use
- x="0"
- y="0"
- xlink:href="#linguist_bubble"
- id="use4240-8"
- width="1"
- height="1"
- transform="matrix(0.08244879,0,0,0.08244879,30.157234,-183.46612)" />
- </g>
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="linguist_icon_32x32"
- transform="translate(0,-1040)"
- clip-path="url(#clipPath5186)">
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use4182" />
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-32"
- id="use4180"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <use
- x="0"
- y="0"
- xlink:href="#linguist_bubble"
- id="use4240-4"
- width="1"
- height="1"
- transform="matrix(0.11533942,0,0,0.11533942,51.68887,-207.81583)" />
- </g>
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,24,1160)"
- id="linguist_icon_48x48"
- xlink:href="#linguist_icon_24x24"
- y="0"
- x="0" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- transform="matrix(0.5,0,0,0.5,36,-580)"
- height="1080"
- width="1920"
- id="linguist_icon_64x64"
- xlink:href="#linguist_icon_128x128"
- y="0"
- x="0" />
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4086)"
- id="linguist_icon_128x128"
- transform="translate(0,-1040)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-128"
- id="use4192"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use4194" />
- <use
- x="0"
- y="0"
- xlink:href="#linguist_bubble"
- id="use4240"
- width="1"
- height="1"
- transform="matrix(0.38936968,0,0,0.38936968,206.05929,-429.28003)" />
- </g>
- <use
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,-72,1160)"
- id="linguist_icon_256x256"
- xlink:href="#linguist_icon_128x128"
- y="0"
- x="0"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- transform="matrix(4,0,0,4,-216,3480)"
- height="1080"
- width="1920"
- id="linguist_icon_512x512"
- xlink:href="#linguist_icon_128x128"
- y="0"
- x="0" />
- <use
- height="1080"
- width="1920"
- transform="matrix(8,0,0,8,-504.4,8120)"
- id="linguist_icon_1024x1024"
- xlink:href="#linguist_icon_128x128"
- y="0"
- x="0"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- transform="translate(0,-2080)"
- id="designer_icon_16x16"
- clip-path="url(#clipPath4101)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use4368"
- xlink:href="#qt-logo-appicon-16"
- y="0"
- x="0" />
- <rect
- id="use4370"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- x="0"
- y="0"
- xlink:href="#designer_pen"
- id="use4408"
- width="1"
- height="1"
- transform="matrix(0.08740465,0,0,0.09924617,15.494916,-176.03251)" />
- </g>
- <g
- transform="translate(0,-2080)"
- id="designer_icon_24x24"
- clip-path="url(#clipPath4156)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use4376"
- xlink:href="#qt-logo-appicon-24"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use4378" />
- <use
- x="0"
- y="0"
- xlink:href="#designer_pen"
- id="use4408-2"
- width="1"
- height="1"
- transform="matrix(0.07759947,-0.07759947,0.07759947,0.07759947,-2.0275119,-162.70546)" />
- </g>
- <g
- transform="translate(0,-2080)"
- id="designer_icon_32x32"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- clip-path="url(#clipPath4448)">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use4384"
- xlink:href="#qt-logo-appicon-32"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use4386" />
- <use
- x="0"
- y="0"
- xlink:href="#designer_pen"
- id="use4408-7"
- width="1"
- height="1"
- transform="matrix(0.09521767,-0.09521767,0.09521767,0.09521767,16.139983,-173.93568)" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#designer_icon_24x24"
- id="designer_icon_48x48"
- transform="matrix(2,0,0,2,24,2200)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#designer_icon_128x128"
- id="designer_icon_64x64"
- width="1920"
- height="1080"
- transform="matrix(0.5,0,0,0.5,36,-1100)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- transform="translate(0,-2080)"
- id="designer_icon_128x128"
- clip-path="url(#clipPath4086)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use4396"
- xlink:href="#qt-logo-appicon-128"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use4398" />
- <use
- x="0"
- y="0"
- xlink:href="#designer_pen"
- id="use4408-79"
- width="1"
- height="1"
- transform="matrix(0.32694672,-0.32336336,0.32694672,0.32336336,83.761839,-302.40583)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#designer_icon_128x128"
- id="designer_icon_256x256"
- transform="matrix(2,0,0,2,-72,2200)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#designer_icon_128x128"
- id="designer_icon_512x512"
- width="1920"
- height="1080"
- transform="matrix(4,0,0,4,-216,6599.6)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- x="0"
- y="0"
- xlink:href="#designer_icon_128x128"
- id="designer_icon_1024x1024"
- transform="matrix(8,0,0,8,-504,15400)"
- width="1920"
- height="1080" />
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4101)"
- id="qdbusviewer_icon_16x16"
- transform="translate(0,-3120)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-16"
- id="use4462"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- id="use4464"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- x="0"
- y="0"
- xlink:href="#dbusviewer_arrows"
- id="use4550"
- transform="matrix(0.10844502,0,0,0.11562764,-26.207541,-187.34041)"
- width="1"
- height="1" />
- </g>
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4156)"
- id="qdbusviewer_icon_24x24"
- transform="translate(0,-3120)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-24"
- id="use4470"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use4472" />
- <use
- x="0"
- y="0"
- xlink:href="#dbusviewer_arrows"
- id="use4550-6"
- transform="matrix(0.14520415,0,0,0.11654081,-23.08533,-188.81915)"
- width="1"
- height="1" />
- </g>
- <g
- clip-path="url(#clipPath4448)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="qdbusviewer_icon_32x32"
- transform="translate(0,-3120)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-32"
- id="use4478"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use4480" />
- <use
- x="0"
- y="0"
- xlink:href="#dbusviewer_arrows"
- id="use4550-3"
- transform="matrix(0.18154379,0,0,0.1765106,-10.48783,-224.72619)"
- width="1"
- height="1" />
- </g>
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,24,3240)"
- id="qdbusviewer_icon_48x48"
- xlink:href="#qdbusviewer_icon_24x24"
- y="0"
- x="0" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- transform="matrix(0.5,0,0,0.5,36,-1620)"
- height="1080"
- width="1920"
- id="qdbusviewer_icon_64x64"
- xlink:href="#qdbusviewer_icon_128x128"
- y="0"
- x="0" />
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4086)"
- id="qdbusviewer_icon_128x128"
- transform="translate(0,-3120)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-128"
- id="use4490"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use4492" />
- <use
- x="0"
- y="0"
- xlink:href="#dbusviewer_icon"
- id="use4617"
- transform="matrix(0.5,0,0,0.5,52,-420)"
- width="1"
- height="1" />
- </g>
- <use
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,-72,3240)"
- id="qdbusviewer_icon_256x256"
- xlink:href="#qdbusviewer_icon_128x128"
- y="0"
- x="0"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- transform="matrix(4,0,0,4,-216,9720)"
- height="1080"
- width="1920"
- id="qdbusviewer_icon_512x512"
- xlink:href="#qdbusviewer_icon_128x128"
- y="0"
- x="0" />
- <use
- height="1080"
- width="1920"
- transform="matrix(8,0,0,8,-504,22680)"
- id="qdbusviewer_icon_1024x1024"
- xlink:href="#qdbusviewer_icon_128x128"
- y="0"
- x="0"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- transform="translate(0,-4160)"
- id="qmlviewer_icon_16x16"
- clip-path="url(#clipPath4101)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3241"
- xlink:href="#qt-logo-appicon-16"
- y="0"
- x="0" />
- <rect
- id="use3243"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer-logo-paperplane"
- id="use6842-37"
- transform="matrix(0.0706897,0,0,0.0706897,19.551994,-175.17505)"
- width="1"
- height="1" />
- </g>
- <g
- transform="translate(0,-4160)"
- id="qmlviewer_icon_24x24"
- clip-path="url(#clipPath4156)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3249"
- xlink:href="#qt-logo-appicon-24"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use3251" />
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer-logo-paperplane"
- id="use6842-3"
- transform="matrix(0.07591317,0,0,0.07591317,42.310501,-180.48261)"
- width="1"
- height="1" />
- </g>
- <g
- transform="translate(0,-4160)"
- id="qmlviewer_icon_32x32"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- clip-path="url(#clipPath4448)">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3257"
- xlink:href="#qt-logo-appicon-32"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use3259" />
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer-logo-paperplane"
- id="use6842-0"
- transform="matrix(0.10052878,0,0,0.10052878,69.48104,-201.12817)"
- width="1"
- height="1" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer_icon_24x24"
- id="qmlviewer_icon_48x48"
- transform="matrix(2,0,0,2,24,4280)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer_icon_128x128"
- id="qmlviewer_icon_64x64"
- width="1920"
- height="1080"
- transform="matrix(0.5,0,0,0.5,36,-2140)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- transform="translate(0,-4160)"
- id="qmlviewer_icon_128x128"
- clip-path="url(#clipPath4086)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-520)"
- id="use3269"
- xlink:href="#qt-logo-appicon-128"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use3271" />
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer-logo-paperplane"
- id="use6842"
- width="1"
- height="1"
- transform="matrix(0.30994785,0,0,0.30994785,265.09631,-372.11607)" />
- </g>
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer_icon_128x128"
- id="qmlviewer_icon_256x256"
- transform="matrix(2,0,0,2,-72,4280)"
- width="1920"
- height="1080"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- x="0"
- y="0"
- xlink:href="#qmlviewer_icon_128x128"
- id="qmlviewer_icon_512x512"
- width="1920"
- height="1080"
- transform="matrix(4,0,0,4,-216,12840)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- x="0"
- y="0"
- xlink:href="#qmlviewer_icon_128x128"
- id="qmlviewer_icon_1024x1024"
- transform="matrix(8,0,0,8,-504,29960)"
- width="1920"
- height="1080" />
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4101)"
- id="qtcreator_icon_16x16"
- transform="translate(0,-5200)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-16"
- id="use4547"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- id="use4549"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- x="0"
- y="0"
- xlink:href="#qtcreator-logo-leaf"
- id="use4579"
- transform="matrix(0.06897093,-0.01079806,0.01079806,0.06897093,-23.03335,-156.337)"
- width="1"
- height="1" />
- </g>
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4156)"
- id="qtcreator_icon_24x24"
- transform="translate(0,-5200)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-24"
- id="use4553"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-144"
- x="32"
- height="24"
- width="24"
- id="use4555" />
- <use
- x="0"
- y="0"
- xlink:href="#qtcreator-logo-leaf"
- id="use4579-5"
- transform="matrix(0.0796062,0,0,0.0796062,-1.4464667,-170.31739)"
- width="1"
- height="1" />
- </g>
- <g
- clip-path="url(#clipPath4448)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="qtcreator_icon_32x32"
- transform="translate(0,-5200)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-32"
- id="use4559"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="use4561" />
- <use
- x="0"
- y="0"
- xlink:href="#qtcreator-logo-leaf"
- id="use4579-7"
- transform="matrix(0.11384673,-0.0042732,0.0042732,0.11384673,3.9071909,-189.9819)"
- width="1"
- height="1" />
- </g>
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,24,5320)"
- id="qtcreator_icon_48x48"
- xlink:href="#qtcreator_icon_24x24"
- y="0"
- x="0" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- transform="matrix(0.5,0,0,0.5,36,-2660)"
- height="1080"
- width="1920"
- id="qtcreator_icon_64x64"
- xlink:href="#qtcreator_icon_128x128"
- y="0"
- x="0" />
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4086)"
- id="qtcreator_icon_128x128"
- transform="translate(0,-5200)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-128"
- id="use4569"
- transform="translate(4,-520)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-248"
- x="200"
- height="128"
- width="128"
- id="use4571" />
- <use
- x="0"
- y="0"
- xlink:href="#qtcreator-logo-leaf"
- id="use4579-6"
- transform="matrix(0.41034847,0,0,0.41034847,32.518174,-380.76856)"
- width="1"
- height="1" />
- </g>
- <use
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,-72,5320)"
- id="qtcreator_icon_256x256"
- xlink:href="#qtcreator_icon_128x128"
- y="0"
- x="0"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <use
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- transform="matrix(4,0,0,4,-216,15960)"
- height="1080"
- width="1920"
- id="qtcreator_icon_512x512"
- xlink:href="#qtcreator_icon_128x128"
- y="0"
- x="0" />
- <use
- height="1080"
- width="1920"
- transform="matrix(8,0,0,8,-504,37240)"
- id="qtcreator_icon_1024x1024"
- xlink:href="#qtcreator_icon_128x128"
- y="0"
- x="0"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90" />
- <g
- transform="translate(0,-6200)"
- id="bugreports_icon_16x16"
- clip-path="url(#clipPath4101)"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-521)"
- id="use4781"
- xlink:href="#qt-logo-appicon-16"
- y="0"
- x="0" />
- <rect
- style="fill:none"
- y="-136"
- x="16"
- height="16"
- width="16"
- id="rect4783" />
- <use
- x="0"
- y="0"
- xlink:href="#bugzilla_bug"
- id="use7433"
- width="100%"
- height="100%"
- transform="translate(-720.49181,-673)" />
- </g>
- <g
- transform="translate(-24,-6200)"
- id="bugreports_icon_32x32"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- clip-path="url(#clipPath4448)">
- <use
- height="1080"
- width="1920"
- transform="translate(4,-522)"
- id="use4797"
- xlink:href="#qt-logo-appicon-32"
- y="0"
- x="0" />
- <rect
- id="rect4799"
- width="32"
- height="32"
- x="56"
- y="-152"
- style="fill:none" />
- <use
- transform="matrix(2,0,0,2,-1416.0026,-1225)"
- x="0"
- y="0"
- xlink:href="#bugzilla_bug"
- id="use7433-3"
- width="100%"
- height="100%" />
- </g>
- <g
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- clip-path="url(#clipPath4101)"
- id="codereview_icon_16x16"
- transform="translate(48,-6200)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-16"
- id="use7473"
- transform="translate(4,-521)"
- width="1920"
- height="1080" />
- <rect
- id="rect7475"
- width="16"
- height="16"
- x="16"
- y="-136"
- style="fill:none" />
- <use
- transform="translate(-731,-673)"
- x="0"
- y="0"
- xlink:href="#g7520"
- id="use7525"
- width="100%"
- height="100%" />
- </g>
- <g
- clip-path="url(#clipPath4448)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="codereview_icon_32x32"
- transform="translate(24,-6200)">
- <use
- x="0"
- y="0"
- xlink:href="#qt-logo-appicon-32"
- id="use7481"
- transform="translate(4,-522)"
- width="1920"
- height="1080" />
- <rect
- style="fill:none"
- y="-152"
- x="56"
- height="32"
- width="32"
- id="rect7483" />
- <use
- transform="matrix(2,0,0,2,-1437,-1225)"
- x="0"
- y="0"
- xlink:href="#g7520"
- id="use7525-4"
- width="100%"
- height="100%" />
- </g>
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="app-icon-images"
- transform="translate(0,7905)">
- <g
- id="designer_pen"
- transform="translate(296.98485,-328.744)"
- inkscape:label="#assistant_pen">
- <rect
- y="853.36218"
- x="-148.98485"
- height="30"
- width="30"
- id="rect4308"
- style="fill:#cd9aa3;fill-opacity:1;stroke:none" />
- <path
- id="path4291"
- transform="translate(0,-27.637817)"
- d="M -267,886.71875 -291,896 l 24,9.25 z"
- style="fill:#47484a;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- <path
- sodipodi:nodetypes="ccccc"
- inkscape:connector-curvature="0"
- id="path4286"
- transform="translate(0,-27.637817)"
- d="M -242.48485,878.91307 -272,888.5625 l 0,14.84375 29.51515,9.60057 z"
- style="fill:#e1cab0;fill-opacity:1;stroke:none" />
- <rect
- y="853.36218"
- x="-246"
- height="19.987"
- width="112"
- id="rect4296"
- style="fill:#f7ec07;fill-opacity:1;stroke:none" />
- <rect
- style="fill:#f7aa03;fill-opacity:1;stroke:none"
- id="rect4298"
- width="112"
- height="20"
- x="-246"
- y="863.36218" />
- <rect
- style="fill:#fbcb00;fill-opacity:1;stroke:none"
- id="rect4300"
- width="112"
- height="10"
- x="-246"
- y="863.36218" />
- <rect
- style="fill:#e4e6e7;fill-opacity:1;stroke:none"
- id="rect4302"
- width="16"
- height="10"
- x="-149"
- y="853.36218" />
- <rect
- y="873.36218"
- x="-149"
- height="10"
- width="16"
- id="rect4304"
- style="fill:#aeb1b3;fill-opacity:1;stroke:none" />
- <rect
- y="863.36218"
- x="-149"
- height="10"
- width="16"
- id="rect4306"
- style="fill:#c8c9c9;fill-opacity:1;stroke:none" />
- <path
- style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
- d="m -292.6875,866.5 c -0.75944,0.28213 -1.30365,1.06485 -1.30365,1.875 0,0.81015 0.54421,1.59287 1.30365,1.875 l 46,17 c 0.21949,0.0815 0.45338,0.11118 0.6875,0.11218 l 129,0 c 1,0 2,-1 2,-2 l 0,-34 c 0,-1 -1,-2 -2,-2 l -128.9688,-0.0809 c -0.25675,0.005 -0.42215,0.017 -0.57042,0.071 z M -119,853.36218 l 0,30 -127,0 -40,-15 40,-15 z"
- id="path3516"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cscccccccccccccccc" />
- </g>
- <g
- id="linguist_bubble"
- transform="translate(80.982993,-144.36552)">
- <path
- style="fill:#a5d750;fill-opacity:1;stroke:none;filter:url(#filter5469)"
- d="m 117,712 c 1,-27 30,-37 49,-37 19,0 48,10 48,38 0,28 -43,57 -62,66 4,-12 6,-22 6,-28 -10,-1 -42,-12 -41,-39 z"
- id="path5467"
- inkscape:connector-curvature="0"
- transform="translate(0,-27.637817)"
- sodipodi:nodetypes="zzzccz" />
- <path
- sodipodi:nodetypes="zzzccz"
- transform="translate(0,-27.637817)"
- inkscape:connector-curvature="0"
- id="path4337"
- d="m 117,712 c 1,-27 30,-37 49,-37 19,0 48,10 48,38 0,28 -43,57 -62,66 4,-12 6,-22 6,-28 -10,-1 -42,-12 -41,-39 z"
- style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- </g>
- <g
- id="assistant_questionmark"
- transform="translate(-180.85534,-135.69775)"
- clip-path="none">
- <path
- id="path3942"
- transform="matrix(1.025641,0,0,1.0256412,-106.13969,148.03106)"
- style="fill:#e8eef4;fill-opacity:1;stroke:none;filter:url(#filter5469)"
- d="m 637,580.5 c 0,10.76955 -8.73045,19.5 -19.5,19.5 -10.76955,0 -19.5,-8.73045 -19.5,-19.5 0,-10.76955 8.73045,-19.5 19.5,-19.5 10.76955,0 19.5,8.73045 19.5,19.5 z M 617.50001,466.42502 c 18.525,0 43.875,5.12805 43.875,33.15 0,29.24999 -31.2,31.19999 -29.25,54.59999 -15.6,0 -21.45,0 -31.2,0 -3.9,-33.15 26.325,-36.075 27.3,-53.62499 0.975,-17.55 -25.35,-10.725 -39,-7.8 0,-8.775 0,-15.6 0,-21.45 7.8,-1.95 16.39219,-4.875 28.275,-4.875 z"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sssssssccsccs" />
- <path
- d="m 637,580.5 c 0,10.76955 -8.73045,19.5 -19.5,19.5 -10.76955,0 -19.5,-8.73045 -19.5,-19.5 0,-10.76955 8.73045,-19.5 19.5,-19.5 10.76955,0 19.5,8.73045 19.5,19.5 z M 617.50001,466.42502 c 18.525,0 43.875,5.1969 43.875,33.15 0,29.24999 -31.2,31.19999 -29.25,54.59999 -15.6,0 -21.45,0 -31.2,0 -3.9,-33.15 26.325,-36.075 27.3,-53.62499 0.975,-17.55 -25.35,-10.725 -39,-7.8 0,-8.775 0,-15.6 0,-21.45 7.8,-1.95 16.33125,-4.875 28.275,-4.875 z"
- style="fill:none;stroke:#ffffff;stroke-width:3.89999962;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- transform="matrix(1.025641,0,0,1.0256412,-106.13969,148.03106)"
- id="path3936"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="sssssssccsccs" />
- </g>
- <g
- id="dbusviewer_icon"
- inkscape:label="#g4545">
- <g
- transform="translate(336,-320.36218)"
- inkscape:label="#g4645"
- id="dbusviewer_arrows">
- <path
- style="fill:#055e29;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 94,852.36218 28,-28 0,14 52,0 0,28 -52,0 0,14 z"
- id="path3850"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccccccc" />
- <path
- style="fill:#1e1261;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 202,880.36218 -28,-28 0,14 -52,0 0,28 52,0 0,14 z"
- id="path3850-1"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccccccc" />
- </g>
- <g
- id="dbusviewer_d"
- style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
- transform="matrix(1.5841611,0,0,1.5841611,247.07354,-842.03118)"
- inkscape:label="#text4638">
- <path
- sodipodi:nodetypes="ccccsssccccccsccscccss"
- inkscape:connector-curvature="0"
- id="path4643"
- style="font-weight:bold;fill:#ffffff;fill-opacity:1;-inkscape-font-specification:Sans Bold"
- d="m 159.65957,890.08068 -5.04999,0 0,-3.35537 c -0.84412,1.13088 -1.87489,1.97284 -3.02416,2.52586 -1.14929,0.55301 -2.30829,0.82951 -3.47704,0.82951 -2.37647,0 -4.41204,-0.91651 -6.10673,-2.74954 -1.6947,-1.83301 -2.54205,-4.38992 -2.54205,-7.67072 0,-3.35536 0.82463,-5.90605 2.47386,-7.6521 1.64924,-1.74601 3.73353,-2.61903 6.25284,-2.61905 2.31153,2e-5 4.73506,0.91963 6.42328,2.75886 l 0,-9.8424 5.04999,0 z m -14.60942,-10.77445 c 0,2.11264 0.30516,3.6412 0.91552,4.58566 0.88305,1.36701 2.11673,2.05052 3.70105,2.05051 1.25965,10e-6 2.33101,-0.51262 3.21407,-1.53788 0.88305,-1.02524 1.32457,-2.55689 1.32459,-4.59498 -2e-5,-2.27418 -0.42856,-3.91146 -1.28563,-4.91187 -0.85709,-1.00038 -1.95444,-1.50058 -3.29198,-1.5006 -1.29864,2e-5 -2.38622,0.494 -3.26278,1.48195 -0.87658,0.98799 -1.31484,2.46372 -1.31484,4.42721 z" />
- </g>
- </g>
- <g
- transform="matrix(15.741449,2.8647469,-2.8647469,15.741449,1914.5274,-8898.7459)"
- style="display:inline"
- id="qtcreator-logo-leaf">
- <path
- style="fill:#b4b4b4;fill-opacity:1;stroke:none;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 24.360144,599.91797 c 0,0 -1.180704,-2.9808 0.17111,-4.35547 1.351814,-1.37467 4.480985,-1.78019 7.03125,-4.875 -1.33283,3.62826 -1.079399,5.25815 -2.546875,7.39063 -1.467476,2.13247 -4.655485,1.83984 -4.655485,1.83984 z"
- id="path3357"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="czczc" />
- <path
- style="fill:url(#linearGradient4541);fill-opacity:1;stroke:none"
- d="m 30.536455,592.1895 -5.953125,7.36719 c 0,0 -1.322826,-3.02036 0.6875,-4.25 2.010326,-1.22963 2.390625,-0.92969 5.265625,-3.11719 z"
- id="path4127"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cczc" />
- <path
- sodipodi:nodetypes="cczc"
- inkscape:connector-curvature="0"
- id="path4131"
- d="m 30.683552,592.28135 -5.942076,7.33784 c 0,0 2.341854,0.0625 3.654354,-1.25 1.3125,-1.3125 2,-4.4375 2.287722,-6.08784 z"
- style="fill:url(#linearGradient4543);fill-opacity:1;stroke:none" />
- <path
- sodipodi:nodetypes="cczc"
- inkscape:connector-curvature="0"
- id="path4135"
- d="m 30.55208,592.30669 c -1.90625,2.4375 -3.96875,5 -5.90625,7.3125 0.184413,-0.3343 0.720466,-1.00456 2.527284,-3.18016 1.806818,-2.17561 1.861243,-2.47029 3.378966,-4.13234 z"
- style="fill:#808080;fill-opacity:1;stroke:none" />
- </g>
- <g
- id="qmlviewer-logo-paperplane"
- inkscape:label="#g6835">
- <path
- sodipodi:nodetypes="ccccccc"
- inkscape:connector-curvature="0"
- id="path6773"
- d="M 2,741 172.5,626 c -5,40 -3.5,98 -3.5,154 -18.15246,-10.35573 -37.70839,-21.6529 -52,-23 L 77,775 64.646447,745.35355 C 45.291043,740.73103 23.986115,740.3611 2,741 z"
- style="fill:#929292;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cccc"
- inkscape:connector-curvature="0"
- id="path6775"
- d="M 15.375,736.75 C 29.458164,736.08324 45,737 67.25,740.375 L 167.875,633.65625 z"
- style="fill:url(#linearGradient5129);fill-opacity:1;stroke:none" />
- <path
- sodipodi:nodetypes="cccc"
- inkscape:connector-curvature="0"
- id="path6777"
- d="M 83,767 115.87868,751.72855 167.875,633.65625 z"
- style="fill:url(#linearGradient6035);fill-opacity:1;stroke:none" />
- <path
- sodipodi:nodetypes="cccc"
- inkscape:connector-curvature="0"
- id="path6779"
- d="M 118.14384,751.46339 C 135,755.375 148,762.99998 164.625,772.5 165.52971,726.16667 163,679.99998 167.875,633.65625 z"
- style="fill:url(#linearGradient3451);fill-opacity:1;stroke:none" />
- <path
- style="fill:url(#linearGradient5587);fill-opacity:1;stroke:none"
- d="M 68.875,741.375 79.198223,765.82322 167.875,633.65625 z"
- id="path6799"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- </g>
- <g
- id="bugzilla_bug">
- <path
- sodipodi:nodetypes="cczccccc"
- inkscape:connector-curvature="0"
- id="path7399"
- d="m 744.5,546.5 1.75,0 c 0,0 -0.25,-1.49996 1.75,-1.5 2,-4e-5 1.75,1.5 1.75,1.5 l 1.75,0 c 0.7124,2.06347 0.61179,4.05423 0,6 l -7,0 c -0.59404,-1.66985 -0.72889,-3.59518 0,-6 z"
- style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- style="fill:#2a2a84;fill-opacity:1"
- sodipodi:nodetypes="zccz"
- d="m 748,545.49998 c -1.2497,0 -1.25,1.125 -1.25,1.5 l 2.5,0 c 0,-0.375 -3e-4,-1.5 -1.25,-1.5 z"
- inkscape:connector-curvature="0"
- id="asdfasdfasdf" />
- <path
- style="fill:#2a2a84;fill-opacity:1"
- sodipodi:nodetypes="ccccccccccccccccc"
- d="m 744.77023,547 -0.15225,1 1.36286,0 -0.24267,0.99998 -1.23817,0 0,1 1.23817,0 0.27709,1.00002 -1.39728,0 0.15225,1 1.13211,0 0.88226,-0.5 0.7154,0.49998 0,-4.5 -0.75,0 0,-0.5 z"
- inkscape:connector-curvature="0"
- id="sdfgdsfgsdfg" />
- <use
- style="fill:#2a2a84;fill-opacity:1"
- x="0"
- y="0"
- xlink:href="#sdfgdsfgsdfg"
- id="use6516"
- transform="matrix(-1,0,0,1,1496,0)"
- width="100%"
- height="100%" />
- <rect
- style="opacity:0.5;fill:#2a2a84;fill-opacity:1"
- id="rect4816"
- width="1"
- height="4.5"
- x="747.5"
- y="547.5" />
- </g>
- <g
- id="g7520"
- transform="translate(0,-5)">
- <path
- sodipodi:nodetypes="cccccccccccccccccc"
- inkscape:connector-curvature="0"
- id="path7515"
- d="m 759,8457 0,2 -2,0 0,1 2,0 0,2 1,0 0,-2 2,0 0,-1 -2,0 0,-2 z m -2,-2 0,1 5,0 0,-1 z"
- style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- transform="translate(0,-7905)" />
- <path
- sodipodi:nodetypes="ccccccccccccc"
- inkscape:connector-curvature="0"
- id="path7499"
- d="m 759,554 0,-2 1,0 0,2 2,0 0,1 -2,0 0,2 -1,0 0,-2 -2,0 0,-1 z"
- style="fill:#2a2a84;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- inkscape:connector-curvature="0"
- id="path7501"
- d="m 757,550 5,0 0,1 -5,0 z"
- style="fill:#2a2a84;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- </g>
- </g>
-</svg>
diff --git a/src/tools/icons/documenttypeicons.svg b/src/tools/icons/documenttypeicons.svg
deleted file mode 100644
index d34829f5b4..0000000000
--- a/src/tools/icons/documenttypeicons.svg
+++ /dev/null
@@ -1,879 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="2104"
- height="4713"
- id="svg3397"
- version="1.1"
- inkscape:version="0.91 r13725"
- sodipodi:docname="documenttypeicons.svg"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <defs
- id="defs3399">
- <clipPath
- clipPathUnits="userSpaceOnUse"
- id="clipPath6024">
- <rect
- style="fill:none;stroke:none"
- id="rect6026"
- width="87"
- height="100"
- x="225"
- y="-5429" />
- </clipPath>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16"
- inkscape:cx="27.375271"
- inkscape:cy="533.28239"
- inkscape:document-units="px"
- inkscape:current-layer="layer3"
- showgrid="true"
- inkscape:window-width="2880"
- inkscape:window-height="1503"
- inkscape:window-x="-13"
- inkscape:window-y="527"
- inkscape:window-maximized="1"
- inkscape:snap-grids="true"
- showguides="true"
- inkscape:guide-bbox="true"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0">
- <inkscape:grid
- type="xygrid"
- id="grid3405"
- empspacing="1"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="false"
- dotted="true"
- originx="-16"
- originy="-7436" />
- <inkscape:grid
- type="xygrid"
- id="grid3407"
- empspacing="1"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="false"
- spacingx="4px"
- spacingy="4px"
- originx="-16"
- originy="-7436" />
- <sodipodi:guide
- position="266,-6734"
- orientation="0,1"
- id="guide4571" />
- <sodipodi:guide
- position="1,-7431"
- orientation="0,1"
- id="guide4573" />
- </sodipodi:namedview>
- <metadata
- id="metadata3402">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="qt-logos"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-16,11096.638)" />
- <g
- inkscape:groupmode="layer"
- id="layer3"
- inkscape:label="qt-appicons"
- transform="translate(-16,11069)">
- <g
- id="uifile_icon_16x16"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-1036)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_16x16.png"
- xlink:href="GenericDocumentIcon.iconset\icon_16x16.png"
- y="-5336"
- x="16"
- id="image3192"
- height="16"
- width="16" />
- <rect
- y="-5325"
- x="27.62998"
- height="2.9999692"
- width="2.3699887"
- id="rect4308-8"
- style="fill:#cd9aa3;fill-opacity:1;stroke:none" />
- <path
- id="path4291-2"
- d="m 19,-5324 -1,0.5082 1,0.4918 z"
- style="fill:#47484a;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- <g
- id="tinyqt"
- transform="translate(-50,-5859)">
- <path
- sodipodi:nodetypes="ccccccccc"
- inkscape:connector-curvature="0"
- id="path5089"
- d="m 78,532.5 -1.5,1.5 -6.5,0 0,-4.5 1.5,-1.5 3.484375,-0.004 0,0.99807 3,0.002 z"
- style="opacity:1;fill:#41cd52;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <g
- id="g4959"
- style="stroke:#ffffff;stroke-width:0.89999998;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- transform="translate(-2,-2)">
- <path
- sodipodi:nodetypes="zzzzz"
- inkscape:connector-curvature="0"
- id="path4945"
- d="m 74.5,531.5 c 1,0 1,0.28518 1,1.5 0,1.21481 0,1.5 -1,1.5 -1,0 -1,-0.30273 -1,-1.5 0,-1.19726 0,-1.5 1,-1.5 z"
- style="opacity:1;fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0"
- id="path4953"
- d="m 75.309359,534.5 1.198874,1.38951"
- style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- sodipodi:nodetypes="ccc"
- inkscape:connector-curvature="0"
- id="path4955"
- d="m 77.484375,530.9963 0,2.89815 c 0.0017,0.96521 0.846972,0.77673 1.515655,0.60555"
- style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0"
- id="path4957"
- d="m 76.5,532.5 2.5,0"
- style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- </g>
- </g>
- <path
- sodipodi:nodetypes="ccccc"
- inkscape:connector-curvature="0"
- id="path4286-4"
- d="m 21,-5325 -2,1 0,1 2,1 z"
- style="fill:#e1cab0;fill-opacity:1;stroke:none" />
- <rect
- y="-5325.0005"
- x="21"
- height="1.999"
- width="6"
- id="rect4296-5"
- style="fill:#f7ec07;fill-opacity:1;stroke:none" />
- <rect
- style="fill:#f7aa03;fill-opacity:1;stroke:none"
- id="rect4298-5"
- width="6"
- height="1.9999794"
- x="21"
- y="-5324" />
- <rect
- style="fill:#fbcb00;fill-opacity:1;stroke:none"
- id="rect4300-1"
- width="6"
- height="0.99998969"
- x="21"
- y="-5324" />
- <rect
- style="fill:#e4e6e7;fill-opacity:1;stroke:none"
- id="rect4302-7"
- width="1"
- height="0.99998969"
- x="26.98"
- y="-5325" />
- <rect
- y="-5323"
- x="26.98"
- height="0.99998969"
- width="1"
- id="rect4304-1"
- style="fill:#aeb1b3;fill-opacity:1;stroke:none" />
- <rect
- y="-5324"
- x="26.98"
- height="0.99998969"
- width="1"
- id="rect4306-1"
- style="fill:#c8c9c9;fill-opacity:1;stroke:none" />
- </g>
- <g
- id="uifile_icon_32x32"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-1036)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_32x32.png"
- xlink:href="GenericDocumentIcon.iconset\icon_32x32.png"
- y="-5352"
- x="32"
- id="image3203"
- height="32"
- width="32" />
- <text
- sodipodi:linespacing="125%"
- id="text3510"
- y="-6087.4551"
- x="39.699661"
- style="font-style:normal;font-weight:normal;font-size:4.71243429px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.1435936,0.87443651)"><tspan
- id="tspan3514"
- y="-6087.4551"
- x="39.699661"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold'">UI</tspan></text>
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_16x16.png"
- xlink:href="designer_icon_16x16.png"
- width="16"
- height="16"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image5338"
- x="40"
- y="-5344" />
- </g>
- <g
- id="uifile_icon_128x128"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-1036)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_128x128.png"
- xlink:href="GenericDocumentIcon.iconset\icon_128x128.png"
- y="-5448"
- x="200"
- id="image3214"
- height="128"
- width="128" />
- <g
- id="g4328"
- clip-path="url(#clipPath6024)"
- transform="translate(-1.6893267,0.99880497)">
- <use
- x="0"
- y="0"
- xlink:href="#text3510"
- id="use4326"
- transform="matrix(2.3826256,0,0,2.9999998,149.91558,10637.104)"
- width="1920"
- height="1080" />
- </g>
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_64x64.png"
- xlink:href="designer_icon_64x64.png"
- width="64"
- height="64"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image5457"
- x="232"
- y="-5416" />
- </g>
- <g
- id="uifile_icon_256x256"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-1036)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_256x256.png"
- xlink:href="GenericDocumentIcon.iconset\icon_256x256.png"
- y="-5576"
- x="328"
- id="image3225"
- height="256"
- width="256" />
- <use
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,-72,5315.173)"
- id="use4332"
- xlink:href="#g4328"
- y="0"
- x="0" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_128x128.png"
- xlink:href="designer_icon_128x128.png"
- y="-5512"
- x="392"
- id="image6519"
- style="image-rendering:optimizeSpeed"
- preserveAspectRatio="none"
- height="128"
- width="128" />
- </g>
- <g
- id="uifile_icon_512x512"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-1036)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_512x512.png"
- xlink:href="GenericDocumentIcon.iconset\icon_512x512.png"
- y="-5832"
- x="584"
- id="image3236"
- height="512"
- width="512" />
- <use
- height="1080"
- width="1920"
- transform="matrix(2,0,0,2,-72,5320.6089)"
- id="use4334"
- xlink:href="#use4332"
- y="0"
- x="0" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_256x256.png"
- xlink:href="designer_icon_256x256.png"
- width="256"
- height="256"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4519"
- x="712"
- y="-5704" />
- </g>
- <g
- transform="translate(0,-1560)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="uifile_icon_16x16@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_16x16@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_16x16@2x.png"
- width="32"
- height="32"
- id="image4080"
- x="32"
- y="-5352" />
- <text
- transform="scale(1.0533249,0.94937471)"
- xml:space="preserve"
- style="font-style:normal;font-weight:normal;font-size:4.34046173px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- x="43.44009"
- y="-5606.9463"
- id="text4082"
- sodipodi:linespacing="125%"><tspan
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold'"
- sodipodi:role="line"
- x="43.44009"
- y="-5606.9463"
- id="tspan4084">UI</tspan></text>
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_16x16.png"
- xlink:href="designer_icon_16x16.png"
- width="16"
- height="16"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image5349"
- x="40"
- y="-5344" />
- </g>
- <g
- id="uifile_icon_32x32@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_32x32@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_32x32@2x.png"
- y="-6944"
- x="64"
- id="image4120"
- height="64"
- width="64" />
- <use
- height="1"
- width="1"
- id="use4442"
- xlink:href="#g4328"
- y="0"
- x="0"
- transform="matrix(0.5,0,0,0.5,-35.699029,-4220.9125)" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_32x32.png"
- xlink:href="designer_icon_32x32.png"
- width="32"
- height="32"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4508"
- x="80"
- y="-6928" />
- </g>
- <g
- transform="translate(0,-1560)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="uifile_icon_128x128@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_128x128@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_128x128@2x.png"
- width="256"
- height="256"
- id="image4101"
- x="328"
- y="-5576" />
- <use
- x="0"
- y="0"
- xlink:href="#g4328"
- id="use4104"
- transform="matrix(2,0,0,2,-72,5315.173)"
- width="1920"
- height="1080" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_128x128.png"
- xlink:href="designer_icon_128x128.png"
- y="-5512"
- x="392"
- id="image6508"
- style="image-rendering:optimizeSpeed"
- preserveAspectRatio="none"
- height="128"
- width="128" />
- </g>
- <g
- transform="translate(0,-1560)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="uifile_icon_256x256@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_256x256@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_256x256@2x.png"
- width="512"
- height="512"
- id="image4108"
- x="584"
- y="-5832" />
- <use
- x="0"
- y="0"
- xlink:href="#use4332"
- id="use4110"
- transform="matrix(2,0,0,2,-72,5320.6089)"
- width="1920"
- height="1080" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_256x256.png"
- xlink:href="designer_icon_256x256.png"
- width="256"
- height="256"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4497"
- x="712"
- y="-5704" />
- </g>
- <g
- id="uifile_icon_512x512@2x"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_512x512@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_512x512@2x.png"
- y="-7905"
- x="1096"
- id="image4131"
- height="1024"
- width="1024" />
- <use
- height="1080"
- width="1920"
- transform="matrix(4,0,0,4,-216,14404.292)"
- id="use5743"
- xlink:href="#use4332"
- y="0"
- x="0" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\designer_icon_512x512.png"
- xlink:href="designer_icon_512x512.png"
- width="512"
- height="512"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4530"
- x="1352"
- y="-7649" />
- </g>
- <g
- id="qtcreator-project_icon_16x16"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-2600)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_16x16.png"
- xlink:href="GenericDocumentIcon.iconset\icon_16x16.png"
- width="16"
- height="16"
- id="image3192-0"
- x="16"
- y="-5336" />
- <use
- transform="translate(-0.015625,0.01594865)"
- x="0"
- y="0"
- xlink:href="#tinyqt"
- id="use4802"
- width="100%"
- height="100%" />
- </g>
- <g
- id="qtcreator-project_icon_32x32"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-2600)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_32x32.png"
- xlink:href="GenericDocumentIcon.iconset\icon_32x32.png"
- width="32"
- height="32"
- id="image3203-1"
- x="32"
- y="-5352" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_16x16.png"
- xlink:href="qtcreator_icon_16x16.png"
- width="16"
- height="16"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4838"
- x="40"
- y="-5344" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4"
- y="-5421.4678"
- x="40.865314"
- style="font-style:normal;font-weight:normal;font-size:5.29114771px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182219)"><tspan
- id="tspan3514-7"
- y="-5421.4678"
- x="40.865314"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold'">PRO</tspan></text>
- </g>
- <g
- id="qtcreator-project_icon_128x128"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-2600)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_128x128.png"
- xlink:href="GenericDocumentIcon.iconset\icon_128x128.png"
- width="128"
- height="128"
- id="image3214-5"
- x="200"
- y="-5448" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_64x64.png"
- xlink:href="qtcreator_icon_64x64.png"
- width="64"
- height="64"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4860"
- x="232"
- y="-5416" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-4"
- y="-5432.687"
- x="258.93604"
- style="font-style:normal;font-weight:normal;font-size:12.62162399px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182216)"><tspan
- id="tspan3514-7-2"
- y="-5432.687"
- x="258.93604"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold';text-align:center;text-anchor:middle">PRO</tspan></text>
- </g>
- <g
- id="qtcreator-project_icon_256x256"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-2600)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_256x256.png"
- xlink:href="GenericDocumentIcon.iconset\icon_256x256.png"
- width="256"
- height="256"
- id="image3225-9"
- x="328"
- y="-5576" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_128x128.png"
- xlink:href="qtcreator_icon_128x128.png"
- width="128"
- height="128"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4882"
- x="392"
- y="-5512" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-4-1"
- y="-5446.8779"
- x="447.18082"
- style="font-style:normal;font-weight:normal;font-size:25.24324799px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182216)"><tspan
- id="tspan3514-7-2-7"
- y="-5446.8779"
- x="447.18082"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold';text-align:center;text-anchor:middle">PRO</tspan></text>
- </g>
- <g
- id="qtcreator-project_icon_512x512"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- transform="translate(0,-2600)">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_512x512.png"
- xlink:href="GenericDocumentIcon.iconset\icon_512x512.png"
- width="512"
- height="512"
- id="image3236-4"
- x="584"
- y="-5832" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_256x256.png"
- xlink:href="qtcreator_icon_256x256.png"
- width="256"
- height="256"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4904"
- x="712"
- y="-5704" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-4-79"
- y="-5475.2593"
- x="823.67059"
- style="font-style:normal;font-weight:normal;font-size:50.48649597px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182216)"><tspan
- id="tspan3514-7-2-3"
- y="-5475.2593"
- x="823.67059"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold';text-align:center;text-anchor:middle">PRO</tspan></text>
- </g>
- <g
- transform="translate(0,-3124)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="qtcreator-project_icon_16x16@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_16x16@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_16x16@2x.png"
- y="-5352"
- x="32"
- id="image4080-8"
- height="32"
- width="32" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_16x16.png"
- xlink:href="qtcreator_icon_16x16.png"
- y="-5344"
- x="40"
- id="image4827"
- style="image-rendering:optimizeSpeed"
- preserveAspectRatio="none"
- height="16"
- width="16" />
- <use
- transform="translate(0.8660239,-0.03536906)"
- x="0"
- y="0"
- xlink:href="#text3510-4"
- id="use5027"
- width="100%"
- height="100%" />
- </g>
- <g
- transform="translate(0,-1564)"
- id="qtcreator-project_icon_32x32@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_32x32@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_32x32@2x.png"
- width="64"
- height="64"
- id="image4120-3"
- x="64"
- y="-6944" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_32x32.png"
- xlink:href="qtcreator_icon_32x32.png"
- width="32"
- height="32"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4849"
- x="80"
- y="-6928" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-9"
- y="-6551.9634"
- x="91.546913"
- style="font-style:normal;font-weight:normal;font-size:7.6833334px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(0.9513572,1.0511299)"><tspan
- id="tspan3514-7-8"
- y="-6551.9634"
- x="91.546913"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold'">PRO</tspan></text>
- </g>
- <g
- transform="translate(0,-3124)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="qtcreator-project_icon_128x128@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_128x128@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_128x128@2x.png"
- y="-5576"
- x="328"
- id="image4101-4"
- height="256"
- width="256" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_128x128.png"
- xlink:href="qtcreator_icon_128x128.png"
- width="128"
- height="128"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4871"
- x="392"
- y="-5512" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-4-7"
- y="-5446.8779"
- x="447.18088"
- style="font-style:normal;font-weight:normal;font-size:25.24324799px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182216)"><tspan
- id="tspan3514-7-2-5"
- y="-5446.8779"
- x="447.18088"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold';text-align:center;text-anchor:middle">PRO</tspan></text>
- </g>
- <g
- transform="translate(0,-3124)"
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- id="qtcreator-project_icon_256x256@2x">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_256x256@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_256x256@2x.png"
- y="-5832"
- x="584"
- id="image4108-5"
- height="512"
- width="512" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_256x256.png"
- xlink:href="qtcreator_icon_256x256.png"
- width="256"
- height="256"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4893"
- x="712"
- y="-5704" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-4-77"
- y="-5473.2202"
- x="823.67059"
- style="font-style:normal;font-weight:normal;font-size:50.48649597px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182216)"><tspan
- id="tspan3514-7-2-6"
- y="-5473.2202"
- x="823.67059"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold';text-align:center;text-anchor:middle">PRO</tspan></text>
- </g>
- <g
- transform="translate(0,-1564)"
- id="qtcreator-project_icon_512x512@2x"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\GenericDocumentIcon.iconset\icon_512x512@2x.png"
- xlink:href="GenericDocumentIcon.iconset\icon_512x512@2x.png"
- width="1024"
- height="1024"
- id="image4131-2"
- x="1096"
- y="-7905" />
- <image
- sodipodi:absref="C:\Users\aportale\dev\qtcreator-super\qtcreator\src\tools\icons\qtcreator_icon_512x512.png"
- xlink:href="qtcreator_icon_512x512.png"
- width="512"
- height="512"
- preserveAspectRatio="none"
- style="image-rendering:optimizeSpeed"
- id="image4915"
- x="1352"
- y="-7649" />
- <text
- sodipodi:linespacing="125%"
- id="text3510-4-4-11"
- y="-7121.9185"
- x="1576.6501"
- style="font-style:normal;font-weight:normal;font-size:100.97299194px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- transform="scale(1.0185144,0.98182216)"><tspan
- id="tspan3514-7-2-0"
- y="-7121.9185"
- x="1576.6501"
- sodipodi:role="line"
- style="font-weight:bold;-inkscape-font-specification:'Sans Bold';text-align:center;text-anchor:middle">PRO</tspan></text>
- </g>
- </g>
- <g
- inkscape:groupmode="layer"
- id="layer2"
- inkscape:label="app-icon-images"
- transform="translate(-16,11069)" />
-</svg>
diff --git a/src/tools/icons/exportapplicationicons.sh b/src/tools/icons/exportapplicationicons.sh
deleted file mode 100644
index eb74acbb90..0000000000
--- a/src/tools/icons/exportapplicationicons.sh
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/bin/sh
-
-# Copyright (C) 2016 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-# This script creates several application icon files by using
-# Inkscape to rasterize .svg items to .png, adding shadows via
-# imagemagick, creating .ico files via imagemagick and .icns
-# files via iconutil (OSX only).
-
-# optipng is required by this script
-if ! hash optipng 2>/dev/null; then
- echo "optipng was not found in PATH" >&2
- exit 1
-fi
-
-# Imagemagick convert is required by this script
-if ! hash convert 2>/dev/null; then
- echo "Imagemagick convert was not found in PATH" >&2
- exit 1
-fi
-
-cd `dirname $0`
-
-applicationNames="qtcreator designer linguist assistant qdbusviewer qmlviewer"
-applicationIconDimensions="16:0 24:0 32:1 48:1 64:1 128:2 256:3 512:7 1024:15"
-
-# Creating the list of svg IDs to export
-for applicationName in $applicationNames;\
-do
- for applicationIconDimension in $applicationIconDimensions;\
- do
- applicationIconSize=`echo $applicationIconDimension | awk -F: '{ print $1 }'`
- iconIDs="${iconIDs} ${applicationName}_icon_${applicationIconSize}x${applicationIconSize}"
- done
-done
-
-# Copying the logos for Qt Creator's sources. Without shadows!
-creatorLogoDir="logo"
-rm -rf $creatorLogoDir
-mkdir $creatorLogoDir
-for uiFileIconSize in 16 24 32 48 64 128 256 512;\
-do
- creatorLogoSource="qtcreator_icon_${uiFileIconSize}x${uiFileIconSize}.png"
- creatorLogoTargetDir="${creatorLogoDir}/${uiFileIconSize}"
- creatorLogoTarget="${creatorLogoTargetDir}/QtProject-qtcreator.png"
- optipng $creatorLogoSource -o 7 -strip all
- mkdir $creatorLogoTargetDir
- cp $creatorLogoSource $creatorLogoTarget
-done
-
-# Adding the shadows to the .png files
-for applicationName in $applicationNames;\
-do
- for applicationIconDimension in $applicationIconDimensions;\
- do
- iconSize=`echo $applicationIconDimension | awk -F: '{ print $1 }'`
- shadowSize=`echo $applicationIconDimension | awk -F: '{ print $2 }'`
- iconFile=${applicationName}_icon_${iconSize}x${iconSize}.png
- if [ "$shadowSize" != "0" ]
- then
- convert -page ${iconSize}x${iconSize} ${iconFile} \( +clone -background black -shadow 25x1+0+0 \) +swap -background none -flatten ${iconFile}
- convert -page ${iconSize}x${iconSize} ${iconFile} \( +clone -background black -shadow 40x${shadowSize}+0+${shadowSize} \) +swap -background none -flatten ${iconFile}
- fi
- done
-done
-
-# Creating the .ico files
-iconSizes="256 128 64 48 32 24 16"
-for applicationName in $applicationNames;\
-do
- icoFiles=""
- for iconSize in $iconSizes;\
- do
- icoFiles="$icoFiles ${applicationName}_icon_${iconSize}x${iconSize}.png"
- done
- convert ${icoFiles} ${applicationName}.ico
-done
-
-# Optimizing the .pngs
-for iconID in $iconIDs;\
-do
- optipng "${iconID}.png" -o 7 -strip all
-done
-
-# Preparing application .iconsets for the conversion to .icns
-for applicationName in $applicationNames;\
-do
- inconsetName=${applicationName}.iconset
- rm -rf $inconsetName
- mkdir $inconsetName
- cp ${applicationName}_icon_16x16.png ${inconsetName}/icon_16x16.png
- cp ${applicationName}_icon_32x32.png ${inconsetName}/icon_32x32.png
- cp ${applicationName}_icon_128x128.png ${inconsetName}/icon_128x128.png
- cp ${applicationName}_icon_256x256.png ${inconsetName}/icon_256x256.png
- cp ${applicationName}_icon_512x512.png ${inconsetName}/icon_512x512.png
- cp ${applicationName}_icon_32x32.png ${inconsetName}/icon_16x16@2x.png
- cp ${applicationName}_icon_64x64.png ${inconsetName}/icon_32x32@2x.png
- cp ${applicationName}_icon_256x256.png ${inconsetName}/icon_128x128@2x.png
- cp ${applicationName}_icon_512x512.png ${inconsetName}/icon_256x256@2x.png
- cp ${applicationName}_icon_1024x1024.png ${inconsetName}/icon_512x512@2x.png
-done
diff --git a/src/tools/icons/exportdocumenttypeicons.sh b/src/tools/icons/exportdocumenttypeicons.sh
deleted file mode 100644
index f0d8351c63..0000000000
--- a/src/tools/icons/exportdocumenttypeicons.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh
-
-# Copyright (C) 2016 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-# optipng is required by this script
-if ! hash optipng 2>/dev/null; then
- echo "optipng was not found in PATH" >&2
- exit 1
-fi
-
-cd `dirname $0`
-
-# Adding the icons for the OSX document type icon for .ui files
-for documentTypeName in "uifile" "qtcreator-project";\
-do
- inconsetName=${documentTypeName}.iconset
- rm -rf $inconsetName
- mkdir $inconsetName
- for iconSize in 16 32 128 256 512;\
- do
- iconShortID="icon_${iconSize}x${iconSize}"
- iconLongID="${documentTypeName}_${iconShortID}"
- for sizeVariation in "" "@2x";\
- do
- iconSourcePng="${iconLongID}${sizeVariation}.png"
- iconTargetPng="${inconsetName}/${iconShortID}${sizeVariation}.png"
- optipng $iconSourcePng -o 7 -strip all
- cp $iconSourcePng $iconTargetPng
- done
- done
-done
diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg
index 8e6a6baa2d..015498217b 100644
--- a/src/tools/icons/qtcreatoricons.svg
+++ b/src/tools/icons/qtcreatoricons.svg
@@ -653,6 +653,14 @@
width="100%"
height="100%"
transform="matrix(1.5,0,0,1.5,0,-242)" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#backgroundRect"
+ id="backgroundRect_12"
+ width="100%"
+ height="100%"
+ transform="matrix(0.75,0,0,0.75,-24,97)" />
<rect
y="452"
x="-16"
@@ -2965,7 +2973,7 @@
<use
height="100%"
width="100%"
- transform="matrix(-1,0,0,1,2336,0)"
+ transform="matrix(-1,0,0,1,2338,0)"
id="use6797"
xlink:href="#g6795"
y="0"
@@ -3148,6 +3156,64 @@
sodipodi:nodetypes="cccccc" />
</g>
<g
+ id="src/plugins/terminal/images/settingscategory_terminal">
+ <use
+ x="0"
+ y="0"
+ xlink:href="#backgroundRect_24"
+ id="use4838"
+ width="100%"
+ height="100%"
+ transform="translate(1115,64)"
+ style="display:inline;fill:none" />
+ <rect
+ style="fill:none;stroke:#000000;stroke-opacity:1.0"
+ id="rect2040"
+ width="15"
+ height="12"
+ x="1095.5"
+ y="481.5" />
+ <path
+ id="path5004"
+ style="fill:none;stroke:#000000;stroke-opacity:1.0"
+ d="m 1100,488.5 h 3 m -5.75,-5.25 2.25,2.25 -2.25,2.25" />
+ </g>
+ <g
+ id="src/plugins/copilot/images/settingscategory_copilot">
+ <use
+ x="0"
+ y="0"
+ xlink:href="#backgroundRect_24"
+ id="use4838-1"
+ width="100%"
+ height="100%"
+ transform="translate(1141,64)" />
+ <path
+ id="path3678"
+ d="m 1133.4931,482.92349 c 1.2309,-7.2e-4 2.6907,0.20509 3.4049,1.34287 0.832,1.39305 0.4718,3.51373 -1.0108,4.32399 -1.1526,0.68867 -2.5252,0.56507 -3.8119,0.57709 0,1.32708 0,2.65416 0,3.98124 -0.4296,0 -0.8592,0 -1.2888,0 0,-3.4084 0,-6.81679 0,-10.22519 0.9022,0 1.8044,0 2.7066,0 z m -0.1145,1.10271 c -0.4344,0 -0.8689,0 -1.3033,0 0,1.34618 0,2.69235 0,4.03853 1.1413,-0.0225 2.414,0.13991 3.3757,-0.60104 0.6576,-0.63013 0.6991,-1.73406 0.3022,-2.5149 -0.4777,-0.81039 -1.5253,-0.91406 -2.3746,-0.92259 z m -7.2608,-0.11456 c -1.2534,-0.0527 -2.5094,0.63954 -3.049,1.78756 -0.6219,1.30647 -0.619,2.86103 -0.2119,4.23045 0.3167,1.06743 1.2059,1.9627 2.3249,2.14365 1.1265,0.21498 2.2861,0.0103 3.3706,-0.31376 0,0.37235 0,0.74469 0,1.11704 -1.2924,0.46392 -2.7269,0.53819 -4.0672,0.2542 -1.5087,-0.32824 -2.6766,-1.61705 -3.0215,-3.09988 -0.4203,-1.74943 -0.3686,-3.7411 0.6344,-5.29133 0.8809,-1.36394 2.5502,-2.02668 4.1347,-1.9587 0.9342,0.01 1.8806,0.17957 2.7206,0.60089 -0.1719,0.3628 -0.3437,0.7256 -0.5156,1.0884 -0.7312,-0.3223 -1.5123,-0.57452 -2.32,-0.55852 z" />
+ </g>
+ <g
+ id="src/plugins/haskell/images/settingscategory_haskell">
+ <use
+ x="0"
+ y="0"
+ xlink:href="#backgroundRect_24"
+ id="use5402"
+ width="100%"
+ height="100%"
+ transform="translate(1166,64)" />
+ <path
+ style="fill:#000000;fill-opacity:1"
+ d="m 1150,482 h 2.5 l 8.67,13 h -2.5 l -3.085,-4.625 L 1152.51,495 h -2.5 l 4.335,-6.5 z"
+ id="path7771"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ id="path10150-5"
+ style="fill:#606060;fill-opacity:1"
+ d="m 1160,491 h 2 v -2 h -3.335 z m -2,-3 h 4 v -2 h -5.335 z m -9.5,-6 4.335,6.5 -4.335,6.5 h -2.5 l 4.335,-6.5 L 1146,482 Z"
+ sodipodi:nodetypes="ccccccccccccccccc" />
+ </g>
+ <g
id="src/plugins/valgrind/images/kcachegrind"
transform="translate(112)">
<use
@@ -3484,6 +3550,22 @@
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
+ <g
+ id="src/plugins/terminal/images/terminal">
+ <use
+ style="display:inline"
+ transform="translate(1948,132)"
+ height="100%"
+ width="100%"
+ id="use1987"
+ xlink:href="#backgroundRect"
+ y="0"
+ x="0" />
+ <path
+ id="path5004-3"
+ style="fill:none;stroke:#000000"
+ d="m 1936.25,571.25 2.25,2.25 -2.25,2.25 m -2.75,-6.25 h 8 v 8 h -8 z" />
+ </g>
</g>
<g
inkscape:groupmode="layer"
@@ -6453,6 +6535,27 @@
d="m 347,597.33936 0.60019,0.60645 3.49128,-3.49127 3.09695,3.09695 c 0.29088,-0.96173 0.20375,-2.0863 -0.1875,-3.07155 l 3.68179,-3.68179 0.55968,0.52841 0.75761,-0.75762 -4.51473,-4.51475 -0.80193,0.80194 0.53076,0.51509 -3.60205,3.60206 c -1.04768,-0.37523 -2.22553,-0.48941 -3.20237,-0.15625 l 3.0615,3.0615 z"
style="fill:#000000" />
</g>
+ <g
+ id="src/libs/utils/images/pinned_small">
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#backgroundRect_12"
+ id="use1988"
+ width="100%"
+ height="100%"
+ transform="translate(397,160)" />
+ <g
+ id="g6007"
+ transform="rotate(45,366.14645,589.64645)">
+ <path
+ style="fill:#000000;fill-opacity:1"
+ d="m 369.5,585 v 1 h -1 l 0.5,3 h 1 v 1 h -2.5 l -0.2,5 h -0.6 l -0.2,-5 H 364 v -1 h 1 l 0.5,-3 h -1 v -1 z"
+ id="path5401"
+ sodipodi:nodetypes="ccccccccccccccccc" />
+ </g>
+ </g>
</g>
<g
inkscape:groupmode="layer"
@@ -9251,6 +9354,20 @@
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
+ id="src/libs/utils/images/iconoverlay_close_small">
+ <rect
+ id="rect5336"
+ height="16"
+ width="16"
+ y="363"
+ x="649"
+ style="fill:#ffffff" />
+ <path
+ id="path4779-7"
+ style="stroke:#000000;stroke-width:2"
+ d="m 656.5,370.5 7,7 m -7,0 7,-7" />
+ </g>
+ <g
id="src/libs/utils/images/iconoverlay_add"
transform="translate(112)">
<rect
diff --git a/src/tools/perfparser b/src/tools/perfparser
-Subproject ac05977da7134f78d9b172271c524a32c317646
+Subproject 5444f96207616f922f3093e9d64bd6000f168c5
diff --git a/src/tools/process_stub/CMakeLists.txt b/src/tools/process_stub/CMakeLists.txt
new file mode 100644
index 0000000000..b8181ad836
--- /dev/null
+++ b/src/tools/process_stub/CMakeLists.txt
@@ -0,0 +1,12 @@
+
+add_qtc_executable(qtcreator_process_stub
+ DEPENDS Qt::Core Qt::Network
+ SOURCES
+ main.cpp
+)
+
+if (WIN32)
+ extend_qtc_executable(qtcreator_process_stub
+ DEFINES _UNICODE UNICODE _CRT_SECURE_NO_WARNINGS
+ )
+endif()
diff --git a/src/tools/process_stub/main.cpp b/src/tools/process_stub/main.cpp
new file mode 100644
index 0000000000..f606b06369
--- /dev/null
+++ b/src/tools/process_stub/main.cpp
@@ -0,0 +1,572 @@
+// 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 <QCommandLineParser>
+#include <QCoreApplication>
+#include <QDir>
+#include <QLocalSocket>
+#include <QLoggingCategory>
+#include <QMutex>
+#include <QProcess>
+#include <QSocketNotifier>
+#include <QThread>
+#include <QTimer>
+#include <QWinEventNotifier>
+
+#ifdef Q_OS_WIN
+#include <windows.h>
+#else
+#include <optional>
+#include <signal.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+#ifdef Q_OS_LINUX
+#include <sys/ptrace.h>
+#endif
+
+#include <iostream>
+
+Q_LOGGING_CATEGORY(log, "qtc.process_stub", QtWarningMsg);
+
+// Global variables
+
+QCommandLineParser commandLineParser;
+
+// The inferior command and arguments
+QStringList inferiorCmdAndArguments;
+// Whether to Suspend the inferior process on startup (to allow a debugger to attach)
+bool debugMode = false;
+// Whether to run in test mode (i.e. to start manually from the command line)
+bool testMode = false;
+// The control socket used to communicate with Qt Creator
+QLocalSocket controlSocket;
+// Environment variables to set for the inferior process
+std::optional<QStringList> environmentVariables;
+
+QProcess inferiorProcess;
+int inferiorId{0};
+
+#ifndef Q_OS_WIN
+
+#ifdef Q_OS_DARWIN
+// A memory mapped helper to retrieve the pid of the inferior process in debugMode
+static int *shared_child_pid = nullptr;
+#endif
+
+using OSSocketNotifier = QSocketNotifier;
+#else
+Q_PROCESS_INFORMATION *win_process_information = nullptr;
+using OSSocketNotifier = QWinEventNotifier;
+#endif
+// Helper to read a single character from stdin in testMode
+OSSocketNotifier *stdInNotifier;
+
+QThread processThread;
+
+// Helper to create the shared memory mapped segment
+void setupSharedPid();
+// Parses the command line, returns a status code in case of error
+std::optional<int> tryParseCommandLine(QCoreApplication &app);
+// Sets the working directory, returns a status code in case of error
+std::optional<int> trySetWorkingDir();
+// Reads the environment variables from the env file, returns a status code in case of error
+std::optional<int> readEnvFile();
+
+void setupControlSocket();
+void setupSignalHandlers();
+void startProcess(const QString &program, const QStringList &arguments, const QString &workingDir);
+void readKey();
+void sendSelfPid();
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ setupSharedPid();
+
+ auto error = tryParseCommandLine(a);
+ if (error)
+ return error.value();
+
+ qCInfo(log) << "Debug helper started: ";
+ qCInfo(log) << "Socket:" << commandLineParser.value("socket");
+ qCInfo(log) << "Inferior:" << inferiorCmdAndArguments.join(QChar::Space);
+ qCInfo(log) << "Working Directory" << commandLineParser.value("workingDir");
+ qCInfo(log) << "Env file:" << commandLineParser.value("envfile");
+ qCInfo(log) << "Mode:"
+ << QLatin1String(testMode ? "test | " : "")
+ + QLatin1String(debugMode ? "debug" : "run");
+
+ error = trySetWorkingDir();
+ if (error)
+ return error.value();
+
+ error = readEnvFile();
+ if (error)
+ return error.value();
+
+ if (testMode) {
+ sendSelfPid();
+ setupSignalHandlers();
+
+ startProcess(inferiorCmdAndArguments[0],
+ inferiorCmdAndArguments.mid(1),
+ commandLineParser.value("workingDir"));
+
+ if (debugMode) {
+ qDebug() << "Press 'c' to continue or 'k' to kill, followed by 'enter'";
+ readKey();
+ }
+
+ return a.exec();
+ }
+
+ setupControlSocket();
+
+ return a.exec();
+}
+
+void sendMsg(const QByteArray &msg)
+{
+ if (controlSocket.state() == QLocalSocket::ConnectedState) {
+ controlSocket.write(msg);
+ } else {
+ qDebug() << "MSG:" << msg;
+ }
+}
+
+void sendPid(int inferiorPid)
+{
+ sendMsg(QString("pid %1\n").arg(inferiorPid).toUtf8());
+}
+
+void sendThreadId(int inferiorThreadPid)
+{
+ sendMsg(QString("thread %1\n").arg(inferiorThreadPid).toUtf8());
+}
+
+void sendSelfPid()
+{
+ sendMsg(QString("spid %1\n").arg(QCoreApplication::applicationPid()).toUtf8());
+}
+
+void sendExit(int exitCode)
+{
+ sendMsg(QString("exit %1\n").arg(exitCode).toUtf8());
+}
+
+void sendCrash(int exitCode)
+{
+ sendMsg(QString("crash %1\n").arg(exitCode).toUtf8());
+}
+
+void sendErrChDir()
+{
+ sendMsg(QString("err:chdir %1\n").arg(errno).toUtf8());
+}
+
+void doExit(int exitCode)
+{
+ if (controlSocket.state() == QLocalSocket::ConnectedState && controlSocket.bytesToWrite())
+ controlSocket.waitForBytesWritten(1000);
+
+ if (!commandLineParser.value("wait").isEmpty()) {
+ std::cout << commandLineParser.value("wait").toStdString();
+ std::cin.get();
+ }
+
+ exit(exitCode);
+}
+
+void onInferiorFinished(int exitCode, QProcess::ExitStatus status)
+{
+ qCInfo(log) << "Inferior finished";
+
+ if (status == QProcess::CrashExit) {
+ sendCrash(exitCode);
+ doExit(exitCode);
+ } else {
+ sendExit(exitCode);
+ doExit(exitCode);
+ }
+}
+
+void onInferiorErrorOccurered(QProcess::ProcessError error)
+{
+ qCInfo(log) << "Inferior error: " << error << inferiorProcess.errorString();
+ sendCrash(inferiorProcess.exitCode());
+ doExit(1);
+}
+
+void onInferiorStarted()
+{
+ inferiorId = inferiorProcess.processId();
+ qCInfo(log) << "Inferior started ( pid:" << inferiorId << ")";
+#ifdef Q_OS_WIN
+ sendThreadId(win_process_information->dwThreadId);
+ sendPid(inferiorId);
+#elif defined(Q_OS_DARWIN)
+ // In debug mode we use the poll timer to send the pid.
+ if (!debugMode)
+ sendPid(inferiorId);
+#else
+ ptrace(PTRACE_DETACH, inferiorId, 0, SIGSTOP);
+ sendPid(inferiorId);
+#endif
+}
+
+void setupUnixInferior()
+{
+#ifndef Q_OS_WIN
+ if (debugMode) {
+ qCInfo(log) << "Debug mode enabled";
+#ifdef Q_OS_DARWIN
+ // We are using raise(SIGSTOP) to stop the child process, macOS does not support ptrace(...)
+ inferiorProcess.setChildProcessModifier([] {
+ // Let the parent know our pid ...
+ *shared_child_pid = getpid();
+ // Suspend ourselves ...
+ raise(SIGSTOP);
+ });
+#else
+ // PTRACE_TRACEME will stop execution of the child process as soon as execve is called.
+ inferiorProcess.setChildProcessModifier([] { ptrace(PTRACE_TRACEME, 0, 0, 0); });
+#endif
+ }
+#endif
+}
+
+void setupWindowsInferior()
+{
+#ifdef Q_OS_WIN
+ inferiorProcess.setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments *args) {
+ if (debugMode)
+ args->flags |= CREATE_SUSPENDED;
+ win_process_information = args->processInformation;
+ });
+#endif
+}
+
+void setupPidPollTimer()
+{
+#ifdef Q_OS_DARWIN
+ if (!debugMode)
+ return;
+
+ static QTimer pollPidTimer;
+
+ pollPidTimer.setInterval(1);
+ pollPidTimer.setSingleShot(false);
+ QObject::connect(&pollPidTimer, &QTimer::timeout, &pollPidTimer, [&] {
+ if (*shared_child_pid) {
+ qCInfo(log) << "Received pid during polling:" << *shared_child_pid;
+ inferiorId = *shared_child_pid;
+ sendPid(inferiorId);
+ pollPidTimer.stop();
+ munmap(shared_child_pid, sizeof(int));
+ } else {
+ qCDebug(log) << "Waiting for inferior to start...";
+ }
+ });
+ pollPidTimer.start();
+#endif
+}
+
+enum class Out { StdOut, StdErr };
+
+void writeToOut(const QByteArray &data, Out out)
+{
+#ifdef Q_OS_WIN
+ static const HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ static const HANDLE errHandle = GetStdHandle(STD_ERROR_HANDLE);
+ WriteFile(out == Out::StdOut ? outHandle : errHandle,
+ data.constData(),
+ data.size(),
+ nullptr,
+ nullptr);
+#else
+ auto fp = out == Out::StdOut ? stdout : stderr;
+ ::fwrite(data.constData(), 1, data.size(), fp);
+ ::fflush(fp);
+#endif
+}
+
+void startProcess(const QString &executable, const QStringList &arguments, const QString &workingDir)
+{
+ setupPidPollTimer();
+
+ qCInfo(log) << "Starting Inferior";
+
+ QObject::connect(&inferiorProcess,
+ &QProcess::finished,
+ QCoreApplication::instance(),
+ &onInferiorFinished);
+ QObject::connect(&inferiorProcess,
+ &QProcess::errorOccurred,
+ QCoreApplication::instance(),
+ &onInferiorErrorOccurered);
+ QObject::connect(&inferiorProcess,
+ &QProcess::started,
+ QCoreApplication::instance(),
+ &onInferiorStarted);
+
+ inferiorProcess.setProcessChannelMode(QProcess::ForwardedChannels);
+
+ if (!(testMode && debugMode))
+ inferiorProcess.setInputChannelMode(QProcess::ForwardedInputChannel);
+ inferiorProcess.setWorkingDirectory(workingDir);
+ inferiorProcess.setProgram(executable);
+ inferiorProcess.setArguments(arguments);
+
+ if (environmentVariables)
+ inferiorProcess.setEnvironment(*environmentVariables);
+
+ setupWindowsInferior();
+ setupUnixInferior();
+
+ inferiorProcess.start();
+}
+
+std::optional<int> readEnvFile()
+{
+ if (!commandLineParser.isSet("envfile"))
+ return std::nullopt;
+
+ const QString path = commandLineParser.value("envfile");
+ qCInfo(log) << "Reading env file: " << path << "...";
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qCWarning(log) << "Failed to open env file: " << path;
+ return 1;
+ }
+
+ environmentVariables = QStringList{};
+
+ while (!file.atEnd()) {
+ QByteArray data = file.readAll();
+ if (!data.isEmpty()) {
+ for (const auto &line : data.split('\0')) {
+ if (!line.isEmpty())
+ *environmentVariables << QString::fromUtf8(line);
+ }
+ }
+ }
+
+ qCDebug(log) << "Env: ";
+ for (const auto &env : *environmentVariables)
+ qCDebug(log) << env;
+
+ return std::nullopt;
+}
+
+#ifndef Q_OS_WIN
+void forwardSignal(int signum)
+{
+ qCDebug(log) << "SIGTERM received, terminating inferior...";
+ kill(inferiorId, signum);
+}
+#else
+static BOOL WINAPI ctrlHandler(DWORD dwCtrlType)
+{
+ if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
+ qCDebug(log) << "Terminate inferior...";
+ inferiorProcess.terminate();
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+void setupSignalHandlers()
+{
+#ifdef Q_OS_WIN
+ SetConsoleCtrlHandler(ctrlHandler, TRUE);
+#else
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+
+ act.sa_handler = SIG_IGN;
+ if (sigaction(SIGTTOU, &act, NULL)) {
+ qCWarning(log) << "sigaction SIGTTOU: " << strerror(errno);
+ doExit(3);
+ }
+
+ act.sa_handler = forwardSignal;
+ if (sigaction(SIGTERM, &act, NULL)) {
+ qCWarning(log) << "sigaction SIGTERM: " << strerror(errno);
+ doExit(3);
+ }
+
+ if (sigaction(SIGINT, &act, NULL)) {
+ qCWarning(log) << "sigaction SIGINT: " << strerror(errno);
+ doExit(3);
+ }
+
+ qCDebug(log) << "Signals set up";
+#endif
+}
+
+std::optional<int> tryParseCommandLine(QCoreApplication &app)
+{
+ commandLineParser.setApplicationDescription("Debug helper for QtCreator");
+ commandLineParser.addHelpOption();
+ commandLineParser.addOption(QCommandLineOption({"d", "debug"}, "Start inferior in debug mode"));
+ commandLineParser.addOption(QCommandLineOption({"t", "test"}, "Don't start the control socket"));
+ commandLineParser.addOption(
+ QCommandLineOption({"s", "socket"}, "Path to the unix socket", "socket"));
+ commandLineParser.addOption(
+ QCommandLineOption({"w", "workingDir"}, "Working directory for inferior", "workingDir"));
+ commandLineParser.addOption(QCommandLineOption({"v", "verbose"}, "Print debug messages"));
+ commandLineParser.addOption(QCommandLineOption({"e", "envfile"}, "Path to env file", "envfile"));
+ commandLineParser.addOption(
+ QCommandLineOption("wait",
+ "Message to display to the user while waiting for key press",
+ "waitmessage",
+ "Press enter to continue ..."));
+
+ commandLineParser.process(app);
+
+ inferiorCmdAndArguments = commandLineParser.positionalArguments();
+ debugMode = commandLineParser.isSet("debug");
+ testMode = commandLineParser.isSet("test");
+
+ if (!(commandLineParser.isSet("socket") || testMode) || inferiorCmdAndArguments.isEmpty()) {
+ commandLineParser.showHelp(1);
+ return 1;
+ }
+
+ if (commandLineParser.isSet("verbose"))
+ QLoggingCategory::setFilterRules("qtc.process_stub=true");
+
+ return std::nullopt;
+}
+
+std::optional<int> trySetWorkingDir()
+{
+ if (commandLineParser.isSet("workingDir")) {
+ if (!QDir::setCurrent(commandLineParser.value("workingDir"))) {
+ qCWarning(log) << "Failed to change working directory to: "
+ << commandLineParser.value("workingDir");
+ sendErrChDir();
+ return 1;
+ }
+ }
+
+ return std::nullopt;
+}
+
+void setupSharedPid()
+{
+#ifdef Q_OS_DARWIN
+ shared_child_pid = (int *) mmap(NULL,
+ sizeof *shared_child_pid,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS,
+ -1,
+ 0);
+ *shared_child_pid = 0;
+#endif
+}
+
+void onControlSocketConnected()
+{
+ qCInfo(log) << "Connected to control socket";
+
+ sendSelfPid();
+ setupSignalHandlers();
+
+ startProcess(inferiorCmdAndArguments[0],
+ inferiorCmdAndArguments.mid(1),
+ commandLineParser.value("workingDir"));
+}
+
+void resumeInferior()
+{
+ qCDebug(log) << "Continuing inferior... (" << inferiorId << ")";
+#ifdef Q_OS_WIN
+ ResumeThread(win_process_information->hThread);
+#else
+ kill(inferiorId, SIGCONT);
+#endif
+}
+
+void killInferior()
+{
+#ifdef Q_OS_WIN
+ inferiorProcess.kill();
+#else
+ kill(inferiorId, SIGKILL);
+#endif
+}
+
+void onControlSocketReadyRead()
+{
+ //k = kill, i = interrupt, c = continue, s = shutdown
+ QByteArray data = controlSocket.readAll();
+ for (auto ch : data) {
+ qCDebug(log) << "Received:" << ch;
+
+ switch (ch) {
+ case 'k': {
+ qCDebug(log) << "Killing inferior...";
+ killInferior();
+ break;
+ }
+#ifndef Q_OS_WIN
+ case 'i': {
+ qCDebug(log) << "Interrupting inferior...";
+ kill(inferiorId, SIGINT);
+ break;
+ }
+#endif
+ case 'c': {
+ resumeInferior();
+ break;
+ }
+ case 's': {
+ qCDebug(log) << "Shutting down...";
+ doExit(0);
+ break;
+ }
+ }
+ }
+}
+
+void onControlSocketErrorOccurred(QLocalSocket::LocalSocketError socketError)
+{
+ qCWarning(log) << "Control socket error:" << socketError;
+ doExit(1);
+}
+
+void setupControlSocket()
+{
+ QObject::connect(&controlSocket, &QLocalSocket::connected, &onControlSocketConnected);
+ QObject::connect(&controlSocket, &QLocalSocket::readyRead, &onControlSocketReadyRead);
+ QObject::connect(&controlSocket, &QLocalSocket::errorOccurred, &onControlSocketErrorOccurred);
+
+ qCInfo(log) << "Waiting for connection...";
+ controlSocket.connectToServer(commandLineParser.value("socket"));
+}
+
+void onStdInReadyRead()
+{
+ char ch;
+ std::cin >> ch;
+ if (ch == 'k') {
+ killInferior();
+ } else {
+ resumeInferior();
+ }
+}
+
+void readKey()
+{
+#ifdef Q_OS_WIN
+ stdInNotifier = new QWinEventNotifier(GetStdHandle(STD_INPUT_HANDLE));
+#else
+ stdInNotifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read);
+#endif
+ QObject::connect(stdInNotifier, &OSSocketNotifier::activated, &onStdInReadyRead);
+}
diff --git a/src/tools/process_stub/process_stub.qbs b/src/tools/process_stub/process_stub.qbs
new file mode 100644
index 0000000000..1fa1bc32a3
--- /dev/null
+++ b/src/tools/process_stub/process_stub.qbs
@@ -0,0 +1,10 @@
+import qbs 1.0
+
+QtcTool {
+ name: "qtcreator_process_stub"
+ consoleApplication: true
+
+ Depends { name: "Qt"; submodules: ["core", "network"]; }
+
+ files: [ "main.cpp" ]
+}
diff --git a/src/tools/processlauncher/CMakeLists.txt b/src/tools/processlauncher/CMakeLists.txt
index 9360d1e9f6..11149814ab 100644
--- a/src/tools/processlauncher/CMakeLists.txt
+++ b/src/tools/processlauncher/CMakeLists.txt
@@ -1,7 +1,8 @@
+set(LIBSDIR "${PROJECT_SOURCE_DIR}/src/libs")
set(UTILSDIR "${PROJECT_SOURCE_DIR}/src/libs/utils")
add_qtc_executable(qtcreator_processlauncher
- INCLUDES "${UTILSDIR}"
+ INCLUDES "${LIBSDIR}"
DEPENDS Qt::Core Qt::Network
DEFINES UTILS_STATIC_LIBRARY
SOURCES
diff --git a/src/tools/processlauncher/launchersockethandler.cpp b/src/tools/processlauncher/launchersockethandler.cpp
index 21167f8d77..c7879dc7cc 100644
--- a/src/tools/processlauncher/launchersockethandler.cpp
+++ b/src/tools/processlauncher/launchersockethandler.cpp
@@ -2,10 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "launchersockethandler.h"
-
#include "launcherlogging.h"
-#include "processreaper.h"
-#include "processutils.h"
+
+#include <utils/processreaper.h>
+#include <utils/processutils.h>
#include <QCoreApplication>
#include <QLocalSocket>
@@ -14,11 +14,11 @@
namespace Utils {
namespace Internal {
-class Process : public ProcessHelper
+class ProcessWithToken : public ProcessHelper
{
Q_OBJECT
public:
- Process(quintptr token, QObject *parent = nullptr) :
+ ProcessWithToken(quintptr token, QObject *parent = nullptr) :
ProcessHelper(parent), m_token(token) { }
quintptr token() const { return m_token; }
@@ -41,7 +41,7 @@ LauncherSocketHandler::LauncherSocketHandler(QString serverPath, QObject *parent
LauncherSocketHandler::~LauncherSocketHandler()
{
for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
- Process *p = it.value();
+ ProcessWithToken *p = it.value();
if (p->state() != QProcess::NotRunning)
logWarn(QStringLiteral("Shutting down while process %1 is running").arg(p->program()));
ProcessReaper::reap(p);
@@ -114,7 +114,7 @@ void LauncherSocketHandler::handleSocketClosed()
qApp->quit();
}
-void LauncherSocketHandler::handleProcessError(Process *process)
+void LauncherSocketHandler::handleProcessError(ProcessWithToken *process)
{
// In case of FailedToStart we won't receive finished signal, so we send the error
// packet and remove the process here and now. For all other errors we should expect
@@ -124,7 +124,7 @@ void LauncherSocketHandler::handleProcessError(Process *process)
handleProcessFinished(process);
}
-void LauncherSocketHandler::handleProcessStarted(Process *process)
+void LauncherSocketHandler::handleProcessStarted(ProcessWithToken *process)
{
ProcessStartedPacket packet(process->token());
packet.processId = process->processId();
@@ -132,21 +132,21 @@ void LauncherSocketHandler::handleProcessStarted(Process *process)
sendPacket(packet);
}
-void LauncherSocketHandler::handleReadyReadStandardOutput(Process *process)
+void LauncherSocketHandler::handleReadyReadStandardOutput(ProcessWithToken *process)
{
ReadyReadStandardOutputPacket packet(process->token());
packet.standardChannel = process->readAllStandardOutput();
sendPacket(packet);
}
-void LauncherSocketHandler::handleReadyReadStandardError(Process *process)
+void LauncherSocketHandler::handleReadyReadStandardError(ProcessWithToken *process)
{
ReadyReadStandardErrorPacket packet(process->token());
packet.standardChannel = process->readAllStandardError();
sendPacket(packet);
}
-void LauncherSocketHandler::handleProcessFinished(Process *process)
+void LauncherSocketHandler::handleProcessFinished(ProcessWithToken *process)
{
ProcessDonePacket packet(process->token());
packet.exitCode = process->exitCode();
@@ -162,7 +162,7 @@ void LauncherSocketHandler::handleProcessFinished(Process *process)
void LauncherSocketHandler::handleStartPacket()
{
- Process *& process = m_processes[m_packetParser.token()];
+ ProcessWithToken *& process = m_processes[m_packetParser.token()];
if (!process)
process = setupProcess(m_packetParser.token());
if (process->state() != QProcess::NotRunning) {
@@ -172,6 +172,7 @@ void LauncherSocketHandler::handleStartPacket()
const auto packet = LauncherPacket::extractPacket<StartProcessPacket>(
m_packetParser.token(),
m_packetParser.packetData());
+
process->setEnvironment(packet.env);
process->setWorkingDirectory(packet.workingDir);
// Forwarding is handled by the LauncherInterface
@@ -179,10 +180,10 @@ void LauncherSocketHandler::handleStartPacket()
? QProcess::MergedChannels : QProcess::SeparateChannels);
process->setStandardInputFile(packet.standardInputFile);
ProcessStartHandler *handler = process->processStartHandler();
+ handler->setWindowsSpecificStartupFlags(packet.belowNormalPriority,
+ packet.createConsoleOnWindows);
handler->setProcessMode(packet.processMode);
handler->setWriteData(packet.writeData);
- if (packet.belowNormalPriority)
- handler->setBelowNormalPriority();
handler->setNativeArguments(packet.nativeArguments);
if (packet.lowPriority)
process->setLowPriority();
@@ -196,7 +197,7 @@ void LauncherSocketHandler::handleStartPacket()
void LauncherSocketHandler::handleWritePacket()
{
- Process * const process = m_processes.value(m_packetParser.token());
+ ProcessWithToken * const process = m_processes.value(m_packetParser.token());
if (!process) {
logWarn("Got write request for unknown process");
return;
@@ -213,7 +214,7 @@ void LauncherSocketHandler::handleWritePacket()
void LauncherSocketHandler::handleControlPacket()
{
- Process * const process = m_processes.value(m_packetParser.token());
+ ProcessWithToken * const process = m_processes.value(m_packetParser.token());
if (!process) {
// This can happen when the process finishes on its own at about the same time the client
// sends the request. In this case the process was already deleted.
@@ -252,9 +253,9 @@ void LauncherSocketHandler::sendPacket(const LauncherPacket &packet)
m_socket->write(packet.serialize());
}
-Process *LauncherSocketHandler::setupProcess(quintptr token)
+ProcessWithToken *LauncherSocketHandler::setupProcess(quintptr token)
{
- const auto p = new Process(token, this);
+ const auto p = new ProcessWithToken(token, this);
connect(p, &QProcess::started, this, [this, p] { handleProcessStarted(p); });
connect(p, &QProcess::errorOccurred, this, [this, p] { handleProcessError(p); });
connect(p, &QProcess::finished, this, [this, p] { handleProcessFinished(p); });
@@ -271,7 +272,7 @@ void LauncherSocketHandler::removeProcess(quintptr token)
if (it == m_processes.constEnd())
return;
- Process *process = it.value();
+ ProcessWithToken *process = it.value();
m_processes.erase(it);
ProcessReaper::reap(process, process->reaperTimeout());
}
@@ -279,4 +280,4 @@ void LauncherSocketHandler::removeProcess(quintptr token)
} // namespace Internal
} // namespace Utils
-#include <launchersockethandler.moc>
+#include "launchersockethandler.moc"
diff --git a/src/tools/processlauncher/launchersockethandler.h b/src/tools/processlauncher/launchersockethandler.h
index e3ec8a65b8..05eefb09b8 100644
--- a/src/tools/processlauncher/launchersockethandler.h
+++ b/src/tools/processlauncher/launchersockethandler.h
@@ -3,7 +3,7 @@
#pragma once
-#include <launcherpackets.h>
+#include <utils/launcherpackets.h>
#include <QByteArray>
#include <QHash>
@@ -15,7 +15,7 @@ QT_END_NAMESPACE
namespace Utils {
namespace Internal {
-class Process;
+class ProcessWithToken;
class LauncherSocketHandler : public QObject
{
@@ -31,11 +31,11 @@ private:
void handleSocketError();
void handleSocketClosed();
- void handleProcessStarted(Process *process);
- void handleProcessError(Process *process);
- void handleProcessFinished(Process *process);
- void handleReadyReadStandardOutput(Process *process);
- void handleReadyReadStandardError(Process *process);
+ void handleProcessStarted(ProcessWithToken *process);
+ void handleProcessError(ProcessWithToken *process);
+ void handleProcessFinished(ProcessWithToken *process);
+ void handleReadyReadStandardOutput(ProcessWithToken *process);
+ void handleReadyReadStandardError(ProcessWithToken *process);
void handleStartPacket();
void handleWritePacket();
@@ -44,13 +44,13 @@ private:
void sendPacket(const LauncherPacket &packet);
- Process *setupProcess(quintptr token);
+ ProcessWithToken *setupProcess(quintptr token);
void removeProcess(quintptr token);
const QString m_serverPath;
QLocalSocket * const m_socket;
PacketParser m_packetParser;
- QHash<quintptr, Process *> m_processes;
+ QHash<quintptr, ProcessWithToken *> m_processes;
};
} // namespace Internal
diff --git a/src/tools/processlauncher/processlauncher-main.cpp b/src/tools/processlauncher/processlauncher-main.cpp
index f23ea88c92..0629ec3c6d 100644
--- a/src/tools/processlauncher/processlauncher-main.cpp
+++ b/src/tools/processlauncher/processlauncher-main.cpp
@@ -3,7 +3,8 @@
#include "launcherlogging.h"
#include "launchersockethandler.h"
-#include "singleton.h"
+
+#include <utils/singleton.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qscopeguard.h>
diff --git a/src/tools/processlauncher/processlauncher.qbs b/src/tools/processlauncher/processlauncher.qbs
index 757d50f9bb..2f03f7fba5 100644
--- a/src/tools/processlauncher/processlauncher.qbs
+++ b/src/tools/processlauncher/processlauncher.qbs
@@ -7,7 +7,7 @@ QtcTool {
Depends { name: "Qt.network" }
cpp.defines: base.concat("UTILS_STATIC_LIBRARY")
- cpp.includePaths: base.concat(pathToUtils)
+ cpp.includePaths: base.concat(pathToLibs)
Properties {
condition: qbs.targetOS.contains("windows")
@@ -24,6 +24,7 @@ QtcTool {
"processlauncher-main.cpp",
]
+ property string pathToLibs: sourceDirectory + "/../../libs"
property string pathToUtils: sourceDirectory + "/../../libs/utils"
Group {
name: "protocol sources"
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index 75c9dd9ce5..56a05aa000 100644
--- a/src/tools/qml2puppet/CMakeLists.txt
+++ b/src/tools/qml2puppet/CMakeLists.txt
@@ -29,8 +29,6 @@ else()
set(QT_VERSION_MAJOR ${Qt6_VERSION_MAJOR})
endif()
-configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES)
-
if (NOT TARGET QmlPuppetCommunication)
include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake)
endif()
@@ -46,28 +44,32 @@ add_qtc_executable(qml2puppet
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
SOURCES
qml2puppet/main.cpp
- qml2puppet/qmlbase.h qml2puppet/appmetadata.h
+ qml2puppet/qmlbase.h
+ qml2puppet/appmetadata.cpp qml2puppet/appmetadata.h
qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h
qmlpuppet.qrc
PROPERTIES
OUTPUT_NAME qml2puppet-${IDE_VERSION}
)
-if(TARGET qml2puppet)
+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_OUTPUT
+ 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_OUTPUT)
- file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA_OUTPUT)
+ if(NOT GIT_SHA)
+ file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA LIMIT_COUNT 1)
endif()
- add_definitions( -D GIT_SHA=${GIT_SHA_OUTPUT} )
+ set(IDE_REVISION_STR ${GIT_SHA})
+
+ configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES)
endif()
extend_qtc_executable(qml2puppet
@@ -201,7 +203,7 @@ extend_qtc_executable(qml2puppet
qmlprivategate.cpp qmlprivategate.h
)
-if(DEFINED MULTILANGUAGE_SUPPORT_SUBDIRECTORY AND Qt6_VERSION VERSION_GREATER_EQUAL 6.2.1)
+if (DEFINED MULTILANGUAGE_SUPPORT_SUBDIRECTORY AND Qt6_VERSION VERSION_GREATER_EQUAL 6.2.1)
add_subdirectory(${MULTILANGUAGE_SUPPORT_SUBDIRECTORY} multilanguagesupport_static_build)
endif()
@@ -222,7 +224,8 @@ extend_qtc_executable(qml2puppet
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.4.0)
extend_qtc_executable(qml2puppet
- DEFINES ENABLE_INTERNAL_QML_RUNTIME
+ # QT_QML_DEBUG is disabled in release builds, but is necessary to have the preview debug channel
+ DEFINES ENABLE_INTERNAL_QML_RUNTIME QT_QML_DEBUG
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/qml2puppet/runner
SOURCES_PREFIX qml2puppet/runner
SOURCES
@@ -251,3 +254,29 @@ extend_qtc_executable(qml2puppet
ENABLE_CRASHPAD
DEPENDS Crashpad::Crashpad
)
+
+# add application icon
+# IDE_LOGO_PATH in a default QtCreator build is empty, all icons are found by relative paths
+# So we can not use the icon then.
+if (TARGET qml2puppet AND NOT "${IDE_LOGO_PATH}" STREQUAL "")
+ if (WIN32)
+ set(RC_APPLICATION_NAME "${IDE_DISPLAY_NAME}")
+ set(RC_VERSION "${IDE_VERSION}.0")
+ set(RC_VERSION_STRING "${IDE_VERSION_DISPLAY}")
+ set(RC_COPYRIGHT "2008-${IDE_COPYRIGHT_YEAR} The Qt Company Ltd")
+
+ string(REPLACE " " "\\x20" RC_APPLICATION_NAME "${RC_APPLICATION_NAME}")
+ string(REPLACE " " "\\x20" RC_COPYRIGHT "${RC_COPYRIGHT}")
+ string(REPLACE "." "," RC_VERSION "${RC_VERSION}")
+
+ target_compile_definitions(qml2puppet PRIVATE
+ RC_APPLICATION_NAME=${RC_APPLICATION_NAME}
+ RC_VERSION=${RC_VERSION}
+ RC_VERSION_STRING=${RC_VERSION_STRING}
+ RC_COPYRIGHT=${RC_COPYRIGHT}
+ RC_ICON_PATH=${IDE_ICON_PATH}
+ )
+
+ target_sources(qml2puppet PRIVATE windows_application_icon/qml2puppet.rc)
+ endif()
+endif()
diff --git a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp
index f3f2b5c8a4..ffafb7bb00 100644
--- a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp
+++ b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp
@@ -367,11 +367,11 @@ void NodeInstanceClientProxy::startNanotrace(const StartNanotraceCommand &comman
std::string fullFilePath =
directory + std::string("/nanotrace_qmlpuppet_") + processName + std::string(".json");
- for (size_t i=0; i<processName.length(); ++i) {
- if (i==0 || processName[i]=='m')
- processName[i] = std::toupper(processName[i]);
+ for (int i=0; i<name.length(); ++i) {
+ if (i==0 || name[i]=='m')
+ name[i] = name.at(i).toUpper();
}
- processName = processName + std::string("Puppet");
+ processName = name.toStdString() + std::string("Puppet");
NANOTRACE_INIT(processName.c_str(), "MainThread", fullFilePath);
diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp b/src/tools/qml2puppet/qml2puppet/appmetadata.cpp
new file mode 100644
index 0000000000..1896e4db92
--- /dev/null
+++ b/src/tools/qml2puppet/qml2puppet/appmetadata.cpp
@@ -0,0 +1,51 @@
+// 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 <app/app_version.h>
+
+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
index d134135fd8..18eb650461 100644
--- a/src/tools/qml2puppet/qml2puppet/appmetadata.h
+++ b/src/tools/qml2puppet/qml2puppet/appmetadata.h
@@ -5,8 +5,6 @@
#include <QCommandLineParser>
#include <QLoggingCategory>
-#include <app/app_version.h>
-
// Common functions can be used in all QDS apps
namespace QDSMeta {
@@ -50,46 +48,8 @@ namespace AppInfo {
#define STRINGIFY_INTERNAL(x) #x
#define QDS_STRINGIFY(x) STRINGIFY_INTERNAL(x)
-inline 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(GIT_SHA)) << 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);
-}
-
-inline 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);
-}
+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 81cebaeeb1..af82ffca16 100644
--- a/src/tools/qml2puppet/qml2puppet/configcrashpad.h
+++ b/src/tools/qml2puppet/qml2puppet/configcrashpad.h
@@ -17,13 +17,21 @@
namespace {
#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
- bool startCrashpad()
+ bool startCrashpad(const QString& libexecPath, const QString& crashReportsPath)
{
using namespace crashpad;
// Cache directory that will store crashpad information and minidumps
- base::FilePath database(L"crashpad_reports");
- base::FilePath handler(L"crashpad_handler.exe");
+ QString databasePath = QDir::cleanPath(crashReportsPath);
+ QString handlerPath = QDir::cleanPath(libexecPath + "/crashpad_handler");
+ #ifdef Q_OS_WIN
+ handlerPath += ".exe";
+ base::FilePath database(databasePath.toStdWString());
+ base::FilePath handler(handlerPath.toStdWString());
+ #elif defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+ base::FilePath database(databasePath.toStdString());
+ base::FilePath handler(handlerPath.toStdString());
+ #endif
// URL used to submit minidumps to
std::string url(CRASHPAD_BACKEND_URL);
@@ -58,7 +66,7 @@ namespace {
QtSystemExceptionHandler systemExceptionHandler(libexecPath);
#endif //#ifdef ENABLE_QT_BREAKPAD
#else //#if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN)
- bool startCrashpad()
+ bool startCrashpad(const QString&, const QString&)
{
return false;
}
diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp
index f7e2757e60..8a2a244996 100644
--- a/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp
+++ b/src/tools/qml2puppet/qml2puppet/editor3d/selectionboxgeometry.cpp
@@ -131,6 +131,31 @@ QSSGRenderGraphObject *SelectionBoxGeometry::updateSpatialNode(QSSGRenderGraphOb
updateGeometry();
}
+#if QT_VERSION_MAJOR == 6 && QT_VERSION_MINOR == 4
+ if (!node) {
+ markAllDirty();
+ auto geometryNode = new QSSGRenderGeometry();
+ node = geometryNode;
+ emit geometryNodeDirty();
+
+ // This is a work around for the issue of incorrect geometry objects getting matched for
+ // cached mesh data in QSSGBufferManager::loadRenderMesh in QtQuick3D in 6.4 (see QDS-8843).
+ // Each setting of stride value increments the generation id of the geometry node.
+ // By incrementing generation id by different amounts, we can ensure QSSGBufferManager cache
+ // never matches wrong mesh data.
+ // The cache should be cleared of old objects after they are unused for one frame.
+ // With puppet reset & multiselection, we can create multiple new boxes per frame,
+ // so there's no count that really guarantees there are no invalid cache matches, but
+ // even just 8 should make them very unlikely.
+ // We start count at 12 here to avoid overlapping with gridgeometry cache ids.
+ static int dirtyCount = 12;
+ if (++dirtyCount > 20)
+ dirtyCount = 12;
+ for (int i = 0; i < dirtyCount; ++i)
+ geometryNode->setStride(stride());
+ }
+#endif
+
return QQuick3DGeometry::updateSpatialNode(node);
}
diff --git a/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp
index 04439d1c45..8fa29118d7 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/qmlpropertychangesnodeinstance.cpp
@@ -50,7 +50,20 @@ void QmlPropertyChangesNodeInstance::setPropertyBinding(const PropertyName &name
if (QmlPrivateGate::PropertyChanges::isNormalProperty(name)) { // 'restoreEntryValues', 'explicit'
ObjectNodeInstance::setPropertyBinding(name, expression);
} else {
+ QObject *state = QmlPrivateGate::PropertyChanges::stateObject(object());
+
+ ServerNodeInstance activeState = nodeInstanceServer()->activeStateInstance();
+ auto activeStateInstance = activeState.internalInstance();
+
+ const bool inState = activeStateInstance && activeStateInstance->object() == state;
+
+ if (inState)
+ activeState.deactivateState();
+
QmlPrivateGate::PropertyChanges::changeExpression(object(), name, expression);
+
+ if (inState)
+ activeState.activateState();
}
}
diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
index 99bd116158..f4da2fcc7b 100644
--- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
+++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp
@@ -853,9 +853,14 @@ void QuickItemNodeInstance::setPropertyVariant(const PropertyName &name, const Q
void QuickItemNodeInstance::setPropertyBinding(const PropertyName &name, const QString &expression)
{
static QList<PropertyName> anchorsTargets = {"anchors.top",
- "acnhors.bottom",
+ "anchors.bottom",
"anchors.left",
- "achors.right"};
+ "anchors.right",
+ "anchors.horizontalCenter",
+ "anchors.verticalCenter",
+ "anchors.fill",
+ "anchors.centerIn",
+ "anchors.baseline"};
if (ignoredProperties().contains(name))
return;
diff --git a/src/tools/qml2puppet/qml2puppet/qmlbase.h b/src/tools/qml2puppet/qml2puppet/qmlbase.h
index c73bd7fcc6..ae8995f489 100644
--- a/src/tools/qml2puppet/qml2puppet/qmlbase.h
+++ b/src/tools/qml2puppet/qml2puppet/qmlbase.h
@@ -40,10 +40,12 @@ public:
, m_args({argc, argv})
{
m_argParser.setApplicationDescription("QML Runtime Provider for QDS");
- m_argParser.addOptions({{"qml-puppet", "Run QML Puppet (default)"},
- {"qml-runtime", "Run QML Runtime"},
- {"appinfo", "Print build information"},
- {"test", "Run test mode"}});
+ m_argParser.addOption({"qml-puppet", "Run QML Puppet (default)"});
+#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"});
}
int run()
@@ -93,8 +95,13 @@ private:
QCommandLineOption optVers = m_argParser.addVersionOption();
if (!m_argParser.parse(m_coreApp->arguments())) {
- std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl
- << std::endl;
+ std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl;
+ if (m_argParser.errorText().contains("qml-runtime")) {
+ std::cout << "Note: --qml-runtime is only availabe when Qt is 6.4.x or higher"
+ << std::endl;
+ }
+ std::cout << std::endl;
+
m_argParser.showHelp(1);
} else if (m_argParser.isSet(optVers)) {
m_argParser.showVersion();
diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
index 4025cae41b..6d3fa2ce36 100644
--- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
+++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp
@@ -8,6 +8,7 @@
#include <sqlitelibraryinitializer.h>
#endif
+#include <app/app_version.h>
#include <qml2puppet/iconrenderer/iconrenderer.h>
#include <qml2puppet/import3d/import3d.h>
@@ -16,6 +17,7 @@
#include <QFileInfo>
#include <QQmlComponent>
#include <QQmlEngine>
+#include <QSettings>
#if defined(Q_OS_WIN) && defined(QT_NO_DEBUG)
#include <Windows.h>
@@ -75,6 +77,24 @@ void QmlPuppet::populateParser()
{"import3dAsset", "Import 3d asset.", "sourceAsset, outDir, importOptJson"}});
}
+// should be in sync with coreplugin/icore.cpp -> FilePath ICore::crashReportsPath()
+// and src\app\main.cpp
+QString crashReportsPath()
+{
+ QSettings settings(
+ QSettings::IniFormat,
+ QSettings::UserScope,
+ QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
+ QLatin1String(Core::Constants::IDE_CASED_ID));
+
+#if defined(Q_OS_MACOS)
+ return QFileInfo(settings.fileName()).path() + "/crashpad_reports";
+#else
+ return QCoreApplication::applicationDirPath()
+ + '/' + RELATIVE_LIBEXEC_PATH + "crashpad_reports";
+#endif
+}
+
void QmlPuppet::initQmlRunner()
{
if (m_coreApp->arguments().count() < 2
@@ -117,7 +137,8 @@ void QmlPuppet::initQmlRunner()
Import3D::import3D(sourceAsset, outDir, options);
}
- startCrashpad();
+ startCrashpad(QCoreApplication::applicationDirPath()
+ + '/' + RELATIVE_LIBEXEC_PATH, crashReportsPath());
new QmlDesigner::Qt5NodeInstanceClientProxy(m_coreApp.get());
diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
index b9a65bb6b2..7fd4e75dc2 100644
--- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
+++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp
@@ -193,6 +193,14 @@ static bool isDelegateModel(QObject *object)
return false;
}
+static bool isConnections(QObject *object)
+{
+ if (object)
+ return isMetaObjectofType(object->metaObject(), "QQmlConnections");
+
+ return false;
+}
+
// This is used in share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
QObject *createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context)
{
@@ -389,7 +397,7 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta
doComponentCompleteRecursive(child, nodeInstanceServer);
}
- if (!isQuickStyleItem(object) && !isDelegateModel(object)) {
+ if (!isQuickStyleItem(object) && !isDelegateModel(object) && !isConnections(object)) {
if (item) {
static_cast<QQmlParserStatus *>(item)->componentComplete();
} else {
diff --git a/src/tools/qml2puppet/windows_application_icon/qml2puppet.rc b/src/tools/qml2puppet/windows_application_icon/qml2puppet.rc
new file mode 100644
index 0000000000..b2d07923e3
--- /dev/null
+++ b/src/tools/qml2puppet/windows_application_icon/qml2puppet.rc
@@ -0,0 +1,30 @@
+#include <windows.h>
+
+#define STRINGIFY1(x) #x
+#define STRINGIFY(x) STRINGIFY1(x)
+
+#define ICON_PATH STRINGIFY(RC_ICON_PATH/qtcreator.ico)
+
+IDI_ICON1 ICON DISCARDABLE ICON_PATH
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION RC_VERSION
+ PRODUCTVERSION RC_VERSION
+{
+ BLOCK "StringFileInfo"
+ {
+ // U.S. English - Windows, Multilingual
+ BLOCK "040904E4"
+ {
+ VALUE "FileDescription", STRINGIFY(RC_APPLICATION_NAME)
+ VALUE "FileVersion", STRINGIFY(RC_VERSION_STRING)
+ VALUE "ProductName", STRINGIFY(RC_APPLICATION_NAME)
+ VALUE "ProductVersion", STRINGIFY(RC_VERSION_STRING)
+ VALUE "LegalCopyright", STRINGIFY(RC_COPYRIGHT)
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x409, 1252 // 1252 = 0x04E4
+ }
+}
diff --git a/src/tools/qtcdebugger/main.cpp b/src/tools/qtcdebugger/main.cpp
index 5aee7b6fe9..cbb4b935d7 100644
--- a/src/tools/qtcdebugger/main.cpp
+++ b/src/tools/qtcdebugger/main.cpp
@@ -520,7 +520,7 @@ int main(int argc, char *argv[])
}
if (debug)
qDebug() << "Mode=" << optMode << " PID=" << argProcessId << " Evt=" << argWinCrashEvent;
- bool ex = 0;
+ int ex = 0;
switch (optMode) {
case HelpMode:
usage(QCoreApplication::applicationFilePath(), errorMessage);
diff --git a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp
index ff1d503a6e..61d1fd4230 100644
--- a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp
+++ b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp
@@ -161,7 +161,7 @@ public:
QCoreApplication::quit();
});
- using namespace Utils::Layouting;
+ using namespace Layouting;
Column {
Row { m_iconLabel, m_introLabel, st },
@@ -205,12 +205,10 @@ CrashHandlerDialog::~CrashHandlerDialog()
bool CrashHandlerDialog::runDebuggerWhileBacktraceNotFinished()
{
// Check settings.
- QSettings settings(QSettings::IniFormat, QSettings::UserScope,
+ QSettings settings(QSettings::IniFormat,
+ QSettings::UserScope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(SettingsApplication));
- if (settings.value(QLatin1String(SettingsKeySkipWarningAbortingBacktrace), false).toBool())
- return true;
-
// Ask user.
const QString title = tr("Run Debugger And Abort Collecting Backtrace?");
const QString message = tr(
@@ -219,15 +217,18 @@ bool CrashHandlerDialog::runDebuggerWhileBacktraceNotFinished()
"<p>You have requested to run the debugger while collecting the backtrace was not "
"finished.</p>"
"</body></html>");
- const QString checkBoxText = tr("Do not &ask again.");
- bool checkBoxSetting = false;
- const QDialogButtonBox::StandardButton button = Utils::CheckableMessageBox::question(this,
- title, message, checkBoxText, &checkBoxSetting,
- QDialogButtonBox::Yes | QDialogButtonBox::No, QDialogButtonBox::No);
- if (checkBoxSetting)
- settings.setValue(QLatin1String(SettingsKeySkipWarningAbortingBacktrace), checkBoxSetting);
-
- return button == QDialogButtonBox::Yes;
+
+ const QMessageBox::StandardButton button
+ = Utils::CheckableMessageBox::question(this,
+ title,
+ message,
+ &settings,
+ QLatin1String(
+ SettingsKeySkipWarningAbortingBacktrace),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No);
+
+ return button == QMessageBox::Yes;
}
void CrashHandlerDialog::setToFinalState()
diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt
index 4adf618b84..937aa04087 100644
--- a/src/tools/sdktool/CMakeLists.txt
+++ b/src/tools/sdktool/CMakeLists.txt
@@ -66,33 +66,7 @@ add_qtc_library(sdktoolLib
rmqtoperation.cpp rmqtoperation.h
rmtoolchainoperation.cpp rmtoolchainoperation.h
settings.cpp settings.h
-)
-
-extend_qtc_library(sdktoolLib
- SOURCES_PREFIX "${UtilsSourcesDir}"
- PUBLIC_DEFINES UTILS_STATIC_LIBRARY
- SOURCES
- commandline.cpp commandline.h
- devicefileaccess.cpp devicefileaccess.h
- environment.cpp environment.h
- filepath.cpp filepath.h
- fileutils.cpp fileutils.h
- hostosinfo.cpp hostosinfo.h
- macroexpander.cpp macroexpander.h
- namevaluedictionary.cpp namevaluedictionary.h
- namevalueitem.cpp namevalueitem.h
- persistentsettings.cpp persistentsettings.h
- qtcassert.cpp qtcassert.h
- savefile.cpp savefile.h
- stringutils.cpp stringutils.h
-)
-
-extend_qtc_library(sdktoolLib CONDITION APPLE
- SOURCES_PREFIX "${UtilsSourcesDir}"
- SOURCES
- fileutils_mac.mm fileutils_mac.h
- PUBLIC_DEPENDS
- ${FWFoundation}
+ sdkpersistentsettings.cpp sdkpersistentsettings.h
)
if (MSVC)
@@ -120,7 +94,7 @@ add_qtc_executable(sdktool
main.cpp
)
-if (MSVC AND TARGET sdktool AND Qt5_VERSION VERSION_LESS 6.0.0)
+if (MSVC AND TARGET sdktool AND TARGET Qt5::Core)
# find out if Qt is static and set /MT if so
get_target_property(_input_type Qt5::Core TYPE)
if (${_input_type} STREQUAL "STATIC_LIBRARY")
diff --git a/src/tools/sdktool/addcmakeoperation.cpp b/src/tools/sdktool/addcmakeoperation.cpp
index 751db965e8..f97fcef2a6 100644
--- a/src/tools/sdktool/addcmakeoperation.cpp
+++ b/src/tools/sdktool/addcmakeoperation.cpp
@@ -4,13 +4,10 @@
#include "addcmakeoperation.h"
#include "addkeysoperation.h"
-#include "findkeyoperation.h"
#include "findvalueoperation.h"
#include "getoperation.h"
#include "rmkeysoperation.h"
-#include "settings.h"
-
#ifdef WITH_TESTS
#include <QTest>
#endif
@@ -205,7 +202,7 @@ QVariantMap AddCMakeData::addCMake(const QVariantMap &map) const
data << KeyValuePair({cm, ID_KEY}, QVariant(m_id));
data << KeyValuePair({cm, DISPLAYNAME_KEY}, QVariant(m_displayName));
data << KeyValuePair({cm, AUTODETECTED_KEY}, QVariant(true));
- data << KeyValuePair({cm, PATH_KEY}, Utils::FilePath::fromUserInput(m_path).toVariant());
+ data << KeyValuePair({cm, PATH_KEY}, QVariant(m_path));
KeyValuePairList extraList;
for (const KeyValuePair &pair : std::as_const(m_extra))
extraList << KeyValuePair(QStringList({cm}) << pair.key, pair.value);
diff --git a/src/tools/sdktool/adddebuggeroperation.cpp b/src/tools/sdktool/adddebuggeroperation.cpp
index c11b20ec81..69de58be05 100644
--- a/src/tools/sdktool/adddebuggeroperation.cpp
+++ b/src/tools/sdktool/adddebuggeroperation.cpp
@@ -222,8 +222,7 @@ QVariantMap AddDebuggerData::addDebugger(const QVariantMap &map) const
data << KeyValuePair(QStringList() << debugger << QLatin1String(ABIS), QVariant(m_abis));
data << KeyValuePair(QStringList() << debugger << QLatin1String(ENGINE_TYPE), QVariant(m_engine));
- data << KeyValuePair(QStringList() << debugger << QLatin1String(BINARY),
- Utils::FilePath::fromUserInput(m_binary).toSettings());
+ data << KeyValuePair(QStringList() << debugger << QLatin1String(BINARY), QVariant(m_binary));
data << KeyValuePair(QStringList() << QLatin1String(COUNT), QVariant(count + 1));
diff --git a/src/tools/sdktool/addkitoperation.cpp b/src/tools/sdktool/addkitoperation.cpp
index 14569ba839..4f0911d011 100644
--- a/src/tools/sdktool/addkitoperation.cpp
+++ b/src/tools/sdktool/addkitoperation.cpp
@@ -15,6 +15,7 @@
#include "settings.h"
+#include <QDir>
#include <QLoggingCategory>
#include <QRegularExpression>
@@ -685,8 +686,7 @@ QVariantMap AddKitData::addKit(const QVariantMap &map,
if (!m_buildDevice.isNull())
data << KeyValuePair({kit, DATA, BUILDDEVICE_ID}, QVariant(m_buildDevice));
if (!m_sysRoot.isNull())
- data << KeyValuePair({kit, DATA, SYSROOT},
- Utils::FilePath::fromUserInput(m_sysRoot).toSettings());
+ data << KeyValuePair({kit, DATA, SYSROOT}, QVariant(QDir::cleanPath(m_sysRoot)));
for (auto i = m_tcs.constBegin(); i != m_tcs.constEnd(); ++i)
data << KeyValuePair({kit, DATA, TOOLCHAIN, i.key()}, QVariant(i.value()));
if (!qtId.isNull())
diff --git a/src/tools/sdktool/addqtoperation.cpp b/src/tools/sdktool/addqtoperation.cpp
index 875e59d914..ee48c1e5ec 100644
--- a/src/tools/sdktool/addqtoperation.cpp
+++ b/src/tools/sdktool/addqtoperation.cpp
@@ -11,19 +11,16 @@
#include "settings.h"
-#include <utils/filepath.h>
-
#ifdef WITH_TESTS
#include <QTest>
#endif
+#include <QDir>
#include <QLoggingCategory>
#include <QRegularExpression>
Q_LOGGING_CATEGORY(log, "qtc.sdktool.operations.addqt", QtWarningMsg)
-using namespace Utils;
-
// Qt version file stuff:
const char PREFIX[] = "QtVersion.";
const char VERSION[] = "Version";
@@ -282,7 +279,7 @@ QVariantMap AddQtData::addQt(const QVariantMap &map) const
const QString qt = QString::fromLatin1(PREFIX) + QString::number(versionCount);
// Sanitize qmake path:
- FilePath saneQmake = FilePath::fromUserInput(m_qmake).cleanPath();
+ QString saneQmake = QDir::cleanPath(m_qmake);
// insert data:
KeyValuePairList data;
@@ -291,7 +288,7 @@ QVariantMap AddQtData::addQt(const QVariantMap &map) const
data << KeyValuePair(QStringList() << qt << QLatin1String(AUTODETECTED), QVariant(true));
data << KeyValuePair(QStringList() << qt << QLatin1String(AUTODETECTION_SOURCE), QVariant(sdkId));
- data << KeyValuePair(QStringList() << qt << QLatin1String(QMAKE), saneQmake.toSettings());
+ data << KeyValuePair(QStringList() << qt << QLatin1String(QMAKE), QVariant(saneQmake));
data << KeyValuePair(QStringList() << qt << QLatin1String(TYPE), QVariant(m_type));
data << KeyValuePair(QStringList() << qt << ABIS, QVariant(m_abis));
diff --git a/src/tools/sdktool/addtoolchainoperation.cpp b/src/tools/sdktool/addtoolchainoperation.cpp
index 1290711e56..74f4965cf8 100644
--- a/src/tools/sdktool/addtoolchainoperation.cpp
+++ b/src/tools/sdktool/addtoolchainoperation.cpp
@@ -4,13 +4,10 @@
#include "addtoolchainoperation.h"
#include "addkeysoperation.h"
-#include "findkeyoperation.h"
#include "findvalueoperation.h"
#include "getoperation.h"
#include "rmkeysoperation.h"
-#include "settings.h"
-
#include <iostream>
#ifdef WITH_TESTS
@@ -283,7 +280,7 @@ QVariantMap AddToolChainData::addToolChain(const QVariantMap &map) const
data << KeyValuePair({tc, LANGUAGE_KEY_V2}, QVariant(newLang));
data << KeyValuePair({tc, DISPLAYNAME}, QVariant(m_displayName));
data << KeyValuePair({tc, AUTODETECTED}, QVariant(true));
- data << KeyValuePair({tc, PATH}, Utils::FilePath::fromUserInput(m_path).toSettings());
+ data << KeyValuePair({tc, PATH}, QVariant(m_path));
data << KeyValuePair({tc, TARGET_ABI}, QVariant(m_targetAbi));
QVariantList abis;
const QStringList abiStrings = m_supportedAbis.split(',');
diff --git a/src/tools/sdktool/main.cpp b/src/tools/sdktool/main.cpp
index 4f28b64b9e..1136aa9d44 100644
--- a/src/tools/sdktool/main.cpp
+++ b/src/tools/sdktool/main.cpp
@@ -28,8 +28,10 @@
#include <app/app_version.h>
#include <iostream>
+#include <memory>
#include <QCoreApplication>
+#include <QDir>
#include <QLibraryInfo>
#include <QStringList>
@@ -60,10 +62,7 @@ void printHelp(const std::vector<std::unique_ptr<Operation>> &operations)
std::cout << " --sdkpath=PATH|-s PATH Set the path to the SDK files" << std::endl << std::endl;
std::cout << "Default sdkpath is \""
- << qPrintable(QDir::cleanPath(
- Utils::FilePath::fromString(QCoreApplication::applicationDirPath())
- .pathAppended(DATA_PATH)
- .toUserOutput()))
+ << qPrintable(QDir::cleanPath(QCoreApplication::applicationDirPath() + '/' + DATA_PATH))
<< "\"" << std::endl
<< std::endl;
@@ -105,7 +104,7 @@ int parseArguments(const QStringList &args, Settings *s,
// sdkpath
if (current.startsWith(QLatin1String("--sdkpath="))) {
- s->sdkPath = Utils::FilePath::fromString(current.mid(10));
+ s->sdkPath = current.mid(10);
continue;
}
if (current == QLatin1String("-s")) {
@@ -114,7 +113,7 @@ int parseArguments(const QStringList &args, Settings *s,
printHelp(operations);
return 1;
}
- s->sdkPath = Utils::FilePath::fromString(next);
+ s->sdkPath = next;
++i; // skip next;
continue;
}
diff --git a/src/tools/sdktool/operation.cpp b/src/tools/sdktool/operation.cpp
index ed673b9b78..7a81c3c7ad 100644
--- a/src/tools/sdktool/operation.cpp
+++ b/src/tools/sdktool/operation.cpp
@@ -4,8 +4,7 @@
#include "operation.h"
#include "settings.h"
-
-#include <utils/persistentsettings.h>
+#include "sdkpersistentsettings.h"
#include <QDir>
#include <QFile>
@@ -65,9 +64,9 @@ QVariantMap Operation::load(const QString &file)
QVariantMap map;
// Read values from original file:
- Utils::FilePath path = Settings::instance()->getPath(file);
- if (path.exists()) {
- Utils::PersistentSettingsReader reader;
+ QString path = Settings::instance()->getPath(file);
+ if (QFileInfo::exists(path)) {
+ SdkPersistentSettingsReader reader;
if (!reader.load(path))
return QVariantMap();
map = reader.restoreValues();
@@ -78,32 +77,32 @@ QVariantMap Operation::load(const QString &file)
bool Operation::save(const QVariantMap &map, const QString &file) const
{
- Utils::FilePath path = Settings::instance()->getPath(file);
+ QString path = Settings::instance()->getPath(file);
if (path.isEmpty()) {
std::cerr << "Error: No path found for " << qPrintable(file) << "." << std::endl;
return false;
}
- Utils::FilePath dirName = path.parentDir();
- QDir dir(dirName.toString());
+ QString dirName = QDir::cleanPath(path + "/..");
+ QDir dir(dirName);
if (!dir.exists() && !dir.mkpath(QLatin1String("."))) {
- std::cerr << "Error: Could not create directory " << qPrintable(dirName.toString())
+ std::cerr << "Error: Could not create directory " << qPrintable(dirName)
<< "." << std::endl;
return false;
}
- Utils::PersistentSettingsWriter writer(path, QLatin1String("QtCreator")
+ SdkPersistentSettingsWriter writer(path, QLatin1String("QtCreator")
+ file[0].toUpper() + file.mid(1));
QString errorMessage;
if (!writer.save(map, &errorMessage)) {
- std::cerr << "Error: Could not save settings " << qPrintable(path.toString())
+ std::cerr << "Error: Could not save settings " << qPrintable(path)
<< "." << std::endl;
return false;
}
- if (!path.setPermissions(QFile::ReadOwner | QFile::WriteOwner
+ if (!QFile(path).setPermissions(QFile::ReadOwner | QFile::WriteOwner
| QFile::ReadGroup | QFile::ReadOther)) {
- std::cerr << "Error: Could not set permissions for " << qPrintable(path.toString())
+ std::cerr << "Error: Could not set permissions for " << qPrintable(path)
<< "." << std::endl;
return false;
}
diff --git a/src/tools/sdktool/operation.h b/src/tools/sdktool/operation.h
index 1b43eb7b44..886ed38667 100644
--- a/src/tools/sdktool/operation.h
+++ b/src/tools/sdktool/operation.h
@@ -3,8 +3,6 @@
#pragma once
-#include <utils/fileutils.h>
-
#include <QStringList>
#include <QVariant>
diff --git a/src/tools/sdktool/sdkpersistentsettings.cpp b/src/tools/sdktool/sdkpersistentsettings.cpp
new file mode 100644
index 0000000000..4ca9b5e370
--- /dev/null
+++ b/src/tools/sdktool/sdkpersistentsettings.cpp
@@ -0,0 +1,871 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sdkpersistentsettings.h"
+
+#include <QCoreApplication>
+#include <QDataStream>
+#include <QDateTime>
+#include <QDebug>
+#include <QDir>
+#include <QRect>
+#include <QRegularExpression>
+#include <QStack>
+#include <QTextStream>
+#include <QXmlStreamAttributes>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QTemporaryFile>
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <sys/stat.h>
+#endif
+
+
+#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
+#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
+#define QTC_ASSERT_STRING(cond) writeAssertLocation(\
+ "\"" cond"\" in " __FILE__ ":" QTC_ASSERT_STRINGIFY(__LINE__))
+
+// The 'do {...} while (0)' idiom is not used for the main block here to be
+// able to use 'break' and 'continue' as 'actions'.
+
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
+#define QTC_CHECK(cond) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); } do {} while (0)
+#define QTC_GUARD(cond) ((Q_LIKELY(cond)) ? true : (QTC_ASSERT_STRING(#cond), false))
+
+void writeAssertLocation(const char *msg)
+{
+ const QByteArray time = QTime::currentTime().toString(Qt::ISODateWithMs).toLatin1();
+ static bool goBoom = qEnvironmentVariableIsSet("QTC_FATAL_ASSERTS");
+ if (goBoom)
+ qFatal("SOFT ASSERT [%s] made fatal: %s", time.data(), msg);
+ else
+ qDebug("SOFT ASSERT [%s]: %s", time.data(), msg);
+}
+
+static QFile::Permissions m_umask;
+
+class SdkSaveFile : public QFile
+{
+public:
+ explicit SdkSaveFile(const QString &filePath) : m_finalFilePath(filePath) {}
+ ~SdkSaveFile() override;
+
+ bool open(OpenMode flags = QIODevice::WriteOnly) override;
+
+ void rollback();
+ bool commit();
+
+ static void initializeUmask();
+
+private:
+ const QString m_finalFilePath;
+ std::unique_ptr<QTemporaryFile> m_tempFile;
+ bool m_finalized = true;
+};
+
+SdkSaveFile::~SdkSaveFile()
+{
+ if (!m_finalized)
+ rollback();
+}
+
+bool SdkSaveFile::open(OpenMode flags)
+{
+ if (m_finalFilePath.isEmpty()) {
+ qWarning("Save file path empty");
+ return false;
+ }
+
+ QFile ofi(m_finalFilePath);
+ // Check whether the existing file is writable
+ if (ofi.exists() && !ofi.open(QIODevice::ReadWrite)) {
+ setErrorString(ofi.errorString());
+ return false;
+ }
+
+ m_tempFile = std::make_unique<QTemporaryFile>(m_finalFilePath);
+ m_tempFile->setAutoRemove(false);
+ if (!m_tempFile->open())
+ return false;
+ setFileName(m_tempFile->fileName());
+
+ if (!QFile::open(flags))
+ return false;
+
+ m_finalized = false; // needs clean up in the end
+ if (ofi.exists()) {
+ setPermissions(ofi.permissions()); // Ignore errors
+ } else {
+ Permissions permAll = QFile::ReadOwner
+ | QFile::ReadGroup
+ | QFile::ReadOther
+ | QFile::WriteOwner
+ | QFile::WriteGroup
+ | QFile::WriteOther;
+
+ // set permissions with respect to the current umask
+ setPermissions(permAll & ~m_umask);
+ }
+
+ return true;
+}
+
+void SdkSaveFile::rollback()
+{
+ close();
+ if (m_tempFile)
+ m_tempFile->remove();
+ m_finalized = true;
+}
+
+static QString resolveSymlinks(QString current)
+{
+ int links = 16;
+ while (links--) {
+ const QFileInfo info(current);
+ if (!info.isSymLink())
+ return {};
+ current = info.symLinkTarget();
+ }
+ return current;
+}
+
+bool SdkSaveFile::commit()
+{
+ QTC_ASSERT(!m_finalized && m_tempFile, return false;);
+ m_finalized = true;
+
+ if (!flush()) {
+ close();
+ m_tempFile->remove();
+ return false;
+ }
+#ifdef Q_OS_WIN
+ FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(handle())));
+#elif _POSIX_SYNCHRONIZED_IO > 0
+ fdatasync(handle());
+#else
+ fsync(handle());
+#endif
+ close();
+ m_tempFile->close();
+ if (error() != NoError) {
+ m_tempFile->remove();
+ return false;
+ }
+
+ QString finalFileName = resolveSymlinks(m_finalFilePath);
+
+#ifdef Q_OS_WIN
+ // Release the file lock
+ m_tempFile.reset();
+ bool result = ReplaceFile(finalFileName.toStdWString().data(),
+ fileName().toStdWString().data(),
+ nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr);
+ if (!result) {
+ DWORD replaceErrorCode = GetLastError();
+ QString errorStr;
+ if (!QFile::exists(finalFileName)) {
+ // Replace failed because finalFileName does not exist, try rename.
+ if (!(result = rename(finalFileName)))
+ errorStr = errorString();
+ } else {
+ if (replaceErrorCode == ERROR_UNABLE_TO_REMOVE_REPLACED) {
+ // If we do not get the rights to remove the original final file we still might try
+ // to replace the file contents
+ result = MoveFileEx(fileName().toStdWString().data(),
+ finalFileName.toStdWString().data(),
+ MOVEFILE_COPY_ALLOWED
+ | MOVEFILE_REPLACE_EXISTING
+ | MOVEFILE_WRITE_THROUGH);
+ if (!result)
+ replaceErrorCode = GetLastError();
+ }
+ if (!result) {
+ wchar_t messageBuffer[256];
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, replaceErrorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ messageBuffer, sizeof(messageBuffer), nullptr);
+ errorStr = QString::fromWCharArray(messageBuffer);
+ }
+ }
+ if (!result) {
+ remove();
+ setErrorString(errorStr);
+ }
+ }
+
+ return result;
+#else
+ const QString backupName = finalFileName + '~';
+
+ // Back up current file.
+ // If it's opened by another application, the lock follows the move.
+ if (QFile::exists(finalFileName)) {
+ // Kill old backup. Might be useful if creator crashed before removing backup.
+ QFile::remove(backupName);
+ QFile finalFile(finalFileName);
+ if (!finalFile.rename(backupName)) {
+ m_tempFile->remove();
+ setErrorString(finalFile.errorString());
+ return false;
+ }
+ }
+
+ bool result = true;
+ if (!m_tempFile->rename(finalFileName)) {
+ // The case when someone else was able to create finalFileName after we've renamed it.
+ // Higher level call may try to save this file again but here we do nothing and
+ // return false while keeping the error string from last rename call.
+ const QString &renameError = m_tempFile->errorString();
+ m_tempFile->remove();
+ setErrorString(renameError);
+ QFile::rename(backupName, finalFileName); // rollback to backup if possible ...
+ return false; // ... or keep the backup copy at least
+ }
+
+ QFile::remove(backupName);
+
+ return result;
+#endif
+}
+
+void SdkSaveFile::initializeUmask()
+{
+#ifdef Q_OS_WIN
+ m_umask = QFile::WriteGroup | QFile::WriteOther;
+#else
+ // Get the current process' file creation mask (umask)
+ // umask() is not thread safe so this has to be done by single threaded
+ // application initialization
+ mode_t mask = umask(0); // get current umask
+ umask(mask); // set it back
+
+ m_umask = ((mask & S_IRUSR) ? QFile::ReadOwner : QFlags<QFile::Permission>())
+ | ((mask & S_IWUSR) ? QFile::WriteOwner : QFlags<QFile::Permission>())
+ | ((mask & S_IXUSR) ? QFile::ExeOwner : QFlags<QFile::Permission>())
+ | ((mask & S_IRGRP) ? QFile::ReadGroup : QFlags<QFile::Permission>())
+ | ((mask & S_IWGRP) ? QFile::WriteGroup : QFlags<QFile::Permission>())
+ | ((mask & S_IXGRP) ? QFile::ExeGroup : QFlags<QFile::Permission>())
+ | ((mask & S_IROTH) ? QFile::ReadOther : QFlags<QFile::Permission>())
+ | ((mask & S_IWOTH) ? QFile::WriteOther : QFlags<QFile::Permission>())
+ | ((mask & S_IXOTH) ? QFile::ExeOther : QFlags<QFile::Permission>());
+#endif
+}
+
+class SdkFileSaverBase
+{
+public:
+ SdkFileSaverBase() = default;
+ virtual ~SdkFileSaverBase() = default;
+
+ QString filePath() const { return m_filePath; }
+ bool hasError() const { return m_hasError; }
+ QString errorString() const { return m_errorString; }
+ virtual bool finalize();
+ bool finalize(QString *errStr);
+
+ bool write(const char *data, int len);
+ bool write(const QByteArray &bytes);
+ bool setResult(QTextStream *stream);
+ bool setResult(QDataStream *stream);
+ bool setResult(QXmlStreamWriter *stream);
+ bool setResult(bool ok);
+
+ QFile *file() { return m_file.get(); }
+
+protected:
+ std::unique_ptr<QFile> m_file;
+ QString m_filePath;
+ QString m_errorString;
+ bool m_hasError = false;
+};
+
+bool SdkFileSaverBase::finalize()
+{
+ m_file->close();
+ setResult(m_file->error() == QFile::NoError);
+ m_file.reset();
+ return !m_hasError;
+}
+
+bool SdkFileSaverBase::finalize(QString *errStr)
+{
+ if (finalize())
+ return true;
+ if (errStr)
+ *errStr = errorString();
+ return false;
+}
+
+bool SdkFileSaverBase::write(const char *data, int len)
+{
+ if (m_hasError)
+ return false;
+ return setResult(m_file->write(data, len) == len);
+}
+
+bool SdkFileSaverBase::write(const QByteArray &bytes)
+{
+ if (m_hasError)
+ return false;
+ return setResult(m_file->write(bytes) == bytes.count());
+}
+
+bool SdkFileSaverBase::setResult(bool ok)
+{
+ if (!ok && !m_hasError) {
+ if (!m_file->errorString().isEmpty()) {
+ m_errorString = QString("Cannot write file %1: %2")
+ .arg(m_filePath, m_file->errorString());
+ } else {
+ m_errorString = QString("Cannot write file %1. Disk full?")
+ .arg(m_filePath);
+ }
+ m_hasError = true;
+ }
+ return ok;
+}
+
+bool SdkFileSaverBase::setResult(QTextStream *stream)
+{
+ stream->flush();
+ return setResult(stream->status() == QTextStream::Ok);
+}
+
+bool SdkFileSaverBase::setResult(QDataStream *stream)
+{
+ return setResult(stream->status() == QDataStream::Ok);
+}
+
+bool SdkFileSaverBase::setResult(QXmlStreamWriter *stream)
+{
+ return setResult(!stream->hasError());
+}
+
+// SdkFileSaver
+
+class SdkFileSaver : public SdkFileSaverBase
+{
+public:
+ // QIODevice::WriteOnly is implicit
+ explicit SdkFileSaver(const QString &filePath, QIODevice::OpenMode mode = QIODevice::NotOpen);
+
+ bool finalize() override;
+
+private:
+ bool m_isSafe = false;
+};
+
+SdkFileSaver::SdkFileSaver(const QString &filePath, QIODevice::OpenMode mode)
+{
+ m_filePath = filePath;
+ // Workaround an assert in Qt -- and provide a useful error message, too:
+#ifdef Q_OS_WIN
+ // Taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ static const QStringList reservedNames
+ = {"CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
+ const QString fn = QFileInfo(filePath).baseName().toUpper();
+ if (reservedNames.contains(fn)) {
+ m_errorString = QString("%1: Is a reserved filename on Windows. Cannot save.").arg(filePath);
+ m_hasError = true;
+ return;
+ }
+#endif
+
+ if (mode & (QIODevice::ReadOnly | QIODevice::Append)) {
+ m_file.reset(new QFile{filePath});
+ m_isSafe = false;
+ } else {
+ m_file.reset(new SdkSaveFile(filePath));
+ m_isSafe = true;
+ }
+ if (!m_file->open(QIODevice::WriteOnly | mode)) {
+ QString err = QFileInfo::exists(filePath) ?
+ QString("Cannot overwrite file %1: %2") : QString("Cannot create file %1: %2");
+ m_errorString = err.arg(filePath, m_file->errorString());
+ m_hasError = true;
+ }
+}
+
+bool SdkFileSaver::finalize()
+{
+ if (!m_isSafe)
+ return SdkFileSaverBase::finalize();
+
+ auto sf = static_cast<SdkSaveFile *>(m_file.get());
+ if (m_hasError) {
+ if (sf->isOpen())
+ sf->rollback();
+ } else {
+ setResult(sf->commit());
+ }
+ m_file.reset();
+ return !m_hasError;
+}
+
+
+// Read and write rectangle in X11 resource syntax "12x12+4+3"
+static QString rectangleToString(const QRect &r)
+{
+ QString result;
+ QTextStream str(&result);
+ str << r.width() << 'x' << r.height();
+ if (r.x() >= 0)
+ str << '+';
+ str << r.x();
+ if (r.y() >= 0)
+ str << '+';
+ str << r.y();
+ return result;
+}
+
+static QRect stringToRectangle(const QString &v)
+{
+ static QRegularExpression pattern("^(\\d+)x(\\d+)([-+]\\d+)([-+]\\d+)$");
+ Q_ASSERT(pattern.isValid());
+ const QRegularExpressionMatch match = pattern.match(v);
+ return match.hasMatch() ?
+ QRect(QPoint(match.captured(3).toInt(), match.captured(4).toInt()),
+ QSize(match.captured(1).toInt(), match.captured(2).toInt())) :
+ QRect();
+}
+
+/*!
+ \class SdkPersistentSettingsReader
+
+ \note This is aQString based fork of Utils::PersistentSettigsReader
+
+ \brief The SdkPersistentSettingsReader class reads a QVariantMap of arbitrary,
+ nested data structures from an XML file.
+
+ Handles all string-serializable simple types and QVariantList and QVariantMap. Example:
+ \code
+<qtcreator>
+ <data>
+ <variable>ProjectExplorer.Project.ActiveTarget</variable>
+ <value type="int">0</value>
+ </data>
+ <data>
+ <variable>ProjectExplorer.Project.EditorSettings</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+ </valuemap>
+ </data>
+ \endcode
+
+ When parsing the structure, a parse stack of ParseValueStackEntry is used for each
+ <data> element. ParseValueStackEntry is a variant/union of:
+ \list
+ \li simple value
+ \li map
+ \li list
+ \endlist
+
+ You can register string-serialize functions for custom types by registering them in the Qt Meta
+ type system. Example:
+ \code
+ QMetaType::registerConverter(&MyCustomType::toString);
+ QMetaType::registerConverter<QString, MyCustomType>(&myCustomTypeFromString);
+ \endcode
+
+ When entering a value element ( \c <value> / \c <valuelist> , \c <valuemap> ), entry is pushed
+ accordingly. When leaving the element, the QVariant-value of the entry is taken off the stack
+ and added to the stack entry below (added to list or inserted into map). The first element
+ of the stack is the value of the <data> element.
+
+ \sa SdkPersistentSettingsWriter
+*/
+
+struct Context // Basic context containing element name string constants.
+{
+ Context() {}
+ const QString qtCreatorElement = QString("qtcreator");
+ const QString dataElement = QString("data");
+ const QString variableElement = QString("variable");
+ const QString typeAttribute = QString("type");
+ const QString valueElement = QString("value");
+ const QString valueListElement = QString("valuelist");
+ const QString valueMapElement = QString("valuemap");
+ const QString keyAttribute = QString("key");
+};
+
+struct ParseValueStackEntry
+{
+ explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = QString()) : type(t), key(k) {}
+ explicit ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k);
+
+ QVariant value() const;
+ void addChild(const QString &key, const QVariant &v);
+
+ QVariant::Type type;
+ QString key;
+ QVariant simpleValue;
+ QVariantList listValue;
+ QVariantMap mapValue;
+};
+
+ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k) :
+ type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue)
+{
+ QTC_ASSERT(simpleValue.isValid(), return);
+}
+
+QVariant ParseValueStackEntry::value() const
+{
+ switch (type) {
+ case QVariant::Invalid:
+ return QVariant();
+ case QVariant::Map:
+ return QVariant(mapValue);
+ case QVariant::List:
+ return QVariant(listValue);
+ default:
+ break;
+ }
+ return simpleValue;
+}
+
+void ParseValueStackEntry::addChild(const QString &key, const QVariant &v)
+{
+ switch (type) {
+ case QVariant::Map:
+ mapValue.insert(key, v);
+ break;
+ case QVariant::List:
+ listValue.push_back(v);
+ break;
+ default:
+ qWarning() << "ParseValueStackEntry::Internal error adding " << key << v << " to "
+ << QVariant::typeToName(type) << value();
+ break;
+ }
+}
+
+class ParseContext : public Context
+{
+public:
+ QVariantMap parse(const QString &file);
+
+private:
+ enum Element { QtCreatorElement, DataElement, VariableElement,
+ SimpleValueElement, ListValueElement, MapValueElement, UnknownElement };
+
+ Element element(const QStringView &r) const;
+ static inline bool isValueElement(Element e)
+ { return e == SimpleValueElement || e == ListValueElement || e == MapValueElement; }
+ QVariant readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttributes &attributes) const;
+
+ bool handleStartElement(QXmlStreamReader &r);
+ bool handleEndElement(const QStringView &name);
+
+ static QString formatWarning(const QXmlStreamReader &r, const QString &message);
+
+ QStack<ParseValueStackEntry> m_valueStack;
+ QVariantMap m_result;
+ QString m_currentVariableName;
+};
+
+static QByteArray fileContents(const QString &path)
+{
+ QFile f(path);
+ if (!f.exists())
+ return {};
+
+ if (!f.open(QFile::ReadOnly))
+ return {};
+
+ return f.readAll();
+}
+
+QVariantMap ParseContext::parse(const QString &file)
+{
+ QXmlStreamReader r(fileContents(file));
+
+ m_result.clear();
+ m_currentVariableName.clear();
+
+ while (!r.atEnd()) {
+ switch (r.readNext()) {
+ case QXmlStreamReader::StartElement:
+ if (handleStartElement(r))
+ return m_result;
+ break;
+ case QXmlStreamReader::EndElement:
+ if (handleEndElement(r.name()))
+ return m_result;
+ break;
+ case QXmlStreamReader::Invalid:
+ qWarning("Error reading %s:%d: %s", qPrintable(file),
+ int(r.lineNumber()), qPrintable(r.errorString()));
+ return QVariantMap();
+ default:
+ break;
+ } // switch token
+ } // while (!r.atEnd())
+ return m_result;
+}
+
+bool ParseContext::handleStartElement(QXmlStreamReader &r)
+{
+ const QStringView name = r.name();
+ const Element e = element(name);
+ if (e == VariableElement) {
+ m_currentVariableName = r.readElementText();
+ return false;
+ }
+ if (!ParseContext::isValueElement(e))
+ return false;
+
+ const QXmlStreamAttributes attributes = r.attributes();
+ const QString key = attributes.hasAttribute(keyAttribute) ?
+ attributes.value(keyAttribute).toString() : QString();
+ switch (e) {
+ case SimpleValueElement: {
+ // This reads away the end element, so, handle end element right here.
+ const QVariant v = readSimpleValue(r, attributes);
+ if (!v.isValid()) {
+ qWarning() << ParseContext::formatWarning(r, QString::fromLatin1("Failed to read element \"%1\".").arg(name.toString()));
+ return false;
+ }
+ m_valueStack.push_back(ParseValueStackEntry(v, key));
+ return handleEndElement(name);
+ }
+ case ListValueElement:
+ m_valueStack.push_back(ParseValueStackEntry(QVariant::List, key));
+ break;
+ case MapValueElement:
+ m_valueStack.push_back(ParseValueStackEntry(QVariant::Map, key));
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool ParseContext::handleEndElement(const QStringView &name)
+{
+ const Element e = element(name);
+ if (ParseContext::isValueElement(e)) {
+ QTC_ASSERT(!m_valueStack.isEmpty(), return true);
+ const ParseValueStackEntry top = m_valueStack.pop();
+ if (m_valueStack.isEmpty()) { // Last element? -> Done with that variable.
+ QTC_ASSERT(!m_currentVariableName.isEmpty(), return true);
+ m_result.insert(m_currentVariableName, top.value());
+ m_currentVariableName.clear();
+ return false;
+ }
+ m_valueStack.top().addChild(top.key, top.value());
+ }
+ return e == QtCreatorElement;
+}
+
+QString ParseContext::formatWarning(const QXmlStreamReader &r, const QString &message)
+{
+ QString result = QLatin1String("Warning reading ");
+ if (const QIODevice *device = r.device())
+ if (const auto file = qobject_cast<const QFile *>(device))
+ result += QDir::toNativeSeparators(file->fileName()) + QLatin1Char(':');
+ result += QString::number(r.lineNumber());
+ result += QLatin1String(": ");
+ result += message;
+ return result;
+}
+
+ParseContext::Element ParseContext::element(const QStringView &r) const
+{
+ if (r == valueElement)
+ return SimpleValueElement;
+ if (r == valueListElement)
+ return ListValueElement;
+ if (r == valueMapElement)
+ return MapValueElement;
+ if (r == qtCreatorElement)
+ return QtCreatorElement;
+ if (r == dataElement)
+ return DataElement;
+ if (r == variableElement)
+ return VariableElement;
+ return UnknownElement;
+}
+
+QVariant ParseContext::readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttributes &attributes) const
+{
+ // Simple value
+ const QStringView type = attributes.value(typeAttribute);
+ const QString text = r.readElementText();
+ if (type == QLatin1String("QChar")) { // Workaround: QTBUG-12345
+ QTC_ASSERT(text.size() == 1, return QVariant());
+ return QVariant(QChar(text.at(0)));
+ }
+ if (type == QLatin1String("QRect")) {
+ const QRect rectangle = stringToRectangle(text);
+ return rectangle.isValid() ? QVariant(rectangle) : QVariant();
+ }
+ QVariant value;
+ value.setValue(text);
+ value.convert(QMetaType::type(type.toLatin1().constData()));
+ return value;
+}
+
+// =================================== SdkPersistentSettingsReader
+
+SdkPersistentSettingsReader::SdkPersistentSettingsReader() = default;
+
+QVariant SdkPersistentSettingsReader::restoreValue(const QString &variable, const QVariant &defaultValue) const
+{
+ if (m_valueMap.contains(variable))
+ return m_valueMap.value(variable);
+ return defaultValue;
+}
+
+QVariantMap SdkPersistentSettingsReader::restoreValues() const
+{
+ return m_valueMap;
+}
+
+bool SdkPersistentSettingsReader::load(const QString &fileName)
+{
+ m_valueMap.clear();
+
+ if (QFileInfo(fileName).size() == 0) // skip empty files
+ return false;
+
+ ParseContext ctx;
+ m_valueMap = ctx.parse(fileName);
+ return true;
+}
+
+/*!
+ \class SdkPersistentSettingsWriter
+
+ \note This is a fork of Utils::PersistentSettingsWriter
+
+ \brief The SdkPersistentSettingsWriter class serializes a QVariantMap of
+ arbitrary, nested data structures to an XML file.
+ \sa SdkPersistentSettingsReader
+*/
+
+static void writeVariantValue(QXmlStreamWriter &w, const Context &ctx,
+ const QVariant &variant, const QString &key = QString())
+{
+ switch (static_cast<int>(variant.type())) {
+ case static_cast<int>(QVariant::StringList):
+ case static_cast<int>(QVariant::List): {
+ w.writeStartElement(ctx.valueListElement);
+ w.writeAttribute(ctx.typeAttribute, QLatin1String(QVariant::typeToName(QVariant::List)));
+ if (!key.isEmpty())
+ w.writeAttribute(ctx.keyAttribute, key);
+ const QList<QVariant> list = variant.toList();
+ for (const QVariant &var : list)
+ writeVariantValue(w, ctx, var);
+ w.writeEndElement();
+ break;
+ }
+ case static_cast<int>(QVariant::Map): {
+ w.writeStartElement(ctx.valueMapElement);
+ w.writeAttribute(ctx.typeAttribute, QLatin1String(QVariant::typeToName(QVariant::Map)));
+ if (!key.isEmpty())
+ w.writeAttribute(ctx.keyAttribute, key);
+ const QVariantMap varMap = variant.toMap();
+ const QVariantMap::const_iterator cend = varMap.constEnd();
+ for (QVariantMap::const_iterator i = varMap.constBegin(); i != cend; ++i)
+ writeVariantValue(w, ctx, i.value(), i.key());
+ w.writeEndElement();
+ }
+ break;
+ case static_cast<int>(QMetaType::QObjectStar): // ignore QObjects!
+ case static_cast<int>(QMetaType::VoidStar): // ignore void pointers!
+ break;
+ default:
+ w.writeStartElement(ctx.valueElement);
+ w.writeAttribute(ctx.typeAttribute, QLatin1String(variant.typeName()));
+ if (!key.isEmpty())
+ w.writeAttribute(ctx.keyAttribute, key);
+ switch (variant.type()) {
+ case QVariant::Rect:
+ w.writeCharacters(rectangleToString(variant.toRect()));
+ break;
+ default:
+ w.writeCharacters(variant.toString());
+ break;
+ }
+ w.writeEndElement();
+ break;
+ }
+}
+
+SdkPersistentSettingsWriter::SdkPersistentSettingsWriter(const QString &fileName, const QString &docType) :
+ m_fileName(fileName), m_docType(docType)
+{ }
+
+bool SdkPersistentSettingsWriter::save(const QVariantMap &data, QString *errorString) const
+{
+ if (data == m_savedData)
+ return true;
+ return write(data, errorString);
+}
+
+QString SdkPersistentSettingsWriter::fileName() const
+{ return m_fileName; }
+
+//** * @brief Set contents of file (e.g. from data read from it). */
+void SdkPersistentSettingsWriter::setContents(const QVariantMap &data)
+{
+ m_savedData = data;
+}
+
+bool SdkPersistentSettingsWriter::write(const QVariantMap &data, QString *errorString) const
+{
+ const QString parentDir = QDir::cleanPath(m_fileName + "/..");
+
+ const QFileInfo fi(parentDir);
+ if (!(fi.exists() && fi.isDir() && fi.isWritable())) {
+ bool res = QDir().mkpath(parentDir);
+ if (!res)
+ return false;
+ }
+
+ SdkFileSaver saver(m_fileName, QIODevice::Text);
+ if (!saver.hasError()) {
+ const Context ctx;
+ QXmlStreamWriter w(saver.file());
+ w.setAutoFormatting(true);
+ w.setAutoFormattingIndent(1); // Historical, used to be QDom.
+ w.writeStartDocument();
+ w.writeDTD(QLatin1String("<!DOCTYPE ") + m_docType + QLatin1Char('>'));
+ w.writeComment(QString::fromLatin1(" Written by %1 %2, %3. ").
+ arg(QCoreApplication::applicationName(),
+ QCoreApplication::applicationVersion(),
+ QDateTime::currentDateTime().toString(Qt::ISODate)));
+ w.writeStartElement(ctx.qtCreatorElement);
+ const QVariantMap::const_iterator cend = data.constEnd();
+ for (QVariantMap::const_iterator it = data.constBegin(); it != cend; ++it) {
+ w.writeStartElement(ctx.dataElement);
+ w.writeTextElement(ctx.variableElement, it.key());
+ writeVariantValue(w, ctx, it.value());
+ w.writeEndElement();
+ }
+ w.writeEndDocument();
+
+ saver.setResult(&w);
+ }
+ bool ok = saver.finalize();
+ if (ok) {
+ m_savedData = data;
+ } else if (errorString) {
+ m_savedData.clear();
+ *errorString = saver.errorString();
+ }
+
+ return ok;
+}
diff --git a/src/tools/sdktool/sdkpersistentsettings.h b/src/tools/sdktool/sdkpersistentsettings.h
new file mode 100644
index 0000000000..691cf0e08a
--- /dev/null
+++ b/src/tools/sdktool/sdkpersistentsettings.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QVariant>
+
+class SdkPersistentSettingsReader
+{
+public:
+ SdkPersistentSettingsReader();
+ QVariant restoreValue(const QString &variable, const QVariant &defaultValue = QVariant()) const;
+ QVariantMap restoreValues() const;
+ bool load(const QString &fileName);
+
+private:
+ QMap<QString, QVariant> m_valueMap;
+};
+
+class SdkPersistentSettingsWriter
+{
+public:
+ SdkPersistentSettingsWriter(const QString &fileName, const QString &docType);
+
+ bool save(const QVariantMap &data, QString *errorString) const;
+
+ QString fileName() const;
+
+ void setContents(const QVariantMap &data);
+
+private:
+ bool write(const QVariantMap &data, QString *errorString) const;
+
+ const QString m_fileName;
+ const QString m_docType;
+ mutable QMap<QString, QVariant> m_savedData;
+};
diff --git a/src/tools/sdktool/sdktoollib.qbs b/src/tools/sdktool/sdktoollib.qbs
index 6082339083..ca40590d8d 100644
--- a/src/tools/sdktool/sdktoollib.qbs
+++ b/src/tools/sdktool/sdktoollib.qbs
@@ -85,34 +85,7 @@ QtcLibrary {
"rmtoolchainoperation.h",
"settings.cpp",
"settings.h",
+ "sdkpersistentsettings.cpp",
+ "sdkpersistentsettings.h",
]
-
- Group {
- name: "Utils"
- prefix: libsDir + "/utils/"
- files: [
- "commandline.cpp", "commandline.h",
- "devicefileaccess.cpp", "devicefileaccess.h",
- "environment.cpp", "environment.h",
- "filepath.cpp", "filepath.h",
- "fileutils.cpp", "fileutils.h",
- "hostosinfo.cpp", "hostosinfo.h",
- "macroexpander.cpp", "macroexpander.h",
- "namevaluedictionary.cpp", "namevaluedictionary.h",
- "namevalueitem.cpp", "namevalueitem.h",
- "persistentsettings.cpp", "persistentsettings.h",
- "qtcassert.cpp", "qtcassert.h",
- "savefile.cpp", "savefile.h",
- "stringutils.cpp"
- ]
- }
- Group {
- name: "Utils/macOS"
- condition: qbs.targetOS.contains("macos")
- prefix: libsDir + "/utils/"
- files: [
- "fileutils_mac.h",
- "fileutils_mac.mm",
- ]
- }
}
diff --git a/src/tools/sdktool/settings.cpp b/src/tools/sdktool/settings.cpp
index d87df504f6..53a1af0565 100644
--- a/src/tools/sdktool/settings.cpp
+++ b/src/tools/sdktool/settings.cpp
@@ -2,11 +2,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "settings.h"
-#include "operation.h"
#include <app/app_version.h>
#include <QCoreApplication>
+#include <QDir>
static Settings *m_instance = nullptr;
@@ -21,28 +21,28 @@ Settings::Settings()
m_instance = this;
// autodetect sdk dir:
- sdkPath = Utils::FilePath::fromUserInput(QCoreApplication::applicationDirPath())
- .pathAppended(DATA_PATH).cleanPath()
- .pathAppended(Core::Constants::IDE_SETTINGSVARIANT_STR)
- .pathAppended(Core::Constants::IDE_ID);
+ sdkPath = QDir::cleanPath(QCoreApplication::applicationDirPath()
+ + '/' + DATA_PATH
+ + '/' + Core::Constants::IDE_SETTINGSVARIANT_STR
+ + '/' + Core::Constants::IDE_ID);
}
-Utils::FilePath Settings::getPath(const QString &file)
+QString Settings::getPath(const QString &file)
{
- Utils::FilePath result = sdkPath;
+ QString result = sdkPath;
const QString lowerFile = file.toLower();
const QStringList identical = {
"android", "cmaketools", "debuggers", "devices", "profiles", "qtversions", "toolchains", "abi"
};
if (lowerFile == "cmake")
- result = result.pathAppended("cmaketools");
+ result += "/cmaketools";
else if (lowerFile == "kits")
- result = result.pathAppended("profiles");
+ result += "/profiles";
else if (lowerFile == "qtversions")
- result = result.pathAppended("qtversion");
+ result += "/qtversion";
else if (identical.contains(lowerFile))
- result = result.pathAppended(lowerFile);
+ result += '/' + lowerFile;
else
- result = result.pathAppended(file); // handle arbitrary file names not known yet
- return result.stringAppended(".xml");
+ result += '/' + file; // handle arbitrary file names not known yet
+ return result += ".xml";
}
diff --git a/src/tools/sdktool/settings.h b/src/tools/sdktool/settings.h
index b94c2c9ad0..3b182e1c96 100644
--- a/src/tools/sdktool/settings.h
+++ b/src/tools/sdktool/settings.h
@@ -3,7 +3,7 @@
#pragma once
-#include <utils/fileutils.h>
+#include <QString>
class Operation;
@@ -13,8 +13,8 @@ public:
Settings();
static Settings *instance();
- Utils::FilePath getPath(const QString &file);
+ QString getPath(const QString &file);
- Utils::FilePath sdkPath;
+ QString sdkPath;
Operation *operation = nullptr;
};
diff --git a/src/tools/tools.qbs b/src/tools/tools.qbs
index cb4230dfde..48a35448d7 100644
--- a/src/tools/tools.qbs
+++ b/src/tools/tools.qbs
@@ -7,6 +7,7 @@ Project {
"buildoutputparser/buildoutputparser.qbs",
"cplusplustools.qbs",
"disclaim/disclaim.qbs",
+ "process_stub/process_stub.qbs",
"processlauncher/processlauncher.qbs",
"qml2puppet/qml2puppet.qbs",
"qtcdebugger/qtcdebugger.qbs",
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index 98ee345896..9849f8776e 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -19,6 +19,7 @@ add_subdirectory(profilewriter)
add_subdirectory(qml)
add_subdirectory(runextensions)
add_subdirectory(sdktool)
+add_subdirectory(solutions)
add_subdirectory(toolchaincache)
add_subdirectory(tracing)
add_subdirectory(treeviewfind)
diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs
index 2ff98d1409..063977537b 100644
--- a/tests/auto/auto.qbs
+++ b/tests/auto/auto.qbs
@@ -22,6 +22,7 @@ Project {
"qml/qml.qbs",
"runextensions/runextensions.qbs",
"sdktool/sdktool.qbs",
+ "solutions/solutions.qbs",
"toolchaincache/toolchaincache.qbs",
"tracing/tracing.qbs",
"treeviewfind/treeviewfind.qbs",
diff --git a/tests/auto/cplusplus/cxx11/tst_cxx11.cpp b/tests/auto/cplusplus/cxx11/tst_cxx11.cpp
index 0dc6484ff4..958aa631c6 100644
--- a/tests/auto/cplusplus/cxx11/tst_cxx11.cpp
+++ b/tests/auto/cplusplus/cxx11/tst_cxx11.cpp
@@ -145,6 +145,11 @@ private Q_SLOTS:
void lambdaType_data();
void lambdaType();
+
+ void concepts();
+ void requiresClause();
+ void coroutines();
+ void genericLambdas();
};
@@ -293,5 +298,257 @@ void tst_cxx11::lambdaType()
QCOMPARE(oo.prettyType(function->type()), expectedType);
}
+void tst_cxx11::concepts()
+{
+ LanguageFeatures features;
+ features.cxxEnabled = true;
+ features.cxx11Enabled = features.cxx14Enabled = features.cxx20Enabled = true;
+
+ const QString source = R"(
+template<typename T> concept IsPointer = requires(T p) { *p; };
+template<IsPointer T> void* func(T p) { return p; }
+void *func2(IsPointer auto p)
+{
+ return p;
+}
+)";
+ QByteArray errors;
+ Document::Ptr doc = Document::create(FilePath::fromPathPart(u"testFile"));
+ processDocument(doc, source.toUtf8(), features, &errors);
+ const bool hasErrors = !errors.isEmpty();
+ if (hasErrors)
+ qDebug().noquote() << errors;
+ QVERIFY(!hasErrors);
+}
+
+void tst_cxx11::requiresClause()
+{
+ LanguageFeatures features;
+ features.cxxEnabled = true;
+ features.cxx11Enabled = features.cxx14Enabled = features.cxx20Enabled = true;
+
+ const QString source = R"(
+template<class T> constexpr bool is_meowable = true;
+template<class T> constexpr bool is_purrable() { return true; }
+template<class T> void f(T) requires is_meowable<T>;
+template<class T> requires is_meowable<T> void g(T) ;
+template<class T> void h(T) requires (is_purrable<T>());
+)";
+ QByteArray errors;
+ Document::Ptr doc = Document::create(FilePath::fromPathPart(u"testFile"));
+ processDocument(doc, source.toUtf8(), features, &errors);
+ const bool hasErrors = !errors.isEmpty();
+ if (hasErrors)
+ qDebug().noquote() << errors;
+ QVERIFY(!hasErrors);
+}
+
+void tst_cxx11::coroutines()
+{
+ LanguageFeatures features;
+ features.cxxEnabled = true;
+ features.cxx11Enabled = features.cxx14Enabled = features.cxx20Enabled = true;
+
+ const QString source = R"(
+struct promise;
+struct coroutine : std::coroutine_handle<promise>
+{
+ using promise_type = struct promise;
+};
+struct promise
+{
+ coroutine get_return_object() { return {coroutine::from_promise(*this)}; }
+ std::suspend_always initial_suspend() noexcept { return {}; }
+ std::suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+};
+struct S
+{
+ int i;
+ coroutine f()
+ {
+ std::cout << i;
+ co_return;
+ }
+};
+void good()
+{
+ coroutine h = [](int i) -> coroutine
+ {
+ std::cout << i;
+ co_return;
+ }(0);
+ h.resume();
+ h.destroy();
+}
+auto switch_to_new_thread(std::jthread& out)
+{
+ struct awaitable
+ {
+ std::jthread* p_out;
+ bool await_ready() { return false; }
+ void await_suspend(std::coroutine_handle<> h)
+ {
+ std::jthread& out = *p_out;
+ if (out.joinable())
+ throw std::runtime_error("Output jthread parameter not empty");
+ out = std::jthread([h] { h.resume(); });
+ std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK
+ }
+ void await_resume() {}
+ };
+ return awaitable{&out};
+}
+struct task
+{
+ struct promise_type
+ {
+ task get_return_object() { return {}; }
+ std::suspend_never initial_suspend() { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+task resuming_on_new_thread(std::jthread& out)
+{
+ std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';
+ co_await switch_to_new_thread(out);
+ std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';
+}
+void run()
+{
+ std::jthread out;
+ resuming_on_new_thread(out);
+}
+template <typename T>
+struct Generator
+{
+ struct promise_type;
+ using handle_type = std::coroutine_handle<promise_type>;
+ struct promise_type // required
+ {
+ T value_;
+ std::exception_ptr exception_;
+
+ Generator get_return_object()
+ {
+ return Generator(handle_type::from_promise(*this));
+ }
+ std::suspend_always initial_suspend() { return {}; }
+ std::suspend_always final_suspend() noexcept { return {}; }
+ void unhandled_exception() { exception_ = std::current_exception(); }
+ template <std::convertible_to<T> From>
+ std::suspend_always yield_value(From&& from)
+ {
+ value_ = std::forward<From>(from);
+ return {};
+ }
+ void return_void() { }
+ };
+ handle_type h_;
+ Generator(handle_type h) : h_(h) {}
+ ~Generator() { h_.destroy(); }
+ explicit operator bool()
+ {
+ fill();
+ return !h_.done();
+ }
+ T operator()()
+ {
+ fill();
+ full_ = false;
+ return std::move(h_.promise().value_);
+ }
+private:
+ bool full_ = false;
+ void fill()
+ {
+ if (!full_)
+ {
+ h_();
+ if (h_.promise().exception_)
+ std::rethrow_exception(h_.promise().exception_);
+ full_ = true;
+ }
+ }
+};
+Generator<std::uint64_t>
+fibonacci_sequence(unsigned n)
+{
+ if (n == 0)
+ co_return;
+ if (n > 94)
+ throw std::runtime_error("Too big Fibonacci sequence. Elements would overflow.");
+ co_yield 0;
+ if (n == 1)
+ co_return;
+ co_yield 1;
+ if (n == 2)
+ co_return;
+ std::uint64_t a = 0;
+ std::uint64_t b = 1;
+ for (unsigned i = 2; i < n; i++)
+ {
+ std::uint64_t s = a + b;
+ co_yield s;
+ a = b;
+ b = s;
+ }
+}
+int main()
+{
+ try
+ {
+ auto gen = fibonacci_sequence(10); // max 94 before uint64_t overflows
+ for (int j = 0; gen; j++)
+ std::cout << "fib(" << j << ")=" << gen() << '\n';
+ } catch (const std::exception& ex) {
+ std::cerr << "Exception: " << ex.what() << '\n';
+ }
+ catch (...)
+ {
+ std::cerr << "Unknown exception.\n";
+ }
+}
+)";
+ QByteArray errors;
+ Document::Ptr doc = Document::create(FilePath::fromPathPart(u"testFile"));
+ processDocument(doc, source.toUtf8(), features, &errors);
+ const bool hasErrors = !errors.isEmpty();
+ if (hasErrors)
+ qDebug().noquote() << errors;
+ QVERIFY(!hasErrors);
+}
+
+void tst_cxx11::genericLambdas()
+{
+ LanguageFeatures features;
+ features.cxxEnabled = true;
+ features.cxx11Enabled = features.cxx14Enabled = features.cxx20Enabled = true;
+
+ const QString source = R"(
+template <typename T> concept C1 = true;
+template <std::size_t N> concept C2 = true;
+template <typename A, typename B> concept C3 = true;
+int main()
+{
+ auto f = []<class T>(T a, auto&& b) { return a < b; };
+ auto g = []<typename... Ts>(Ts&&... ts) { return foo(std::forward<Ts>(ts)...); };
+ auto h = []<typename T1, C1 T2> requires C2<sizeof(T1) + sizeof(T2)>
+ (T1 a1, T1 b1, T2 a2, auto a3, auto a4) requires C3<decltype(a4), T2> {
+ };
+}
+)";
+ QByteArray errors;
+ Document::Ptr doc = Document::create(FilePath::fromPathPart(u"testFile"));
+ processDocument(doc, source.toUtf8(), features, &errors);
+ const bool hasErrors = !errors.isEmpty();
+ if (hasErrors)
+ qDebug().noquote() << errors;
+ QVERIFY(!hasErrors);
+}
+
QTEST_APPLESS_MAIN(tst_cxx11)
#include "tst_cxx11.moc"
diff --git a/tests/auto/cplusplus/lexer/tst_lexer.cpp b/tests/auto/cplusplus/lexer/tst_lexer.cpp
index 641b62f248..09a474504e 100644
--- a/tests/auto/cplusplus/lexer/tst_lexer.cpp
+++ b/tests/auto/cplusplus/lexer/tst_lexer.cpp
@@ -43,6 +43,7 @@ public:
private slots:
void basic();
void basic_data();
+ void cxx20();
void incremental();
void incremental_data();
void literals();
@@ -250,6 +251,38 @@ void tst_SimpleLexer::basic_data()
QTest::newRow(source) << source << expectedTokenKindList;
}
+void tst_SimpleLexer::cxx20()
+{
+ LanguageFeatures features;
+ features.cxxEnabled = features.cxx11Enabled = features.cxx14Enabled
+ = features.cxx20Enabled = true;
+ const QString source = R"(
+template<typename T> concept IsPointer = requires(T p) { *p; };
+SomeType coroutine()
+{
+ constinit const char8_t = 'c';
+ if consteval {} else {}
+ co_await std::suspend_always{};
+ co_yield 1;
+ co_return;
+}
+)";
+ const TokenKindList expectedTokens = {
+ T_TEMPLATE, T_LESS, T_TYPENAME, T_IDENTIFIER, T_GREATER, T_CONCEPT, T_IDENTIFIER, T_EQUAL,
+ T_REQUIRES, T_LPAREN, T_IDENTIFIER, T_IDENTIFIER, T_RPAREN, T_LBRACE, T_STAR, T_IDENTIFIER,
+ T_SEMICOLON, T_RBRACE, T_SEMICOLON,
+ T_IDENTIFIER, T_IDENTIFIER, T_LPAREN, T_RPAREN,
+ T_LBRACE,
+ T_CONSTINIT, T_CONST, T_CHAR8_T, T_EQUAL, T_CHAR_LITERAL, T_SEMICOLON,
+ T_IF, T_CONSTEVAL, T_LBRACE, T_RBRACE, T_ELSE, T_LBRACE, T_RBRACE,
+ T_CO_AWAIT, T_IDENTIFIER, T_COLON_COLON, T_IDENTIFIER, T_LBRACE, T_RBRACE, T_SEMICOLON,
+ T_CO_YIELD, T_NUMERIC_LITERAL, T_SEMICOLON,
+ T_CO_RETURN, T_SEMICOLON,
+ T_RBRACE
+ };
+ run(source.toUtf8(), toTokens(expectedTokens), false, CompareKind, false, features);
+}
+
void tst_SimpleLexer::literals()
{
QFETCH(QByteArray, source);
diff --git a/tests/auto/debugger/CMakeLists.txt b/tests/auto/debugger/CMakeLists.txt
index 5ed44a94d2..4abe98c05e 100644
--- a/tests/auto/debugger/CMakeLists.txt
+++ b/tests/auto/debugger/CMakeLists.txt
@@ -27,7 +27,7 @@ if (NOT QT_CREATOR_API_DEFINED)
set(WITH_TESTS ON)
- find_package(Qt5
+ find_package(Qt6
COMPONENTS
Gui Core Core5Compat Widgets Network Qml Concurrent Test Xml MODULE)
find_package(Threads)
diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp
index 3f2f66116f..a1df2ae70c 100644
--- a/tests/auto/debugger/tst_dumpers.cpp
+++ b/tests/auto/debugger/tst_dumpers.cpp
@@ -1736,7 +1736,8 @@ void tst_Dumpers::dumper()
expandedq.append(',');
}
expanded += iname;
- expandedq += '\'' + iname + '\'';
+ expandedq += '\'' + iname + "':";
+ expandedq += data.bigArray ? "10000" : "100";
}
QString exe = m_debuggerBinary;
@@ -1769,7 +1770,7 @@ void tst_Dumpers::dumper()
"'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1,"
"'testing':1,'qobjectnames':1,"
- "'expanded':[" + expandedq + "]})\n";
+ "'expanded':{" + expandedq + "}})\n";
cmds += "quit\n";
@@ -1792,7 +1793,7 @@ void tst_Dumpers::dumper()
"'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':0,"
"'testing':1,'qobjectnames':1,"
- "'expanded':[" + expandedq + "]})\n"
+ "'expanded':{" + expandedq + "}})\n"
"q\n";
} else if (m_debuggerEngine == LldbEngine) {
QFile fullLldb(t->buildPath + "/lldbcommand.txt");
@@ -1808,7 +1809,7 @@ void tst_Dumpers::dumper()
"'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1,"
"'testing':1,'qobjectnames':1,"
- "'expanded':[" + expandedq + "]})\n"
+ "'expanded':{" + expandedq + "}})\n"
"quit\n";
fullLldb.write(cmds.toUtf8());
@@ -5314,6 +5315,7 @@ void tst_Dumpers::dumper_data()
"&v0, &v1, &v2, &v3, &v4, &v5, &b0, &b1, &b2, &b3")
+ Cxx11Profile()
+ + BigArrayProfile()
+ Check("v0", "<0 items>", "std::valarray<double>")
+ Check("v1", "<3 items>", "std::valarray<double>")
diff --git a/tests/auto/environment/tst_environment.cpp b/tests/auto/environment/tst_environment.cpp
index 994027308d..1b870ddd6d 100644
--- a/tests/auto/environment/tst_environment.cpp
+++ b/tests/auto/environment/tst_environment.cpp
@@ -40,6 +40,9 @@ private slots:
void incrementalChanges();
+ void pathChanges_data();
+ void pathChanges();
+
void find_data();
void find();
@@ -270,8 +273,9 @@ void tst_Environment::incrementalChanges()
newEnv.modify(changes);
QVERIFY(!newEnv.hasKey("VAR1"));
QCOMPARE(newEnv.value("VAR2"), QString());
- QCOMPARE(newEnv.constFind("VAR2")->first, "VALUE2");
- QVERIFY(!newEnv.isEnabled(newEnv.constFind("VAR2")));
+ Environment::FindResult res = newEnv.find("VAR2");
+ QCOMPARE(res->value, "VALUE2");
+ QVERIFY(!res->enabled);
const QChar sep = HostOsInfo::pathListSeparator();
QCOMPARE(newEnv.value("PATH"),
QString("/tmp").append(sep).append("/usr/bin").append(sep).append("/usr/local/bin"));
@@ -295,6 +299,82 @@ void tst_Environment::incrementalChanges()
reverseDiff);
}
+void tst_Environment::pathChanges_data()
+{
+ const Environment origEnvLinux({"PATH=/bin:/usr/bin", "VAR=VALUE"}, OsTypeLinux);
+ const Environment origEnvWin({"PATH=C:\\Windows\\System32;D:\\gnu\\bin", "VAR=VALUE"}, OsTypeWindows);
+
+ QTest::addColumn<Environment>("environment");
+ QTest::addColumn<bool>("prepend"); // if false => append
+ QTest::addColumn<QString>("variable");
+ QTest::addColumn<QString>("value");
+ QTest::addColumn<Environment>("expected");
+
+ QTest::newRow("appendOrSetPath existingLeading Unix")
+ << origEnvLinux << false << "PATH" << "/bin"
+ << Environment({"PATH=/bin:/usr/bin:/bin", "VAR=VALUE"}, OsTypeLinux);
+ QTest::newRow("appendOrSetPath existingLeading Win")
+ << origEnvWin << false << "PATH" << "C:\\Windows\\System32"
+ << Environment({"PATH=C:\\Windows\\System32;D:\\gnu\\bin;C:\\Windows\\System32",
+ "VAR=VALUE"}, OsTypeWindows);
+ QTest::newRow("appendOrSetPath existingTrailing Unix")
+ << origEnvLinux << false << "PATH" << "/usr/bin"
+ << Environment({"PATH=/bin:/usr/bin", "VAR=VALUE"}, OsTypeLinux);
+ QTest::newRow("appendOrSetPath existingTrailing Win")
+ << origEnvWin << false << "PATH" << "D:\\gnu\\bin"
+ << Environment({"PATH=C:\\Windows\\System32;D:\\gnu\\bin",
+ "VAR=VALUE"}, OsTypeWindows);
+ QTest::newRow("prependOrSetPath existingLeading Unix")
+ << origEnvLinux << true << "PATH" << "/bin"
+ << Environment({"PATH=/bin:/usr/bin", "VAR=VALUE"}, OsTypeLinux);
+ QTest::newRow("prependOrSetPath existingLeading Win")
+ << origEnvWin << true << "PATH" << "C:\\Windows\\System32"
+ << Environment({"PATH=C:\\Windows\\System32;D:\\gnu\\bin",
+ "VAR=VALUE"}, OsTypeWindows);
+ QTest::newRow("prependOrSetPath existingTrailing Unix")
+ << origEnvLinux << true << "PATH" << "/usr/bin"
+ << Environment({"PATH=/usr/bin:/bin:/usr/bin", "VAR=VALUE"}, OsTypeLinux);
+ QTest::newRow("prependOrSetPath existingTrailing Win")
+ << origEnvWin << true << "PATH" << "D:\\gnu\\bin"
+ << Environment({"PATH=D:\\gnu\\bin;C:\\Windows\\System32;D:\\gnu\\bin",
+ "VAR=VALUE"}, OsTypeWindows);
+
+ QTest::newRow("appendOrSetPath non-existing Unix")
+ << origEnvLinux << false << "PATH" << "/opt"
+ << Environment({"PATH=/bin:/usr/bin:/opt", "VAR=VALUE"}, OsTypeLinux);
+ QTest::newRow("appendOrSetPath non-existing Win")
+ << origEnvWin << false << "PATH" << "C:\\Windows"
+ << Environment({"PATH=C:\\Windows\\System32;D:\\gnu\\bin;C:\\Windows",
+ "VAR=VALUE"}, OsTypeWindows);
+ QTest::newRow("prependOrSetPath non-existing half-matching Unix")
+ << origEnvLinux << true << "PATH" << "/bi"
+ << Environment({"PATH=/bi:/bin:/usr/bin", "VAR=VALUE"}, OsTypeLinux);
+ QTest::newRow("prependOrSetPath non-existing half-matching Win")
+ << origEnvWin << true << "PATH" << "C:\\Windows"
+ << Environment({"PATH=C:\\Windows;C:\\Windows\\System32;D:\\gnu\\bin",
+ "VAR=VALUE"}, OsTypeWindows);
+}
+
+void tst_Environment::pathChanges()
+{
+ QFETCH(Environment, environment);
+ QFETCH(bool, prepend);
+ QFETCH(QString, variable);
+ QFETCH(QString, value);
+ QFETCH(Environment, expected);
+
+ const QString sep = OsSpecificAspects::pathListSeparator(environment.osType());
+
+ if (prepend)
+ environment.prependOrSet(variable, value, sep);
+ else
+ environment.appendOrSet(variable, value, sep);
+
+ qDebug() << "Actual :" << environment.toStringList();
+ qDebug() << "Expected:" << expected.toStringList();
+ QCOMPARE(environment, expected);
+}
+
void tst_Environment::find_data()
{
QTest::addColumn<Utils::OsType>("osType");
@@ -317,13 +397,12 @@ void tst_Environment::find()
Environment env(QStringList({"Foo=bar", "Hi=HO"}), osType);
- auto end = env.constEnd();
- auto it = env.constFind(variable);
+ Environment::FindResult res = env.find(variable);
- QCOMPARE((end != it), contains);
+ QCOMPARE(bool(res), contains);
if (contains)
- QCOMPARE(env.value(it), QString("bar"));
+ QCOMPARE(res->value, QString("bar"));
}
diff --git a/tests/auto/examples/tst_examples.cpp b/tests/auto/examples/tst_examples.cpp
index 5a18f1e449..eef0429d29 100644
--- a/tests/auto/examples/tst_examples.cpp
+++ b/tests/auto/examples/tst_examples.cpp
@@ -111,14 +111,14 @@ void tst_Examples::parsing_data()
<< "The Analog Clock example shows how to draw the contents of a custom widget."
<< "qthelp://org.qt-project.qtwidgets.660/qtwidgets/images/analogclock-example.png"
<< QStringList{"ios", "widgets"}
- << FilePath::fromUserInput("manifest/widgets/widgets/analogclock/CMakeLists.txt")
+ << FilePath::fromUserInput("examples/widgets/widgets/analogclock/CMakeLists.txt")
<< "qthelp://org.qt-project.qtwidgets.660/qtwidgets/"
"qtwidgets-widgets-analogclock-example.html"
- << FilePaths{FilePath::fromUserInput("manifest/widgets/widgets/analogclock/main.cpp"),
- FilePath::fromUserInput("manifest/widgets/widgets/analogclock/analogclock.h"),
+ << FilePaths{FilePath::fromUserInput("examples/widgets/widgets/analogclock/main.cpp"),
+ FilePath::fromUserInput("examples/widgets/widgets/analogclock/analogclock.h"),
FilePath::fromUserInput(
- "manifest/widgets/widgets/analogclock/analogclock.cpp")}
- << FilePath::fromUserInput("manifest/widgets/widgets/analogclock/analogclock.cpp")
+ "examples/widgets/widgets/analogclock/analogclock.cpp")}
+ << FilePath::fromUserInput("examples/widgets/widgets/analogclock/analogclock.cpp")
<< FilePaths() << Example << true << false << false << ""
<< "" << QStringList() << MetaData({{"category", {"Graphics"}}, {"tags", {"widgets"}}});
}
diff --git a/tests/auto/extensionsystem/pluginmanager/tst_pluginmanager.cpp b/tests/auto/extensionsystem/pluginmanager/tst_pluginmanager.cpp
index 9665747145..e57e71c533 100644
--- a/tests/auto/extensionsystem/pluginmanager/tst_pluginmanager.cpp
+++ b/tests/auto/extensionsystem/pluginmanager/tst_pluginmanager.cpp
@@ -57,9 +57,9 @@ void tst_PluginManager::init()
m_pm = new PluginManager;
PluginManager::setSettings(new Utils::QtcSettings);
PluginManager::setPluginIID(QLatin1String("plugin"));
- m_objectAdded = new QSignalSpy(m_pm, SIGNAL(objectAdded(QObject*)));
- m_aboutToRemoveObject = new QSignalSpy(m_pm, SIGNAL(aboutToRemoveObject(QObject*)));
- m_pluginsChanged = new QSignalSpy(m_pm, SIGNAL(pluginsChanged()));
+ m_objectAdded = new QSignalSpy(m_pm, &PluginManager::objectAdded);
+ m_aboutToRemoveObject = new QSignalSpy(m_pm, &PluginManager::aboutToRemoveObject);
+ m_pluginsChanged = new QSignalSpy(m_pm, &PluginManager::pluginsChanged);
}
void tst_PluginManager::cleanup()
diff --git a/tests/auto/filesearch/tst_filesearch.cpp b/tests/auto/filesearch/tst_filesearch.cpp
index 51f7d5a8bf..95bec53e09 100644
--- a/tests/auto/filesearch/tst_filesearch.cpp
+++ b/tests/auto/filesearch/tst_filesearch.cpp
@@ -22,68 +22,85 @@ private slots:
void caseSensitive();
void caseInSensitive();
void matchCaseReplacement();
+
+private:
+ const FilePath m_filePath = FilePath::fromString(":/tst_filesearch/testfile.txt");
};
-namespace {
- const char * const FILENAME = ":/tst_filesearch/testfile.txt";
-
- void test_helper(const Utils::FileSearchResultList &expectedResults,
- const QString &term,
- QTextDocument::FindFlags flags, tst_FileSearch::RegExpFlag regexp = tst_FileSearch::NoRegExp)
- {
- Utils::FileIterator *it = new Utils::FileListIterator(FilePaths{FilePath::fromString(
- FILENAME)},
- QList<QTextCodec *>()
- << QTextCodec::codecForLocale());
- QFutureWatcher<Utils::FileSearchResultList> watcher;
- QSignalSpy ready(&watcher, SIGNAL(resultsReadyAt(int,int)));
- if (regexp == tst_FileSearch::NoRegExp)
- watcher.setFuture(Utils::findInFiles(term, it, flags));
- else
- watcher.setFuture(Utils::findInFilesRegExp(term, it, flags));
- watcher.future().waitForFinished();
- QTest::qWait(100); // process events
- QCOMPARE(ready.count(), 1);
- Utils::FileSearchResultList results = watcher.resultAt(0);
- QCOMPARE(results.count(), expectedResults.count());
- for (int i = 0; i < expectedResults.size(); ++i) {
- QCOMPARE(results.at(i), expectedResults.at(i));
- }
- }
+SearchResultItem searchResult(const FilePath &fileName, const QString &matchingLine,
+ int lineNumber, int matchStart, int matchLength,
+ const QStringList &regexpCapturedTexts = {})
+{
+ SearchResultItem result;
+ result.setFilePath(fileName);
+ result.setLineText(matchingLine);
+ result.setMainRange(lineNumber, matchStart, matchLength);
+ result.setUserData(regexpCapturedTexts);
+ result.setUseTextEditorFont(true);
+ return result;
+}
+
+void test_helper(const FilePath &filePath, const SearchResultItems &expectedResults,
+ const QString &term, QTextDocument::FindFlags flags = {},
+ tst_FileSearch::RegExpFlag regexp = tst_FileSearch::NoRegExp)
+{
+ FileIterator *it = new FileListIterator({filePath}, {QTextCodec::codecForLocale()});
+ QFutureWatcher<SearchResultItems> watcher;
+ QSignalSpy ready(&watcher, &QFutureWatcherBase::resultsReadyAt);
+ if (regexp == tst_FileSearch::NoRegExp)
+ watcher.setFuture(Utils::findInFiles(term, it, flags));
+ else
+ watcher.setFuture(Utils::findInFilesRegExp(term, it, flags));
+ watcher.future().waitForFinished();
+ QTest::qWait(100); // process events
+ QCOMPARE(ready.count(), 1);
+ SearchResultItems results = watcher.resultAt(0);
+ QCOMPARE(results.count(), expectedResults.count());
+ for (int i = 0; i < expectedResults.size(); ++i)
+ QCOMPARE(results.at(i), expectedResults.at(i));
}
void tst_FileSearch::multipleResults()
{
- Utils::FileSearchResultList expectedResults;
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 2, QLatin1String("search to find multiple find results"), 10, 4, QStringList());
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 2, QLatin1String("search to find multiple find results"), 24, 4, QStringList());
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 4, QLatin1String("here you find another result"), 9, 4, QStringList());
- test_helper(expectedResults, QLatin1String("find"), QTextDocument::FindFlags());
+ SearchResultItems expectedResults;
+ expectedResults << searchResult(m_filePath, "search to find multiple find results", 2, 10, 4);
+ expectedResults << searchResult(m_filePath, "search to find multiple find results", 2, 24, 4);
+ expectedResults << searchResult(m_filePath, "here you find another result", 4, 9, 4);
+ test_helper(m_filePath, expectedResults, "find");
expectedResults.clear();
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 0, 4, QStringList());
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 4, 4, QStringList());
- test_helper(expectedResults, QLatin1String("aaaa"), QTextDocument::FindFlags());
+ expectedResults << searchResult(m_filePath,
+ "aaaaaaaa this line has 2 results for four a in a row",
+ 5, 0, 4);
+ expectedResults << searchResult(m_filePath,
+ "aaaaaaaa this line has 2 results for four a in a row",
+ 5, 4, 4);
+ test_helper(m_filePath, expectedResults, "aaaa");
expectedResults.clear();
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 0, 4, QStringList() << QLatin1String("aaaa"));
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 5, QLatin1String("aaaaaaaa this line has 2 results for four a in a row"), 4, 4, QStringList() << QLatin1String("aaaa"));
- test_helper(expectedResults, QLatin1String("aaaa"), QTextDocument::FindFlags(), RegExp);
+ expectedResults << searchResult(m_filePath,
+ "aaaaaaaa this line has 2 results for four a in a row",
+ 5, 0, 4, {"aaaa"});
+ expectedResults << searchResult(m_filePath,
+ "aaaaaaaa this line has 2 results for four a in a row",
+ 5, 4, 4, {"aaaa"});
+ test_helper(m_filePath, expectedResults, "aaaa", {}, RegExp);
}
void tst_FileSearch::caseSensitive()
{
- Utils::FileSearchResultList expectedResults;
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 3, QLatin1String("search CaseSensitively for casesensitive"), 7, 13, QStringList());
- test_helper(expectedResults, QLatin1String("CaseSensitive"), QTextDocument::FindCaseSensitively);
+ SearchResultItems expectedResults;
+ expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive",
+ 3, 7, 13);
+ test_helper(m_filePath, expectedResults, "CaseSensitive", QTextDocument::FindCaseSensitively);
}
void tst_FileSearch::caseInSensitive()
{
- Utils::FileSearchResultList expectedResults;
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 3, QLatin1String("search CaseSensitively for casesensitive"), 7, 13, QStringList());
- expectedResults << FileSearchResult(FilePath::fromString(FILENAME), 3, QLatin1String("search CaseSensitively for casesensitive"), 27, 13, QStringList());
- test_helper(expectedResults, QLatin1String("CaseSensitive"), QTextDocument::FindFlags());
+ SearchResultItems expectedResults;
+ expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive", 3, 7, 13);
+ expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive", 3, 27, 13);
+ test_helper(m_filePath, expectedResults, "CaseSensitive");
}
void tst_FileSearch::matchCaseReplacement()
diff --git a/tests/auto/qml/codemodel/check/tst_check.cpp b/tests/auto/qml/codemodel/check/tst_check.cpp
index 46542b8bf8..56ccdfd2a3 100644
--- a/tests/auto/qml/codemodel/check/tst_check.cpp
+++ b/tests/auto/qml/codemodel/check/tst_check.cpp
@@ -72,12 +72,11 @@ void tst_Check::initTestCase()
new ExtensionSystem::PluginManager;
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
- QFutureInterface<void> result;
PathsAndLanguages lPaths;
QStringList paths(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath));
for (auto p: paths)
lPaths.maybeInsert(Utils::FilePath::fromString(p), Dialect::Qml);
- ModelManagerInterface::importScan(result, ModelManagerInterface::workingCopy(), lPaths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), lPaths,
modelManager, false);
modelManager->test_joinAllThreads();
}
diff --git a/tests/auto/qml/codemodel/dependencies/tst_dependencies.cpp b/tests/auto/qml/codemodel/dependencies/tst_dependencies.cpp
index 1a09449c11..e550383724 100644
--- a/tests/auto/qml/codemodel/dependencies/tst_dependencies.cpp
+++ b/tests/auto/qml/codemodel/dependencies/tst_dependencies.cpp
@@ -3,7 +3,6 @@
#include <QString>
#include <QStringList>
-#include <QFutureInterface>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
@@ -122,13 +121,12 @@ void tst_Dependencies::test()
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
- QFutureInterface<void> result;
PathsAndLanguages lPaths;
QStringList paths(m_basePaths);
paths << m_path;
for (auto p: paths)
lPaths.maybeInsert(Utils::FilePath::fromString(p), Dialect::Qml);
- ModelManagerInterface::importScan(result, ModelManagerInterface::workingCopy(), lPaths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), lPaths,
ModelManagerInterface::instance(), false);
ModelManagerInterface::instance()->test_joinAllThreads();
TestData data = testData(filename);
diff --git a/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp b/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp
index 9ca768cbee..003630ab0b 100644
--- a/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp
+++ b/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp
@@ -3,7 +3,6 @@
#include <QString>
#include <QStringList>
-#include <QFutureInterface>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
@@ -149,12 +148,11 @@ void tst_Ecmascript::test()
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
- QFutureInterface<void> result;
PathsAndLanguages lPaths;
QStringList paths(m_basePaths);
for (auto p: paths)
lPaths.maybeInsert(Utils::FilePath::fromString(p), Dialect::Qml);
- ModelManagerInterface::importScan(result, ModelManagerInterface::workingCopy(), lPaths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), lPaths,
ModelManagerInterface::instance(), false);
TestData data = testData(filename);
diff --git a/tests/auto/qml/codemodel/importscheck/tst_importscheck.cpp b/tests/auto/qml/codemodel/importscheck/tst_importscheck.cpp
index 5cad7930e8..e47c77db48 100644
--- a/tests/auto/qml/codemodel/importscheck/tst_importscheck.cpp
+++ b/tests/auto/qml/codemodel/importscheck/tst_importscheck.cpp
@@ -51,10 +51,9 @@ private:
void scanDirectory(const QString &dir)
{
auto dirPath = Utils::FilePath::fromString(dir);
- QFutureInterface<void> result;
PathsAndLanguages paths;
paths.maybeInsert(dirPath, Dialect::Qml);
- ModelManagerInterface::importScan(result, ModelManagerInterface::workingCopy(), paths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), paths,
ModelManagerInterface::instance(), false);
ModelManagerInterface::instance()->test_joinAllThreads();
ViewerContext vCtx;
@@ -170,11 +169,10 @@ void tst_ImportCheck::test()
const auto pathPaths = Utils::transform(paths, [](const QString &s) {
return Utils::FilePath::fromString(s);
});
- QFutureInterface<void> result;
PathsAndLanguages lPaths;
for (const Utils::FilePath &path : pathPaths)
lPaths.maybeInsert(path, Dialect::Qml);
- ModelManagerInterface::importScan(result, ModelManagerInterface::workingCopy(), lPaths,
+ ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), lPaths,
ModelManagerInterface::instance(), false);
ModelManagerInterface::instance()->test_joinAllThreads();
ViewerContext vCtx;
diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
index d6e6226380..2206e24242 100644
--- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
+++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp
@@ -159,6 +159,7 @@ public:
bool hasStartupTarget() const override { return true; }
PuppetStartData puppetStartData(const class Model &) const override { return {}; }
bool instantQmlTextUpdate() const override { return true; }
+ Utils::FilePath qmlPuppetPath() const override { return {}; }
public:
QSettings qsettings;
@@ -233,12 +234,10 @@ void tst_TestCore::initTestCase()
QStringList basePaths;
basePaths.append(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath));
-
- QFutureInterface<void> result;
QmlJS::PathsAndLanguages lPaths;
lPaths.maybeInsert(Utils::FilePath::fromString(basePaths.first()), QmlJS::Dialect::Qml);
- QmlJS::ModelManagerInterface::importScan(result, QmlJS::ModelManagerInterface::workingCopy(),
+ QmlJS::ModelManagerInterface::importScan(QmlJS::ModelManagerInterface::workingCopy(),
lPaths, QmlJS::ModelManagerInterface::instance(), false);
// Load plugins
diff --git a/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp b/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
index 7dffbef416..0605791315 100644
--- a/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
+++ b/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
@@ -44,8 +44,6 @@ void PrintTo(const std::vector<UserPresetData> &presets, std::ostream *os)
using namespace StudioWelcome;
-constexpr char ARRAY_NAME[] = "UserPresets";
-
class FakeStoreIo : public StoreIo
{
public:
diff --git a/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp b/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp
index 8d2194086b..934bf5d317 100644
--- a/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp
+++ b/tests/auto/qml/qmleditor/qmlcodeformatter/tst_qmlcodeformatter.cpp
@@ -83,6 +83,7 @@ private Q_SLOTS:
void bug1();
void bug2();
void bug3();
+ void indentFunctionWithReturnTypeAnnotation();
};
enum { DontCheck = -2, DontIndent = -1 };
@@ -1564,6 +1565,18 @@ void tst_QMLCodeFormatter::bug3()
checkIndent(data);
}
+
+void tst_QMLCodeFormatter::indentFunctionWithReturnTypeAnnotation()
+{
+ QList<Line> data;
+ data << Line("function test() : void {", 0)
+ << Line("", 4)
+ << Line(" }", 0)
+ ;
+ checkIndent(data);
+}
+
+
QTEST_GUILESS_MAIN(tst_QMLCodeFormatter)
#include "tst_qmlcodeformatter.moc"
diff --git a/tests/auto/qml/reformatter/forEachType.qml b/tests/auto/qml/reformatter/forEachType.qml
new file mode 100644
index 0000000000..6fdabbde1b
--- /dev/null
+++ b/tests/auto/qml/reformatter/forEachType.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ Component.onCompleted: {
+ for (var i of ["one", "two", "free"]) {
+ console.debug(i)
+ }
+ }
+}
diff --git a/tests/auto/qml/reformatter/jsdirectives.js b/tests/auto/qml/reformatter/jsdirectives.js
new file mode 100644
index 0000000000..9029ba7233
--- /dev/null
+++ b/tests/auto/qml/reformatter/jsdirectives.js
@@ -0,0 +1,15 @@
+// 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 library
+
+.import QtQml as QTQML
+
+
+
+.import QtQuick as QTQUICK
+
+
+
+function test() {}
diff --git a/tests/auto/qml/reformatter/tst_reformatter.cpp b/tests/auto/qml/reformatter/tst_reformatter.cpp
index 42d98e02be..68b192fa60 100644
--- a/tests/auto/qml/reformatter/tst_reformatter.cpp
+++ b/tests/auto/qml/reformatter/tst_reformatter.cpp
@@ -68,25 +68,15 @@ void tst_Reformatter::test()
QString rewritten = reformat(doc);
- QStringList sourceLines = source.split(QLatin1Char('\n'));
- QStringList newLines = rewritten.split(QLatin1Char('\n'));
+ QStringList sourceLines = source.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
+ QStringList newLines = rewritten.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
// compare line by line
int commonLines = qMin(newLines.size(), sourceLines.size());
- bool insideMultiLineComment = false;
for (int i = 0; i < commonLines; ++i) {
// names intentional to make 'Actual (sourceLine): ...\nExpected (newLinee): ...' line up
const QString &sourceLine = sourceLines.at(i);
const QString &newLinee = newLines.at(i);
- if (!insideMultiLineComment && sourceLine.trimmed().startsWith("/*")) {
- insideMultiLineComment = true;
- sourceLines.insert(i, "\n");
- continue;
- }
- if (sourceLine.trimmed().endsWith("*/"))
- insideMultiLineComment = false;
- if (sourceLine.trimmed().isEmpty() && newLinee.trimmed().isEmpty())
- continue;
bool fail = !QCOMPARE_NOEXIT(newLinee, sourceLine);
if (fail) {
qDebug() << "in line" << (i + 1);
diff --git a/tests/auto/runextensions/tst_runextensions.cpp b/tests/auto/runextensions/tst_runextensions.cpp
index 50a3f8a40d..4ffee27002 100644
--- a/tests/auto/runextensions/tst_runextensions.cpp
+++ b/tests/auto/runextensions/tst_runextensions.cpp
@@ -22,7 +22,6 @@ private slots:
void threadPriority();
void runAsyncNoFutureInterface();
void crefFunction();
- void onResultReady();
};
void report3(QFutureInterface<int> &fi)
@@ -536,64 +535,6 @@ void tst_RunExtensions::crefFunction()
QCOMPARE(value, true);
}
-class ObjWithProperty : public QObject
-{
- Q_OBJECT
-
-public slots:
- void setValue(const QString &s)
- {
- value = s;
- }
-
-public:
- QString value;
-};
-
-void tst_RunExtensions::onResultReady()
-{
- { // lambda
- QFuture<QString> f = Utils::runAsync([](QFutureInterface<QString> &fi) {
- fi.reportResult("Hi");
- fi.reportResult("there");
- });
- int count = 0;
- QString res;
- Utils::onResultReady(f, [&count, &res](const QString &s) {
- ++count;
- res = s;
- });
- f.waitForFinished();
- QCoreApplication::processEvents();
- QCOMPARE(count, 2);
- QCOMPARE(res, QString("there"));
- }
- { // lambda with guard
- QFuture<QString> f = Utils::runAsync([](QFutureInterface<QString> &fi) {
- fi.reportResult("Hi");
- fi.reportResult("there");
- });
- int count = 0;
- ObjWithProperty obj;
- Utils::onResultReady(f, &obj, [&count, &obj](const QString &s) {
- ++count;
- obj.setValue(s);
- });
- f.waitForFinished();
- QCoreApplication::processEvents();
- QCOMPARE(count, 2);
- QCOMPARE(obj.value, QString("there"));
- }
- { // member
- QFuture<QString> f = Utils::runAsync([]() { return QString("Hi"); });
- ObjWithProperty obj;
- Utils::onResultReady(f, &obj, &ObjWithProperty::setValue);
- f.waitForFinished();
- QCoreApplication::processEvents();
- QCOMPARE(obj.value, QString("Hi"));
- }
-}
-
QTEST_GUILESS_MAIN(tst_RunExtensions)
#include "tst_runextensions.moc"
diff --git a/tests/auto/solutions/CMakeLists.txt b/tests/auto/solutions/CMakeLists.txt
new file mode 100644
index 0000000000..694d940195
--- /dev/null
+++ b/tests/auto/solutions/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(tasking)
diff --git a/tests/auto/solutions/solutions.qbs b/tests/auto/solutions/solutions.qbs
new file mode 100644
index 0000000000..cc445753cc
--- /dev/null
+++ b/tests/auto/solutions/solutions.qbs
@@ -0,0 +1,8 @@
+import qbs
+
+Project {
+ name: "Solutions autotests"
+ references: [
+ "tasking/tasking.qbs",
+ ]
+}
diff --git a/tests/auto/solutions/tasking/CMakeLists.txt b/tests/auto/solutions/tasking/CMakeLists.txt
new file mode 100644
index 0000000000..534d215ccb
--- /dev/null
+++ b/tests/auto/solutions/tasking/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_test(tst_solutions_tasking
+ DEPENDS Utils
+ SOURCES tst_tasking.cpp
+)
diff --git a/tests/auto/solutions/tasking/tasking.qbs b/tests/auto/solutions/tasking/tasking.qbs
new file mode 100644
index 0000000000..173c1fc575
--- /dev/null
+++ b/tests/auto/solutions/tasking/tasking.qbs
@@ -0,0 +1,7 @@
+QtcAutotest {
+ name: "Tasking autotest"
+
+ Depends { name: "Utils" }
+
+ files: "tst_tasking.cpp"
+}
diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp
new file mode 100644
index 0000000000..9efba89bb2
--- /dev/null
+++ b/tests/auto/solutions/tasking/tst_tasking.cpp
@@ -0,0 +1,1592 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <solutions/tasking/barrier.h>
+
+#include <utils/async.h>
+
+#include <QtTest>
+
+#include <QDeadlineTimer>
+
+using namespace std::literals::chrono_literals;
+
+using namespace Utils;
+using namespace Tasking;
+
+using TestTask = Async<void>;
+using Test = AsyncTask<void>;
+
+enum class Handler {
+ Setup,
+ Done,
+ Error,
+ GroupSetup,
+ GroupDone,
+ GroupError,
+ Sync,
+ BarrierAdvance,
+};
+
+using Log = QList<QPair<int, Handler>>;
+
+struct CustomStorage
+{
+ CustomStorage() { ++s_count; }
+ ~CustomStorage() { --s_count; }
+ Log m_log;
+ static int instanceCount() { return s_count; }
+private:
+ static int s_count;
+};
+
+int CustomStorage::s_count = 0;
+static const char s_taskIdProperty[] = "__taskId";
+
+static FutureSynchronizer *s_futureSynchronizer = nullptr;
+
+enum class OnStart { Running, NotRunning };
+enum class OnDone { Success, Failure };
+
+struct TestData {
+ TreeStorage<CustomStorage> storage;
+ Group root;
+ Log expectedLog;
+ int taskCount = 0;
+ OnStart onStart = OnStart::Running;
+ OnDone onDone = OnDone::Success;
+};
+
+class tst_Tasking : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+
+ void validConstructs(); // compile test
+ void testTree_data();
+ void testTree();
+ void storageOperators();
+ void storageDestructor();
+
+ void cleanupTestCase();
+};
+
+void tst_Tasking::initTestCase()
+{
+ s_futureSynchronizer = new FutureSynchronizer;
+}
+
+void tst_Tasking::cleanupTestCase()
+{
+ delete s_futureSynchronizer;
+ s_futureSynchronizer = nullptr;
+}
+
+void tst_Tasking::validConstructs()
+{
+ const Group task {
+ parallel,
+ Test([](TestTask &) {}, [](const TestTask &) {}),
+ Test([](TestTask &) {}, [](const TestTask &) {}),
+ Test([](TestTask &) {}, [](const TestTask &) {})
+ };
+
+ const Group group1 {
+ task
+ };
+
+ const Group group2 {
+ parallel,
+ Group {
+ parallel,
+ Test([](TestTask &) {}, [](const TestTask &) {}),
+ Group {
+ parallel,
+ Test([](TestTask &) {}, [](const TestTask &) {}),
+ Group {
+ parallel,
+ Test([](TestTask &) {}, [](const TestTask &) {})
+ }
+ },
+ Group {
+ parallel,
+ Test([](TestTask &) {}, [](const TestTask &) {}),
+ OnGroupDone([] {})
+ }
+ },
+ task,
+ OnGroupDone([] {}),
+ OnGroupError([] {})
+ };
+
+ const auto setupHandler = [](TestTask &) {};
+ const auto doneHandler = [](const TestTask &) {};
+ const auto errorHandler = [](const TestTask &) {};
+
+ // Not fluent interface
+
+ const Group task2 {
+ parallel,
+ Test(setupHandler),
+ Test(setupHandler, doneHandler),
+ Test(setupHandler, doneHandler, errorHandler),
+ // need to explicitly pass empty handler for done
+ Test(setupHandler, {}, errorHandler)
+ };
+
+ // Fluent interface
+
+ const Group fluent {
+ parallel,
+ Test().onSetup(setupHandler),
+ Test().onSetup(setupHandler).onDone(doneHandler),
+ Test().onSetup(setupHandler).onDone(doneHandler).onError(errorHandler),
+ // possible to skip the empty done
+ Test().onSetup(setupHandler).onError(errorHandler),
+ // possible to set handlers in a different order
+ Test().onError(errorHandler).onDone(doneHandler).onSetup(setupHandler),
+ };
+
+
+ // When turning each of below blocks on, you should see the specific compiler error message.
+
+#if 0
+ {
+ // "Sync element: The synchronous function has to return void or bool."
+ const auto setupSync = [] { return 3; };
+ const Sync sync(setupSync);
+ }
+#endif
+
+#if 0
+ {
+ // "Sync element: The synchronous function can't take any arguments."
+ const auto setupSync = [](int) { };
+ const Sync sync(setupSync);
+ }
+#endif
+
+#if 0
+ {
+ // "Sync element: The synchronous function can't take any arguments."
+ const auto setupSync = [](int) { return true; };
+ const Sync sync(setupSync);
+ }
+#endif
+}
+
+static void runTask(QPromise<void> &promise, bool success, std::chrono::milliseconds sleep)
+{
+ QDeadlineTimer deadline(sleep);
+ while (!deadline.hasExpired()) {
+ QThread::msleep(1);
+ if (promise.isCanceled())
+ return;
+ }
+ if (!success)
+ promise.future().cancel();
+}
+
+static void reportAndSleep(QPromise<bool> &promise)
+{
+ promise.addResult(false);
+ QThread::msleep(5);
+};
+
+template <typename SharedBarrierType>
+auto setupBarrierAdvance(const TreeStorage<CustomStorage> &storage,
+ const SharedBarrierType &barrier, int taskId)
+{
+ return [storage, barrier, taskId](Async<bool> &async) {
+ async.setFutureSynchronizer(s_futureSynchronizer);
+ async.setConcurrentCallData(reportAndSleep);
+ async.setProperty(s_taskIdProperty, taskId);
+ storage->m_log.append({taskId, Handler::Setup});
+
+ CustomStorage *currentStorage = storage.activeStorage();
+ Barrier *sharedBarrier = barrier->barrier();
+ QObject::connect(&async, &TestTask::resultReadyAt, sharedBarrier,
+ [currentStorage, sharedBarrier, taskId](int index) {
+ Q_UNUSED(index)
+ currentStorage->m_log.append({taskId, Handler::BarrierAdvance});
+ sharedBarrier->advance();
+ });
+ };
+}
+
+void tst_Tasking::testTree_data()
+{
+ QTest::addColumn<TestData>("testData");
+
+ TreeStorage<CustomStorage> storage;
+
+ const auto setupTaskHelper = [storage](TestTask &task, int taskId, bool success = true,
+ std::chrono::milliseconds sleep = 0ms) {
+ task.setFutureSynchronizer(s_futureSynchronizer);
+ task.setConcurrentCallData(runTask, success, sleep);
+ task.setProperty(s_taskIdProperty, taskId);
+ storage->m_log.append({taskId, Handler::Setup});
+ };
+ const auto setupTask = [setupTaskHelper](int taskId) {
+ return [=](TestTask &task) { setupTaskHelper(task, taskId); };
+ };
+ const auto setupFailingTask = [setupTaskHelper](int taskId) {
+ return [=](TestTask &task) { setupTaskHelper(task, taskId, false); };
+ };
+ const auto setupSleepingTask = [setupTaskHelper](int taskId, std::chrono::milliseconds sleep) {
+ return [=](TestTask &task) { setupTaskHelper(task, taskId, true, sleep); };
+ };
+ const auto setupDynamicTask = [setupTaskHelper](int taskId, TaskAction action) {
+ return [=](TestTask &task) {
+ setupTaskHelper(task, taskId);
+ return action;
+ };
+ };
+ const auto logDone = [storage](const TestTask &task) {
+ storage->m_log.append({task.property(s_taskIdProperty).toInt(), Handler::Done});
+ };
+ const auto logError = [storage](const TestTask &task) {
+ storage->m_log.append({task.property(s_taskIdProperty).toInt(), Handler::Error});
+ };
+ const auto groupSetup = [storage](int taskId) {
+ return [=] { storage->m_log.append({taskId, Handler::GroupSetup}); };
+ };
+ const auto groupDone = [storage](int taskId) {
+ return [=] { storage->m_log.append({taskId, Handler::GroupDone}); };
+ };
+ const auto groupError = [storage](int taskId) {
+ return [=] { storage->m_log.append({taskId, Handler::GroupError}); };
+ };
+ const auto setupSync = [storage](int taskId) {
+ return [=] { storage->m_log.append({taskId, Handler::Sync}); };
+ };
+ const auto setupSyncWithReturn = [storage](int taskId, bool success) {
+ return [=] { storage->m_log.append({taskId, Handler::Sync}); return success; };
+ };
+
+ const auto constructSimpleSequence = [=](const Workflow &policy) {
+ return Group {
+ Storage(storage),
+ policy,
+ Test(setupTask(1), logDone),
+ Test(setupFailingTask(2), logDone, logError),
+ Test(setupTask(3), logDone),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ };
+ const auto constructDynamicHierarchy = [=](TaskAction taskAction) {
+ return Group {
+ Storage(storage),
+ Group {
+ Test(setupTask(1), logDone)
+ },
+ Group {
+ OnGroupSetup([=] { return taskAction; }),
+ Test(setupTask(2), logDone),
+ Test(setupTask(3), logDone),
+ Test(setupTask(4), logDone)
+ },
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ };
+
+ {
+ const Group root1 {
+ Storage(storage),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ const Group root2 {
+ Storage(storage),
+ OnGroupSetup([] { return TaskAction::Continue; }),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ const Group root3 {
+ Storage(storage),
+ OnGroupSetup([] { return TaskAction::StopWithDone; }),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ const Group root4 {
+ Storage(storage),
+ OnGroupSetup([] { return TaskAction::StopWithError; }),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ const Log logDone {{0, Handler::GroupDone}};
+ const Log logError {{0, Handler::GroupError}};
+ QTest::newRow("Empty")
+ << TestData{storage, root1, logDone, 0, OnStart::NotRunning, OnDone::Success};
+ QTest::newRow("EmptyContinue")
+ << TestData{storage, root2, logDone, 0, OnStart::NotRunning, OnDone::Success};
+ QTest::newRow("EmptyDone")
+ << TestData{storage, root3, logDone, 0, OnStart::NotRunning, OnDone::Success};
+ QTest::newRow("EmptyError")
+ << TestData{storage, root4, logError, 0, OnStart::NotRunning, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Test(setupDynamicTask(1, TaskAction::StopWithDone), logDone, logError),
+ Test(setupDynamicTask(2, TaskAction::StopWithDone), logDone, logError)
+ };
+ const Log log {{1, Handler::Setup}, {2, Handler::Setup}};
+ QTest::newRow("DynamicTaskDone")
+ << TestData{storage, root, log, 2, OnStart::NotRunning, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Test(setupDynamicTask(1, TaskAction::StopWithError), logDone, logError),
+ Test(setupDynamicTask(2, TaskAction::StopWithError), logDone, logError)
+ };
+ const Log log {{1, Handler::Setup}};
+ QTest::newRow("DynamicTaskError")
+ << TestData{storage, root, log, 2, OnStart::NotRunning, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError),
+ Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError),
+ Test(setupDynamicTask(3, TaskAction::StopWithError), logDone, logError),
+ Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError)
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Done},
+ {3, Handler::Setup}
+ };
+ QTest::newRow("DynamicMixed")
+ << TestData{storage, root, log, 4, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ parallel,
+ Storage(storage),
+ Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError),
+ Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError),
+ Test(setupDynamicTask(3, TaskAction::StopWithError), logDone, logError),
+ Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError)
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {2, Handler::Setup},
+ {3, Handler::Setup},
+ {1, Handler::Error},
+ {2, Handler::Error}
+ };
+ QTest::newRow("DynamicParallel")
+ << TestData{storage, root, log, 4, OnStart::NotRunning, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ parallel,
+ Storage(storage),
+ Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError),
+ Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError),
+ Group {
+ Test(setupDynamicTask(3, TaskAction::StopWithError), logDone, logError)
+ },
+ Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError)
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {2, Handler::Setup},
+ {3, Handler::Setup},
+ {1, Handler::Error},
+ {2, Handler::Error}
+ };
+ QTest::newRow("DynamicParallelGroup")
+ << TestData{storage, root, log, 4, OnStart::NotRunning, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ parallel,
+ Storage(storage),
+ Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError),
+ Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError),
+ Group {
+ OnGroupSetup([storage] {
+ storage->m_log.append({0, Handler::GroupSetup});
+ return TaskAction::StopWithError;
+ }),
+ Test(setupDynamicTask(3, TaskAction::Continue), logDone, logError)
+ },
+ Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError)
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {2, Handler::Setup},
+ {0, Handler::GroupSetup},
+ {1, Handler::Error},
+ {2, Handler::Error}
+ };
+ QTest::newRow("DynamicParallelGroupSetup")
+ << TestData{storage, root, log, 4, OnStart::NotRunning, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Group {
+ Group {
+ Group {
+ Group {
+ Group {
+ Test(setupTask(5), logDone, logError),
+ OnGroupSetup(groupSetup(5)),
+ OnGroupDone(groupDone(5))
+ },
+ OnGroupSetup(groupSetup(4)),
+ OnGroupDone(groupDone(4))
+ },
+ OnGroupSetup(groupSetup(3)),
+ OnGroupDone(groupDone(3))
+ },
+ OnGroupSetup(groupSetup(2)),
+ OnGroupDone(groupDone(2))
+ },
+ OnGroupSetup(groupSetup(1)),
+ OnGroupDone(groupDone(1))
+ },
+ OnGroupDone(groupDone(0))
+ };
+ const Log log {
+ {1, Handler::GroupSetup},
+ {2, Handler::GroupSetup},
+ {3, Handler::GroupSetup},
+ {4, Handler::GroupSetup},
+ {5, Handler::GroupSetup},
+ {5, Handler::Setup},
+ {5, Handler::Done},
+ {5, Handler::GroupDone},
+ {4, Handler::GroupDone},
+ {3, Handler::GroupDone},
+ {2, Handler::GroupDone},
+ {1, Handler::GroupDone},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("Nested")
+ << TestData{storage, root, log, 1, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const auto logDoneAnonymously = [=](const TestTask &) {
+ storage->m_log.append({0, Handler::Done});
+ };
+ const Group root {
+ Storage(storage),
+ parallel,
+ Test(setupTask(1), logDoneAnonymously),
+ Test(setupTask(2), logDoneAnonymously),
+ Test(setupTask(3), logDoneAnonymously),
+ Test(setupTask(4), logDoneAnonymously),
+ Test(setupTask(5), logDoneAnonymously),
+ OnGroupDone(groupDone(0))
+ };
+ const Log log {
+ {1, Handler::Setup}, // Setup order is determined in parallel mode
+ {2, Handler::Setup},
+ {3, Handler::Setup},
+ {4, Handler::Setup},
+ {5, Handler::Setup},
+ {0, Handler::Done}, // Done order isn't determined in parallel mode
+ {0, Handler::Done},
+ {0, Handler::Done},
+ {0, Handler::Done},
+ {0, Handler::Done},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("Parallel")
+ << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ auto setupSubTree = [=](TaskTree &taskTree) {
+ const Group nestedRoot {
+ Storage(storage),
+ Test(setupTask(2), logDone),
+ Test(setupTask(3), logDone),
+ Test(setupTask(4), logDone)
+ };
+ taskTree.setupRoot(nestedRoot);
+ CustomStorage *activeStorage = storage.activeStorage();
+ auto collectSubLog = [activeStorage](CustomStorage *subTreeStorage){
+ activeStorage->m_log += subTreeStorage->m_log;
+ };
+ taskTree.onStorageDone(storage, collectSubLog);
+ };
+ const Group root1 {
+ Storage(storage),
+ Test(setupTask(1), logDone),
+ Test(setupTask(2), logDone),
+ Test(setupTask(3), logDone),
+ Test(setupTask(4), logDone),
+ Test(setupTask(5), logDone),
+ OnGroupDone(groupDone(0))
+ };
+ const Group root2 {
+ Storage(storage),
+ Group { Test(setupTask(1), logDone) },
+ Group { Test(setupTask(2), logDone) },
+ Group { Test(setupTask(3), logDone) },
+ Group { Test(setupTask(4), logDone) },
+ Group { Test(setupTask(5), logDone) },
+ OnGroupDone(groupDone(0))
+ };
+ const Group root3 {
+ Storage(storage),
+ Test(setupTask(1), logDone),
+ TaskTreeTask(setupSubTree),
+ Test(setupTask(5), logDone),
+ OnGroupDone(groupDone(0))
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Done},
+ {3, Handler::Setup},
+ {3, Handler::Done},
+ {4, Handler::Setup},
+ {4, Handler::Done},
+ {5, Handler::Setup},
+ {5, Handler::Done},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("Sequential")
+ << TestData{storage, root1, log, 5, OnStart::Running, OnDone::Success};
+ QTest::newRow("SequentialEncapsulated")
+ << TestData{storage, root2, log, 5, OnStart::Running, OnDone::Success};
+ QTest::newRow("SequentialSubTree") // We don't inspect subtrees, so taskCount is 3, not 5.
+ << TestData{storage, root3, log, 3, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Group {
+ Test(setupTask(1), logDone),
+ Group {
+ Test(setupTask(2), logDone),
+ Group {
+ Test(setupTask(3), logDone),
+ Group {
+ Test(setupTask(4), logDone),
+ Group {
+ Test(setupTask(5), logDone),
+ OnGroupDone(groupDone(5))
+ },
+ OnGroupDone(groupDone(4))
+ },
+ OnGroupDone(groupDone(3))
+ },
+ OnGroupDone(groupDone(2))
+ },
+ OnGroupDone(groupDone(1))
+ },
+ OnGroupDone(groupDone(0))
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Done},
+ {3, Handler::Setup},
+ {3, Handler::Done},
+ {4, Handler::Setup},
+ {4, Handler::Done},
+ {5, Handler::Setup},
+ {5, Handler::Done},
+ {5, Handler::GroupDone},
+ {4, Handler::GroupDone},
+ {3, Handler::GroupDone},
+ {2, Handler::GroupDone},
+ {1, Handler::GroupDone},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("SequentialNested")
+ << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Test(setupTask(1), logDone),
+ Test(setupTask(2), logDone),
+ Test(setupFailingTask(3), logDone, logError),
+ Test(setupTask(4), logDone),
+ Test(setupTask(5), logDone),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Done},
+ {3, Handler::Setup},
+ {3, Handler::Error},
+ {0, Handler::GroupError}
+ };
+ QTest::newRow("SequentialError")
+ << TestData{storage, root, log, 5, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root = constructSimpleSequence(stopOnError);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Error},
+ {0, Handler::GroupError}
+ };
+ QTest::newRow("StopOnError")
+ << TestData{storage, root, log, 3, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root = constructSimpleSequence(continueOnError);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Error},
+ {3, Handler::Setup},
+ {3, Handler::Done},
+ {0, Handler::GroupError}
+ };
+ QTest::newRow("ContinueOnError")
+ << TestData{storage, root, log, 3, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root = constructSimpleSequence(stopOnDone);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("StopOnDone")
+ << TestData{storage, root, log, 3, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root = constructSimpleSequence(continueOnDone);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Error},
+ {3, Handler::Setup},
+ {3, Handler::Done},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("ContinueOnDone")
+ << TestData{storage, root, log, 3, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ optional,
+ Test(setupFailingTask(1), logDone, logError),
+ Test(setupFailingTask(2), logDone, logError),
+ OnGroupDone(groupDone(0)),
+ OnGroupError(groupError(0))
+ };
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Error},
+ {2, Handler::Setup},
+ {2, Handler::Error},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("Optional")
+ << TestData{storage, root, log, 2, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root = constructDynamicHierarchy(TaskAction::StopWithDone);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("DynamicSetupDone")
+ << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root = constructDynamicHierarchy(TaskAction::StopWithError);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {0, Handler::GroupError}
+ };
+ QTest::newRow("DynamicSetupError")
+ << TestData{storage, root, log, 4, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root = constructDynamicHierarchy(TaskAction::Continue);
+ const Log log {
+ {1, Handler::Setup},
+ {1, Handler::Done},
+ {2, Handler::Setup},
+ {2, Handler::Done},
+ {3, Handler::Setup},
+ {3, Handler::Done},
+ {4, Handler::Setup},
+ {4, Handler::Done},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("DynamicSetupContinue")
+ << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ OnGroupSetup(groupSetup(1)),
+ Test(setupTask(1))
+ },
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ Test(setupTask(2))
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ Test(setupTask(3))
+ },
+ Group {
+ OnGroupSetup(groupSetup(4)),
+ Test(setupTask(4))
+ }
+ };
+ const Log log {
+ {1, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::GroupSetup},
+ {3, Handler::Setup},
+ {4, Handler::GroupSetup},
+ {4, Handler::Setup}
+ };
+ QTest::newRow("NestedParallel")
+ << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ OnGroupSetup(groupSetup(1)),
+ Test(setupTask(1))
+ },
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ Test(setupTask(2))
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ Test(setupDynamicTask(3, TaskAction::StopWithDone))
+ },
+ Group {
+ OnGroupSetup(groupSetup(4)),
+ Test(setupTask(4))
+ },
+ Group {
+ OnGroupSetup(groupSetup(5)),
+ Test(setupTask(5))
+ }
+ };
+ const Log log {
+ {1, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::GroupSetup},
+ {3, Handler::Setup},
+ {4, Handler::GroupSetup},
+ {4, Handler::Setup},
+ {5, Handler::GroupSetup},
+ {5, Handler::Setup}
+ };
+ QTest::newRow("NestedParallelDone")
+ << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root1 {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ OnGroupSetup(groupSetup(1)),
+ Test(setupTask(1))
+ },
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ Test(setupTask(2))
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ Test(setupDynamicTask(3, TaskAction::StopWithError))
+ },
+ Group {
+ OnGroupSetup(groupSetup(4)),
+ Test(setupTask(4))
+ },
+ Group {
+ OnGroupSetup(groupSetup(5)),
+ Test(setupTask(5))
+ }
+ };
+
+ // Inside this test the task 2 should finish first, then synchonously:
+ // - task 3 should exit setup with error
+ // - task 1 should be stopped as a consequence of error inside the group
+ // - tasks 4 and 5 should be skipped
+ const Group root2 {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ OnGroupSetup(groupSetup(1)),
+ Test(setupSleepingTask(1, 10ms))
+ },
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ Test(setupTask(2))
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ Test(setupDynamicTask(3, TaskAction::StopWithError))
+ },
+ Group {
+ OnGroupSetup(groupSetup(4)),
+ Test(setupTask(4))
+ },
+ Group {
+ OnGroupSetup(groupSetup(5)),
+ Test(setupTask(5))
+ }
+ };
+
+ // This test ensures that the task 1 doesn't invoke its done handler,
+ // being ready while sleeping in the task's 2 done handler.
+ // Inside this test the task 2 should finish first, then synchonously:
+ // - task 3 should exit setup with error
+ // - task 1 should be stopped as a consequence of error inside the group
+ // - task 4 should be skipped
+ // - the first child group of root should finish with error
+ // - task 5 should be started (because of root's continueOnError policy)
+ const Group root3 {
+ continueOnError,
+ Storage(storage),
+ Group {
+ ParallelLimit(2),
+ Group {
+ OnGroupSetup(groupSetup(1)),
+ Test(setupSleepingTask(1, 20ms))
+ },
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ Test(setupTask(2), [](const TestTask &) { QThread::msleep(10); })
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ Test(setupDynamicTask(3, TaskAction::StopWithError))
+ },
+ Group {
+ OnGroupSetup(groupSetup(4)),
+ Test(setupTask(4))
+ }
+ },
+ Group {
+ OnGroupSetup(groupSetup(5)),
+ Test(setupTask(5))
+ }
+ };
+ const Log shortLog {
+ {1, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::GroupSetup},
+ {3, Handler::Setup}
+ };
+ const Log longLog = shortLog + Log {{5, Handler::GroupSetup}, {5, Handler::Setup}};
+ QTest::newRow("NestedParallelError1")
+ << TestData{storage, root1, shortLog, 5, OnStart::Running, OnDone::Failure};
+ QTest::newRow("NestedParallelError2")
+ << TestData{storage, root2, shortLog, 5, OnStart::Running, OnDone::Failure};
+ QTest::newRow("NestedParallelError3")
+ << TestData{storage, root3, longLog, 5, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(1)),
+ Group {
+ parallel,
+ Test(setupTask(1))
+ }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(2)),
+ Group {
+ parallel,
+ Test(setupTask(2))
+ }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(3)),
+ Group {
+ parallel,
+ Test(setupTask(3))
+ }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(4)),
+ Group {
+ parallel,
+ Test(setupTask(4))
+ }
+ }
+ };
+ const Log log {
+ {1, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::GroupSetup},
+ {3, Handler::Setup},
+ {4, Handler::GroupSetup},
+ {4, Handler::Setup}
+ };
+ QTest::newRow("DeeplyNestedParallel")
+ << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(1)),
+ Group { Test(setupTask(1)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(2)),
+ Group { Test(setupTask(2)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(3)),
+ Group { Test(setupDynamicTask(3, TaskAction::StopWithDone)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(4)),
+ Group { Test(setupTask(4)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(5)),
+ Group { Test(setupTask(5)) }
+ }
+ };
+ const Log log {
+ {1, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::GroupSetup},
+ {3, Handler::Setup},
+ {4, Handler::GroupSetup},
+ {4, Handler::Setup},
+ {5, Handler::GroupSetup},
+ {5, Handler::Setup}
+ };
+ QTest::newRow("DeeplyNestedParallelDone")
+ << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ ParallelLimit(2),
+ Storage(storage),
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(1)),
+ Group { Test(setupTask(1)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(2)),
+ Group { Test(setupTask(2)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(3)),
+ Group { Test(setupDynamicTask(3, TaskAction::StopWithError)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(4)),
+ Group { Test(setupTask(4)) }
+ },
+ Group {
+ Storage(TreeStorage<CustomStorage>()),
+ OnGroupSetup(groupSetup(5)),
+ Group { Test(setupTask(5)) }
+ }
+ };
+ const Log log {
+ {1, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::GroupSetup},
+ {3, Handler::Setup}
+ };
+ QTest::newRow("DeeplyNestedParallelError")
+ << TestData{storage, root, log, 5, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Sync(setupSync(1)),
+ Sync(setupSync(2)),
+ Sync(setupSync(3)),
+ Sync(setupSync(4)),
+ Sync(setupSync(5))
+ };
+ const Log log {
+ {1, Handler::Sync},
+ {2, Handler::Sync},
+ {3, Handler::Sync},
+ {4, Handler::Sync},
+ {5, Handler::Sync}
+ };
+ QTest::newRow("SyncSequential")
+ << TestData{storage, root, log, 0, OnStart::NotRunning, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Sync(setupSyncWithReturn(1, true)),
+ Sync(setupSyncWithReturn(2, true)),
+ Sync(setupSyncWithReturn(3, true)),
+ Sync(setupSyncWithReturn(4, true)),
+ Sync(setupSyncWithReturn(5, true))
+ };
+ const Log log {
+ {1, Handler::Sync},
+ {2, Handler::Sync},
+ {3, Handler::Sync},
+ {4, Handler::Sync},
+ {5, Handler::Sync}
+ };
+ QTest::newRow("SyncWithReturn")
+ << TestData{storage, root, log, 0, OnStart::NotRunning, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ parallel,
+ Sync(setupSync(1)),
+ Sync(setupSync(2)),
+ Sync(setupSync(3)),
+ Sync(setupSync(4)),
+ Sync(setupSync(5))
+ };
+ const Log log {
+ {1, Handler::Sync},
+ {2, Handler::Sync},
+ {3, Handler::Sync},
+ {4, Handler::Sync},
+ {5, Handler::Sync}
+ };
+ QTest::newRow("SyncParallel")
+ << TestData{storage, root, log, 0, OnStart::NotRunning, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ parallel,
+ Sync(setupSync(1)),
+ Sync(setupSync(2)),
+ Sync(setupSyncWithReturn(3, false)),
+ Sync(setupSync(4)),
+ Sync(setupSync(5))
+ };
+ const Log log {
+ {1, Handler::Sync},
+ {2, Handler::Sync},
+ {3, Handler::Sync}
+ };
+ QTest::newRow("SyncError")
+ << TestData{storage, root, log, 0, OnStart::NotRunning, OnDone::Failure};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Sync(setupSync(1)),
+ Test(setupTask(2)),
+ Sync(setupSync(3)),
+ Test(setupTask(4)),
+ Sync(setupSync(5)),
+ OnGroupDone(groupDone(0))
+ };
+ const Log log {
+ {1, Handler::Sync},
+ {2, Handler::Setup},
+ {3, Handler::Sync},
+ {4, Handler::Setup},
+ {5, Handler::Sync},
+ {0, Handler::GroupDone}
+ };
+ QTest::newRow("SyncAndAsync")
+ << TestData{storage, root, log, 2, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ const Group root {
+ Storage(storage),
+ Sync(setupSync(1)),
+ Test(setupTask(2)),
+ Sync(setupSyncWithReturn(3, false)),
+ Test(setupTask(4)),
+ Sync(setupSync(5)),
+ OnGroupError(groupError(0))
+ };
+ const Log log {
+ {1, Handler::Sync},
+ {2, Handler::Setup},
+ {3, Handler::Sync},
+ {0, Handler::GroupError}
+ };
+ QTest::newRow("SyncAndAsyncError")
+ << TestData{storage, root, log, 2, OnStart::Running, OnDone::Failure};
+ }
+
+ {
+ SingleBarrier barrier;
+
+ // Test that barrier advance, triggered from inside the task described by
+ // setupBarrierAdvance, placed BEFORE the group containing the waitFor() element
+ // in the tree order, works OK in SEQUENTIAL mode.
+ const Group root1 {
+ Storage(storage),
+ Storage(barrier),
+ sequential,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 1)),
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(2)),
+ Test(setupTask(3))
+ }
+ };
+ const Log log1 {
+ {1, Handler::Setup},
+ {1, Handler::BarrierAdvance},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::Setup}
+ };
+
+ // Test that barrier advance, triggered from inside the task described by
+ // setupTaskWithCondition, placed BEFORE the group containing the waitFor() element
+ // in the tree order, works OK in PARALLEL mode.
+ const Group root2 {
+ Storage(storage),
+ Storage(barrier),
+ parallel,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 1)),
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(2)),
+ Test(setupTask(3))
+ }
+ };
+ const Log log2 {
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {1, Handler::BarrierAdvance},
+ {2, Handler::Setup},
+ {3, Handler::Setup}
+ };
+
+ // Test that barrier advance, triggered from inside the task described by
+ // setupTaskWithCondition, placed AFTER the group containing the waitFor() element
+ // in the tree order, works OK in PARALLEL mode.
+ //
+ // Notice: This won't work in SEQUENTIAL mode, since the advancing barrier, placed after the
+ // group containing the WaitFor element, has no chance to be started in SEQUENTIAL mode,
+ // as in SEQUENTIAL mode the next task may only be started after the previous one finished.
+ // In this case, the previous task (Group element) awaits for the barrier's advance to
+ // come from the not yet started next task, causing a deadlock.
+ // The minimal requirement for this scenario to succeed is to set ParallelLimit(2) or more.
+ const Group root3 {
+ Storage(storage),
+ Storage(barrier),
+ parallel,
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(2)),
+ Test(setupTask(3))
+ },
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 1))
+ };
+ const Log log3 {
+ {2, Handler::GroupSetup},
+ {1, Handler::Setup},
+ {1, Handler::BarrierAdvance},
+ {2, Handler::Setup},
+ {3, Handler::Setup}
+ };
+
+ // Test that barrier advance, triggered from inside the task described by
+ // setupBarrierAdvance, placed BEFORE the groups containing the waitFor() element
+ // in the tree order, wakes both waitFor tasks.
+ const Group root4 {
+ Storage(storage),
+ Storage(barrier),
+ parallel,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 1)),
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(4))
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(5))
+ }
+ };
+ const Log log4 {
+ {1, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {3, Handler::GroupSetup},
+ {1, Handler::BarrierAdvance},
+ {4, Handler::Setup},
+ {5, Handler::Setup}
+ };
+
+ // Test two separate single barriers.
+
+ SingleBarrier barrier2;
+
+ const Group root5 {
+ Storage(storage),
+ Storage(barrier),
+ Storage(barrier2),
+ parallel,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0)),
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier2, 0)),
+ Group {
+ Group {
+ parallel,
+ OnGroupSetup(groupSetup(1)),
+ WaitForBarrierTask(barrier),
+ WaitForBarrierTask(barrier2)
+ },
+ Test(setupTask(2))
+ },
+ };
+ const Log log5 {
+ {0, Handler::Setup},
+ {0, Handler::Setup},
+ {1, Handler::GroupSetup},
+ {0, Handler::BarrierAdvance},
+ {0, Handler::BarrierAdvance},
+ {2, Handler::Setup}
+ };
+
+ // Notice the different log order for each scenario.
+ QTest::newRow("BarrierSequential")
+ << TestData{storage, root1, log1, 4, OnStart::Running, OnDone::Success};
+ QTest::newRow("BarrierParallelAdvanceFirst")
+ << TestData{storage, root2, log2, 4, OnStart::Running, OnDone::Success};
+ QTest::newRow("BarrierParallelWaitForFirst")
+ << TestData{storage, root3, log3, 4, OnStart::Running, OnDone::Success};
+ QTest::newRow("BarrierParallelMultiWaitFor")
+ << TestData{storage, root4, log4, 5, OnStart::Running, OnDone::Success};
+ QTest::newRow("BarrierParallelTwoSingleBarriers")
+ << TestData{storage, root5, log5, 5, OnStart::Running, OnDone::Success};
+ }
+
+ {
+ MultiBarrier<2> barrier;
+
+ // Test that multi barrier advance, triggered from inside the tasks described by
+ // setupBarrierAdvance, placed BEFORE the group containing the waitFor() element
+ // in the tree order, works OK in SEQUENTIAL mode.
+ const Group root1 {
+ Storage(storage),
+ Storage(barrier),
+ sequential,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 1)),
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 2)),
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(2)),
+ Test(setupTask(3))
+ }
+ };
+ const Log log1 {
+ {1, Handler::Setup},
+ {1, Handler::BarrierAdvance},
+ {2, Handler::Setup},
+ {2, Handler::BarrierAdvance},
+ {2, Handler::GroupSetup},
+ {2, Handler::Setup},
+ {3, Handler::Setup}
+ };
+
+ // Test that multi barrier advance, triggered from inside the tasks described by
+ // setupBarrierAdvance, placed BEFORE the group containing the waitFor() element
+ // in the tree order, works OK in PARALLEL mode.
+ const Group root2 {
+ Storage(storage),
+ Storage(barrier),
+ parallel,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0)),
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0)),
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(2)),
+ Test(setupTask(3))
+ }
+ };
+ const Log log2 {
+ {0, Handler::Setup},
+ {0, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {0, Handler::BarrierAdvance}, // Barrier advances may come in different order in
+ {0, Handler::BarrierAdvance}, // parallel mode, that's why id = 0 (same for both).
+ {2, Handler::Setup},
+ {3, Handler::Setup}
+ };
+
+ // Test that multi barrier advance, triggered from inside the tasks described by
+ // setupBarrierAdvance, placed AFTER the group containing the waitFor() element
+ // in the tree order, works OK in PARALLEL mode.
+ //
+ // Notice: This won't work in SEQUENTIAL mode, since the advancing barriers, placed after
+ // the group containing the WaitFor element, has no chance to be started in SEQUENTIAL mode,
+ // as in SEQUENTIAL mode the next task may only be started after the previous one finished.
+ // In this case, the previous task (Group element) awaits for the barrier's advance to
+ // come from the not yet started next task, causing a deadlock.
+ // The minimal requirement for this scenario to succeed is to set ParallelLimit(2) or more.
+ const Group root3 {
+ Storage(storage),
+ Storage(barrier),
+ parallel,
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(2)),
+ Test(setupTask(3))
+ },
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0)),
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0))
+ };
+ const Log log3 {
+ {2, Handler::GroupSetup},
+ {0, Handler::Setup},
+ {0, Handler::Setup},
+ {0, Handler::BarrierAdvance}, // Barrier advances may come in different order in
+ {0, Handler::BarrierAdvance}, // parallel mode, that's why id = 0 (same for both).
+ {2, Handler::Setup},
+ {3, Handler::Setup}
+ };
+
+ // Test that multi barrier advance, triggered from inside the task described by
+ // setupBarrierAdvance, placed BEFORE the groups containing the waitFor() element
+ // in the tree order, wakes both waitFor tasks.
+ const Group root4 {
+ Storage(storage),
+ Storage(barrier),
+ parallel,
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0)),
+ AsyncTask<bool>(setupBarrierAdvance(storage, barrier, 0)),
+ Group {
+ OnGroupSetup(groupSetup(2)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(4))
+ },
+ Group {
+ OnGroupSetup(groupSetup(3)),
+ WaitForBarrierTask(barrier),
+ Test(setupTask(5))
+ }
+ };
+ const Log log4 {
+ {0, Handler::Setup},
+ {0, Handler::Setup},
+ {2, Handler::GroupSetup},
+ {3, Handler::GroupSetup},
+ {0, Handler::BarrierAdvance},
+ {0, Handler::BarrierAdvance},
+ {4, Handler::Setup},
+ {5, Handler::Setup}
+ };
+
+ // Notice the different log order for each scenario.
+ QTest::newRow("MultiBarrierSequential")
+ << TestData{storage, root1, log1, 5, OnStart::Running, OnDone::Success};
+ QTest::newRow("MultiBarrierParallelAdvanceFirst")
+ << TestData{storage, root2, log2, 5, OnStart::Running, OnDone::Success};
+ QTest::newRow("MultiBarrierParallelWaitForFirst")
+ << TestData{storage, root3, log3, 5, OnStart::Running, OnDone::Success};
+ QTest::newRow("MultiBarrierParallelMultiWaitFor")
+ << TestData{storage, root4, log4, 6, OnStart::Running, OnDone::Success};
+ }
+}
+
+void tst_Tasking::testTree()
+{
+ QFETCH(TestData, testData);
+
+ QEventLoop eventLoop;
+ TaskTree taskTree(testData.root);
+ QCOMPARE(taskTree.taskCount(), testData.taskCount);
+ int doneCount = 0;
+ int errorCount = 0;
+ connect(&taskTree, &TaskTree::done, this, [&doneCount, &eventLoop] {
+ ++doneCount;
+ eventLoop.quit();
+ });
+ connect(&taskTree, &TaskTree::errorOccurred, this, [&errorCount, &eventLoop] {
+ ++errorCount;
+ eventLoop.quit();
+ });
+ Log actualLog;
+ auto collectLog = [&actualLog](CustomStorage *storage){
+ actualLog = storage->m_log;
+ };
+ taskTree.onStorageDone(testData.storage, collectLog);
+ taskTree.start();
+ const bool expectRunning = testData.onStart == OnStart::Running;
+ QCOMPARE(taskTree.isRunning(), expectRunning);
+
+ if (expectRunning) {
+ QTimer timer;
+ bool timedOut = false;
+ connect(&timer, &QTimer::timeout, &eventLoop, [&eventLoop, &timedOut] {
+ timedOut = true;
+ eventLoop.quit();
+ });
+ timer.setInterval(2000);
+ timer.setSingleShot(true);
+ timer.start();
+ eventLoop.exec();
+ QCOMPARE(timedOut, false);
+ QCOMPARE(taskTree.isRunning(), false);
+ }
+
+ QCOMPARE(taskTree.progressValue(), testData.taskCount);
+ QCOMPARE(actualLog, testData.expectedLog);
+ QCOMPARE(CustomStorage::instanceCount(), 0);
+
+ const bool expectSuccess = testData.onDone == OnDone::Success;
+ const int expectedDoneCount = expectSuccess ? 1 : 0;
+ const int expectedErrorCount = expectSuccess ? 0 : 1;
+ QCOMPARE(doneCount, expectedDoneCount);
+ QCOMPARE(errorCount, expectedErrorCount);
+}
+
+void tst_Tasking::storageOperators()
+{
+ TreeStorageBase storage1 = TreeStorage<CustomStorage>();
+ TreeStorageBase storage2 = TreeStorage<CustomStorage>();
+ TreeStorageBase storage3 = storage1;
+
+ QVERIFY(storage1 == storage3);
+ QVERIFY(storage1 != storage2);
+ QVERIFY(storage2 != storage3);
+}
+
+// This test checks whether a running task tree may be safely destructed.
+// It also checks whether the destructor of a task tree deletes properly the storage created
+// while starting the task tree. When running task tree is destructed, the storage done
+// handler shouldn't be invoked.
+void tst_Tasking::storageDestructor()
+{
+ bool setupCalled = false;
+ const auto setupHandler = [&setupCalled](CustomStorage *) {
+ setupCalled = true;
+ };
+ bool doneCalled = false;
+ const auto doneHandler = [&doneCalled](CustomStorage *) {
+ doneCalled = true;
+ };
+ QCOMPARE(CustomStorage::instanceCount(), 0);
+ {
+ TreeStorage<CustomStorage> storage;
+ const auto setupSleepingTask = [](TestTask &task) {
+ task.setFutureSynchronizer(s_futureSynchronizer);
+ task.setConcurrentCallData(runTask, true, 1000ms);
+ };
+ const Group root {
+ Storage(storage),
+ Test(setupSleepingTask)
+ };
+
+ TaskTree taskTree(root);
+ QCOMPARE(CustomStorage::instanceCount(), 0);
+ taskTree.onStorageSetup(storage, setupHandler);
+ taskTree.onStorageDone(storage, doneHandler);
+ taskTree.start();
+ QCOMPARE(CustomStorage::instanceCount(), 1);
+ QThread::msleep(5); // Give the sleeping task a change to start
+ }
+ QCOMPARE(CustomStorage::instanceCount(), 0);
+ QVERIFY(setupCalled);
+ QVERIFY(!doneCalled);
+}
+
+QTEST_GUILESS_MAIN(tst_Tasking)
+
+#include "tst_tasking.moc"
diff --git a/tests/auto/tracing/timelineabstractrenderer/tst_timelineabstractrenderer.cpp b/tests/auto/tracing/timelineabstractrenderer/tst_timelineabstractrenderer.cpp
index 0da3dd1f6a..66ec7f6709 100644
--- a/tests/auto/tracing/timelineabstractrenderer/tst_timelineabstractrenderer.cpp
+++ b/tests/auto/tracing/timelineabstractrenderer/tst_timelineabstractrenderer.cpp
@@ -36,7 +36,7 @@ void tst_TimelineAbstractRenderer::privateCtor()
void tst_TimelineAbstractRenderer::selectionLocked()
{
TimelineAbstractRenderer renderer;
- QSignalSpy spy(&renderer, SIGNAL(selectionLockedChanged(bool)));
+ QSignalSpy spy(&renderer, &TimelineAbstractRenderer::selectionLockedChanged);
QCOMPARE(spy.count(), 0);
QVERIFY(renderer.selectionLocked());
renderer.setSelectionLocked(false);
@@ -50,7 +50,7 @@ void tst_TimelineAbstractRenderer::selectionLocked()
void tst_TimelineAbstractRenderer::selectedItem()
{
TimelineAbstractRenderer renderer;
- QSignalSpy spy(&renderer, SIGNAL(selectedItemChanged(int)));
+ QSignalSpy spy(&renderer, &TimelineAbstractRenderer::selectedItemChanged);
QCOMPARE(spy.count(), 0);
QCOMPARE(renderer.selectedItem(), -1);
renderer.setSelectedItem(12);
@@ -62,7 +62,7 @@ void tst_TimelineAbstractRenderer::model()
{
TimelineAbstractRenderer renderer;
TimelineModelAggregator aggregator;
- QSignalSpy spy(&renderer, SIGNAL(modelChanged(TimelineModel*)));
+ QSignalSpy spy(&renderer, &TimelineAbstractRenderer::modelChanged);
QVERIFY(!renderer.modelDirty());
QCOMPARE(spy.count(), 0);
TimelineModel model(&aggregator);
@@ -80,7 +80,7 @@ void tst_TimelineAbstractRenderer::model()
void tst_TimelineAbstractRenderer::notes()
{
TimelineAbstractRenderer renderer;
- QSignalSpy spy(&renderer, SIGNAL(notesChanged(TimelineNotesModel*)));
+ QSignalSpy spy(&renderer, &TimelineAbstractRenderer::notesChanged);
QVERIFY(!renderer.notesDirty());
QCOMPARE(spy.count(), 0);
TimelineNotesModel notes;
@@ -98,7 +98,7 @@ void tst_TimelineAbstractRenderer::notes()
void tst_TimelineAbstractRenderer::zoomer()
{
TimelineAbstractRenderer renderer;
- QSignalSpy spy(&renderer, SIGNAL(zoomerChanged(TimelineZoomControl*)));
+ QSignalSpy spy(&renderer, &TimelineAbstractRenderer::zoomerChanged);
QCOMPARE(spy.count(), 0);
TimelineZoomControl zoomer;
QCOMPARE(renderer.zoomer(), static_cast<TimelineZoomControl *>(0));
diff --git a/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp b/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp
index 27b457ce3f..559b827de2 100644
--- a/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp
+++ b/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp
@@ -6,17 +6,19 @@
#include <tracing/timelinemodel_p.h>
#include <tracing/timelinemodelaggregator.h>
+using namespace Timeline;
+
static const int NumItems = 32;
static const qint64 ItemDuration = 1 << 19;
static const qint64 ItemSpacing = 1 << 20;
-class DummyModel : public Timeline::TimelineModel
+class DummyModel : public TimelineModel
{
Q_OBJECT
friend class tst_TimelineModel;
public:
- DummyModel(Timeline::TimelineModelAggregator *parent);
- DummyModel(QString displayName, Timeline::TimelineModelAggregator *parent);
+ DummyModel(TimelineModelAggregator *parent);
+ DummyModel(QString displayName, TimelineModelAggregator *parent);
int expandedRow(int) const { return 2; }
int collapsedRow(int) const { return 1; }
@@ -57,15 +59,15 @@ private slots:
void parentingOfEqualStarts();
private:
- Timeline::TimelineModelAggregator aggregator;
+ TimelineModelAggregator aggregator;
};
-DummyModel::DummyModel(Timeline::TimelineModelAggregator *parent) :
- Timeline::TimelineModel(parent)
+DummyModel::DummyModel(TimelineModelAggregator *parent) :
+ TimelineModel(parent)
{
}
-DummyModel::DummyModel(QString displayName, Timeline::TimelineModelAggregator *parent) :
+DummyModel::DummyModel(QString displayName, TimelineModelAggregator *parent) :
TimelineModel(parent)
{
setDisplayName(displayName);
@@ -90,7 +92,7 @@ void DummyModel::loadData()
}
tst_TimelineModel::tst_TimelineModel() :
- DefaultRowHeight(Timeline::TimelineModel::defaultRowHeight())
+ DefaultRowHeight(TimelineModel::defaultRowHeight())
{
}
@@ -147,7 +149,7 @@ void tst_TimelineModel::rowHeight()
QCOMPARE(dummy.rowHeight(0), 100);
QCOMPARE(dummy.rowHeight(1), 50);
- QSignalSpy expandedSpy(&dummy, SIGNAL(expandedRowHeightChanged(int,int)));
+ QSignalSpy expandedSpy(&dummy, &TimelineModel::expandedRowHeightChanged);
dummy.clear();
QCOMPARE(expandedSpy.count(), 1);
}
@@ -194,7 +196,7 @@ void tst_TimelineModel::height()
DummyModel dummy(&aggregator);
int heightAfterLastSignal = 0;
int heightChangedSignals = 0;
- connect(&dummy, &Timeline::TimelineModel::heightChanged, [&](){
+ connect(&dummy, &TimelineModel::heightChanged, [&] {
++heightChangedSignals;
heightAfterLastSignal = dummy.height();
});
@@ -237,7 +239,7 @@ void tst_TimelineModel::count()
QCOMPARE(dummy.count(), 0);
dummy.loadData();
QCOMPARE(dummy.count(), NumItems);
- QSignalSpy emptySpy(&dummy, SIGNAL(contentChanged()));
+ QSignalSpy emptySpy(&dummy, &TimelineModel::contentChanged);
dummy.clear();
QCOMPARE(emptySpy.count(), 1);
QCOMPARE(dummy.count(), 0);
@@ -283,7 +285,7 @@ void tst_TimelineModel::firstLast()
void tst_TimelineModel::expand()
{
DummyModel dummy(&aggregator);
- QSignalSpy spy(&dummy, SIGNAL(expandedChanged()));
+ QSignalSpy spy(&dummy, &TimelineModel::expandedChanged);
QVERIFY(!dummy.expanded());
dummy.setExpanded(true);
QVERIFY(dummy.expanded());
@@ -302,7 +304,7 @@ void tst_TimelineModel::expand()
void tst_TimelineModel::hide()
{
DummyModel dummy(&aggregator);
- QSignalSpy spy(&dummy, SIGNAL(hiddenChanged()));
+ QSignalSpy spy(&dummy, &TimelineModel::hiddenChanged);
QVERIFY(!dummy.hidden());
dummy.setHidden(true);
QVERIFY(dummy.hidden());
@@ -322,7 +324,7 @@ void tst_TimelineModel::displayName()
{
QLatin1String name("testest");
DummyModel dummy(name, &aggregator);
- QSignalSpy spy(&dummy, SIGNAL(displayNameChanged()));
+ QSignalSpy spy(&dummy, &TimelineModel::displayNameChanged);
QCOMPARE(dummy.displayName(), name);
QCOMPARE(spy.count(), 0);
dummy.setDisplayName(name);
@@ -336,7 +338,7 @@ void tst_TimelineModel::displayName()
void tst_TimelineModel::defaultValues()
{
- Timeline::TimelineModel dummy(&aggregator);
+ TimelineModel dummy(&aggregator);
QCOMPARE(dummy.location(0), QVariantMap());
QCOMPARE(dummy.handlesTypeId(0), false);
QCOMPARE(dummy.relativeHeight(0), 1.0);
@@ -361,7 +363,7 @@ void tst_TimelineModel::row()
void tst_TimelineModel::colorByHue()
{
- Timeline::TimelineModelAggregator aggregator;
+ TimelineModelAggregator aggregator;
DummyModel dummy(&aggregator);
QCOMPARE(dummy.colorByHue(10), QColor::fromHsl(10, 150, 166).rgb());
QCOMPARE(dummy.colorByHue(500), QColor::fromHsl(140, 150, 166).rgb());
@@ -410,7 +412,7 @@ void tst_TimelineModel::insertStartEnd()
void tst_TimelineModel::rowCount()
{
DummyModel dummy(&aggregator);
- QSignalSpy contentSpy(&dummy, SIGNAL(contentChanged()));
+ QSignalSpy contentSpy(&dummy, &TimelineModel::contentChanged);
QCOMPARE(dummy.rowCount(), 1);
dummy.setExpanded(true);
QCOMPARE(dummy.rowCount(), 1);
diff --git a/tests/auto/tracing/timelinemodelaggregator/tst_timelinemodelaggregator.cpp b/tests/auto/tracing/timelinemodelaggregator/tst_timelinemodelaggregator.cpp
index 77c9c155e5..ac30ff0ad3 100644
--- a/tests/auto/tracing/timelinemodelaggregator/tst_timelinemodelaggregator.cpp
+++ b/tests/auto/tracing/timelinemodelaggregator/tst_timelinemodelaggregator.cpp
@@ -4,6 +4,8 @@
#include <QtTest>
#include <tracing/timelinemodelaggregator.h>
+using namespace Timeline;
+
class tst_TimelineModelAggregator : public QObject
{
Q_OBJECT
@@ -14,9 +16,9 @@ private slots:
void prevNext();
};
-class HeightTestModel : public Timeline::TimelineModel {
+class HeightTestModel : public TimelineModel {
public:
- HeightTestModel(Timeline::TimelineModelAggregator *parent) : TimelineModel(parent)
+ HeightTestModel(TimelineModelAggregator *parent) : TimelineModel(parent)
{
insert(0, 1, 1);
}
@@ -24,11 +26,11 @@ public:
void tst_TimelineModelAggregator::height()
{
- Timeline::TimelineModelAggregator aggregator;
+ TimelineModelAggregator aggregator;
QCOMPARE(aggregator.height(), 0);
- QSignalSpy heightSpy(&aggregator, SIGNAL(heightChanged()));
- Timeline::TimelineModel *model = new Timeline::TimelineModel(&aggregator);
+ QSignalSpy heightSpy(&aggregator, &TimelineModelAggregator::heightChanged);
+ TimelineModel *model = new TimelineModel(&aggregator);
aggregator.addModel(model);
QCOMPARE(aggregator.height(), 0);
QCOMPARE(heightSpy.count(), 0);
@@ -42,15 +44,15 @@ void tst_TimelineModelAggregator::height()
void tst_TimelineModelAggregator::addRemoveModel()
{
- Timeline::TimelineNotesModel notes;
- Timeline::TimelineModelAggregator aggregator;
+ TimelineNotesModel notes;
+ TimelineModelAggregator aggregator;
aggregator.setNotes(&notes);
- QSignalSpy spy(&aggregator, SIGNAL(modelsChanged()));
+ QSignalSpy spy(&aggregator, &TimelineModelAggregator::modelsChanged);
QCOMPARE(aggregator.notes(), &notes);
- Timeline::TimelineModel *model1 = new Timeline::TimelineModel(&aggregator);
- Timeline::TimelineModel *model2 = new Timeline::TimelineModel(&aggregator);
+ TimelineModel *model1 = new TimelineModel(&aggregator);
+ TimelineModel *model2 = new TimelineModel(&aggregator);
aggregator.addModel(model1);
QCOMPARE(spy.count(), 1);
QCOMPARE(aggregator.modelCount(), 1);
@@ -75,10 +77,10 @@ void tst_TimelineModelAggregator::addRemoveModel()
QCOMPARE(aggregator.modelCount(), 0);
}
-class PrevNextTestModel : public Timeline::TimelineModel
+class PrevNextTestModel : public TimelineModel
{
public:
- PrevNextTestModel(Timeline::TimelineModelAggregator *parent) : TimelineModel(parent)
+ PrevNextTestModel(TimelineModelAggregator *parent) : TimelineModel(parent)
{
for (int i = 0; i < 20; ++i)
insert(i + modelId(), i * modelId(), modelId());
@@ -87,14 +89,14 @@ public:
void tst_TimelineModelAggregator::prevNext()
{
- Timeline::TimelineModelAggregator aggregator;
+ TimelineModelAggregator aggregator;
aggregator.generateModelId(); // start modelIds at 1
aggregator.addModel(new PrevNextTestModel(&aggregator));
aggregator.addModel(new PrevNextTestModel(&aggregator));
aggregator.addModel(new PrevNextTestModel(&aggregator));
// Add an empty model to trigger the special code paths that skip it
- aggregator.addModel(new Timeline::TimelineModel(&aggregator));
+ aggregator.addModel(new TimelineModel(&aggregator));
QLatin1String item("item");
QLatin1String model("model");
QVariantMap result;
diff --git a/tests/auto/tracing/timelinenotesmodel/tst_timelinenotesmodel.cpp b/tests/auto/tracing/timelinenotesmodel/tst_timelinenotesmodel.cpp
index d01db8f12e..d00ba47154 100644
--- a/tests/auto/tracing/timelinenotesmodel/tst_timelinenotesmodel.cpp
+++ b/tests/auto/tracing/timelinenotesmodel/tst_timelinenotesmodel.cpp
@@ -63,7 +63,7 @@ void tst_TimelineNotesModel::addRemove()
TestModel model(&aggregator);
notes.addTimelineModel(&model);
- QSignalSpy spy(&notes, SIGNAL(changed(int,int,int)));
+ QSignalSpy spy(&notes, &TestNotesModel::changed);
int id = notes.add(model.modelId(), 0, QLatin1String("xyz"));
QCOMPARE(spy.count(), 1);
QCOMPARE(notes.isModified(), true);
@@ -129,7 +129,7 @@ void tst_TimelineNotesModel::modify()
TestNotesModel notes;
TestModel model(&aggregator);
notes.addTimelineModel(&model);
- QSignalSpy spy(&notes, SIGNAL(changed(int,int,int)));
+ QSignalSpy spy(&notes, &TestNotesModel::changed);
int id = notes.add(model.modelId(), 0, QLatin1String("a"));
QCOMPARE(spy.count(), 1);
notes.resetModified();
diff --git a/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp b/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp
index 4e00fc26d5..3e1c936c0d 100644
--- a/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp
+++ b/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp
@@ -5,11 +5,13 @@
#include <QColor>
#include <tracing/timelinezoomcontrol.h>
+using namespace Timeline;
+
class tst_TimelineZoomControl : public QObject
{
Q_OBJECT
private:
- void verifyWindow(const Timeline::TimelineZoomControl &zoomControl);
+ void verifyWindow(const TimelineZoomControl &zoomControl);
private slots:
void trace();
@@ -18,7 +20,7 @@ private slots:
void selection();
};
-void tst_TimelineZoomControl::verifyWindow(const Timeline::TimelineZoomControl &zoomControl)
+void tst_TimelineZoomControl::verifyWindow(const TimelineZoomControl &zoomControl)
{
QVERIFY(zoomControl.windowStart() <= zoomControl.rangeStart());
QVERIFY(zoomControl.windowEnd() >= zoomControl.rangeEnd());
@@ -28,8 +30,8 @@ void tst_TimelineZoomControl::verifyWindow(const Timeline::TimelineZoomControl &
void tst_TimelineZoomControl::trace()
{
- Timeline::TimelineZoomControl zoomControl;
- QSignalSpy spy(&zoomControl, SIGNAL(traceChanged(qint64,qint64)));
+ TimelineZoomControl zoomControl;
+ QSignalSpy spy(&zoomControl, &TimelineZoomControl::traceChanged);
QCOMPARE(zoomControl.traceStart(), -1);
QCOMPARE(zoomControl.traceEnd(), -1);
QCOMPARE(zoomControl.traceDuration(), 0);
@@ -49,18 +51,18 @@ void tst_TimelineZoomControl::trace()
void tst_TimelineZoomControl::window()
{
- Timeline::TimelineZoomControl zoomControl;
+ TimelineZoomControl zoomControl;
QTimer timer;
timer.setSingleShot(true);
- connect(&timer, &QTimer::timeout, [&](){
+ connect(&timer, &QTimer::timeout, [&] {
QVERIFY(zoomControl.windowLocked());
zoomControl.setWindowLocked(false);
});
int numWindowChanges = 0;
- connect(&zoomControl, &Timeline::TimelineZoomControl::windowChanged,
+ connect(&zoomControl, &TimelineZoomControl::windowChanged,
[&](qint64, qint64) {
verifyWindow(zoomControl);
@@ -98,8 +100,7 @@ void tst_TimelineZoomControl::window()
zoomControl.setRange(152000, 152005); // move right
QMetaObject::Connection connection = connect(
- &zoomControl, &Timeline::TimelineZoomControl::windowMovingChanged,
- [&](bool moving) {
+ &zoomControl, &TimelineZoomControl::windowMovingChanged, [&](bool moving) {
if (moving)
return;
@@ -129,7 +130,7 @@ void tst_TimelineZoomControl::window()
disconnect(connection);
bool stopDetected = false;
- connect(&zoomControl, &Timeline::TimelineZoomControl::windowMovingChanged, [&](bool moving) {
+ connect(&zoomControl, &TimelineZoomControl::windowMovingChanged, [&](bool moving) {
if (!moving) {
QCOMPARE(stopDetected, false);
stopDetected = true;
@@ -148,8 +149,8 @@ void tst_TimelineZoomControl::window()
void tst_TimelineZoomControl::range()
{
- Timeline::TimelineZoomControl zoomControl;
- QSignalSpy spy(&zoomControl, SIGNAL(rangeChanged(qint64,qint64)));
+ TimelineZoomControl zoomControl;
+ QSignalSpy spy(&zoomControl, &TimelineZoomControl::rangeChanged);
QCOMPARE(zoomControl.rangeStart(), -1);
QCOMPARE(zoomControl.rangeEnd(), -1);
QCOMPARE(zoomControl.rangeDuration(), 0);
@@ -175,8 +176,8 @@ void tst_TimelineZoomControl::range()
void tst_TimelineZoomControl::selection()
{
- Timeline::TimelineZoomControl zoomControl;
- QSignalSpy spy(&zoomControl, SIGNAL(selectionChanged(qint64,qint64)));
+ TimelineZoomControl zoomControl;
+ QSignalSpy spy(&zoomControl, &TimelineZoomControl::selectionChanged);
QCOMPARE(zoomControl.selectionStart(), -1);
QCOMPARE(zoomControl.selectionEnd(), -1);
QCOMPARE(zoomControl.selectionDuration(), 0);
diff --git a/tests/auto/utils/CMakeLists.txt b/tests/auto/utils/CMakeLists.txt
index 29548a5680..e78cb3380c 100644
--- a/tests/auto/utils/CMakeLists.txt
+++ b/tests/auto/utils/CMakeLists.txt
@@ -1,8 +1,9 @@
add_subdirectory(ansiescapecodehandler)
-add_subdirectory(asynctask)
+add_subdirectory(async)
add_subdirectory(commandline)
add_subdirectory(deviceshell)
add_subdirectory(expected)
+add_subdirectory(filepath)
add_subdirectory(fileutils)
add_subdirectory(fsengine)
add_subdirectory(fuzzymatcher)
@@ -10,9 +11,10 @@ add_subdirectory(indexedcontainerproxyconstiterator)
add_subdirectory(mathutils)
add_subdirectory(multicursor)
add_subdirectory(persistentsettings)
-add_subdirectory(qtcprocess)
+add_subdirectory(process)
add_subdirectory(settings)
add_subdirectory(stringutils)
-add_subdirectory(tasktree)
add_subdirectory(templateengine)
add_subdirectory(treemodel)
+add_subdirectory(text)
+add_subdirectory(unixdevicefileaccess)
diff --git a/tests/auto/utils/async/CMakeLists.txt b/tests/auto/utils/async/CMakeLists.txt
new file mode 100644
index 0000000000..0e6de32f56
--- /dev/null
+++ b/tests/auto/utils/async/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_test(tst_utils_async
+ DEPENDS Utils
+ SOURCES tst_async.cpp
+)
diff --git a/tests/auto/utils/async/async.qbs b/tests/auto/utils/async/async.qbs
new file mode 100644
index 0000000000..696b0959a5
--- /dev/null
+++ b/tests/auto/utils/async/async.qbs
@@ -0,0 +1,7 @@
+import qbs
+
+QtcAutotest {
+ name: "Async autotest"
+ Depends { name: "Utils" }
+ files: "tst_async.cpp"
+}
diff --git a/tests/auto/utils/async/tst_async.cpp b/tests/auto/utils/async/tst_async.cpp
new file mode 100644
index 0000000000..bf7cae7eff
--- /dev/null
+++ b/tests/auto/utils/async/tst_async.cpp
@@ -0,0 +1,596 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <utils/algorithm.h>
+#include <utils/async.h>
+
+#include <QtTest>
+
+using namespace Utils;
+
+class tst_Async : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void runAsync();
+ void crefFunction();
+ void onResultReady();
+ void futureSynchonizer();
+ void taskTree();
+ void mapReduce_data();
+ void mapReduce();
+private:
+ QThreadPool m_threadPool;
+};
+
+void report3(QPromise<int> &promise)
+{
+ promise.addResult(0);
+ promise.addResult(2);
+ promise.addResult(1);
+}
+
+void reportN(QPromise<double> &promise, int n)
+{
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+}
+
+void reportString1(QPromise<QString> &promise, const QString &s)
+{
+ promise.addResult(s);
+}
+
+void reportString2(QPromise<QString> &promise, QString s)
+{
+ promise.addResult(s);
+}
+
+class Callable {
+public:
+ void operator()(QPromise<double> &promise, int n) const
+ {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ }
+};
+
+class MyObject {
+public:
+ static void staticMember0(QPromise<double> &promise)
+ {
+ promise.addResult(0);
+ promise.addResult(2);
+ promise.addResult(1);
+ }
+
+ static void staticMember1(QPromise<double> &promise, int n)
+ {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ }
+
+ void member0(QPromise<double> &promise) const
+ {
+ promise.addResult(0);
+ promise.addResult(2);
+ promise.addResult(1);
+ }
+
+ void member1(QPromise<double> &promise, int n) const
+ {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ }
+
+ void memberString1(QPromise<QString> &promise, const QString &s) const
+ {
+ promise.addResult(s);
+ }
+
+ void memberString2(QPromise<QString> &promise, QString s) const
+ {
+ promise.addResult(s);
+ }
+
+ void nonConstMember(QPromise<double> &promise)
+ {
+ promise.addResult(0);
+ promise.addResult(2);
+ promise.addResult(1);
+ }
+};
+
+template <typename...>
+struct FutureArgType;
+
+template <typename Arg>
+struct FutureArgType<QFuture<Arg>>
+{
+ using Type = Arg;
+};
+
+template <typename...>
+struct ConcurrentResultType;
+
+template<typename Function, typename ...Args>
+struct ConcurrentResultType<Function, Args...>
+{
+ using Type = typename FutureArgType<decltype(QtConcurrent::run(
+ std::declval<Function>(), std::declval<Args>()...))>::Type;
+};
+
+template <typename Function, typename ...Args,
+ typename ResultType = typename ConcurrentResultType<Function, Args...>::Type>
+std::shared_ptr<Async<ResultType>> createAsyncTask(Function &&function, Args &&...args)
+{
+ auto asyncTask = std::make_shared<Async<ResultType>>();
+ asyncTask->setConcurrentCallData(std::forward<Function>(function), std::forward<Args>(args)...);
+ asyncTask->start();
+ return asyncTask;
+}
+
+void tst_Async::runAsync()
+{
+ // free function pointer
+ QCOMPARE(createAsyncTask(&report3)->results(),
+ QList<int>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(&report3).results(),
+ QList<int>({0, 2, 1}));
+ QCOMPARE(createAsyncTask(report3)->results(),
+ QList<int>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(report3).results(),
+ QList<int>({0, 2, 1}));
+
+ QCOMPARE(createAsyncTask(reportN, 4)->results(),
+ QList<double>({0, 0, 0, 0}));
+ QCOMPARE(Utils::asyncRun(reportN, 4).results(),
+ QList<double>({0, 0, 0, 0}));
+ QCOMPARE(createAsyncTask(reportN, 2)->results(),
+ QList<double>({0, 0}));
+ QCOMPARE(Utils::asyncRun(reportN, 2).results(),
+ QList<double>({0, 0}));
+
+ QString s = QLatin1String("string");
+ const QString &crs = QLatin1String("cr string");
+ const QString cs = QLatin1String("c string");
+
+ QCOMPARE(createAsyncTask(reportString1, s)->results(),
+ QList<QString>({s}));
+ QCOMPARE(Utils::asyncRun(reportString1, s).results(),
+ QList<QString>({s}));
+ QCOMPARE(createAsyncTask(reportString1, crs)->results(),
+ QList<QString>({crs}));
+ QCOMPARE(Utils::asyncRun(reportString1, crs).results(),
+ QList<QString>({crs}));
+ QCOMPARE(createAsyncTask(reportString1, cs)->results(),
+ QList<QString>({cs}));
+ QCOMPARE(Utils::asyncRun(reportString1, cs).results(),
+ QList<QString>({cs}));
+ QCOMPARE(createAsyncTask(reportString1, QString(QLatin1String("rvalue")))->results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+ QCOMPARE(Utils::asyncRun(reportString1, QString(QLatin1String("rvalue"))).results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+
+ QCOMPARE(createAsyncTask(reportString2, s)->results(),
+ QList<QString>({s}));
+ QCOMPARE(Utils::asyncRun(reportString2, s).results(),
+ QList<QString>({s}));
+ QCOMPARE(createAsyncTask(reportString2, crs)->results(),
+ QList<QString>({crs}));
+ QCOMPARE(Utils::asyncRun(reportString2, crs).results(),
+ QList<QString>({crs}));
+ QCOMPARE(createAsyncTask(reportString2, cs)->results(),
+ QList<QString>({cs}));
+ QCOMPARE(Utils::asyncRun(reportString2, cs).results(),
+ QList<QString>({cs}));
+ QCOMPARE(createAsyncTask(reportString2, QString(QLatin1String("rvalue")))->results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+ QCOMPARE(Utils::asyncRun(reportString2, QString(QLatin1String("rvalue"))).results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+
+ // lambda
+ QCOMPARE(createAsyncTask([](QPromise<double> &promise, int n) {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ }, 3)->results(),
+ QList<double>({0, 0, 0}));
+ QCOMPARE(Utils::asyncRun([](QPromise<double> &promise, int n) {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ }, 3).results(),
+ QList<double>({0, 0, 0}));
+
+ // std::function
+ const std::function<void(QPromise<double>&,int)> fun = [](QPromise<double> &promise, int n) {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ };
+ QCOMPARE(createAsyncTask(fun, 2)->results(),
+ QList<double>({0, 0}));
+ QCOMPARE(Utils::asyncRun(fun, 2).results(),
+ QList<double>({0, 0}));
+
+ // operator()
+ QCOMPARE(createAsyncTask(Callable(), 3)->results(),
+ QList<double>({0, 0, 0}));
+ QCOMPARE(Utils::asyncRun(Callable(), 3).results(),
+ QList<double>({0, 0, 0}));
+ const Callable c{};
+ QCOMPARE(createAsyncTask(c, 2)->results(),
+ QList<double>({0, 0}));
+ QCOMPARE(Utils::asyncRun(c, 2).results(),
+ QList<double>({0, 0}));
+
+ // static member functions
+ QCOMPARE(createAsyncTask(&MyObject::staticMember0)->results(),
+ QList<double>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(&MyObject::staticMember0).results(),
+ QList<double>({0, 2, 1}));
+ QCOMPARE(createAsyncTask(&MyObject::staticMember1, 2)->results(),
+ QList<double>({0, 0}));
+ QCOMPARE(Utils::asyncRun(&MyObject::staticMember1, 2).results(),
+ QList<double>({0, 0}));
+
+ // member functions
+ const MyObject obj{};
+ QCOMPARE(createAsyncTask(&MyObject::member0, &obj)->results(),
+ QList<double>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(&MyObject::member0, &obj).results(),
+ QList<double>({0, 2, 1}));
+ QCOMPARE(createAsyncTask(&MyObject::member1, &obj, 4)->results(),
+ QList<double>({0, 0, 0, 0}));
+ QCOMPARE(Utils::asyncRun(&MyObject::member1, &obj, 4).results(),
+ QList<double>({0, 0, 0, 0}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, s)->results(),
+ QList<QString>({s}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString1, &obj, s).results(),
+ QList<QString>({s}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, crs)->results(),
+ QList<QString>({crs}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString1, &obj, crs).results(),
+ QList<QString>({crs}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, cs)->results(),
+ QList<QString>({cs}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString1, &obj, cs).results(),
+ QList<QString>({cs}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue")))->results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, s)->results(),
+ QList<QString>({s}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString2, &obj, s).results(),
+ QList<QString>({s}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, crs)->results(),
+ QList<QString>({crs}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString2, &obj, crs).results(),
+ QList<QString>({crs}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, cs)->results(),
+ QList<QString>({cs}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString2, &obj, cs).results(),
+ QList<QString>({cs}));
+ QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue")))->results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+ QCOMPARE(Utils::asyncRun(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(),
+ QList<QString>({QString(QLatin1String("rvalue"))}));
+ MyObject nonConstObj{};
+ QCOMPARE(createAsyncTask(&MyObject::nonConstMember, &nonConstObj)->results(),
+ QList<double>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(&MyObject::nonConstMember, &nonConstObj).results(),
+ QList<double>({0, 2, 1}));
+}
+
+void tst_Async::crefFunction()
+{
+ // free function pointer with promise
+ auto fun = &report3;
+ QCOMPARE(createAsyncTask(std::cref(fun))->results(),
+ QList<int>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(std::cref(fun)).results(),
+ QList<int>({0, 2, 1}));
+
+ // lambda with promise
+ auto lambda = [](QPromise<double> &promise, int n) {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ };
+ QCOMPARE(createAsyncTask(std::cref(lambda), 3)->results(),
+ QList<double>({0, 0, 0}));
+ QCOMPARE(Utils::asyncRun(std::cref(lambda), 3).results(),
+ QList<double>({0, 0, 0}));
+
+ // std::function with promise
+ const std::function<void(QPromise<double>&,int)> funObj = [](QPromise<double> &promise, int n) {
+ for (int i = 0; i < n; ++i)
+ promise.addResult(0);
+ };
+ QCOMPARE(createAsyncTask(std::cref(funObj), 2)->results(),
+ QList<double>({0, 0}));
+ QCOMPARE(Utils::asyncRun(std::cref(funObj), 2).results(),
+ QList<double>({0, 0}));
+
+ // callable with promise
+ const Callable c{};
+ QCOMPARE(createAsyncTask(std::cref(c), 2)->results(),
+ QList<double>({0, 0}));
+ QCOMPARE(Utils::asyncRun(std::cref(c), 2).results(),
+ QList<double>({0, 0}));
+
+ // member functions with promise
+ auto member = &MyObject::member0;
+ const MyObject obj{};
+ QCOMPARE(createAsyncTask(std::cref(member), &obj)->results(),
+ QList<double>({0, 2, 1}));
+ QCOMPARE(Utils::asyncRun(std::cref(member), &obj).results(),
+ QList<double>({0, 2, 1}));
+}
+
+class ObjWithProperty : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void setValue(const QString &s)
+ {
+ value = s;
+ }
+
+public:
+ QString value;
+};
+
+void tst_Async::onResultReady()
+{
+ { // lambda
+ QObject context;
+ QFuture<QString> f = Utils::asyncRun([](QPromise<QString> &fi) {
+ fi.addResult("Hi");
+ fi.addResult("there");
+ });
+ int count = 0;
+ QString res;
+ Utils::onResultReady(f, &context, [&count, &res](const QString &s) {
+ ++count;
+ res = s;
+ });
+ f.waitForFinished();
+ QCoreApplication::processEvents();
+ QCOMPARE(count, 2);
+ QCOMPARE(res, QString("there"));
+ }
+ { // lambda with guard
+ QFuture<QString> f = Utils::asyncRun([](QPromise<QString> &fi) {
+ fi.addResult("Hi");
+ fi.addResult("there");
+ });
+ int count = 0;
+ ObjWithProperty obj;
+ Utils::onResultReady(f, &obj, [&count, &obj](const QString &s) {
+ ++count;
+ obj.setValue(s);
+ });
+ f.waitForFinished();
+ QCoreApplication::processEvents();
+ QCOMPARE(count, 2);
+ QCOMPARE(obj.value, QString("there"));
+ }
+ { // member
+ QFuture<QString> f = Utils::asyncRun([] { return QString("Hi"); });
+ ObjWithProperty obj;
+ Utils::onResultReady(f, &obj, &ObjWithProperty::setValue);
+ f.waitForFinished();
+ QCoreApplication::processEvents();
+ QCOMPARE(obj.value, QString("Hi"));
+ }
+}
+
+void tst_Async::futureSynchonizer()
+{
+ auto lambda = [](QPromise<int> &promise) {
+ while (true) {
+ if (promise.isCanceled()) {
+ promise.future().cancel();
+ promise.finish();
+ return;
+ }
+ QThread::msleep(100);
+ }
+ };
+
+ FutureSynchronizer synchronizer;
+ synchronizer.setCancelOnWait(false);
+ {
+ Async<int> task;
+ task.setConcurrentCallData(lambda);
+ task.setFutureSynchronizer(&synchronizer);
+ task.start();
+ QThread::msleep(10);
+ // We assume here that worker thread will still work for about 90 ms.
+ QVERIFY(!task.isCanceled());
+ QVERIFY(!task.isDone());
+ }
+ synchronizer.flushFinishedFutures();
+ QVERIFY(!synchronizer.isEmpty());
+ // The destructor of synchronizer should wait for about 90 ms for worker thread to be canceled
+}
+
+void multiplyBy2(QPromise<int> &promise, int input) { promise.addResult(input * 2); }
+
+void tst_Async::taskTree()
+{
+ using namespace Tasking;
+
+ int value = 1;
+
+ const auto setupIntAsync = [&](Async<int> &task) {
+ task.setConcurrentCallData(multiplyBy2, value);
+ };
+ const auto handleIntAsync = [&](const Async<int> &task) {
+ value = task.result();
+ };
+
+ const Group root {
+ AsyncTask<int>(setupIntAsync, handleIntAsync),
+ AsyncTask<int>(setupIntAsync, handleIntAsync),
+ AsyncTask<int>(setupIntAsync, handleIntAsync),
+ AsyncTask<int>(setupIntAsync, handleIntAsync),
+ };
+
+ TaskTree tree(root);
+
+ QEventLoop eventLoop;
+ connect(&tree, &TaskTree::done, &eventLoop, &QEventLoop::quit);
+ tree.start();
+ eventLoop.exec();
+
+ QCOMPARE(value, 16);
+}
+
+static int returnxx(int x)
+{
+ return x * x;
+}
+
+static void returnxxWithPromise(QPromise<int> &promise, int x)
+{
+ promise.addResult(x * x);
+}
+
+static double s_sum = 0;
+static QList<double> s_results;
+
+void tst_Async::mapReduce_data()
+{
+ using namespace Tasking;
+
+ QTest::addColumn<Group>("root");
+ QTest::addColumn<double>("sum");
+ QTest::addColumn<QList<double>>("results");
+
+ const auto initTree = [] {
+ s_sum = 0;
+ s_results.append(s_sum);
+ };
+ const auto setupAsync = [](Async<int> &task, int input) {
+ task.setConcurrentCallData(returnxx, input);
+ };
+ const auto setupAsyncWithFI = [](Async<int> &task, int input) {
+ task.setConcurrentCallData(returnxxWithPromise, input);
+ };
+ const auto setupAsyncWithTP = [this](Async<int> &task, int input) {
+ task.setConcurrentCallData(returnxx, input);
+ task.setThreadPool(&m_threadPool);
+ };
+ const auto handleAsync = [](const Async<int> &task) {
+ s_sum += task.result();
+ s_results.append(task.result());
+ };
+ const auto handleTreeParallel = [] {
+ s_sum /= 2;
+ s_results.append(s_sum);
+ Utils::sort(s_results); // mapping order is undefined
+ };
+ const auto handleTreeSequential = [] {
+ s_sum /= 2;
+ s_results.append(s_sum);
+ };
+
+ using namespace Tasking;
+ using namespace std::placeholders;
+
+ using SetupHandler = std::function<void(Async<int> &task, int input)>;
+ using DoneHandler = std::function<void()>;
+
+ const auto createTask = [=](const TaskItem &executeMode,
+ const SetupHandler &setupHandler,
+ const DoneHandler &doneHandler) {
+ return Group {
+ executeMode,
+ OnGroupSetup(initTree),
+ AsyncTask<int>(std::bind(setupHandler, _1, 1), handleAsync),
+ AsyncTask<int>(std::bind(setupHandler, _1, 2), handleAsync),
+ AsyncTask<int>(std::bind(setupHandler, _1, 3), handleAsync),
+ AsyncTask<int>(std::bind(setupHandler, _1, 4), handleAsync),
+ AsyncTask<int>(std::bind(setupHandler, _1, 5), handleAsync),
+ OnGroupDone(doneHandler)
+ };
+ };
+
+ const Group parallelRoot = createTask(parallel, setupAsync, handleTreeParallel);
+ const Group parallelRootWithFI = createTask(parallel, setupAsyncWithFI, handleTreeParallel);
+ const Group parallelRootWithTP = createTask(parallel, setupAsyncWithTP, handleTreeParallel);
+ const Group sequentialRoot = createTask(sequential, setupAsync, handleTreeSequential);
+ const Group sequentialRootWithFI = createTask(sequential, setupAsyncWithFI, handleTreeSequential);
+ const Group sequentialRootWithTP = createTask(sequential, setupAsyncWithTP, handleTreeSequential);
+
+ const double defaultSum = 27.5;
+ const QList<double> defaultResult{0., 1., 4., 9., 16., 25., 27.5};
+
+ QTest::newRow("Parallel") << parallelRoot << defaultSum << defaultResult;
+ QTest::newRow("ParallelWithFutureInterface") << parallelRootWithFI << defaultSum << defaultResult;
+ QTest::newRow("ParallelWithThreadPool") << parallelRootWithTP << defaultSum << defaultResult;
+ QTest::newRow("Sequential") << sequentialRoot << defaultSum << defaultResult;
+ QTest::newRow("SequentialWithFutureInterface") << sequentialRootWithFI << defaultSum << defaultResult;
+ QTest::newRow("SequentialWithThreadPool") << sequentialRootWithTP << defaultSum << defaultResult;
+
+ const auto setupSimpleAsync = [](Async<int> &task, int input) {
+ task.setConcurrentCallData([](int input) { return input * 2; }, input);
+ };
+ const auto handleSimpleAsync = [](const Async<int> &task) {
+ s_sum += task.result() / 4.;
+ s_results.append(s_sum);
+ };
+ const Group simpleRoot = {
+ sequential,
+ OnGroupSetup([] { s_sum = 0; }),
+ AsyncTask<int>(std::bind(setupSimpleAsync, _1, 1), handleSimpleAsync),
+ AsyncTask<int>(std::bind(setupSimpleAsync, _1, 2), handleSimpleAsync),
+ AsyncTask<int>(std::bind(setupSimpleAsync, _1, 3), handleSimpleAsync)
+ };
+ QTest::newRow("Simple") << simpleRoot << 3.0 << QList<double>({.5, 1.5, 3.});
+
+ const auto setupStringAsync = [](Async<int> &task, const QString &input) {
+ task.setConcurrentCallData([](const QString &input) -> int { return input.size(); }, input);
+ };
+ const auto handleStringAsync = [](const Async<int> &task) {
+ s_sum /= task.result();
+ };
+ const Group stringRoot = {
+ parallel,
+ OnGroupSetup([] { s_sum = 90.0; }),
+ AsyncTask<int>(std::bind(setupStringAsync, _1, "blubb"), handleStringAsync),
+ AsyncTask<int>(std::bind(setupStringAsync, _1, "foo"), handleStringAsync),
+ AsyncTask<int>(std::bind(setupStringAsync, _1, "blah"), handleStringAsync)
+ };
+ QTest::newRow("String") << stringRoot << 1.5 << QList<double>({});
+}
+
+void tst_Async::mapReduce()
+{
+ QThreadPool pool;
+
+ s_sum = 0;
+ s_results.clear();
+
+ using namespace Tasking;
+
+ QFETCH(Group, root);
+ QFETCH(double, sum);
+ QFETCH(QList<double>, results);
+
+ TaskTree tree(root);
+
+ QEventLoop eventLoop;
+ connect(&tree, &TaskTree::done, &eventLoop, &QEventLoop::quit);
+ tree.start();
+ eventLoop.exec();
+
+ QCOMPARE(s_results, results);
+ QCOMPARE(s_sum, sum);
+}
+
+QTEST_GUILESS_MAIN(tst_Async)
+
+#include "tst_async.moc"
diff --git a/tests/auto/utils/asynctask/CMakeLists.txt b/tests/auto/utils/asynctask/CMakeLists.txt
deleted file mode 100644
index 8b234dbb02..0000000000
--- a/tests/auto/utils/asynctask/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-add_qtc_test(tst_utils_asynctask
- DEPENDS Utils
- SOURCES tst_asynctask.cpp
-)
diff --git a/tests/auto/utils/asynctask/asynctask.qbs b/tests/auto/utils/asynctask/asynctask.qbs
deleted file mode 100644
index 8f478147e8..0000000000
--- a/tests/auto/utils/asynctask/asynctask.qbs
+++ /dev/null
@@ -1,7 +0,0 @@
-import qbs
-
-QtcAutotest {
- name: "AsyncTask autotest"
- Depends { name: "Utils" }
- files: "tst_asynctask.cpp"
-}
diff --git a/tests/auto/utils/asynctask/tst_asynctask.cpp b/tests/auto/utils/asynctask/tst_asynctask.cpp
deleted file mode 100644
index 83b3c82aed..0000000000
--- a/tests/auto/utils/asynctask/tst_asynctask.cpp
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "utils/asynctask.h"
-
-#include "utils/algorithm.h"
-
-#include <QtTest>
-
-using namespace Utils;
-
-class tst_AsyncTask : public QObject
-{
- Q_OBJECT
-
-private slots:
- void runAsync();
- void crefFunction();
- void futureSynchonizer();
- void taskTree();
- void mapReduce_data();
- void mapReduce();
-private:
- QThreadPool m_threadPool;
-};
-
-void report3(QFutureInterface<int> &fi)
-{
- fi.reportResults({0, 2, 1});
-}
-
-void reportN(QFutureInterface<double> &fi, int n)
-{
- fi.reportResults(QVector<double>(n, 0));
-}
-
-void reportString1(QFutureInterface<QString> &fi, const QString &s)
-{
- fi.reportResult(s);
-}
-
-void reportString2(QFutureInterface<QString> &fi, QString s)
-{
- fi.reportResult(s);
-}
-
-class Callable {
-public:
- void operator()(QFutureInterface<double> &fi, int n) const
- {
- fi.reportResults(QVector<double>(n, 0));
- }
-};
-
-class MyObject {
-public:
- static void staticMember0(QFutureInterface<double> &fi)
- {
- fi.reportResults({0, 2, 1});
- }
-
- static void staticMember1(QFutureInterface<double> &fi, int n)
- {
- fi.reportResults(QVector<double>(n, 0));
- }
-
- void member0(QFutureInterface<double> &fi) const
- {
- fi.reportResults({0, 2, 1});
- }
-
- void member1(QFutureInterface<double> &fi, int n) const
- {
- fi.reportResults(QVector<double>(n, 0));
- }
-
- void memberString1(QFutureInterface<QString> &fi, const QString &s) const
- {
- fi.reportResult(s);
- }
-
- void memberString2(QFutureInterface<QString> &fi, QString s) const
- {
- fi.reportResult(s);
- }
-
- void nonConstMember(QFutureInterface<double> &fi)
- {
- fi.reportResults({0, 2, 1});
- }
-};
-
-template <typename Function, typename ...Args,
- typename ResultType = typename Internal::resultType<Function>::type>
-std::shared_ptr<AsyncTask<ResultType>> createAsyncTask(const Function &function, const Args &...args)
-{
- auto asyncTask = std::make_shared<AsyncTask<ResultType>>();
- asyncTask->setAsyncCallData(function, args...);
- asyncTask->start();
- return asyncTask;
-}
-
-void tst_AsyncTask::runAsync()
-{
- // free function pointer
- QCOMPARE(createAsyncTask(&report3)->results(),
- QList<int>({0, 2, 1}));
- QCOMPARE(createAsyncTask(report3)->results(),
- QList<int>({0, 2, 1}));
-
- QCOMPARE(createAsyncTask(reportN, 4)->results(),
- QList<double>({0, 0, 0, 0}));
- QCOMPARE(createAsyncTask(reportN, 2)->results(),
- QList<double>({0, 0}));
-
- QString s = QLatin1String("string");
- const QString &crs = QLatin1String("cr string");
- const QString cs = QLatin1String("c string");
-
- QCOMPARE(createAsyncTask(reportString1, s)->results(),
- QList<QString>({s}));
- QCOMPARE(createAsyncTask(reportString1, crs)->results(),
- QList<QString>({crs}));
- QCOMPARE(createAsyncTask(reportString1, cs)->results(),
- QList<QString>({cs}));
- QCOMPARE(createAsyncTask(reportString1, QString(QLatin1String("rvalue")))->results(),
- QList<QString>({QString(QLatin1String("rvalue"))}));
-
- QCOMPARE(createAsyncTask(reportString2, s)->results(),
- QList<QString>({s}));
- QCOMPARE(createAsyncTask(reportString2, crs)->results(),
- QList<QString>({crs}));
- QCOMPARE(createAsyncTask(reportString2, cs)->results(),
- QList<QString>({cs}));
- QCOMPARE(createAsyncTask(reportString2, QString(QLatin1String("rvalue")))->results(),
- QList<QString>({QString(QLatin1String("rvalue"))}));
-
- // lambda
- QCOMPARE(createAsyncTask([](QFutureInterface<double> &fi, int n) {
- fi.reportResults(QVector<double>(n, 0));
- }, 3)->results(),
- QList<double>({0, 0, 0}));
-
- // std::function
- const std::function<void(QFutureInterface<double>&,int)> fun = [](QFutureInterface<double> &fi, int n) {
- fi.reportResults(QVector<double>(n, 0));
- };
- QCOMPARE(createAsyncTask(fun, 2)->results(),
- QList<double>({0, 0}));
-
- // operator()
- QCOMPARE(createAsyncTask(Callable(), 3)->results(),
- QList<double>({0, 0, 0}));
- const Callable c{};
- QCOMPARE(createAsyncTask(c, 2)->results(),
- QList<double>({0, 0}));
-
- // static member functions
- QCOMPARE(createAsyncTask(&MyObject::staticMember0)->results(),
- QList<double>({0, 2, 1}));
- QCOMPARE(createAsyncTask(&MyObject::staticMember1, 2)->results(),
- QList<double>({0, 0}));
-
- // member functions
- const MyObject obj{};
- QCOMPARE(createAsyncTask(&MyObject::member0, &obj)->results(),
- QList<double>({0, 2, 1}));
- QCOMPARE(createAsyncTask(&MyObject::member1, &obj, 4)->results(),
- QList<double>({0, 0, 0, 0}));
- QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, s)->results(),
- QList<QString>({s}));
- QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, crs)->results(),
- QList<QString>({crs}));
- QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, cs)->results(),
- QList<QString>({cs}));
- QCOMPARE(createAsyncTask(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue")))->results(),
- QList<QString>({QString(QLatin1String("rvalue"))}));
- QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, s)->results(),
- QList<QString>({s}));
- QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, crs)->results(),
- QList<QString>({crs}));
- QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, cs)->results(),
- QList<QString>({cs}));
- QCOMPARE(createAsyncTask(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue")))->results(),
- QList<QString>({QString(QLatin1String("rvalue"))}));
- MyObject nonConstObj{};
- QCOMPARE(createAsyncTask(&MyObject::nonConstMember, &nonConstObj)->results(),
- QList<double>({0, 2, 1}));
-}
-
-void tst_AsyncTask::crefFunction()
-{
- // free function pointer with future interface
- auto fun = &report3;
- QCOMPARE(createAsyncTask(std::cref(fun))->results(),
- QList<int>({0, 2, 1}));
-
- // lambda with future interface
- auto lambda = [](QFutureInterface<double> &fi, int n) {
- fi.reportResults(QVector<double>(n, 0));
- };
- QCOMPARE(createAsyncTask(std::cref(lambda), 3)->results(),
- QList<double>({0, 0, 0}));
-
- // std::function with future interface
- const std::function<void(QFutureInterface<double>&,int)> funObj = [](QFutureInterface<double> &fi, int n) {
- fi.reportResults(QVector<double>(n, 0));
- };
- QCOMPARE(createAsyncTask(std::cref(funObj), 2)->results(),
- QList<double>({0, 0}));
-
- // callable with future interface
- const Callable c{};
- QCOMPARE(createAsyncTask(std::cref(c), 2)->results(),
- QList<double>({0, 0}));
-
- // member functions with future interface
- auto member = &MyObject::member0;
- const MyObject obj{};
- QCOMPARE(createAsyncTask(std::cref(member), &obj)->results(),
- QList<double>({0, 2, 1}));
-}
-
-template <typename Function, typename ...Args,
- typename ResultType = typename Internal::resultType<Function>::type>
-typename AsyncTask<ResultType>::StartHandler startHandler(const Function &function, const Args &...args)
-{
- return [=] { return Utils::runAsync(function, args...); };
-}
-
-void tst_AsyncTask::futureSynchonizer()
-{
- auto lambda = [](QFutureInterface<int> &fi) {
- while (true) {
- if (fi.isCanceled()) {
- fi.reportCanceled();
- fi.reportFinished();
- return;
- }
- QThread::msleep(100);
- }
- };
-
- FutureSynchronizer synchronizer;
- {
- AsyncTask<int> task;
- task.setAsyncCallData(lambda);
- task.setFutureSynchronizer(&synchronizer);
- task.start();
- QThread::msleep(10);
- // We assume here that worker thread will still work for about 90 ms.
- QVERIFY(!task.isCanceled());
- QVERIFY(!task.isDone());
- }
- synchronizer.flushFinishedFutures();
- QVERIFY(!synchronizer.isEmpty());
- // The destructor of synchronizer should wait for about 90 ms for worker thread to be canceled
-}
-
-void multiplyBy2(QFutureInterface<int> &fi, int input) { fi.reportResult(input * 2); }
-
-void tst_AsyncTask::taskTree()
-{
- using namespace Tasking;
-
- int value = 1;
-
- const auto setupIntAsync = [&](AsyncTask<int> &task) {
- task.setAsyncCallData(multiplyBy2, value);
- };
- const auto handleIntAsync = [&](const AsyncTask<int> &task) {
- value = task.result();
- };
-
- const Group root {
- Async<int>(setupIntAsync, handleIntAsync),
- Async<int>(setupIntAsync, handleIntAsync),
- Async<int>(setupIntAsync, handleIntAsync),
- Async<int>(setupIntAsync, handleIntAsync),
- };
-
- TaskTree tree(root);
-
- QEventLoop eventLoop;
- connect(&tree, &TaskTree::done, &eventLoop, &QEventLoop::quit);
- tree.start();
- eventLoop.exec();
-
- QCOMPARE(value, 16);
-}
-
-static int returnxx(int x)
-{
- return x * x;
-}
-
-static void returnxxWithFI(QFutureInterface<int> &fi, int x)
-{
- fi.reportResult(x * x);
-}
-
-static double s_sum = 0;
-static QList<double> s_results;
-
-void tst_AsyncTask::mapReduce_data()
-{
- using namespace Tasking;
-
- QTest::addColumn<Group>("root");
- QTest::addColumn<double>("sum");
- QTest::addColumn<QList<double>>("results");
-
- const auto initTree = [] {
- s_sum = 0;
- s_results.append(s_sum);
- };
- const auto setupAsync = [](AsyncTask<int> &task, int input) {
- task.setAsyncCallData(returnxx, input);
- };
- const auto setupAsyncWithFI = [](AsyncTask<int> &task, int input) {
- task.setAsyncCallData(returnxxWithFI, input);
- };
- const auto setupAsyncWithTP = [this](AsyncTask<int> &task, int input) {
- task.setAsyncCallData(returnxx, input);
- task.setThreadPool(&m_threadPool);
- };
- const auto handleAsync = [](const AsyncTask<int> &task) {
- s_sum += task.result();
- s_results.append(task.result());
- };
- const auto handleTreeParallel = [] {
- s_sum /= 2;
- s_results.append(s_sum);
- Utils::sort(s_results); // mapping order is undefined
- };
- const auto handleTreeSequential = [] {
- s_sum /= 2;
- s_results.append(s_sum);
- };
-
- using namespace Tasking;
- using namespace std::placeholders;
-
- using SetupHandler = std::function<void(AsyncTask<int> &task, int input)>;
- using DoneHandler = std::function<void()>;
-
- const auto createTask = [=](const TaskItem &executeMode,
- const SetupHandler &setupHandler,
- const DoneHandler &doneHandler) {
- return Group {
- executeMode,
- OnGroupSetup(initTree),
- Async<int>(std::bind(setupHandler, _1, 1), handleAsync),
- Async<int>(std::bind(setupHandler, _1, 2), handleAsync),
- Async<int>(std::bind(setupHandler, _1, 3), handleAsync),
- Async<int>(std::bind(setupHandler, _1, 4), handleAsync),
- Async<int>(std::bind(setupHandler, _1, 5), handleAsync),
- OnGroupDone(doneHandler)
- };
- };
-
- const Group parallelRoot = createTask(parallel, setupAsync, handleTreeParallel);
- const Group parallelRootWithFI = createTask(parallel, setupAsyncWithFI, handleTreeParallel);
- const Group parallelRootWithTP = createTask(parallel, setupAsyncWithTP, handleTreeParallel);
- const Group sequentialRoot = createTask(sequential, setupAsync, handleTreeSequential);
- const Group sequentialRootWithFI = createTask(sequential, setupAsyncWithFI, handleTreeSequential);
- const Group sequentialRootWithTP = createTask(sequential, setupAsyncWithTP, handleTreeSequential);
-
- const double defaultSum = 27.5;
- const QList<double> defaultResult{0., 1., 4., 9., 16., 25., 27.5};
-
- QTest::newRow("Parallel") << parallelRoot << defaultSum << defaultResult;
- QTest::newRow("ParallelWithFutureInterface") << parallelRootWithFI << defaultSum << defaultResult;
- QTest::newRow("ParallelWithThreadPool") << parallelRootWithTP << defaultSum << defaultResult;
- QTest::newRow("Sequential") << sequentialRoot << defaultSum << defaultResult;
- QTest::newRow("SequentialWithFutureInterface") << sequentialRootWithFI << defaultSum << defaultResult;
- QTest::newRow("SequentialWithThreadPool") << sequentialRootWithTP << defaultSum << defaultResult;
-
- const auto setupSimpleAsync = [](AsyncTask<int> &task, int input) {
- task.setAsyncCallData([](int input) { return input * 2; }, input);
- };
- const auto handleSimpleAsync = [](const AsyncTask<int> &task) {
- s_sum += task.result() / 4.;
- s_results.append(s_sum);
- };
- const Group simpleRoot = {
- sequential,
- OnGroupSetup([] { s_sum = 0; }),
- Async<int>(std::bind(setupSimpleAsync, _1, 1), handleSimpleAsync),
- Async<int>(std::bind(setupSimpleAsync, _1, 2), handleSimpleAsync),
- Async<int>(std::bind(setupSimpleAsync, _1, 3), handleSimpleAsync)
- };
- QTest::newRow("Simple") << simpleRoot << 3.0 << QList<double>({.5, 1.5, 3.});
-
- const auto setupStringAsync = [](AsyncTask<int> &task, const QString &input) {
- task.setAsyncCallData([](const QString &input) -> int { return input.size(); }, input);
- };
- const auto handleStringAsync = [](const AsyncTask<int> &task) {
- s_sum /= task.result();
- };
- const Group stringRoot = {
- parallel,
- OnGroupSetup([] { s_sum = 90.0; }),
- Async<int>(std::bind(setupStringAsync, _1, "blubb"), handleStringAsync),
- Async<int>(std::bind(setupStringAsync, _1, "foo"), handleStringAsync),
- Async<int>(std::bind(setupStringAsync, _1, "blah"), handleStringAsync)
- };
- QTest::newRow("String") << stringRoot << 1.5 << QList<double>({});
-}
-
-void tst_AsyncTask::mapReduce()
-{
- QThreadPool pool;
-
- s_sum = 0;
- s_results.clear();
-
- using namespace Tasking;
-
- QFETCH(Group, root);
- QFETCH(double, sum);
- QFETCH(QList<double>, results);
-
- TaskTree tree(root);
-
- QEventLoop eventLoop;
- connect(&tree, &TaskTree::done, &eventLoop, &QEventLoop::quit);
- tree.start();
- eventLoop.exec();
-
- QCOMPARE(s_results, results);
- QCOMPARE(s_sum, sum);
-}
-
-QTEST_GUILESS_MAIN(tst_AsyncTask)
-
-#include "tst_asynctask.moc"
diff --git a/tests/auto/utils/commandline/tst_commandline.cpp b/tests/auto/utils/commandline/tst_commandline.cpp
index 05357d2526..2691b1e84a 100644
--- a/tests/auto/utils/commandline/tst_commandline.cpp
+++ b/tests/auto/utils/commandline/tst_commandline.cpp
@@ -7,8 +7,9 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/launcherinterface.h>
+#include <utils/macroexpander.h>
+#include <utils/process.h>
#include <utils/processinterface.h>
-#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <QObject>
@@ -29,7 +30,7 @@ private:
QString run(const CommandLine &cmd)
{
- QtcProcess p;
+ Process p;
p.setCommand(cmd);
p.setEnvironment(testEnv);
p.runBlocking();
@@ -123,6 +124,82 @@ private slots:
QString actual = run(shell);
QCOMPARE(actual, expected);
}
+
+ void testFromUserInput_data()
+ {
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("executable");
+ QTest::addColumn<QString>("arguments");
+
+ QTest::newRow("empty") << ""
+ << ""
+ << "";
+ QTest::newRow("command") << "command"
+ << "command"
+ << "";
+ QTest::newRow("command-with-args") << "command and args"
+ << "command"
+ << "and args";
+
+ if (!HostOsInfo::isWindowsHost()) {
+ QTest::newRow("command-with-space-slash") << "command\\ with-space and args"
+ << "command with-space"
+ << "and args";
+ QTest::newRow("command-with-space-single-quote") << "'command with-space' and args"
+ << "command with-space"
+ << "and args";
+ }
+ QTest::newRow("command-with-space-double-quote") << "\"command with-space\" and args"
+ << "command with-space"
+ << "and args";
+
+ QTest::newRow("command-with-space-double-quote-in-name")
+ << "\"command\\\"with-quote\" and args"
+ << "command\"with-quote"
+ << "and args";
+
+ QTest::newRow("inside-space-quoted") << "command\" \"withspace args here"
+ << "command withspace"
+ << "args here";
+ }
+
+ void testFromUserInput()
+ {
+ QFETCH(QString, input);
+ QFETCH(QString, executable);
+ QFETCH(QString, arguments);
+
+ CommandLine cmd = CommandLine::fromUserInput(input);
+ QCOMPARE(cmd.executable(), FilePath::fromUserInput(executable));
+ QCOMPARE(cmd.arguments(), arguments);
+ }
+
+ void testFromInputFails()
+ {
+ if (HostOsInfo::isWindowsHost())
+ QSKIP("The test does not work on Windows.");
+
+ CommandLine cmd = CommandLine::fromUserInput("command\\\\\\ with-space and args");
+ QEXPECT_FAIL("",
+ "CommandLine::fromUserInput (and FilePath::fromUserInput) does not handle "
+ "backslashes correctly",
+ Continue);
+ QCOMPARE(cmd.executable().fileName(), "command\\ with-space");
+ QCOMPARE(cmd.arguments(), "and args");
+ }
+
+ void testFromInputWithMacro()
+ {
+ MacroExpander expander;
+ expander.registerVariable("hello", "world var", [] { return "hello world"; });
+ CommandLine cmd = CommandLine::fromUserInput("command macroarg: %{hello}", &expander);
+ QCOMPARE(cmd.executable(), "command");
+
+ if (HostOsInfo::isWindowsHost())
+ QEXPECT_FAIL("", "Windows does not correctly quote macro arguments", Continue);
+
+ QCOMPARE(cmd.arguments(), "macroarg: 'hello world'");
+ }
};
int main(int argc, char *argv[])
diff --git a/tests/auto/utils/deviceshell/tst_deviceshell.cpp b/tests/auto/utils/deviceshell/tst_deviceshell.cpp
index 6080f99bf7..6e081495c3 100644
--- a/tests/auto/utils/deviceshell/tst_deviceshell.cpp
+++ b/tests/auto/utils/deviceshell/tst_deviceshell.cpp
@@ -3,16 +3,16 @@
#include <app/app_version.h>
+#include <utils/algorithm.h>
#include <utils/deviceshell.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/launcherinterface.h>
-#include <utils/mapreduce.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <utils/temporarydirectory.h>
#include <QObject>
+#include <QtConcurrent>
#include <QtTest>
using namespace Utils;
@@ -28,7 +28,7 @@ public:
}
private:
- void setupShellProcess(QtcProcess *shellProcess) override
+ void setupShellProcess(Process *shellProcess) override
{
shellProcess->setCommand(m_cmdLine);
}
@@ -38,7 +38,7 @@ private:
bool testDocker(const FilePath &executable)
{
- QtcProcess p;
+ Process p;
p.setCommand({executable, {"info", "--format", "{{.OSType}}"}});
p.runBlocking();
const QString platform = p.cleanedStdOut().trimmed();
@@ -312,15 +312,14 @@ private slots:
TestShell shell(cmdLine);
QCOMPARE(shell.state(), DeviceShell::State::Succeeded);
- QList<int> runs{1,2,3,4,5,6,7,8,9};
-
int maxDepth = 4;
int numMs = 0;
while (true) {
QElapsedTimer t;
t.start();
- RunResult result = shell.runInShell({"find", {"/usr", "-maxdepth", QString::number(maxDepth)}});
+ const RunResult result = shell.runInShell({"find", {"/usr", "-maxdepth",
+ QString::number(maxDepth)}});
numMs = t.elapsed();
qDebug() << "adjusted maxDepth" << maxDepth << "took" << numMs << "ms";
if (numMs < 100 || maxDepth == 1) {
@@ -329,15 +328,17 @@ private slots:
maxDepth--;
}
- QList<QByteArray> results = Utils::mapped<QList>(runs, [&shell, maxDepth](const int i) -> QByteArray{
+ const auto find = [&shell, maxDepth](int i) {
QElapsedTimer t;
t.start();
- RunResult result = shell.runInShell({"find", {"/usr", "-maxdepth", QString::number(maxDepth)}});
+ const RunResult result = shell.runInShell({"find", {"/usr", "-maxdepth",
+ QString::number(maxDepth)}});
qDebug() << i << "took" << t.elapsed() << "ms";
return result.stdOut;
- });
-
- QVERIFY (!Utils::anyOf(results, [&results](const QByteArray r){ return r != results[0]; }));
+ };
+ const QList<int> runs{1,2,3,4,5,6,7,8,9};
+ const QList<QByteArray> results = QtConcurrent::blockingMapped(runs, find);
+ QVERIFY(!Utils::anyOf(results, [&results](const QByteArray r) { return r != results[0]; }));
}
void testNoScript_data()
diff --git a/tests/auto/utils/filepath/CMakeLists.txt b/tests/auto/utils/filepath/CMakeLists.txt
new file mode 100644
index 0000000000..3d85eb0c64
--- /dev/null
+++ b/tests/auto/utils/filepath/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_test(tst_utils_filepath
+ DEPENDS Utils
+ SOURCES tst_filepath.cpp
+)
diff --git a/tests/auto/utils/filepath/filepath.qbs b/tests/auto/utils/filepath/filepath.qbs
new file mode 100644
index 0000000000..73c106b2e8
--- /dev/null
+++ b/tests/auto/utils/filepath/filepath.qbs
@@ -0,0 +1,11 @@
+import qbs
+
+QtcAutotest {
+ name: "FilePath autotest"
+ Depends { name: "Utils" }
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.cxxFlags: base.concat(["-Wno-trigraphs"])
+ }
+ files: "tst_filepath.cpp"
+}
diff --git a/tests/auto/utils/filepath/tst_filepath.cpp b/tests/auto/utils/filepath/tst_filepath.cpp
new file mode 100644
index 0000000000..3699ec6b2e
--- /dev/null
+++ b/tests/auto/utils/filepath/tst_filepath.cpp
@@ -0,0 +1,1688 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QRandomGenerator>
+#include <QtTest>
+
+#include <utils/algorithm.h>
+#include <utils/filepath.h>
+#include <utils/hostosinfo.h>
+#include <utils/link.h>
+
+using namespace Utils;
+
+namespace QTest {
+template<>
+char *toString(const FilePath &filePath)
+{
+ return qstrdup(filePath.toString().toLocal8Bit().constData());
+}
+} // namespace QTest
+
+class tst_filepath : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+
+ void isEmpty_data();
+ void isEmpty();
+
+ void parentDir_data();
+ void parentDir();
+
+ void isChildOf_data();
+ void isChildOf();
+
+ void fileName_data();
+ void fileName();
+
+ void calcRelativePath_data();
+ void calcRelativePath();
+
+ void relativePath_specials();
+ void relativePath_data();
+ void relativePath();
+
+ void absolute_data();
+ void absolute();
+
+ void fromToString_data();
+ void fromToString();
+
+ void fromString_data();
+ void fromString();
+
+ void fromUserInput_data();
+ void fromUserInput();
+
+ void toString_data();
+ void toString();
+
+ void toFSPathString_data();
+ void toFSPathString();
+
+ void comparison_data();
+ void comparison();
+
+ void linkFromString_data();
+ void linkFromString();
+
+ void pathAppended_data();
+ void pathAppended();
+
+ void resolvePath_data();
+ void resolvePath();
+
+ void relativeChildPath_data();
+ void relativeChildPath();
+
+ void rootLength_data();
+ void rootLength();
+
+ void schemeAndHostLength_data();
+ void schemeAndHostLength();
+
+ void asyncLocalCopy();
+ void startsWithDriveLetter();
+ void startsWithDriveLetter_data();
+
+ void withNewMappedPath_data();
+ void withNewMappedPath();
+
+ void stringAppended();
+ void stringAppended_data();
+ void url();
+ void url_data();
+
+ void cleanPath_data();
+ void cleanPath();
+
+ void isSameFile_data();
+ void isSameFile();
+
+ void hostSpecialChars_data();
+ void hostSpecialChars();
+
+ void tmp();
+ void tmp_data();
+
+ void searchInWithFilter();
+
+ void sort();
+ void sort_data();
+
+private:
+ QTemporaryDir tempDir;
+ QString rootPath;
+ QString exeExt;
+};
+
+static void touch(const QDir &dir, const QString &filename, bool fill, bool executable = false)
+{
+ QFile file(dir.absoluteFilePath(filename));
+ file.open(QIODevice::WriteOnly);
+ if (executable)
+ file.setPermissions(file.permissions() | QFileDevice::ExeUser);
+
+ if (fill) {
+ QRandomGenerator *random = QRandomGenerator::global();
+ for (int i = 0; i < 10; ++i)
+ file.write(QString::number(random->generate(), 16).toUtf8());
+ }
+ file.close();
+}
+
+void tst_filepath::initTestCase()
+{
+ // initialize test for tst_filepath::relativePath*()
+ QVERIFY(tempDir.isValid());
+ rootPath = tempDir.path();
+ QDir dir(rootPath);
+ dir.mkpath("a/b/c/d");
+ dir.mkpath("a/x/y/z");
+ dir.mkpath("a/b/x/y/z");
+ dir.mkpath("x/y/z");
+ touch(dir, "a/b/c/d/file1.txt", false);
+ touch(dir, "a/x/y/z/file2.txt", false);
+ touch(dir, "a/file3.txt", false);
+ touch(dir, "x/y/file4.txt", false);
+
+ // initialize test for tst_filepath::asyncLocalCopy()
+ touch(dir, "x/y/fileToCopy.txt", true);
+
+// initialize test for tst_filepath::searchIn()
+#ifdef Q_OS_WIN
+ exeExt = ".exe";
+#endif
+
+ dir.mkpath("s/1");
+ dir.mkpath("s/2");
+ touch(dir, "s/1/testexe" + exeExt, false, true);
+ touch(dir, "s/2/testexe" + exeExt, false, true);
+}
+
+void tst_filepath::searchInWithFilter()
+{
+ const FilePaths dirs = {FilePath::fromUserInput(rootPath) / "s" / "1",
+ FilePath::fromUserInput(rootPath) / "s" / "2"};
+
+ FilePath exe = FilePath::fromUserInput("testexe" + exeExt)
+ .searchInDirectories(dirs, [](const FilePath &path) {
+ return path.path().contains("/2/");
+ });
+
+ QVERIFY(!exe.path().endsWith("/1/testexe" + exeExt)
+ && exe.path().endsWith("/2/testexe" + exeExt));
+
+ FilePath exe2 = FilePath::fromUserInput("testexe" + exeExt)
+ .searchInDirectories(dirs, [](const FilePath &path) {
+ return path.path().contains("/1/");
+ });
+
+ QVERIFY(!exe2.path().endsWith("/2/testexe" + exeExt)
+ && exe2.path().endsWith("/1/testexe" + exeExt));
+}
+
+void tst_filepath::isEmpty_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("result");
+
+ QTest::newRow("empty path") << "" << true;
+ QTest::newRow("root only") << "/" << false;
+ QTest::newRow("//") << "//" << false;
+ QTest::newRow("scheme://host") << "scheme://host" << true; // Intentional (for now?)
+ QTest::newRow("scheme://host/") << "scheme://host/" << false;
+ QTest::newRow("scheme://host/a") << "scheme://host/a" << false;
+ QTest::newRow("scheme://host/.") << "scheme://host/." << false;
+}
+
+void tst_filepath::isEmpty()
+{
+ QFETCH(QString, path);
+ QFETCH(bool, result);
+
+ FilePath filePath = FilePath::fromString(path);
+ QCOMPARE(filePath.isEmpty(), result);
+}
+
+void tst_filepath::parentDir_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("parentPath");
+ QTest::addColumn<QString>("expectFailMessage");
+
+ QTest::newRow("empty path") << ""
+ << ""
+ << "";
+ QTest::newRow("root only") << "/"
+ << ""
+ << "";
+ QTest::newRow("//") << "//"
+ << ""
+ << "";
+ QTest::newRow("/tmp/dir") << "/tmp/dir"
+ << "/tmp"
+ << "";
+ QTest::newRow("relative/path") << "relative/path"
+ << "relative"
+ << "";
+ QTest::newRow("relativepath") << "relativepath"
+ << "."
+ << "";
+
+ // Windows stuff:
+ QTest::newRow("C:/data") << "C:/data"
+ << "C:/"
+ << "";
+ QTest::newRow("C:/") << "C:/"
+ << ""
+ << "";
+ QTest::newRow("//./com1") << "//./com1"
+ << "//./"
+ << "";
+ QTest::newRow("//?/path") << "//?/path"
+ << "/"
+ << "Qt 4 cannot handle this path.";
+ QTest::newRow("/Global?\?/UNC/host") << "/Global?\?/UNC/host"
+ << "/Global?\?/UNC/host"
+ << "Qt 4 cannot handle this path.";
+ QTest::newRow("//server/directory/file") << "//server/directory/file"
+ << "//server/directory"
+ << "";
+ QTest::newRow("//server/directory") << "//server/directory"
+ << "//server/"
+ << "";
+ QTest::newRow("//server") << "//server"
+ << ""
+ << "";
+
+ QTest::newRow("qrc") << ":/foo/bar.txt"
+ << ":/foo"
+ << "";
+}
+
+void tst_filepath::parentDir()
+{
+ QFETCH(QString, path);
+ QFETCH(QString, parentPath);
+ QFETCH(QString, expectFailMessage);
+
+ FilePath result = FilePath::fromUserInput(path).parentDir();
+ if (!expectFailMessage.isEmpty())
+ QEXPECT_FAIL("", expectFailMessage.toUtf8().constData(), Continue);
+ QCOMPARE(result.toString(), parentPath);
+}
+
+void tst_filepath::isChildOf_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("childPath");
+ QTest::addColumn<bool>("result");
+
+ QTest::newRow("empty path") << ""
+ << "/tmp" << false;
+ QTest::newRow("root only") << "/"
+ << "/tmp" << true;
+ QTest::newRow("/tmp/dir") << "/tmp"
+ << "/tmp/dir" << true;
+ QTest::newRow("relative/path") << "relative"
+ << "relative/path" << true;
+ QTest::newRow("/tmpdir") << "/tmp"
+ << "/tmpdir" << false;
+ QTest::newRow("same") << "/tmp/dir"
+ << "/tmp/dir" << false;
+
+ // Windows stuff:
+ QTest::newRow("C:/data") << "C:/"
+ << "C:/data" << true;
+ QTest::newRow("C:/") << ""
+ << "C:/" << false;
+ QTest::newRow("com-port") << "//./"
+ << "//./com1" << true;
+ QTest::newRow("extended-length-path") << "\\\\?\\C:\\"
+ << "\\\\?\\C:\\path" << true;
+ QTest::newRow("/Global?\?/UNC/host") << "/Global?\?/UNC/host"
+ << "/Global?\?/UNC/host/file" << true;
+ QTest::newRow("//server/directory/file") << "//server/directory"
+ << "//server/directory/file" << true;
+ QTest::newRow("//server/directory") << "//server"
+ << "//server/directory" << true;
+
+ QTest::newRow("qrc") << ":/foo/bar"
+ << ":/foo/bar/blah" << true;
+}
+
+void tst_filepath::isChildOf()
+{
+ QFETCH(QString, path);
+ QFETCH(QString, childPath);
+ QFETCH(bool, result);
+
+ const FilePath child = FilePath::fromUserInput(childPath);
+ const FilePath parent = FilePath::fromUserInput(path);
+
+ QCOMPARE(child.isChildOf(parent), result);
+}
+
+void tst_filepath::fileName_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<int>("components");
+ QTest::addColumn<QString>("result");
+
+ QTest::newRow("empty 1") << "" << 0 << "";
+ QTest::newRow("empty 2") << "" << 1 << "";
+ QTest::newRow("basic") << "/foo/bar/baz" << 0 << "baz";
+ QTest::newRow("2 parts") << "/foo/bar/baz" << 1 << "bar/baz";
+ QTest::newRow("root no depth") << "/foo" << 0 << "foo";
+ QTest::newRow("root full") << "/foo" << 1 << "/foo";
+ QTest::newRow("root included") << "/foo/bar/baz" << 2 << "/foo/bar/baz";
+ QTest::newRow("too many parts") << "/foo/bar/baz" << 5 << "/foo/bar/baz";
+ QTest::newRow("windows root") << "C:/foo/bar/baz" << 2 << "C:/foo/bar/baz";
+ QTest::newRow("smb share") << "//server/share/file" << 2 << "//server/share/file";
+ QTest::newRow("no slashes") << "foobar" << 0 << "foobar";
+ QTest::newRow("no slashes with depth") << "foobar" << 1 << "foobar";
+ QTest::newRow("multiple slashes 1") << "/foo/bar////baz" << 0 << "baz";
+ QTest::newRow("multiple slashes 2") << "/foo/bar////baz" << 1 << "bar////baz";
+ QTest::newRow("multiple slashes 3") << "/foo////bar/baz" << 2 << "/foo////bar/baz";
+ QTest::newRow("single char 1") << "/a/b/c" << 0 << "c";
+ QTest::newRow("single char 2") << "/a/b/c" << 1 << "b/c";
+ QTest::newRow("single char 3") << "/a/b/c" << 2 << "/a/b/c";
+ QTest::newRow("slash at end 1") << "/a/b/" << 0 << "";
+ QTest::newRow("slash at end 2") << "/a/b/" << 1 << "b/";
+ QTest::newRow("slashes at end 1") << "/a/b//" << 0 << "";
+ QTest::newRow("slashes at end 2") << "/a/b//" << 1 << "b//";
+ QTest::newRow("root only 1") << "/" << 0 << "";
+ QTest::newRow("root only 2") << "/" << 1 << "/";
+ QTest::newRow("qrc 0") << ":/foo/bar" << 0 << "bar";
+ QTest::newRow("qrc with root") << ":/foo/bar" << 1 << ":/foo/bar";
+}
+
+void tst_filepath::fileName()
+{
+ QFETCH(QString, path);
+ QFETCH(int, components);
+ QFETCH(QString, result);
+ QCOMPARE(FilePath::fromString(path).fileNameWithPathComponents(components), result);
+}
+
+void tst_filepath::calcRelativePath_data()
+{
+ QTest::addColumn<QString>("absolutePath");
+ QTest::addColumn<QString>("anchorPath");
+ QTest::addColumn<QString>("result");
+
+ QTest::newRow("empty") << ""
+ << ""
+ << "";
+ QTest::newRow("leftempty") << ""
+ << "/"
+ << "";
+ QTest::newRow("rightempty") << "/"
+ << ""
+ << "";
+ QTest::newRow("root") << "/"
+ << "/"
+ << ".";
+ QTest::newRow("simple1") << "/a"
+ << "/"
+ << "a";
+ QTest::newRow("simple2") << "/"
+ << "/a"
+ << "..";
+ QTest::newRow("simple3") << "/a"
+ << "/a"
+ << ".";
+ QTest::newRow("extraslash1") << "/a/b/c"
+ << "/a/b/c"
+ << ".";
+ QTest::newRow("extraslash2") << "/a/b/c"
+ << "/a/b/c/"
+ << ".";
+ QTest::newRow("extraslash3") << "/a/b/c/"
+ << "/a/b/c"
+ << ".";
+ QTest::newRow("normal1") << "/a/b/c"
+ << "/a/x"
+ << "../b/c";
+ QTest::newRow("normal2") << "/a/b/c"
+ << "/a/x/y"
+ << "../../b/c";
+ QTest::newRow("normal3") << "/a/b/c"
+ << "/x/y"
+ << "../../a/b/c";
+}
+
+void tst_filepath::calcRelativePath()
+{
+ QFETCH(QString, absolutePath);
+ QFETCH(QString, anchorPath);
+ QFETCH(QString, result);
+ QString relativePath = Utils::FilePath::calcRelativePath(absolutePath, anchorPath);
+ QCOMPARE(relativePath, result);
+}
+
+void tst_filepath::relativePath_specials()
+{
+ QString path = FilePath("").relativePathFrom("").toString();
+ QCOMPARE(path, "");
+}
+
+void tst_filepath::relativePath_data()
+{
+ QTest::addColumn<QString>("relative");
+ QTest::addColumn<QString>("anchor");
+ QTest::addColumn<QString>("result");
+
+ QTest::newRow("samedir") << "/"
+ << "/"
+ << ".";
+ QTest::newRow("samedir_but_file") << "a/b/c/d/file1.txt"
+ << "a/b/c/d"
+ << "file1.txt";
+ QTest::newRow("samedir_but_file2") << "a/b/c/d"
+ << "a/b/c/d/file1.txt"
+ << ".";
+ QTest::newRow("dir2dir_1") << "a/b/c/d"
+ << "a/x/y/z"
+ << "../../../b/c/d";
+ QTest::newRow("dir2dir_2") << "a/b"
+ << "a/b/c"
+ << "..";
+ QTest::newRow("file2file_1") << "a/b/c/d/file1.txt"
+ << "a/file3.txt"
+ << "b/c/d/file1.txt";
+ QTest::newRow("dir2file_1") << "a/b/c"
+ << "a/x/y/z/file2.txt"
+ << "../../../b/c";
+ QTest::newRow("file2dir_1") << "a/b/c/d/file1.txt"
+ << "x/y"
+ << "../../a/b/c/d/file1.txt";
+}
+
+void tst_filepath::relativePath()
+{
+ QFETCH(QString, relative);
+ QFETCH(QString, anchor);
+ QFETCH(QString, result);
+ FilePath actualPath = FilePath::fromString(rootPath + "/" + relative)
+ .relativePathFrom(FilePath::fromString(rootPath + "/" + anchor));
+ QCOMPARE(actualPath.toString(), result);
+}
+
+void tst_filepath::rootLength_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<int>("result");
+
+ QTest::newRow("empty") << "" << 0;
+ QTest::newRow("slash") << "/" << 1;
+ QTest::newRow("slash-rest") << "/abc" << 1;
+ QTest::newRow("rest") << "abc" << 0;
+ QTest::newRow("drive-slash") << "x:/" << 3;
+ QTest::newRow("drive-rest") << "x:abc" << 0;
+ QTest::newRow("drive-slash-rest") << "x:/abc" << 3;
+
+ QTest::newRow("unc-root") << "//" << 2;
+ QTest::newRow("unc-localhost-unfinished") << "//localhost" << 11;
+ QTest::newRow("unc-localhost") << "//localhost/" << 12;
+ QTest::newRow("unc-localhost-rest") << "//localhost/abs" << 12;
+ QTest::newRow("unc-localhost-drive") << "//localhost/c$" << 12;
+ QTest::newRow("unc-localhost-drive-slash") << "//localhost//c$/" << 12;
+ QTest::newRow("unc-localhost-drive-slash-rest") << "//localhost//c$/x" << 12;
+}
+
+void tst_filepath::rootLength()
+{
+ QFETCH(QString, path);
+ QFETCH(int, result);
+
+ int actual = FilePath::rootLength(path);
+ QCOMPARE(actual, result);
+}
+
+void tst_filepath::schemeAndHostLength_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<int>("result");
+
+ QTest::newRow("empty") << "" << 0;
+ QTest::newRow("drive-slash-rest") << "x:/abc" << 0;
+ QTest::newRow("rest") << "abc" << 0;
+ QTest::newRow("slash-rest") << "/abc" << 0;
+ QTest::newRow("dev-empty") << "dev://" << 6;
+ QTest::newRow("dev-localhost-unfinished") << "dev://localhost" << 15;
+ QTest::newRow("dev-localhost") << "dev://localhost/" << 16;
+ QTest::newRow("dev-localhost-rest") << "dev://localhost/abs" << 16;
+ QTest::newRow("dev-localhost-drive") << "dev://localhost/c$" << 16;
+ QTest::newRow("dev-localhost-drive-slash") << "dev://localhost//c$/" << 16;
+ QTest::newRow("dev-localhost-drive-slash-rest") << "dev://localhost//c$/x" << 16;
+}
+
+void tst_filepath::schemeAndHostLength()
+{
+ QFETCH(QString, path);
+ QFETCH(int, result);
+
+ int actual = FilePath::schemeAndHostLength(path);
+ QCOMPARE(actual, result);
+}
+
+void tst_filepath::absolute_data()
+{
+ QTest::addColumn<FilePath>("path");
+ QTest::addColumn<FilePath>("absoluteFilePath");
+ QTest::addColumn<FilePath>("absolutePath");
+
+ QTest::newRow("absolute1") << FilePath::fromString("/") << FilePath::fromString("/")
+ << FilePath::fromString("/");
+ QTest::newRow("absolute2") << FilePath::fromString("C:/a/b") << FilePath::fromString("C:/a/b")
+ << FilePath::fromString("C:/a");
+ QTest::newRow("absolute3") << FilePath::fromString("/a/b") << FilePath::fromString("/a/b")
+ << FilePath::fromString("/a");
+ QTest::newRow("absolute4") << FilePath::fromString("/a/b/..") << FilePath::fromString("/a")
+ << FilePath::fromString("/");
+ QTest::newRow("absolute5") << FilePath::fromString("/a/b/c/../d")
+ << FilePath::fromString("/a/b/d") << FilePath::fromString("/a/b");
+ QTest::newRow("absolute6") << FilePath::fromString("/a/../b/c/d")
+ << FilePath::fromString("/b/c/d") << FilePath::fromString("/b/c");
+ QTest::newRow("default-constructed") << FilePath() << FilePath() << FilePath();
+ QTest::newRow("relative") << FilePath::fromString("a/b")
+ << FilePath::fromString(QDir::currentPath() + "/a/b")
+ << FilePath::fromString(QDir::currentPath() + "/a");
+ QTest::newRow("qrc") << FilePath::fromString(":/foo/bar.txt")
+ << FilePath::fromString(":/foo/bar.txt") << FilePath::fromString(":/foo");
+}
+
+void tst_filepath::absolute()
+{
+ QFETCH(FilePath, path);
+ QFETCH(FilePath, absoluteFilePath);
+ QFETCH(FilePath, absolutePath);
+ QCOMPARE(path.absoluteFilePath(), absoluteFilePath);
+ QCOMPARE(path.absolutePath(), absolutePath);
+}
+
+void tst_filepath::toString_data()
+{
+ QTest::addColumn<QString>("scheme");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("userResult");
+
+ QTest::newRow("empty") << ""
+ << ""
+ << ""
+ << ""
+ << "";
+ QTest::newRow("scheme") << "http"
+ << ""
+ << ""
+ << "http://"
+ << "http://";
+ QTest::newRow("scheme-and-host") << "http"
+ << "127.0.0.1"
+ << ""
+ << "http://127.0.0.1"
+ << "http://127.0.0.1";
+ QTest::newRow("root") << "http"
+ << "127.0.0.1"
+ << "/"
+ << "http://127.0.0.1/"
+ << "http://127.0.0.1/";
+
+ QTest::newRow("root-folder") << ""
+ << ""
+ << "/"
+ << "/"
+ << "/";
+ QTest::newRow("qtc-dev-root-folder-linux") << ""
+ << ""
+ << "/__qtc_devices__"
+ << "/__qtc_devices__"
+ << "/__qtc_devices__";
+ QTest::newRow("qtc-dev-root-folder-win") << ""
+ << ""
+ << "c:/__qtc_devices__"
+ << "c:/__qtc_devices__"
+ << "c:/__qtc_devices__";
+ QTest::newRow("qtc-dev-type-root-folder-linux") << ""
+ << ""
+ << "/__qtc_devices__/docker"
+ << "/__qtc_devices__/docker"
+ << "/__qtc_devices__/docker";
+ QTest::newRow("qtc-dev-type-root-folder-win") << ""
+ << ""
+ << "c:/__qtc_devices__/docker"
+ << "c:/__qtc_devices__/docker"
+ << "c:/__qtc_devices__/docker";
+ QTest::newRow("qtc-root-folder") << "docker"
+ << "alpine:latest"
+ << "/"
+ << "docker://alpine:latest/"
+ << "docker://alpine:latest/";
+ QTest::newRow("qtc-root-folder-rel") << "docker"
+ << "alpine:latest"
+ << ""
+ << "docker://alpine:latest"
+ << "docker://alpine:latest";
+}
+
+void tst_filepath::toString()
+{
+ QFETCH(QString, scheme);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+ QFETCH(QString, result);
+ QFETCH(QString, userResult);
+
+ FilePath filePath = FilePath::fromParts(scheme, host, path);
+ QCOMPARE(filePath.toString(), result);
+ QString cleanedOutput = filePath.needsDevice() ? filePath.toUserOutput()
+ : QDir::cleanPath(filePath.toUserOutput());
+ QCOMPARE(cleanedOutput, userResult);
+}
+
+void tst_filepath::toFSPathString_data()
+{
+ QTest::addColumn<QString>("scheme");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("userResult");
+
+ QTest::newRow("empty") << ""
+ << ""
+ << ""
+ << ""
+ << "";
+ QTest::newRow("scheme") << "http"
+ << ""
+ << "" << QDir::rootPath() + "__qtc_devices__/http/"
+ << "http://";
+ QTest::newRow("scheme-and-host") << "http"
+ << "127.0.0.1"
+ << "" << QDir::rootPath() + "__qtc_devices__/http/127.0.0.1"
+ << "http://127.0.0.1";
+ QTest::newRow("root") << "http"
+ << "127.0.0.1"
+ << "/" << QDir::rootPath() + "__qtc_devices__/http/127.0.0.1/"
+ << "http://127.0.0.1/";
+
+ QTest::newRow("root-folder") << ""
+ << ""
+ << "/"
+ << "/"
+ << "/";
+ QTest::newRow("qtc-dev-root-folder")
+ << ""
+ << "" << QDir::rootPath() + "__qtc_devices__" << QDir::rootPath() + "__qtc_devices__"
+ << QDir::rootPath() + "__qtc_devices__";
+ QTest::newRow("qtc-dev-type-root-folder") << ""
+ << "" << QDir::rootPath() + "__qtc_devices__/docker"
+ << QDir::rootPath() + "__qtc_devices__/docker"
+ << QDir::rootPath() + "__qtc_devices__/docker";
+ QTest::newRow("qtc-root-folder")
+ << "docker"
+ << "alpine:latest"
+ << "/" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest/"
+ << "docker://alpine:latest/";
+ QTest::newRow("qtc-root-folder-rel")
+ << "docker"
+ << "alpine:latest"
+ << "" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest"
+ << "docker://alpine:latest";
+}
+
+void tst_filepath::toFSPathString()
+{
+ QFETCH(QString, scheme);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+ QFETCH(QString, result);
+ QFETCH(QString, userResult);
+
+ FilePath filePath = FilePath::fromParts(scheme, host, path);
+ QCOMPARE(filePath.toFSPathString(), result);
+ QString cleanedOutput = filePath.needsDevice() ? filePath.toUserOutput()
+ : QDir::cleanPath(filePath.toUserOutput());
+ QCOMPARE(cleanedOutput, userResult);
+}
+
+enum ExpectedPass { PassEverywhere = 0, FailOnWindows = 1, FailOnLinux = 2, FailEverywhere = 3 };
+
+class FromStringData
+{
+public:
+ FromStringData(const QString &input,
+ const QString &scheme,
+ const QString &host,
+ const QString &path,
+ ExpectedPass expectedPass = PassEverywhere)
+ : input(input)
+ , scheme(scheme)
+ , host(host)
+ , path(path)
+ , expectedPass(expectedPass)
+ {}
+
+ QString input;
+ QString scheme;
+ QString host;
+ QString path;
+ ExpectedPass expectedPass = PassEverywhere;
+};
+
+Q_DECLARE_METATYPE(FromStringData);
+
+void tst_filepath::fromString_data()
+{
+ using D = FromStringData;
+ QTest::addColumn<D>("data");
+
+ QTest::newRow("empty") << D("", "", "", "");
+ QTest::newRow("single-colon") << D(":", "", "", ":");
+ QTest::newRow("single-slash") << D("/", "", "", "/");
+ QTest::newRow("single-char") << D("a", "", "", "a");
+ QTest::newRow("relative") << D("./rel", "", "", "./rel");
+ QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt");
+ QTest::newRow("qrc-no-slash") << D(":test.txt", "", "", ":test.txt");
+
+ QTest::newRow("unc-incomplete") << D("//", "", "", "//");
+ QTest::newRow("unc-incomplete-only-server") << D("//server", "", "", "//server");
+ QTest::newRow("unc-incomplete-only-server-2") << D("//server/", "", "", "//server/");
+ QTest::newRow("unc-server-and-share") << D("//server/share", "", "", "//server/share");
+ QTest::newRow("unc-server-and-share-2") << D("//server/share/", "", "", "//server/share/");
+ QTest::newRow("unc-full") << D("//server/share/test.txt", "", "", "//server/share/test.txt");
+
+ QTest::newRow("unix-root") << D("/", "", "", "/");
+ QTest::newRow("unix-folder") << D("/tmp", "", "", "/tmp");
+ QTest::newRow("unix-folder-with-trailing-slash") << D("/tmp/", "", "", "/tmp/");
+
+ QTest::newRow("windows-root") << D("c:", "", "", "c:");
+ QTest::newRow("windows-folder") << D("c:/Windows", "", "", "c:/Windows");
+ QTest::newRow("windows-folder-with-trailing-slash") << D("c:/Windows/", "", "", "c:/Windows/");
+ QTest::newRow("windows-folder-slash") << D("C:/Windows", "", "", "C:/Windows");
+
+ QTest::newRow("docker-root-url") << D("docker://1234/", "docker", "1234", "/");
+ QTest::newRow("docker-root-url-special-linux")
+ << D("/__qtc_devices__/docker/1234/", "docker", "1234", "/");
+ QTest::newRow("docker-root-url-special-win")
+ << D("c:/__qtc_devices__/docker/1234/", "docker", "1234", "/");
+ QTest::newRow("docker-relative-path") << D("docker://1234/./rel", "docker", "1234", "rel");
+
+ QTest::newRow("qtc-dev-linux") << D("/__qtc_devices__", "", "", "/__qtc_devices__");
+ QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__");
+ QTest::newRow("qtc-dev-type-linux")
+ << D("/__qtc_devices__/docker", "", "", "/__qtc_devices__/docker");
+ QTest::newRow("qtc-dev-type-win")
+ << D("c:/__qtc_devices__/docker", "", "", "c:/__qtc_devices__/docker");
+ QTest::newRow("qtc-dev-type-dev-linux")
+ << D("/__qtc_devices__/docker/1234", "docker", "1234", "/");
+ QTest::newRow("qtc-dev-type-dev-win")
+ << D("c:/__qtc_devices__/docker/1234", "docker", "1234", "/");
+
+ // "Remote Windows" is currently truly not supported.
+ QTest::newRow("cross-os-linux") << D("/__qtc_devices__/docker/1234/c:/test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+ QTest::newRow("cross-os-win") << D("c:/__qtc_devices__/docker/1234/c:/test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+ QTest::newRow("cross-os-unclean-linux") << D("/__qtc_devices__/docker/1234/c:\\test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+ QTest::newRow("cross-os-unclean-win") << D("c:/__qtc_devices__/docker/1234/c:\\test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+
+ QTest::newRow("unc-full-in-docker-linux")
+ << D("/__qtc_devices__/docker/1234//server/share/test.txt",
+ "docker",
+ "1234",
+ "//server/share/test.txt");
+ QTest::newRow("unc-full-in-docker-win")
+ << D("c:/__qtc_devices__/docker/1234//server/share/test.txt",
+ "docker",
+ "1234",
+ "//server/share/test.txt");
+
+ QTest::newRow("unc-dos-1") << D("//?/c:", "", "", "//?/c:");
+ QTest::newRow("unc-dos-com") << D("//./com1", "", "", "//./com1");
+}
+
+void tst_filepath::fromString()
+{
+ QFETCH(FromStringData, data);
+
+ FilePath filePath = FilePath::fromString(data.input);
+
+ bool expectFail = ((data.expectedPass & FailOnLinux) && !HostOsInfo::isWindowsHost())
+ || ((data.expectedPass & FailOnWindows) && HostOsInfo::isWindowsHost());
+
+ if (expectFail) {
+ QString actual = filePath.scheme() + '|' + filePath.host() + '|' + filePath.path();
+ QString expected = data.scheme + '|' + data.host + '|' + data.path;
+ QEXPECT_FAIL("", "", Continue);
+ QCOMPARE(actual, expected);
+ return;
+ }
+
+ QCOMPARE(filePath.scheme(), data.scheme);
+ QCOMPARE(filePath.host(), data.host);
+ QCOMPARE(filePath.path(), data.path);
+}
+
+void tst_filepath::fromUserInput_data()
+{
+ using D = FromStringData;
+ QTest::addColumn<D>("data");
+
+ QTest::newRow("empty") << D("", "", "", "");
+ QTest::newRow("single-colon") << D(":", "", "", ":");
+ QTest::newRow("single-slash") << D("/", "", "", "/");
+ QTest::newRow("single-char") << D("a", "", "", "a");
+ QTest::newRow("relative") << D("./rel", "", "", "rel");
+ QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt");
+ QTest::newRow("qrc-no-slash") << D(":test.txt", "", "", ":test.txt");
+ QTest::newRow("tilde") << D("~/", "", "", QDir::homePath());
+ QTest::newRow("tilde-with-path") << D("~/foo", "", "", QDir::homePath() + "/foo");
+ QTest::newRow("tilde-only") << D("~", "", "", "~");
+
+ QTest::newRow("unc-incomplete") << D("//", "", "", "//");
+ QTest::newRow("unc-incomplete-only-server") << D("//server", "", "", "//server");
+ QTest::newRow("unc-incomplete-only-server-2") << D("//server/", "", "", "//server/");
+ QTest::newRow("unc-server-and-share") << D("//server/share", "", "", "//server/share");
+ QTest::newRow("unc-server-and-share-2") << D("//server/share/", "", "", "//server/share");
+ QTest::newRow("unc-full") << D("//server/share/test.txt", "", "", "//server/share/test.txt");
+
+ QTest::newRow("unix-root") << D("/", "", "", "/");
+ QTest::newRow("unix-folder") << D("/tmp", "", "", "/tmp");
+ QTest::newRow("unix-folder-with-trailing-slash") << D("/tmp/", "", "", "/tmp");
+
+ QTest::newRow("windows-root") << D("c:", "", "", "c:");
+ QTest::newRow("windows-folder") << D("c:/Windows", "", "", "c:/Windows");
+ QTest::newRow("windows-folder-with-trailing-slash") << D("c:\\Windows\\", "", "", "c:/Windows");
+ QTest::newRow("windows-folder-slash") << D("C:/Windows", "", "", "C:/Windows");
+
+ QTest::newRow("docker-root-url") << D("docker://1234/", "docker", "1234", "/");
+ QTest::newRow("docker-root-url-special-linux")
+ << D("/__qtc_devices__/docker/1234/", "docker", "1234", "/");
+ QTest::newRow("docker-root-url-special-win")
+ << D("c:/__qtc_devices__/docker/1234/", "docker", "1234", "/");
+ QTest::newRow("docker-relative-path")
+ << D("docker://1234/./rel", "docker", "1234", "rel", FailEverywhere);
+
+ QTest::newRow("qtc-dev-linux") << D("/__qtc_devices__", "", "", "/__qtc_devices__");
+ QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__");
+ QTest::newRow("qtc-dev-type-linux")
+ << D("/__qtc_devices__/docker", "", "", "/__qtc_devices__/docker");
+ QTest::newRow("qtc-dev-type-win")
+ << D("c:/__qtc_devices__/docker", "", "", "c:/__qtc_devices__/docker");
+ QTest::newRow("qtc-dev-type-dev-linux")
+ << D("/__qtc_devices__/docker/1234", "docker", "1234", "/");
+ QTest::newRow("qtc-dev-type-dev-win")
+ << D("c:/__qtc_devices__/docker/1234", "docker", "1234", "/");
+
+ // "Remote Windows" is currently truly not supported.
+ QTest::newRow("cross-os-linux") << D("/__qtc_devices__/docker/1234/c:/test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+ QTest::newRow("cross-os-win") << D("c:/__qtc_devices__/docker/1234/c:/test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+ QTest::newRow("cross-os-unclean-linux") << D("/__qtc_devices__/docker/1234/c:\\test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+ QTest::newRow("cross-os-unclean-win") << D("c:/__qtc_devices__/docker/1234/c:\\test.txt",
+ "docker",
+ "1234",
+ "c:/test.txt",
+ FailEverywhere);
+
+ QTest::newRow("unc-full-in-docker-linux")
+ << D("/__qtc_devices__/docker/1234//server/share/test.txt",
+ "docker",
+ "1234",
+ "//server/share/test.txt",
+ FailEverywhere);
+ QTest::newRow("unc-full-in-docker-win")
+ << D("c:/__qtc_devices__/docker/1234//server/share/test.txt",
+ "docker",
+ "1234",
+ "//server/share/test.txt",
+ FailEverywhere);
+
+ QTest::newRow("unc-dos-1") << D("//?/c:", "", "", "c:");
+ QTest::newRow("unc-dos-com") << D("//./com1", "", "", "//./com1");
+}
+
+void tst_filepath::fromUserInput()
+{
+ QFETCH(FromStringData, data);
+
+ FilePath filePath = FilePath::fromUserInput(data.input);
+
+ bool expectFail = ((data.expectedPass & FailOnLinux) && !HostOsInfo::isWindowsHost())
+ || ((data.expectedPass & FailOnWindows) && HostOsInfo::isWindowsHost());
+
+ if (expectFail) {
+ QString actual = filePath.scheme() + '|' + filePath.host() + '|' + filePath.path();
+ QString expected = data.scheme + '|' + data.host + '|' + data.path;
+ QEXPECT_FAIL("", "", Continue);
+ QCOMPARE(actual, expected);
+ return;
+ }
+
+ QCOMPARE(filePath.scheme(), data.scheme);
+ QCOMPARE(filePath.host(), data.host);
+ QCOMPARE(filePath.path(), data.path);
+}
+
+void tst_filepath::fromToString_data()
+{
+ QTest::addColumn<QString>("scheme");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("full");
+
+ QTest::newRow("s0") << ""
+ << ""
+ << ""
+ << "";
+ QTest::newRow("s1") << ""
+ << ""
+ << "/"
+ << "/";
+ QTest::newRow("s2") << ""
+ << ""
+ << "a/b/c/d"
+ << "a/b/c/d";
+ QTest::newRow("s3") << ""
+ << ""
+ << "/a/b"
+ << "/a/b";
+
+ QTest::newRow("s4") << "docker"
+ << "1234abcdef"
+ << "/bin/ls"
+ << "docker://1234abcdef/bin/ls";
+ QTest::newRow("s5") << "docker"
+ << "1234"
+ << "/bin/ls"
+ << "docker://1234/bin/ls";
+
+ // This is not a proper URL.
+ QTest::newRow("s6") << "docker"
+ << "1234"
+ << "somefile"
+ << "docker://1234/./somefile";
+
+ // Local Windows paths:
+ QTest::newRow("w1") << ""
+ << ""
+ << "C:/data"
+ << "C:/data";
+ QTest::newRow("w2") << ""
+ << ""
+ << "C:/"
+ << "C:/";
+ QTest::newRow("w3") << ""
+ << ""
+ << "/Global?\?/UNC/host"
+ << "/Global?\?/UNC/host";
+ QTest::newRow("w4") << ""
+ << ""
+ << "//server/dir/file"
+ << "//server/dir/file";
+}
+
+void tst_filepath::fromToString()
+{
+ QFETCH(QString, full);
+ QFETCH(QString, scheme);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+
+ FilePath filePath = FilePath::fromString(full);
+
+ QCOMPARE(filePath.toString(), full);
+
+ QCOMPARE(filePath.scheme(), scheme);
+ QCOMPARE(filePath.host(), host);
+ QCOMPARE(filePath.path(), path);
+
+ FilePath copy = FilePath::fromParts(scheme, host, path);
+ QCOMPARE(copy.toString(), full);
+}
+
+void tst_filepath::comparison()
+{
+ QFETCH(QString, left);
+ QFETCH(QString, right);
+ QFETCH(bool, hostSensitive);
+ QFETCH(bool, expected);
+
+ HostOsInfo::setOverrideFileNameCaseSensitivity(hostSensitive ? Qt::CaseSensitive
+ : Qt::CaseInsensitive);
+
+ FilePath l = FilePath::fromUserInput(left);
+ FilePath r = FilePath::fromUserInput(right);
+ QCOMPARE(l == r, expected);
+}
+
+void tst_filepath::comparison_data()
+{
+ QTest::addColumn<QString>("left");
+ QTest::addColumn<QString>("right");
+ QTest::addColumn<bool>("hostSensitive");
+ QTest::addColumn<bool>("expected");
+
+ QTest::newRow("r1") << "Abc"
+ << "abc" << true << false;
+ QTest::newRow("r2") << "Abc"
+ << "abc" << false << true;
+ QTest::newRow("r3") << "x://y/Abc"
+ << "x://y/abc" << true << false;
+ QTest::newRow("r4") << "x://y/Abc"
+ << "x://y/abc" << false << false;
+
+ QTest::newRow("s1") << "abc"
+ << "abc" << true << true;
+ QTest::newRow("s2") << "abc"
+ << "abc" << false << true;
+ QTest::newRow("s3") << "x://y/abc"
+ << "x://y/abc" << true << true;
+ QTest::newRow("s4") << "x://y/abc"
+ << "x://y/abc" << false << true;
+}
+
+void tst_filepath::linkFromString()
+{
+ QFETCH(QString, testFile);
+ QFETCH(Utils::FilePath, filePath);
+ QFETCH(int, line);
+ QFETCH(int, column);
+ const Link link = Link::fromString(testFile, true);
+ QCOMPARE(link.targetFilePath, filePath);
+ QCOMPARE(link.targetLine, line);
+ QCOMPARE(link.targetColumn, column);
+}
+
+void tst_filepath::linkFromString_data()
+{
+ QTest::addColumn<QString>("testFile");
+ QTest::addColumn<Utils::FilePath>("filePath");
+ QTest::addColumn<int>("line");
+ QTest::addColumn<int>("column");
+
+ QTest::newRow("no-line-no-column")
+ << QString("someFile.txt") << FilePath("someFile.txt") << 0 << -1;
+ QTest::newRow(":line-:column") << QString::fromLatin1("/some/path/file.txt:42:3")
+ << FilePath("/some/path/file.txt") << 42 << 2;
+ QTest::newRow("(42) at end") << QString::fromLatin1("/some/path/file.txt(42)")
+ << FilePath("/some/path/file.txt") << 42 << 0;
+}
+
+void tst_filepath::pathAppended()
+{
+ QFETCH(QString, left);
+ QFETCH(QString, right);
+ QFETCH(QString, expected);
+
+ const FilePath fleft = FilePath::fromString(left);
+ const FilePath fexpected = FilePath::fromString(expected);
+
+ const FilePath result = fleft.pathAppended(right);
+
+ QCOMPARE(result, fexpected);
+}
+
+void tst_filepath::pathAppended_data()
+{
+ QTest::addColumn<QString>("left");
+ QTest::addColumn<QString>("right");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("p0") << ""
+ << ""
+ << "";
+ QTest::newRow("p1") << ""
+ << "/"
+ << "/";
+ QTest::newRow("p2") << ""
+ << "c/"
+ << "c/";
+ QTest::newRow("p3") << ""
+ << "/d"
+ << "/d";
+ QTest::newRow("p4") << ""
+ << "c/d"
+ << "c/d";
+
+ QTest::newRow("r0") << "/"
+ << ""
+ << "/";
+ QTest::newRow("r1") << "/"
+ << "/"
+ << "/";
+ QTest::newRow("r2") << "/"
+ << "c/"
+ << "/c/";
+ QTest::newRow("r3") << "/"
+ << "/d"
+ << "/d";
+ QTest::newRow("r4") << "/"
+ << "c/d"
+ << "/c/d";
+
+ QTest::newRow("s0") << "/b"
+ << ""
+ << "/b";
+ QTest::newRow("s1") << "/b"
+ << "/"
+ << "/b/";
+ QTest::newRow("s2") << "/b"
+ << "c/"
+ << "/b/c/";
+ QTest::newRow("s3") << "/b"
+ << "/d"
+ << "/b/d";
+ QTest::newRow("s4") << "/b"
+ << "c/d"
+ << "/b/c/d";
+
+ QTest::newRow("t0") << "a/"
+ << ""
+ << "a/";
+ QTest::newRow("t1") << "a/"
+ << "/"
+ << "a/";
+ QTest::newRow("t2") << "a/"
+ << "c/"
+ << "a/c/";
+ QTest::newRow("t3") << "a/"
+ << "/d"
+ << "a/d";
+ QTest::newRow("t4") << "a/"
+ << "c/d"
+ << "a/c/d";
+
+ QTest::newRow("u0") << "a/b"
+ << ""
+ << "a/b";
+ QTest::newRow("u1") << "a/b"
+ << "/"
+ << "a/b/";
+ QTest::newRow("u2") << "a/b"
+ << "c/"
+ << "a/b/c/";
+ QTest::newRow("u3") << "a/b"
+ << "/d"
+ << "a/b/d";
+ QTest::newRow("u4") << "a/b"
+ << "c/d"
+ << "a/b/c/d";
+
+ if (HostOsInfo::isWindowsHost()) {
+ QTest::newRow("win-1") << "c:"
+ << "/a/b"
+ << "c:/a/b";
+ QTest::newRow("win-2") << "c:/"
+ << "/a/b"
+ << "c:/a/b";
+ QTest::newRow("win-3") << "c:/"
+ << "a/b"
+ << "c:/a/b";
+ }
+}
+
+void tst_filepath::resolvePath_data()
+{
+ QTest::addColumn<FilePath>("left");
+ QTest::addColumn<FilePath>("right");
+ QTest::addColumn<FilePath>("expected");
+
+ QTest::newRow("empty") << FilePath() << FilePath() << FilePath();
+ QTest::newRow("s0") << FilePath("/") << FilePath("b") << FilePath("/b");
+ QTest::newRow("s1") << FilePath() << FilePath("b") << FilePath("b");
+ QTest::newRow("s2") << FilePath("a") << FilePath() << FilePath("a");
+ QTest::newRow("s3") << FilePath("a") << FilePath("b") << FilePath("a/b");
+ QTest::newRow("s4") << FilePath("/a") << FilePath("/b") << FilePath("/b");
+ QTest::newRow("s5") << FilePath("a") << FilePath("/b") << FilePath("/b");
+ QTest::newRow("s6") << FilePath("/a") << FilePath("b") << FilePath("/a/b");
+ QTest::newRow("s7") << FilePath("/a") << FilePath(".") << FilePath("/a");
+ QTest::newRow("s8") << FilePath("/a") << FilePath("./b") << FilePath("/a/b");
+}
+
+void tst_filepath::resolvePath()
+{
+ QFETCH(FilePath, left);
+ QFETCH(FilePath, right);
+ QFETCH(FilePath, expected);
+
+ const FilePath result = left.resolvePath(right);
+
+ QCOMPARE(result, expected);
+}
+
+void tst_filepath::relativeChildPath_data()
+{
+ QTest::addColumn<FilePath>("parent");
+ QTest::addColumn<FilePath>("child");
+ QTest::addColumn<FilePath>("expected");
+
+ QTest::newRow("empty") << FilePath() << FilePath() << FilePath();
+
+ QTest::newRow("simple-0") << FilePath("/a") << FilePath("/a/b") << FilePath("b");
+ QTest::newRow("simple-1") << FilePath("/a/") << FilePath("/a/b") << FilePath("b");
+ QTest::newRow("simple-2") << FilePath("/a") << FilePath("/a/b/c/d/e/f")
+ << FilePath("b/c/d/e/f");
+
+ QTest::newRow("not-0") << FilePath("/x") << FilePath("/a/b") << FilePath();
+ QTest::newRow("not-1") << FilePath("/a/b/c") << FilePath("/a/b") << FilePath();
+}
+
+void tst_filepath::relativeChildPath()
+{
+ QFETCH(FilePath, parent);
+ QFETCH(FilePath, child);
+ QFETCH(FilePath, expected);
+
+ const FilePath result = child.relativeChildPath(parent);
+
+ QCOMPARE(result, expected);
+}
+
+void tst_filepath::asyncLocalCopy()
+{
+ const FilePath orig = FilePath::fromString(rootPath).pathAppended("x/y/fileToCopy.txt");
+ QVERIFY(orig.exists());
+ const FilePath dest = FilePath::fromString(rootPath).pathAppended("x/fileToCopyDest.txt");
+ bool wasCalled = false;
+ // When QTRY_VERIFY failed, don't call the continuation after we leave this method
+ QObject context;
+ auto afterCopy = [&orig, &dest, &wasCalled](expected_str<void> result) {
+ QVERIFY(result);
+ // check existence, size and content
+ QVERIFY(dest.exists());
+ QCOMPARE(dest.fileSize(), orig.fileSize());
+ QCOMPARE(dest.fileContents(), orig.fileContents());
+ wasCalled = true;
+ };
+ orig.asyncCopy(dest, &context, afterCopy);
+ QTRY_VERIFY(wasCalled);
+}
+
+void tst_filepath::startsWithDriveLetter_data()
+{
+ QTest::addColumn<FilePath>("path");
+ QTest::addColumn<bool>("expected");
+
+ QTest::newRow("empty") << FilePath() << false;
+ QTest::newRow("simple-win") << FilePath::fromString("c:/a") << true;
+ QTest::newRow("simple-linux") << FilePath::fromString("/c:/a") << false;
+ QTest::newRow("relative") << FilePath("a/b") << false;
+}
+
+void tst_filepath::startsWithDriveLetter()
+{
+ QFETCH(FilePath, path);
+ QFETCH(bool, expected);
+
+ QCOMPARE(path.startsWithDriveLetter(), expected);
+}
+
+void tst_filepath::withNewMappedPath_data()
+{
+ QTest::addColumn<FilePath>("path");
+ QTest::addColumn<FilePath>("templatePath");
+ QTest::addColumn<FilePath>("expected");
+
+ QTest::newRow("empty") << FilePath() << FilePath() << FilePath();
+ QTest::newRow("same-local") << FilePath("/a/b") << FilePath("/a/b") << FilePath("/a/b");
+ QTest::newRow("same-docker") << FilePath("docker://1234/a/b") << FilePath("docker://1234/e")
+ << FilePath("docker://1234/a/b");
+
+ QTest::newRow("docker-to-local")
+ << FilePath("docker://1234/a/b") << FilePath("/c/d") << FilePath("/a/b");
+ QTest::newRow("local-to-docker")
+ << FilePath("/a/b") << FilePath("docker://1234/c/d") << FilePath("docker://1234/a/b");
+}
+
+void tst_filepath::withNewMappedPath()
+{
+ QFETCH(FilePath, path);
+ QFETCH(FilePath, templatePath);
+ QFETCH(FilePath, expected);
+
+ QCOMPARE(templatePath.withNewMappedPath(path), expected);
+}
+
+void tst_filepath::stringAppended_data()
+{
+ QTest::addColumn<FilePath>("left");
+ QTest::addColumn<QString>("right");
+ QTest::addColumn<FilePath>("expected");
+
+ QTest::newRow("empty") << FilePath() << QString() << FilePath();
+ QTest::newRow("empty-left") << FilePath() << "a" << FilePath("a");
+ QTest::newRow("empty-right") << FilePath("a") << QString() << FilePath("a");
+ QTest::newRow("add-root") << FilePath() << QString("/") << FilePath("/");
+ QTest::newRow("add-root-and-more")
+ << FilePath() << QString("/test/blah") << FilePath("/test/blah");
+ QTest::newRow("add-extension")
+ << FilePath::fromString("/a") << QString(".txt") << FilePath("/a.txt");
+ QTest::newRow("trailing-slash")
+ << FilePath::fromString("/a") << QString("b/") << FilePath("/ab/");
+ QTest::newRow("slash-trailing-slash")
+ << FilePath::fromString("/a/") << QString("b/") << FilePath("/a/b/");
+}
+
+void tst_filepath::stringAppended()
+{
+ QFETCH(FilePath, left);
+ QFETCH(QString, right);
+ QFETCH(FilePath, expected);
+
+ const FilePath result = left.stringAppended(right);
+
+ QCOMPARE(expected, result);
+}
+
+void tst_filepath::url()
+{
+ QFETCH(QString, url);
+ QFETCH(QString, expectedScheme);
+ QFETCH(QString, expectedHost);
+ QFETCH(QString, expectedPath);
+
+ const FilePath result = FilePath::fromString(url);
+ QCOMPARE(result.scheme(), expectedScheme);
+ QCOMPARE(result.host(), expectedHost);
+ QCOMPARE(result.path(), expectedPath);
+}
+
+void tst_filepath::url_data()
+{
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<QString>("expectedScheme");
+ QTest::addColumn<QString>("expectedHost");
+ QTest::addColumn<QString>("expectedPath");
+ QTest::newRow("empty") << QString() << QString() << QString() << QString();
+ QTest::newRow("simple-file") << QString("file:///a/b") << QString("file") << QString()
+ << QString("/a/b");
+ QTest::newRow("simple-file-root")
+ << QString("file:///") << QString("file") << QString() << QString("/");
+ QTest::newRow("simple-docker")
+ << QString("docker://1234/a/b") << QString("docker") << QString("1234") << QString("/a/b");
+ QTest::newRow("simple-ssh") << QString("ssh://user@host/a/b") << QString("ssh")
+ << QString("user@host") << QString("/a/b");
+ QTest::newRow("simple-ssh-with-port") << QString("ssh://user@host:1234/a/b") << QString("ssh")
+ << QString("user@host:1234") << QString("/a/b");
+ QTest::newRow("http-qt.io") << QString("http://qt.io") << QString("http") << QString("qt.io")
+ << QString();
+ QTest::newRow("http-qt.io-index.html") << QString("http://qt.io/index.html") << QString("http")
+ << QString("qt.io") << QString("/index.html");
+}
+
+void tst_filepath::cleanPath_data()
+{
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("data0") << "/Users/sam/troll/qt4.0//.."
+ << "/Users/sam/troll";
+ QTest::newRow("data1") << "/Users/sam////troll/qt4.0//.."
+ << "/Users/sam/troll";
+ QTest::newRow("data2") << "/"
+ << "/";
+ QTest::newRow("data2-up") << "/path/.."
+ << "/";
+ QTest::newRow("data2-above-root") << "/.."
+ << "/..";
+ QTest::newRow("data3") << QDir::cleanPath("../.") << "..";
+ QTest::newRow("data4") << QDir::cleanPath("../..") << "../..";
+ QTest::newRow("data5") << "d:\\a\\bc\\def\\.."
+ << "d:/a/bc"; // QDir/Linux had: "d:\\a\\bc\\def\\.."
+ QTest::newRow("data6") << "d:\\a\\bc\\def\\../../.."
+ << "d:/"; // QDir/Linux had: ".."
+ QTest::newRow("data7") << ".//file1.txt"
+ << "file1.txt";
+ QTest::newRow("data8") << "/foo/bar/..//file1.txt"
+ << "/foo/file1.txt";
+ QTest::newRow("data9") << "//"
+ << "//"; // QDir had: "/"
+ QTest::newRow("data10w") << "c:\\"
+ << "c:/";
+ QTest::newRow("data10l") << "/:/"
+ << "/:";
+ QTest::newRow("data11") << "//foo//bar"
+ << "//foo/bar"; // QDir/Win had: "//foo/bar"
+ QTest::newRow("data12") << "ab/a/"
+ << "ab/a"; // Path item with length of 2
+ QTest::newRow("data13w") << "c:/"
+ << "c:/";
+ QTest::newRow("data13w2") << "c:\\"
+ << "c:/";
+ //QTest::newRow("data13l") << "c://" << "c:";
+
+ // QTest::newRow("data14") << "c://foo" << "c:/foo";
+ QTest::newRow("data15") << "//c:/foo"
+ << "//c:/foo"; // QDir/Lin had: "/c:/foo";
+ QTest::newRow("drive-up") << "A:/path/.."
+ << "A:/";
+ QTest::newRow("drive-above-root") << "A:/.."
+ << "A:/..";
+ QTest::newRow("unc-server-up") << "//server/path/.."
+ << "//server/";
+ QTest::newRow("unc-server-above-root") << "//server/.."
+ << "//server/..";
+
+ QTest::newRow("longpath") << "\\\\?\\d:\\"
+ << "d:/";
+ QTest::newRow("longpath-slash") << "//?/d:/"
+ << "d:/";
+ QTest::newRow("longpath-mixed-slashes") << "//?/d:\\"
+ << "d:/";
+ QTest::newRow("longpath-mixed-slashes-2") << "\\\\?\\d:/"
+ << "d:/";
+
+ QTest::newRow("unc-network-share") << "\\\\?\\UNC\\localhost\\c$\\tmp.txt"
+ << "//localhost/c$/tmp.txt";
+ QTest::newRow("unc-network-share-slash") << "//?/UNC/localhost/c$/tmp.txt"
+ << "//localhost/c$/tmp.txt";
+ QTest::newRow("unc-network-share-mixed-slashes") << "//?/UNC/localhost\\c$\\tmp.txt"
+ << "//localhost/c$/tmp.txt";
+ QTest::newRow("unc-network-share-mixed-slashes-2") << "\\\\?\\UNC\\localhost/c$/tmp.txt"
+ << "//localhost/c$/tmp.txt";
+
+ QTest::newRow("QTBUG-23892_0") << "foo/.."
+ << ".";
+ QTest::newRow("QTBUG-23892_1") << "foo/../"
+ << ".";
+
+ QTest::newRow("QTBUG-3472_0") << "/foo/./bar"
+ << "/foo/bar";
+ QTest::newRow("QTBUG-3472_1") << "./foo/.."
+ << ".";
+ QTest::newRow("QTBUG-3472_2") << "./foo/../"
+ << ".";
+
+ QTest::newRow("resource0") << ":/prefix/foo.bar"
+ << ":/prefix/foo.bar";
+ QTest::newRow("resource1") << ":/prefix/..//prefix/foo.bar"
+ << ":/prefix/foo.bar";
+
+ QTest::newRow("ssh") << "ssh://host/prefix/../foo.bar"
+ << "ssh://host/foo.bar";
+ QTest::newRow("ssh2") << "ssh://host/../foo.bar"
+ << "ssh://host/../foo.bar";
+}
+
+void tst_filepath::cleanPath()
+{
+ QFETCH(QString, path);
+ QFETCH(QString, expected);
+ QString cleaned = doCleanPath(path);
+ QCOMPARE(cleaned, expected);
+}
+
+void tst_filepath::isSameFile_data()
+{
+ QTest::addColumn<FilePath>("left");
+ QTest::addColumn<FilePath>("right");
+ QTest::addColumn<bool>("shouldBeEqual");
+
+ QTest::addRow("/==/") << FilePath::fromString("/") << FilePath::fromString("/") << true;
+ QTest::addRow("/!=tmp") << FilePath::fromString("/") << FilePath::fromString(tempDir.path())
+ << false;
+
+ QDir dir(tempDir.path());
+ touch(dir, "target-file", false);
+
+ QFile file(dir.absoluteFilePath("target-file"));
+ if (file.link(dir.absoluteFilePath("source-file"))) {
+ QTest::addRow("real==link")
+ << FilePath::fromString(file.fileName())
+ << FilePath::fromString(dir.absoluteFilePath("target-file")) << true;
+ }
+
+ QTest::addRow("/!=non-existing")
+ << FilePath::fromString("/") << FilePath::fromString("/this-path/does-not-exist") << false;
+
+ QTest::addRow("two-devices") << FilePath::fromString(
+ "docker://boot2qt-raspberrypi4-64:6.5.0/opt/toolchain/sysroots/aarch64-pokysdk-linux/usr/"
+ "bin/aarch64-poky-linux/aarch64-poky-linux-g++")
+ << FilePath::fromString("docker://qt-linux:6/usr/bin/g++")
+ << false;
+}
+
+void tst_filepath::isSameFile()
+{
+ QFETCH(FilePath, left);
+ QFETCH(FilePath, right);
+ QFETCH(bool, shouldBeEqual);
+
+ QCOMPARE(left.isSameFile(right), shouldBeEqual);
+}
+
+void tst_filepath::hostSpecialChars_data()
+{
+ QTest::addColumn<QString>("scheme");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<FilePath>("expected");
+
+ QTest::addRow("slash-in-host") << "device"
+ << "host/name"
+ << "/" << FilePath::fromString("device://host%2fname/");
+ QTest::addRow("percent-in-host") << "device"
+ << "host%name"
+ << "/" << FilePath::fromString("device://host%25name/");
+ QTest::addRow("percent-and-slash-in-host")
+ << "device"
+ << "host/%name"
+ << "/" << FilePath::fromString("device://host%2f%25name/");
+ QTest::addRow("qtc-dev-slash-in-host-linux")
+ << "device"
+ << "host/name"
+ << "/" << FilePath::fromString("/__qtc_devices__/device/host%2fname/");
+ QTest::addRow("qtc-dev-slash-in-host-windows")
+ << "device"
+ << "host/name"
+ << "/" << FilePath::fromString("c:/__qtc_devices__/device/host%2fname/");
+}
+
+void tst_filepath::hostSpecialChars()
+{
+ QFETCH(QString, scheme);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+ QFETCH(FilePath, expected);
+
+ FilePath fp;
+ fp.setParts(scheme, host, path);
+
+ // Check that setParts and fromString give the same result
+ QCOMPARE(fp, expected);
+ QCOMPARE(fp.host(), expected.host());
+ QCOMPARE(fp.host(), host);
+ QCOMPARE(expected.host(), host);
+
+ QString toStringExpected = expected.toString();
+ QString toStringActual = fp.toString();
+
+ // Check that toString gives the same result
+ QCOMPARE(toStringActual, toStringExpected);
+
+ // Check that fromString => toString => fromString gives the same result
+ FilePath toFromExpected = FilePath::fromString(expected.toString());
+ QCOMPARE(toFromExpected, expected);
+ QCOMPARE(toFromExpected, fp);
+
+ // Check that setParts => toString => fromString gives the same result
+ FilePath toFromActual = FilePath::fromString(fp.toString());
+ QCOMPARE(toFromActual, fp);
+ QCOMPARE(toFromExpected, expected);
+}
+
+void tst_filepath::tmp_data()
+{
+ QTest::addColumn<QString>("templatepath");
+ QTest::addColumn<bool>("expected");
+
+ QTest::addRow("empty") << "" << true;
+ QTest::addRow("no-template") << "foo" << true;
+ QTest::addRow("realtive-template") << "my-file-XXXXXXXX" << true;
+ QTest::addRow("absolute-template") << QDir::tempPath() + "/my-file-XXXXXXXX" << true;
+ QTest::addRow("non-existing-dir") << "/this/path/does/not/exist/my-file-XXXXXXXX" << false;
+
+ QTest::addRow("on-device") << "device://test/./my-file-XXXXXXXX" << true;
+}
+
+void tst_filepath::tmp()
+{
+ QFETCH(QString, templatepath);
+ QFETCH(bool, expected);
+
+ FilePath fp = FilePath::fromString(templatepath);
+
+ const auto result = fp.createTempFile();
+ QCOMPARE(result.has_value(), expected);
+
+ if (result.has_value()) {
+ QVERIFY(result->exists());
+ QVERIFY(result->removeFile());
+ }
+}
+
+void tst_filepath::sort()
+{
+ QFETCH(QStringList, input);
+
+ FilePaths filePaths = Utils::transform(input, &FilePath::fromString);
+ QStringList sorted = input;
+ sorted.sort();
+
+ FilePath::sort(filePaths);
+ QStringList sortedPaths = Utils::transform(filePaths, &FilePath::toString);
+
+ QCOMPARE(sortedPaths, sorted);
+}
+
+void tst_filepath::sort_data()
+{
+ QTest::addColumn<QStringList>("input");
+
+ QTest::addRow("empty") << QStringList{};
+
+ QTest::addRow("one") << QStringList{"foo"};
+ QTest::addRow("two") << QStringList{"foo", "bar"};
+ QTest::addRow("three") << QStringList{"foo", "bar", "baz"};
+
+ QTest::addRow("one-absolute") << QStringList{"/foo"};
+ QTest::addRow("two-absolute") << QStringList{"/foo", "/bar"};
+
+ QTest::addRow("one-relative") << QStringList{"foo"};
+
+ QTest::addRow("one-absolute-one-relative") << QStringList{"/foo", "bar"};
+
+ QTest::addRow("host") << QStringList{"ssh://test/blah", "ssh://gulp/blah", "ssh://zzz/blah"};
+
+ QTest::addRow("scheme") << QStringList{"ssh://test/blah",
+ "ssh://gulp/blah",
+ "ssh://zzz/blah",
+ "aaa://gulp/blah",
+ "xyz://test/blah"};
+
+ QTest::addRow("others") << QStringList{"a://a//a",
+ "a://b//a",
+ "a://a//b",
+ "a://b//b",
+ "b://b//b"};
+ QTest::addRow("others-reversed")
+ << QStringList{"b://b//b", "a://b//b", "a://a//b", "a://b//a", "a://a//a"};
+}
+
+QTEST_GUILESS_MAIN(tst_filepath)
+
+#include "tst_filepath.moc"
diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp
index 033cf680be..f4596cb1a5 100644
--- a/tests/auto/utils/fileutils/tst_fileutils.cpp
+++ b/tests/auto/utils/fileutils/tst_fileutils.cpp
@@ -1,13 +1,10 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QDebug>
-#include <QRandomGenerator>
#include <QtTest>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/link.h>
//TESTED_COMPONENT=src/libs/utils
using namespace Utils;
@@ -24,948 +21,20 @@ class tst_fileutils : public QObject
{
Q_OBJECT
-signals:
- void asyncTestDone(); // test internal helper signal
-
private slots:
void initTestCase();
- void isEmpty_data();
- void isEmpty();
-
- void parentDir_data();
- void parentDir();
-
- void isChildOf_data();
- void isChildOf();
-
- void fileName_data();
- void fileName();
-
- void calcRelativePath_data();
- void calcRelativePath();
-
- void relativePath_specials();
- void relativePath_data();
- void relativePath();
-
- void absolute_data();
- void absolute();
-
- void fromToString_data();
- void fromToString();
-
- void fromString_data();
- void fromString();
-
- void fromUserInput_data();
- void fromUserInput();
-
- void toString_data();
- void toString();
-
- void toFSPathString_data();
- void toFSPathString();
-
- void comparison_data();
- void comparison();
-
- void linkFromString_data();
- void linkFromString();
-
- void pathAppended_data();
- void pathAppended();
-
void commonPath_data();
void commonPath();
- void resolvePath_data();
- void resolvePath();
-
- void relativeChildPath_data();
- void relativeChildPath();
-
void bytesAvailableFromDF_data();
void bytesAvailableFromDF();
- void rootLength_data();
- void rootLength();
-
- void schemeAndHostLength_data();
- void schemeAndHostLength();
-
- void asyncLocalCopy();
- void startsWithDriveLetter();
- void startsWithDriveLetter_data();
-
- void onDevice_data();
- void onDevice();
-
- void stringAppended();
- void stringAppended_data();
- void url();
- void url_data();
-
- void cleanPath_data();
- void cleanPath();
-
- void isSameFile_data();
- void isSameFile();
-
- void hostSpecialChars_data();
- void hostSpecialChars();
-
- void tmp_data();
- void tmp();
-
void filePathInfoFromTriple_data();
void filePathInfoFromTriple();
-
-private:
- QTemporaryDir tempDir;
- QString rootPath;
};
-static void touch(const QDir &dir, const QString &filename, bool fill)
-{
- QFile file(dir.absoluteFilePath(filename));
- file.open(QIODevice::WriteOnly);
- if (fill) {
- QRandomGenerator *random = QRandomGenerator::global();
- for (int i = 0; i < 10; ++i)
- file.write(QString::number(random->generate(), 16).toUtf8());
- }
- file.close();
-}
-
-void tst_fileutils::initTestCase()
-{
- // initialize test for tst_fileutiles::relativePath*()
- QVERIFY(tempDir.isValid());
- rootPath = tempDir.path();
- QDir dir(rootPath);
- dir.mkpath("a/b/c/d");
- dir.mkpath("a/x/y/z");
- dir.mkpath("a/b/x/y/z");
- dir.mkpath("x/y/z");
- touch(dir, "a/b/c/d/file1.txt", false);
- touch(dir, "a/x/y/z/file2.txt", false);
- touch(dir, "a/file3.txt", false);
- touch(dir, "x/y/file4.txt", false);
-
- // initialize test for tst_fileutils::asyncLocalCopy()
- touch(dir, "x/y/fileToCopy.txt", true);
-}
-
-void tst_fileutils::isEmpty_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<bool>("result");
-
- QTest::newRow("empty path") << "" << true;
- QTest::newRow("root only") << "/" << false;
- QTest::newRow("//") << "//" << false;
- QTest::newRow("scheme://host") << "scheme://host" << true; // Intentional (for now?)
- QTest::newRow("scheme://host/") << "scheme://host/" << false;
- QTest::newRow("scheme://host/a") << "scheme://host/a" << false;
- QTest::newRow("scheme://host/.") << "scheme://host/." << false;
-}
-
-void tst_fileutils::isEmpty()
-{
- QFETCH(QString, path);
- QFETCH(bool, result);
-
- FilePath filePath = FilePath::fromString(path);
- QCOMPARE(filePath.isEmpty(), result);
-}
-
-void tst_fileutils::parentDir_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<QString>("parentPath");
- QTest::addColumn<QString>("expectFailMessage");
-
- QTest::newRow("empty path") << "" << "" << "";
- QTest::newRow("root only") << "/" << "" << "";
- QTest::newRow("//") << "//" << "" << "";
- QTest::newRow("/tmp/dir") << "/tmp/dir" << "/tmp" << "";
- QTest::newRow("relative/path") << "relative/path" << "relative" << "";
- QTest::newRow("relativepath") << "relativepath" << "." << "";
-
- // Windows stuff:
- QTest::newRow("C:/data") << "C:/data" << "C:/" << "";
- QTest::newRow("C:/") << "C:/" << "" << "";
- QTest::newRow("//./com1") << "//./com1" << "//./" << "";
- QTest::newRow("//?/path") << "//?/path" << "/" << "Qt 4 cannot handle this path.";
- QTest::newRow("/Global?\?/UNC/host") << "/Global?\?/UNC/host" << "/Global?\?/UNC/host"
- << "Qt 4 cannot handle this path.";
- QTest::newRow("//server/directory/file")
- << "//server/directory/file" << "//server/directory" << "";
- QTest::newRow("//server/directory") << "//server/directory" << "//server/" << "";
- QTest::newRow("//server") << "//server" << "" << "";
-
- QTest::newRow("qrc") << ":/foo/bar.txt" << ":/foo" << "";
-}
-
-void tst_fileutils::parentDir()
-{
- QFETCH(QString, path);
- QFETCH(QString, parentPath);
- QFETCH(QString, expectFailMessage);
-
- FilePath result = FilePath::fromUserInput(path).parentDir();
- if (!expectFailMessage.isEmpty())
- QEXPECT_FAIL("", expectFailMessage.toUtf8().constData(), Continue);
- QCOMPARE(result.toString(), parentPath);
-}
-
-void tst_fileutils::isChildOf_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<QString>("childPath");
- QTest::addColumn<bool>("result");
-
- QTest::newRow("empty path") << "" << "/tmp" << false;
- QTest::newRow("root only") << "/" << "/tmp" << true;
- QTest::newRow("/tmp/dir") << "/tmp" << "/tmp/dir" << true;
- QTest::newRow("relative/path") << "relative" << "relative/path" << true;
- QTest::newRow("/tmpdir") << "/tmp" << "/tmpdir" << false;
- QTest::newRow("same") << "/tmp/dir" << "/tmp/dir" << false;
-
- // Windows stuff:
- QTest::newRow("C:/data") << "C:/" << "C:/data" << true;
- QTest::newRow("C:/") << "" << "C:/" << false;
- QTest::newRow("com-port") << "//./" << "//./com1" << true;
- QTest::newRow("extended-length-path") << "\\\\?\\C:\\" << "\\\\?\\C:\\path" << true;
- QTest::newRow("/Global?\?/UNC/host") << "/Global?\?/UNC/host"
- << "/Global?\?/UNC/host/file" << true;
- QTest::newRow("//server/directory/file")
- << "//server/directory" << "//server/directory/file" << true;
- QTest::newRow("//server/directory")
- << "//server" << "//server/directory" << true;
-
- QTest::newRow("qrc") << ":/foo/bar" << ":/foo/bar/blah" << true;
-}
-
-void tst_fileutils::isChildOf()
-{
- QFETCH(QString, path);
- QFETCH(QString, childPath);
- QFETCH(bool, result);
-
- const FilePath child = FilePath::fromUserInput(childPath);
- const FilePath parent = FilePath::fromUserInput(path);
-
- QCOMPARE(child.isChildOf(parent), result);
-}
-
-void tst_fileutils::fileName_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<int>("components");
- QTest::addColumn<QString>("result");
-
- QTest::newRow("empty 1") << "" << 0 << "";
- QTest::newRow("empty 2") << "" << 1 << "";
- QTest::newRow("basic") << "/foo/bar/baz" << 0 << "baz";
- QTest::newRow("2 parts") << "/foo/bar/baz" << 1 << "bar/baz";
- QTest::newRow("root no depth") << "/foo" << 0 << "foo";
- QTest::newRow("root full") << "/foo" << 1 << "/foo";
- QTest::newRow("root included") << "/foo/bar/baz" << 2 << "/foo/bar/baz";
- QTest::newRow("too many parts") << "/foo/bar/baz" << 5 << "/foo/bar/baz";
- QTest::newRow("windows root") << "C:/foo/bar/baz" << 2 << "C:/foo/bar/baz";
- QTest::newRow("smb share") << "//server/share/file" << 2 << "//server/share/file";
- QTest::newRow("no slashes") << "foobar" << 0 << "foobar";
- QTest::newRow("no slashes with depth") << "foobar" << 1 << "foobar";
- QTest::newRow("multiple slashes 1") << "/foo/bar////baz" << 0 << "baz";
- QTest::newRow("multiple slashes 2") << "/foo/bar////baz" << 1 << "bar////baz";
- QTest::newRow("multiple slashes 3") << "/foo////bar/baz" << 2 << "/foo////bar/baz";
- QTest::newRow("single char 1") << "/a/b/c" << 0 << "c";
- QTest::newRow("single char 2") << "/a/b/c" << 1 << "b/c";
- QTest::newRow("single char 3") << "/a/b/c" << 2 << "/a/b/c";
- QTest::newRow("slash at end 1") << "/a/b/" << 0 << "";
- QTest::newRow("slash at end 2") << "/a/b/" << 1 << "b/";
- QTest::newRow("slashes at end 1") << "/a/b//" << 0 << "";
- QTest::newRow("slashes at end 2") << "/a/b//" << 1 << "b//";
- QTest::newRow("root only 1") << "/" << 0 << "";
- QTest::newRow("root only 2") << "/" << 1 << "/";
- QTest::newRow("qrc 0") << ":/foo/bar" << 0 << "bar";
- QTest::newRow("qrc with root") << ":/foo/bar" << 1 << ":/foo/bar";
-}
-
-void tst_fileutils::fileName()
-{
- QFETCH(QString, path);
- QFETCH(int, components);
- QFETCH(QString, result);
- QCOMPARE(FilePath::fromString(path).fileNameWithPathComponents(components), result);
-}
-
-void tst_fileutils::calcRelativePath_data()
-{
- QTest::addColumn<QString>("absolutePath");
- QTest::addColumn<QString>("anchorPath");
- QTest::addColumn<QString>("result");
-
- QTest::newRow("empty") << "" << "" << "";
- QTest::newRow("leftempty") << "" << "/" << "";
- QTest::newRow("rightempty") << "/" << "" << "";
- QTest::newRow("root") << "/" << "/" << ".";
- QTest::newRow("simple1") << "/a" << "/" << "a";
- QTest::newRow("simple2") << "/" << "/a" << "..";
- QTest::newRow("simple3") << "/a" << "/a" << ".";
- QTest::newRow("extraslash1") << "/a/b/c" << "/a/b/c" << ".";
- QTest::newRow("extraslash2") << "/a/b/c" << "/a/b/c/" << ".";
- QTest::newRow("extraslash3") << "/a/b/c/" << "/a/b/c" << ".";
- QTest::newRow("normal1") << "/a/b/c" << "/a/x" << "../b/c";
- QTest::newRow("normal2") << "/a/b/c" << "/a/x/y" << "../../b/c";
- QTest::newRow("normal3") << "/a/b/c" << "/x/y" << "../../a/b/c";
-}
-
-void tst_fileutils::calcRelativePath()
-{
- QFETCH(QString, absolutePath);
- QFETCH(QString, anchorPath);
- QFETCH(QString, result);
- QString relativePath = Utils::FilePath::calcRelativePath(absolutePath, anchorPath);
- QCOMPARE(relativePath, result);
-}
-
-void tst_fileutils::relativePath_specials()
-{
- QString path = FilePath("").relativePathFrom("").toString();
- QCOMPARE(path, "");
-}
-
-void tst_fileutils::relativePath_data()
-{
- QTest::addColumn<QString>("relative");
- QTest::addColumn<QString>("anchor");
- QTest::addColumn<QString>("result");
-
- QTest::newRow("samedir") << "/" << "/" << ".";
- QTest::newRow("samedir_but_file") << "a/b/c/d/file1.txt" << "a/b/c/d" << "file1.txt";
- QTest::newRow("samedir_but_file2") << "a/b/c/d" << "a/b/c/d/file1.txt" << ".";
- QTest::newRow("dir2dir_1") << "a/b/c/d" << "a/x/y/z" << "../../../b/c/d";
- QTest::newRow("dir2dir_2") << "a/b" <<"a/b/c" << "..";
- QTest::newRow("file2file_1") << "a/b/c/d/file1.txt" << "a/file3.txt" << "b/c/d/file1.txt";
- QTest::newRow("dir2file_1") << "a/b/c" << "a/x/y/z/file2.txt" << "../../../b/c";
- QTest::newRow("file2dir_1") << "a/b/c/d/file1.txt" << "x/y" << "../../a/b/c/d/file1.txt";
-}
-
-void tst_fileutils::relativePath()
-{
- QFETCH(QString, relative);
- QFETCH(QString, anchor);
- QFETCH(QString, result);
- FilePath actualPath = FilePath::fromString(rootPath + "/" + relative)
- .relativePathFrom(FilePath::fromString(rootPath + "/" + anchor));
- QCOMPARE(actualPath.toString(), result);
-}
-
-void tst_fileutils::rootLength_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<int>("result");
-
- QTest::newRow("empty") << "" << 0;
- QTest::newRow("slash") << "/" << 1;
- QTest::newRow("slash-rest") << "/abc" << 1;
- QTest::newRow("rest") << "abc" << 0;
- QTest::newRow("drive-slash") << "x:/" << 3;
- QTest::newRow("drive-rest") << "x:abc" << 0;
- QTest::newRow("drive-slash-rest") << "x:/abc" << 3;
-
- QTest::newRow("unc-root") << "//" << 2;
- QTest::newRow("unc-localhost-unfinished") << "//localhost" << 11;
- QTest::newRow("unc-localhost") << "//localhost/" << 12;
- QTest::newRow("unc-localhost-rest") << "//localhost/abs" << 12;
- QTest::newRow("unc-localhost-drive") << "//localhost/c$" << 12;
- QTest::newRow("unc-localhost-drive-slash") << "//localhost//c$/" << 12;
- QTest::newRow("unc-localhost-drive-slash-rest") << "//localhost//c$/x" << 12;
-}
-
-void tst_fileutils::rootLength()
-{
- QFETCH(QString, path);
- QFETCH(int, result);
-
- int actual = FilePath::rootLength(path);
- QCOMPARE(actual, result);
-}
-
-void tst_fileutils::schemeAndHostLength_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<int>("result");
-
- QTest::newRow("empty") << "" << 0;
- QTest::newRow("drive-slash-rest") << "x:/abc" << 0;
- QTest::newRow("rest") << "abc" << 0;
- QTest::newRow("slash-rest") << "/abc" << 0;
- QTest::newRow("dev-empty") << "dev://" << 6;
- QTest::newRow("dev-localhost-unfinished") << "dev://localhost" << 15;
- QTest::newRow("dev-localhost") << "dev://localhost/" << 16;
- QTest::newRow("dev-localhost-rest") << "dev://localhost/abs" << 16;
- QTest::newRow("dev-localhost-drive") << "dev://localhost/c$" << 16;
- QTest::newRow("dev-localhost-drive-slash") << "dev://localhost//c$/" << 16;
- QTest::newRow("dev-localhost-drive-slash-rest") << "dev://localhost//c$/x" << 16;
-}
-
-void tst_fileutils::schemeAndHostLength()
-{
- QFETCH(QString, path);
- QFETCH(int, result);
-
- int actual = FilePath::schemeAndHostLength(path);
- QCOMPARE(actual, result);
-}
-
-void tst_fileutils::absolute_data()
-{
- QTest::addColumn<FilePath>("path");
- QTest::addColumn<FilePath>("absoluteFilePath");
- QTest::addColumn<FilePath>("absolutePath");
-
- QTest::newRow("absolute1") << FilePath::fromString("/")
- << FilePath::fromString("/")
- << FilePath::fromString("/");
- QTest::newRow("absolute2") << FilePath::fromString("C:/a/b")
- << FilePath::fromString("C:/a/b")
- << FilePath::fromString("C:/a");
- QTest::newRow("absolute3") << FilePath::fromString("/a/b")
- << FilePath::fromString("/a/b")
- << FilePath::fromString("/a");
- QTest::newRow("absolute4") << FilePath::fromString("/a/b/..")
- << FilePath::fromString("/a")
- << FilePath::fromString("/");
- QTest::newRow("absolute5") << FilePath::fromString("/a/b/c/../d")
- << FilePath::fromString("/a/b/d")
- << FilePath::fromString("/a/b");
- QTest::newRow("absolute6") << FilePath::fromString("/a/../b/c/d")
- << FilePath::fromString("/b/c/d")
- << FilePath::fromString("/b/c");
- QTest::newRow("default-constructed") << FilePath() << FilePath() << FilePath();
- QTest::newRow("relative") << FilePath::fromString("a/b")
- << FilePath::fromString(QDir::currentPath() + "/a/b")
- << FilePath::fromString(QDir::currentPath() + "/a");
- QTest::newRow("qrc") << FilePath::fromString(":/foo/bar.txt")
- << FilePath::fromString(":/foo/bar.txt")
- << FilePath::fromString(":/foo");
-}
-
-void tst_fileutils::absolute()
-{
- QFETCH(FilePath, path);
- QFETCH(FilePath, absoluteFilePath);
- QFETCH(FilePath, absolutePath);
- QCOMPARE(path.absoluteFilePath(), absoluteFilePath);
- QCOMPARE(path.absolutePath(), absolutePath);
-}
-
-void tst_fileutils::toString_data()
-{
- QTest::addColumn<QString>("scheme");
- QTest::addColumn<QString>("host");
- QTest::addColumn<QString>("path");
- QTest::addColumn<QString>("result");
- QTest::addColumn<QString>("userResult");
-
- QTest::newRow("empty") << "" << "" << "" << "" << "";
- QTest::newRow("scheme") << "http" << "" << "" << "http:///./" << "http:///./";
- QTest::newRow("scheme-and-host") << "http" << "127.0.0.1" << "" << "http://127.0.0.1/./" << "http://127.0.0.1/./";
- QTest::newRow("root") << "http" << "127.0.0.1" << "/" << "http://127.0.0.1/" << "http://127.0.0.1/";
-
- QTest::newRow("root-folder") << "" << "" << "/" << "/" << "/";
- QTest::newRow("qtc-dev-root-folder-linux") << "" << "" << "/__qtc_devices__" << "/__qtc_devices__" << "/__qtc_devices__";
- QTest::newRow("qtc-dev-root-folder-win") << "" << "" << "c:/__qtc_devices__" << "c:/__qtc_devices__" << "c:/__qtc_devices__";
- QTest::newRow("qtc-dev-type-root-folder-linux") << "" << "" << "/__qtc_devices__/docker" << "/__qtc_devices__/docker" << "/__qtc_devices__/docker";
- QTest::newRow("qtc-dev-type-root-folder-win") << "" << "" << "c:/__qtc_devices__/docker" << "c:/__qtc_devices__/docker" << "c:/__qtc_devices__/docker";
- QTest::newRow("qtc-root-folder") << "docker" << "alpine:latest" << "/" << "docker://alpine:latest/" << "docker://alpine:latest/";
- QTest::newRow("qtc-root-folder-rel") << "docker" << "alpine:latest" << "" << "docker://alpine:latest/./" << "docker://alpine:latest/./";
-}
-
-void tst_fileutils::toString()
-{
- QFETCH(QString, scheme);
- QFETCH(QString, host);
- QFETCH(QString, path);
- QFETCH(QString, result);
- QFETCH(QString, userResult);
-
- FilePath filePath = FilePath::fromParts(scheme, host, path);
- QCOMPARE(filePath.toString(), result);
- QString cleanedOutput = filePath.needsDevice() ? filePath.toUserOutput() : QDir::cleanPath(filePath.toUserOutput());
- QCOMPARE(cleanedOutput, userResult);
-}
-
-void tst_fileutils::toFSPathString_data()
-{
- QTest::addColumn<QString>("scheme");
- QTest::addColumn<QString>("host");
- QTest::addColumn<QString>("path");
- QTest::addColumn<QString>("result");
- QTest::addColumn<QString>("userResult");
-
- QTest::newRow("empty") << "" << "" << "" << "" << "";
- QTest::newRow("scheme") << "http" << "" << "" << QDir::rootPath() + "__qtc_devices__/http//./" << "http:///./";
- QTest::newRow("scheme-and-host") << "http" << "127.0.0.1" << "" << QDir::rootPath() + "__qtc_devices__/http/127.0.0.1/./" << "http://127.0.0.1/./";
- QTest::newRow("root") << "http" << "127.0.0.1" << "/" << QDir::rootPath() + "__qtc_devices__/http/127.0.0.1/" << "http://127.0.0.1/";
-
- QTest::newRow("root-folder") << "" << "" << "/" << "/" << "/";
- QTest::newRow("qtc-dev-root-folder") << "" << "" << QDir::rootPath() + "__qtc_devices__" << QDir::rootPath() + "__qtc_devices__" << QDir::rootPath() + "__qtc_devices__";
- QTest::newRow("qtc-dev-type-root-folder") << "" << "" << QDir::rootPath() + "__qtc_devices__/docker" << QDir::rootPath() + "__qtc_devices__/docker" << QDir::rootPath() + "__qtc_devices__/docker";
- QTest::newRow("qtc-root-folder") << "docker" << "alpine:latest" << "/" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest/" << "docker://alpine:latest/";
- QTest::newRow("qtc-root-folder-rel") << "docker" << "alpine:latest" << "" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest/./" << "docker://alpine:latest/./";
-}
-
-void tst_fileutils::toFSPathString()
-{
- QFETCH(QString, scheme);
- QFETCH(QString, host);
- QFETCH(QString, path);
- QFETCH(QString, result);
- QFETCH(QString, userResult);
-
- FilePath filePath = FilePath::fromParts(scheme, host, path);
- QCOMPARE(filePath.toFSPathString(), result);
- QString cleanedOutput = filePath.needsDevice() ? filePath.toUserOutput() : QDir::cleanPath(filePath.toUserOutput());
- QCOMPARE(cleanedOutput, userResult);
-}
-
-enum ExpectedPass
-{
- PassEverywhere = 0,
- FailOnWindows = 1,
- FailOnLinux = 2,
- FailEverywhere = 3
-};
-
-class FromStringData
-{
-public:
- FromStringData(const QString &input, const QString &scheme, const QString &host,
- const QString &path, ExpectedPass expectedPass = PassEverywhere)
- : input(input), scheme(scheme), host(host),
- path(path), expectedPass(expectedPass)
- {}
-
- QString input;
- QString scheme;
- QString host;
- QString path;
- ExpectedPass expectedPass = PassEverywhere;
-};
-
-Q_DECLARE_METATYPE(FromStringData);
-
-void tst_fileutils::fromString_data()
-{
- using D = FromStringData;
- QTest::addColumn<D>("data");
-
- QTest::newRow("empty") << D("", "", "", "");
- QTest::newRow("single-colon") << D(":", "", "", ":");
- QTest::newRow("single-slash") << D("/", "", "", "/");
- QTest::newRow("single-char") << D("a", "", "", "a");
- QTest::newRow("relative") << D("./rel", "", "", "./rel");
- QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt");
- QTest::newRow("qrc-no-slash") << D(":test.txt", "", "", ":test.txt");
-
- QTest::newRow("unc-incomplete") << D("//", "", "", "//");
- QTest::newRow("unc-incomplete-only-server") << D("//server", "", "", "//server");
- QTest::newRow("unc-incomplete-only-server-2") << D("//server/", "", "", "//server/");
- QTest::newRow("unc-server-and-share") << D("//server/share", "", "", "//server/share");
- QTest::newRow("unc-server-and-share-2") << D("//server/share/", "", "", "//server/share/");
- QTest::newRow("unc-full") << D("//server/share/test.txt", "", "", "//server/share/test.txt");
-
- QTest::newRow("unix-root") << D("/", "", "", "/");
- QTest::newRow("unix-folder") << D("/tmp", "", "", "/tmp");
- QTest::newRow("unix-folder-with-trailing-slash") << D("/tmp/", "", "", "/tmp/");
-
- QTest::newRow("windows-root") << D("c:", "", "", "c:");
- QTest::newRow("windows-folder") << D("c:/Windows", "", "", "c:/Windows");
- QTest::newRow("windows-folder-with-trailing-slash") << D("c:/Windows/", "", "", "c:/Windows/");
- QTest::newRow("windows-folder-slash") << D("C:/Windows", "", "", "C:/Windows");
-
- QTest::newRow("docker-root-url") << D("docker://1234/", "docker", "1234", "/");
- QTest::newRow("docker-root-url-special-linux") << D("/__qtc_devices__/docker/1234/", "docker", "1234", "/");
- QTest::newRow("docker-root-url-special-win") << D("c:/__qtc_devices__/docker/1234/", "docker", "1234", "/");
- QTest::newRow("docker-relative-path") << D("docker://1234/./rel", "docker", "1234", "rel");
-
- QTest::newRow("qtc-dev-linux") << D("/__qtc_devices__", "", "", "/__qtc_devices__");
- QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__");
- QTest::newRow("qtc-dev-type-linux") << D("/__qtc_devices__/docker", "", "", "/__qtc_devices__/docker");
- QTest::newRow("qtc-dev-type-win") << D("c:/__qtc_devices__/docker", "", "", "c:/__qtc_devices__/docker");
- QTest::newRow("qtc-dev-type-dev-linux") << D("/__qtc_devices__/docker/1234", "docker", "1234", "/");
- QTest::newRow("qtc-dev-type-dev-win") << D("c:/__qtc_devices__/docker/1234", "docker", "1234", "/");
-
- // "Remote Windows" is currently truly not supported.
- QTest::newRow("cross-os-linux")
- << D("/__qtc_devices__/docker/1234/c:/test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
- QTest::newRow("cross-os-win")
- << D("c:/__qtc_devices__/docker/1234/c:/test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
- QTest::newRow("cross-os-unclean-linux")
- << D("/__qtc_devices__/docker/1234/c:\\test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
- QTest::newRow("cross-os-unclean-win")
- << D("c:/__qtc_devices__/docker/1234/c:\\test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
-
- QTest::newRow("unc-full-in-docker-linux")
- << D("/__qtc_devices__/docker/1234//server/share/test.txt", "docker", "1234", "//server/share/test.txt");
- QTest::newRow("unc-full-in-docker-win")
- << D("c:/__qtc_devices__/docker/1234//server/share/test.txt", "docker", "1234", "//server/share/test.txt");
-
- QTest::newRow("unc-dos-1") << D("//?/c:", "", "", "//?/c:");
- QTest::newRow("unc-dos-com") << D("//./com1", "", "", "//./com1");
-}
-
-void tst_fileutils::fromString()
-{
- QFETCH(FromStringData, data);
-
- FilePath filePath = FilePath::fromString(data.input);
-
- bool expectFail = ((data.expectedPass & FailOnLinux) && !HostOsInfo::isWindowsHost())
- || ((data.expectedPass & FailOnWindows) && HostOsInfo::isWindowsHost());
-
- if (expectFail) {
- QString actual = filePath.scheme() + '|' + filePath.host() + '|' + filePath.path();
- QString expected = data.scheme + '|' + data.host + '|' + data.path;
- QEXPECT_FAIL("", "", Continue);
- QCOMPARE(actual, expected);
- return;
- }
-
- QCOMPARE(filePath.scheme(), data.scheme);
- QCOMPARE(filePath.host(), data.host);
- QCOMPARE(filePath.path(), data.path);
-}
-
-void tst_fileutils::fromUserInput_data()
-{
- using D = FromStringData;
- QTest::addColumn<D>("data");
-
- QTest::newRow("empty") << D("", "", "", "");
- QTest::newRow("single-colon") << D(":", "", "", ":");
- QTest::newRow("single-slash") << D("/", "", "", "/");
- QTest::newRow("single-char") << D("a", "", "", "a");
- QTest::newRow("relative") << D("./rel", "", "", "rel");
- QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt");
- QTest::newRow("qrc-no-slash") << D(":test.txt", "", "", ":test.txt");
-
- QTest::newRow("unc-incomplete") << D("//", "", "", "//");
- QTest::newRow("unc-incomplete-only-server") << D("//server", "", "", "//server");
- QTest::newRow("unc-incomplete-only-server-2") << D("//server/", "", "", "//server/");
- QTest::newRow("unc-server-and-share") << D("//server/share", "", "", "//server/share");
- QTest::newRow("unc-server-and-share-2") << D("//server/share/", "", "", "//server/share");
- QTest::newRow("unc-full") << D("//server/share/test.txt", "", "", "//server/share/test.txt");
-
- QTest::newRow("unix-root") << D("/", "", "", "/");
- QTest::newRow("unix-folder") << D("/tmp", "", "", "/tmp");
- QTest::newRow("unix-folder-with-trailing-slash") << D("/tmp/", "", "", "/tmp");
-
- QTest::newRow("windows-root") << D("c:", "", "", "c:");
- QTest::newRow("windows-folder") << D("c:/Windows", "", "", "c:/Windows");
- QTest::newRow("windows-folder-with-trailing-slash") << D("c:\\Windows\\", "", "", "c:/Windows");
- QTest::newRow("windows-folder-slash") << D("C:/Windows", "", "", "C:/Windows");
-
- QTest::newRow("docker-root-url") << D("docker://1234/", "docker", "1234", "/");
- QTest::newRow("docker-root-url-special-linux") << D("/__qtc_devices__/docker/1234/", "docker", "1234", "/");
- QTest::newRow("docker-root-url-special-win") << D("c:/__qtc_devices__/docker/1234/", "docker", "1234", "/");
- QTest::newRow("docker-relative-path") << D("docker://1234/./rel", "docker", "1234", "rel", FailEverywhere);
-
- QTest::newRow("qtc-dev-linux") << D("/__qtc_devices__", "", "", "/__qtc_devices__");
- QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__");
- QTest::newRow("qtc-dev-type-linux") << D("/__qtc_devices__/docker", "", "", "/__qtc_devices__/docker");
- QTest::newRow("qtc-dev-type-win") << D("c:/__qtc_devices__/docker", "", "", "c:/__qtc_devices__/docker");
- QTest::newRow("qtc-dev-type-dev-linux") << D("/__qtc_devices__/docker/1234", "docker", "1234", "/");
- QTest::newRow("qtc-dev-type-dev-win") << D("c:/__qtc_devices__/docker/1234", "docker", "1234", "/");
-
- // "Remote Windows" is currently truly not supported.
- QTest::newRow("cross-os-linux")
- << D("/__qtc_devices__/docker/1234/c:/test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
- QTest::newRow("cross-os-win")
- << D("c:/__qtc_devices__/docker/1234/c:/test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
- QTest::newRow("cross-os-unclean-linux")
- << D("/__qtc_devices__/docker/1234/c:\\test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
- QTest::newRow("cross-os-unclean-win")
- << D("c:/__qtc_devices__/docker/1234/c:\\test.txt", "docker", "1234", "c:/test.txt", FailEverywhere);
-
- QTest::newRow("unc-full-in-docker-linux")
- << D("/__qtc_devices__/docker/1234//server/share/test.txt", "docker", "1234", "//server/share/test.txt", FailEverywhere);
- QTest::newRow("unc-full-in-docker-win")
- << D("c:/__qtc_devices__/docker/1234//server/share/test.txt", "docker", "1234", "//server/share/test.txt", FailEverywhere);
-
- QTest::newRow("unc-dos-1") << D("//?/c:", "", "", "c:");
- QTest::newRow("unc-dos-com") << D("//./com1", "", "", "//./com1");
-}
-
-void tst_fileutils::fromUserInput()
-{
- QFETCH(FromStringData, data);
-
- FilePath filePath = FilePath::fromUserInput(data.input);
-
- bool expectFail = ((data.expectedPass & FailOnLinux) && !HostOsInfo::isWindowsHost())
- || ((data.expectedPass & FailOnWindows) && HostOsInfo::isWindowsHost());
-
- if (expectFail) {
- QString actual = filePath.scheme() + '|' + filePath.host() + '|' + filePath.path();
- QString expected = data.scheme + '|' + data.host + '|' + data.path;
- QEXPECT_FAIL("", "", Continue);
- QCOMPARE(actual, expected);
- return;
- }
-
- QCOMPARE(filePath.scheme(), data.scheme);
- QCOMPARE(filePath.host(), data.host);
- QCOMPARE(filePath.path(), data.path);
-}
-
-void tst_fileutils::fromToString_data()
-{
- QTest::addColumn<QString>("scheme");
- QTest::addColumn<QString>("host");
- QTest::addColumn<QString>("path");
- QTest::addColumn<QString>("full");
-
- QTest::newRow("s0") << "" << "" << "" << "";
- QTest::newRow("s1") << "" << "" << "/" << "/";
- QTest::newRow("s2") << "" << "" << "a/b/c/d" << "a/b/c/d";
- QTest::newRow("s3") << "" << "" << "/a/b" << "/a/b";
-
- QTest::newRow("s4") << "docker" << "1234abcdef" << "/bin/ls" << "docker://1234abcdef/bin/ls";
- QTest::newRow("s5") << "docker" << "1234" << "/bin/ls" << "docker://1234/bin/ls";
-
- // This is not a proper URL.
- QTest::newRow("s6") << "docker" << "1234" << "somefile" << "docker://1234/./somefile";
-
- // Local Windows paths:
- QTest::newRow("w1") << "" << "" << "C:/data" << "C:/data";
- QTest::newRow("w2") << "" << "" << "C:/" << "C:/";
- QTest::newRow("w3") << "" << "" << "/Global?\?/UNC/host" << "/Global?\?/UNC/host";
- QTest::newRow("w4") << "" << "" << "//server/dir/file" << "//server/dir/file";
-}
-
-void tst_fileutils::fromToString()
-{
- QFETCH(QString, full);
- QFETCH(QString, scheme);
- QFETCH(QString, host);
- QFETCH(QString, path);
-
- FilePath filePath = FilePath::fromString(full);
-
- QCOMPARE(filePath.toString(), full);
-
- QCOMPARE(filePath.scheme(), scheme);
- QCOMPARE(filePath.host(), host);
- QCOMPARE(filePath.path(), path);
-
- FilePath copy = FilePath::fromParts(scheme, host, path);
- QCOMPARE(copy.toString(), full);
-}
-
-void tst_fileutils::comparison()
-{
- QFETCH(QString, left);
- QFETCH(QString, right);
- QFETCH(bool, hostSensitive);
- QFETCH(bool, expected);
-
- HostOsInfo::setOverrideFileNameCaseSensitivity(
- hostSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
-
- FilePath l = FilePath::fromUserInput(left);
- FilePath r = FilePath::fromUserInput(right);
- QCOMPARE(l == r, expected);
-}
-
-void tst_fileutils::comparison_data()
-{
- QTest::addColumn<QString>("left");
- QTest::addColumn<QString>("right");
- QTest::addColumn<bool>("hostSensitive");
- QTest::addColumn<bool>("expected");
-
- QTest::newRow("r1") << "Abc" << "abc" << true << false;
- QTest::newRow("r2") << "Abc" << "abc" << false << true;
- QTest::newRow("r3") << "x://y/Abc" << "x://y/abc" << true << false;
- QTest::newRow("r4") << "x://y/Abc" << "x://y/abc" << false << false;
-
- QTest::newRow("s1") << "abc" << "abc" << true << true;
- QTest::newRow("s2") << "abc" << "abc" << false << true;
- QTest::newRow("s3") << "x://y/abc" << "x://y/abc" << true << true;
- QTest::newRow("s4") << "x://y/abc" << "x://y/abc" << false << true;
-}
-
-void tst_fileutils::linkFromString()
-{
- QFETCH(QString, testFile);
- QFETCH(Utils::FilePath, filePath);
- QFETCH(int, line);
- QFETCH(int, column);
- const Link link = Link::fromString(testFile, true);
- QCOMPARE(link.targetFilePath, filePath);
- QCOMPARE(link.targetLine, line);
- QCOMPARE(link.targetColumn, column);
-}
-
-void tst_fileutils::linkFromString_data()
-{
- QTest::addColumn<QString>("testFile");
- QTest::addColumn<Utils::FilePath>("filePath");
- QTest::addColumn<int>("line");
- QTest::addColumn<int>("column");
-
- QTest::newRow("no-line-no-column") << QString("someFile.txt")
- << FilePath("someFile.txt") << -1 << -1;
- QTest::newRow(": at end") << QString::fromLatin1("someFile.txt:")
- << FilePath("someFile.txt") << 0 << -1;
- QTest::newRow("+ at end") << QString::fromLatin1("someFile.txt+")
- << FilePath("someFile.txt") << 0 << -1;
- QTest::newRow(": for column") << QString::fromLatin1("someFile.txt:10:")
- << FilePath("someFile.txt") << 10 << -1;
- QTest::newRow("+ for column") << QString::fromLatin1("someFile.txt:10+")
- << FilePath("someFile.txt") << 10 << -1;
- QTest::newRow(": and + at end") << QString::fromLatin1("someFile.txt:+")
- << FilePath("someFile.txt") << 0 << -1;
- QTest::newRow("empty line") << QString::fromLatin1("someFile.txt:+10")
- << FilePath("someFile.txt") << 0 << 9;
- QTest::newRow(":line-no-column") << QString::fromLatin1("/some/path/file.txt:42")
- << FilePath("/some/path/file.txt") << 42 << -1;
- QTest::newRow("+line-no-column") << QString::fromLatin1("/some/path/file.txt+42")
- << FilePath("/some/path/file.txt") << 42 << -1;
- QTest::newRow(":line-:column") << QString::fromLatin1("/some/path/file.txt:42:3")
- << FilePath("/some/path/file.txt") << 42 << 2;
- QTest::newRow(":line-+column") << QString::fromLatin1("/some/path/file.txt:42+33")
- << FilePath("/some/path/file.txt") << 42 << 32;
- QTest::newRow("+line-:column") << QString::fromLatin1("/some/path/file.txt+142:30")
- << FilePath("/some/path/file.txt") << 142 << 29;
- QTest::newRow("+line-+column") << QString::fromLatin1("/some/path/file.txt+142+33")
- << FilePath("/some/path/file.txt") << 142 << 32;
- QTest::newRow("( at end") << QString::fromLatin1("/some/path/file.txt(")
- << FilePath("/some/path/file.txt") << -1 << -1;
- QTest::newRow("(42 at end") << QString::fromLatin1("/some/path/file.txt(42")
- << FilePath("/some/path/file.txt") << 42 << -1;
- QTest::newRow("(42) at end") << QString::fromLatin1("/some/path/file.txt(42)")
- << FilePath("/some/path/file.txt") << 42 << -1;
-}
-
-void tst_fileutils::pathAppended()
-{
- QFETCH(QString, left);
- QFETCH(QString, right);
- QFETCH(QString, expected);
-
- const FilePath fleft = FilePath::fromString(left);
- const FilePath fexpected = FilePath::fromString(expected);
- const FilePath result = fleft.pathAppended(right);
-
- QCOMPARE(result, fexpected);
-}
-
-void tst_fileutils::pathAppended_data()
-{
- QTest::addColumn<QString>("left");
- QTest::addColumn<QString>("right");
- QTest::addColumn<QString>("expected");
-
- QTest::newRow("p0") << "" << "" << "";
- QTest::newRow("p1") << "" << "/" << "/";
- QTest::newRow("p2") << "" << "c/" << "c/";
- QTest::newRow("p3") << "" << "/d" << "/d";
- QTest::newRow("p4") << "" << "c/d" << "c/d";
-
- QTest::newRow("r0") << "/" << "" << "/";
- QTest::newRow("r1") << "/" << "/" << "/";
- QTest::newRow("r2") << "/" << "c/" << "/c/";
- QTest::newRow("r3") << "/" << "/d" << "/d";
- QTest::newRow("r4") << "/" << "c/d" << "/c/d";
-
- QTest::newRow("s0") << "/b" << "" << "/b";
- QTest::newRow("s1") << "/b" << "/" << "/b/";
- QTest::newRow("s2") << "/b" << "c/" << "/b/c/";
- QTest::newRow("s3") << "/b" << "/d" << "/b/d";
- QTest::newRow("s4") << "/b" << "c/d" << "/b/c/d";
-
- QTest::newRow("t0") << "a/" << "" << "a/";
- QTest::newRow("t1") << "a/" << "/" << "a/";
- QTest::newRow("t2") << "a/" << "c/" << "a/c/";
- QTest::newRow("t3") << "a/" << "/d" << "a/d";
- QTest::newRow("t4") << "a/" << "c/d" << "a/c/d";
-
- QTest::newRow("u0") << "a/b" << "" << "a/b";
- QTest::newRow("u1") << "a/b" << "/" << "a/b/";
- QTest::newRow("u2") << "a/b" << "c/" << "a/b/c/";
- QTest::newRow("u3") << "a/b" << "/d" << "a/b/d";
- QTest::newRow("u4") << "a/b" << "c/d" << "a/b/c/d";
-
- if (HostOsInfo::isWindowsHost()) {
- QTest::newRow("win-1") << "c:" << "/a/b" << "c:/a/b";
- QTest::newRow("win-2") << "c:/" << "/a/b" << "c:/a/b";
- QTest::newRow("win-3") << "c:/" << "a/b" << "c:/a/b";
- }
-}
-
-void tst_fileutils::resolvePath_data()
-{
- QTest::addColumn<FilePath>("left");
- QTest::addColumn<FilePath>("right");
- QTest::addColumn<FilePath>("expected");
-
- QTest::newRow("empty") << FilePath() << FilePath() << FilePath();
- QTest::newRow("s0") << FilePath("/") << FilePath("b") << FilePath("/b");
- QTest::newRow("s1") << FilePath() << FilePath("b") << FilePath("b");
- QTest::newRow("s2") << FilePath("a") << FilePath() << FilePath("a");
- QTest::newRow("s3") << FilePath("a") << FilePath("b") << FilePath("a/b");
- QTest::newRow("s4") << FilePath("/a") << FilePath("/b") << FilePath("/b");
- QTest::newRow("s5") << FilePath("a") << FilePath("/b") << FilePath("/b");
- QTest::newRow("s6") << FilePath("/a") << FilePath("b") << FilePath("/a/b");
- QTest::newRow("s7") << FilePath("/a") << FilePath(".") << FilePath("/a");
- QTest::newRow("s8") << FilePath("/a") << FilePath("./b") << FilePath("/a/b");
-}
-
-void tst_fileutils::resolvePath()
-{
- QFETCH(FilePath, left);
- QFETCH(FilePath, right);
- QFETCH(FilePath, expected);
-
- const FilePath result = left.resolvePath(right);
-
- QCOMPARE(result, expected);
-}
-
-void tst_fileutils::relativeChildPath_data()
-{
- QTest::addColumn<FilePath>("parent");
- QTest::addColumn<FilePath>("child");
- QTest::addColumn<FilePath>("expected");
-
- QTest::newRow("empty") << FilePath() << FilePath() << FilePath();
-
- QTest::newRow("simple-0") << FilePath("/a") << FilePath("/a/b") << FilePath("b");
- QTest::newRow("simple-1") << FilePath("/a/") << FilePath("/a/b") << FilePath("b");
- QTest::newRow("simple-2") << FilePath("/a") << FilePath("/a/b/c/d/e/f") << FilePath("b/c/d/e/f");
-
- QTest::newRow("not-0") << FilePath("/x") << FilePath("/a/b") << FilePath();
- QTest::newRow("not-1") << FilePath("/a/b/c") << FilePath("/a/b") << FilePath();
-
-}
-
-void tst_fileutils::relativeChildPath()
-{
- QFETCH(FilePath, parent);
- QFETCH(FilePath, child);
- QFETCH(FilePath, expected);
-
- const FilePath result = child.relativeChildPath(parent);
-
- QCOMPARE(result, expected);
-}
+void tst_fileutils::initTestCase() {}
void tst_fileutils::commonPath()
{
@@ -996,133 +65,15 @@ void tst_fileutils::commonPath_data()
const FilePath url6 = FilePath::fromString("http:///./");
QTest::newRow("Zero paths") << FilePaths{} << FilePath();
- QTest::newRow("Single path") << FilePaths{ p1 } << p1;
- QTest::newRow("3 identical paths") << FilePaths{ p1, p1, p1 } << p1;
- QTest::newRow("3 paths, common path") << FilePaths{ p1, p2, p3 } << p1;
- QTest::newRow("3 paths, no common path") << FilePaths{ p1, p2, p4 } << p5;
- QTest::newRow("3 paths, first is part of second") << FilePaths{ p4, p1, p3 } << p5;
- QTest::newRow("Common scheme") << FilePaths{ url1, url3 } << url6;
- QTest::newRow("Different scheme") << FilePaths{ url1, url2 } << FilePath();
- QTest::newRow("Common host") << FilePaths{ url4, url5 } << url4;
- QTest::newRow("Different host") << FilePaths{ url1, url3 } << url6;
-}
-
-void tst_fileutils::asyncLocalCopy()
-{
- const FilePath orig = FilePath::fromString(rootPath).pathAppended("x/y/fileToCopy.txt");
- QVERIFY(orig.exists());
- const FilePath dest = FilePath::fromString(rootPath).pathAppended("x/fileToCopyDest.txt");
- auto afterCopy = [&orig, &dest, this](expected_str<void> result) {
- QVERIFY(result);
- // check existence, size and content
- QVERIFY(dest.exists());
- QCOMPARE(dest.fileSize(), orig.fileSize());
- QCOMPARE(dest.fileContents(), orig.fileContents());
- emit asyncTestDone();
- };
- QSignalSpy spy(this, &tst_fileutils::asyncTestDone);
- orig.asyncCopyFile(afterCopy, dest);
- // we usually have already received the signal, but if it fails wait 3s
- QVERIFY(spy.count() == 1 || spy.wait(3000));
-}
-
-void tst_fileutils::startsWithDriveLetter_data()
-{
- QTest::addColumn<FilePath>("path");
- QTest::addColumn<bool>("expected");
-
- QTest::newRow("empty") << FilePath() << false;
- QTest::newRow("simple-win") << FilePath::fromString("c:/a") << true;
- QTest::newRow("simple-linux") << FilePath::fromString("/c:/a") << false;
- QTest::newRow("relative") << FilePath("a/b") << false;
-}
-
-void tst_fileutils::startsWithDriveLetter()
-{
- QFETCH(FilePath, path);
- QFETCH(bool, expected);
-
- QCOMPARE(path.startsWithDriveLetter(), expected);
-}
-
-void tst_fileutils::onDevice_data()
-{
- QTest::addColumn<FilePath>("path");
- QTest::addColumn<FilePath>("templatePath");
- QTest::addColumn<FilePath>("expected");
-
- QTest::newRow("empty") << FilePath() << FilePath() << FilePath();
- QTest::newRow("same-local") << FilePath("/a/b") << FilePath("/a/b") << FilePath("/a/b");
- QTest::newRow("same-docker") << FilePath("docker://1234/a/b") << FilePath("docker://1234/e") << FilePath("docker://1234/a/b");
-
- QTest::newRow("docker-to-local") << FilePath("docker://1234/a/b") << FilePath("/c/d") << FilePath("/a/b");
- QTest::newRow("local-to-docker") << FilePath("/a/b") << FilePath("docker://1234/c/d") << FilePath("docker://1234/a/b");
-
-}
-
-void tst_fileutils::onDevice()
-{
- QFETCH(FilePath, path);
- QFETCH(FilePath, templatePath);
- QFETCH(FilePath, expected);
-
- QCOMPARE(path.onDevice(templatePath), expected);
-}
-
-void tst_fileutils::stringAppended_data()
-{
- QTest::addColumn<FilePath>("left");
- QTest::addColumn<QString>("right");
- QTest::addColumn<FilePath>("expected");
-
- QTest::newRow("empty") << FilePath() << QString() << FilePath();
- QTest::newRow("empty-left") << FilePath() << "a" << FilePath("a");
- QTest::newRow("empty-right") << FilePath("a") << QString() << FilePath("a");
- QTest::newRow("add-root") << FilePath() << QString("/") << FilePath("/");
- QTest::newRow("add-root-and-more") << FilePath() << QString("/test/blah") << FilePath("/test/blah");
- QTest::newRow("add-extension") << FilePath::fromString("/a") << QString(".txt") << FilePath("/a.txt");
- QTest::newRow("trailing-slash") << FilePath::fromString("/a") << QString("b/") << FilePath("/ab/");
- QTest::newRow("slash-trailing-slash") << FilePath::fromString("/a/") << QString("b/") << FilePath("/a/b/");
-}
-
-void tst_fileutils::stringAppended()
-{
- QFETCH(FilePath, left);
- QFETCH(QString, right);
- QFETCH(FilePath, expected);
-
- const FilePath result = left.stringAppended(right);
-
- QCOMPARE(expected, result);
-}
-
-void tst_fileutils::url()
-{
- QFETCH(QString, url);
- QFETCH(QString, expectedScheme);
- QFETCH(QString, expectedHost);
- QFETCH(QString, expectedPath);
-
- const FilePath result = FilePath::fromString(url);
- QCOMPARE(result.scheme(), expectedScheme);
- QCOMPARE(result.host(), expectedHost);
- QCOMPARE(result.path(), expectedPath);
-}
-
-void tst_fileutils::url_data()
-{
- QTest::addColumn<QString>("url");
- QTest::addColumn<QString>("expectedScheme");
- QTest::addColumn<QString>("expectedHost");
- QTest::addColumn<QString>("expectedPath");
- QTest::newRow("empty") << QString() << QString() << QString() << QString();
- QTest::newRow("simple-file") << QString("file:///a/b") << QString("file") << QString() << QString("/a/b");
- QTest::newRow("simple-file-root") << QString("file:///") << QString("file") << QString() << QString("/");
- QTest::newRow("simple-docker") << QString("docker://1234/a/b") << QString("docker") << QString("1234") << QString("/a/b");
- QTest::newRow("simple-ssh") << QString("ssh://user@host/a/b") << QString("ssh") << QString("user@host") << QString("/a/b");
- QTest::newRow("simple-ssh-with-port") << QString("ssh://user@host:1234/a/b") << QString("ssh") << QString("user@host:1234") << QString("/a/b");
- QTest::newRow("http-qt.io") << QString("http://qt.io") << QString("http") << QString("qt.io") << QString();
- QTest::newRow("http-qt.io-index.html") << QString("http://qt.io/index.html") << QString("http") << QString("qt.io") << QString("/index.html");
+ QTest::newRow("Single path") << FilePaths{p1} << p1;
+ QTest::newRow("3 identical paths") << FilePaths{p1, p1, p1} << p1;
+ QTest::newRow("3 paths, common path") << FilePaths{p1, p2, p3} << p1;
+ QTest::newRow("3 paths, no common path") << FilePaths{p1, p2, p4} << p5;
+ QTest::newRow("3 paths, first is part of second") << FilePaths{p4, p1, p3} << p5;
+ QTest::newRow("Common scheme") << FilePaths{url1, url3} << url6;
+ QTest::newRow("Different scheme") << FilePaths{url1, url2} << FilePath();
+ QTest::newRow("Common host") << FilePaths{url4, url5} << url4;
+ QTest::newRow("Different host") << FilePaths{url1, url3} << url6;
}
void tst_fileutils::bytesAvailableFromDF_data()
@@ -1131,13 +82,33 @@ void tst_fileutils::bytesAvailableFromDF_data()
QTest::addColumn<qint64>("expected");
QTest::newRow("empty") << QByteArray("") << qint64(-1);
- QTest::newRow("mac") << QByteArray("Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on\n/dev/disk3s5 971350180 610014564 342672532 65% 4246780 3426725320 0% /System/Volumes/Data\n") << qint64(342672532);
- QTest::newRow("alpine") << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\noverlay 569466448 163526072 376983360 30% /\n") << qint64(376983360);
- QTest::newRow("alpine-no-trailing-br") << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\noverlay 569466448 163526072 376983360 30% /") << qint64(376983360);
- QTest::newRow("alpine-missing-line") << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\n") << qint64(-1);
- QTest::newRow("wrong-header") << QByteArray("Filesystem 1K-blocks Used avail Use% Mounted on\noverlay 569466448 163526072 376983360 30% /\n") << qint64(-1);
- QTest::newRow("not-enough-fields") << QByteArray("Filesystem 1K-blocks Used avail Use% Mounted on\noverlay 569466448\n") << qint64(-1);
- QTest::newRow("not-enough-header-fields") << QByteArray("Filesystem 1K-blocks Used \noverlay 569466448 163526072 376983360 30% /\n") << qint64(-1);
+ QTest::newRow("mac") << QByteArray(
+ "Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted "
+ "on\n/dev/disk3s5 971350180 610014564 342672532 65% 4246780 3426725320 0% "
+ "/System/Volumes/Data\n")
+ << qint64(342672532);
+ QTest::newRow("alpine") << QByteArray(
+ "Filesystem 1K-blocks Used Available Use% Mounted on\noverlay "
+ "569466448 163526072 376983360 30% /\n")
+ << qint64(376983360);
+ QTest::newRow("alpine-no-trailing-br")
+ << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\noverlay "
+ " 569466448 163526072 376983360 30% /")
+ << qint64(376983360);
+ QTest::newRow("alpine-missing-line")
+ << QByteArray("Filesystem 1K-blocks Used Available Use% Mounted on\n")
+ << qint64(-1);
+ QTest::newRow("wrong-header") << QByteArray(
+ "Filesystem 1K-blocks Used avail Use% Mounted on\noverlay "
+ "569466448 163526072 376983360 30% /\n")
+ << qint64(-1);
+ QTest::newRow("not-enough-fields") << QByteArray(
+ "Filesystem 1K-blocks Used avail Use% Mounted on\noverlay 569466448\n")
+ << qint64(-1);
+ QTest::newRow("not-enough-header-fields")
+ << QByteArray("Filesystem 1K-blocks Used \noverlay 569466448 "
+ "163526072 376983360 30% /\n")
+ << qint64(-1);
}
void tst_fileutils::bytesAvailableFromDF()
@@ -1152,194 +123,6 @@ void tst_fileutils::bytesAvailableFromDF()
QCOMPARE(result, expected);
}
-void tst_fileutils::cleanPath_data()
-{
- QTest::addColumn<QString>("path");
- QTest::addColumn<QString>("expected");
-
- QTest::newRow("data0") << "/Users/sam/troll/qt4.0//.." << "/Users/sam/troll";
- QTest::newRow("data1") << "/Users/sam////troll/qt4.0//.." << "/Users/sam/troll";
- QTest::newRow("data2") << "/" << "/";
- QTest::newRow("data2-up") << "/path/.." << "/";
- QTest::newRow("data2-above-root") << "/.." << "/..";
- QTest::newRow("data3") << QDir::cleanPath("../.") << "..";
- QTest::newRow("data4") << QDir::cleanPath("../..") << "../..";
- QTest::newRow("data5") << "d:\\a\\bc\\def\\.." << "d:/a/bc"; // QDir/Linux had: "d:\\a\\bc\\def\\.."
- QTest::newRow("data6") << "d:\\a\\bc\\def\\../../.." << "d:/"; // QDir/Linux had: ".."
- QTest::newRow("data7") << ".//file1.txt" << "file1.txt";
- QTest::newRow("data8") << "/foo/bar/..//file1.txt" << "/foo/file1.txt";
- QTest::newRow("data9") << "//" << "//"; // QDir had: "/"
- QTest::newRow("data10w") << "c:\\" << "c:/";
- QTest::newRow("data10l") << "/:/" << "/:";
- QTest::newRow("data11") << "//foo//bar" << "//foo/bar"; // QDir/Win had: "//foo/bar"
- QTest::newRow("data12") << "ab/a/" << "ab/a"; // Path item with length of 2
- QTest::newRow("data13w") << "c:/" << "c:/";
- QTest::newRow("data13w2") << "c:\\" << "c:/";
- //QTest::newRow("data13l") << "c://" << "c:";
-
-// QTest::newRow("data14") << "c://foo" << "c:/foo";
- QTest::newRow("data15") << "//c:/foo" << "//c:/foo"; // QDir/Lin had: "/c:/foo";
- QTest::newRow("drive-up") << "A:/path/.." << "A:/";
- QTest::newRow("drive-above-root") << "A:/.." << "A:/..";
- QTest::newRow("unc-server-up") << "//server/path/.." << "//server/";
- QTest::newRow("unc-server-above-root") << "//server/.." << "//server/..";
-
- QTest::newRow("longpath") << "\\\\?\\d:\\" << "d:/";
- QTest::newRow("longpath-slash") << "//?/d:/" << "d:/";
- QTest::newRow("longpath-mixed-slashes") << "//?/d:\\" << "d:/";
- QTest::newRow("longpath-mixed-slashes-2") << "\\\\?\\d:/" << "d:/";
-
- QTest::newRow("unc-network-share") << "\\\\?\\UNC\\localhost\\c$\\tmp.txt"
- << "//localhost/c$/tmp.txt";
- QTest::newRow("unc-network-share-slash") << "//?/UNC/localhost/c$/tmp.txt"
- << "//localhost/c$/tmp.txt";
- QTest::newRow("unc-network-share-mixed-slashes") << "//?/UNC/localhost\\c$\\tmp.txt"
- << "//localhost/c$/tmp.txt";
- QTest::newRow("unc-network-share-mixed-slashes-2") << "\\\\?\\UNC\\localhost/c$/tmp.txt"
- << "//localhost/c$/tmp.txt";
-
- QTest::newRow("QTBUG-23892_0") << "foo/.." << ".";
- QTest::newRow("QTBUG-23892_1") << "foo/../" << ".";
-
- QTest::newRow("QTBUG-3472_0") << "/foo/./bar" << "/foo/bar";
- QTest::newRow("QTBUG-3472_1") << "./foo/.." << ".";
- QTest::newRow("QTBUG-3472_2") << "./foo/../" << ".";
-
- QTest::newRow("resource0") << ":/prefix/foo.bar" << ":/prefix/foo.bar";
- QTest::newRow("resource1") << ":/prefix/..//prefix/foo.bar" << ":/prefix/foo.bar";
-
- QTest::newRow("ssh") << "ssh://host/prefix/../foo.bar" << "ssh://host/foo.bar";
- QTest::newRow("ssh2") << "ssh://host/../foo.bar" << "ssh://host/../foo.bar";
-}
-
-void tst_fileutils::cleanPath()
-{
- QFETCH(QString, path);
- QFETCH(QString, expected);
- QString cleaned = doCleanPath(path);
- QCOMPARE(cleaned, expected);
-}
-
-void tst_fileutils::isSameFile_data()
-{
- QTest::addColumn<FilePath>("left");
- QTest::addColumn<FilePath>("right");
- QTest::addColumn<bool>("shouldBeEqual");
-
- QTest::addRow("/==/")
- << FilePath::fromString("/") << FilePath::fromString("/") << true;
- QTest::addRow("/!=tmp")
- << FilePath::fromString("/") << FilePath::fromString(tempDir.path()) << false;
-
-
- QDir dir(tempDir.path());
- touch(dir, "target-file", false);
-
- QFile file(dir.absoluteFilePath("target-file"));
- if (file.link(dir.absoluteFilePath("source-file"))) {
- QTest::addRow("real==link")
- << FilePath::fromString(file.fileName())
- << FilePath::fromString(dir.absoluteFilePath("target-file"))
- << true;
- }
-
- QTest::addRow("/!=non-existing")
- << FilePath::fromString("/") << FilePath::fromString("/this-path/does-not-exist") << false;
-
- QTest::addRow("two-devices") << FilePath::fromString(
- "docker://boot2qt-raspberrypi4-64:6.5.0/opt/toolchain/sysroots/aarch64-pokysdk-linux/usr/"
- "bin/aarch64-poky-linux/aarch64-poky-linux-g++")
- << FilePath::fromString("docker://qt-linux:6/usr/bin/g++")
- << false;
-}
-
-void tst_fileutils::isSameFile()
-{
- QFETCH(FilePath, left);
- QFETCH(FilePath, right);
- QFETCH(bool, shouldBeEqual);
-
- QCOMPARE(left.isSameFile(right), shouldBeEqual);
-}
-
-void tst_fileutils::hostSpecialChars_data()
-{
- QTest::addColumn<QString>("scheme");
- QTest::addColumn<QString>("host");
- QTest::addColumn<QString>("path");
- QTest::addColumn<FilePath>("expected");
-
- QTest::addRow("slash-in-host") << "device" << "host/name" << "/" << FilePath::fromString("device://host%2fname/");
- QTest::addRow("percent-in-host") << "device" << "host%name" << "/" << FilePath::fromString("device://host%25name/");
- QTest::addRow("percent-and-slash-in-host") << "device" << "host/%name" << "/" << FilePath::fromString("device://host%2f%25name/");
- QTest::addRow("qtc-dev-slash-in-host-linux") << "device" << "host/name" << "/" << FilePath::fromString("/__qtc_devices__/device/host%2fname/");
- QTest::addRow("qtc-dev-slash-in-host-windows") << "device" << "host/name" << "/" << FilePath::fromString("c:/__qtc_devices__/device/host%2fname/");
-
-}
-
-void tst_fileutils::hostSpecialChars()
-{
- QFETCH(QString, scheme);
- QFETCH(QString, host);
- QFETCH(QString, path);
- QFETCH(FilePath, expected);
-
- FilePath fp;
- fp.setParts(scheme, host, path);
-
- // Check that setParts and fromString give the same result
- QCOMPARE(fp, expected);
- QCOMPARE(fp.host(), expected.host());
- QCOMPARE(fp.host(), host);
- QCOMPARE(expected.host(), host);
-
- QString toStringExpected = expected.toString();
- QString toStringActual = fp.toString();
-
- // Check that toString gives the same result
- QCOMPARE(toStringActual, toStringExpected);
-
- // Check that fromString => toString => fromString gives the same result
- FilePath toFromExpected = FilePath::fromString(expected.toString());
- QCOMPARE(toFromExpected, expected);
- QCOMPARE(toFromExpected, fp);
-
- // Check that setParts => toString => fromString gives the same result
- FilePath toFromActual = FilePath::fromString(fp.toString());
- QCOMPARE(toFromActual, fp);
- QCOMPARE(toFromExpected, expected);
-}
-
-void tst_fileutils::tmp_data()
-{
- QTest::addColumn<QString>("templatepath");
- QTest::addColumn<bool>("expected");
-
- QTest::addRow("empty") << "" << true;
- QTest::addRow("no-template") << "foo" << true;
- QTest::addRow("realtive-template") << "my-file-XXXXXXXX" << true;
- QTest::addRow("absolute-template") << QDir::tempPath() + "/my-file-XXXXXXXX" << true;
- QTest::addRow("non-existing-dir") << "/this/path/does/not/exist/my-file-XXXXXXXX" << false;
-
- QTest::addRow("on-device") << "device://test/./my-file-XXXXXXXX" << true;
-}
-
-void tst_fileutils::tmp()
-{
- QFETCH(QString, templatepath);
- QFETCH(bool, expected);
-
- FilePath fp = FilePath::fromString(templatepath);
-
- const auto result = fp.createTempFile();
- QCOMPARE(result.has_value(), expected);
-
- if (result.has_value()) {
- QVERIFY(result->exists());
- QVERIFY(result->removeFile());
- }
-}
-
void tst_fileutils::filePathInfoFromTriple_data()
{
QTest::addColumn<QString>("statoutput");
@@ -1389,7 +172,7 @@ void tst_fileutils::filePathInfoFromTriple()
QFETCH(QString, statoutput);
QFETCH(FilePathInfo, expected);
- const FilePathInfo result = FileUtils::filePathInfoFromTriple(statoutput);
+ const FilePathInfo result = FileUtils::filePathInfoFromTriple(statoutput, 16);
QCOMPARE(result, expected);
}
diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp
index 8b0cdfc1b4..d516bda8c2 100644
--- a/tests/auto/utils/fsengine/tst_fsengine.cpp
+++ b/tests/auto/utils/fsengine/tst_fsengine.cpp
@@ -31,6 +31,8 @@ private slots:
void testBrokenWindowsPath();
void testRead();
void testWrite();
+ void testRootFromDotDot();
+ void testDirtyPaths();
private:
QString makeTestPath(QString path, bool asUrl = false);
@@ -93,6 +95,10 @@ void tst_fsengine::testRootPathContainsFakeDir()
const QStringList schemeList = schemes.entryList();
QVERIFY(schemeList.contains("device"));
+ QDir devices(FilePath::specialDeviceRootPath());
+ const QStringList deviceList = devices.entryList();
+ QVERIFY(deviceList.contains("test"));
+
QDir deviceRoot(FilePath::specialDeviceRootPath() + "/test" + startWithSlash(QDir::rootPath()));
const QStringList deviceRootList = deviceRoot.entryList();
QVERIFY(!deviceRootList.isEmpty());
@@ -129,7 +135,8 @@ QString tst_fsengine::makeTestPath(QString path, bool asUrl)
return QString("device://test%1/tst_fsengine/%2").arg(tempFolder, path);
return QString(FilePath::specialDeviceRootPath() + "/test%1/tst_fsengine/%2")
- .arg(startWithSlash(tempFolder), path);
+ .arg(startWithSlash(tempFolder))
+ .arg(path);
}
void tst_fsengine::testListDir()
@@ -209,5 +216,49 @@ void tst_fsengine::testWrite()
QCOMPARE(f.readAll(), data);
}
+void tst_fsengine::testRootFromDotDot()
+{
+ const QString path = QDir::rootPath() + "some-folder/..";
+ QFileInfo fInfo(path);
+
+ QCOMPARE(fInfo.fileName(), QString(".."));
+
+ QDir dRoot(path);
+ const auto dRootEntryList = dRoot.entryList();
+ QVERIFY(dRootEntryList.contains(FilePath::specialRootName()));
+
+ QFileInfo fInfo2(FilePath::specialRootPath() + "/xyz/..");
+ QCOMPARE(fInfo2.fileName(), "..");
+
+ QDir schemesWithDotDot(FilePath::specialRootPath() + "/xyz/..");
+ const QStringList schemeWithDotDotList = schemesWithDotDot.entryList();
+ QVERIFY(schemeWithDotDotList.contains("device"));
+
+ QFileInfo fInfo3(FilePath::specialDeviceRootPath() + "/xyz/..");
+ QCOMPARE(fInfo3.fileName(), "..");
+
+ QDir devicesWithDotDot(FilePath::specialDeviceRootPath() + "/test/..");
+ const QStringList deviceListWithDotDot = devicesWithDotDot.entryList();
+ QVERIFY(deviceListWithDotDot.contains("test"));
+
+ QFileInfo fInfo4(FilePath::specialDeviceRootPath() + "/test/tmp/..");
+ QCOMPARE(fInfo4.fileName(), "..");
+}
+
+void tst_fsengine::testDirtyPaths()
+{
+ // "//__qtc_devices"
+ QVERIFY(QFileInfo("/" + FilePath::specialRootPath()).exists());
+
+ // "///__qtc_devices/device"
+ QVERIFY(QFileInfo("//" + FilePath::specialDeviceRootPath()).exists());
+
+ // "////__qtc_devices/device////test"
+ QVERIFY(QFileInfo("///" + FilePath::specialDeviceRootPath() + "////test").exists());
+
+ // "/////__qtc_devices/device/test/..."
+ QVERIFY(QFileInfo("////" + makeTestPath("")).exists());
+}
+
QTEST_GUILESS_MAIN(tst_fsengine)
#include "tst_fsengine.moc"
diff --git a/tests/auto/utils/mathutils/tst_mathutils.cpp b/tests/auto/utils/mathutils/tst_mathutils.cpp
index b817f15743..a29f908748 100644
--- a/tests/auto/utils/mathutils/tst_mathutils.cpp
+++ b/tests/auto/utils/mathutils/tst_mathutils.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "utils/mathutils.h"
+#include <utils/mathutils.h>
#include <QtTest>
diff --git a/tests/auto/utils/qtcprocess/CMakeLists.txt b/tests/auto/utils/process/CMakeLists.txt
index 51720bbeb5..29bdf61451 100644
--- a/tests/auto/utils/qtcprocess/CMakeLists.txt
+++ b/tests/auto/utils/process/CMakeLists.txt
@@ -3,11 +3,11 @@ add_subdirectory(processtestapp)
file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}")
-add_qtc_test(tst_qtcprocess
+add_qtc_test(tst_process
DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\""
"PROCESS_TESTAPP=\"${CMAKE_CURRENT_BINARY_DIR}/processtestapp\""
DEPENDS Utils app_version
- SOURCES tst_qtcprocess.cpp
+ SOURCES tst_process.cpp
processtestapp/processtestapp.h
processtestapp/processtestapp.cpp
)
diff --git a/tests/auto/utils/qtcprocess/qtcprocess.qbs b/tests/auto/utils/process/process.qbs
index cc37a0124f..0ff6e929fc 100644
--- a/tests/auto/utils/qtcprocess/qtcprocess.qbs
+++ b/tests/auto/utils/process/process.qbs
@@ -2,7 +2,7 @@ import qbs.FileInfo
Project {
QtcAutotest {
- name: "QtcProcess autotest"
+ name: "Process autotest"
Depends { name: "Utils" }
Depends { name: "app_version_header" }
@@ -10,7 +10,7 @@ Project {
files: [
"processtestapp/processtestapp.cpp",
"processtestapp/processtestapp.h",
- "tst_qtcprocess.cpp",
+ "tst_process.cpp",
]
cpp.defines: {
var defines = base;
diff --git a/tests/auto/utils/qtcprocess/processtestapp/CMakeLists.txt b/tests/auto/utils/process/processtestapp/CMakeLists.txt
index 7372cda14a..7372cda14a 100644
--- a/tests/auto/utils/qtcprocess/processtestapp/CMakeLists.txt
+++ b/tests/auto/utils/process/processtestapp/CMakeLists.txt
diff --git a/tests/auto/utils/qtcprocess/processtestapp/main.cpp b/tests/auto/utils/process/processtestapp/main.cpp
index 6851a3597c..91952c2528 100644
--- a/tests/auto/utils/qtcprocess/processtestapp/main.cpp
+++ b/tests/auto/utils/process/processtestapp/main.cpp
@@ -6,7 +6,7 @@
#include <app/app_version.h>
#include <utils/launcherinterface.h>
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <utils/temporarydirectory.h>
#include <QCoreApplication>
diff --git a/tests/auto/utils/qtcprocess/processtestapp/processtestapp.cpp b/tests/auto/utils/process/processtestapp/processtestapp.cpp
index 0884ab44a7..3905d0a22b 100644
--- a/tests/auto/utils/qtcprocess/processtestapp/processtestapp.cpp
+++ b/tests/auto/utils/process/processtestapp/processtestapp.cpp
@@ -3,7 +3,7 @@
#include "processtestapp.h"
-#include <utils/qtcprocess.h>
+#include <utils/process.h>
#include <QCoreApplication>
#include <QDebug>
@@ -77,7 +77,7 @@ SubProcessConfig::SubProcessConfig(const char *envVar, const QString &envVal)
{
}
-void SubProcessConfig::setupSubProcess(QtcProcess *subProcess) const
+void SubProcessConfig::setupSubProcess(Process *subProcess) const
{
subProcess->setEnvironment(m_environment);
const FilePath filePath = FilePath::fromString(s_pathToProcessTestApp
@@ -148,7 +148,7 @@ int ProcessTestApp::ChannelForwarding::main()
qunsetenv(envVar());
SubProcessConfig subConfig(StandardOutputAndErrorWriter::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.setProcessChannelMode(channelMode);
@@ -206,7 +206,7 @@ int ProcessTestApp::RecursiveCrashingProcess::main()
return 1;
}
SubProcessConfig subConfig(envVar(), QString::number(currentDepth - 1));
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
process.waitForFinished();
@@ -249,7 +249,7 @@ int ProcessTestApp::RecursiveBlockingProcess::main()
}
}
SubProcessConfig subConfig(envVar(), QString::number(currentDepth - 1));
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start();
diff --git a/tests/auto/utils/qtcprocess/processtestapp/processtestapp.h b/tests/auto/utils/process/processtestapp/processtestapp.h
index d529ac8265..adc0ebb9c8 100644
--- a/tests/auto/utils/qtcprocess/processtestapp/processtestapp.h
+++ b/tests/auto/utils/process/processtestapp/processtestapp.h
@@ -11,7 +11,7 @@ QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
-namespace Utils { class QtcProcess; }
+namespace Utils { class Process; }
#define SUB_PROCESS(SubProcessClass)\
class SubProcessClass\
@@ -35,7 +35,7 @@ public:
static void invokeSubProcess();
- // Many tests inside tst_qtcprocess need to start a new subprocess with custom code.
+ // Many tests inside tst_Process need to start a new subprocess with custom code.
// In order to simplify things we produce just one separate executable - processtestapp.
// We embed all our custom subprocesses in processtestapp and enclose them in separate
// classes. We select desired process to run by setting the relevant environment variable.
@@ -72,7 +72,7 @@ class SubProcessConfig
{
public:
SubProcessConfig(const char *envVar, const QString &envVal);
- void setupSubProcess(Utils::QtcProcess *subProcess) const;
+ void setupSubProcess(Utils::Process *subProcess) const;
void setupSubProcess(QProcess *subProcess) const;
static void setPathToProcessTestApp(const QString &path);
diff --git a/tests/auto/utils/qtcprocess/processtestapp/processtestapp.qbs b/tests/auto/utils/process/processtestapp/processtestapp.qbs
index d10bc8763a..d10bc8763a 100644
--- a/tests/auto/utils/qtcprocess/processtestapp/processtestapp.qbs
+++ b/tests/auto/utils/process/processtestapp/processtestapp.qbs
diff --git a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp b/tests/auto/utils/process/tst_process.cpp
index a26924089e..589adbb6fa 100644
--- a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
+++ b/tests/auto/utils/process/tst_process.cpp
@@ -8,10 +8,10 @@
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/launcherinterface.h>
+#include <utils/process.h>
#include <utils/processinfo.h>
#include <utils/processinterface.h>
#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
#include <utils/singleton.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
@@ -87,13 +87,38 @@ private:
static constexpr char s_skipTerminateOnWindows[] =
"Windows implementation of this test is lacking handling of WM_CLOSE message.";
-class tst_QtcProcess : public QObject
+class tst_Process : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
+ void testEnv()
+ {
+ if (HostOsInfo::isWindowsHost())
+ QSKIP("Skipping env test on Windows");
+
+ QProcess qproc;
+ FilePath envPath = Environment::systemEnvironment().searchInPath("env");
+
+ qproc.setProgram(envPath.nativePath());
+ qproc.start();
+ qproc.waitForFinished();
+ QByteArray qoutput = qproc.readAllStandardOutput() + qproc.readAllStandardError();
+ qDebug() << "QProcess output:" << qoutput;
+ QCOMPARE(qproc.exitCode(), 0);
+
+ Process proc;
+ proc.setCommand({envPath, {}});
+ proc.runBlocking();
+ QCOMPARE(proc.exitCode(), 0);
+ const QByteArray output = proc.readAllRawStandardOutput() + proc.readAllRawStandardError();
+ qDebug() << "Process output:" << output;
+
+ QCOMPARE(output.size() > 0, qoutput.size() > 0);
+ }
+
void multiRead();
void splitArgs_data();
@@ -135,6 +160,7 @@ private slots:
void quitBlockingProcess_data();
void quitBlockingProcess();
void tarPipe();
+ void stdinToShell();
void cleanupTestCase();
@@ -152,7 +178,7 @@ private:
MessageHandler *msgHandler = nullptr;
};
-void tst_QtcProcess::initTestCase()
+void tst_Process::initTestCase()
{
msgHandler = new MessageHandler;
Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/"
@@ -203,7 +229,7 @@ void tst_QtcProcess::initTestCase()
mxUnix.insert("z", "");
}
-void tst_QtcProcess::cleanupTestCase()
+void tst_Process::cleanupTestCase()
{
Utils::Singleton::deleteAll();
const int destroyCount = msgHandler->destroyCount();
@@ -217,13 +243,13 @@ Q_DECLARE_METATYPE(ProcessArgs::SplitError)
Q_DECLARE_METATYPE(Utils::OsType)
Q_DECLARE_METATYPE(Utils::ProcessResult)
-void tst_QtcProcess::multiRead()
+void tst_Process::multiRead()
{
if (HostOsInfo::isWindowsHost())
QSKIP("This test uses /bin/sh.");
QByteArray buffer;
- QtcProcess process;
+ Process process;
process.setCommand({"/bin/sh", {}});
process.setProcessChannelMode(QProcess::SeparateChannels);
@@ -245,7 +271,7 @@ void tst_QtcProcess::multiRead()
QCOMPARE(buffer, QByteArray("you\n"));
}
-void tst_QtcProcess::splitArgs_data()
+void tst_Process::splitArgs_data()
{
QTest::addColumn<QString>("in");
QTest::addColumn<QString>("out");
@@ -302,7 +328,7 @@ void tst_QtcProcess::splitArgs_data()
}
}
-void tst_QtcProcess::splitArgs()
+void tst_Process::splitArgs()
{
QFETCH(QString, in);
QFETCH(QString, out);
@@ -316,7 +342,7 @@ void tst_QtcProcess::splitArgs()
QCOMPARE(outstr, out);
}
-void tst_QtcProcess::prepareArgs_data()
+void tst_Process::prepareArgs_data()
{
QTest::addColumn<QString>("in");
QTest::addColumn<QString>("out");
@@ -370,7 +396,7 @@ void tst_QtcProcess::prepareArgs_data()
}
}
-void tst_QtcProcess::prepareArgs()
+void tst_Process::prepareArgs()
{
QFETCH(QString, in);
QFETCH(QString, out);
@@ -386,7 +412,7 @@ void tst_QtcProcess::prepareArgs()
QCOMPARE(outstr, out);
}
-void tst_QtcProcess::prepareArgsEnv_data()
+void tst_Process::prepareArgsEnv_data()
{
QTest::addColumn<QString>("in");
QTest::addColumn<QString>("out");
@@ -460,7 +486,7 @@ void tst_QtcProcess::prepareArgsEnv_data()
}
}
-void tst_QtcProcess::prepareArgsEnv()
+void tst_Process::prepareArgsEnv()
{
QFETCH(QString, in);
QFETCH(QString, out);
@@ -476,7 +502,7 @@ void tst_QtcProcess::prepareArgsEnv()
QCOMPARE(outstr, out);
}
-void tst_QtcProcess::expandMacros_data()
+void tst_Process::expandMacros_data()
{
QTest::addColumn<QString>("in");
@@ -698,11 +724,11 @@ void tst_QtcProcess::expandMacros_data()
title = vals[i].in;
} else {
char buf[80];
- sprintf(buf, "%s: %s", title, vals[i].in);
+ snprintf(buf, 80, "%s: %s", title, vals[i].in);
QTest::newRow(buf) << QString::fromLatin1(vals[i].in)
<< QString::fromLatin1(vals[i].out)
<< vals[i].os;
- sprintf(buf, "padded %s: %s", title, vals[i].in);
+ snprintf(buf, 80, "padded %s: %s", title, vals[i].in);
QTest::newRow(buf) << QString(sp + QString::fromLatin1(vals[i].in) + sp)
<< QString(sp + QString::fromLatin1(vals[i].out) + sp)
<< vals[i].os;
@@ -710,7 +736,7 @@ void tst_QtcProcess::expandMacros_data()
}
}
-void tst_QtcProcess::expandMacros()
+void tst_Process::expandMacros()
{
QFETCH(QString, in);
QFETCH(QString, out);
@@ -723,7 +749,7 @@ void tst_QtcProcess::expandMacros()
QCOMPARE(in, out);
}
-void tst_QtcProcess::iterations_data()
+void tst_Process::iterations_data()
{
QTest::addColumn<QString>("in");
QTest::addColumn<QString>("out");
@@ -778,7 +804,7 @@ void tst_QtcProcess::iterations_data()
<< vals[i].os;
}
-void tst_QtcProcess::iterations()
+void tst_Process::iterations()
{
QFETCH(QString, in);
QFETCH(QString, out);
@@ -794,7 +820,7 @@ void tst_QtcProcess::iterations()
QCOMPARE(outstr, out);
}
-void tst_QtcProcess::iteratorEditsHelper(OsType osType)
+void tst_Process::iteratorEditsHelper(OsType osType)
{
QString in1 = "one two three", in2 = in1, in3 = in1, in4 = in1, in5 = in1;
@@ -845,17 +871,17 @@ void tst_QtcProcess::iteratorEditsHelper(OsType osType)
QCOMPARE(in5, QString::fromLatin1("one two"));
}
-void tst_QtcProcess::iteratorEditsWindows()
+void tst_Process::iteratorEditsWindows()
{
iteratorEditsHelper(OsTypeWindows);
}
-void tst_QtcProcess::iteratorEditsLinux()
+void tst_Process::iteratorEditsLinux()
{
iteratorEditsHelper(OsTypeLinux);
}
-void tst_QtcProcess::exitCode_data()
+void tst_Process::exitCode_data()
{
QTest::addColumn<int>("exitCode");
@@ -869,13 +895,13 @@ void tst_QtcProcess::exitCode_data()
QTest::newRow(exitCode) << QString::fromLatin1(exitCode).toInt();
}
-void tst_QtcProcess::exitCode()
+void tst_Process::exitCode()
{
QFETCH(int, exitCode);
SubProcessConfig subConfig(ProcessTestApp::ExitCode::envVar(), QString::number(exitCode));
{
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
const bool finished = process.waitForFinished();
@@ -885,7 +911,7 @@ void tst_QtcProcess::exitCode()
QCOMPARE(process.exitCode() == 0, process.result() == ProcessResult::FinishedWithSuccess);
}
{
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.runBlocking();
@@ -894,7 +920,7 @@ void tst_QtcProcess::exitCode()
}
}
-void tst_QtcProcess::runBlockingStdOut_data()
+void tst_Process::runBlockingStdOut_data()
{
QTest::addColumn<bool>("withEndl");
QTest::addColumn<int>("timeOutS");
@@ -915,14 +941,14 @@ void tst_QtcProcess::runBlockingStdOut_data()
<< false << 20 << ProcessResult::FinishedWithSuccess;
}
-void tst_QtcProcess::runBlockingStdOut()
+void tst_Process::runBlockingStdOut()
{
QFETCH(bool, withEndl);
QFETCH(int, timeOutS);
QFETCH(ProcessResult, expectedResult);
SubProcessConfig subConfig(ProcessTestApp::RunBlockingStdOut::envVar(), withEndl ? "true" : "false");
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.setTimeoutS(timeOutS);
@@ -935,31 +961,31 @@ void tst_QtcProcess::runBlockingStdOut()
});
process.runBlocking();
- // See also QTCREATORBUG-25667 for why it is a bad idea to use QtcProcess::runBlocking
+ // See also QTCREATORBUG-25667 for why it is a bad idea to use Process::runBlocking
// with interactive cli tools.
QCOMPARE(process.result(), expectedResult);
QVERIFY2(readLastLine, "Last line was read.");
}
-void tst_QtcProcess::runBlockingSignal_data()
+void tst_Process::runBlockingSignal_data()
{
runBlockingStdOut_data();
}
-void tst_QtcProcess::runBlockingSignal()
+void tst_Process::runBlockingSignal()
{
QFETCH(bool, withEndl);
QFETCH(int, timeOutS);
QFETCH(ProcessResult, expectedResult);
SubProcessConfig subConfig(ProcessTestApp::RunBlockingStdOut::envVar(), withEndl ? "true" : "false");
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.setTimeoutS(timeOutS);
bool readLastLine = false;
process.setTextChannelMode(Channel::Output, TextChannelMode::MultiLine);
- connect(&process, &QtcProcess::textOnStandardOutput,
+ connect(&process, &Process::textOnStandardOutput,
this, [&readLastLine, &process](const QString &out) {
if (out.startsWith(s_runBlockingStdOutSubProcessMagicWord)) {
readLastLine = true;
@@ -968,16 +994,16 @@ void tst_QtcProcess::runBlockingSignal()
});
process.runBlocking();
- // See also QTCREATORBUG-25667 for why it is a bad idea to use QtcProcess::runBlocking
+ // See also QTCREATORBUG-25667 for why it is a bad idea to use Process::runBlocking
// with interactive cli tools.
QCOMPARE(process.result(), expectedResult);
QVERIFY2(readLastLine, "Last line was read.");
}
-void tst_QtcProcess::lineCallback()
+void tst_Process::lineCallback()
{
SubProcessConfig subConfig(ProcessTestApp::LineCallback::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
const QStringList lines = QString(s_lineCallbackData).split('|');
@@ -997,16 +1023,16 @@ void tst_QtcProcess::lineCallback()
QCOMPARE(lineNumber, lines.size());
}
-void tst_QtcProcess::lineSignal()
+void tst_Process::lineSignal()
{
SubProcessConfig subConfig(ProcessTestApp::LineCallback::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
const QStringList lines = QString(s_lineCallbackData).split('|');
int lineNumber = 0;
process.setTextChannelMode(Channel::Error, TextChannelMode::SingleLine);
- connect(&process, &QtcProcess::textOnStandardError,
+ connect(&process, &Process::textOnStandardError,
this, [lines, &lineNumber](const QString &actual) {
QString expected = lines.at(lineNumber);
expected.replace("\r\n", "\n");
@@ -1022,15 +1048,15 @@ void tst_QtcProcess::lineSignal()
QCOMPARE(lineNumber, lines.size());
}
-void tst_QtcProcess::waitForStartedAfterStarted()
+void tst_Process::waitForStartedAfterStarted()
{
SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
bool started = false;
bool waitForStartedResult = false;
- connect(&process, &QtcProcess::started, this, [&] {
+ connect(&process, &Process::started, this, [&] {
started = true;
waitForStartedResult = process.waitForStarted();
});
@@ -1043,7 +1069,7 @@ void tst_QtcProcess::waitForStartedAfterStarted()
}
// This version is using QProcess
-void tst_QtcProcess::waitForStartedAfterStarted2()
+void tst_Process::waitForStartedAfterStarted2()
{
SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {});
QProcess process;
@@ -1063,10 +1089,10 @@ void tst_QtcProcess::waitForStartedAfterStarted2()
QVERIFY(!process.waitForStarted());
}
-void tst_QtcProcess::waitForStartedAndFinished()
+void tst_Process::waitForStartedAndFinished()
{
SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
@@ -1079,7 +1105,7 @@ void tst_QtcProcess::waitForStartedAndFinished()
Q_DECLARE_METATYPE(ProcessSignalType)
-void tst_QtcProcess::notRunningAfterStartingNonExistingProgram_data()
+void tst_Process::notRunningAfterStartingNonExistingProgram_data()
{
QTest::addColumn<ProcessSignalType>("signalType");
@@ -1088,16 +1114,16 @@ void tst_QtcProcess::notRunningAfterStartingNonExistingProgram_data()
QTest::newRow("Done") << ProcessSignalType::Done;
}
-void tst_QtcProcess::notRunningAfterStartingNonExistingProgram()
+void tst_Process::notRunningAfterStartingNonExistingProgram()
{
QFETCH(ProcessSignalType, signalType);
- QtcProcess process;
+ Process process;
process.setCommand({ FilePath::fromString(
"there_is_a_big_chance_that_executable_with_that_name_does_not_exists"), {} });
int doneCount = 0;
- QObject::connect(&process, &QtcProcess::done, [&process, &doneCount]() {
+ QObject::connect(&process, &Process::done, [&process, &doneCount]() {
++doneCount;
QCOMPARE(process.error(), QProcess::FailedToStart);
});
@@ -1138,7 +1164,7 @@ void tst_QtcProcess::notRunningAfterStartingNonExistingProgram()
// and the error channels. Then ChannelForwarding::main() either forwards these channels
// or not - we check it in the outer channelForwarding() test.
-void tst_QtcProcess::channelForwarding_data()
+void tst_Process::channelForwarding_data()
{
QTest::addColumn<QProcess::ProcessChannelMode>("channelMode");
QTest::addColumn<bool>("outputForwarded");
@@ -1151,7 +1177,7 @@ void tst_QtcProcess::channelForwarding_data()
QTest::newRow("ForwardedErrorChannel") << QProcess::ForwardedErrorChannel << false << true;
}
-void tst_QtcProcess::channelForwarding()
+void tst_Process::channelForwarding()
{
QFETCH(QProcess::ProcessChannelMode, channelMode);
QFETCH(bool, outputForwarded);
@@ -1159,7 +1185,7 @@ void tst_QtcProcess::channelForwarding()
SubProcessConfig subConfig(ProcessTestApp::ChannelForwarding::envVar(),
QString::number(int(channelMode)));
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
@@ -1172,7 +1198,7 @@ void tst_QtcProcess::channelForwarding()
QCOMPARE(error.contains(QByteArray(s_errorData)), errorForwarded);
}
-void tst_QtcProcess::mergedChannels_data()
+void tst_Process::mergedChannels_data()
{
QTest::addColumn<QProcess::ProcessChannelMode>("channelMode");
QTest::addColumn<bool>("outputOnOutput");
@@ -1193,7 +1219,7 @@ void tst_QtcProcess::mergedChannels_data()
}
-void tst_QtcProcess::mergedChannels()
+void tst_Process::mergedChannels()
{
QFETCH(QProcess::ProcessChannelMode, channelMode);
QFETCH(bool, outputOnOutput);
@@ -1202,7 +1228,7 @@ void tst_QtcProcess::mergedChannels()
QFETCH(bool, errorOnError);
SubProcessConfig subConfig(ProcessTestApp::StandardOutputAndErrorWriter::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.setProcessChannelMode(channelMode);
@@ -1218,7 +1244,7 @@ void tst_QtcProcess::mergedChannels()
QCOMPARE(error.contains(QByteArray(s_errorData)), errorOnError);
}
-void tst_QtcProcess::destroyBlockingProcess_data()
+void tst_Process::destroyBlockingProcess_data()
{
QTest::addColumn<BlockType>("blockType");
@@ -1228,24 +1254,24 @@ void tst_QtcProcess::destroyBlockingProcess_data()
QTest::newRow("EventLoop") << BlockType::EventLoop;
}
-void tst_QtcProcess::destroyBlockingProcess()
+void tst_Process::destroyBlockingProcess()
{
QFETCH(BlockType, blockType);
SubProcessConfig subConfig(ProcessTestApp::BlockingProcess::envVar(),
QString::number(int(blockType)));
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
QVERIFY(process.waitForStarted());
QVERIFY(!process.waitForFinished(1000));
}
-void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead()
+void tst_Process::flushFinishedWhileWaitingForReadyRead()
{
SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
@@ -1267,10 +1293,10 @@ void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead()
QVERIFY(reply.contains(s_simpleTestData));
}
-void tst_QtcProcess::crash()
+void tst_Process::crash()
{
SubProcessConfig subConfig(ProcessTestApp::Crash::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
@@ -1278,17 +1304,17 @@ void tst_QtcProcess::crash()
QVERIFY(process.isRunning());
QEventLoop loop;
- connect(&process, &QtcProcess::done, &loop, &QEventLoop::quit);
+ connect(&process, &Process::done, &loop, &QEventLoop::quit);
loop.exec();
QCOMPARE(process.error(), QProcess::Crashed);
QCOMPARE(process.exitStatus(), QProcess::CrashExit);
}
-void tst_QtcProcess::crashAfterOneSecond()
+void tst_Process::crashAfterOneSecond()
{
SubProcessConfig subConfig(ProcessTestApp::CrashAfterOneSecond::envVar(), {});
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
@@ -1303,12 +1329,12 @@ void tst_QtcProcess::crashAfterOneSecond()
QCOMPARE(process.error(), QProcess::Crashed);
}
-void tst_QtcProcess::recursiveCrashingProcess()
+void tst_Process::recursiveCrashingProcess()
{
const int recursionDepth = 5; // must be at least 2
SubProcessConfig subConfig(ProcessTestApp::RecursiveCrashingProcess::envVar(),
QString::number(recursionDepth));
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
QVERIFY(process.waitForStarted(1000));
@@ -1329,7 +1355,7 @@ static int runningTestProcessCount()
return testProcessCounter;
}
-void tst_QtcProcess::recursiveBlockingProcess()
+void tst_Process::recursiveBlockingProcess()
{
if (HostOsInfo::isWindowsHost())
QSKIP(s_skipTerminateOnWindows);
@@ -1340,7 +1366,7 @@ void tst_QtcProcess::recursiveBlockingProcess()
SubProcessConfig subConfig(ProcessTestApp::RecursiveBlockingProcess::envVar(),
QString::number(recursionDepth));
{
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
process.start();
QVERIFY(process.waitForStarted(1000));
@@ -1368,7 +1394,7 @@ enum class QuitType {
Q_DECLARE_METATYPE(QuitType)
-void tst_QtcProcess::quitBlockingProcess_data()
+void tst_Process::quitBlockingProcess_data()
{
QTest::addColumn<QuitType>("quitType");
QTest::addColumn<bool>("doneExpected");
@@ -1380,7 +1406,7 @@ void tst_QtcProcess::quitBlockingProcess_data()
QTest::newRow("Close") << QuitType::Close << false << true;
}
-void tst_QtcProcess::quitBlockingProcess()
+void tst_Process::quitBlockingProcess()
{
QFETCH(QuitType, quitType);
QFETCH(bool, doneExpected);
@@ -1394,10 +1420,10 @@ void tst_QtcProcess::quitBlockingProcess()
SubProcessConfig subConfig(ProcessTestApp::RecursiveBlockingProcess::envVar(),
QString::number(recursionDepth));
- QtcProcess process;
+ Process process;
subConfig.setupSubProcess(&process);
bool done = false;
- connect(&process, &QtcProcess::done, this, [&done] { done = true; });
+ connect(&process, &Process::done, this, [&done] { done = true; });
process.start();
QVERIFY(process.waitForStarted());
@@ -1440,17 +1466,17 @@ void tst_QtcProcess::quitBlockingProcess()
}
}
-void tst_QtcProcess::tarPipe()
+void tst_Process::tarPipe()
{
if (!FilePath::fromString("tar").searchInPath().isExecutableFile())
QSKIP("This test uses \"tar\" command.");
- QtcProcess sourceProcess;
- QtcProcess targetProcess;
+ Process sourceProcess;
+ Process targetProcess;
targetProcess.setProcessMode(ProcessMode::Writer);
- QObject::connect(&sourceProcess, &QtcProcess::readyReadStandardOutput,
+ QObject::connect(&sourceProcess, &Process::readyReadStandardOutput,
&targetProcess, [&sourceProcess, &targetProcess]() {
targetProcess.writeRaw(sourceProcess.readAllRawStandardOutput());
});
@@ -1494,6 +1520,21 @@ void tst_QtcProcess::tarPipe()
QCOMPARE(sourceFile.fileSize(), destinationFile.fileSize());
}
-QTEST_GUILESS_MAIN(tst_QtcProcess)
+void tst_Process::stdinToShell()
+{
+ // proc.setCommand({"cmd.exe", {}}); - Piping into cmd.exe does not appear to work.
+ if (HostOsInfo::isWindowsHost())
+ QSKIP("Skipping env test on Windows");
+
+ Process proc;
+ proc.setCommand({"sh", {}});
+ proc.setWriteData("echo hallo");
+ proc.runBlocking();
+
+ QString result = proc.readAllStandardOutput().trimmed();
+ QCOMPARE(result, "hallo");
+}
+
+QTEST_GUILESS_MAIN(tst_Process)
-#include "tst_qtcprocess.moc"
+#include "tst_process.moc"
diff --git a/tests/auto/utils/settings/tst_settings.cpp b/tests/auto/utils/settings/tst_settings.cpp
index a436ac45e8..d45d673e4d 100644
--- a/tests/auto/utils/settings/tst_settings.cpp
+++ b/tests/auto/utils/settings/tst_settings.cpp
@@ -10,7 +10,6 @@
using namespace Utils;
-const char TESTACCESSOR_DN[] = "Test Settings Accessor";
const char TESTACCESSOR_APPLICATION_DN[] = "SettingsAccessor Test (Basic)";
const char TESTACCESSOR_DEFAULT_ID[] = "testId";
@@ -143,10 +142,11 @@ public:
using Utils::MergingSettingsAccessor::upgradeSettings;
};
-BasicTestSettingsAccessor::BasicTestSettingsAccessor(const FilePath &baseName, const QByteArray &id) :
- Utils::MergingSettingsAccessor(std::make_unique<TestBackUpStrategy>(this),
- "TestData", TESTACCESSOR_DN, TESTACCESSOR_APPLICATION_DN)
+BasicTestSettingsAccessor::BasicTestSettingsAccessor(const FilePath &baseName, const QByteArray &id)
{
+ setDocType("TestData");
+ setApplicationDisplayName(TESTACCESSOR_APPLICATION_DN);
+ setStrategy(std::make_unique<TestBackUpStrategy>(this));
setSettingsId(id);
setBaseFilePath(baseName);
}
diff --git a/tests/auto/utils/stringutils/tst_stringutils.cpp b/tests/auto/utils/stringutils/tst_stringutils.cpp
index e13e8411a5..4863bfd950 100644
--- a/tests/auto/utils/stringutils/tst_stringutils.cpp
+++ b/tests/auto/utils/stringutils/tst_stringutils.cpp
@@ -79,6 +79,8 @@ private slots:
void testTrim();
void testWildcardToRegularExpression_data();
void testWildcardToRegularExpression();
+ void testSplitAtFirst_data();
+ void testSplitAtFirst();
private:
TestMacroExpander mx;
@@ -404,6 +406,38 @@ void tst_StringUtils::testWildcardToRegularExpression()
QCOMPARE(string.contains(re), matches);
}
+void tst_StringUtils::testSplitAtFirst_data()
+{
+ QTest::addColumn<QString>("string");
+ QTest::addColumn<QChar>("separator");
+ QTest::addColumn<QString>("left");
+ QTest::addColumn<QString>("right");
+
+ QTest::newRow("Empty") << QString{} << QChar{} << QString{} << QString{};
+ QTest::newRow("EmptyString") << QString{} << QChar{'a'} << QString{} << QString{};
+ QTest::newRow("EmptySeparator") << QString{"abc"} << QChar{} << QString{"abc"} << QString{};
+ QTest::newRow("NoSeparator") << QString{"abc"} << QChar{'d'} << QString{"abc"} << QString{};
+ QTest::newRow("SeparatorAtStart") << QString{"abc"} << QChar{'a'} << QString{} << QString{"bc"};
+ QTest::newRow("SeparatorAtEnd") << QString{"abc"} << QChar{'c'} << QString{"ab"} << QString{};
+ QTest::newRow("SeparatorInMiddle")
+ << QString{"abc"} << QChar{'b'} << QString{"a"} << QString{"c"};
+ QTest::newRow("SeparatorAtStartAndEnd")
+ << QString{"abca"} << QChar{'a'} << QString{} << QString{"bca"};
+}
+
+void tst_StringUtils::testSplitAtFirst()
+{
+ QFETCH(QString, string);
+ QFETCH(QChar, separator);
+ QFETCH(QString, left);
+ QFETCH(QString, right);
+
+ const auto [l, r] = Utils::splitAtFirst(string, separator);
+
+ QCOMPARE(l, left);
+ QCOMPARE(r, right);
+}
+
QTEST_GUILESS_MAIN(tst_StringUtils)
#include "tst_stringutils.moc"
diff --git a/tests/auto/utils/tasktree/CMakeLists.txt b/tests/auto/utils/tasktree/CMakeLists.txt
deleted file mode 100644
index 87c31f1886..0000000000
--- a/tests/auto/utils/tasktree/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-add_subdirectory(testapp)
-
-file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
-file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}")
-
-add_qtc_test(tst_utils_tasktree
- DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\""
- "TESTAPP_PATH=\"${CMAKE_CURRENT_BINARY_DIR}/testapp\""
- DEPENDS Utils app_version
- SOURCES tst_tasktree.cpp
-)
diff --git a/tests/auto/utils/tasktree/tasktree.qbs b/tests/auto/utils/tasktree/tasktree.qbs
deleted file mode 100644
index ed138d9f39..0000000000
--- a/tests/auto/utils/tasktree/tasktree.qbs
+++ /dev/null
@@ -1,27 +0,0 @@
-import qbs.FileInfo
-
-Project {
- QtcAutotest {
- name: "TaskTree autotest"
-
- Depends { name: "Utils" }
- Depends { name: "app_version_header" }
-
- files: [
- "tst_tasktree.cpp",
- ]
- cpp.defines: {
- var defines = base;
- if (qbs.targetOS === "windows")
- defines.push("_CRT_SECURE_NO_WARNINGS");
- var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix,
- qtc.ide_libexec_path);
- var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath);
- defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"');
- defines.push('TESTAPP_PATH="'
- + FileInfo.joinPaths(destinationDirectory, "testapp") + '"');
- return defines;
- }
- }
- references: "testapp/testapp.qbs"
-}
diff --git a/tests/auto/utils/tasktree/testapp/CMakeLists.txt b/tests/auto/utils/tasktree/testapp/CMakeLists.txt
deleted file mode 100644
index 27a1289137..0000000000
--- a/tests/auto/utils/tasktree/testapp/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-add_qtc_executable(testapp
- DEPENDS Utils app_version
- SOURCES main.cpp
- SKIP_INSTALL
- INTERNAL_ONLY
-)
-
-set_target_properties(testapp PROPERTIES
- LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
- ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
-)
diff --git a/tests/auto/utils/tasktree/testapp/main.cpp b/tests/auto/utils/tasktree/testapp/main.cpp
deleted file mode 100644
index 59e2d7baa0..0000000000
--- a/tests/auto/utils/tasktree/testapp/main.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <app/app_version.h>
-
-#include <QString>
-#include <QThread>
-
-#ifdef Q_OS_WIN
-#include <crtdbg.h>
-#include <cstdlib>
-#endif
-
-const char CRASH_OPTION[] = "-crash";
-const char RETURN_OPTION[] = "-return";
-const char SLEEP_OPTION[] = "-sleep";
-
-int main(int argc, char **argv)
-{
-#ifdef Q_OS_WIN
- // avoid crash reporter dialog
- _set_error_mode(_OUT_TO_STDERR);
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
-#endif
-
- if (argc > 1) {
- const auto arg = QString::fromLocal8Bit(argv[1]);
- if (arg == CRASH_OPTION) {
- qFatal("The application has crashed purposefully!");
- return 1;
- }
- if (arg == RETURN_OPTION) {
- if (argc > 2) {
- const auto retString = QString::fromLocal8Bit(argv[2]);
- bool ok = false;
- const int retVal = retString.toInt(&ok);
- if (ok)
- return retVal;
- // not an int return value
- return 1;
- }
- // lacking return value
- return 1;
- }
- if (arg == SLEEP_OPTION) {
- if (argc > 2) {
- const auto msecsString = QString::fromLocal8Bit(argv[2]);
- bool ok = false;
- const int msecsVal = msecsString.toInt(&ok);
- if (ok) {
- QThread::msleep(msecsVal);
- return 0;
- }
- // not an int return value
- return 1;
- }
- // lacking return value
- return 1;
- }
- }
- // not recognized option
- return 1;
-}
diff --git a/tests/auto/utils/tasktree/testapp/testapp.qbs b/tests/auto/utils/tasktree/testapp/testapp.qbs
deleted file mode 100644
index a106e85146..0000000000
--- a/tests/auto/utils/tasktree/testapp/testapp.qbs
+++ /dev/null
@@ -1,20 +0,0 @@
-import qbs.FileInfo
-
-QtApplication {
- name: "testapp"
- Depends { name: "qtc" }
- Depends { name: "Utils" }
- Depends { name: "app_version_header" }
-
- consoleApplication: true
- cpp.cxxLanguageVersion: "c++17"
- cpp.rpaths: project.buildDirectory + '/' + qtc.ide_library_path
-
- install: false
- destinationDirectory: project.buildDirectory + '/'
- + FileInfo.relativePath(project.ide_source_tree, sourceDirectory)
-
- files: [
- "main.cpp",
- ]
-}
diff --git a/tests/auto/utils/tasktree/tst_tasktree.cpp b/tests/auto/utils/tasktree/tst_tasktree.cpp
deleted file mode 100644
index 43bf0468c5..0000000000
--- a/tests/auto/utils/tasktree/tst_tasktree.cpp
+++ /dev/null
@@ -1,1095 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include <app/app_version.h>
-
-#include <utils/launcherinterface.h>
-#include <utils/qtcprocess.h>
-#include <utils/singleton.h>
-#include <utils/temporarydirectory.h>
-
-#include <QtTest>
-
-using namespace Utils;
-using namespace Utils::Tasking;
-
-enum class Handler {
- Setup,
- Done,
- Error,
- GroupSetup,
- GroupDone,
- GroupError
-};
-
-using Log = QList<QPair<int, Handler>>;
-
-struct CustomStorage
-{
- CustomStorage() { ++s_count; }
- ~CustomStorage() { --s_count; }
- Log m_log;
- static int instanceCount() { return s_count; }
-private:
- static int s_count;
-};
-
-int CustomStorage::s_count = 0;
-static const char s_processIdProperty[] = "__processId";
-
-enum class OnStart { Running, NotRunning };
-enum class OnDone { Success, Failure };
-
-struct TestData {
- TreeStorage<CustomStorage> storage;
- Group root;
- Log expectedLog;
- int taskCount = 0;
- OnStart onStart = OnStart::Running;
- OnDone onDone = OnDone::Success;
-};
-
-class tst_TaskTree : public QObject
-{
- Q_OBJECT
-
-private slots:
- void initTestCase();
-
- void validConstructs(); // compile test
- void processTree_data();
- void processTree();
- void storageOperators();
- void storageDestructor();
-
- void cleanupTestCase();
-
-private:
- FilePath m_testAppPath;
-};
-
-void tst_TaskTree::initTestCase()
-{
- TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/"
- + Core::Constants::IDE_CASED_ID + "-XXXXXX");
- const QString libExecPath(qApp->applicationDirPath() + '/'
- + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
- LauncherInterface::setPathToLauncher(libExecPath);
- m_testAppPath = FilePath::fromString(QLatin1String(TESTAPP_PATH)
- + QLatin1String("/testapp")).withExecutableSuffix();
-}
-
-void tst_TaskTree::cleanupTestCase()
-{
- Singleton::deleteAll();
-}
-
-void tst_TaskTree::validConstructs()
-{
- const Group process {
- parallel,
- Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
- Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
- Process([](QtcProcess &) {}, [](const QtcProcess &) {})
- };
-
- const Group group1 {
- process
- };
-
- const Group group2 {
- parallel,
- Group {
- parallel,
- Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
- Group {
- parallel,
- Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
- Group {
- parallel,
- Process([](QtcProcess &) {}, [](const QtcProcess &) {})
- }
- },
- Group {
- parallel,
- Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
- OnGroupDone([] {})
- }
- },
- process,
- OnGroupDone([] {}),
- OnGroupError([] {})
- };
-}
-
-void tst_TaskTree::processTree_data()
-{
- QTest::addColumn<TestData>("testData");
-
- TreeStorage<CustomStorage> storage;
-
- const auto setupProcessHelper = [storage, testAppPath = m_testAppPath]
- (QtcProcess &process, const QStringList &args, int processId) {
- process.setCommand(CommandLine(testAppPath, args));
- process.setProperty(s_processIdProperty, processId);
- storage->m_log.append({processId, Handler::Setup});
- };
- const auto setupProcess = [setupProcessHelper](int processId) {
- return [=](QtcProcess &process) {
- setupProcessHelper(process, {"-return", "0"}, processId);
- };
- };
- const auto setupCrashProcess = [setupProcessHelper](int processId) {
- return [=](QtcProcess &process) {
- setupProcessHelper(process, {"-crash"}, processId);
- };
- };
- const auto setupSleepProcess = [setupProcessHelper](int processId, int msecs) {
- return [=](QtcProcess &process) {
- setupProcessHelper(process, {"-sleep", QString::number(msecs)}, processId);
- };
- };
- const auto setupDynamicProcess = [setupProcessHelper](int processId, TaskAction action) {
- return [=](QtcProcess &process) {
- setupProcessHelper(process, {"-return", "0"}, processId);
- return action;
- };
- };
- const auto readResult = [storage](const QtcProcess &process) {
- const int processId = process.property(s_processIdProperty).toInt();
- storage->m_log.append({processId, Handler::Done});
- };
- const auto readError = [storage](const QtcProcess &process) {
- const int processId = process.property(s_processIdProperty).toInt();
- storage->m_log.append({processId, Handler::Error});
- };
- const auto groupSetup = [storage](int groupId) {
- return [=] { storage->m_log.append({groupId, Handler::GroupSetup}); };
- };
- const auto groupDone = [storage](int groupId) {
- return [=] { storage->m_log.append({groupId, Handler::GroupDone}); };
- };
- const auto groupError = [storage](int groupId) {
- return [=] { storage->m_log.append({groupId, Handler::GroupError}); };
- };
-
- const auto constructSimpleSequence = [=](const Workflow &policy) {
- return Group {
- Storage(storage),
- policy,
- Process(setupProcess(1), readResult),
- Process(setupCrashProcess(2), readResult, readError),
- Process(setupProcess(3), readResult),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- };
- const auto constructDynamicHierarchy = [=](TaskAction taskAction) {
- return Group {
- Storage(storage),
- Group {
- Process(setupProcess(1), readResult)
- },
- Group {
- OnGroupSetup([=] { return taskAction; }),
- Process(setupProcess(2), readResult),
- Process(setupProcess(3), readResult),
- Process(setupProcess(4), readResult)
- },
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- };
-
- {
- const Group root1 {
- Storage(storage),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- const Group root2 {
- Storage(storage),
- OnGroupSetup([] { return TaskAction::Continue; }),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- const Group root3 {
- Storage(storage),
- OnGroupSetup([] { return TaskAction::StopWithDone; }),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- const Group root4 {
- Storage(storage),
- OnGroupSetup([] { return TaskAction::StopWithError; }),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- const Log logDone {{0, Handler::GroupDone}};
- const Log logError {{0, Handler::GroupError}};
- QTest::newRow("Empty")
- << TestData{storage, root1, logDone, 0, OnStart::NotRunning, OnDone::Success};
- QTest::newRow("EmptyContinue")
- << TestData{storage, root2, logDone, 0, OnStart::NotRunning, OnDone::Success};
- QTest::newRow("EmptyDone")
- << TestData{storage, root3, logDone, 0, OnStart::NotRunning, OnDone::Success};
- QTest::newRow("EmptyError")
- << TestData{storage, root4, logError, 0, OnStart::NotRunning, OnDone::Failure};
- }
-
- {
- const Group root {
- Storage(storage),
- Process(setupDynamicProcess(1, TaskAction::StopWithDone), readResult, readError),
- Process(setupDynamicProcess(2, TaskAction::StopWithDone), readResult, readError)
- };
- const Log log {{1, Handler::Setup}, {2, Handler::Setup}};
- QTest::newRow("DynamicTaskDone")
- << TestData{storage, root, log, 2, OnStart::NotRunning, OnDone::Success};
- }
-
- {
- const Group root {
- Storage(storage),
- Process(setupDynamicProcess(1, TaskAction::StopWithError), readResult, readError),
- Process(setupDynamicProcess(2, TaskAction::StopWithError), readResult, readError)
- };
- const Log log {{1, Handler::Setup}};
- QTest::newRow("DynamicTaskError")
- << TestData{storage, root, log, 2, OnStart::NotRunning, OnDone::Failure};
- }
-
- {
- const Group root {
- Storage(storage),
- Process(setupDynamicProcess(1, TaskAction::Continue), readResult, readError),
- Process(setupDynamicProcess(2, TaskAction::Continue), readResult, readError),
- Process(setupDynamicProcess(3, TaskAction::StopWithError), readResult, readError),
- Process(setupDynamicProcess(4, TaskAction::Continue), readResult, readError)
- };
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Done},
- {3, Handler::Setup}
- };
- QTest::newRow("DynamicMixed")
- << TestData{storage, root, log, 4, OnStart::Running, OnDone::Failure};
- }
-
- {
- const Group root {
- parallel,
- Storage(storage),
- Process(setupDynamicProcess(1, TaskAction::Continue), readResult, readError),
- Process(setupDynamicProcess(2, TaskAction::Continue), readResult, readError),
- Process(setupDynamicProcess(3, TaskAction::StopWithError), readResult, readError),
- Process(setupDynamicProcess(4, TaskAction::Continue), readResult, readError)
- };
- const Log log {
- {1, Handler::Setup},
- {2, Handler::Setup},
- {3, Handler::Setup},
- {1, Handler::Error},
- {2, Handler::Error}
- };
- QTest::newRow("DynamicParallel")
- << TestData{storage, root, log, 4, OnStart::NotRunning, OnDone::Failure};
- }
-
- {
- const Group root {
- parallel,
- Storage(storage),
- Process(setupDynamicProcess(1, TaskAction::Continue), readResult, readError),
- Process(setupDynamicProcess(2, TaskAction::Continue), readResult, readError),
- Group {
- Process(setupDynamicProcess(3, TaskAction::StopWithError), readResult, readError)
- },
- Process(setupDynamicProcess(4, TaskAction::Continue), readResult, readError)
- };
- const Log log {
- {1, Handler::Setup},
- {2, Handler::Setup},
- {3, Handler::Setup},
- {1, Handler::Error},
- {2, Handler::Error}
- };
- QTest::newRow("DynamicParallelGroup")
- << TestData{storage, root, log, 4, OnStart::NotRunning, OnDone::Failure};
- }
-
- {
- const Group root {
- parallel,
- Storage(storage),
- Process(setupDynamicProcess(1, TaskAction::Continue), readResult, readError),
- Process(setupDynamicProcess(2, TaskAction::Continue), readResult, readError),
- Group {
- OnGroupSetup([storage] {
- storage->m_log.append({0, Handler::GroupSetup});
- return TaskAction::StopWithError;
- }),
- Process(setupDynamicProcess(3, TaskAction::Continue), readResult, readError)
- },
- Process(setupDynamicProcess(4, TaskAction::Continue), readResult, readError)
- };
- const Log log {
- {1, Handler::Setup},
- {2, Handler::Setup},
- {0, Handler::GroupSetup},
- {1, Handler::Error},
- {2, Handler::Error}
- };
- QTest::newRow("DynamicParallelGroupSetup")
- << TestData{storage, root, log, 4, OnStart::NotRunning, OnDone::Failure};
- }
-
- {
- const Group root {
- Storage(storage),
- Group {
- Group {
- Group {
- Group {
- Group {
- Process(setupProcess(5), readResult, readError),
- OnGroupSetup(groupSetup(5)),
- OnGroupDone(groupDone(5))
- },
- OnGroupSetup(groupSetup(4)),
- OnGroupDone(groupDone(4))
- },
- OnGroupSetup(groupSetup(3)),
- OnGroupDone(groupDone(3))
- },
- OnGroupSetup(groupSetup(2)),
- OnGroupDone(groupDone(2))
- },
- OnGroupSetup(groupSetup(1)),
- OnGroupDone(groupDone(1))
- },
- OnGroupDone(groupDone(0))
- };
- const Log log {
- {1, Handler::GroupSetup},
- {2, Handler::GroupSetup},
- {3, Handler::GroupSetup},
- {4, Handler::GroupSetup},
- {5, Handler::GroupSetup},
- {5, Handler::Setup},
- {5, Handler::Done},
- {5, Handler::GroupDone},
- {4, Handler::GroupDone},
- {3, Handler::GroupDone},
- {2, Handler::GroupDone},
- {1, Handler::GroupDone},
- {0, Handler::GroupDone}
- };
- QTest::newRow("Nested")
- << TestData{storage, root, log, 1, OnStart::Running, OnDone::Success};
- }
-
- {
- const auto readResultAnonymous = [=](const QtcProcess &) {
- storage->m_log.append({0, Handler::Done});
- };
- const Group root {
- Storage(storage),
- parallel,
- Process(setupProcess(1), readResultAnonymous),
- Process(setupProcess(2), readResultAnonymous),
- Process(setupProcess(3), readResultAnonymous),
- Process(setupProcess(4), readResultAnonymous),
- Process(setupProcess(5), readResultAnonymous),
- OnGroupDone(groupDone(0))
- };
- const Log log {
- {1, Handler::Setup}, // Setup order is determined in parallel mode
- {2, Handler::Setup},
- {3, Handler::Setup},
- {4, Handler::Setup},
- {5, Handler::Setup},
- {0, Handler::Done}, // Done order isn't determined in parallel mode
- {0, Handler::Done},
- {0, Handler::Done},
- {0, Handler::Done},
- {0, Handler::Done},
- {0, Handler::GroupDone}
- };
- QTest::newRow("Parallel")
- << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
- }
-
- {
- auto setupSubTree = [=](TaskTree &taskTree) {
- const Group nestedRoot {
- Storage(storage),
- Process(setupProcess(2), readResult),
- Process(setupProcess(3), readResult),
- Process(setupProcess(4), readResult)
- };
- taskTree.setupRoot(nestedRoot);
- CustomStorage *activeStorage = storage.activeStorage();
- auto collectSubLog = [activeStorage](CustomStorage *subTreeStorage){
- activeStorage->m_log += subTreeStorage->m_log;
- };
- taskTree.onStorageDone(storage, collectSubLog);
- };
- const Group root1 {
- Storage(storage),
- Process(setupProcess(1), readResult),
- Process(setupProcess(2), readResult),
- Process(setupProcess(3), readResult),
- Process(setupProcess(4), readResult),
- Process(setupProcess(5), readResult),
- OnGroupDone(groupDone(0))
- };
- const Group root2 {
- Storage(storage),
- Group { Process(setupProcess(1), readResult) },
- Group { Process(setupProcess(2), readResult) },
- Group { Process(setupProcess(3), readResult) },
- Group { Process(setupProcess(4), readResult) },
- Group { Process(setupProcess(5), readResult) },
- OnGroupDone(groupDone(0))
- };
- const Group root3 {
- Storage(storage),
- Process(setupProcess(1), readResult),
- Tree(setupSubTree),
- Process(setupProcess(5), readResult),
- OnGroupDone(groupDone(0))
- };
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Done},
- {3, Handler::Setup},
- {3, Handler::Done},
- {4, Handler::Setup},
- {4, Handler::Done},
- {5, Handler::Setup},
- {5, Handler::Done},
- {0, Handler::GroupDone}
- };
- QTest::newRow("Sequential")
- << TestData{storage, root1, log, 5, OnStart::Running, OnDone::Success};
- QTest::newRow("SequentialEncapsulated")
- << TestData{storage, root2, log, 5, OnStart::Running, OnDone::Success};
- QTest::newRow("SequentialSubTree") // We don't inspect subtrees, so taskCount is 3, not 5.
- << TestData{storage, root3, log, 3, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- Storage(storage),
- Group {
- Process(setupProcess(1), readResult),
- Group {
- Process(setupProcess(2), readResult),
- Group {
- Process(setupProcess(3), readResult),
- Group {
- Process(setupProcess(4), readResult),
- Group {
- Process(setupProcess(5), readResult),
- OnGroupDone(groupDone(5))
- },
- OnGroupDone(groupDone(4))
- },
- OnGroupDone(groupDone(3))
- },
- OnGroupDone(groupDone(2))
- },
- OnGroupDone(groupDone(1))
- },
- OnGroupDone(groupDone(0))
- };
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Done},
- {3, Handler::Setup},
- {3, Handler::Done},
- {4, Handler::Setup},
- {4, Handler::Done},
- {5, Handler::Setup},
- {5, Handler::Done},
- {5, Handler::GroupDone},
- {4, Handler::GroupDone},
- {3, Handler::GroupDone},
- {2, Handler::GroupDone},
- {1, Handler::GroupDone},
- {0, Handler::GroupDone}
- };
- QTest::newRow("SequentialNested")
- << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- Storage(storage),
- Process(setupProcess(1), readResult),
- Process(setupProcess(2), readResult),
- Process(setupCrashProcess(3), readResult, readError),
- Process(setupProcess(4), readResult),
- Process(setupProcess(5), readResult),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Done},
- {3, Handler::Setup},
- {3, Handler::Error},
- {0, Handler::GroupError}
- };
- QTest::newRow("SequentialError")
- << TestData{storage, root, log, 5, OnStart::Running, OnDone::Failure};
- }
-
- {
- const Group root = constructSimpleSequence(stopOnError);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Error},
- {0, Handler::GroupError}
- };
- QTest::newRow("StopOnError")
- << TestData{storage, root, log, 3, OnStart::Running, OnDone::Failure};
- }
-
- {
- const Group root = constructSimpleSequence(continueOnError);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Error},
- {3, Handler::Setup},
- {3, Handler::Done},
- {0, Handler::GroupError}
- };
- QTest::newRow("ContinueOnError")
- << TestData{storage, root, log, 3, OnStart::Running, OnDone::Failure};
- }
-
- {
- const Group root = constructSimpleSequence(stopOnDone);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {0, Handler::GroupDone}
- };
- QTest::newRow("StopOnDone")
- << TestData{storage, root, log, 3, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root = constructSimpleSequence(continueOnDone);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Error},
- {3, Handler::Setup},
- {3, Handler::Done},
- {0, Handler::GroupDone}
- };
- QTest::newRow("ContinueOnDone")
- << TestData{storage, root, log, 3, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- Storage(storage),
- optional,
- Process(setupCrashProcess(1), readResult, readError),
- Process(setupCrashProcess(2), readResult, readError),
- OnGroupDone(groupDone(0)),
- OnGroupError(groupError(0))
- };
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Error},
- {2, Handler::Setup},
- {2, Handler::Error},
- {0, Handler::GroupDone}
- };
- QTest::newRow("Optional")
- << TestData{storage, root, log, 2, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root = constructDynamicHierarchy(TaskAction::StopWithDone);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {0, Handler::GroupDone}
- };
- QTest::newRow("DynamicSetupDone")
- << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root = constructDynamicHierarchy(TaskAction::StopWithError);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {0, Handler::GroupError}
- };
- QTest::newRow("DynamicSetupError")
- << TestData{storage, root, log, 4, OnStart::Running, OnDone::Failure};
- }
-
- {
- const Group root = constructDynamicHierarchy(TaskAction::Continue);
- const Log log {
- {1, Handler::Setup},
- {1, Handler::Done},
- {2, Handler::Setup},
- {2, Handler::Done},
- {3, Handler::Setup},
- {3, Handler::Done},
- {4, Handler::Setup},
- {4, Handler::Done},
- {0, Handler::GroupDone}
- };
- QTest::newRow("DynamicSetupContinue")
- << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- ParallelLimit(2),
- Storage(storage),
- Group {
- OnGroupSetup(groupSetup(1)),
- Process(setupProcess(1))
- },
- Group {
- OnGroupSetup(groupSetup(2)),
- Process(setupProcess(2))
- },
- Group {
- OnGroupSetup(groupSetup(3)),
- Process(setupProcess(3))
- },
- Group {
- OnGroupSetup(groupSetup(4)),
- Process(setupProcess(4))
- }
- };
- const Log log {
- {1, Handler::GroupSetup},
- {1, Handler::Setup},
- {2, Handler::GroupSetup},
- {2, Handler::Setup},
- {3, Handler::GroupSetup},
- {3, Handler::Setup},
- {4, Handler::GroupSetup},
- {4, Handler::Setup}
- };
- QTest::newRow("NestedParallel")
- << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- ParallelLimit(2),
- Storage(storage),
- Group {
- OnGroupSetup(groupSetup(1)),
- Process(setupProcess(1))
- },
- Group {
- OnGroupSetup(groupSetup(2)),
- Process(setupProcess(2))
- },
- Group {
- OnGroupSetup(groupSetup(3)),
- Process(setupDynamicProcess(3, TaskAction::StopWithDone))
- },
- Group {
- OnGroupSetup(groupSetup(4)),
- Process(setupProcess(4))
- },
- Group {
- OnGroupSetup(groupSetup(5)),
- Process(setupProcess(5))
- }
- };
- const Log log {
- {1, Handler::GroupSetup},
- {1, Handler::Setup},
- {2, Handler::GroupSetup},
- {2, Handler::Setup},
- {3, Handler::GroupSetup},
- {3, Handler::Setup},
- {4, Handler::GroupSetup},
- {4, Handler::Setup},
- {5, Handler::GroupSetup},
- {5, Handler::Setup}
- };
- QTest::newRow("NestedParallelDone")
- << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root1 {
- ParallelLimit(2),
- Storage(storage),
- Group {
- OnGroupSetup(groupSetup(1)),
- Process(setupProcess(1))
- },
- Group {
- OnGroupSetup(groupSetup(2)),
- Process(setupProcess(2))
- },
- Group {
- OnGroupSetup(groupSetup(3)),
- Process(setupDynamicProcess(3, TaskAction::StopWithError))
- },
- Group {
- OnGroupSetup(groupSetup(4)),
- Process(setupProcess(4))
- },
- Group {
- OnGroupSetup(groupSetup(5)),
- Process(setupProcess(5))
- }
- };
-
- // Inside this test the process 2 should finish first, then synchonously:
- // - process 3 should exit setup with error
- // - process 1 should be stopped as a consequence of error inside the group
- // - processes 4 and 5 should be skipped
- const Group root2 {
- ParallelLimit(2),
- Storage(storage),
- Group {
- OnGroupSetup(groupSetup(1)),
- Process(setupSleepProcess(1, 100))
- },
- Group {
- OnGroupSetup(groupSetup(2)),
- Process(setupProcess(2))
- },
- Group {
- OnGroupSetup(groupSetup(3)),
- Process(setupDynamicProcess(3, TaskAction::StopWithError))
- },
- Group {
- OnGroupSetup(groupSetup(4)),
- Process(setupProcess(4))
- },
- Group {
- OnGroupSetup(groupSetup(5)),
- Process(setupProcess(5))
- }
- };
-
- // This test ensures process 1 doesn't invoke its done handler,
- // being ready while sleeping in process 2 done handler.
- // Inside this test the process 2 should finish first, then synchonously:
- // - process 3 should exit setup with error
- // - process 1 should be stopped as a consequence of error inside the group
- // - process 4 should be skipped
- // - the first child group of root should finish with error
- // - process 5 should be started (because of root's continueOnError policy)
- const Group root3 {
- continueOnError,
- Storage(storage),
- Group {
- ParallelLimit(2),
- Group {
- OnGroupSetup(groupSetup(1)),
- Process(setupSleepProcess(1, 100))
- },
- Group {
- OnGroupSetup(groupSetup(2)),
- Process(setupProcess(2), [](const QtcProcess &) { QThread::msleep(200); })
- },
- Group {
- OnGroupSetup(groupSetup(3)),
- Process(setupDynamicProcess(3, TaskAction::StopWithError))
- },
- Group {
- OnGroupSetup(groupSetup(4)),
- Process(setupProcess(4))
- }
- },
- Group {
- OnGroupSetup(groupSetup(5)),
- Process(setupProcess(5))
- }
- };
- const Log shortLog {
- {1, Handler::GroupSetup},
- {1, Handler::Setup},
- {2, Handler::GroupSetup},
- {2, Handler::Setup},
- {3, Handler::GroupSetup},
- {3, Handler::Setup}
- };
- const Log longLog = shortLog + Log {{5, Handler::GroupSetup}, {5, Handler::Setup}};
- QTest::newRow("NestedParallelError1")
- << TestData{storage, root1, shortLog, 5, OnStart::Running, OnDone::Failure};
- QTest::newRow("NestedParallelError2")
- << TestData{storage, root2, shortLog, 5, OnStart::Running, OnDone::Failure};
- QTest::newRow("NestedParallelError3")
- << TestData{storage, root3, longLog, 5, OnStart::Running, OnDone::Failure};
- }
-
- {
- const Group root {
- ParallelLimit(2),
- Storage(storage),
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(1)),
- Group {
- parallel,
- Process(setupProcess(1))
- }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(2)),
- Group {
- parallel,
- Process(setupProcess(2))
- }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(3)),
- Group {
- parallel,
- Process(setupProcess(3))
- }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(4)),
- Group {
- parallel,
- Process(setupProcess(4))
- }
- }
- };
- const Log log {
- {1, Handler::GroupSetup},
- {1, Handler::Setup},
- {2, Handler::GroupSetup},
- {2, Handler::Setup},
- {3, Handler::GroupSetup},
- {3, Handler::Setup},
- {4, Handler::GroupSetup},
- {4, Handler::Setup}
- };
- QTest::newRow("DeeplyNestedParallel")
- << TestData{storage, root, log, 4, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- ParallelLimit(2),
- Storage(storage),
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(1)),
- Group { Process(setupProcess(1)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(2)),
- Group { Process(setupProcess(2)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(3)),
- Group { Process(setupDynamicProcess(3, TaskAction::StopWithDone)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(4)),
- Group { Process(setupProcess(4)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(5)),
- Group { Process(setupProcess(5)) }
- }
- };
- const Log log {
- {1, Handler::GroupSetup},
- {1, Handler::Setup},
- {2, Handler::GroupSetup},
- {2, Handler::Setup},
- {3, Handler::GroupSetup},
- {3, Handler::Setup},
- {4, Handler::GroupSetup},
- {4, Handler::Setup},
- {5, Handler::GroupSetup},
- {5, Handler::Setup}
- };
- QTest::newRow("DeeplyNestedParallelDone")
- << TestData{storage, root, log, 5, OnStart::Running, OnDone::Success};
- }
-
- {
- const Group root {
- ParallelLimit(2),
- Storage(storage),
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(1)),
- Group { Process(setupProcess(1)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(2)),
- Group { Process(setupProcess(2)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(3)),
- Group { Process(setupDynamicProcess(3, TaskAction::StopWithError)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(4)),
- Group { Process(setupProcess(4)) }
- },
- Group {
- Storage(TreeStorage<CustomStorage>()),
- OnGroupSetup(groupSetup(5)),
- Group { Process(setupProcess(5)) }
- }
- };
- const Log log {
- {1, Handler::GroupSetup},
- {1, Handler::Setup},
- {2, Handler::GroupSetup},
- {2, Handler::Setup},
- {3, Handler::GroupSetup},
- {3, Handler::Setup}
- };
- QTest::newRow("DeeplyNestedParallelError")
- << TestData{storage, root, log, 5, OnStart::Running, OnDone::Failure};
- }
-}
-
-void tst_TaskTree::processTree()
-{
- QFETCH(TestData, testData);
-
- QEventLoop eventLoop;
- TaskTree taskTree(testData.root);
- QCOMPARE(taskTree.taskCount(), testData.taskCount);
- int doneCount = 0;
- int errorCount = 0;
- connect(&taskTree, &TaskTree::done, this, [&doneCount, &eventLoop] {
- ++doneCount;
- eventLoop.quit();
- });
- connect(&taskTree, &TaskTree::errorOccurred, this, [&errorCount, &eventLoop] {
- ++errorCount;
- eventLoop.quit();
- });
- Log actualLog;
- auto collectLog = [&actualLog](CustomStorage *storage){
- actualLog = storage->m_log;
- };
- taskTree.onStorageDone(testData.storage, collectLog);
- taskTree.start();
- const bool expectRunning = testData.onStart == OnStart::Running;
- QCOMPARE(taskTree.isRunning(), expectRunning);
-
- if (expectRunning) {
- QTimer timer;
- bool timedOut = false;
- connect(&timer, &QTimer::timeout, &eventLoop, [&eventLoop, &timedOut] {
- timedOut = true;
- eventLoop.quit();
- });
- timer.setInterval(2000);
- timer.setSingleShot(true);
- timer.start();
- eventLoop.exec();
- QCOMPARE(timedOut, false);
- QCOMPARE(taskTree.isRunning(), false);
- }
-
- QCOMPARE(taskTree.progressValue(), testData.taskCount);
- QCOMPARE(actualLog, testData.expectedLog);
- QCOMPARE(CustomStorage::instanceCount(), 0);
-
- const bool expectSuccess = testData.onDone == OnDone::Success;
- const int expectedDoneCount = expectSuccess ? 1 : 0;
- const int expectedErrorCount = expectSuccess ? 0 : 1;
- QCOMPARE(doneCount, expectedDoneCount);
- QCOMPARE(errorCount, expectedErrorCount);
-}
-
-void tst_TaskTree::storageOperators()
-{
- TreeStorageBase storage1 = TreeStorage<CustomStorage>();
- TreeStorageBase storage2 = TreeStorage<CustomStorage>();
- TreeStorageBase storage3 = storage1;
-
- QVERIFY(storage1 == storage3);
- QVERIFY(storage1 != storage2);
- QVERIFY(storage2 != storage3);
-}
-
-// This test checks whether a running task tree may be safely destructed.
-// It also checks whether the destructor of a task tree deletes properly the storage created
-// while starting the task tree. When running task tree is destructed, the storage done
-// handler shouldn't be invoked.
-void tst_TaskTree::storageDestructor()
-{
- bool setupCalled = false;
- const auto setupHandler = [&setupCalled](CustomStorage *) {
- setupCalled = true;
- };
- bool doneCalled = false;
- const auto doneHandler = [&doneCalled](CustomStorage *) {
- doneCalled = true;
- };
- QCOMPARE(CustomStorage::instanceCount(), 0);
- {
- TreeStorage<CustomStorage> storage;
- const auto setupProcess = [testAppPath = m_testAppPath](QtcProcess &process) {
- process.setCommand(CommandLine(testAppPath, {"-sleep", "1000"}));
- };
- const Group root {
- Storage(storage),
- Process(setupProcess)
- };
-
- TaskTree taskTree(root);
- QCOMPARE(CustomStorage::instanceCount(), 0);
- taskTree.onStorageSetup(storage, setupHandler);
- taskTree.onStorageDone(storage, doneHandler);
- taskTree.start();
- QCOMPARE(CustomStorage::instanceCount(), 1);
- }
- QCOMPARE(CustomStorage::instanceCount(), 0);
- QVERIFY(setupCalled);
- QVERIFY(!doneCalled);
-}
-
-QTEST_GUILESS_MAIN(tst_TaskTree)
-
-#include "tst_tasktree.moc"
diff --git a/tests/auto/utils/text/CMakeLists.txt b/tests/auto/utils/text/CMakeLists.txt
new file mode 100644
index 0000000000..78fd3838f4
--- /dev/null
+++ b/tests/auto/utils/text/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_test(tst_utils_text
+ DEPENDS Utils
+ SOURCES tst_text.cpp
+)
diff --git a/tests/auto/utils/text/text.qbs b/tests/auto/utils/text/text.qbs
new file mode 100644
index 0000000000..828dec8d11
--- /dev/null
+++ b/tests/auto/utils/text/text.qbs
@@ -0,0 +1,7 @@
+import qbs
+
+QtcAutotest {
+ name: "Utils::Text autotest"
+ Depends { name: "Utils" }
+ files: "tst_text.cpp"
+}
diff --git a/tests/auto/utils/text/tst_text.cpp b/tests/auto/utils/text/tst_text.cpp
new file mode 100644
index 0000000000..3672eb2ce2
--- /dev/null
+++ b/tests/auto/utils/text/tst_text.cpp
@@ -0,0 +1,211 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <utils/textutils.h>
+
+#include <QTextCursor>
+#include <QTextDocument>
+#include <QtTest>
+
+using namespace Utils::Text;
+
+class tst_Text : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testPositionFromFileName_data();
+ void testPositionFromFileName();
+
+ void testPositionFromPositionInDocument_data();
+ void testPositionFromPositionInDocument();
+
+ void testPositionFromCursor_data();
+ void testPositionFromCursor();
+
+ void testRangeLength_data();
+ void testRangeLength();
+};
+
+void tst_Text::testPositionFromFileName_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<Position>("pos");
+ QTest::addColumn<int>("expectedPostfixPos");
+
+ const QString file("/foo/bar");
+ const QString fileWin("C:\\foo\\bar");
+
+ QTest::newRow("no pos") << file << Position() << -1;
+ QTest::newRow("no pos win") << fileWin << Position() << -1;
+
+ QTest::newRow("empty:") << file + ":" << Position() << 8;
+ QTest::newRow("empty: win") << fileWin + ":" << Position() << 10;
+
+ QTest::newRow("empty+") << file + "+" << Position() << 8;
+ QTest::newRow("empty+ win") << fileWin + "+" << Position() << 10;
+
+ QTest::newRow("empty::") << file + "::" << Position() << 8;
+ QTest::newRow("empty:: win") << fileWin + "::" << Position() << 10;
+
+ QTest::newRow("empty++") << file + "++" << Position() << 8;
+ QTest::newRow("empty++ win") << fileWin + "++" << Position() << 10;
+
+ QTest::newRow("line:") << file + ":1" << Position{1, 0} << 8;
+ QTest::newRow("line: win") << fileWin + ":1" << Position{1, 0} << 10;
+
+ QTest::newRow("line+") << file + "+8" << Position{8, 0} << 8;
+ QTest::newRow("line+ win") << fileWin + "+8" << Position{8, 0} << 10;
+
+ QTest::newRow("multi digit line:") << file + ":42" << Position{42, 0} << 8;
+ QTest::newRow("multi digit line: win") << fileWin + ":42" << Position{42, 0} << 10;
+
+ QTest::newRow("multi digit line+") << file + "+1234567890" << Position{1234567890, 0} << 8;
+ QTest::newRow("multi digit line+ win")
+ << fileWin + "+1234567890" << Position{1234567890, 0} << 10;
+
+ QTest::newRow("line: empty column:") << file + ":1:" << Position{1, 0} << 8;
+ QTest::newRow("line: empty column: win") << fileWin + ":1:" << Position{1, 0} << 10;
+
+ QTest::newRow("line+ empty column+") << file + "+8+" << Position{8, 0} << 8;
+ QTest::newRow("line+ empty column+ win") << fileWin + "+8+" << Position{8, 0} << 10;
+
+ QTest::newRow("line: column:") << file + ":1:2" << Position{1, 1} << 8;
+ QTest::newRow("line: column: win") << fileWin + ":1:2" << Position{1, 1} << 10;
+
+ QTest::newRow("line+ column+") << file + "+8+3" << Position{8, 2} << 8;
+ QTest::newRow("line+ column+ win") << fileWin + "+8+3" << Position{8, 2} << 10;
+
+ QTest::newRow("mixed:+") << file + ":1+2" << Position{1, 1} << 8;
+ QTest::newRow("mixed:+ win") << fileWin + ":1+2" << Position{1, 1} << 10;
+
+ QTest::newRow("mixed+:") << file + "+8:3" << Position{8, 2} << 8;
+ QTest::newRow("mixed+: win") << fileWin + "+8:3" << Position{8, 2} << 10;
+
+ QTest::newRow("garbage:") << file + ":foo" << Position() << -1;
+ QTest::newRow("garbage: win") << fileWin + ":bar" << Position() << -1;
+
+ QTest::newRow("garbage+") << file + "+snu" << Position() << -1;
+ QTest::newRow("garbage+ win") << fileWin + "+snu" << Position() << -1;
+
+ QTest::newRow("msvc(") << file + "(" << Position() << 8;
+ QTest::newRow("msvc( win") << fileWin + "(" << Position() << 10;
+
+ QTest::newRow("msvc(empty)") << file + "()" << Position() << -1;
+ QTest::newRow("msvc(empty) win") << fileWin + "()" << Position() << -1;
+
+ QTest::newRow("msvc(line") << file + "(1" << Position{1, 0} << 8;
+ QTest::newRow("msvc(line win") << fileWin + "(4569871" << Position{4569871, 0} << 10;
+
+ QTest::newRow("msvc(line)") << file + "(1)" << Position{1, 0} << 8;
+ QTest::newRow("msvc(line) win") << fileWin + "(4569871)" << Position{4569871, 0} << 10;
+
+ QTest::newRow("msvc(garbage)") << file + "(foo)" << Position() << -1;
+ QTest::newRow("msvc(garbage) win") << fileWin + "(bar)" << Position() << -1;
+}
+
+void tst_Text::testPositionFromFileName()
+{
+ QFETCH(QString, fileName);
+ QFETCH(Position, pos);
+ QFETCH(int, expectedPostfixPos);
+
+ int postfixPos = -1;
+ QCOMPARE(Position::fromFileName(fileName, postfixPos), pos);
+ QCOMPARE(postfixPos, expectedPostfixPos);
+}
+
+void tst_Text::testPositionFromPositionInDocument_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<int>("documentPosition");
+ QTest::addColumn<Position>("position");
+
+ QTest::newRow("0") << QString() << 0 << Position{1, 0};
+ QTest::newRow("-1") << QString() << -1 << Position();
+ QTest::newRow("mid line") << QString("foo\n") << 1 << Position{1, 1};
+ QTest::newRow("second line") << QString("foo\n") << 4 << Position{2, 0};
+ QTest::newRow("second mid line") << QString("foo\nbar") << 5 << Position{2, 1};
+ QTest::newRow("behind content") << QString("foo\nbar") << 8 << Position();
+}
+
+void tst_Text::testPositionFromPositionInDocument()
+{
+ QFETCH(QString, text);
+ QFETCH(int, documentPosition);
+ QFETCH(Position, position);
+
+ const QTextDocument doc(text);
+ QCOMPARE(Position::fromPositionInDocument(&doc, documentPosition), position);
+}
+
+void tst_Text::testPositionFromCursor_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<int>("documentPosition");
+ QTest::addColumn<Position>("position");
+
+ QTest::newRow("0") << QString() << 0 << Position{1, 0};
+ QTest::newRow("-1") << QString() << -1 << Position{};
+ QTest::newRow("mid line") << QString("foo\n") << 1 << Position{1, 1};
+ QTest::newRow("second line") << QString("foo\n") << 4 << Position{2, 0};
+ QTest::newRow("second mid line") << QString("foo\nbar") << 5 << Position{2, 1};
+ QTest::newRow("behind content") << QString("foo\nbar") << 8 << Position{1, 0};
+}
+
+void tst_Text::testPositionFromCursor()
+{
+ QFETCH(QString, text);
+ QFETCH(int, documentPosition);
+ QFETCH(Position, position);
+
+ if (documentPosition < 0) {// test invalid Cursor {
+ QCOMPARE(Position::fromCursor(QTextCursor()), position);
+ } else {
+ QTextDocument doc(text);
+ QTextCursor cursor(&doc);
+ cursor.setPosition(documentPosition);
+ QCOMPARE(Position::fromCursor(cursor), position);
+ }
+}
+
+void tst_Text::testRangeLength_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<Range>("range");
+ QTest::addColumn<int>("length");
+
+ QTest::newRow("empty range") << QString() << Range{{1, 0}, {1, 0}} << 0;
+
+ QTest::newRow("range on same line") << QString() << Range{{1, 0}, {1, 1}} << 1;
+
+ QTest::newRow("range spanning lines") << QString("foo\nbar") << Range{{1, 0}, {2, 0}} << 4;
+
+ QTest::newRow("range spanning lines and column offset")
+ << QString("foo\nbar") << Range{{1, 1}, {2, 1}} << 4;
+
+ QTest::newRow("range spanning lines and begin column offset")
+ << QString("foo\nbar") << Range{{1, 1}, {2, 0}} << 3;
+
+ QTest::newRow("range spanning lines and end column offset")
+ << QString("foo\nbar") << Range{{1, 0}, {2, 1}} << 5;
+
+ QTest::newRow("hyper range") << QString("foo\nbar\nfoobar") << Range{{2, 1}, {3, 1}} << 4;
+
+ QTest::newRow("flipped range") << QString() << Range{{2, 0}, {1, 0}} << -1;
+
+ QTest::newRow("out of range") << QString() << Range{{1, 0}, {2, 0}} << -1;
+}
+
+void tst_Text::testRangeLength()
+{
+ QFETCH(QString, text);
+ QFETCH(Range, range);
+ QFETCH(int, length);
+
+ QCOMPARE(range.length(text), length);
+}
+
+QTEST_GUILESS_MAIN(tst_Text)
+
+#include "tst_text.moc"
diff --git a/tests/auto/utils/unixdevicefileaccess/CMakeLists.txt b/tests/auto/utils/unixdevicefileaccess/CMakeLists.txt
new file mode 100644
index 0000000000..0cf8d43c71
--- /dev/null
+++ b/tests/auto/utils/unixdevicefileaccess/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_test(tst_utils_unixdevicefileaccess
+ DEPENDS Utils
+ SOURCES tst_unixdevicefileaccess.cpp
+)
diff --git a/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp b/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp
new file mode 100644
index 0000000000..b5aef12d06
--- /dev/null
+++ b/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QDebug>
+#include <QRandomGenerator>
+#include <QtCore/qiodevice.h>
+#include <QtTest>
+
+#include <utils/commandline.h>
+#include <utils/devicefileaccess.h>
+#include <utils/hostosinfo.h>
+#include <utils/link.h>
+
+//TESTED_COMPONENT=src/libs/utils
+using namespace Utils;
+
+namespace QTest {
+template<>
+char *toString(const FilePath &filePath)
+{
+ return qstrdup(filePath.toString().toLocal8Bit().constData());
+}
+} // namespace QTest
+
+class TestDFA : public UnixDeviceFileAccess
+{
+public:
+ using UnixDeviceFileAccess::UnixDeviceFileAccess;
+
+ virtual RunResult runInShell(const CommandLine &cmdLine,
+ const QByteArray &inputData = {}) const override
+ {
+ QProcess p;
+ p.setProgram(cmdLine.executable().toString());
+ p.setArguments(cmdLine.splitArguments());
+ p.setProcessChannelMode(QProcess::SeparateChannels);
+
+ p.start();
+ p.waitForStarted();
+ if (inputData.size() > 0) {
+ p.write(inputData);
+ p.closeWriteChannel();
+ }
+ p.waitForFinished();
+ return {p.exitCode(), p.readAllStandardOutput(), p.readAllStandardError()};
+ }
+};
+
+class tst_unixdevicefileaccess : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ if (HostOsInfo::isWindowsHost())
+ QSKIP("This test is only for Unix hosts");
+
+ m_fileSizeTestFile.writeFileContents(QByteArray(1024, 'a'));
+ }
+
+ void fileSize()
+ {
+ const auto size = m_dfaPtr->fileSize(m_fileSizeTestFile);
+ QCOMPARE(size, 1024);
+ }
+
+private:
+ TestDFA m_dfa;
+ DeviceFileAccess *m_dfaPtr = &m_dfa;
+
+ QTemporaryDir m_tempDir;
+ FilePath m_fileSizeTestFile = FilePath::fromString(m_tempDir.filePath("size-test"));
+};
+
+QTEST_GUILESS_MAIN(tst_unixdevicefileaccess)
+
+#include "tst_unixdevicefileaccess.moc"
diff --git a/tests/auto/utils/unixdevicefileaccess/unixdevicefileaccess.qbs b/tests/auto/utils/unixdevicefileaccess/unixdevicefileaccess.qbs
new file mode 100644
index 0000000000..1776ef4a6e
--- /dev/null
+++ b/tests/auto/utils/unixdevicefileaccess/unixdevicefileaccess.qbs
@@ -0,0 +1,11 @@
+import qbs
+
+QtcAutotest {
+ name: "UnixDeviceFileAccess autotest"
+ Depends { name: "Utils" }
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.cxxFlags: base.concat(["-Wno-trigraphs"])
+ }
+ files: "tst_unixdevicefileaccess.cpp"
+}
diff --git a/tests/auto/utils/utils.qbs b/tests/auto/utils/utils.qbs
index eea3664423..e267f009f7 100644
--- a/tests/auto/utils/utils.qbs
+++ b/tests/auto/utils/utils.qbs
@@ -4,10 +4,11 @@ Project {
name: "Utils autotests"
references: [
"ansiescapecodehandler/ansiescapecodehandler.qbs",
- "asynctask/asynctask.qbs",
+ "async/async.qbs",
"commandline/commandline.qbs",
"deviceshell/deviceshell.qbs",
"expected/expected.qbs",
+ "filepath/filepath.qbs",
"fileutils/fileutils.qbs",
"fsengine/fsengine.qbs",
"fuzzymatcher/fuzzymatcher.qbs",
@@ -15,11 +16,11 @@ Project {
"mathutils/mathutils.qbs",
"multicursor/multicursor.qbs",
"persistentsettings/persistentsettings.qbs",
- "qtcprocess/qtcprocess.qbs",
+ "process/process.qbs",
"settings/settings.qbs",
"stringutils/stringutils.qbs",
- "tasktree/tasktree.qbs",
"templateengine/templateengine.qbs",
"treemodel/treemodel.qbs",
+ "unixdevicefileaccess/unixdevicefileaccess.qbs",
]
}
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/CMakeLists.txt b/tests/manual/cmakeprojectmanager/hello-widgets/CMakeLists.txt
new file mode 100644
index 0000000000..8c0c413d66
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.18)
+
+project(hello-widgets)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Widgets)
+
+qt_add_executable(hello-widgets
+ main.cpp
+ mainwindow.cpp
+ mainwindow.h
+ mainwindow.ui
+)
+
+target_link_libraries(hello-widgets PRIVATE Qt6::Widgets)
+
+include(my_add_executable.cmake)
+
+my_add_executable(hello-my-widgets
+ main.cpp
+ mainwindow.cpp
+ mainwindow.h
+ mainwindow.ui
+)
+
+target_link_libraries(hello-my-widgets PRIVATE Qt6::Widgets)
+
+file(GLOB SOURCE_FILES CONFIGURE_DEPENDS *.cpp *.h *.ui)
+
+add_executable(hello-widgets-glob ${SOURCE_FILES})
+target_link_libraries(hello-widgets-glob PRIVATE Qt6::Widgets)
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/README.md b/tests/manual/cmakeprojectmanager/hello-widgets/README.md
new file mode 100644
index 0000000000..52da33ef8a
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/README.md
@@ -0,0 +1,4 @@
+Qt6 Widgets project to test adding new or existing files to a CMake project.
+
+The project uses both custom CMake API and normal Qt6 qt_add_executable API
+function call.
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/main.cpp b/tests/manual/cmakeprojectmanager/hello-widgets/main.cpp
new file mode 100644
index 0000000000..a0ccba3ce9
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/main.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.cpp b/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.cpp
new file mode 100644
index 0000000000..c36872995c
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.cpp
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwindow.h"
+#include "./ui_mainwindow.h"
+
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.h b/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.h
new file mode 100644
index 0000000000..35d90b0cfd
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private:
+ Ui::MainWindow *ui;
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.ui b/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.ui
new file mode 100644
index 0000000000..b232854ba8
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/mainwindow.ui
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar"/>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/my_add_executable.cmake b/tests/manual/cmakeprojectmanager/hello-widgets/my_add_executable.cmake
new file mode 100644
index 0000000000..dca664be47
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/my_add_executable.cmake
@@ -0,0 +1,5 @@
+function(my_add_executable targetName)
+ add_executable(${targetName}
+ ${ARGN}
+ )
+endfunction()
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/myclass.cpp b/tests/manual/cmakeprojectmanager/hello-widgets/myclass.cpp
new file mode 100644
index 0000000000..ce14aae2ce
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/myclass.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "myclass.h"
+
+myclass::myclass()
+{
+
+}
+
diff --git a/tests/manual/cmakeprojectmanager/hello-widgets/myclass.h b/tests/manual/cmakeprojectmanager/hello-widgets/myclass.h
new file mode 100644
index 0000000000..98e0e47cb1
--- /dev/null
+++ b/tests/manual/cmakeprojectmanager/hello-widgets/myclass.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MYCLASS_H
+#define MYCLASS_H
+
+
+
+
+class myclass
+{
+public:
+ myclass();
+};
+
+#endif // MYCLASS_H
diff --git a/tests/manual/deviceshell/tst_deviceshell.cpp b/tests/manual/deviceshell/tst_deviceshell.cpp
index 10c1414975..99c506d1bd 100644
--- a/tests/manual/deviceshell/tst_deviceshell.cpp
+++ b/tests/manual/deviceshell/tst_deviceshell.cpp
@@ -5,14 +5,12 @@
#include <utils/deviceshell.h>
#include <utils/environment.h>
-#include <utils/hostosinfo.h>
#include <utils/launcherinterface.h>
-#include <utils/qtcprocess.h>
-#include <utils/runextensions.h>
+#include <utils/process.h>
#include <utils/temporarydirectory.h>
-#include <utils/mapreduce.h>
#include <QObject>
+#include <QtConcurrent>
#include <QtTest>
using namespace Utils;
@@ -56,7 +54,7 @@ public:
}
private:
- void setupShellProcess(QtcProcess *shellProcess) override
+ void setupShellProcess(Process *shellProcess) override
{
shellProcess->setCommand(cmdLine());
}
@@ -86,45 +84,47 @@ class tst_DeviceShell : public QObject
return result;
}
- void test(int maxNumThreads, int numCalls)
+ void test(int numCalls, int maxNumThreads)
{
TestShell shell;
QCOMPARE(shell.state(), DeviceShell::State::Succeeded);
QThreadPool::globalInstance()->setMaxThreadCount(maxNumThreads);
- QList<QByteArray> testArray = testArrays(numCalls);
+ const QList<QByteArray> testArray = testArrays(numCalls);
QElapsedTimer t;
t.start();
- const QList<QByteArray> result
- = mapped<QList>(testArray, [&shell](QByteArray data) -> QByteArray {
- return shell.runInShell({"cat", {}}, data).stdOut;
- }, MapReduceOption::Ordered, QThreadPool::globalInstance());
-
- QCOMPARE(result, testArray);
+ const auto cat = [&shell](const QByteArray &data) {
+ return shell.runInShell({"cat", {}}, data).stdOut;
+ };
+ const QList<QByteArray> results = QtConcurrent::blockingMapped(testArray, cat);
+ QCOMPARE(results, testArray);
qDebug() << "maxThreads:" << maxNumThreads << ", took:" << t.elapsed() / 1000.0
<< "seconds";
}
- void testSleep(QList<int> testData, int nThreads)
+ void testSleep(QList<int> testData, int maxNumThreads)
{
TestShell shell;
QCOMPARE(shell.state(), DeviceShell::State::Succeeded);
- QThreadPool::globalInstance()->setMaxThreadCount(nThreads);
+ QThreadPool::globalInstance()->setMaxThreadCount(maxNumThreads);
QElapsedTimer t;
t.start();
- const auto result = mapped<QList>(testData, [&shell](const int &time) {
+ const auto sleep = [&shell](int time) {
shell.runInShell({"sleep", {QString("%1").arg(time)}});
return 0;
- }, MapReduceOption::Unordered, QThreadPool::globalInstance());
+ };
+ const QList<int> results = QtConcurrent::blockingMapped(testData, sleep);
+ QCOMPARE(results, QList<int>(testData.size(), 0));
- qDebug() << "maxThreads:" << nThreads << ", took:" << t.elapsed() / 1000.0 << "seconds";
+ qDebug() << "maxThreads:" << maxNumThreads << ", took:" << t.elapsed() / 1000.0
+ << "seconds";
}
private slots:
@@ -183,7 +183,7 @@ private slots:
QFETCH(int, numThreads);
QFETCH(int, numIterations);
- test(numThreads, numIterations);
+ test(numIterations, numThreads);
}
void testSleepMulti()
diff --git a/tests/manual/fakevim/main.cpp b/tests/manual/fakevim/main.cpp
index c733dd6319..288c244e43 100644
--- a/tests/manual/fakevim/main.cpp
+++ b/tests/manual/fakevim/main.cpp
@@ -187,12 +187,12 @@ int main(int argc, char *argv[])
// Create FakeVimHandler instance which will emulate Vim behavior in editor widget.
FakeVimHandler handler(editor, nullptr);
- handler.commandBufferChanged.connect([&](const QString &msg, int cursorPos, int, int) {
+ handler.commandBufferChanged.set([&](const QString &msg, int cursorPos, int, int) {
statusData.setStatusMessage(msg, cursorPos);
mainWindow.statusBar()->showMessage(statusData.currentStatusLine());
});
- handler.selectionChanged.connect([&handler](const QList<QTextEdit::ExtraSelection> &s) {
+ handler.selectionChanged.set([&handler](const QList<QTextEdit::ExtraSelection> &s) {
QWidget *widget = handler.widget();
if (auto ed = qobject_cast<QPlainTextEdit *>(widget))
ed->setExtraSelections(s);
@@ -200,21 +200,20 @@ int main(int argc, char *argv[])
ed->setExtraSelections(s);
});
- handler.extraInformationChanged.connect([&](const QString &info) {
+ handler.extraInformationChanged.set([&](const QString &info) {
statusData.setStatusInfo(info);
mainWindow.statusBar()->showMessage(statusData.currentStatusLine());
});
- handler.statusDataChanged.connect([&](const QString &info) {
+ handler.statusDataChanged.set([&](const QString &info) {
statusData.setStatusInfo(info);
mainWindow.statusBar()->showMessage(statusData.currentStatusLine());
});
- handler.highlightMatches.connect([&](const QString &needle) {
- highlightMatches(handler.widget(), needle);
- });
+ handler.highlightMatches.set(
+ [&](const QString &needle) { highlightMatches(handler.widget(), needle); });
- handler.handleExCommandRequested.connect([](bool *handled, const ExCommand &cmd) {
+ handler.handleExCommandRequested.set([](bool *handled, const ExCommand &cmd) {
if (cmd.matches("q", "quit") || cmd.matches("qa", "qall")) {
QApplication::quit();
*handled = true;
diff --git a/tests/manual/layoutbuilder/comparison/CMakeLists.txt b/tests/manual/layoutbuilder/comparison/CMakeLists.txt
new file mode 100644
index 0000000000..ac1c62fabd
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+project(Comparison)
+
+add_subdirectory(quick)
+add_subdirectory(widgets)
+add_subdirectory(layoutbuilder)
diff --git a/tests/manual/layoutbuilder/comparison/layoutbuilder/CMakeLists.txt b/tests/manual/layoutbuilder/comparison/layoutbuilder/CMakeLists.txt
new file mode 100644
index 0000000000..326985aae0
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/layoutbuilder/CMakeLists.txt
@@ -0,0 +1,46 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(layoutbuilder VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Widgets)
+
+set(PROJECT_SOURCES
+ main.cpp
+ mainwindow.cpp
+ mainwindow.h
+ ../../../../../src/libs/utils/layoutbuilder.cpp
+ ../../../../../src/libs/utils/layoutbuilder.h
+)
+
+add_executable(layoutbuilder
+ ${PROJECT_SOURCES}
+)
+
+target_include_directories(layoutbuilder PRIVATE
+ ../../../../../src/libs/utils/
+)
+
+target_link_libraries(layoutbuilder PRIVATE
+ Qt6::Widgets
+)
+
+set_target_properties(layoutbuilder PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+install(TARGETS layoutbuilder
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
diff --git a/tests/manual/layoutbuilder/comparison/layoutbuilder/main.cpp b/tests/manual/layoutbuilder/comparison/layoutbuilder/main.cpp
new file mode 100644
index 0000000000..6620ba52e2
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/layoutbuilder/main.cpp
@@ -0,0 +1,6 @@
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ return app.exec(argc, argv);
+}
diff --git a/tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.cpp b/tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.cpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.cpp
diff --git a/tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.h b/tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.h
new file mode 100644
index 0000000000..e238a5cfcd
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/layoutbuilder/mainwindow.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "layoutbuilder.h"
+
+#include <QApplication>
+
+using namespace Layouting;
+
+Application app
+{
+ resize(600, 400),
+ title("Hello World"),
+
+ Column {
+ TextEdit {
+ text("Hallo")
+ },
+
+ PushButton {
+ text("Quit"),
+ onClicked(QApplication::quit)
+ }
+ }
+};
diff --git a/tests/manual/layoutbuilder/comparison/quick/CMakeLists.txt b/tests/manual/layoutbuilder/comparison/quick/CMakeLists.txt
new file mode 100644
index 0000000000..4166619afd
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/quick/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.16)
+
+project(quick VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
+
+qt_standard_project_setup()
+
+qt_add_executable(appquick
+ main.cpp
+)
+
+qt_add_qml_module(appquick
+ URI quick
+ VERSION 1.0
+ QML_FILES Main.qml
+)
+
+set_target_properties(appquick PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+target_link_libraries(appquick
+ PRIVATE Qt6::Quick
+)
+
+install(TARGETS appquick
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
diff --git a/tests/manual/layoutbuilder/comparison/quick/Main.qml b/tests/manual/layoutbuilder/comparison/quick/Main.qml
new file mode 100644
index 0000000000..617b573caa
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/quick/Main.qml
@@ -0,0 +1,30 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow
+{
+ width: 640
+ height: 480
+ visible: true
+ title: "Hello World"
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Text {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ text: "Hallo"
+ }
+
+ Button {
+ text: "Quit"
+ height: 20
+ Layout.fillWidth: true
+
+ onClicked: { Qt.quit() }
+ }
+ }
+}
diff --git a/tests/manual/layoutbuilder/comparison/quick/main.cpp b/tests/manual/layoutbuilder/comparison/quick/main.cpp
new file mode 100644
index 0000000000..76df95e6ea
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/quick/main.cpp
@@ -0,0 +1,16 @@
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QUrl url(u"qrc:/quick/Main.qml"_qs);
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/tests/manual/layoutbuilder/comparison/widgets/CMakeLists.txt b/tests/manual/layoutbuilder/comparison/widgets/CMakeLists.txt
new file mode 100644
index 0000000000..8f895f4121
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/widgets/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(widgets VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Widgets)
+
+set(PROJECT_SOURCES
+ main.cpp
+ mainwindow.cpp
+ mainwindow.h
+)
+
+add_executable(widgets
+ ${PROJECT_SOURCES}
+)
+
+target_link_libraries(widgets PRIVATE
+ Qt6::Widgets
+)
+
+set_target_properties(widgets PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+install(TARGETS widgets
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
diff --git a/tests/manual/layoutbuilder/comparison/widgets/main.cpp b/tests/manual/layoutbuilder/comparison/widgets/main.cpp
new file mode 100644
index 0000000000..c610fc6a49
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/widgets/main.cpp
@@ -0,0 +1,9 @@
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ ApplicationWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/manual/layoutbuilder/comparison/widgets/mainwindow.cpp b/tests/manual/layoutbuilder/comparison/widgets/mainwindow.cpp
new file mode 100644
index 0000000000..346e6a1ca0
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/widgets/mainwindow.cpp
@@ -0,0 +1,24 @@
+//#include "mainwindow.h"
+
+//#include <QApplication>
+//#include <QPushButton>
+//#include <QTextEdit>
+//#include <QVBoxLayout>
+
+//ApplicationWindow::ApplicationWindow()
+//{
+// resize(600, 400);
+// setWindowTitle("Hello World");
+
+// auto textEdit = new QTextEdit;
+// textEdit->setText("Hallo");
+
+// auto pushButton = new QPushButton("Quit");
+
+// auto l = new QVBoxLayout(this);
+// l->addWidget(textEdit);
+// l->addWidget(pushButton);
+
+// connect(pushButton, &QPushButton::clicked,
+// qApp, &QCoreApplication::quit);
+//}
diff --git a/tests/manual/layoutbuilder/comparison/widgets/mainwindow.h b/tests/manual/layoutbuilder/comparison/widgets/mainwindow.h
new file mode 100644
index 0000000000..1dc8ad6f83
--- /dev/null
+++ b/tests/manual/layoutbuilder/comparison/widgets/mainwindow.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <QApplication>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QVBoxLayout>
+
+class ApplicationWindow : public QWidget
+{
+public:
+ ApplicationWindow()
+ {
+ resize(600, 400);
+ setWindowTitle("Hello World");
+
+ auto textEdit = new QTextEdit;
+ textEdit->setText("Hallo");
+
+ auto pushButton = new QPushButton("Quit");
+
+ auto l = new QVBoxLayout(this);
+ l->addWidget(textEdit);
+ l->addWidget(pushButton);
+
+ connect(pushButton, &QPushButton::clicked,
+ qApp, &QApplication::quit);
+ }
+};
diff --git a/tests/manual/layoutbuilder/demo/CMakeLists.txt b/tests/manual/layoutbuilder/demo/CMakeLists.txt
new file mode 100644
index 0000000000..3a93056bea
--- /dev/null
+++ b/tests/manual/layoutbuilder/demo/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(layoutbuilderdemo VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Widgets)
+
+set(PROJECT_SOURCES
+ main.cpp
+ ../../../../src/libs/utils/layoutbuilder.h
+ ../../../../src/libs/utils/layoutbuilder.cpp
+)
+
+add_executable(layoutbuilderdemo
+ ${PROJECT_SOURCES}
+)
+
+target_include_directories(layoutbuilderdemo PRIVATE
+ ../../../../src/libs/utils/
+)
+
+target_link_libraries(layoutbuilderdemo PRIVATE
+ Qt6::Widgets
+)
+
+set_target_properties(layoutbuilderdemo PROPERTIES
+ MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+install(TARGETS layoutbuilderdemo
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
diff --git a/tests/manual/layoutbuilder/demo/main.cpp b/tests/manual/layoutbuilder/demo/main.cpp
new file mode 100644
index 0000000000..2dd95dfddb
--- /dev/null
+++ b/tests/manual/layoutbuilder/demo/main.cpp
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "layoutbuilder.h"
+
+#include <QApplication>
+
+using namespace Layouting;
+
+int main(int argc, char *argv[])
+{
+ ID textId;
+
+ Application app
+ {
+ resize(600, 400),
+ title("Hello World"),
+
+ Column {
+ TextEdit {
+ id(textId),
+ text("Hallo")
+ },
+
+ Row {
+ SpinBox {
+ onTextChanged([&](const QString &text) { setText(textId, text); })
+ },
+
+ Stretch(),
+
+ PushButton {
+ text("Quit"),
+ onClicked(QApplication::quit)
+ },
+ }
+ }
+ };
+
+ return app.exec(argc, argv);
+}
diff --git a/tests/manual/layoutbuilder/demo/mainwindow.cpp b/tests/manual/layoutbuilder/demo/mainwindow.cpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/manual/layoutbuilder/demo/mainwindow.cpp
diff --git a/tests/manual/layoutbuilder/demo/mainwindow.h b/tests/manual/layoutbuilder/demo/mainwindow.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/manual/layoutbuilder/demo/mainwindow.h
diff --git a/tests/manual/tasktree/main.cpp b/tests/manual/tasktree/main.cpp
index b57481bfc4..6b6db61ade 100644
--- a/tests/manual/tasktree/main.cpp
+++ b/tests/manual/tasktree/main.cpp
@@ -3,7 +3,7 @@
#include "taskwidget.h"
-#include <utils/asynctask.h>
+#include <utils/async.h>
#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
#include <utils/theme/theme_p.h>
@@ -18,11 +18,11 @@
using namespace Utils;
// TODO: make tasks cancellable
-static void sleepInThread(QFutureInterface<void> &fi, int seconds, bool reportSuccess)
+static void sleepInThread(QPromise<void> &promise, int seconds, bool reportSuccess)
{
QThread::sleep(seconds);
if (!reportSuccess)
- fi.reportCanceled();
+ promise.future().cancel();
}
int main(int argc, char *argv[])
@@ -146,27 +146,27 @@ int main(int argc, char *argv[])
// Task tree creator (takes configuation from GUI)
+ using namespace Tasking;
+
std::unique_ptr<TaskTree> taskTree;
FutureSynchronizer synchronizer;
- synchronizer.setCancelOnWait(true);
auto treeRoot = [&] {
- using namespace Tasking;
auto taskItem = [sync = &synchronizer, synchronizerCheckBox](TaskWidget *widget) {
- const auto setupHandler = [=](AsyncTask<void> &task) {
- task.setAsyncCallData(sleepInThread, widget->busyTime(), widget->isSuccess());
+ const auto setupHandler = [=](Async<void> &task) {
+ task.setConcurrentCallData(sleepInThread, widget->busyTime(), widget->isSuccess());
if (synchronizerCheckBox->isChecked())
task.setFutureSynchronizer(sync);
widget->setState(State::Running);
};
- const auto doneHandler = [widget](const AsyncTask<void> &) {
+ const auto doneHandler = [widget](const Async<void> &) {
widget->setState(State::Done);
};
- const auto errorHandler = [widget](const AsyncTask<void> &) {
+ const auto errorHandler = [widget](const Async<void> &) {
widget->setState(State::Error);
};
- return Async<void>(setupHandler, doneHandler, errorHandler);
+ return AsyncTask<void>(setupHandler, doneHandler, errorHandler);
};
const Group root {
diff --git a/tests/manual/tasktree/taskwidget.cpp b/tests/manual/tasktree/taskwidget.cpp
index cca5394bbb..0f72164181 100644
--- a/tests/manual/tasktree/taskwidget.cpp
+++ b/tests/manual/tasktree/taskwidget.cpp
@@ -91,8 +91,9 @@ TaskWidget::TaskWidget()
m_infoLabel,
m_spinBox,
m_checkBox,
- st
- }.attachTo(this, WithoutMargins);
+ st,
+ noMargin,
+ }.attachTo(this);
}
void TaskWidget::setBusyTime(int seconds)
@@ -145,9 +146,10 @@ GroupWidget::GroupWidget()
m_executeCombo,
new QLabel("Workflow:"),
m_workflowCombo,
- st
+ st,
+ noMargin
}
- }.attachTo(this, WithoutMargins);
+ }.attachTo(this);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
}
@@ -184,7 +186,8 @@ Tasking::WorkflowPolicy GroupWidget::workflowPolicy() const
return m_workflowPolicy;
}
-TaskGroup::TaskGroup(QWidget *group, std::initializer_list<LayoutItem> items)
- : Row({ group, Group { Column { items } } })
-{}
+void createItem(Layouting::LayoutItem *item, const TaskGroup &taskGroup)
+{
+ item->addItems({taskGroup.group, Group { taskGroup.items }, br});
+}
diff --git a/tests/manual/tasktree/taskwidget.h b/tests/manual/tasktree/taskwidget.h
index 1f867dfeae..dc8821af5e 100644
--- a/tests/manual/tasktree/taskwidget.h
+++ b/tests/manual/tasktree/taskwidget.h
@@ -1,8 +1,9 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include <solutions/tasking/tasktree.h>
+
#include <utils/layoutbuilder.h>
-#include <utils/tasktree.h>
#include <QWidget>
@@ -60,10 +61,10 @@ public:
GroupWidget();
void setExecuteMode(ExecuteMode mode);
- Utils::Tasking::ParallelLimit executeMode() const;
+ Tasking::ParallelLimit executeMode() const;
- void setWorkflowPolicy(Utils::Tasking::WorkflowPolicy policy);
- Utils::Tasking::WorkflowPolicy workflowPolicy() const;
+ void setWorkflowPolicy(Tasking::WorkflowPolicy policy);
+ Tasking::WorkflowPolicy workflowPolicy() const;
private:
void updateExecuteMode();
@@ -73,11 +74,14 @@ private:
QComboBox *m_workflowCombo = nullptr;
ExecuteMode m_executeMode = ExecuteMode::Sequential;
- Utils::Tasking::WorkflowPolicy m_workflowPolicy = Utils::Tasking::WorkflowPolicy::StopOnError;
+ Tasking::WorkflowPolicy m_workflowPolicy = Tasking::WorkflowPolicy::StopOnError;
};
-class TaskGroup : public Utils::Layouting::Row
+class TaskGroup
{
public:
- TaskGroup(QWidget *group, std::initializer_list<Utils::Layouting::LayoutItem> items);
+ QWidget *group;
+ Layouting::Column items;
};
+
+void createItem(Layouting::LayoutItem *item, const TaskGroup &taskGroup);
diff --git a/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp b/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp
index 4f6bf710b2..03a1cbc4be 100644
--- a/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp
+++ b/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp
@@ -7,7 +7,7 @@
#include <QLineEdit>
#include <QTextEdit>
-using namespace Utils::Layouting;
+using namespace Layouting;
int main(int argc, char *argv[])
{
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 3a36658ae4..7baf81f84b 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -30,7 +30,7 @@ if (NOT QT_CREATOR_API_DEFINED)
set(GOOGLETEST_DIR ${CMAKE_CURRENT_LIST_DIR}/unittest/3rdparty/googletest)
find_package(Clang MODULE)
- find_package(Qt5
+ find_package(Qt6
COMPONENTS
Gui Core Core5Compat Widgets Network Qml Concurrent Test Xml MODULE)
find_package(Threads)
diff --git a/tests/unit/unittest/3rdparty/googletest b/tests/unit/unittest/3rdparty/googletest
-Subproject 58d77fa8070e8cec2dc1ed015d66b454c8d7885
+Subproject b796f7d44681514f58a683a3a71ff17c94edb0c
diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt
index fd73f4cb12..7522a8e180 100644
--- a/tests/unit/unittest/CMakeLists.txt
+++ b/tests/unit/unittest/CMakeLists.txt
@@ -1,5 +1,6 @@
find_package(Googletest MODULE)
find_package(GoogleBenchmark MODULE)
+find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate)
if (NOT Googletest_FOUND)
message(STATUS "Googletest was not found. Please set GOOGLETEST_DIR (CMake or Environment) variable.")
@@ -18,13 +19,14 @@ file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_B
file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}")
add_qtc_test(unittest GTEST
+ PROPERTIES COMPILE_WARNING_AS_ERROR OFF
INCLUDES
BEFORE "../mockup"
BEFORE "../mockup/qmldesigner/designercore/include"
DEPENDS
Qt::Core Qt::Network Qt::Widgets
Qt::Xml Qt::Concurrent Qt::Qml Qt::Gui
- Qt6Core5Compat QmlJS Sqlite SqliteC
+ Qt::Core5Compat QmlJS Sqlite SqliteC
Googletest
DEFINES
QT_NO_CAST_TO_ASCII
@@ -36,7 +38,6 @@ add_qtc_test(unittest GTEST
TEST_RELATIVE_LIBEXEC_PATH="${TEST_RELATIVE_LIBEXEC_PATH}"
SOURCES
abstractviewmock.h
- compare-operators.h
conditionally-disabled-tests.h
dynamicastmatcherdiagnosticcontainer-matcher.h
eventspy.cpp eventspy.h
@@ -70,6 +71,7 @@ add_qtc_test(unittest GTEST
sqlitecolumn-test.cpp
sqlitedatabasebackend-test.cpp
sqlitedatabase-test.cpp
+ sqlitefunctionregistry-test.cpp
sqlitesessions-test.cpp
sqlitestatement-test.cpp
sqlitetable-test.cpp
@@ -248,6 +250,7 @@ extend_qtc_test(unittest
projectstorage/filestatus.h
projectstorage/filestatuscache.cpp projectstorage/filestatuscache.h
projectstorage/nonlockingmutex.h
+ projectstorage/projectstorageexceptions.cpp projectstorage/projectstorageexceptions.h
projectstorage/projectstorageinterface.h
projectstorage/projectstorage.cpp projectstorage/projectstorage.h
projectstorage/projectstoragepathwatcher.h
@@ -311,26 +314,18 @@ endif()
extend_qtc_test(unittest DEPENDS Utils CPlusPlus)
extend_qtc_test(unittest
- SOURCES_PREFIX ../../../src/plugins/coreplugin
- DEFINES CORE_STATIC_LIBRARY
- SOURCES
- coreicons.cpp coreicons.h
- find/ifindfilter.cpp find/ifindfilter.h
- locator/ilocatorfilter.cpp locator/ilocatorfilter.h
-)
-
-extend_qtc_test(unittest
- CONDITION TARGET qmldomlib
- DEPENDS qmldomlib
+ CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0
+ DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate
SOURCES
qmldocumentparser-test.cpp
qmltypesparser-test.cpp
)
extend_qtc_test(unittest
+ CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0
SOURCES_PREFIX "${QmlDesignerDir}/designercore"
- CONDITION TARGET qmldomlib
- DEPENDS qmldomlib
+ DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate
+ DEFINES QDS_HAS_QMLDOM
SOURCES
projectstorage/qmldocumentparser.cpp projectstorage/qmldocumentparser.h
projectstorage/qmltypesparser.cpp projectstorage/qmltypesparser.h
diff --git a/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp b/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp
index 9f0f63d06a..3bad57fc83 100644
--- a/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp
+++ b/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp
@@ -22,6 +22,7 @@ protected:
NiceMock<MockImageCacheStorage> mockStorage;
QmlDesigner::AsynchronousExplicitImageCache cache{mockStorage};
QImage image1{10, 10, QImage::Format_ARGB32};
+ QImage midSizeImage1{5, 5, QImage::Format_ARGB32};
QImage smallImage1{1, 1, QImage::Format_ARGB32};
};
@@ -73,7 +74,7 @@ TEST_F(AsynchronousExplicitImageCache, RequestImageCallsAbortCallbackWithoutEntr
ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{}));
- EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)))
+ EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::NoEntry)))
.WillRepeatedly([&](auto) { notification.notify(); });
cache.requestImage("/path/to/Component.qml",
@@ -96,6 +97,63 @@ TEST_F(AsynchronousExplicitImageCache, RequestImageCallsAbortCallbackWithoutImag
notification.wait();
}
+TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageFetchesMidSizeImageFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{})))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::ImageEntry{};
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsCaptureCallbackWithImageFromStorage)
+{
+ ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage1}));
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(midSizeImage1))).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsAbortCallbackWithoutEntry)
+{
+ ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{}));
+
+ EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::NoEntry)))
+ .WillRepeatedly([&](auto) { notification.notify(); });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsAbortCallbackWithoutMidSizeImage)
+{
+ ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}}));
+
+ EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)))
+ .WillRepeatedly([&](auto) { notification.notify(); });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
TEST_F(AsynchronousExplicitImageCache, RequestSmallImageFetchesSmallImageFromStorage)
{
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{})))
@@ -130,7 +188,7 @@ TEST_F(AsynchronousExplicitImageCache, RequestSmallImageCallsAbortCallbackWithou
ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _))
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{}));
- EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)))
+ EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::NoEntry)))
.WillRepeatedly([&](auto) { notification.notify(); });
cache.requestSmallImage("/path/to/Component.qml",
@@ -223,6 +281,21 @@ TEST_F(AsynchronousExplicitImageCache, RequestImageWithExtraIdFetchesImageFromSt
notification.wait();
}
+TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageWithExtraIdFetchesImageFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml+extraId1"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::ImageEntry{};
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction(),
+ "extraId1");
+ notification.wait();
+}
+
TEST_F(AsynchronousExplicitImageCache, RequestSmallImageWithExtraIdFetchesImageFromStorage)
{
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), _))
diff --git a/tests/unit/unittest/asynchronousimagecache-test.cpp b/tests/unit/unittest/asynchronousimagecache-test.cpp
index 711a34da19..16e1a59d19 100644
--- a/tests/unit/unittest/asynchronousimagecache-test.cpp
+++ b/tests/unit/unittest/asynchronousimagecache-test.cpp
@@ -24,6 +24,7 @@ protected:
NiceMock<MockTimeStampProvider> mockTimeStampProvider;
QmlDesigner::AsynchronousImageCache cache{mockStorage, mockGenerator, mockTimeStampProvider};
QImage image1{10, 10, QImage::Format_ARGB32};
+ QImage midSizeImage1{5, 5, QImage::Format_ARGB32};
QImage smallImage1{1, 1, QImage::Format_ARGB32};
};
@@ -105,7 +106,7 @@ TEST_F(AsynchronousImageCache, RequestImageCallsCaptureCallbackWithImageFromGene
{
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) {
- callback(QImage{image1}, QImage{smallImage1});
+ callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
notification.notify();
});
@@ -133,6 +134,113 @@ TEST_F(AsynchronousImageCache, RequestImageCallsAbortCallbackFromGenerator)
notification.wait();
}
+TEST_F(AsynchronousImageCache, RequestMidSizeImageFetchesMidSizeImageFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::ImageEntry{};
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousImageCache, RequestMidSizeImageFetchesMidSizeImageFromStorageWithTimeStamp)
+{
+ EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillRepeatedly(Return(Sqlite::TimeStamp{123}));
+ EXPECT_CALL(mockStorage,
+ fetchMidSizeImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::ImageEntry{};
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsCaptureCallbackWithImageFromStorage)
+{
+ ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1}));
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(smallImage1))).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsAbortCallbackWithoutMidSizeImage)
+{
+ ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}}));
+
+ EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)))
+ .WillRepeatedly([&](auto) { notification.notify(); });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousImageCache, RequestMidSizeImageRequestImageFromGenerator)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _))
+ .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsCaptureCallbackWithImageFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
+ .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) {
+ callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(midSizeImage1)));
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsAbortCallbackFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
+ .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) {
+ abortCallback(QmlDesigner::ImageCache::AbortReason::Failed);
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)));
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
TEST_F(AsynchronousImageCache, RequestSmallImageFetchesSmallImageFromStorage)
{
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _))
@@ -211,7 +319,7 @@ TEST_F(AsynchronousImageCache, RequestSmallImageCallsCaptureCallbackWithImageFro
{
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) {
- callback(QImage{image1}, QImage{smallImage1});
+ callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
notification.notify();
});
@@ -243,7 +351,7 @@ TEST_F(AsynchronousImageCache, CleanRemovesEntries)
{
EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _, _))
.WillRepeatedly([&](auto, auto, auto, auto &&captureCallback, auto &&, auto) {
- captureCallback(QImage{}, QImage{});
+ captureCallback(QImage{}, QImage{}, QImage{});
waitInThread.wait();
});
cache.requestSmallImage("/path/to/Component1.qml",
@@ -315,6 +423,21 @@ TEST_F(AsynchronousImageCache, RequestImageWithExtraIdFetchesImageFromStorage)
notification.wait();
}
+TEST_F(AsynchronousImageCache, RequestMidSizeImageWithExtraIdFetchesImageFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml+extraId1"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::ImageEntry{};
+ });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction(),
+ "extraId1");
+ notification.wait();
+}
+
TEST_F(AsynchronousImageCache, RequestSmallImageWithExtraIdFetchesImageFromStorage)
{
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), _))
@@ -347,6 +470,23 @@ TEST_F(AsynchronousImageCache, RequestImageWithExtraIdRequestImageFromGenerator)
notification.wait();
}
+TEST_F(AsynchronousImageCache, RequestMidSizeImageWithExtraIdRequestImageFromGenerator)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(
+ Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _))
+ .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction(),
+ "extraId1");
+ notification.wait();
+}
+
TEST_F(AsynchronousImageCache, RequestSmallImageWithExtraIdRequestImageFromGenerator)
{
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
@@ -392,6 +532,34 @@ TEST_F(AsynchronousImageCache, RequestImageWithAuxiliaryDataRequestImageFromGene
notification.wait();
}
+TEST_F(AsynchronousImageCache, RequestMidSizeImageWithAuxiliaryDataRequestImageFromGenerator)
+{
+ using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
+ std::vector<QSize> sizes{{20, 11}};
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(Eq("/path/to/Component.qml"),
+ Eq("extraId1"),
+ Eq(Sqlite::TimeStamp{123}),
+ _,
+ _,
+ VariantWith<FontCollectorSizesAuxiliaryData>(
+ AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
+ ElementsAre(QSize{20, 11})),
+ Field(&FontCollectorSizesAuxiliaryData::colorName,
+ Eq(u"color"))))))
+ .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); });
+
+ cache.requestMidSizeImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction(),
+ "extraId1",
+ FontCollectorSizesAuxiliaryData{sizes, "color", "text"});
+ notification.wait();
+}
+
TEST_F(AsynchronousImageCache, RequestSmallImageWithAuxiliaryDataRequestImageFromGenerator)
{
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
diff --git a/tests/unit/unittest/asynchronousimagefactory-test.cpp b/tests/unit/unittest/asynchronousimagefactory-test.cpp
index bb76117432..49a44c9afe 100644
--- a/tests/unit/unittest/asynchronousimagefactory-test.cpp
+++ b/tests/unit/unittest/asynchronousimagefactory-test.cpp
@@ -32,6 +32,7 @@ protected:
NiceMock<MockTimeStampProvider> timeStampProviderMock;
QmlDesigner::AsynchronousImageFactory factory{storageMock, timeStampProviderMock, collectorMock};
QImage image1{10, 10, QImage::Format_ARGB32};
+ QImage midSizeImage1{5, 5, QImage::Format_ARGB32};
QImage smallImage1{1, 1, QImage::Format_ARGB32};
};
@@ -162,14 +163,17 @@ TEST_F(AsynchronousImageFactory, CaptureImageCallbackStoresImage)
VariantWith<std::monostate>(std::monostate{}),
_,
_))
- .WillByDefault([&](auto, auto, auto, auto capture, auto) { capture(image1, smallImage1); });
+ .WillByDefault([&](auto, auto, auto, auto capture, auto) {
+ capture(image1, midSizeImage1, smallImage1);
+ });
EXPECT_CALL(storageMock,
storeImage(Eq("/path/to/Component.qml+id"),
Sqlite::TimeStamp{125},
Eq(image1),
+ Eq(midSizeImage1),
Eq(smallImage1)))
- .WillOnce([&](auto, auto, auto, auto) { notification.notify(); });
+ .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); });
factory.generate("/path/to/Component.qml", "id");
notification.wait();
diff --git a/tests/unit/unittest/compare-operators.h b/tests/unit/unittest/compare-operators.h
deleted file mode 100644
index dc3b3357a2..0000000000
--- a/tests/unit/unittest/compare-operators.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <coreplugin/find/searchresultitem.h>
-
-namespace Core {
-namespace Search {
-
-inline
-bool operator==(const TextPosition first, class TextPosition second)
-{
- return first.line == second.line
- && first.column == second.column;
-}
-
-inline
-bool operator==(const TextRange first, class TextRange second)
-{
- return first.begin == second.begin
- && first.end == second.end;
-}
-}
-}
diff --git a/tests/unit/unittest/googletest.h b/tests/unit/unittest/googletest.h
index 72ece6a602..a3d3012a09 100644
--- a/tests/unit/unittest/googletest.h
+++ b/tests/unit/unittest/googletest.h
@@ -13,8 +13,6 @@
#include <gtest/gtest-printers.h>
#include <gtest/gtest-typed-test.h>
-#include "compare-operators.h"
-
#include "conditionally-disabled-tests.h"
#include "gtest-creator-printing.h"
#include "gtest-llvm-printing.h"
diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp
index 85335f084d..602e973af1 100644
--- a/tests/unit/unittest/gtest-creator-printing.cpp
+++ b/tests/unit/unittest/gtest-creator-printing.cpp
@@ -21,7 +21,6 @@
#include <sqlitesessionchangeset.h>
#include <sqlitevalue.h>
#include <utils/fileutils.h>
-#include <utils/linecolumn.h>
#include <variantproperty.h>
#include <qmldesigner/designercore/imagecache/imagecachestorageinterface.h>
@@ -42,11 +41,6 @@ std::ostream &operator<<(std::ostream &out, const monostate &)
} // namespace std
namespace Utils {
-
-std::ostream &operator<<(std::ostream &out, const LineColumn &lineColumn)
-{
- return out << "(" << lineColumn.line << ", " << lineColumn.column << ")";
-}
namespace {
const char * toText(Utils::Language language)
{
@@ -454,6 +448,8 @@ const char *sourceTypeToText(SourceType sourceType)
return "QmlDir";
case SourceType::QmlTypes:
return "QmlTypes";
+ case SourceType::Directory:
+ return "Directory";
}
return "";
diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h
index d9d3cba559..4fb8b7d316 100644
--- a/tests/unit/unittest/gtest-creator-printing.h
+++ b/tests/unit/unittest/gtest-creator-printing.h
@@ -72,7 +72,6 @@ class LineColumn;
class SmallStringView;
class FilePath;
-std::ostream &operator<<(std::ostream &out, const LineColumn &lineColumn);
std::ostream &operator<<(std::ostream &out, const Utils::Language &language);
std::ostream &operator<<(std::ostream &out, const Utils::LanguageVersion &languageVersion);
std::ostream &operator<<(std::ostream &out, const Utils::LanguageExtension &languageExtension);
diff --git a/tests/unit/unittest/imagecachecollectormock.h b/tests/unit/unittest/imagecachecollectormock.h
index d31ff6daeb..81575b7604 100644
--- a/tests/unit/unittest/imagecachecollectormock.h
+++ b/tests/unit/unittest/imagecachecollectormock.h
@@ -19,7 +19,7 @@ public:
ImageCacheCollectorInterface::AbortCallback abortCallback),
(override));
- MOCK_METHOD(ImagePair,
+ MOCK_METHOD(ImageTuple,
createImage,
(Utils::SmallStringView filePath,
Utils::SmallStringView state,
diff --git a/tests/unit/unittest/imagecachedispatchcollector-test.cpp b/tests/unit/unittest/imagecachedispatchcollector-test.cpp
index 540641837c..756304e7d1 100644
--- a/tests/unit/unittest/imagecachedispatchcollector-test.cpp
+++ b/tests/unit/unittest/imagecachedispatchcollector-test.cpp
@@ -19,14 +19,16 @@ MATCHER_P(IsIcon, icon, std::string(negation ? "isn't " : "is ") + PrintToString
return icon.availableSizes() == other.availableSizes();
}
-MATCHER_P2(IsImage,
+MATCHER_P3(IsImage,
image,
+ midSizeImage,
smallImage,
- std::string(negation ? "aren't " : "are ") + PrintToString(image) + " and "
- + PrintToString(smallImage))
+ std::string(negation ? "aren't " : "are ") + PrintToString(image) + ", "
+ + PrintToString(midSizeImage) + " and " + PrintToString(smallImage))
{
- const std::pair<QImage, QImage> &other = arg;
- return other.first == image && other.second == smallImage;
+ const std::tuple<QImage, QImage, QImage> &other = arg;
+ return std::get<0>(other) == image && std::get<1>(other) == midSizeImage
+ && std::get<2>(other) == smallImage;
}
class ImageCacheDispatchCollector : public ::testing::Test
@@ -38,21 +40,23 @@ protected:
ON_CALL(collectorMock2, createIcon(_, _, _)).WillByDefault(Return(icon2));
ON_CALL(collectorMock1, createImage(_, _, _))
- .WillByDefault(Return(std::pair{image1, smallImage1}));
+ .WillByDefault(Return(std::tuple{image1, midSizeImage1, smallImage1}));
ON_CALL(collectorMock2, createImage(_, _, _))
- .WillByDefault(Return(std::pair{image2, smallImage2}));
+ .WillByDefault(Return(std::tuple{image2, midSizeImage2, smallImage2}));
}
protected:
std::vector<QSize> sizes{{20, 11}};
- NiceMock<MockFunction<void(const QImage &, const QImage &)>> captureCallbackMock;
+ NiceMock<MockFunction<void(const QImage &, const QImage &, const QImage &)>> captureCallbackMock;
NiceMock<MockFunction<void(QmlDesigner::ImageCache::AbortReason)>> abortCallbackMock;
NiceMock<ImageCacheCollectorMock> collectorMock1;
NiceMock<ImageCacheCollectorMock> collectorMock2;
QImage image1{1, 1, QImage::Format_ARGB32};
- QImage image2{2, 2, QImage::Format_ARGB32};
- QImage smallImage1{1, 1, QImage::Format_ARGB32};
- QImage smallImage2{2, 1, QImage::Format_ARGB32};
+ QImage image2{1, 2, QImage::Format_ARGB32};
+ QImage midSizeImage1{2, 1, QImage::Format_ARGB32};
+ QImage midSizeImage2{2, 2, QImage::Format_ARGB32};
+ QImage smallImage1{3, 1, QImage::Format_ARGB32};
+ QImage smallImage2{3, 2, QImage::Format_ARGB32};
QIcon icon1{QPixmap::fromImage(image1)};
QIcon icon2{QPixmap::fromImage(image2)};
};
@@ -79,7 +83,7 @@ TEST_F(ImageCacheDispatchCollector, CallQmlCollectorStart)
},
&collectorMock2))};
- EXPECT_CALL(captureCallbackMock, Call(_, _));
+ EXPECT_CALL(captureCallbackMock, Call(_, _, _));
EXPECT_CALL(abortCallbackMock, Call(_));
EXPECT_CALL(collectorMock2,
start(Eq("foo.qml"),
@@ -91,7 +95,7 @@ TEST_F(ImageCacheDispatchCollector, CallQmlCollectorStart)
_,
_))
.WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback) {
- captureCallback(QImage{}, QImage{});
+ captureCallback({}, {}, {});
abortCallback(QmlDesigner::ImageCache::AbortReason::Abort);
});
EXPECT_CALL(collectorMock1, start(_, _, _, _, _)).Times(0);
@@ -115,7 +119,7 @@ TEST_F(ImageCacheDispatchCollector, CallUiFileCollectorStart)
const QmlDesigner::ImageCache::AuxiliaryData &) { return true; },
&collectorMock2))};
- EXPECT_CALL(captureCallbackMock, Call(_, _));
+ EXPECT_CALL(captureCallbackMock, Call(_, _, _));
EXPECT_CALL(abortCallbackMock, Call(_));
EXPECT_CALL(collectorMock1,
start(Eq("foo.ui.qml"),
@@ -127,7 +131,7 @@ TEST_F(ImageCacheDispatchCollector, CallUiFileCollectorStart)
_,
_))
.WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback) {
- captureCallback(QImage{}, QImage{});
+ captureCallback({}, {}, {});
abortCallback(QmlDesigner::ImageCache::AbortReason::Abort);
});
EXPECT_CALL(collectorMock2, start(_, _, _, _, _)).Times(0);
@@ -288,7 +292,7 @@ TEST_F(ImageCacheDispatchCollector, CallFirstCollectorCreateImage)
"state",
FontCollectorSizesAuxiliaryData{sizes, "color", "text"});
- ASSERT_THAT(image, IsImage(image1, smallImage1));
+ ASSERT_THAT(image, IsImage(image1, midSizeImage1, smallImage1));
}
TEST_F(ImageCacheDispatchCollector, FirstCollectorCreateImageCalls)
@@ -334,7 +338,7 @@ TEST_F(ImageCacheDispatchCollector, CallSecondCollectorCreateImage)
"state",
FontCollectorSizesAuxiliaryData{sizes, "color", "text"});
- ASSERT_THAT(image, IsImage(image2, smallImage2));
+ ASSERT_THAT(image, IsImage(image2, midSizeImage2, smallImage2));
}
TEST_F(ImageCacheDispatchCollector, SecondCollectorCreateImageCalls)
@@ -380,7 +384,8 @@ TEST_F(ImageCacheDispatchCollector, DontCallCollectorCreateImageForUnknownFile)
"state",
FontCollectorSizesAuxiliaryData{sizes, "color", "text"});
- ASSERT_TRUE(image.first.isNull() && image.second.isNull());
+ ASSERT_TRUE(std::get<0>(image).isNull() && std::get<1>(image).isNull()
+ && std::get<2>(image).isNull());
}
} // namespace
diff --git a/tests/unit/unittest/imagecachegenerator-test.cpp b/tests/unit/unittest/imagecachegenerator-test.cpp
index e39def87d4..871a3768bb 100644
--- a/tests/unit/unittest/imagecachegenerator-test.cpp
+++ b/tests/unit/unittest/imagecachegenerator-test.cpp
@@ -32,8 +32,9 @@ protected:
Notification waitInThread;
Notification notification;
QImage image1{10, 10, QImage::Format_ARGB32};
+ QImage midSizeImage1{5, 5, QImage::Format_ARGB32};
QImage smallImage1{1, 1, QImage::Format_ARGB32};
- NiceMock<MockFunction<void(const QImage &, const QImage &)>> imageCallbackMock;
+ NiceMock<MockFunction<void(const QImage &, const QImage &, const QImage &)>> imageCallbackMock;
NiceMock<MockFunction<void(QmlDesigner::ImageCache::AbortReason)>> abortCallbackMock;
NiceMock<ImageCacheCollectorMock> collectorMock;
NiceMock<MockImageCacheStorage> storageMock;
@@ -44,11 +45,11 @@ TEST_F(ImageCacheGenerator, CallsCollectorWithCaptureCallback)
{
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
- EXPECT_CALL(imageCallbackMock, Call(_, _)).WillRepeatedly([&](const QImage &, const QImage &) {
- notification.notify();
- });
+ EXPECT_CALL(imageCallbackMock, Call(_, _, _))
+ .WillRepeatedly(
+ [&](const QImage &, const QImage &, const QImage &) { notification.notify(); });
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
notification.wait();
@@ -66,17 +67,16 @@ TEST_F(ImageCacheGenerator, CallsCollectorOnlyIfNotProcessing)
TEST_F(ImageCacheGenerator, ProcessTaskAfterFirstFinished)
{
- ON_CALL(imageCallbackMock, Call(_, _)).WillByDefault([&](const QImage &, const QImage &) {
- notification.notify();
- });
+ ON_CALL(imageCallbackMock, Call(_, _, _))
+ .WillByDefault([&](const QImage &, const QImage &, const QImage &) { notification.notify(); });
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillOnce([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _))
.WillOnce([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
@@ -88,7 +88,7 @@ TEST_F(ImageCacheGenerator, DontCrashAtDestructingGenerator)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
generator.generateImage(
@@ -105,12 +105,16 @@ TEST_F(ImageCacheGenerator, StoreImage)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
EXPECT_CALL(storageMock,
- storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(image1), Eq(smallImage1)))
- .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
+ storeImage(Eq("name"),
+ Eq(Sqlite::TimeStamp{11}),
+ Eq(image1),
+ Eq(midSizeImage1),
+ Eq(smallImage1)))
+ .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
generator.generateImage("name", {}, {11}, imageCallbackMock.AsStdFunction(), {}, {});
notification.wait();
@@ -120,12 +124,16 @@ TEST_F(ImageCacheGenerator, StoreImageWithExtraId)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
EXPECT_CALL(storageMock,
- storeImage(Eq("name+extraId"), Eq(Sqlite::TimeStamp{11}), Eq(image1), Eq(smallImage1)))
- .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
+ storeImage(Eq("name+extraId"),
+ Eq(Sqlite::TimeStamp{11}),
+ Eq(image1),
+ Eq(midSizeImage1),
+ Eq(smallImage1)))
+ .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
generator.generateImage("name", "extraId", {11}, imageCallbackMock.AsStdFunction(), {}, {});
notification.wait();
@@ -135,12 +143,13 @@ TEST_F(ImageCacheGenerator, StoreNullImage)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{}, QImage{});
+ captureCallback(QImage{}, QImage{}, QImage{});
});
- EXPECT_CALL(storageMock,
- storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{})))
- .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
+ EXPECT_CALL(
+ storageMock,
+ storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}), Eq(QImage{})))
+ .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
generator.generateImage(
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
@@ -151,12 +160,16 @@ TEST_F(ImageCacheGenerator, StoreNullImageWithExtraId)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{}, QImage{});
+ captureCallback(QImage{}, QImage{}, QImage{});
});
EXPECT_CALL(storageMock,
- storeImage(Eq("name+extraId"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{})))
- .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
+ storeImage(Eq("name+extraId"),
+ Eq(Sqlite::TimeStamp{11}),
+ Eq(QImage{}),
+ Eq(QImage{}),
+ Eq(QImage{})))
+ .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
generator.generateImage("name",
"extraId",
@@ -171,16 +184,15 @@ TEST_F(ImageCacheGenerator, AbortCallback)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto, auto abortCallback) {
abortCallback(QmlDesigner::ImageCache::AbortReason::Failed);
});
- EXPECT_CALL(imageCallbackMock, Call(_, _)).WillOnce([&](const QImage &, const QImage &) {
- notification.notify();
- });
+ EXPECT_CALL(imageCallbackMock, Call(_, _, _))
+ .WillOnce([&](const QImage &, const QImage &, const QImage &) { notification.notify(); });
EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)))
.WillOnce([&](auto) { notification.notify(); });
@@ -200,7 +212,7 @@ TEST_F(ImageCacheGenerator, StoreNullImageForAbortCallbackAbort)
ON_CALL(collectorMock, start(Eq("dummyNotify"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); });
- EXPECT_CALL(storageMock, storeImage(Eq("name"), _, _, _)).Times(0);
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), _, _, _, _)).Times(0);
generator.generateImage(
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
@@ -215,20 +227,21 @@ TEST_F(ImageCacheGenerator, DontStoreNullImageForAbortCallbackFailed)
abortCallback(QmlDesigner::ImageCache::AbortReason::Failed);
});
- EXPECT_CALL(storageMock,
- storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{})))
- .WillOnce([&](auto, auto, auto, auto) { notification.notify(); });
+ EXPECT_CALL(
+ storageMock,
+ storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}), Eq(QImage{})))
+ .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); });
generator.generateImage(
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
notification.wait();
}
-TEST_F(ImageCacheGenerator, AbortForEmptyImage)
+TEST_F(ImageCacheGenerator, AbortForNullImage)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{}, QImage{});
+ captureCallback(QImage{}, QImage{}, QImage{});
});
EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed)))
@@ -239,10 +252,99 @@ TEST_F(ImageCacheGenerator, AbortForEmptyImage)
notification.wait();
}
+TEST_F(ImageCacheGenerator, CallImageCallbackIfSmallImageIsNotNull)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
+ .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
+ captureCallback({}, {}, smallImage1);
+ });
+
+ EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(QImage()), Eq(smallImage1)))
+ .WillOnce([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage(
+ "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, StoreImageIfSmallImageIsNotNull)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
+ .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
+ captureCallback({}, {}, smallImage1);
+ });
+
+ EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(QImage()), Eq(smallImage1)))
+ .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage(
+ "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, CallImageCallbackIfMidSizeImageIsNotNull)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
+ .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
+ captureCallback({}, midSizeImage1, {});
+ });
+
+ EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(midSizeImage1), Eq(QImage{})))
+ .WillOnce([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage(
+ "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, StoreImageIfMidSizeImageIsNotNull)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
+ .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
+ captureCallback({}, midSizeImage1, {});
+ });
+
+ EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(midSizeImage1), Eq(QImage())))
+ .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage(
+ "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, CallImageCallbackIfImageIsNotNull)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
+ .WillByDefault(
+ [&](auto, auto, auto, auto captureCallback, auto) { captureCallback(image1, {}, {}); });
+
+ EXPECT_CALL(imageCallbackMock, Call(Eq(image1), Eq(QImage{}), Eq(QImage{})))
+ .WillOnce([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage(
+ "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, StoreImageIfImageIsNotNull)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
+ .WillByDefault(
+ [&](auto, auto, auto, auto captureCallback, auto) { captureCallback(image1, {}, {}); });
+
+ EXPECT_CALL(storageMock, storeImage(_, _, Eq(image1), Eq(QImage{}), Eq(QImage{})))
+ .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage(
+ "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
TEST_F(ImageCacheGenerator, CallWalCheckpointFullIfQueueIsEmpty)
{
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
- .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { captureCallback({}, {}); });
+ .WillByDefault(
+ [&](auto, auto, auto, auto captureCallback, auto) { captureCallback({}, {}, {}); });
EXPECT_CALL(storageMock, walCheckpointFull()).WillRepeatedly([&]() { notification.notify(); });
@@ -277,11 +379,11 @@ TEST_F(ImageCacheGenerator, WaitForFinished)
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
waitInThread.wait();
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{image1}, QImage{smallImage1});
+ captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
generator.generateImage(
@@ -289,7 +391,7 @@ TEST_F(ImageCacheGenerator, WaitForFinished)
generator.generateImage(
"name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
- EXPECT_CALL(imageCallbackMock, Call(_, _)).Times(2);
+ EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(2);
waitInThread.notify();
generator.waitForFinished();
@@ -407,7 +509,7 @@ TEST_F(ImageCacheGenerator, UseLastTimeStampIfTasksAreMerged)
abortCallback(QmlDesigner::ImageCache::AbortReason::Failed);
});
- EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{4}), _, _));
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{4}), _, _, _));
generator.generateImage("waitDummy", {}, {}, {}, {}, {});
generator.generateImage("name", {}, {3}, {}, abortCallbackMock.AsStdFunction(), {});
@@ -419,18 +521,18 @@ TEST_F(ImageCacheGenerator, UseLastTimeStampIfTasksAreMerged)
TEST_F(ImageCacheGenerator, MergeCaptureCallbackIfTasksAreMerged)
{
- NiceMock<MockFunction<void(const QImage &, const QImage &)>> newerImageCallbackMock;
+ NiceMock<MockFunction<void(const QImage &, const QImage &, const QImage &)>> newerImageCallbackMock;
ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto, auto) { waitInThread.wait(); });
ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); });
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
.WillByDefault([&](auto, auto, auto, auto imageCallback, auto) {
- imageCallback(QImage{image1}, QImage{smallImage1});
+ imageCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1});
});
- EXPECT_CALL(imageCallbackMock, Call(_, _));
- EXPECT_CALL(newerImageCallbackMock, Call(_, _));
+ EXPECT_CALL(imageCallbackMock, Call(_, _, _));
+ EXPECT_CALL(newerImageCallbackMock, Call(_, _, _));
generator.generateImage("waitDummy", {}, {}, {}, {}, {});
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
@@ -467,7 +569,7 @@ TEST_F(ImageCacheGenerator, DontCallNullImageCallback)
{
EXPECT_CALL(collectorMock, start(_, _, _, _, _))
.WillOnce([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(image1, smallImage1);
+ captureCallback(image1, midSizeImage1, smallImage1);
notification.notify();
});
@@ -479,7 +581,7 @@ TEST_F(ImageCacheGenerator, DontCallNullAbortCallbackForNullImage)
{
EXPECT_CALL(collectorMock, start(_, _, _, _, _))
.WillOnce([&](auto, auto, auto, auto captureCallback, auto) {
- captureCallback(QImage{}, QImage{});
+ captureCallback(QImage{}, QImage{}, QImage{});
notification.notify();
});
diff --git a/tests/unit/unittest/imagecachestorage-test.cpp b/tests/unit/unittest/imagecachestorage-test.cpp
index 4ad6a90eae..f596fce9d2 100644
--- a/tests/unit/unittest/imagecachestorage-test.cpp
+++ b/tests/unit/unittest/imagecachestorage-test.cpp
@@ -14,6 +14,83 @@ MATCHER_P(IsIcon, icon, std::string(negation ? "is't" : "is") + PrintToString(ic
{
return arg.availableSizes() == icon.availableSizes();
}
+
+TEST(ImageCacheStorageUpdateTest, CheckVersionIfDatabaseIsAlreadyInitialized)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true));
+
+ EXPECT_CALL(databaseMock, version());
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST(ImageCacheStorageUpdateTest, AddColumnMidSizeIfVersionIsZero)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true));
+ EXPECT_CALL(databaseMock, execute(_)).Times(AnyNumber());
+
+ EXPECT_CALL(databaseMock, execute(Eq("ALTER TABLE images ADD COLUMN midSizeImage")));
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST(ImageCacheStorageUpdateTest, DeleteAllRowsBeforeAddingMidSizeColumn)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true));
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, execute(Eq("DELETE FROM images")));
+ EXPECT_CALL(databaseMock, execute(Eq("ALTER TABLE images ADD COLUMN midSizeImage")));
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST(ImageCacheStorageUpdateTest, DontCallAddColumnMidSizeIfDatabaseWasNotAlreadyInitialized)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(false));
+ EXPECT_CALL(databaseMock, execute(_)).Times(2);
+
+ EXPECT_CALL(databaseMock, execute(Eq("ALTER TABLE images ADD COLUMN midSizeImage"))).Times(0);
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST(ImageCacheStorageUpdateTest, SetVersionToOneIfVersionIsZero)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true));
+
+ EXPECT_CALL(databaseMock, setVersion(Eq(1)));
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST(ImageCacheStorageUpdateTest, DontSetVersionIfVersionIsOne)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true));
+ ON_CALL(databaseMock, version()).WillByDefault(Return(1));
+
+ EXPECT_CALL(databaseMock, setVersion(_)).Times(0);
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST(ImageCacheStorageUpdateTest, SetVersionToOneForInitialization)
+{
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(false));
+ ON_CALL(databaseMock, version()).WillByDefault(Return(1));
+
+ EXPECT_CALL(databaseMock, setVersion(Eq(1)));
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
class ImageCacheStorageTest : public testing::Test
{
protected:
@@ -26,12 +103,14 @@ protected:
NiceMock<SqliteDatabaseMock> databaseMock;
QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
ReadStatement<1, 2> &selectImageStatement = storage.selectImageStatement;
+ ReadStatement<1, 2> &selectMidSizeImageStatement = storage.selectMidSizeImageStatement;
ReadStatement<1, 2> &selectSmallImageStatement = storage.selectSmallImageStatement;
ReadStatement<1, 2> &selectIconStatement = storage.selectIconStatement;
- WriteStatement<4> &upsertImageStatement = storage.upsertImageStatement;
+ WriteStatement<5> &upsertImageStatement = storage.upsertImageStatement;
WriteStatement<3> &upsertIconStatement = storage.upsertIconStatement;
QImage image1{10, 10, QImage::Format_ARGB32};
- QImage smallImage1{10, 10, QImage::Format_ARGB32};
+ QImage midSizeImage1{5, 5, QImage::Format_ARGB32};
+ QImage smallImage1{1, 1, QImage::Format_ARGB32};
QIcon icon1{QPixmap::fromImage(image1)};
};
@@ -67,6 +146,38 @@ TEST_F(ImageCacheStorageTest, FetchImageCallsIsBusy)
storage.fetchImage("/path/to/component", {123});
}
+TEST_F(ImageCacheStorageTest, FetchMidSizeImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectMidSizeImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchMidSizeImage("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchMidSizeImageCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectMidSizeImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)))
+ .WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, rollback());
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectMidSizeImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchMidSizeImage("/path/to/component", {123});
+}
+
TEST_F(ImageCacheStorageTest, FetchSmallImageCalls)
{
InSequence s;
@@ -140,10 +251,11 @@ TEST_F(ImageCacheStorageTest, StoreImageCalls)
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
TypedEq<long long>(123),
Not(IsEmpty()),
+ Not(IsEmpty()),
Not(IsEmpty())));
EXPECT_CALL(databaseMock, commit());
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
}
TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls)
@@ -155,10 +267,11 @@ TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls)
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
TypedEq<long long>(123),
IsEmpty(),
+ IsEmpty(),
IsEmpty()));
EXPECT_CALL(databaseMock, commit());
- storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
+ storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{});
}
TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy)
@@ -171,10 +284,11 @@ TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy)
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
TypedEq<long long>(123),
IsEmpty(),
+ IsEmpty(),
IsEmpty()));
EXPECT_CALL(databaseMock, commit());
- storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
+ storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{});
}
TEST_F(ImageCacheStorageTest, StoreIconCalls)
@@ -253,29 +367,62 @@ protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
QmlDesigner::ImageCacheStorage<Sqlite::Database> storage{database};
QImage image1{createImage()};
- QImage smallImage1{image1.scaled(96, 96)};
+ QImage midSizeImage1{image1.scaled(96, 96)};
+ QImage smallImage1{image1.scaled(48, 48)};
QIcon icon1{QPixmap::fromImage(image1)};
};
TEST_F(ImageCacheStorageSlowTest, StoreImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, QImage{}, QImage{});
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), Optional(image1));
}
TEST_F(ImageCacheStorageSlowTest, StoreEmptyImageAfterEntry)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
- storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
+ storage.storeImage("/path/to/component", {123}, QImage{}, midSizeImage1, smallImage1);
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), Optional(QImage{}));
}
+TEST_F(ImageCacheStorageSlowTest, StoreMidSizeImage)
+{
+ storage.storeImage("/path/to/component", {123}, QImage{}, midSizeImage1, QImage{});
+
+ ASSERT_THAT(storage.fetchMidSizeImage("/path/to/component", {123}), Optional(midSizeImage1));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreEmptyMidSizeImageAfterEntry)
+{
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
+
+ storage.storeImage("/path/to/component", {123}, image1, QImage{}, smallImage1);
+
+ ASSERT_THAT(storage.fetchMidSizeImage("/path/to/component", {123}), Optional(QImage{}));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreSmallImage)
+{
+ storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, smallImage1);
+
+ ASSERT_THAT(storage.fetchSmallImage("/path/to/component", {123}), Optional(smallImage1));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreEmptySmallImageAfterEntry)
+{
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
+
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, QImage{});
+
+ ASSERT_THAT(storage.fetchSmallImage("/path/to/component", {123}), Optional(QImage{}));
+}
+
TEST_F(ImageCacheStorageSlowTest, StoreEmptyEntry)
{
- storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
+ storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{});
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), Optional(QImage{}));
}
@@ -289,7 +436,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchNonExistingImageIsEmpty)
TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto image = storage.fetchImage("/path/to/component", {123});
@@ -298,7 +445,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage)
TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto image = storage.fetchImage("/path/to/component", {124});
@@ -307,13 +454,47 @@ TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage)
TEST_F(ImageCacheStorageSlowTest, FetchNewerImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto image = storage.fetchImage("/path/to/component", {122});
ASSERT_THAT(image, Optional(image1));
}
+TEST_F(ImageCacheStorageSlowTest, FetchNonExistingMidSizeImageIsEmpty)
+{
+ auto image = storage.fetchMidSizeImage("/path/to/component", {123});
+
+ ASSERT_THAT(image, Eq(std::nullopt));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchSameTimeMidSizeImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
+
+ auto image = storage.fetchMidSizeImage("/path/to/component", {123});
+
+ ASSERT_THAT(image, Optional(midSizeImage1));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderMidSizeImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
+
+ auto image = storage.fetchMidSizeImage("/path/to/component", {124});
+
+ ASSERT_THAT(image, Eq(std::nullopt));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNewerMidSizeImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
+
+ auto image = storage.fetchMidSizeImage("/path/to/component", {122});
+
+ ASSERT_THAT(image, Optional(midSizeImage1));
+}
+
TEST_F(ImageCacheStorageSlowTest, FetchNonExistingSmallImageIsEmpty)
{
auto image = storage.fetchSmallImage("/path/to/component", {123});
@@ -323,7 +504,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchNonExistingSmallImageIsEmpty)
TEST_F(ImageCacheStorageSlowTest, FetchSameTimeSmallImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto image = storage.fetchSmallImage("/path/to/component", {123});
@@ -332,7 +513,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchSameTimeSmallImage)
TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderSmallImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto image = storage.fetchSmallImage("/path/to/component", {124});
@@ -341,7 +522,7 @@ TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderSmallImage)
TEST_F(ImageCacheStorageSlowTest, FetchNewerSmallImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto image = storage.fetchSmallImage("/path/to/component", {122});
@@ -407,7 +588,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchNewerIcon)
TEST_F(ImageCacheStorageSlowTest, FetchModifiedImageTime)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto timeStamp = storage.fetchModifiedImageTime("/path/to/component");
@@ -416,7 +597,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchModifiedImageTime)
TEST_F(ImageCacheStorageSlowTest, FetchInvalidModifiedImageTimeForNoEntry)
{
- storage.storeImage("/path/to/component2", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component2", {123}, image1, midSizeImage1, smallImage1);
auto timeStamp = storage.fetchModifiedImageTime("/path/to/component");
@@ -425,7 +606,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchInvalidModifiedImageTimeForNoEntry)
TEST_F(ImageCacheStorageSlowTest, FetchHasImage)
{
- storage.storeImage("/path/to/component", {123}, image1, smallImage1);
+ storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1);
auto hasImage = storage.fetchHasImage("/path/to/component");
@@ -434,7 +615,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchHasImage)
TEST_F(ImageCacheStorageSlowTest, FetchHasImageForNullImage)
{
- storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
+ storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{});
auto hasImage = storage.fetchHasImage("/path/to/component");
@@ -443,7 +624,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchHasImageForNullImage)
TEST_F(ImageCacheStorageSlowTest, FetchHasImageForNoEntry)
{
- storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
+ storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{});
auto hasImage = storage.fetchHasImage("/path/to/component");
diff --git a/tests/unit/unittest/mockimagecachegenerator.h b/tests/unit/unittest/mockimagecachegenerator.h
index f466e29a48..e7118de566 100644
--- a/tests/unit/unittest/mockimagecachegenerator.h
+++ b/tests/unit/unittest/mockimagecachegenerator.h
@@ -15,7 +15,7 @@ public:
(Utils::SmallStringView name,
Utils::SmallStringView state,
Sqlite::TimeStamp timeStamp,
- QmlDesigner::ImageCache::CaptureImageWithSmallImageCallback &&captureCallback,
+ QmlDesigner::ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback,
QmlDesigner::ImageCache::AbortCallback &&abortCallback,
QmlDesigner::ImageCache::AuxiliaryData &&auxiliaryData),
(override));
diff --git a/tests/unit/unittest/mockimagecachestorage.h b/tests/unit/unittest/mockimagecachestorage.h
index 7e3ad3d2d7..9eb8af5372 100644
--- a/tests/unit/unittest/mockimagecachestorage.h
+++ b/tests/unit/unittest/mockimagecachestorage.h
@@ -16,6 +16,11 @@ public:
(const, override));
MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::ImageEntry,
+ fetchMidSizeImage,
+ (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
+ (const, override));
+
+ MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::ImageEntry,
fetchSmallImage,
(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
(const, override));
@@ -30,6 +35,7 @@ public:
(Utils::SmallStringView name,
Sqlite::TimeStamp newTimeStamp,
const QImage &image,
+ const QImage &midSizeImage,
const QImage &smallImage),
(override));
diff --git a/tests/unit/unittest/projectstoragepathwatcher-test.cpp b/tests/unit/unittest/projectstoragepathwatcher-test.cpp
index 6b13075ff0..ad5e0d1487 100644
--- a/tests/unit/unittest/projectstoragepathwatcher-test.cpp
+++ b/tests/unit/unittest/projectstoragepathwatcher-test.cpp
@@ -7,18 +7,19 @@
#include "mockqfilesystemwatcher.h"
#include "mocktimer.h"
#include "projectstoragepathwatchernotifiermock.h"
-#include "sourcepathcachemock.h"
+#include <projectstorage/projectstorage.h>
#include <projectstorage/projectstoragepathwatcher.h>
-#include <projectstorage/sourcepath.h>
+#include <projectstorage/sourcepathcache.h>
+#include <sqlitedatabase.h>
#include <utils/smallstring.h>
namespace {
-
+using SourcePathCache = QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>>;
using Watcher = QmlDesigner::ProjectStoragePathWatcher<NiceMock<MockQFileSytemWatcher>,
NiceMock<MockTimer>,
- NiceMock<SourcePathCacheMock>>;
+ SourcePathCache>;
using QmlDesigner::FileStatus;
using QmlDesigner::IdPaths;
using QmlDesigner::ProjectChunkId;
@@ -40,47 +41,16 @@ class ProjectStoragePathWatcher : public testing::Test
protected:
ProjectStoragePathWatcher()
{
- ON_CALL(sourcePathCacheMock, sourceId(Eq(path1))).WillByDefault(Return(pathIds[0]));
- ON_CALL(sourcePathCacheMock, sourceId(Eq(path2))).WillByDefault(Return(pathIds[1]));
- ON_CALL(sourcePathCacheMock, sourceId(Eq(path3))).WillByDefault(Return(pathIds[2]));
- ON_CALL(sourcePathCacheMock, sourceId(Eq(path4))).WillByDefault(Return(pathIds[3]));
- ON_CALL(sourcePathCacheMock, sourceId(Eq(path5))).WillByDefault(Return(pathIds[4]));
- ON_CALL(sourcePathCacheMock, sourcePath(Eq(pathIds[0]))).WillByDefault(Return(SourcePath{path1}));
- ON_CALL(sourcePathCacheMock, sourcePath(Eq(pathIds[1]))).WillByDefault(Return(SourcePath{path2}));
- ON_CALL(sourcePathCacheMock, sourcePath(Eq(pathIds[2]))).WillByDefault(Return(SourcePath{path3}));
- ON_CALL(sourcePathCacheMock, sourcePath(Eq(pathIds[3]))).WillByDefault(Return(SourcePath{path4}));
- ON_CALL(sourcePathCacheMock, sourcePath(Eq(pathIds[4]))).WillByDefault(Return(SourcePath{path5}));
- ON_CALL(sourcePathCacheMock, sourceContextId(TypedEq<SourceId>(pathIds[0])))
- .WillByDefault(Return(sourceContextIds[0]));
- ON_CALL(sourcePathCacheMock, sourceContextId(TypedEq<SourceId>(pathIds[1])))
- .WillByDefault(Return(sourceContextIds[0]));
- ON_CALL(sourcePathCacheMock, sourceContextId(TypedEq<SourceId>(pathIds[2])))
- .WillByDefault(Return(sourceContextIds[1]));
- ON_CALL(sourcePathCacheMock, sourceContextId(TypedEq<SourceId>(pathIds[3])))
- .WillByDefault(Return(sourceContextIds[1]));
- ON_CALL(sourcePathCacheMock, sourceContextId(TypedEq<SourceId>(pathIds[4])))
- .WillByDefault(Return(sourceContextIds[2]));
ON_CALL(mockFileSystem, fileStatus(_)).WillByDefault([](auto sourceId) {
return FileStatus{sourceId, 1, 1};
});
- ON_CALL(sourcePathCacheMock,
- sourceContextId(TypedEq<Utils::SmallStringView>(sourceContextPathString)))
- .WillByDefault(Return(sourceContextIds[0]));
- ON_CALL(sourcePathCacheMock,
- sourceContextId(TypedEq<Utils::SmallStringView>(sourceContextPathString2)))
- .WillByDefault(Return(sourceContextIds[1]));
- ON_CALL(sourcePathCacheMock, sourceContextPath(Eq(sourceContextIds[0])))
- .WillByDefault(Return(sourceContextPath));
- ON_CALL(sourcePathCacheMock, sourceContextPath(Eq(sourceContextIds[1])))
- .WillByDefault(Return(sourceContextPath2));
- ON_CALL(sourcePathCacheMock, sourceContextPath(Eq(sourceContextIds[2])))
- .WillByDefault(Return(sourceContextPath3));
+
ON_CALL(mockFileSystem, directoryEntries(Eq(sourceContextPath)))
- .WillByDefault(Return(SourceIds{pathIds[0], pathIds[1]}));
+ .WillByDefault(Return(SourceIds{sourceIds[0], sourceIds[1]}));
ON_CALL(mockFileSystem, directoryEntries(Eq(sourceContextPath2)))
- .WillByDefault(Return(SourceIds{pathIds[2], pathIds[3]}));
+ .WillByDefault(Return(SourceIds{sourceIds[2], sourceIds[3]}));
ON_CALL(mockFileSystem, directoryEntries(Eq(sourceContextPath3)))
- .WillByDefault(Return(SourceIds{pathIds[4]}));
+ .WillByDefault(Return(SourceIds{sourceIds[4]}));
}
static WatcherEntries sorted(WatcherEntries &&entries)
{
@@ -90,14 +60,17 @@ protected:
}
protected:
- NiceMock<SourcePathCacheMock> sourcePathCacheMock;
NiceMock<ProjectStoragePathWatcherNotifierMock> notifier;
NiceMock<FileSystemMock> mockFileSystem;
- Watcher watcher{sourcePathCacheMock, mockFileSystem, &notifier};
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()};
+ SourcePathCache pathCache{storage};
+ Watcher watcher{pathCache, mockFileSystem, &notifier};
NiceMock<MockQFileSytemWatcher> &mockQFileSytemWatcher = watcher.fileSystemWatcher();
- ProjectChunkId id1{ProjectPartId::create(2), SourceType::Qml};
- ProjectChunkId id2{ProjectPartId::create(2), SourceType::QmlUi};
- ProjectChunkId id3{ProjectPartId::create(4), SourceType::QmlTypes};
+ ProjectChunkId projectChunkId1{ProjectPartId::create(2), SourceType::Qml};
+ ProjectChunkId projectChunkId2{ProjectPartId::create(2), SourceType::QmlUi};
+ ProjectChunkId projectChunkId3{ProjectPartId::create(3), SourceType::QmlTypes};
+ ProjectChunkId projectChunkId4{ProjectPartId::create(4), SourceType::Qml};
SourcePathView path1{"/path/path1"};
SourcePathView path2{"/path/path2"};
SourcePathView path3{"/path2/path1"};
@@ -110,23 +83,28 @@ protected:
QString sourceContextPath3 = "/path3";
Utils::PathString sourceContextPathString = sourceContextPath;
Utils::PathString sourceContextPathString2 = sourceContextPath2;
- SourceIds pathIds = {SourceId::create(1),
- SourceId::create(2),
- SourceId::create(3),
- SourceId::create(4),
- SourceId::create(5)};
- SourceContextIds sourceContextIds = {SourceContextId::create(1),
- SourceContextId::create(2),
- SourceContextId::create(3)};
- ProjectChunkIds ids{id1, id2, id3};
- WatcherEntry watcherEntry1{id1, sourceContextIds[0], pathIds[0]};
- WatcherEntry watcherEntry2{id2, sourceContextIds[0], pathIds[0]};
- WatcherEntry watcherEntry3{id1, sourceContextIds[0], pathIds[1]};
- WatcherEntry watcherEntry4{id2, sourceContextIds[0], pathIds[1]};
- WatcherEntry watcherEntry5{id3, sourceContextIds[0], pathIds[1]};
- WatcherEntry watcherEntry6{id1, sourceContextIds[1], pathIds[2]};
- WatcherEntry watcherEntry7{id2, sourceContextIds[1], pathIds[3]};
- WatcherEntry watcherEntry8{id3, sourceContextIds[1], pathIds[3]};
+ SourceIds sourceIds = {pathCache.sourceId(path1),
+ pathCache.sourceId(path2),
+ pathCache.sourceId(path3),
+ pathCache.sourceId(path4),
+ pathCache.sourceId(path5)};
+ SourceContextIds sourceContextIds = {pathCache.sourceContextId(sourceIds[0]),
+ pathCache.sourceContextId(sourceIds[2]),
+ pathCache.sourceContextId(sourceIds[4])};
+ ProjectChunkIds ids{projectChunkId1, projectChunkId2, projectChunkId3};
+ WatcherEntry watcherEntry1{projectChunkId1, sourceContextIds[0], sourceIds[0]};
+ WatcherEntry watcherEntry2{projectChunkId2, sourceContextIds[0], sourceIds[0]};
+ WatcherEntry watcherEntry3{projectChunkId1, sourceContextIds[0], sourceIds[1]};
+ WatcherEntry watcherEntry4{projectChunkId2, sourceContextIds[0], sourceIds[1]};
+ WatcherEntry watcherEntry5{projectChunkId3, sourceContextIds[0], sourceIds[1]};
+ WatcherEntry watcherEntry6{projectChunkId1, sourceContextIds[1], sourceIds[2]};
+ WatcherEntry watcherEntry7{projectChunkId2, sourceContextIds[1], sourceIds[3]};
+ WatcherEntry watcherEntry8{projectChunkId3, sourceContextIds[1], sourceIds[3]};
+ WatcherEntry watcherEntry9{projectChunkId4, sourceContextIds[0], sourceIds[0]};
+ WatcherEntry watcherEntry10{projectChunkId4, sourceContextIds[0], sourceIds[1]};
+ WatcherEntry watcherEntry11{projectChunkId4, sourceContextIds[1], sourceIds[2]};
+ WatcherEntry watcherEntry12{projectChunkId4, sourceContextIds[1], sourceIds[3]};
+ WatcherEntry watcherEntry13{projectChunkId4, sourceContextIds[2], sourceIds[4]};
};
TEST_F(ProjectStoragePathWatcher, AddIdPaths)
@@ -135,53 +113,59 @@ TEST_F(ProjectStoragePathWatcher, AddIdPaths)
addPaths(
UnorderedElementsAre(QString(sourceContextPath), QString(sourceContextPath2))));
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
}
TEST_F(ProjectStoragePathWatcher, UpdateIdPathsCallsAddPathInFileWatcher)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(UnorderedElementsAre(QString(sourceContextPath2))));
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
}
TEST_F(ProjectStoragePathWatcher, UpdateIdPathsAndRemoveUnusedPathsCallsRemovePathInFileWatcher)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(UnorderedElementsAre(QString(sourceContextPath2))));
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1]}}});
}
TEST_F(ProjectStoragePathWatcher, UpdateIdPathsAndRemoveUnusedPathsDoNotCallsRemovePathInFileWatcher)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2]}},
- {id2, {pathIds[0], pathIds[1], pathIds[3]}},
- {id3, {pathIds[0]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}},
+ {projectChunkId3, {sourceIds[0]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)).Times(0);
- watcher.updateIdPaths({{id1, {pathIds[1]}}, {id2, {pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[1]}}, {projectChunkId2, {sourceIds[3]}}});
}
TEST_F(ProjectStoragePathWatcher, UpdateIdPathsAndRemoveUnusedPaths)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId3, {sourceIds[1]}}});
- watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0]}}, {projectChunkId2, {sourceIds[1]}}});
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry4, watcherEntry5));
}
TEST_F(ProjectStoragePathWatcher, ExtractSortedEntriesFromConvertIdPaths)
{
- auto entriesAndIds = watcher.convertIdPathsToWatcherEntriesAndIds({{id2, {pathIds[0], pathIds[1]}}, {id1, {pathIds[0], pathIds[1]}}});
+ auto entriesAndIds = watcher.convertIdPathsToWatcherEntriesAndIds(
+ {{projectChunkId2, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId1, {sourceIds[0], sourceIds[1]}}});
ASSERT_THAT(entriesAndIds.first,
ElementsAre(watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4));
@@ -189,23 +173,24 @@ TEST_F(ProjectStoragePathWatcher, ExtractSortedEntriesFromConvertIdPaths)
TEST_F(ProjectStoragePathWatcher, ExtractSortedIdsFromConvertIdPaths)
{
- auto entriesAndIds = watcher.convertIdPathsToWatcherEntriesAndIds({{id2, {}}, {id1, {}}, {id3, {}}});
+ auto entriesAndIds = watcher.convertIdPathsToWatcherEntriesAndIds(
+ {{projectChunkId2, {}}, {projectChunkId1, {}}, {projectChunkId3, {}}});
ASSERT_THAT(entriesAndIds.second, ElementsAre(ids[0], ids[1], ids[2]));
}
TEST_F(ProjectStoragePathWatcher, MergeEntries)
{
- watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0]}}, {projectChunkId2, {sourceIds[1]}}});
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry4));
}
TEST_F(ProjectStoragePathWatcher, MergeMoreEntries)
{
- watcher.updateIdPaths({{id2, {pathIds[0], pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId2, {sourceIds[0], sourceIds[1]}}});
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}}});
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4));
}
@@ -223,39 +208,44 @@ TEST_F(ProjectStoragePathWatcher, AddEntriesWithSameIdAndDifferentPaths)
EXPECT_CALL(mockQFileSytemWatcher,
addPaths(ElementsAre(sourceContextPath, sourceContextPath2, sourceContextPath3)));
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[4]}}});
+ watcher.updateIdPaths(
+ {{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[4]}}});
}
TEST_F(ProjectStoragePathWatcher, AddEntriesWithDifferentIdAndSamePaths)
{
EXPECT_CALL(mockQFileSytemWatcher, addPaths(ElementsAre(sourceContextPath)));
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}}});
}
TEST_F(ProjectStoragePathWatcher, DontAddNewEntriesWithSameIdAndSamePaths)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
+ watcher.updateIdPaths(
+ {{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3], sourceIds[4]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)).Times(0);
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
+ watcher.updateIdPaths(
+ {{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3], sourceIds[4]}}});
}
TEST_F(ProjectStoragePathWatcher, DontAddNewEntriesWithDifferentIdAndSamePaths)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
+ watcher.updateIdPaths(
+ {{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3], sourceIds[4]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)).Times(0);
- watcher.updateIdPaths({{id2, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
+ watcher.updateIdPaths(
+ {{projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3], sourceIds[4]}}});
}
TEST_F(ProjectStoragePathWatcher, RemoveEntriesWithId)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}},
- {id2, {pathIds[0], pathIds[1]}},
- {id3, {pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId3, {sourceIds[1], sourceIds[3]}}});
watcher.removeIds({ProjectPartId::create(2)});
@@ -272,62 +262,67 @@ TEST_F(ProjectStoragePathWatcher, RemoveNoPathsForEmptyIds)
TEST_F(ProjectStoragePathWatcher, RemoveNoPathsForOneId)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(_))
.Times(0);
- watcher.removeIds({id3.id});
+ watcher.removeIds({projectChunkId3.id});
}
TEST_F(ProjectStoragePathWatcher, RemovePathForOneId)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1]}}, {id3, {pathIds[0], pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId3, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(sourceContextPath2)));
- watcher.removeIds({id3.id});
+ watcher.removeIds({projectChunkId3.id});
}
TEST_F(ProjectStoragePathWatcher, RemoveNoPathSecondTime)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
- watcher.removeIds({id2.id});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
+ watcher.removeIds({projectChunkId2.id});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)).Times(0);
- watcher.removeIds({id2.id});
+ watcher.removeIds({projectChunkId2.id});
}
TEST_F(ProjectStoragePathWatcher, RemoveAllPathsForThreeId)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher,
removePaths(ElementsAre(sourceContextPath, sourceContextPath2)));
- watcher.removeIds({id1.id, id2.id, id3.id});
+ watcher.removeIds({projectChunkId1.id, projectChunkId2.id, projectChunkId3.id});
}
TEST_F(ProjectStoragePathWatcher, RemoveOnePathForTwoId)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1]}},
+ {projectChunkId3, {sourceIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(sourceContextPath)));
- watcher.removeIds({id1.id, id2.id});
+ watcher.removeIds({projectChunkId1.id, projectChunkId2.id});
}
TEST_F(ProjectStoragePathWatcher, NotAnymoreWatchedEntriesWithId)
{
+ auto notContainsdId = [&](WatcherEntry entry) {
+ return entry.id != ids[0] && entry.id != ids[1];
+ };
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}));
- auto oldEntries = watcher.notAnymoreWatchedEntriesWithIds({watcherEntry1, watcherEntry4}, {ids[0], ids[1]});
+ auto oldEntries = watcher.notAnymoreWatchedEntriesWithIds({watcherEntry1, watcherEntry4},
+ notContainsdId);
ASSERT_THAT(oldEntries, ElementsAre(watcherEntry2, watcherEntry3));
}
@@ -343,20 +338,22 @@ TEST_F(ProjectStoragePathWatcher, RemoveUnusedEntries)
TEST_F(ProjectStoragePathWatcher, TwoNotifyFileChanges)
{
- watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2]}},
- {id2, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}},
- {id3, {pathIds[4]}}});
- ON_CALL(mockFileSystem, fileStatus(Eq(pathIds[0])))
- .WillByDefault(Return(FileStatus{pathIds[0], 1, 2}));
- ON_CALL(mockFileSystem, fileStatus(Eq(pathIds[1])))
- .WillByDefault(Return(FileStatus{pathIds[1], 1, 2}));
- ON_CALL(mockFileSystem, fileStatus(Eq(pathIds[3])))
- .WillByDefault(Return(FileStatus{pathIds[3], 1, 2}));
+ watcher.updateIdPaths(
+ {{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3], sourceIds[4]}},
+ {projectChunkId3, {sourceIds[4]}}});
+ ON_CALL(mockFileSystem, fileStatus(Eq(sourceIds[0])))
+ .WillByDefault(Return(FileStatus{sourceIds[0], 1, 2}));
+ ON_CALL(mockFileSystem, fileStatus(Eq(sourceIds[1])))
+ .WillByDefault(Return(FileStatus{sourceIds[1], 1, 2}));
+ ON_CALL(mockFileSystem, fileStatus(Eq(sourceIds[3])))
+ .WillByDefault(Return(FileStatus{sourceIds[3], 1, 2}));
EXPECT_CALL(notifier,
pathsWithIdsChanged(ElementsAre(
- IdPaths{id1, {SourceId::create(1), SourceId::create(2)}},
- IdPaths{id2, {SourceId::create(1), SourceId::create(2), SourceId::create(4)}})));
+ IdPaths{projectChunkId1, {SourceId::create(1), SourceId::create(2)}},
+ IdPaths{projectChunkId2,
+ {SourceId::create(1), SourceId::create(2), SourceId::create(4)}})));
mockQFileSytemWatcher.directoryChanged(sourceContextPath);
mockQFileSytemWatcher.directoryChanged(sourceContextPath2);
@@ -364,22 +361,22 @@ TEST_F(ProjectStoragePathWatcher, TwoNotifyFileChanges)
TEST_F(ProjectStoragePathWatcher, NotifyForPathChanges)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
- ON_CALL(mockFileSystem, fileStatus(Eq(pathIds[0])))
- .WillByDefault(Return(FileStatus{pathIds[0], 1, 2}));
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
+ ON_CALL(mockFileSystem, fileStatus(Eq(sourceIds[0])))
+ .WillByDefault(Return(FileStatus{sourceIds[0], 1, 2}));
- ON_CALL(mockFileSystem, fileStatus(Eq(pathIds[3])))
- .WillByDefault(Return(FileStatus{pathIds[3], 1, 2}));
+ ON_CALL(mockFileSystem, fileStatus(Eq(sourceIds[3])))
+ .WillByDefault(Return(FileStatus{sourceIds[3], 1, 2}));
- EXPECT_CALL(notifier, pathsChanged(ElementsAre(pathIds[0])));
+ EXPECT_CALL(notifier, pathsChanged(ElementsAre(sourceIds[0])));
mockQFileSytemWatcher.directoryChanged(sourceContextPath);
}
TEST_F(ProjectStoragePathWatcher, NoNotifyForUnwatchedPathChanges)
{
- watcher.updateIdPaths({{id1, {pathIds[3]}}, {id2, {pathIds[3]}}});
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[3]}}, {projectChunkId2, {sourceIds[3]}}});
EXPECT_CALL(notifier, pathsChanged(IsEmpty()));
@@ -388,14 +385,74 @@ TEST_F(ProjectStoragePathWatcher, NoNotifyForUnwatchedPathChanges)
TEST_F(ProjectStoragePathWatcher, NoDuplicatePathChanges)
{
- watcher.updateIdPaths(
- {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
- ON_CALL(mockFileSystem, fileStatus(Eq(pathIds[0])))
- .WillByDefault(Return(FileStatus{pathIds[0], 1, 2}));
+ watcher.updateIdPaths({{projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId2, {sourceIds[0], sourceIds[1], sourceIds[3]}}});
+ ON_CALL(mockFileSystem, fileStatus(Eq(sourceIds[0])))
+ .WillByDefault(Return(FileStatus{sourceIds[0], 1, 2}));
- EXPECT_CALL(notifier, pathsChanged(ElementsAre(pathIds[0])));
+ EXPECT_CALL(notifier, pathsChanged(ElementsAre(sourceIds[0])));
mockQFileSytemWatcher.directoryChanged(sourceContextPath);
mockQFileSytemWatcher.directoryChanged(sourceContextPath);
}
+
+TEST_F(ProjectStoragePathWatcher, UpdateContextIdPathsAddsEntryInNewDirectory)
+{
+ watcher.updateIdPaths({
+ {projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId4, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3]}},
+ });
+
+ watcher.updateContextIdPaths({{projectChunkId4, {sourceIds[4]}}}, {sourceContextIds[2]});
+
+ ASSERT_THAT(watcher.watchedEntries(),
+ UnorderedElementsAre(watcherEntry1,
+ watcherEntry3,
+ watcherEntry6,
+ watcherEntry9,
+ watcherEntry10,
+ watcherEntry11,
+ watcherEntry12,
+ watcherEntry13));
+}
+
+TEST_F(ProjectStoragePathWatcher, UpdateContextIdPathsAddsEntryToDirectory)
+{
+ watcher.updateIdPaths({
+ {projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId4, {sourceIds[1], sourceIds[3]}},
+ });
+
+ watcher.updateContextIdPaths({{projectChunkId4,
+ {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3]}}},
+ {sourceContextIds[1]});
+
+ ASSERT_THAT(watcher.watchedEntries(),
+ UnorderedElementsAre(watcherEntry1,
+ watcherEntry3,
+ watcherEntry6,
+ watcherEntry9,
+ watcherEntry10,
+ watcherEntry11,
+ watcherEntry12));
+}
+
+TEST_F(ProjectStoragePathWatcher, UpdateContextIdPathsRemovesEntry)
+{
+ watcher.updateIdPaths({
+ {projectChunkId1, {sourceIds[0], sourceIds[1], sourceIds[2]}},
+ {projectChunkId4, {sourceIds[0], sourceIds[1], sourceIds[2], sourceIds[3]}},
+ });
+
+ watcher.updateContextIdPaths({{projectChunkId4, {sourceIds[3]}}}, {sourceContextIds[1]});
+
+ ASSERT_THAT(watcher.watchedEntries(),
+ UnorderedElementsAre(watcherEntry1,
+ watcherEntry3,
+ watcherEntry6,
+ watcherEntry9,
+ watcherEntry10,
+ watcherEntry12));
+}
+
} // namespace
diff --git a/tests/unit/unittest/projectstoragepathwatchermock.h b/tests/unit/unittest/projectstoragepathwatchermock.h
index 22e81b35d7..2c47291256 100644
--- a/tests/unit/unittest/projectstoragepathwatchermock.h
+++ b/tests/unit/unittest/projectstoragepathwatchermock.h
@@ -7,7 +7,7 @@
#include "projectstorage/projectstoragepathwatcherinterface.h"
-class MockProjectStoragePathWatcher : public QmlDesigner::ProjectStoragePathWatcherInterface
+class ProjectStoragePathWatcherMock : public QmlDesigner::ProjectStoragePathWatcherInterface
{
public:
MOCK_METHOD(void, updateIdPaths, (const std::vector<QmlDesigner::IdPaths> &idPaths), ());
diff --git a/tests/unit/unittest/projectstorageupdater-test.cpp b/tests/unit/unittest/projectstorageupdater-test.cpp
index 46fa202f90..da7e2f3adb 100644
--- a/tests/unit/unittest/projectstorageupdater-test.cpp
+++ b/tests/unit/unittest/projectstorageupdater-test.cpp
@@ -5,6 +5,7 @@
#include "filesystemmock.h"
#include "projectstoragemock.h"
+#include "projectstoragepathwatchermock.h"
#include "qmldocumentparsermock.h"
#include "qmltypesparsermock.h"
@@ -124,44 +125,29 @@ class ProjectStorageUpdater : public testing::Test
public:
ProjectStorageUpdater()
{
- ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypesPathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypesPathSourceId, 2, 421}));
-
- ON_CALL(fileSystemMock, fileStatus(Eq(qmltypes2PathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 2, 421}));
-
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 2, 421}));
-
- ON_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId)))
- .WillByDefault(Return(FileStatus{directoryPathSourceId, 2, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(directoryPathSourceId)))
- .WillByDefault(Return(FileStatus{directoryPathSourceId, 2, 421}));
-
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
- .WillByDefault(Return(qmldirContent));
-
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml", "Second.qml"}));
-
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 12}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId3)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 2}));
+ setFilesChanged({qmltypesPathSourceId,
+ qmltypes2PathSourceId,
+ qmlDirPathSourceId,
+ qmlDocumentSourceId1,
+ qmlDocumentSourceId2,
+ qmlDocumentSourceId3});
+
+ setFilesDontChanged({directoryPathSourceId,
+ path1SourceId,
+ path2SourceId,
+ path3SourceId,
+ firstSourceId,
+ secondSourceId,
+ thirdSourceId,
+ qmltypes1SourceId,
+ qmltypes2SourceId});
+
+ setFilesAdded({qmldir1SourceId, qmldir2SourceId, qmldir3SourceId});
+
+ setContent(u"/path/qmldir", qmldirContent);
+
+ setQmlFileNames(u"/path", {"First.qml", "First2.qml", "Second.qml"});
+
ON_CALL(projectStorageMock, moduleId(_)).WillByDefault([&](const auto &name) {
return storage.moduleId(name);
});
@@ -170,16 +156,16 @@ public:
secondType.prototype = Storage::Synchronization::ImportedType{"Object2"};
thirdType.prototype = Storage::Synchronization::ImportedType{"Object3"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml"))))
- .WillByDefault(Return(qmlDocument1));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First2.qml"))))
- .WillByDefault(Return(qmlDocument2));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml"))))
- .WillByDefault(Return(qmlDocument3));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))))
- .WillByDefault(Return(qmltypes1));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/types/example2.qmltypes"))))
- .WillByDefault(Return(qmltypes2));
+ setContent(u"/path/First.qml", qmlDocument1);
+ setContent(u"/path/First2.qml", qmlDocument2);
+ setContent(u"/path/Second.qml", qmlDocument3);
+ setContent(u"/path/example.qmltypes", qmltypes1);
+ setContent(u"/path/types/example2.qmltypes", qmltypes2);
+ setContent(u"/path/one/First.qml", qmlDocument1);
+ setContent(u"/path/one/Second.qml", qmlDocument2);
+ setContent(u"/path/two/Third.qml", qmlDocument3);
+ setContent(u"/path/one/example.qmltypes", qmltypes1);
+ setContent(u"/path/two/example2.qmltypes", qmltypes2);
ON_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _, _))
.WillByDefault([&](auto, auto &imports, auto, auto) {
@@ -208,6 +194,76 @@ public:
});
}
+ void setFilesDontChanged(const QmlDesigner::SourceIds &sourceIds)
+ {
+ for (auto sourceId : sourceIds) {
+ ON_CALL(fileSystemMock, fileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{sourceId, 2, 421}));
+ ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{sourceId, 2, 421}));
+ }
+ }
+
+ void setFilesChanged(const QmlDesigner::SourceIds &sourceIds)
+ {
+ for (auto sourceId : sourceIds) {
+ ON_CALL(fileSystemMock, fileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{sourceId, 1, 21}));
+ ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{sourceId, 2, 421}));
+ }
+ }
+
+ void setFilesAdded(const QmlDesigner::SourceIds &sourceIds)
+ {
+ for (auto sourceId : sourceIds) {
+ ON_CALL(fileSystemMock, fileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{sourceId, 1, 21}));
+ ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{}));
+ }
+ }
+
+ void setFilesRemoved(const QmlDesigner::SourceIds &sourceIds)
+ {
+ for (auto sourceId : sourceIds) {
+ ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))).WillByDefault(Return(FileStatus{}));
+ ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{sourceId, 1, 21}));
+ }
+ }
+
+ void setFilesDontExists(const QmlDesigner::SourceIds &sourceIds)
+ {
+ for (auto sourceId : sourceIds) {
+ ON_CALL(fileSystemMock, fileStatus(Eq(sourceId))).WillByDefault(Return(FileStatus{}));
+ ON_CALL(projectStorageMock, fetchFileStatus(Eq(sourceId)))
+ .WillByDefault(Return(FileStatus{}));
+ }
+ }
+
+ void setQmlFileNames(QStringView directoryPath, const QStringList &qmlFileNames)
+ {
+ ON_CALL(fileSystemMock, qmlFileNames(Eq(directoryPath))).WillByDefault(Return(qmlFileNames));
+ }
+
+ void setProjectDatas(SourceId directoryPathSourceId,
+ const QmlDesigner::Storage::Synchronization::ProjectDatas &projectDatas)
+ {
+ ON_CALL(projectStorageMock, fetchProjectDatas(Eq(directoryPathSourceId)))
+ .WillByDefault(Return(projectDatas));
+ }
+
+ void setContent(QStringView path, const QString &content)
+ {
+ ON_CALL(fileSystemMock, contentAsQString(Eq(path))).WillByDefault(Return(content));
+ }
+
+ void setExpectedContent(QStringView path, const QString &content)
+ {
+ EXPECT_CALL(fileSystemMock, contentAsQString(Eq(path))).WillRepeatedly(Return(content));
+ }
+
protected:
NiceMock<FileSystemMock> fileSystemMock;
NiceMock<ProjectStorageMock> projectStorageMock;
@@ -218,12 +274,14 @@ protected:
QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()};
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>> sourcePathCache{
storage};
+ NiceMock<ProjectStoragePathWatcherMock> patchWatcherMock;
QmlDesigner::ProjectStorageUpdater updater{fileSystemMock,
projectStorageMock,
fileStatusCache,
sourcePathCache,
qmlDocumentParserMock,
- qmlTypesParserMock};
+ qmlTypesParserMock,
+ patchWatcherMock};
SourceId qmltypesPathSourceId = sourcePathCache.sourceId("/path/example.qmltypes");
SourceId qmltypes2PathSourceId = sourcePathCache.sourceId("/path/types/example2.qmltypes");
SourceId qmlDirPathSourceId = sourcePathCache.sourceId("/path/qmldir");
@@ -281,22 +339,31 @@ protected:
QString qmltypes1{"Module {\ndependencies: [module1]}"};
QString qmltypes2{"Module {\ndependencies: [module2]}"};
QStringList directories = {"/path"};
+ QStringList directories2 = {"/path/one", "/path/two"};
+ QStringList directories3 = {"/path/one", "/path/two", "/path/three"};
+ QmlDesigner::ProjectPartId projectPartId = QmlDesigner::ProjectPartId::create(1);
+ SourceId path1SourceId = sourcePathCache.sourceId("/path/one/.");
+ SourceId path2SourceId = sourcePathCache.sourceId("/path/two/.");
+ SourceId path3SourceId = sourcePathCache.sourceId("/path/three/.");
+ SourceId qmldir1SourceId = sourcePathCache.sourceId("/path/one/qmldir");
+ SourceId qmldir2SourceId = sourcePathCache.sourceId("/path/two/qmldir");
+ SourceId qmldir3SourceId = sourcePathCache.sourceId("/path/three/qmldir");
+ SourceId firstSourceId = sourcePathCache.sourceId("/path/one/First.qml");
+ SourceId secondSourceId = sourcePathCache.sourceId("/path/one/Second.qml");
+ SourceId thirdSourceId = sourcePathCache.sourceId("/path/two/Third.qml");
+ SourceId qmltypes1SourceId = sourcePathCache.sourceId("/path/one/example.qmltypes");
+ SourceId qmltypes2SourceId = sourcePathCache.sourceId("/path/two/example2.qmltypes");
};
TEST_F(ProjectStorageUpdater, GetContentForQmlDirPathsIfFileStatusIsDifferent)
{
+ 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", "/path/two", "/path/three"};
- ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](auto sourceId) {
- return FileStatus{sourceId, 21, 421};
- });
- ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](auto sourceId) {
- return FileStatus{sourceId, 2, 421};
- });
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDir3PathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDir3PathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDir3PathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDir3PathSourceId, 21, 421}));
+ setFilesChanged({qmlDir1PathSourceId, qmlDir2PathSourceId});
+ setFilesDontChanged({qmlDir3PathSourceId, path3SourceId});
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir"))));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir"))));
@@ -306,9 +373,9 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlDirPathsIfFileStatusIsDifferent)
TEST_F(ProjectStorageUpdater, RequestFileStatusFromFileSystem)
{
- EXPECT_CALL(fileSystemMock, fileStatus(Ne(qmlDirPathSourceId))).Times(AnyNumber());
+ EXPECT_CALL(fileSystemMock, fileStatus(Ne(directoryPathSourceId))).Times(AnyNumber());
- EXPECT_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)));
+ EXPECT_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId)));
updater.update(directories, {});
}
@@ -317,9 +384,8 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlTypes)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes)"};
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path")))).WillByDefault(Return(QStringList{}));
- EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
- .WillRepeatedly(Return(qmldir));
+ setQmlFileNames(u"/path", {});
+ setExpectedContent(u"/path/qmldir", qmldir);
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))));
@@ -330,43 +396,25 @@ TEST_F(ProjectStorageUpdater, GetContentForQmlTypesIfProjectStorageFileStatusIsI
{
QString qmldir{R"(module Example
typeinfo example.qmltypes)"};
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path")))).WillByDefault(Return(QStringList{}));
- EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
- .WillRepeatedly(Return(qmldir));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId)))
- .WillByDefault(Return(FileStatus{}));
+ setQmlFileNames(u"/path", {});
+ setExpectedContent(u"/path/qmldir", qmldir);
+ setFilesAdded({qmltypesPathSourceId});
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))));
updater.update(directories, {});
}
-TEST_F(ProjectStorageUpdater, DontGetContentForQmlTypesIfFileSystemFileStatusIsInvalid)
-{
- QString qmldir{R"(module Example
- typeinfo example.qmltypes)"};
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path")))).WillByDefault(Return(QStringList{}));
- EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
- .WillRepeatedly(Return(qmldir));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId))).WillByDefault(Return(FileStatus{}));
-
- EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))).Times(0);
-
- updater.update(directories, {});
-}
-
TEST_F(ProjectStorageUpdater, ParseQmlTypes)
{
QString qmldir{R"(module Example
typeinfo example.qmltypes
typeinfo types/example2.qmltypes)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
QString qmltypes{"Module {\ndependencies: []}"};
QString qmltypes2{"Module {\ndependencies: [foo]}"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))))
- .WillByDefault(Return(qmltypes));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/types/example2.qmltypes"))))
- .WillByDefault(Return(qmltypes2));
+ setContent(u"/path/example.qmltypes", qmltypes);
+ setContent(u"/path/types/example2.qmltypes", qmltypes2);
EXPECT_CALL(qmlTypesParserMock,
parse(qmltypes, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId)));
@@ -378,12 +426,7 @@ TEST_F(ProjectStorageUpdater, ParseQmlTypes)
TEST_F(ProjectStorageUpdater, SynchronizeIsEmptyForNoChange)
{
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypesPathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypesPathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
+ setFilesDontChanged({qmltypesPathSourceId, qmltypes2PathSourceId, qmlDirPathSourceId});
EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty()));
@@ -396,9 +439,8 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes)
Storage::Synchronization::Version{2, 3},
qmltypesPathSourceId};
QString qmltypes{"Module {\ndependencies: []}"};
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path")))).WillByDefault(Return(QStringList{}));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))))
- .WillByDefault(Return(qmltypes));
+ setQmlFileNames(u"/path", {});
+ setContent(u"/path/example.qmltypes", qmltypes);
ON_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _))
.WillByDefault([&](auto, auto &imports, auto &types, auto) {
types.push_back(objectType);
@@ -415,32 +457,36 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypes)
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
- IsFileStatus(qmltypesPathSourceId, 21, 421))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
+ IsFileStatus(qmltypesPathSourceId, 1, 21))),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
qmltypesPathSourceId,
exampleCppNativeModuleId,
FileType::QmlTypes))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)))));
+ UnorderedElementsAre(directoryPathSourceId)))));
updater.update(directories, {});
}
+TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesThrowsIfQmltpesDoesNotExists)
+{
+ Storage::Synchronization::Import import{qmlModuleId,
+ Storage::Synchronization::Version{2, 3},
+ qmltypesPathSourceId};
+ setFilesDontExists({qmltypesPathSourceId});
+
+ ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlTypesFile);
+}
+
TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesAreEmptyIfFileDoesNotChanged)
{
QString qmltypes{"Module {\ndependencies: []}"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes"))))
- .WillByDefault(Return(qmltypes));
+ setContent(u"/path/example.qmltypes", qmltypes);
ON_CALL(qmlTypesParserMock, parse(qmltypes, _, _, _))
.WillByDefault([&](auto, auto &, auto &types, auto) { types.push_back(objectType); });
- ON_CALL(fileSystemMock, fileStatus(Eq(qmltypesPathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypesPathSourceId, 2, 421}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmltypes2PathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 2, 421}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 2, 421}));
+ setFilesDontChanged({qmltypesPathSourceId, qmltypes2PathSourceId, qmlDirPathSourceId});
EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty()));
@@ -450,19 +496,15 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlTypesAreEmptyIfFileDoesNotChanged)
TEST_F(ProjectStorageUpdater, GetContentForQmlDocuments)
{
SourceId oldSecondSourceId3 = sourcePathCache.sourceId("/path/OldSecond.qml");
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml", "OldSecond.qml", "Second.qml"}));
- ON_CALL(fileSystemMock, fileStatus(Eq(oldSecondSourceId3)))
- .WillByDefault(Return(FileStatus{oldSecondSourceId3, 22, 14}));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/OldSecond.qml"))))
- .WillByDefault(Return(qmlDocument3));
+ setQmlFileNames(u"/path", {"First.qml", "First2.qml", "OldSecond.qml", "Second.qml"});
+ setFilesAdded({oldSecondSourceId3});
+ setContent(u"/path/OldSecond.qml", qmlDocument3);
QString qmldir{R"(module Example
FirstType 1.0 First.qml
FirstTypeV2 2.2 First2.qml
SecondType 2.1 OldSecond.qml
SecondType 2.2 Second.qml)"};
- EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir"))))
- .WillRepeatedly(Return(qmldir));
+ setExpectedContent(u"/path/qmldir", qmldir);
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml"))));
EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First2.qml"))));
@@ -481,13 +523,10 @@ TEST_F(ProjectStorageUpdater, ParseQmlDocuments)
QString qmlDocument1{"First{}"};
QString qmlDocument2{"Second{}"};
QString qmlDocument3{"Third{}"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First.qml"))))
- .WillByDefault(Return(qmlDocument1));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/First2.qml"))))
- .WillByDefault(Return(qmlDocument2));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml"))))
- .WillByDefault(Return(qmlDocument3));
+ setContent(u"/path/qmldir", qmldir);
+ setContent(u"/path/First.qml", qmlDocument1);
+ setContent(u"/path/First2.qml", qmlDocument2);
+ setContent(u"/path/Second.qml", qmlDocument3);
EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument1, _, _, _));
EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _, _));
@@ -500,7 +539,7 @@ TEST_F(ProjectStorageUpdater, ParseQmlDocumentsWithNonExistingQmlDocumentThrows)
{
QString qmldir{R"(module Example
NonexitingType 1.0 NonexitingType.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlDocumentFile);
}
@@ -511,7 +550,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments)
FirstType 1.0 First.qml
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(
projectStorageMock,
@@ -555,20 +594,25 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocuments)
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
- IsFileStatus(qmlDocumentSourceId1, 22, 12),
- IsFileStatus(qmlDocumentSourceId2, 22, 13),
- IsFileStatus(qmlDocumentSourceId3, 22, 14))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21),
+ IsFileStatus(qmlDocumentSourceId2, 1, 21),
+ IsFileStatus(qmlDocumentSourceId3, 1, 21))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
+ UnorderedElementsAre(directoryPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(
- IsProjectData(qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {});
}
@@ -577,50 +621,54 @@ TEST_F(ProjectStorageUpdater, SynchronizeAddOnlyQmlDocumentInDirectory)
{
QString qmldir{R"(module Example
FirstType 1.0 First.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesChanged({directoryPathSourceId});
+ setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1});
+ setFilesAdded({qmlDocumentSourceId2});
+ setProjectDatas(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}});
+ setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
- EXPECT_CALL(
- projectStorageMock,
- synchronize(AllOf(
- Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)),
- Field(&SynchronizationPackage::types,
- UnorderedElementsAre(
- AllOf(IsStorageType("First.qml",
- Storage::Synchronization::ImportedType{"Object"},
- TypeTraits::Reference,
- qmlDocumentSourceId1,
- Storage::Synchronization::ChangeLevel::Minimal),
- Field(&Storage::Synchronization::Type::exportedTypes,
- UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0),
- IsExportedType(pathModuleId, "First", -1, -1)))),
- AllOf(IsStorageType("First2.qml",
- Storage::Synchronization::ImportedType{"Object2"},
- TypeTraits::Reference,
- qmlDocumentSourceId2,
- Storage::Synchronization::ChangeLevel::Full),
- Field(&Storage::Synchronization::Type::exportedTypes,
- UnorderedElementsAre(IsExportedType(pathModuleId, "First2", -1, -1)))))),
- Field(&SynchronizationPackage::updatedSourceIds,
- UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::updatedFileStatusSourceIds,
- UnorderedElementsAre(qmlDocumentSourceId2)),
- Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 22, 13))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
- qmlDocumentSourceId1,
- ModuleId{},
- FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
- qmlDocumentSourceId2,
- ModuleId{},
- FileType::QmlDocument))))));
+ EXPECT_CALL(projectStorageMock,
+ synchronize(AllOf(
+ Field(&SynchronizationPackage::imports, UnorderedElementsAre(import2)),
+ Field(&SynchronizationPackage::types,
+ UnorderedElementsAre(
+ AllOf(IsStorageType("First.qml",
+ Storage::Synchronization::ImportedType{},
+ TypeTraits::Reference,
+ qmlDocumentSourceId1,
+ Storage::Synchronization::ChangeLevel::Minimal),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(
+ IsExportedType(exampleModuleId, "FirstType", 1, 0),
+ IsExportedType(pathModuleId, "First", -1, -1)))),
+ AllOf(IsStorageType("First2.qml",
+ Storage::Synchronization::ImportedType{"Object2"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId2,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(
+ IsExportedType(pathModuleId, "First2", -1, -1)))))),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(directoryPathSourceId, qmlDocumentSourceId2)),
+ Field(&SynchronizationPackage::fileStatuses,
+ UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21),
+ IsFileStatus(directoryPathSourceId, 1, 21))),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {});
}
@@ -631,22 +679,15 @@ TEST_F(ProjectStorageUpdater, SynchronizeRemovesQmlDocument)
FirstType 1.0 First.qml
FirstType 2.2 First2.qml
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 422}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId3)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, -1, -1}));
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesChanged({qmlDirPathSourceId});
+ setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
+ setFilesRemoved({qmlDocumentSourceId3});
+ setProjectDatas(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,
synchronize(AllOf(
@@ -679,15 +720,15 @@ TEST_F(ProjectStorageUpdater, SynchronizeRemovesQmlDocument)
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 422))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
+ UnorderedElementsAre(directoryPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
qmlDocumentSourceId1,
ModuleId{},
FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
+ IsProjectData(directoryPathSourceId,
qmlDocumentSourceId2,
ModuleId{},
FileType::QmlDocument))))));
@@ -700,19 +741,13 @@ TEST_F(ProjectStorageUpdater, SynchronizeRemovesQmlDocumentInQmldirOnly)
QString qmldir{R"(module Example
FirstType 1.0 First.qml
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 422}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 2}));
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesChanged({qmlDirPathSourceId});
+ setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
+ setProjectDatas(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
projectStorageMock,
@@ -740,15 +775,15 @@ TEST_F(ProjectStorageUpdater, SynchronizeRemovesQmlDocumentInQmldirOnly)
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 422))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
+ UnorderedElementsAre(directoryPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
qmlDocumentSourceId1,
ModuleId{},
FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
+ IsProjectData(directoryPathSourceId,
qmlDocumentSourceId2,
ModuleId{},
FileType::QmlDocument))))));
@@ -762,19 +797,13 @@ TEST_F(ProjectStorageUpdater, SynchronizeAddQmlDocumentToQmldir)
FirstType 1.0 First.qml
FirstType 2.2 First2.qml
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 422}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 2}));
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesChanged({qmlDirPathSourceId});
+ setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
+ setProjectDatas(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
projectStorageMock,
@@ -804,15 +833,15 @@ TEST_F(ProjectStorageUpdater, SynchronizeAddQmlDocumentToQmldir)
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 422))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
+ UnorderedElementsAre(directoryPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
qmlDocumentSourceId1,
ModuleId{},
FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
+ IsProjectData(directoryPathSourceId,
qmlDocumentSourceId2,
ModuleId{},
FileType::QmlDocument))))));
@@ -820,24 +849,18 @@ TEST_F(ProjectStorageUpdater, SynchronizeAddQmlDocumentToQmldir)
updater.update(directories, {});
}
-TEST_F(ProjectStorageUpdater, SynchronizeAddQmlDocumentToDirectory)
+TEST_F(ProjectStorageUpdater, SynchronizeRemoveQmlDocumentFromQmldir)
{
QString qmldir{R"(module Example
FirstType 1.0 First.qml
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 422}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId1)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId1, 22, 2}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 2}));
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml", "First2.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
+ setFilesChanged({qmlDirPathSourceId});
+ setProjectDatas(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+ setQmlFileNames(u"/path", {"First.qml", "First2.qml"});
EXPECT_CALL(
projectStorageMock,
@@ -865,15 +888,15 @@ TEST_F(ProjectStorageUpdater, SynchronizeAddQmlDocumentToDirectory)
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 422))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
+ UnorderedElementsAre(directoryPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(qmlDirPathSourceId,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
qmlDocumentSourceId1,
ModuleId{},
FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
+ IsProjectData(directoryPathSourceId,
qmlDocumentSourceId2,
ModuleId{},
FileType::QmlDocument))))));
@@ -887,9 +910,8 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate)
FirstType 1.0 First.qml
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesDontChanged({qmlDocumentSourceId3});
EXPECT_CALL(
projectStorageMock,
@@ -928,21 +950,26 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsDontUpdateIfUpToDate)
qmlDocumentSourceId2,
qmlDocumentSourceId3)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
- IsFileStatus(qmlDocumentSourceId1, 22, 12),
- IsFileStatus(qmlDocumentSourceId2, 22, 13))),
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21),
+ IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)),
Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
+ UnorderedElementsAre(directoryPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(
- IsProjectData(qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument),
- IsProjectData(qmlDirPathSourceId,
- qmlDocumentSourceId3,
- ModuleId{},
- FileType::QmlDocument))))));
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {});
}
@@ -953,44 +980,21 @@ TEST_F(ProjectStorageUpdater, UpdateQmldirDocuments)
FirstType 1.1 First.qml
FirstType 2.2 First2.qml
SecondType 2.2 Second.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId3)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId3, 22, 14}));
+ setContent(u"/path/qmldir", qmldir);
+ setFilesDontChanged({qmlDocumentSourceId3});
updater.pathsWithIdsChanged({});
}
-TEST_F(ProjectStorageUpdater, AddSourceIdForForInvalidQmldirFileStatus)
-{
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
- {qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}));
- ON_CALL(fileSystemMock, fileStatus(Eq(qmlDirPathSourceId))).WillByDefault(Return(FileStatus{}));
-
- EXPECT_CALL(projectStorageMock,
- synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()),
- Field(&SynchronizationPackage::types, IsEmpty()),
- Field(&SynchronizationPackage::updatedSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId,
- qmltypesPathSourceId,
- qmltypes2PathSourceId)),
- Field(&SynchronizationPackage::fileStatuses, IsEmpty()),
- Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()),
- Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
- updater.update(directories, {});
-}
-
TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged)
{
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
- {qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
- {qmlDirPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
+ setProjectDatas(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
+ {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
+ {directoryPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}});
+ setFilesDontChanged({qmlDirPathSourceId});
EXPECT_CALL(
projectStorageMock,
@@ -1019,10 +1023,10 @@ TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged)
qmlDocumentSourceId1,
qmlDocumentSourceId2)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421),
- IsFileStatus(qmltypes2PathSourceId, 21, 421),
- IsFileStatus(qmlDocumentSourceId1, 22, 12),
- IsFileStatus(qmlDocumentSourceId2, 22, 13))),
+ UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21),
+ IsFileStatus(qmltypes2PathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21),
+ IsFileStatus(qmlDocumentSourceId2, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId,
qmltypes2PathSourceId,
@@ -1035,18 +1039,13 @@ TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChanged)
TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChangedAndSomeUpdatedFiles)
{
- ON_CALL(projectStorageMock, fetchProjectDatas(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(QmlDesigner::Storage::Synchronization::ProjectDatas{
- {qmlDirPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
- {qmlDirPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
- {qmlDirPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
- {qmlDirPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDirPathSourceId)))
- .WillByDefault(Return(FileStatus{qmlDirPathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmlDocumentSourceId2)))
- .WillByDefault(Return(FileStatus{qmlDocumentSourceId2, 22, 13}));
+ setProjectDatas(
+ directoryPathSourceId,
+ {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes},
+ {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes},
+ {directoryPathSourceId, qmlDocumentSourceId1, exampleModuleId, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, exampleModuleId, FileType::QmlDocument}});
+ setFilesDontChanged({qmlDirPathSourceId, qmltypes2PathSourceId, qmlDocumentSourceId2});
EXPECT_CALL(
projectStorageMock,
@@ -1064,8 +1063,8 @@ TEST_F(ProjectStorageUpdater, SynchronizIfQmldirFileHasNotChangedAndSomeUpdatedF
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421),
- IsFileStatus(qmlDocumentSourceId1, 22, 12))),
+ UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)),
Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
@@ -1097,8 +1096,8 @@ TEST_F(ProjectStorageUpdater, UpdateQmlTypesFiles)
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421),
- IsFileStatus(qmltypes2PathSourceId, 21, 421))),
+ UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21),
+ IsFileStatus(qmltypes2PathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)),
Field(&SynchronizationPackage::projectDatas,
@@ -1117,8 +1116,7 @@ TEST_F(ProjectStorageUpdater, UpdateQmlTypesFiles)
TEST_F(ProjectStorageUpdater, DontUpdateQmlTypesFilesIfUnchanged)
{
- ON_CALL(projectStorageMock, fetchFileStatus(Eq(qmltypes2PathSourceId)))
- .WillByDefault(Return(FileStatus{qmltypes2PathSourceId, 21, 421}));
+ setFilesDontChanged({qmltypes2PathSourceId});
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -1127,7 +1125,7 @@ TEST_F(ProjectStorageUpdater, DontUpdateQmlTypesFilesIfUnchanged)
Field(&SynchronizationPackage::updatedSourceIds,
UnorderedElementsAre(qmltypesPathSourceId)),
Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 21, 421))),
+ UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21))),
Field(&SynchronizationPackage::updatedFileStatusSourceIds,
UnorderedElementsAre(qmltypesPathSourceId)),
Field(&SynchronizationPackage::projectDatas,
@@ -1146,38 +1144,39 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithDifferentVersionButSame
FirstType 1.0 First.qml
FirstType 1.1 First.qml
FirstType 6.0 First.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setQmlFileNames(u"/path", {"First.qml"});
- EXPECT_CALL(
- projectStorageMock,
- synchronize(AllOf(
- Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1)),
- Field(&SynchronizationPackage::types,
- UnorderedElementsAre(AllOf(
- IsStorageType("First.qml",
- Storage::Synchronization::ImportedType{"Object"},
- TypeTraits::Reference,
- qmlDocumentSourceId1,
- Storage::Synchronization::ChangeLevel::Full),
- Field(&Storage::Synchronization::Type::exportedTypes,
- UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0),
- IsExportedType(exampleModuleId, "FirstType", 1, 1),
- IsExportedType(exampleModuleId, "FirstType", 6, 0),
- IsExportedType(pathModuleId, "First", -1, -1)))))),
- Field(&SynchronizationPackage::updatedSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
- Field(&SynchronizationPackage::updatedFileStatusSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
- Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
- IsFileStatus(qmlDocumentSourceId1, 22, 12))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(
- qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument))))));
+ EXPECT_CALL(projectStorageMock,
+ synchronize(
+ AllOf(Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1)),
+ Field(&SynchronizationPackage::types,
+ UnorderedElementsAre(AllOf(
+ IsStorageType("First.qml",
+ Storage::Synchronization::ImportedType{"Object"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId1,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(
+ IsExportedType(exampleModuleId, "FirstType", 1, 0),
+ IsExportedType(exampleModuleId, "FirstType", 1, 1),
+ IsExportedType(exampleModuleId, "FirstType", 6, 0),
+ IsExportedType(pathModuleId, "First", -1, -1)))))),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
+ Field(&SynchronizationPackage::fileStatuses,
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21))),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {});
}
@@ -1187,52 +1186,50 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithDifferentTypeNameButSam
QString qmldir{R"(module Example
FirstType 1.0 First.qml
FirstType2 1.0 First.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setQmlFileNames(u"/path", {"First.qml"});
- EXPECT_CALL(
- projectStorageMock,
- synchronize(AllOf(
- Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1)),
- Field(&SynchronizationPackage::types,
- UnorderedElementsAre(AllOf(
- IsStorageType("First.qml",
- Storage::Synchronization::ImportedType{"Object"},
- TypeTraits::Reference,
- qmlDocumentSourceId1,
- Storage::Synchronization::ChangeLevel::Full),
- Field(&Storage::Synchronization::Type::exportedTypes,
- UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0),
- IsExportedType(exampleModuleId, "FirstType2", 1, 0),
- IsExportedType(pathModuleId, "First", -1, -1)))))),
- Field(&SynchronizationPackage::updatedSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
- Field(&SynchronizationPackage::updatedFileStatusSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
- Field(&SynchronizationPackage::fileStatuses,
- UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 21, 421),
- IsFileStatus(qmlDocumentSourceId1, 22, 12))),
- Field(&SynchronizationPackage::updatedProjectSourceIds,
- UnorderedElementsAre(qmlDirPathSourceId)),
- Field(&SynchronizationPackage::projectDatas,
- UnorderedElementsAre(IsProjectData(
- qmlDirPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument))))));
+ EXPECT_CALL(projectStorageMock,
+ synchronize(
+ AllOf(Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1)),
+ Field(&SynchronizationPackage::types,
+ UnorderedElementsAre(AllOf(
+ IsStorageType("First.qml",
+ Storage::Synchronization::ImportedType{"Object"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId1,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(
+ IsExportedType(exampleModuleId, "FirstType", 1, 0),
+ IsExportedType(exampleModuleId, "FirstType2", 1, 0),
+ IsExportedType(pathModuleId, "First", -1, -1)))))),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1)),
+ Field(&SynchronizationPackage::fileStatuses,
+ UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21))),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument))))));
updater.update(directories, {});
}
TEST_F(ProjectStorageUpdater, DontSynchronizeSelectors)
{
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/+First.qml"))))
- .WillByDefault(Return(qmlDocument1));
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qml/+First.qml"))))
- .WillByDefault(Return(qmlDocument1));
+ setContent(u"/path/+First.qml", qmlDocument1);
+ setContent(u"/path/qml/+First.qml", qmlDocument1);
QString qmldir{R"(module Example
FirstType 1.0 +First.qml)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
- ON_CALL(fileSystemMock, qmlFileNames(Eq(QString("/path"))))
- .WillByDefault(Return(QStringList{"First.qml"}));
+ setContent(u"/path/qmldir", qmldir);
+ setQmlFileNames(u"/path", {"First.qml"});
EXPECT_CALL(projectStorageMock,
synchronize(Not(Field(
@@ -1251,7 +1248,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependencies)
typeinfo example.qmltypes
typeinfo types/example2.qmltypes
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -1283,7 +1280,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependenciesWithDoubleEntries)
typeinfo example.qmltypes
typeinfo types/example2.qmltypes
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -1315,7 +1312,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirDependenciesWithCollidingImports)
typeinfo example.qmltypes
typeinfo types/example2.qmltypes
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -1344,7 +1341,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirWithNoDependencies)
typeinfo example.qmltypes
typeinfo types/example2.qmltypes
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(projectStorageMock,
synchronize(
@@ -1362,7 +1359,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirImports)
import QML 2.1
import Quick
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(
projectStorageMock,
@@ -1401,7 +1398,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirWithNoImports)
{
QString qmldir{R"(module Example
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(projectStorageMock,
synchronize(AllOf(Field(&SynchronizationPackage::moduleExportedImports, IsEmpty()),
@@ -1419,7 +1416,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirImportsWithDoubleEntries)
import Quick
import Qml
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(
projectStorageMock,
@@ -1461,7 +1458,7 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirOptionalImports)
import QML 2.1
optional import Quick
)"};
- ON_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/qmldir")))).WillByDefault(Return(qmldir));
+ setContent(u"/path/qmldir", qmldir);
EXPECT_CALL(
projectStorageMock,
@@ -1496,4 +1493,453 @@ TEST_F(ProjectStorageUpdater, SynchronizeQmldirOptionalImports)
updater.update(directories, {});
}
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectories)
+{
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Directory,
+ {path1SourceId, path2SourceId, path3SourceId}})));
+
+ updater.update(directories3, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotExists)
+{
+ setFilesDontExists({path2SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Directory,
+ {path1SourceId, path3SourceId}})));
+
+ updater.update(directories3, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryDoesNotChanged)
+{
+ setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Directory,
+ {path1SourceId, path2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherDirectoryRemoved)
+{
+ setFilesRemoved({qmldir1SourceId, path1SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(
+ IdPaths{projectPartId, QmlDesigner::SourceType::Directory, {path2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirs)
+{
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlDir,
+ {qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}})));
+
+ updater.update(directories3, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotExists)
+{
+ setFilesDontExists({qmldir2SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlDir,
+ {qmldir1SourceId, qmldir3SourceId}})));
+
+ updater.update(directories3, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirDoesNotChanged)
+{
+ setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlDir,
+ {qmldir1SourceId, qmldir2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmldirRemoved)
+{
+ setFilesRemoved({qmldir1SourceId, path1SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(
+ IdPaths{projectPartId, QmlDesigner::SourceType::QmlDir, {qmldir2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFiles)
+{
+ QString qmldir1{R"(module Example
+ FirstType 1.0 First.qml
+ Second 1.0 Second.qml)"};
+ setQmlFileNames(u"/path/one", {"First.qml", "Second.qml"});
+ setQmlFileNames(u"/path/two", {"Third.qml"});
+ setContent(u"/path/one/qmldir", qmldir1);
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Qml,
+ {firstSourceId, secondSourceId, thirdSourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmlFilesDontChanged)
+{
+ QString qmldir1{R"(module Example
+ FirstType 1.0 First.qml
+ Second 1.0 Second.qml)"};
+ setQmlFileNames(u"/path/one", {"First.qml", "Second.qml"});
+ setQmlFileNames(u"/path/two", {"Third.qml"});
+ setContent(u"/path/one/qmldir", qmldir1);
+ setFilesDontChanged({firstSourceId, secondSourceId, thirdSourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Qml,
+ {firstSourceId, secondSourceId, thirdSourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmlFilesChanged)
+{
+ 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}});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Qml,
+ {firstSourceId, secondSourceId, thirdSourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmlFilesAndDirectoriesDontChanged)
+{
+ setFilesDontChanged({qmldir1SourceId,
+ qmldir2SourceId,
+ path1SourceId,
+ path2SourceId,
+ firstSourceId,
+ secondSourceId,
+ thirdSourceId});
+ setProjectDatas(path1SourceId,
+ {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument},
+ {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}});
+ setProjectDatas(path2SourceId,
+ {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::Qml,
+ {firstSourceId, secondSourceId, thirdSourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesInQmldir)
+{
+ QString qmldir1{R"(module Example
+ typeinfo example.qmltypes)"};
+ QString qmldir2{R"(module Example2
+ typeinfo example2.qmltypes)"};
+ setContent(u"/path/one/qmldir", qmldir1);
+ setContent(u"/path/two/qmldir", qmldir2);
+
+ setFilesDontChanged({firstSourceId, secondSourceId, thirdSourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlTypes,
+ {qmltypes1SourceId, qmltypes2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmltypesFilesInQmldirDontChanged)
+{
+ QString qmldir1{R"(module Example
+ typeinfo example.qmltypes)"};
+ QString qmldir2{R"(module Example2
+ typeinfo example2.qmltypes)"};
+ setContent(u"/path/one/qmldir", qmldir1);
+ setContent(u"/path/two/qmldir", qmldir2);
+ setFilesDontChanged({qmltypes1SourceId, qmltypes2SourceId});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlTypes,
+ {qmltypes1SourceId, qmltypes2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherOnlyQmltypesFilesChanged)
+{
+ setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId});
+ setFilesChanged({qmltypes1SourceId, qmltypes2SourceId});
+ setProjectDatas(path1SourceId,
+ {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}});
+ setProjectDatas(path2SourceId,
+ {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlTypes,
+ {qmltypes1SourceId, qmltypes2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherQmltypesFilesAndDirectoriesDontChanged)
+{
+ setFilesDontChanged({qmldir1SourceId,
+ qmldir2SourceId,
+ path1SourceId,
+ path2SourceId,
+ qmltypes1SourceId,
+ qmltypes2SourceId});
+ setProjectDatas(path1SourceId,
+ {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}});
+ setProjectDatas(path2SourceId,
+ {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}});
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlTypes,
+ {qmltypes1SourceId, qmltypes2SourceId}})));
+
+ updater.update(directories2, {}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, UpdatePathWatcherBuiltinQmltypesFiles)
+{
+ QString builtinQmltyplesPath1{"/path/one/example.qmltypes"};
+ QString builtinQmltyplesPath2{"/path/two/example2.qmltypes"};
+ setContent(builtinQmltyplesPath1, qmltypes1);
+ setContent(builtinQmltyplesPath2, qmltypes2);
+
+ EXPECT_CALL(patchWatcherMock,
+ updateIdPaths(Contains(IdPaths{projectPartId,
+ QmlDesigner::SourceType::QmlTypes,
+ {qmltypes1SourceId, qmltypes2SourceId}})));
+
+ updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}, projectPartId);
+}
+
+TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldir)
+{
+ setFilesDontExists({qmlDirPathSourceId});
+ setFilesChanged({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"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId1,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(pathModuleId, "First", -1, -1)))),
+ AllOf(IsStorageType("First2.qml",
+ Storage::Synchronization::ImportedType{"Object2"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId2,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(pathModuleId, "First2", -1, -1)))),
+ AllOf(IsStorageType("Second.qml",
+ Storage::Synchronization::ImportedType{"Object3"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId3,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(pathModuleId, "Second", -1, -1)))))),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId,
+ qmlDocumentSourceId1,
+ qmlDocumentSourceId2,
+ qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(directoryPathSourceId,
+ qmlDirPathSourceId,
+ qmlDocumentSourceId1,
+ qmlDocumentSourceId2,
+ qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::fileStatuses,
+ UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId1, 1, 21),
+ IsFileStatus(qmlDocumentSourceId2, 1, 21),
+ IsFileStatus(qmlDocumentSourceId3, 1, 21))),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
+
+ updater.update(directories, {});
+}
+
+TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldirThrowsIfQmlDocumentDoesNotExists)
+{
+ setFilesDontExists({qmlDirPathSourceId, qmlDocumentSourceId1});
+ setFilesAdded({directoryPathSourceId});
+
+ ASSERT_THROW(updater.update(directories, {}), QmlDesigner::CannotParseQmlDocumentFile);
+}
+
+TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldirThrowsIfDirectoryDoesNotExists)
+{
+ setFilesDontExists({qmlDirPathSourceId, directoryPathSourceId});
+ setProjectDatas(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()),
+ Field(&SynchronizationPackage::types, IsEmpty()),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId,
+ qmlDocumentSourceId1,
+ qmlDocumentSourceId2,
+ qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(directoryPathSourceId,
+ qmlDirPathSourceId,
+ qmlDocumentSourceId1,
+ qmlDocumentSourceId2,
+ qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::fileStatuses, IsEmpty()),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas, IsEmpty()))));
+
+ updater.update(directories, {});
+}
+
+TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldirAddQmlDocument)
+{
+ setFilesDontExists({qmlDirPathSourceId});
+ setFilesChanged({directoryPathSourceId});
+ setFilesAdded({qmlDocumentSourceId3});
+ setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2});
+ setProjectDatas(directoryPathSourceId,
+ {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument},
+ {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}});
+
+ EXPECT_CALL(
+ projectStorageMock,
+ synchronize(AllOf(
+ Field(&SynchronizationPackage::imports, UnorderedElementsAre(import3)),
+ Field(&SynchronizationPackage::types,
+ UnorderedElementsAre(AllOf(
+ IsStorageType("Second.qml",
+ Storage::Synchronization::ImportedType{"Object3"},
+ TypeTraits::Reference,
+ qmlDocumentSourceId3,
+ Storage::Synchronization::ChangeLevel::Full),
+ Field(&Storage::Synchronization::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(pathModuleId, "Second", -1, -1)))))),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(directoryPathSourceId, qmlDirPathSourceId, qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::fileStatuses,
+ UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21),
+ IsFileStatus(qmlDocumentSourceId3, 1, 21))),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId3,
+ ModuleId{},
+ FileType::QmlDocument))))));
+
+ updater.update(directories, {});
+}
+
+TEST_F(ProjectStorageUpdater, SynchronizeQmlDocumentsWithoutQmldirRemovesQmlDocument)
+{
+ setFilesDontExists({qmlDirPathSourceId});
+ setFilesChanged({directoryPathSourceId});
+ 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}});
+
+ EXPECT_CALL(projectStorageMock,
+ synchronize(
+ AllOf(Field(&SynchronizationPackage::imports, IsEmpty()),
+ Field(&SynchronizationPackage::types, IsEmpty()),
+ Field(&SynchronizationPackage::updatedSourceIds,
+ UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::updatedFileStatusSourceIds,
+ UnorderedElementsAre(directoryPathSourceId,
+ qmlDirPathSourceId,
+ qmlDocumentSourceId3)),
+ Field(&SynchronizationPackage::fileStatuses,
+ UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21))),
+ Field(&SynchronizationPackage::updatedProjectSourceIds,
+ UnorderedElementsAre(directoryPathSourceId)),
+ Field(&SynchronizationPackage::projectDatas,
+ UnorderedElementsAre(IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId1,
+ ModuleId{},
+ FileType::QmlDocument),
+ IsProjectData(directoryPathSourceId,
+ qmlDocumentSourceId2,
+ ModuleId{},
+ FileType::QmlDocument))))));
+
+ updater.update(directories, {});
+}
+
} // namespace
diff --git a/tests/unit/unittest/qmldocumentparser-test.cpp b/tests/unit/unittest/qmldocumentparser-test.cpp
index fd2a333964..7ed83ef891 100644
--- a/tests/unit/unittest/qmldocumentparser-test.cpp
+++ b/tests/unit/unittest/qmldocumentparser-test.cpp
@@ -11,7 +11,7 @@
namespace {
-namespace Storage = QmlDesigner::Storage;
+namespace Storage = QmlDesigner::Storage::Synchronization;
using QmlDesigner::ModuleId;
using QmlDesigner::SourceContextId;
using QmlDesigner::SourceId;
@@ -177,9 +177,10 @@ TEST_F(QmlDocumentParser, Properties)
auto type = parser.parse(R"(Example{ property int foo })", imports, qmlFileSourceId, directoryPath);
ASSERT_THAT(type.propertyDeclarations,
- UnorderedElementsAre(IsPropertyDeclaration("foo",
- Storage::ImportedType{"int"},
- Storage::PropertyDeclarationTraits::None)));
+ UnorderedElementsAre(
+ IsPropertyDeclaration("foo",
+ Storage::ImportedType{"int"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)));
}
TEST_F(QmlDocumentParser, QualifiedProperties)
@@ -199,7 +200,7 @@ TEST_F(QmlDocumentParser, QualifiedProperties)
Storage::Import{exampleModuleId,
Storage::Version{2, 1},
qmlFileSourceId}),
- Storage::PropertyDeclarationTraits::None)));
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)));
}
TEST_F(QmlDocumentParser, EnumerationInProperties)
@@ -211,9 +212,10 @@ TEST_F(QmlDocumentParser, EnumerationInProperties)
directoryPath);
ASSERT_THAT(type.propertyDeclarations,
- UnorderedElementsAre(IsPropertyDeclaration("foo",
- Storage::ImportedType("Enumeration.Foo"),
- Storage::PropertyDeclarationTraits::None)));
+ UnorderedElementsAre(
+ IsPropertyDeclaration("foo",
+ Storage::ImportedType("Enumeration.Foo"),
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)));
}
TEST_F(QmlDocumentParser, QualifiedEnumerationInProperties)
@@ -233,7 +235,7 @@ TEST_F(QmlDocumentParser, QualifiedEnumerationInProperties)
Storage::Import{exampleModuleId,
Storage::Version{2, 1},
qmlFileSourceId}),
- Storage::PropertyDeclarationTraits::None)));
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)));
}
TEST_F(QmlDocumentParser, Imports)
@@ -393,9 +395,10 @@ TEST_F(QmlDocumentParser, AliasItemProperties)
directoryPath);
ASSERT_THAT(type.propertyDeclarations,
- UnorderedElementsAre(IsPropertyDeclaration("delegate",
- Storage::ImportedType{"Item"},
- Storage::PropertyDeclarationTraits::None)));
+ UnorderedElementsAre(
+ IsPropertyDeclaration("delegate",
+ Storage::ImportedType{"Item"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)));
}
TEST_F(QmlDocumentParser, AliasProperties)
@@ -411,10 +414,11 @@ TEST_F(QmlDocumentParser, AliasProperties)
directoryPath);
ASSERT_THAT(type.propertyDeclarations,
- UnorderedElementsAre(IsAliasPropertyDeclaration("text",
- Storage::ImportedType{"Item"},
- Storage::PropertyDeclarationTraits::None,
- "text2")));
+ UnorderedElementsAre(
+ IsAliasPropertyDeclaration("text",
+ Storage::ImportedType{"Item"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None,
+ "text2")));
}
TEST_F(QmlDocumentParser, IndirectAliasProperties)
@@ -430,11 +434,12 @@ TEST_F(QmlDocumentParser, IndirectAliasProperties)
directoryPath);
ASSERT_THAT(type.propertyDeclarations,
- UnorderedElementsAre(IsAliasPropertyDeclaration("textSize",
- Storage::ImportedType{"Item"},
- Storage::PropertyDeclarationTraits::None,
- "text",
- "size")));
+ UnorderedElementsAre(
+ IsAliasPropertyDeclaration("textSize",
+ Storage::ImportedType{"Item"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None,
+ "text",
+ "size")));
}
TEST_F(QmlDocumentParser, InvalidAliasPropertiesAreSkipped)
@@ -465,7 +470,7 @@ TEST_F(QmlDocumentParser, ListProperty)
UnorderedElementsAre(
IsPropertyDeclaration("foos",
Storage::ImportedType{"Foo"},
- Storage::PropertyDeclarationTraits::IsList)));
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsList)));
}
TEST_F(QmlDocumentParser, AliasOnListProperty)
@@ -486,7 +491,7 @@ TEST_F(QmlDocumentParser, AliasOnListProperty)
UnorderedElementsAre(
IsPropertyDeclaration("foos",
Storage::ImportedType{"Foo"},
- Storage::PropertyDeclarationTraits::IsList)));
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsList)));
}
TEST_F(QmlDocumentParser, QualifiedListProperty)
@@ -507,7 +512,7 @@ TEST_F(QmlDocumentParser, QualifiedListProperty)
Storage::Import{exampleModuleId,
Storage::Version{2, 1},
qmlFileSourceId}},
- Storage::PropertyDeclarationTraits::IsList)));
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsList)));
}
} // namespace
diff --git a/tests/unit/unittest/qmltypesparser-test.cpp b/tests/unit/unittest/qmltypesparser-test.cpp
index f7f8548c79..405eec57df 100644
--- a/tests/unit/unittest/qmltypesparser-test.cpp
+++ b/tests/unit/unittest/qmltypesparser-test.cpp
@@ -12,7 +12,7 @@
namespace {
-namespace Storage = QmlDesigner::Storage;
+namespace Storage = QmlDesigner::Storage::Synchronization;
using QmlDesigner::ModuleId;
using QmlDesigner::SourceContextId;
using QmlDesigner::SourceId;
@@ -36,18 +36,20 @@ MATCHER_P(HasPrototype, prototype, std::string(negation ? "isn't " : "is ") + Pr
return Storage::ImportedTypeName{prototype} == type.prototype;
}
-MATCHER_P4(IsType,
+MATCHER_P5(IsType,
typeName,
prototype,
+ extension,
traits,
sourceId,
std::string(negation ? "isn't " : "is ")
- + PrintToString(Storage::Type{typeName, prototype, traits, sourceId}))
+ + PrintToString(Storage::Type{typeName, prototype, extension, traits, sourceId}))
{
const Storage::Type &type = arg;
return type.typeName == typeName && type.prototype == Storage::ImportedTypeName{prototype}
- && type.traits == traits && type.sourceId == sourceId;
+ && type.extension == Storage::ImportedTypeName{extension} && type.traits == traits
+ && type.sourceId == sourceId;
}
MATCHER_P3(IsPropertyDeclaration,
@@ -152,10 +154,10 @@ protected:
Storage::Types types;
SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")};
ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml-cppnative");
- QmlDesigner::Storage::ProjectData projectData{qmltypesFileSourceId,
- qmltypesFileSourceId,
- qtQmlNativeModuleId,
- Storage::FileType::QmlTypes};
+ Storage::ProjectData projectData{qmltypesFileSourceId,
+ qmltypesFileSourceId,
+ qtQmlNativeModuleId,
+ Storage::FileType::QmlTypes};
SourceContextId qmltypesFileSourceContextId{sourcePathCache.sourceContextId(qmltypesFileSourceId)};
ModuleId directoryModuleId{storage.moduleId("path/to/")};
};
@@ -184,6 +186,28 @@ TEST_F(QmlTypesParser, Types)
QString source{R"(import QtQuick.tooling 1.2
Module{
Component { name: "QObject"}
+ Component { name: "QQmlComponent"}})"};
+
+ parser.parse(source, imports, types, projectData);
+
+ ASSERT_THAT(types,
+ UnorderedElementsAre(IsType("QObject",
+ Storage::ImportedType{},
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Reference,
+ qmltypesFileSourceId),
+ IsType("QQmlComponent",
+ Storage::ImportedType{},
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Reference,
+ qmltypesFileSourceId)));
+}
+
+TEST_F(QmlTypesParser, Prototype)
+{
+ QString source{R"(import QtQuick.tooling 1.2
+ Module{
+ Component { name: "QObject"}
Component { name: "QQmlComponent"
prototype: "QObject"}})"};
@@ -192,11 +216,36 @@ TEST_F(QmlTypesParser, Types)
ASSERT_THAT(types,
UnorderedElementsAre(IsType("QObject",
Storage::ImportedType{},
- Storage::TypeTraits::Reference,
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Reference,
qmltypesFileSourceId),
IsType("QQmlComponent",
Storage::ImportedType{"QObject"},
- Storage::TypeTraits::Reference,
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Reference,
+ qmltypesFileSourceId)));
+}
+
+TEST_F(QmlTypesParser, Extension)
+{
+ QString source{R"(import QtQuick.tooling 1.2
+ Module{
+ Component { name: "QObject"}
+ Component { name: "QQmlComponent"
+ extension: "QObject"}})"};
+
+ parser.parse(source, imports, types, projectData);
+
+ ASSERT_THAT(types,
+ UnorderedElementsAre(IsType("QObject",
+ Storage::ImportedType{},
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Reference,
+ qmltypesFileSourceId),
+ IsType("QQmlComponent",
+ Storage::ImportedType{},
+ Storage::ImportedType{"QObject"},
+ QmlDesigner::Storage::TypeTraits::Reference,
qmltypesFileSourceId)));
}
@@ -234,24 +283,25 @@ TEST_F(QmlTypesParser, Properties)
parser.parse(source, imports, types, projectData);
- ASSERT_THAT(types,
- ElementsAre(Field(
- &Storage::Type::propertyDeclarations,
- UnorderedElementsAre(
- IsPropertyDeclaration("objectName",
- Storage::ImportedType{"string"},
- Storage::PropertyDeclarationTraits::None),
- IsPropertyDeclaration("target",
- Storage::ImportedType{"QObject"},
- Storage::PropertyDeclarationTraits::IsPointer),
- IsPropertyDeclaration("progress",
- Storage::ImportedType{"double"},
- Storage::PropertyDeclarationTraits::IsReadOnly),
- IsPropertyDeclaration("targets",
- Storage::ImportedType{"QQuickItem"},
- Storage::PropertyDeclarationTraits::IsReadOnly
- | Storage::PropertyDeclarationTraits::IsList
- | Storage::PropertyDeclarationTraits::IsPointer)))));
+ ASSERT_THAT(
+ types,
+ ElementsAre(Field(
+ &Storage::Type::propertyDeclarations,
+ UnorderedElementsAre(
+ IsPropertyDeclaration("objectName",
+ Storage::ImportedType{"string"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None),
+ IsPropertyDeclaration("target",
+ Storage::ImportedType{"QObject"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsPointer),
+ IsPropertyDeclaration("progress",
+ Storage::ImportedType{"double"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsReadOnly),
+ IsPropertyDeclaration("targets",
+ Storage::ImportedType{"QQuickItem"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsReadOnly
+ | QmlDesigner::Storage::PropertyDeclarationTraits::IsList
+ | QmlDesigner::Storage::PropertyDeclarationTraits::IsPointer)))));
}
TEST_F(QmlTypesParser, PropertiesWithQualifiedTypes)
@@ -268,18 +318,20 @@ TEST_F(QmlTypesParser, PropertiesWithQualifiedTypes)
parser.parse(source, imports, types, projectData);
- ASSERT_THAT(types,
- Contains(Field(&Storage::Type::propertyDeclarations,
- UnorderedElementsAre(
- IsPropertyDeclaration("values",
- Storage::ImportedType{"Qt::Vector"},
- Storage::PropertyDeclarationTraits::None),
- IsPropertyDeclaration("items",
- Storage::ImportedType{"Qt::List"},
- Storage::PropertyDeclarationTraits::None),
- IsPropertyDeclaration("values2",
- Storage::ImportedType{"Qt::Vector"},
- Storage::PropertyDeclarationTraits::None)))));
+ ASSERT_THAT(
+ types,
+ Contains(
+ Field(&Storage::Type::propertyDeclarations,
+ UnorderedElementsAre(
+ IsPropertyDeclaration("values",
+ Storage::ImportedType{"Qt::Vector"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None),
+ IsPropertyDeclaration("items",
+ Storage::ImportedType{"Qt::List"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None),
+ IsPropertyDeclaration("values2",
+ Storage::ImportedType{"Qt::Vector"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)))));
}
TEST_F(QmlTypesParser, PropertiesWithoutType)
@@ -294,12 +346,11 @@ TEST_F(QmlTypesParser, PropertiesWithoutType)
parser.parse(source, imports, types, projectData);
ASSERT_THAT(types,
- ElementsAre(
- Field(&Storage::Type::propertyDeclarations,
- UnorderedElementsAre(
- IsPropertyDeclaration("target",
- Storage::ImportedType{"QObject"},
- Storage::PropertyDeclarationTraits::IsPointer)))));
+ ElementsAre(Field(&Storage::Type::propertyDeclarations,
+ UnorderedElementsAre(IsPropertyDeclaration(
+ "target",
+ Storage::ImportedType{"QObject"},
+ QmlDesigner::Storage::PropertyDeclarationTraits::IsPointer)))));
}
TEST_F(QmlTypesParser, Functions)
@@ -516,26 +567,30 @@ TEST_F(QmlTypesParser, EnumerationIsExportedAsType)
parser.parse(source, imports, types, projectData);
- ASSERT_THAT(
- types,
- UnorderedElementsAre(
- AllOf(IsType("QObject::NamedColorSpace",
- Storage::ImportedType{},
- Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum,
- qmltypesFileSourceId),
- Field(&Storage::Type::exportedTypes,
- UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId,
- "QObject::NamedColorSpace",
- Storage::Version{})))),
- AllOf(IsType("QObject::VerticalLayoutDirection",
- Storage::ImportedType{},
- Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum,
- qmltypesFileSourceId),
- Field(&Storage::Type::exportedTypes,
- UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId,
- "QObject::VerticalLayoutDirection",
- Storage::Version{})))),
- _));
+ ASSERT_THAT(types,
+ UnorderedElementsAre(
+ AllOf(IsType("QObject::NamedColorSpace",
+ Storage::ImportedType{},
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Value
+ | QmlDesigner::Storage::TypeTraits::IsEnum,
+ qmltypesFileSourceId),
+ Field(&Storage::Type::exportedTypes,
+ UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId,
+ "QObject::NamedColorSpace",
+ Storage::Version{})))),
+ AllOf(IsType("QObject::VerticalLayoutDirection",
+ Storage::ImportedType{},
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Value
+ | QmlDesigner::Storage::TypeTraits::IsEnum,
+ qmltypesFileSourceId),
+ Field(&Storage::Type::exportedTypes,
+ UnorderedElementsAre(
+ IsExportedType(qtQmlNativeModuleId,
+ "QObject::VerticalLayoutDirection",
+ Storage::Version{})))),
+ _));
}
TEST_F(QmlTypesParser, EnumerationIsExportedAsTypeWithAlias)
@@ -562,7 +617,9 @@ TEST_F(QmlTypesParser, EnumerationIsExportedAsTypeWithAlias)
UnorderedElementsAre(
AllOf(IsType("QObject::NamedColorSpaces",
Storage::ImportedType{},
- Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum,
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Value
+ | QmlDesigner::Storage::TypeTraits::IsEnum,
qmltypesFileSourceId),
Field(&Storage::Type::exportedTypes,
UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId,
@@ -607,7 +664,9 @@ TEST_F(QmlTypesParser, EnumerationIsExportedAsTypeWithAliasToo)
UnorderedElementsAre(
AllOf(IsType("QObject::NamedColorSpaces",
Storage::ImportedType{},
- Storage::TypeTraits::Value | Storage::TypeTraits::IsEnum,
+ Storage::ImportedType{},
+ QmlDesigner::Storage::TypeTraits::Value
+ | QmlDesigner::Storage::TypeTraits::IsEnum,
qmltypesFileSourceId),
Field(&Storage::Type::exportedTypes,
UnorderedElementsAre(IsExportedType(qtQmlNativeModuleId,
@@ -643,7 +702,7 @@ TEST_F(QmlTypesParser, EnumerationIsReferencedByQualifiedName)
ElementsAre(IsPropertyDeclaration(
"colorSpace",
Storage::ImportedType{"QObject::NamedColorSpace"},
- Storage::PropertyDeclarationTraits::None)))));
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)))));
}
TEST_F(QmlTypesParser, AliasEnumerationIsReferencedByQualifiedName)
@@ -671,7 +730,7 @@ TEST_F(QmlTypesParser, AliasEnumerationIsReferencedByQualifiedName)
ElementsAre(IsPropertyDeclaration(
"colorSpace",
Storage::ImportedType{"QObject::NamedColorSpaces"},
- Storage::PropertyDeclarationTraits::None)))));
+ QmlDesigner::Storage::PropertyDeclarationTraits::None)))));
}
} // namespace
diff --git a/tests/unit/unittest/smallstring-test.cpp b/tests/unit/unittest/smallstring-test.cpp
index f708fd605e..d8321ec98f 100644
--- a/tests/unit/unittest/smallstring-test.cpp
+++ b/tests/unit/unittest/smallstring-test.cpp
@@ -1535,6 +1535,11 @@ TEST(SmallString, LongPathStringMoveConstuctor)
"text"));
}
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-move"
+#endif
+
TEST(SmallString, ShortSmallStringMoveConstuctorToSelf)
{
SmallString text("text");
@@ -1580,6 +1585,10 @@ TEST(SmallString, LongPathStringMoveConstuctorToSelf)
"text"));
}
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
TEST(SmallString, ShortSmallStringCopyAssignment)
{
SmallString text("text");
@@ -1600,6 +1609,11 @@ TEST(SmallString, LongSmallStringCopyAssignment)
ASSERT_THAT(copy, text);
}
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-assign-overloaded"
+#endif
+
TEST(SmallString, LongSmallStringCopySelfAssignment)
{
SmallString text("this is a very very very very long text");
@@ -1609,6 +1623,10 @@ TEST(SmallString, LongSmallStringCopySelfAssignment)
ASSERT_THAT(text, SmallString("this is a very very very very long text"));
}
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
TEST(SmallString, ShortSmallStringMoveAssignment)
{
SmallString text("text");
diff --git a/tests/unit/unittest/sqlitedatabase-test.cpp b/tests/unit/unittest/sqlitedatabase-test.cpp
index 771969446a..a7a8585db5 100644
--- a/tests/unit/unittest/sqlitedatabase-test.cpp
+++ b/tests/unit/unittest/sqlitedatabase-test.cpp
@@ -6,6 +6,7 @@
#include "spydummy.h"
#include <sqlitedatabase.h>
+#include <sqliteprogresshandler.h>
#include <sqlitereadstatement.h>
#include <sqlitetable.h>
#include <sqlitewritestatement.h>
@@ -32,16 +33,10 @@ class SqliteDatabase : public ::testing::Test
protected:
SqliteDatabase()
{
- database.lock();
- database.setJournalMode(JournalMode::Memory);
- database.setDatabaseFilePath(databaseFilePath);
- Table table;
table.setName("test");
table.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
table.addColumn("name");
- database.open();
-
table.initialize(database);
}
@@ -49,7 +44,6 @@ protected:
{
if (database.isOpen())
database.close();
- database.unlock();
}
std::vector<Utils::SmallString> names() const
@@ -68,17 +62,18 @@ protected:
protected:
SpyDummy spyDummy;
- QString databaseFilePath{":memory:"};
- mutable Sqlite::Database database;
+ Table table;
+ mutable Sqlite::Database database{":memory:", JournalMode::Memory};
Sqlite::TransactionInterface &transactionInterface = database;
MockFunction<void(Sqlite::ChangeType tupe, char const *, char const *, long long)> callbackMock;
std::function<void(Sqlite::ChangeType tupe, char const *, char const *, long long)>
callback = callbackMock.AsStdFunction();
+ std::unique_lock<Sqlite::Database> lock{database};
};
TEST_F(SqliteDatabase, SetDatabaseFilePath)
{
- ASSERT_THAT(database.databaseFilePath(), databaseFilePath);
+ ASSERT_THAT(database.databaseFilePath(), ":memory:");
}
TEST_F(SqliteDatabase, SetJournalMode)
@@ -335,7 +330,7 @@ TEST_F(SqliteDatabase, SessionsCommit)
.write(2, "hoo");
database.applyAndUpdateSessions();
- ASSERT_THAT(names(), ElementsAre("foo", "bar"));
+ ASSERT_THAT(names(), UnorderedElementsAre("foo", "bar"));
}
TEST_F(SqliteDatabase, SessionsRollback)
@@ -353,7 +348,41 @@ TEST_F(SqliteDatabase, SessionsRollback)
.write(2, "hoo");
database.applyAndUpdateSessions();
- ASSERT_THAT(names(), ElementsAre("foo", "hoo"));
+ ASSERT_THAT(names(), UnorderedElementsAre("foo", "hoo"));
+}
+
+TEST_F(SqliteDatabase, ProgressHandlerInterrupts)
+{
+ Sqlite::WriteStatement<1> statement("INSERT INTO test(name) VALUES (?)", database);
+ lock.unlock();
+ Sqlite::ProgressHandler handler{[] { return Sqlite::Progress::Interrupt; }, 1, database};
+ lock.lock();
+
+ ASSERT_THROW(statement.write(42), Sqlite::ExecutionInterrupted);
+ lock.unlock();
+}
+
+TEST_F(SqliteDatabase, ProgressHandlerContinues)
+{
+ Sqlite::WriteStatement<1> statement("INSERT INTO test(name) VALUES (?)", database);
+ lock.unlock();
+ Sqlite::ProgressHandler handler{[] { return Sqlite::Progress::Continue; }, 1, database};
+ lock.lock();
+
+ ASSERT_NO_THROW(statement.write(42));
+ lock.unlock();
+}
+
+TEST_F(SqliteDatabase, ProgressHandlerResetsAfterLeavingScope)
+{
+ lock.unlock();
+ {
+ Sqlite::ProgressHandler handler{[] { return Sqlite::Progress::Interrupt; }, 1, database};
+ }
+ lock.lock();
+ Sqlite::WriteStatement<1> statement("INSERT INTO test(name) VALUES (?)", database);
+
+ ASSERT_NO_THROW(statement.write(42));
}
} // namespace
diff --git a/tests/unit/unittest/sqlitedatabasebackend-test.cpp b/tests/unit/unittest/sqlitedatabasebackend-test.cpp
index 81d0d9a219..9e08eb5b00 100644
--- a/tests/unit/unittest/sqlitedatabasebackend-test.cpp
+++ b/tests/unit/unittest/sqlitedatabasebackend-test.cpp
@@ -31,7 +31,7 @@ protected:
{
database.lock();
QDir::temp().remove(QStringLiteral("SqliteDatabaseBackendTest.db"));
- databaseBackend.open(databaseFilePath, OpenMode::ReadWrite);
+ databaseBackend.open(databaseFilePath, OpenMode::ReadWrite, Sqlite::JournalMode::Wal);
}
~SqliteDatabaseBackend() noexcept(true)
@@ -49,7 +49,7 @@ using SqliteDatabaseBackendSlowTest = SqliteDatabaseBackend;
TEST_F(SqliteDatabaseBackend, OpenAlreadyOpenDatabase)
{
- ASSERT_THROW(databaseBackend.open(databaseFilePath, OpenMode::ReadWrite),
+ ASSERT_THROW(databaseBackend.open(databaseFilePath, OpenMode::ReadWrite, Sqlite::JournalMode::Wal),
Sqlite::DatabaseIsAlreadyOpen);
}
@@ -62,7 +62,9 @@ TEST_F(SqliteDatabaseBackend, CloseAlreadyClosedDatabase)
TEST_F(SqliteDatabaseBackend, OpenWithWrongPath)
{
- ASSERT_THROW(databaseBackend.open("/xxx/SqliteDatabaseBackendTest.db", OpenMode::ReadWrite),
+ ASSERT_THROW(databaseBackend.open("/xxx/SqliteDatabaseBackendTest.db",
+ OpenMode::ReadWrite,
+ Sqlite::JournalMode::Wal),
Sqlite::WrongFilePath);
}
@@ -101,15 +103,24 @@ TEST_F(SqliteDatabaseBackend, PersistJournalMode)
TEST_F(SqliteDatabaseBackend, OpenModeReadOnly)
{
- auto mode = Backend::openMode(OpenMode::ReadOnly);
+ auto mode = Backend::createOpenFlags(OpenMode::ReadOnly, Sqlite::JournalMode::Wal);
- ASSERT_THAT(mode, SQLITE_OPEN_CREATE | SQLITE_OPEN_READONLY);
+ ASSERT_THAT(mode, SQLITE_OPEN_CREATE | SQLITE_OPEN_READONLY | SQLITE_OPEN_EXRESCODE);
}
TEST_F(SqliteDatabaseBackend, OpenModeReadWrite)
{
- auto mode = Backend::openMode(OpenMode::ReadWrite);
+ auto mode = Backend::createOpenFlags(OpenMode::ReadWrite, Sqlite::JournalMode::Wal);
- ASSERT_THAT(mode, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE);
+ ASSERT_THAT(mode, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXRESCODE);
+}
+
+TEST_F(SqliteDatabaseBackend, OpenModeReadWriteAndMemoryJournal)
+{
+ auto mode = Backend::createOpenFlags(OpenMode::ReadWrite, Sqlite::JournalMode::Memory);
+
+ ASSERT_THAT(mode,
+ SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXRESCODE
+ | SQLITE_OPEN_MEMORY);
}
} // namespace
diff --git a/tests/unit/unittest/sqlitedatabasemock.h b/tests/unit/unittest/sqlitedatabasemock.h
index 1316e3f680..658a0ec88b 100644
--- a/tests/unit/unittest/sqlitedatabasemock.h
+++ b/tests/unit/unittest/sqlitedatabasemock.h
@@ -28,11 +28,14 @@ public:
MOCK_METHOD(void, prepare, (Utils::SmallStringView sqlStatement), ());
- MOCK_METHOD(void, execute, (Utils::SmallStringView sqlStatement), ());
+ MOCK_METHOD(void, execute, (Utils::SmallStringView sqlStatement), (override));
MOCK_METHOD(int64_t, lastInsertedRowId, (), (const));
- MOCK_METHOD(void, setLastInsertedRowId, (int64_t), (const));
+ MOCK_METHOD(void, setLastInsertedRowId, (int64_t), ());
+
+ MOCK_METHOD(int, version, (), (const));
+ MOCK_METHOD(void, setVersion, (int), ());
MOCK_METHOD(bool, isInitialized, (), (const));
diff --git a/tests/unit/unittest/sqlitefunctionregistry-test.cpp b/tests/unit/unittest/sqlitefunctionregistry-test.cpp
new file mode 100644
index 0000000000..de9a566fd5
--- /dev/null
+++ b/tests/unit/unittest/sqlitefunctionregistry-test.cpp
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "googletest.h"
+
+#include <sqlitefunctionregistry.h>
+
+namespace {
+
+class SqliteFunctionRegistry : public testing::Test
+{
+public:
+ SqliteFunctionRegistry() { Sqlite::FunctionRegistry::registerPathExists(database); }
+
+protected:
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+};
+
+TEST_F(SqliteFunctionRegistry, PathExists)
+{
+ std::lock_guard lock{database};
+ Sqlite::ReadStatement<1> statement{"SELECT pathExists('" TESTDATA_DIR "/sqlite_database.db')",
+ database};
+
+ auto pathExists = statement.value<bool>();
+
+ ASSERT_TRUE(pathExists);
+}
+
+TEST_F(SqliteFunctionRegistry, PathDoesntExists)
+{
+ std::lock_guard lock{database};
+ Sqlite::ReadStatement<1> statement{"SELECT pathExists('" TESTDATA_DIR "/sqlite_database2.db')",
+ database};
+
+ auto pathExists = statement.value<bool>();
+
+ ASSERT_FALSE(pathExists);
+}
+
+} // namespace
diff --git a/tests/unit/unittest/sqlitereadwritestatementmock.h b/tests/unit/unittest/sqlitereadwritestatementmock.h
index 3dab28d994..aaf5953074 100644
--- a/tests/unit/unittest/sqlitereadwritestatementmock.h
+++ b/tests/unit/unittest/sqlitereadwritestatementmock.h
@@ -43,7 +43,7 @@ public:
());
template<typename ResultType, typename... QueryTypes>
- auto optionalValue(const QueryTypes &...queryValues)
+ auto optionalValue([[maybe_unused]] const QueryTypes &...queryValues)
{
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::value does not handle result type!");
diff --git a/tests/unit/unittest/sqlitetransaction-test.cpp b/tests/unit/unittest/sqlitetransaction-test.cpp
index 7777358f78..fa502218c7 100644
--- a/tests/unit/unittest/sqlitetransaction-test.cpp
+++ b/tests/unit/unittest/sqlitetransaction-test.cpp
@@ -203,8 +203,7 @@ TEST_F(SqliteTransaction, ExclusiveTNonThrowingDestructorransactionRollBack)
TEST_F(SqliteTransaction, DeferredTransactionBeginThrows)
{
- ON_CALL(mockTransactionBackend, deferredBegin())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, deferredBegin()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_THROW(DeferredTransaction{mockTransactionBackend},
Sqlite::Exception);
@@ -212,8 +211,7 @@ TEST_F(SqliteTransaction, DeferredTransactionBeginThrows)
TEST_F(SqliteTransaction, ImmediateTransactionBeginThrows)
{
- ON_CALL(mockTransactionBackend, immediateBegin())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, immediateBegin()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_THROW(ImmediateTransaction{mockTransactionBackend},
Sqlite::Exception);
@@ -221,8 +219,7 @@ TEST_F(SqliteTransaction, ImmediateTransactionBeginThrows)
TEST_F(SqliteTransaction, ExclusiveTransactionBeginThrows)
{
- ON_CALL(mockTransactionBackend, exclusiveBegin())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, exclusiveBegin()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_THROW(ExclusiveTransaction{mockTransactionBackend},
Sqlite::Exception);
@@ -233,8 +230,7 @@ TEST_F(SqliteTransaction, DeferredTransactionBeginThrowsAndNotRollback)
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
- EXPECT_CALL(mockTransactionBackend, deferredBegin())
- .WillOnce(Throw(Sqlite::Exception("foo")));
+ EXPECT_CALL(mockTransactionBackend, deferredBegin()).WillOnce(Throw(Sqlite::Exception()));
EXPECT_CALL(mockTransactionBackend, rollback()).Times(0);
EXPECT_CALL(mockTransactionBackend, unlock());
@@ -246,8 +242,7 @@ TEST_F(SqliteTransaction, ImmediateTransactionBeginThrowsAndNotRollback)
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
- EXPECT_CALL(mockTransactionBackend, immediateBegin())
- .WillOnce(Throw(Sqlite::Exception("foo")));
+ EXPECT_CALL(mockTransactionBackend, immediateBegin()).WillOnce(Throw(Sqlite::Exception()));
EXPECT_CALL(mockTransactionBackend, rollback()).Times(0);
EXPECT_CALL(mockTransactionBackend, unlock());
@@ -260,8 +255,7 @@ TEST_F(SqliteTransaction, ExclusiveTransactionBeginThrowsAndNotRollback)
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
- EXPECT_CALL(mockTransactionBackend, exclusiveBegin())
- .WillOnce(Throw(Sqlite::Exception("foo")));
+ EXPECT_CALL(mockTransactionBackend, exclusiveBegin()).WillOnce(Throw(Sqlite::Exception()));
EXPECT_CALL(mockTransactionBackend, rollback()).Times(0);
EXPECT_CALL(mockTransactionBackend, unlock());
@@ -270,8 +264,7 @@ TEST_F(SqliteTransaction, ExclusiveTransactionBeginThrowsAndNotRollback)
TEST_F(SqliteTransaction, TransactionCommitThrows)
{
- ON_CALL(mockTransactionBackend, commit())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, commit()).WillByDefault(Throw(Sqlite::Exception()));
ImmediateTransaction transaction{mockTransactionBackend};
ASSERT_THROW(transaction.commit(),
@@ -280,8 +273,7 @@ TEST_F(SqliteTransaction, TransactionCommitThrows)
TEST_F(SqliteTransaction, TransactionRollbackInDestructorThrows)
{
- ON_CALL(mockTransactionBackend, rollback())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, rollback()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_THROW(ExclusiveTransaction{mockTransactionBackend},
Sqlite::Exception);
@@ -289,8 +281,7 @@ TEST_F(SqliteTransaction, TransactionRollbackInDestructorThrows)
TEST_F(SqliteTransaction, TransactionRollbackInDestructorDontThrows)
{
- ON_CALL(mockTransactionBackend, rollback())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, rollback()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_NO_THROW(ExclusiveNonThrowingDestructorTransaction{mockTransactionBackend});
}
@@ -322,15 +313,14 @@ TEST_F(SqliteTransaction, ImmediateSessionTransactionRollBack)
TEST_F(SqliteTransaction, SessionTransactionRollbackInDestructorThrows)
{
- ON_CALL(mockTransactionBackend, sessionRollback()).WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, sessionRollback()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_THROW(ImmediateSessionTransaction{mockTransactionBackend}, Sqlite::Exception);
}
TEST_F(SqliteTransaction, ImmidiateSessionTransactionBeginThrows)
{
- ON_CALL(mockTransactionBackend, immediateSessionBegin())
- .WillByDefault(Throw(Sqlite::Exception("foo")));
+ ON_CALL(mockTransactionBackend, immediateSessionBegin()).WillByDefault(Throw(Sqlite::Exception()));
ASSERT_THROW(ImmediateSessionTransaction{mockTransactionBackend}, Sqlite::Exception);
}
@@ -340,7 +330,7 @@ TEST_F(SqliteTransaction, ImmediateSessionTransactionBeginThrowsAndNotRollback)
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
- EXPECT_CALL(mockTransactionBackend, immediateSessionBegin()).WillOnce(Throw(Sqlite::Exception("foo")));
+ EXPECT_CALL(mockTransactionBackend, immediateSessionBegin()).WillOnce(Throw(Sqlite::Exception()));
EXPECT_CALL(mockTransactionBackend, sessionRollback()).Times(0);
EXPECT_CALL(mockTransactionBackend, unlock());
diff --git a/tests/unit/unittest/sqlitewritestatementmock.h b/tests/unit/unittest/sqlitewritestatementmock.h
index 47f7917985..2fa3146601 100644
--- a/tests/unit/unittest/sqlitewritestatementmock.h
+++ b/tests/unit/unittest/sqlitewritestatementmock.h
@@ -40,6 +40,10 @@ public:
MOCK_METHOD(void, write, (Utils::SmallStringView, long long, Sqlite::BlobView, Sqlite::BlobView), ());
MOCK_METHOD(void,
write,
+ (Utils::SmallStringView, long long, Sqlite::BlobView, Sqlite::BlobView, Sqlite::BlobView),
+ ());
+ MOCK_METHOD(void,
+ write,
(Utils::SmallStringView,
Utils::SmallStringView,
Utils::SmallStringView,
diff --git a/tests/unit/unittest/synchronousimagecache-test.cpp b/tests/unit/unittest/synchronousimagecache-test.cpp
index 02b3571508..d239c22b0d 100644
--- a/tests/unit/unittest/synchronousimagecache-test.cpp
+++ b/tests/unit/unittest/synchronousimagecache-test.cpp
@@ -29,9 +29,15 @@ protected:
ON_CALL(mockStorage,
fetchImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image2}));
+ ON_CALL(mockStorage,
+ fetchMidSizeImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage1}));
ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1}));
ON_CALL(mockStorage,
+ fetchMidSizeImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage2}));
+ ON_CALL(mockStorage,
fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage2}));
ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
@@ -40,7 +46,7 @@ protected:
fetchIcon(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::IconEntry{icon2}));
ON_CALL(mockCollector, createImage(Eq("/path/to/Component.qml"), Eq("extraId1"), _))
- .WillByDefault(Return(std::make_pair(image3, smallImage3)));
+ .WillByDefault(Return(std::make_tuple(image3, midSizeImage3, smallImage3)));
ON_CALL(mockCollector, createIcon(Eq("/path/to/Component.qml"), Eq("extraId1"), _))
.WillByDefault(Return(icon3));
}
@@ -52,10 +58,13 @@ protected:
NiceMock<MockTimeStampProvider> mockTimeStampProvider;
QmlDesigner::SynchronousImageCache cache{mockStorage, mockTimeStampProvider, mockCollector};
QImage image1{1, 1, QImage::Format_ARGB32};
- QImage image2{2, 2, QImage::Format_ARGB32};
- QImage image3{3, 3, QImage::Format_ARGB32};
- QImage smallImage1{1, 1, QImage::Format_ARGB32};
- QImage smallImage2{2, 1, QImage::Format_ARGB32};
+ QImage image2{1, 2, QImage::Format_ARGB32};
+ QImage image3{1, 3, QImage::Format_ARGB32};
+ QImage midSizeImage1{2, 1, QImage::Format_ARGB32};
+ QImage midSizeImage2{2, 2, QImage::Format_ARGB32};
+ QImage midSizeImage3{2, 3, QImage::Format_ARGB32};
+ QImage smallImage1{3, 1, QImage::Format_ARGB32};
+ QImage smallImage2{3, 1, QImage::Format_ARGB32};
QImage smallImage3{3, 1, QImage::Format_ARGB32};
QIcon icon1{QPixmap::fromImage(image1)};
QIcon icon2{QPixmap::fromImage(image2)};
@@ -95,11 +104,51 @@ TEST_F(SynchronousImageCache, GetImageWithOutdatedTimeStampStored)
storeImage(Eq("/path/to/Component.qml+extraId1"),
Eq(Sqlite::TimeStamp{124}),
Eq(image3),
+ Eq(midSizeImage3),
Eq(smallImage3)));
auto image = cache.image("/path/to/Component.qml", "extraId1");
}
+TEST_F(SynchronousImageCache, GetMidSizeImageFromStorage)
+{
+ auto image = cache.midSizeImage("/path/to/Component.qml");
+
+ ASSERT_THAT(image, midSizeImage1);
+}
+
+TEST_F(SynchronousImageCache, GetMidSizeImageWithExtraIdFromStorage)
+{
+ auto image = cache.midSizeImage("/path/to/Component.qml", "extraId1");
+
+ ASSERT_THAT(image, midSizeImage2);
+}
+
+TEST_F(SynchronousImageCache, GetMidSizeImageWithOutdatedTimeStampFromCollector)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{124}));
+
+ auto image = cache.midSizeImage("/path/to/Component.qml", "extraId1");
+
+ ASSERT_THAT(image, midSizeImage3);
+}
+
+TEST_F(SynchronousImageCache, GetMidSizeImageWithOutdatedTimeStampStored)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{124}));
+
+ EXPECT_CALL(mockStorage,
+ storeImage(Eq("/path/to/Component.qml+extraId1"),
+ Eq(Sqlite::TimeStamp{124}),
+ Eq(image3),
+ Eq(midSizeImage3),
+ Eq(smallImage3)));
+
+ auto image = cache.midSizeImage("/path/to/Component.qml", "extraId1");
+}
+
TEST_F(SynchronousImageCache, GetSmallImageFromStorage)
{
auto image = cache.smallImage("/path/to/Component.qml");
@@ -133,6 +182,7 @@ TEST_F(SynchronousImageCache, GetSmallImageWithOutdatedTimeStampStored)
storeImage(Eq("/path/to/Component.qml+extraId1"),
Eq(Sqlite::TimeStamp{124}),
Eq(image3),
+ Eq(midSizeImage3),
Eq(smallImage3)));
auto image = cache.smallImage("/path/to/Component.qml", "extraId1");